Add first party support for k256 and p256 under feature flags

Given the lack of vectors for k256, it's currently a match of the p256 
spec (with a distinct context string), yet p256 is still always used 
when testing.
This commit is contained in:
Luke Parker
2022-06-05 16:08:51 -04:00
parent 5313210526
commit 55a895d65a
8 changed files with 288 additions and 220 deletions

View File

@@ -0,0 +1,133 @@
use core::{marker::PhantomData, convert::TryInto};
use rand_core::{RngCore, CryptoRng};
use ff::{Field, PrimeField};
use group::{Group, GroupEncoding};
use sha2::{digest::Update, Digest, Sha256};
#[cfg(feature = "k256")]
use k256::elliptic_curve::bigint::{Encoding, U384};
#[cfg(all(not(feature = "k256"), any(test, feature = "p256")))]
use p256::elliptic_curve::bigint::{Encoding, U384};
use crate::{CurveError, Curve, curves::expand_message_xmd_sha256};
#[allow(non_snake_case)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct KP256<P: Group> {
_P: PhantomData<P>
}
pub(crate) trait KP256Instance<P> {
const CONTEXT: &'static [u8];
const ID: &'static [u8];
const GENERATOR: P;
}
#[cfg(any(test, feature = "p256"))]
pub type P256 = KP256<p256::ProjectivePoint>;
#[cfg(any(test, feature = "p256"))]
impl KP256Instance<p256::ProjectivePoint> for P256 {
const CONTEXT: &'static [u8] = b"FROST-P256-SHA256-v5";
const ID: &'static [u8] = b"P-256";
const GENERATOR: p256::ProjectivePoint = p256::ProjectivePoint::GENERATOR;
}
#[cfg(feature = "k256")]
pub type K256 = KP256<k256::ProjectivePoint>;
#[cfg(feature = "k256")]
impl KP256Instance<k256::ProjectivePoint> for K256 {
const CONTEXT: &'static [u8] = b"FROST-secp256k1-SHA256-v5";
const ID: &'static [u8] = b"secp256k1";
const GENERATOR: k256::ProjectivePoint = k256::ProjectivePoint::GENERATOR;
}
impl<P: Group + GroupEncoding> Curve for KP256<P> where
KP256<P>: KP256Instance<P>,
P::Scalar: PrimeField,
<P::Scalar as PrimeField>::Repr: From<[u8; 32]> + AsRef<[u8]>,
P::Repr: From<[u8; 33]> + AsRef<[u8]> {
type F = P::Scalar;
type G = P;
type T = P;
const ID: &'static [u8] = <Self as KP256Instance<P>>::ID;
const GENERATOR: Self::G = <Self as KP256Instance<P>>::GENERATOR;
const GENERATOR_TABLE: Self::G = <Self as KP256Instance<P>>::GENERATOR;
const LITTLE_ENDIAN: bool = false;
fn random_nonce<R: RngCore + CryptoRng>(secret: Self::F, rng: &mut R) -> Self::F {
let mut seed = vec![0; 32];
rng.fill_bytes(&mut seed);
seed.extend(secret.to_repr().as_ref());
Self::hash_to_F(&[Self::CONTEXT, b"nonce"].concat(), &seed)
}
fn hash_msg(msg: &[u8]) -> Vec<u8> {
(&Sha256::new()
.chain(Self::CONTEXT)
.chain(b"digest")
.chain(msg)
.finalize()
).to_vec()
}
fn hash_binding_factor(binding: &[u8]) -> Self::F {
Self::hash_to_F(&[Self::CONTEXT, b"rho"].concat(), binding)
}
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
let mut modulus = vec![0; 16];
modulus.extend((Self::F::zero() - Self::F::one()).to_repr().as_ref());
let modulus = U384::from_be_slice(&modulus).wrapping_add(&U384::ONE);
Self::F_from_slice(
&U384::from_be_slice(
&expand_message_xmd_sha256(dst, msg, 48).unwrap()
).reduce(&modulus).unwrap().to_be_bytes()[16 ..]
).unwrap()
}
fn F_len() -> usize {
32
}
fn G_len() -> usize {
33
}
fn F_from_slice(slice: &[u8]) -> Result<Self::F, CurveError> {
let bytes: [u8; 32] = slice.try_into()
.map_err(|_| CurveError::InvalidLength(32, slice.len()))?;
let scalar = Self::F::from_repr(bytes.into());
if scalar.is_none().into() {
Err(CurveError::InvalidScalar)?;
}
Ok(scalar.unwrap())
}
fn G_from_slice(slice: &[u8]) -> Result<Self::G, CurveError> {
let bytes: [u8; 33] = slice.try_into()
.map_err(|_| CurveError::InvalidLength(33, slice.len()))?;
let point = Self::G::from_bytes(&bytes.into());
if point.is_none().into() || point.unwrap().is_identity().into() {
Err(CurveError::InvalidPoint)?;
}
Ok(point.unwrap())
}
fn F_to_bytes(f: &Self::F) -> Vec<u8> {
f.to_repr().as_ref().to_vec()
}
fn G_to_bytes(g: &Self::G) -> Vec<u8> {
g.to_bytes().as_ref().to_vec()
}
}

View File

@@ -0,0 +1,48 @@
use sha2::{Digest, Sha256};
pub mod kp256;
// TODO: Actually make proper or replace with something from another crate
pub(crate) fn expand_message_xmd_sha256(dst: &[u8], msg: &[u8], len: u16) -> Option<Vec<u8>> {
const OUTPUT_SIZE: u16 = 32;
const BLOCK_SIZE: u16 = 64;
let blocks = ((len + OUTPUT_SIZE) - 1) / OUTPUT_SIZE;
if blocks > 255 {
return None;
}
let blocks = blocks as u8;
let mut dst = dst;
let oversize = Sha256::digest([b"H2C-OVERSIZE-DST-", dst].concat());
if dst.len() > 255 {
dst = &oversize;
}
let dst_prime = &[dst, &[dst.len() as u8]].concat();
let mut msg_prime = vec![0; BLOCK_SIZE.into()];
msg_prime.extend(msg);
msg_prime.extend(len.to_be_bytes());
msg_prime.push(0);
msg_prime.extend(dst_prime);
let mut b = vec![Sha256::digest(&msg_prime).to_vec()];
{
let mut b1 = b[0].clone();
b1.push(1);
b1.extend(dst_prime);
b.push(Sha256::digest(&b1).to_vec());
}
for i in 2 ..= blocks {
let mut msg = b[0]
.iter().zip(b[usize::from(i) - 1].iter())
.map(|(a, b)| *a ^ b).collect::<Vec<_>>();
msg.push(i);
msg.extend(dst_prime);
b.push(Sha256::digest(msg).to_vec());
}
Some(b[1 ..].concat()[.. usize::from(len)].to_vec())
}