use super::prelude::*;
use arrayvec::ArrayVec;
use openssl::bn::{BigNum, BigNumContextRef, BigNumRef};
use safemem::prepend;
lazy_static! {
static ref Q: BigNum = BigNum::from_dec_str(
"57896044618658097711785492504343953926634992332820282019728792003956564819949",
).unwrap();
static ref L: BigNum = BigNum::from_dec_str(
"7237005577332262213973186563042994240857116359379907606001950938285454250989",
).unwrap();
static ref D: BigNum = BigNum::from_dec_str(
"-4513249062541557337682894930092624173785641285191125241628941591882900924598840740",
).unwrap();
static ref B: Point = Point {
x: BigNum::from_dec_str(
"15112221349535400772501151409588531511454012693041857206046113283949847762202",
).unwrap(),
y: BigNum::from_dec_str(
"46316835694926478169428394003475163141307993866256225615783033603165251855960",
).unwrap(),
};
static ref ZERO: BigNum = BigNum::from_u32(0).unwrap();
static ref ONE: BigNum = BigNum::from_u32(1).unwrap();
}
#[derive(Debug, PartialEq, Eq)]
pub struct Point {
pub x: BigNum,
pub y: BigNum,
}
impl Point {
pub fn to_owned(&self) -> Result<Self> {
Ok(Point {
x: self.x.to_owned()?,
y: self.y.to_owned()?,
})
}
}
pub fn sc_reduce32(bytes: &mut [u8; 32], ctx: &mut BigNumContextRef) -> Result<BigNum> {
#[cfg(target_endian = "little")]
bytes.reverse();
let number = BigNum::from_slice(&bytes[..])?;
let mut reduced = BigNum::new()?;
reduced.checked_rem(&number, &*L, ctx)?;
Ok(reduced)
}
pub fn derive_pubkey(bytes: &mut [u8; 32], ctx: &mut BigNumContextRef) -> Result<()> {
#[cfg(target_endian = "little")]
bytes.reverse();
let number = BigNum::from_slice(&bytes[..])?;
let pt = scalar_mult(&*B, &number, ctx)?;
encode_point(bytes, &pt);
Ok(())
}
fn encode_point(bytes: &mut [u8; 32], point: &Point) {
let mut bits = ArrayVec::<[bool; 256]>::new();
for i in 0..255 {
bits.push(point.y.is_bit_set(i));
}
bits.push(point.x.is_bit_set(0));
for i in 0..32 {
let mut byte = 0;
for j in 0..8 {
let bit = bits[i * 8 + j] as u8;
byte |= bit << j;
}
bytes[i] = byte;
}
bits.dispose();
}
pub fn inv(x: &BigNumRef, ctx: &mut BigNumContextRef) -> Result<BigNum> {
let mut q2 = Q.to_owned()?;
q2.sub_word(2)?;
let mut result = BigNum::new()?;
result.mod_exp(x, &q2, &*Q, ctx)?;
Ok(result)
}
pub fn edwards(pt1: &Point, pt2: &Point, ctx: &mut BigNumContextRef) -> Result<Point> {
let &Point {
x: ref x1,
y: ref y1,
} = pt1;
let &Point {
x: ref x2,
y: ref y2,
} = pt2;
let x_mul = {
let mut a = BigNum::new()?;
a.checked_mul(x1, y2, ctx)?;
let mut b = BigNum::new()?;
b.checked_mul(x2, y1, ctx)?;
let mut result = BigNum::new()?;
result.checked_add(&a, &b)?;
result
};
let y_mul = {
let mut a = BigNum::new()?;
a.checked_mul(y1, y2, ctx)?;
let mut b = BigNum::new()?;
b.checked_mul(x1, x2, ctx)?;
let mut result = BigNum::new()?;
result.checked_add(&a, &b)?;
result
};
let d_mul = {
let mut a = BigNum::new()?;
a.checked_mul(&*D, x1, ctx)?;
let mut b = BigNum::new()?;
b.checked_mul(&a, x2, ctx)?;
let mut c = BigNum::new()?;
c.checked_mul(&b, y1, ctx)?;
let mut result = BigNum::new()?;
result.checked_mul(&c, y2, ctx)?;
result
};
let x3 = {
let mut a = BigNum::new()?;
a.checked_add(&*ONE, &d_mul)?;
let b = inv(&a, ctx)?;
let mut result = BigNum::new()?;
result.checked_mul(&x_mul, &b, ctx)?;
result
};
let y3 = {
let mut a = BigNum::new()?;
a.checked_sub(&*ONE, &d_mul)?;
let b = inv(&a, ctx)?;
let mut result = BigNum::new()?;
result.checked_mul(&y_mul, &b, ctx)?;
result
};
let x3_q = {
let mut result = BigNum::new()?;
result.checked_rem(&x3, &*Q, ctx)?;
result
};
let y3_q = {
let mut result = BigNum::new()?;
result.checked_rem(&y3, &*Q, ctx)?;
result
};
Ok(Point { x: x3_q, y: y3_q })
}
pub fn scalar_mult(p: &Point, e: &BigNumRef, ctx: &mut BigNumContextRef) -> Result<Point> {
if e == &*ZERO {
return Ok(Point {
x: ONE.to_owned()?,
y: ZERO.to_owned()?,
});
}
let mut e2 = e.to_owned()?;
e2.div_word(2)?;
let mut q = scalar_mult(p, &e2, ctx)?;
q = edwards(&q, &q, ctx)?;
if e.is_bit_set(0) {
q = edwards(&q, p, ctx)?;
}
q.to_owned()
}
pub fn bn_to_vec32(number: &BigNumRef) -> Vec<u8> {
let mut result = number.to_vec();
let missing = 32 - result.len();
let zeroes = &b"00000000000000000000000000000000"[..missing];
prepend(zeroes, &mut result);
assert_eq!(result.len(), 32);
#[cfg(target_endian = "little")]
result.reverse();
result
}
#[cfg(test)]
use openssl::bn::BigNumContext;
#[test]
fn test_derive_pubkey() {
let mut ctx = BigNumContext::new().unwrap();
let mut bytes = [
0xac, 0xf4, 0x5e, 0x9e, 0x9b, 0x00, 0xda, 0xa8, 0x97, 0x60, 0xb9, 0x82, 0xad, 0xe2, 0x57,
0xe2, 0x26, 0x82, 0x77, 0x5a, 0x17, 0x70, 0xdb, 0x66, 0xbe, 0xb0, 0x57, 0x82, 0x0b, 0x46,
0x77, 0x00,
];
derive_pubkey(&mut bytes, &mut ctx).unwrap();
assert_eq!(
&bytes[..],
b"\x15\xf4\x4b\x26\x18\x1c\x20\x1a\x44\x59\x80\xbd\xed\x64\x16\x63\xd8\xf9\x12\xf1\x40\x92\x2f\x69\x09\xf7\x12\x49\x77\xc1\x7c\xc7",
);
}
#[test]
fn test_encode_point() {
let mut buffer = [0; 32];
encode_point(&mut buffer, &*B);
assert_eq!(
&buffer[..],
b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66",
);
encode_point(
&mut buffer,
&Point {
x: BigNum::from_dec_str("239480239840293842309840923").unwrap(),
y: BigNum::from_dec_str("58910865193789017923075092").unwrap(),
},
);
assert_eq!(
&buffer[..],
b"\x14\x3c\x51\x36\x52\xf2\xbb\x66\xdc\xba\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80",
);
}
#[test]
fn test_edwards() {
let mut ctx = BigNumContext::new().unwrap();
let pt1 = Point {
x: BigNum::from_dec_str("1923374821399491313195").unwrap(),
y: BigNum::from_dec_str("886801747184909184381943").unwrap(),
};
let pt2 = Point {
x: BigNum::from_dec_str("6777193769071361351005019").unwrap(),
y: BigNum::from_dec_str("99681818311341583949189090").unwrap(),
};
let Point { x, y } = edwards(&pt1, &pt2, &mut ctx).unwrap();
assert_eq!(
&x,
&BigNum::from_dec_str(
"19145305399556633246416803965847122123950043313116994625199152792839726592320",
).unwrap(),
);
assert_eq!(
&y,
&BigNum::from_dec_str(
"1311926177560311914111494945666819815903779764478329976178982182484658429924",
).unwrap(),
);
}
#[test]
fn test_scalar_mult() {
let mut ctx = BigNumContext::new().unwrap();
let e = BigNum::from_dec_str("923589108657107938910930183980").unwrap();
let pt = Point {
x: BigNum::from_dec_str("38049823940823904823904801805").unwrap(),
y: BigNum::from_dec_str("90148109258910285903285093819").unwrap(),
};
let Point { x, y } = scalar_mult(&pt, &e, &mut ctx).unwrap();
assert_eq!(
&x,
&BigNum::from_dec_str(
"52693087480432376829905685868955052399509450169404031178816965074041171202152",
).unwrap(),
);
assert_eq!(
&y,
&BigNum::from_dec_str(
"37289752567154786074414612316137518169923258170068403397141657798034183258505",
).unwrap(),
);
}