#![allow(dead_code)]
use core::cmp::Ordering;
use crate::drbg::HmacDrbgSha256;
use crate::hash::noxtls_sha256;
use crate::internal_alloc::Vec;
use noxtls_core::{Error, Result};
use super::bignum::BigUint;
#[path = "p256_fixed.rs"]
mod fixed_backend;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct P256PrivateKey {
scalar: BigUint,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct P256PublicKey {
point: CurvePoint,
}
#[derive(Debug, Clone, Eq, PartialEq)]
struct CurvePoint {
x: BigUint,
y: BigUint,
infinity: bool,
}
#[derive(Debug, Clone, Eq, PartialEq)]
struct JacobianPoint {
x: BigUint,
y: BigUint,
z: BigUint,
infinity: bool,
}
fn scalar_to_array(scalar: &BigUint) -> Result<[u8; 32]> {
let bytes = scalar.to_be_bytes_padded(32)?;
let mut out = [0_u8; 32];
out.copy_from_slice(&bytes);
Ok(out)
}
fn point_to_coordinate_arrays(point: &CurvePoint) -> Result<([u8; 32], [u8; 32])> {
let x = scalar_to_array(&point.x)?;
let y = scalar_to_array(&point.y)?;
Ok((x, y))
}
fn public_key_from_coordinate_bytes(x: &[u8; 32], y: &[u8; 32]) -> P256PublicKey {
P256PublicKey {
point: CurvePoint {
x: BigUint::from_be_bytes(x),
y: BigUint::from_be_bytes(y),
infinity: false,
},
}
}
impl P256PrivateKey {
pub fn from_bytes(bytes: [u8; 32]) -> Result<Self> {
let scalar = BigUint::from_be_bytes(&bytes);
if scalar.is_zero() {
return Err(Error::CryptoFailure("p256 private scalar must be non-zero"));
}
let n = curve_order_n();
if scalar.cmp(&n) != Ordering::Less {
return Err(Error::CryptoFailure("p256 private scalar out of range"));
}
Ok(Self { scalar })
}
pub fn to_bytes(&self) -> Result<[u8; 32]> {
let bytes = self.scalar.to_be_bytes_padded(32)?;
let mut out = [0_u8; 32];
out.copy_from_slice(&bytes);
Ok(out)
}
pub fn clear(&mut self) {
self.scalar.clear();
}
pub fn public_key(&self) -> Result<P256PublicKey> {
let scalar = scalar_to_array(&self.scalar)?;
let (x, y) = fixed_backend::scalar_mul_basepoint_bytes(&scalar)?;
Ok(public_key_from_coordinate_bytes(&x, &y))
}
pub fn diffie_hellman(&self, peer: &P256PublicKey) -> Result<[u8; 32]> {
peer.validate()?;
let scalar = scalar_to_array(&self.scalar)?;
let (x, y) = point_to_coordinate_arrays(&peer.point)?;
fixed_backend::ecdh_shared_secret_bytes(&scalar, &x, &y)
}
pub fn sign_sha256(&self, message: &[u8]) -> Result<([u8; 32], [u8; 32])> {
let digest = noxtls_sha256(message);
self.sign_digest(&digest)
}
pub fn sign_sha256_auto(
&self,
message: &[u8],
drbg: &mut HmacDrbgSha256,
) -> Result<([u8; 32], [u8; 32])> {
let digest = noxtls_sha256(message);
self.sign_digest_auto(&digest, drbg)
}
pub fn sign_digest(&self, digest: &[u8; 32]) -> Result<([u8; 32], [u8; 32])> {
let scalar = scalar_to_array(&self.scalar)?;
fixed_backend::ecdsa_sign_digest_bytes(&scalar, digest)
}
pub fn sign_digest_auto(
&self,
digest: &[u8; 32],
drbg: &mut HmacDrbgSha256,
) -> Result<([u8; 32], [u8; 32])> {
let scalar = scalar_to_array(&self.scalar)?;
for _ in 0..64 {
let nonce_bytes = drbg.generate(32, b"p256_ecdsa_nonce")?;
let nonce_arr: [u8; 32] = nonce_bytes
.as_slice()
.try_into()
.map_err(|_| Error::InvalidLength("p256 ecdsa nonce length mismatch"))?;
if let Some(sig) =
fixed_backend::ecdsa_sign_digest_with_nonce_bytes(&scalar, digest, &nonce_arr)?
{
return Ok(sig);
}
}
Err(Error::CryptoFailure(
"p256 ecdsa nonce generation exhausted retry budget",
))
}
}
impl Drop for P256PrivateKey {
fn drop(&mut self) {
self.clear();
}
}
impl P256PublicKey {
pub fn from_uncompressed(bytes: &[u8]) -> Result<Self> {
if bytes.len() != 65 {
return Err(Error::InvalidLength(
"p256 uncompressed public key must be 65 bytes",
));
}
if bytes[0] != 0x04 {
return Err(Error::ParseFailure(
"p256 public key must be uncompressed SEC1 format",
));
}
let x = BigUint::from_be_bytes(&bytes[1..33]);
let y = BigUint::from_be_bytes(&bytes[33..65]);
let point = CurvePoint {
x,
y,
infinity: false,
};
let key = Self { point };
key.validate()?;
Ok(key)
}
pub fn to_uncompressed(&self) -> Result<[u8; 65]> {
self.validate()?;
let mut out = [0_u8; 65];
out[0] = 0x04;
let x = self.point.x.to_be_bytes_padded(32)?;
let y = self.point.y.to_be_bytes_padded(32)?;
out[1..33].copy_from_slice(&x);
out[33..65].copy_from_slice(&y);
Ok(out)
}
pub fn validate(&self) -> Result<()> {
if self.point.infinity {
return Err(Error::CryptoFailure(
"p256 public point at infinity is invalid",
));
}
let (x, y) = point_to_coordinate_arrays(&self.point)?;
if !fixed_backend::validate_public_key_bytes(&x, &y) {
return Err(Error::CryptoFailure("p256 public point is not on curve"));
}
Ok(())
}
}
pub fn noxtls_p256_ecdh_shared_secret(
private_key: &P256PrivateKey,
peer_public_key: &P256PublicKey,
) -> Result<[u8; 32]> {
private_key.diffie_hellman(peer_public_key)
}
pub fn noxtls_p256_ecdsa_sign_sha256(
private_key: &P256PrivateKey,
message: &[u8],
) -> Result<([u8; 32], [u8; 32])> {
private_key.sign_sha256(message)
}
pub fn noxtls_p256_ecdsa_sign_sha256_auto(
private_key: &P256PrivateKey,
message: &[u8],
drbg: &mut HmacDrbgSha256,
) -> Result<([u8; 32], [u8; 32])> {
private_key.sign_sha256_auto(message, drbg)
}
pub fn noxtls_p256_ecdsa_sign_digest(
private_key: &P256PrivateKey,
digest: &[u8; 32],
) -> Result<([u8; 32], [u8; 32])> {
private_key.sign_digest(digest)
}
pub fn noxtls_p256_ecdsa_sign_digest_auto(
private_key: &P256PrivateKey,
digest: &[u8; 32],
drbg: &mut HmacDrbgSha256,
) -> Result<([u8; 32], [u8; 32])> {
private_key.sign_digest_auto(digest, drbg)
}
pub fn noxtls_p256_ecdsa_verify_sha256(
public_key: &P256PublicKey,
message: &[u8],
r: &[u8; 32],
s: &[u8; 32],
) -> Result<()> {
let digest = noxtls_sha256(message);
noxtls_p256_ecdsa_verify_digest(public_key, &digest, r, s)
}
pub fn noxtls_p256_ecdsa_verify_digest(
public_key: &P256PublicKey,
digest: &[u8; 32],
r: &[u8; 32],
s: &[u8; 32],
) -> Result<()> {
let (x, y) = point_to_coordinate_arrays(&public_key.point)?;
fixed_backend::ecdsa_verify_digest_bytes(&x, &y, digest, r, s)
}
pub fn noxtls_p256_generate_private_key_auto(drbg: &mut HmacDrbgSha256) -> Result<P256PrivateKey> {
for _ in 0..64 {
let scalar = drbg.generate(32, b"p256_private_scalar")?;
let bytes: [u8; 32] = scalar
.as_slice()
.try_into()
.map_err(|_| Error::InvalidLength("p256 private scalar length mismatch"))?;
if let Ok(key) = P256PrivateKey::from_bytes(bytes) {
return Ok(key);
}
}
Err(Error::CryptoFailure(
"p256 private key generation exhausted retry budget",
))
}
fn scalar_mul(scalar: &BigUint, point: &CurvePoint) -> Result<CurvePoint> {
let p = curve_modulus_p();
scalar_mul_jacobian(scalar, point, &p).to_affine(&p)
}
fn scalar_mul_basepoint(scalar: &BigUint) -> Result<CurvePoint> {
let p = curve_modulus_p();
scalar_mul_basepoint_jacobian(scalar, &p).to_affine(&p)
}
fn scalar_mul_jacobian(scalar: &BigUint, point: &CurvePoint, p: &BigUint) -> JacobianPoint {
if point.infinity {
return JacobianPoint::infinity();
}
let base = JacobianPoint::from_affine(point);
let table = precompute_nibble_window(&base, p);
let mut acc = JacobianPoint::infinity();
let nibble_count = scalar.bit_len().div_ceil(4);
for nibble_idx in (0..nibble_count).rev() {
for _ in 0..4 {
acc = jacobian_double(&acc, p);
}
let nibble = usize::from(scalar.nibble_le(nibble_idx));
if nibble != 0 {
acc = jacobian_add(&acc, &table[nibble], p);
}
}
acc
}
fn scalar_mul_basepoint_jacobian(scalar: &BigUint, p: &BigUint) -> JacobianPoint {
let table = basepoint_window_table(p);
let mut acc = JacobianPoint::infinity();
let nibble_count = scalar.bit_len().div_ceil(4);
for nibble_idx in (0..nibble_count).rev() {
for _ in 0..4 {
acc = jacobian_double(&acc, p);
}
let nibble = usize::from(scalar.nibble_le(nibble_idx));
if nibble != 0 {
acc = jacobian_add(&acc, &table[nibble], p);
}
}
acc
}
fn precompute_nibble_window(base: &JacobianPoint, p: &BigUint) -> Vec<JacobianPoint> {
let mut table = Vec::with_capacity(16);
table.push(JacobianPoint::infinity());
table.push(base.clone());
for idx in 2..16 {
let next = jacobian_add(&table[idx - 1], base, p);
table.push(next);
}
table
}
fn basepoint_window_table(p: &BigUint) -> [JacobianPoint; 16] {
let base = JacobianPoint::from_affine(&curve_base_point());
build_basepoint_window_table(&base, p)
}
fn build_basepoint_window_table(base: &JacobianPoint, p: &BigUint) -> [JacobianPoint; 16] {
let mut table = core::array::from_fn(|_| JacobianPoint::infinity());
table[1] = base.clone();
for idx in 2..16 {
table[idx] = jacobian_add(&table[idx - 1], base, p);
}
table
}
fn is_point_on_curve(point: &CurvePoint) -> bool {
if point.infinity {
return false;
}
let p = curve_modulus_p();
let a = curve_a();
let b = curve_b();
let y_sq = mod_mul(&point.y, &point.y, &p);
let x_sq = mod_mul(&point.x, &point.x, &p);
let x_cu = mod_mul(&x_sq, &point.x, &p);
let ax = mod_mul(&a, &point.x, &p);
let rhs = mod_add(&mod_add(&x_cu, &ax, &p), &b, &p);
y_sq == rhs
}
impl CurvePoint {
fn infinity() -> Self {
Self {
x: BigUint::zero(),
y: BigUint::zero(),
infinity: true,
}
}
}
impl JacobianPoint {
fn infinity() -> Self {
Self {
x: BigUint::zero(),
y: BigUint::zero(),
z: BigUint::zero(),
infinity: true,
}
}
fn from_affine(point: &CurvePoint) -> Self {
if point.infinity {
return Self::infinity();
}
Self {
x: point.x.clone(),
y: point.y.clone(),
z: BigUint::one(),
infinity: false,
}
}
fn to_affine(&self, p: &BigUint) -> Result<CurvePoint> {
if self.infinity || self.z.is_zero() {
return Ok(CurvePoint::infinity());
}
let z_inv = mod_inv(&self.z, p)?;
let z_inv2 = mod_mul(&z_inv, &z_inv, p);
let z_inv3 = mod_mul(&z_inv2, &z_inv, p);
Ok(CurvePoint {
x: mod_mul(&self.x, &z_inv2, p),
y: mod_mul(&self.y, &z_inv3, p),
infinity: false,
})
}
}
fn jacobian_double(a: &JacobianPoint, p: &BigUint) -> JacobianPoint {
if a.infinity || a.y.is_zero() {
return JacobianPoint::infinity();
}
let yy = mod_mul(&a.y, &a.y, p);
let yyyy = mod_mul(&yy, &yy, p);
let x_yy = mod_mul(&a.x, &yy, p);
let s = mod_quadruple(&x_yy, p);
let zz = mod_mul(&a.z, &a.z, p);
let x_minus_zz = mod_sub(&a.x, &zz, p);
let x_plus_zz = mod_add(&a.x, &zz, p);
let m_term = mod_mul(&x_minus_zz, &x_plus_zz, p);
let m = mod_triple(&m_term, p);
let m2 = mod_mul(&m, &m, p);
let two_s = mod_double(&s, p);
let x3 = mod_sub(&m2, &two_s, p);
let s_minus_x3 = mod_sub(&s, &x3, p);
let m_s_minus_x3 = mod_mul(&m, &s_minus_x3, p);
let eight_yyyy = mod_octuple(&yyyy, p);
let y3 = mod_sub(&m_s_minus_x3, &eight_yyyy, p);
let yz = mod_mul(&a.y, &a.z, p);
let z3 = mod_double(&yz, p);
JacobianPoint {
x: x3,
y: y3,
z: z3,
infinity: false,
}
}
fn jacobian_add(a: &JacobianPoint, b: &JacobianPoint, p: &BigUint) -> JacobianPoint {
if a.infinity {
return b.clone();
}
if b.infinity {
return a.clone();
}
let z1z1 = mod_mul(&a.z, &a.z, p);
let z2z2 = mod_mul(&b.z, &b.z, p);
let u1 = mod_mul(&a.x, &z2z2, p);
let u2 = mod_mul(&b.x, &z1z1, p);
let z1_cubed = mod_mul(&z1z1, &a.z, p);
let z2_cubed = mod_mul(&z2z2, &b.z, p);
let s1 = mod_mul(&a.y, &z2_cubed, p);
let s2 = mod_mul(&b.y, &z1_cubed, p);
if u1 == u2 {
if s1 != s2 {
return JacobianPoint::infinity();
}
return jacobian_double(a, p);
}
let h = mod_sub(&u2, &u1, p);
let two_h = mod_double(&h, p);
let i = mod_mul(&two_h, &two_h, p);
let j = mod_mul(&h, &i, p);
let s2_minus_s1 = mod_sub(&s2, &s1, p);
let r = mod_double(&s2_minus_s1, p);
let v = mod_mul(&u1, &i, p);
let r2 = mod_mul(&r, &r, p);
let two_v = mod_double(&v, p);
let x3 = mod_sub(&mod_sub(&r2, &j, p), &two_v, p);
let v_minus_x3 = mod_sub(&v, &x3, p);
let r_v_minus_x3 = mod_mul(&r, &v_minus_x3, p);
let two_s1 = mod_double(&s1, p);
let two_s1_j = mod_mul(&two_s1, &j, p);
let y3 = mod_sub(&r_v_minus_x3, &two_s1_j, p);
let z1_plus_z2 = mod_add(&a.z, &b.z, p);
let z1_plus_z2_sq = mod_mul(&z1_plus_z2, &z1_plus_z2, p);
let z_sum = mod_sub(&mod_sub(&z1_plus_z2_sq, &z1z1, p), &z2z2, p);
let z3 = mod_mul(&z_sum, &h, p);
JacobianPoint {
x: x3,
y: y3,
z: z3,
infinity: false,
}
}
fn mod_add(a: &BigUint, b: &BigUint, m: &BigUint) -> BigUint {
let sum = a.add(b);
if sum.cmp(m) != Ordering::Less {
sum.sub(m)
} else {
sum
}
}
fn mod_sub(a: &BigUint, b: &BigUint, m: &BigUint) -> BigUint {
if a.cmp(b) != Ordering::Less {
a.sub(b)
} else {
m.sub(&b.sub(a))
}
}
fn mod_mul(a: &BigUint, b: &BigUint, m: &BigUint) -> BigUint {
if let Some(value) = mod_mul_u256_fast(a, b, m) {
value
} else {
a.mul(b).modulo(m)
}
}
fn mod_inv(a: &BigUint, m: &BigUint) -> Result<BigUint> {
if a.is_zero() {
return Err(Error::CryptoFailure(
"p256 modular inverse of zero is undefined",
));
}
let one = BigUint::one();
let mut u = a.modulo(m);
let mut v = m.clone();
let mut x1 = one.clone();
let mut x2 = BigUint::zero();
while u != one && v != one {
while u.is_even() {
u.shr1_assign();
mod_inv_halve_assign(&mut x1, m);
}
while v.is_even() {
v.shr1_assign();
mod_inv_halve_assign(&mut x2, m);
}
if u.cmp(&v) != Ordering::Less {
u.sub_assign(&v);
mod_sub_assign(&mut x1, &x2, m);
} else {
v.sub_assign(&u);
mod_sub_assign(&mut x2, &x1, m);
}
}
if u == one {
Ok(x1.modulo(m))
} else if v == one {
Ok(x2.modulo(m))
} else {
Err(Error::CryptoFailure("p256 modular inverse does not exist"))
}
}
fn mod_inv_halve_assign(value: &mut BigUint, modulus: &BigUint) {
if value.is_even() {
value.shr1_assign();
} else {
value.add_assign(modulus);
value.shr1_assign();
}
}
fn mod_sub_assign(value: &mut BigUint, other: &BigUint, modulus: &BigUint) {
if value.cmp(other) != Ordering::Less {
value.sub_assign(other);
} else {
value.add_assign(modulus);
value.sub_assign(other);
}
}
fn mod_mul_u256_fast(a: &BigUint, b: &BigUint, modulus: &BigUint) -> Option<BigUint> {
if a.limbs_len() > 8 || b.limbs_len() > 8 || modulus.limbs_len() != 8 {
return None;
}
let mut a_limbs = [0_u32; 8];
let mut b_limbs = [0_u32; 8];
let mut m_limbs = [0_u32; 8];
a.copy_limbs_le_padded(&mut a_limbs);
b.copy_limbs_le_padded(&mut b_limbs);
modulus.copy_limbs_le_padded(&mut m_limbs);
let product = mul_u256x_u256(&a_limbs, &b_limbs);
let reduced = mod_512_by_256_limb(&product, &m_limbs)?;
Some(BigUint::from_limbs_le_slice(&reduced))
}
fn mul_u256x_u256(a: &[u32; 8], b: &[u32; 8]) -> [u32; 16] {
let mut out = [0_u32; 16];
for (i, &a_limb) in a.iter().enumerate() {
let mut carry = 0_u64;
for (j, &b_limb) in b.iter().enumerate() {
let idx = i + j;
let cur = u64::from(out[idx]) + u64::from(a_limb) * u64::from(b_limb) + carry;
out[idx] = cur as u32;
carry = cur >> 32;
}
let mut idx = i + 8;
while carry != 0 && idx < out.len() {
let cur = u64::from(out[idx]) + carry;
out[idx] = cur as u32;
carry = cur >> 32;
idx += 1;
}
}
out
}
fn mod_512_by_256_limb(dividend: &[u32; 16], divisor: &[u32; 8]) -> Option<[u32; 8]> {
if divisor[7] == 0 {
return None;
}
let mut v = *divisor;
let mut u = [0_u32; 17];
u[..16].copy_from_slice(dividend);
let norm_shift = v[7].leading_zeros() as usize;
if norm_shift > 0 {
limbs_shl_bits_fixed(&mut v, norm_shift);
limbs_shl_bits_fixed(&mut u, norm_shift);
}
for j in (0..=8).rev() {
let num = (u64::from(u[j + 8]) << 32) | u64::from(u[j + 7]);
let den = u64::from(v[7]);
let mut qhat = (num / den).min(u64::from(u32::MAX));
let mut rhat = num - (qhat * den);
loop {
let lhs = qhat * u64::from(v[6]);
if (rhat >> 32) != 0 {
break;
}
let rhs = (rhat << 32) + u64::from(u[j + 6]);
if lhs <= rhs {
break;
}
qhat -= 1;
rhat += den;
}
if qhat != 0 {
let borrow = limb_mul_sub_fixed(&mut u, j, qhat as u32, &v);
if borrow {
let carry_out = limb_add_at_fixed(&mut u, j, &v);
if carry_out != 0 && (j + 9) <= 16 {
u[j + 9] = u[j + 9].wrapping_add(carry_out);
}
}
}
}
while u[8] != 0 || ge_limbs_prefix_fixed(&u, divisor, 8) {
if sub_limbs_borrow_prefix_fixed(&mut u, divisor, 8) {
if u[8] != 0 {
u[8] -= 1;
} else {
break;
}
}
}
if norm_shift > 0 {
limbs_shr_bits_fixed(&mut u[..8], norm_shift);
}
let mut rem = [0_u32; 8];
rem.copy_from_slice(&u[..8]);
if ge_limbs_prefix_fixed(&rem, divisor, 8) {
sub_limbs_prefix_fixed(&mut rem, divisor, 8);
}
Some(rem)
}
fn limbs_shl_bits_fixed(limbs: &mut [u32], k: usize) {
if k == 0 || k >= 32 {
return;
}
let mut carry = 0_u32;
for limb in limbs {
let v = *limb;
*limb = (v << k) | carry;
carry = v >> (32 - k);
}
}
fn limbs_shr_bits_fixed(limbs: &mut [u32], k: usize) {
if k == 0 || k >= 32 {
return;
}
let mut carry = 0_u32;
for idx in (0..limbs.len()).rev() {
let v = limbs[idx];
limbs[idx] = (v >> k) | carry;
carry = v << (32 - k);
}
}
fn ge_limbs_prefix_fixed(a: &[u32], b: &[u32; 8], len: usize) -> bool {
for idx in (0..len).rev() {
if a[idx] != b[idx] {
return a[idx] > b[idx];
}
}
true
}
fn sub_limbs_prefix_fixed(a: &mut [u32], b: &[u32; 8], len: usize) {
let mut borrow = 0_u64;
for idx in 0..len {
let av = u64::from(a[idx]);
let bv = u64::from(b[idx]) + borrow;
if av < bv {
a[idx] = (av + (1_u64 << 32) - bv) as u32;
borrow = 1;
} else {
a[idx] = (av - bv) as u32;
borrow = 0;
}
}
}
fn sub_limbs_borrow_prefix_fixed(a: &mut [u32], b: &[u32; 8], len: usize) -> bool {
let mut borrow = 0_u64;
for idx in 0..len {
let av = u64::from(a[idx]);
let bv = u64::from(b[idx]) + borrow;
if av < bv {
a[idx] = (av + (1_u64 << 32) - bv) as u32;
borrow = 1;
} else {
a[idx] = (av - bv) as u32;
borrow = 0;
}
}
borrow != 0
}
fn limb_mul_sub_fixed(u: &mut [u32; 17], offset: usize, qhat: u32, v: &[u32; 8]) -> bool {
let mut borrow = 0_u64;
let mut carry = 0_u64;
for idx in 0..8 {
let prod = u64::from(qhat) * u64::from(v[idx]) + carry;
carry = prod >> 32;
let sub = (prod & 0xFFFF_FFFF) + borrow;
let cur = u64::from(u[offset + idx]);
if cur < sub {
u[offset + idx] = (cur + (1_u64 << 32) - sub) as u32;
borrow = 1;
} else {
u[offset + idx] = (cur - sub) as u32;
borrow = 0;
}
}
let sub_hi = carry + borrow;
let cur = u64::from(u[offset + 8]);
if cur < sub_hi {
u[offset + 8] = (cur + (1_u64 << 32) - sub_hi) as u32;
true
} else {
u[offset + 8] = (cur - sub_hi) as u32;
false
}
}
fn limb_add_at_fixed(u: &mut [u32; 17], offset: usize, v: &[u32; 8]) -> u32 {
let mut carry = 0_u64;
for idx in 0..8 {
let cur = u64::from(u[offset + idx]) + u64::from(v[idx]) + carry;
u[offset + idx] = cur as u32;
carry = cur >> 32;
}
let cur = u64::from(u[offset + 8]) + carry;
u[offset + 8] = cur as u32;
(cur >> 32) as u32
}
fn mod_double(a: &BigUint, m: &BigUint) -> BigUint {
mod_add(a, a, m)
}
fn mod_triple(a: &BigUint, m: &BigUint) -> BigUint {
mod_add(&mod_double(a, m), a, m)
}
fn mod_quadruple(a: &BigUint, m: &BigUint) -> BigUint {
mod_double(&mod_double(a, m), m)
}
fn mod_octuple(a: &BigUint, m: &BigUint) -> BigUint {
mod_double(&mod_quadruple(a, m), m)
}
fn curve_modulus_p() -> BigUint {
BigUint::from_be_bytes(&[
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff,
])
}
fn curve_a() -> BigUint {
BigUint::from_be_bytes(&[
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xfc,
])
}
fn curve_b() -> BigUint {
BigUint::from_be_bytes(&[
0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7, 0xb3, 0xeb, 0xbd, 0x55, 0x76, 0x98, 0x86,
0xbc, 0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53, 0xb0, 0xf6, 0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2,
0x60, 0x4b,
])
}
fn curve_order_n() -> BigUint {
BigUint::from_be_bytes(&[
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63,
0x25, 0x51,
])
}
fn curve_base_point() -> CurvePoint {
CurvePoint {
x: BigUint::from_be_bytes(&[
0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47, 0xf8, 0xbc, 0xe6, 0xe5, 0x63, 0xa4,
0x40, 0xf2, 0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb, 0x33, 0xa0, 0xf4, 0xa1, 0x39, 0x45,
0xd8, 0x98, 0xc2, 0x96,
]),
y: BigUint::from_be_bytes(&[
0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b, 0x8e, 0xe7, 0xeb, 0x4a, 0x7c, 0x0f,
0x9e, 0x16, 0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, 0xce, 0xcb, 0xb6, 0x40, 0x68,
0x37, 0xbf, 0x51, 0xf5,
]),
infinity: false,
}
}
fn is_all_zero(bytes: &[u8; 32]) -> bool {
let mut acc = 0_u8;
for byte in bytes {
acc |= *byte;
}
acc == 0
}
fn ct_bytes_eq(left: &[u8], right: &[u8]) -> bool {
if left.len() != right.len() {
return false;
}
let mut diff = 0_u8;
for (&l, &r) in left.iter().zip(right.iter()) {
diff |= l ^ r;
}
diff == 0
}
fn derive_signing_nonce(private_scalar: &BigUint, digest: &[u8; 32], counter: u32) -> BigUint {
let mut seed = Vec::with_capacity(68);
let scalar_bytes = private_scalar
.to_be_bytes_padded(32)
.expect("p256 private scalar should fit in 32 bytes");
seed.extend_from_slice(&scalar_bytes);
seed.extend_from_slice(digest);
seed.extend_from_slice(&counter.to_be_bytes());
BigUint::from_be_bytes(&noxtls_sha256(&seed))
}
#[cfg(test)]
mod tests {
use super::{
noxtls_p256_ecdsa_sign_sha256, noxtls_p256_ecdsa_verify_sha256, P256PrivateKey,
P256PublicKey,
};
use p256::ecdsa::{
signature::{Signer, Verifier},
Signature, SigningKey,
};
const SCALAR: [u8; 32] = [
0xC9, 0x90, 0x39, 0x9A, 0xF2, 0x7C, 0x70, 0x26, 0xB0, 0x7C, 0x9E, 0x4D, 0x4B, 0x04, 0x99,
0xF8, 0x80, 0x83, 0xD6, 0x27, 0xD1, 0x97, 0xF5, 0x74, 0x3B, 0x49, 0xB4, 0xF7, 0xE3, 0xA0,
0xBC, 0x11,
];
#[test]
fn noxtls_p256_public_key_matches_rustcrypto() {
let noxtls_key = P256PrivateKey::from_bytes(SCALAR).expect("noxtls key");
let noxtls_pub = noxtls_key
.public_key()
.expect("noxtls public key")
.to_uncompressed()
.expect("uncompressed");
let rustcrypto_key = SigningKey::from_bytes(&SCALAR.into()).expect("rustcrypto key");
let rustcrypto_pub = rustcrypto_key.verifying_key().to_encoded_point(false);
assert_eq!(noxtls_pub.as_slice(), rustcrypto_pub.as_bytes());
}
#[test]
fn noxtls_p256_sign_and_verify_cross_check_with_rustcrypto() {
let message = b"p256 cross-check message";
let noxtls_key = P256PrivateKey::from_bytes(SCALAR).expect("noxtls key");
let noxtls_pub = noxtls_key.public_key().expect("noxtls public key");
let (r, s) = noxtls_p256_ecdsa_sign_sha256(&noxtls_key, message).expect("noxtls sign");
let mut sig_bytes = [0_u8; 64];
sig_bytes[..32].copy_from_slice(&r);
sig_bytes[32..].copy_from_slice(&s);
let rustcrypto_sig = Signature::from_slice(&sig_bytes).expect("signature");
let rustcrypto_key = SigningKey::from_bytes(&SCALAR.into()).expect("rustcrypto key");
assert!(rustcrypto_key
.verifying_key()
.verify(message, &rustcrypto_sig)
.is_ok());
let rustcrypto_sig: Signature = rustcrypto_key.sign(message);
let rustcrypto_pub = P256PublicKey::from_uncompressed(
rustcrypto_key
.verifying_key()
.to_encoded_point(false)
.as_bytes(),
)
.expect("noxtls public key parse");
let rustcrypto_bytes = rustcrypto_sig.to_bytes();
let mut r_bytes = [0_u8; 32];
let mut s_bytes = [0_u8; 32];
r_bytes.copy_from_slice(&rustcrypto_bytes[..32]);
s_bytes.copy_from_slice(&rustcrypto_bytes[32..]);
assert!(noxtls_p256_ecdsa_verify_sha256(&noxtls_pub, message, &r, &s).is_ok());
assert!(
noxtls_p256_ecdsa_verify_sha256(&rustcrypto_pub, message, &r_bytes, &s_bytes).is_ok()
);
}
}