// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright by contributors to this project. // SPDX-License-Identifier: (Apache-2.0 OR MIT) // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 use nss_gk_api::{PrivateKey, PublicKey}; use alloc::vec::Vec; use mls_rs_crypto_traits::Curve; #[cfg(feature = "std")] use std::array::TryFromSliceError; #[cfg(not(feature = "std"))] use core::array::TryFromSliceError; use core::fmt::{self, Debug}; use crate::Hash; #[derive(Debug, Clone)] pub enum EcPublicKey { X25519(nss_gk_api::PublicKey), Ed25519(nss_gk_api::PublicKey), P256(nss_gk_api::PublicKey), } #[derive(Clone)] pub enum EcPrivateKey { X25519(nss_gk_api::PrivateKey), Ed25519(nss_gk_api::PrivateKey), P256(nss_gk_api::PrivateKey), } #[derive(Debug)] #[cfg_attr(feature = "std", derive(thiserror::Error))] pub enum EcError { #[cfg_attr(feature = "std", error("unsupported curve type"))] UnsupportedCurve, #[cfg_attr(feature = "std", error("invalid public key data"))] EcKeyInvalidKeyData, #[cfg_attr(feature = "std", error("ec key is not a signature key"))] EcKeyNotSignature, #[cfg_attr(feature = "std", error(transparent))] TryFromSliceError(TryFromSliceError), #[cfg_attr(feature = "std", error("rand error: {0:?}"))] RandCoreError(rand_core::Error), #[cfg_attr(feature = "std", error("ecdh key type mismatch"))] EcdhKeyTypeMismatch, #[cfg_attr(feature = "std", error("ec key is not an ecdh key"))] EcKeyNotEcdh, #[cfg_attr(feature = "std", error("general nss failure"))] GeneralFailure, } // Constants pub const DER_SEQUENCE: u8 = 0x30; pub const DER_INTEGER: u8 = 0x02; pub const DER_BITSTRING: u8 = 0x03; pub const DER_OCTETSTRING: u8 = 0x04; impl From for EcError { fn from(value: rand_core::Error) -> Self { EcError::RandCoreError(value) } } impl From for EcError { fn from(e: TryFromSliceError) -> Self { EcError::TryFromSliceError(e) } } impl core::fmt::Debug for EcPrivateKey { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::X25519(_) => f.write_str("X25519 Secret Key"), Self::Ed25519(_) => f.write_str("Ed25519 Secret Key"), Self::P256(_) => f.write_str("P256 Secret Key"), } } } fn private_key_len(curve: Curve) -> usize { match curve { Curve::P256 => 0x20 as usize, Curve::Ed25519 | Curve::X25519 => 0x20 as usize, _ => 0 as usize, } } fn public_key_len(curve: Curve) -> usize { match curve { Curve::P256 => 0x41 as usize, Curve::Ed25519 | Curve::X25519 => 32 as usize, _ => 0 as usize, } } fn max_size_ecdsa_part(curve: Curve) -> Result { // Currently supporting only ECDSA_P256 match curve { Curve::P256 => return Ok(0x20), _ => return Err(EcError::EcKeyInvalidKeyData), } } fn build_spki_from_raw_public_key(key: Vec, curve: Curve) -> Result, EcError> { let mut lh = { match curve { Curve::P256 => vec![ DER_SEQUENCE, 0x59, // length DER_SEQUENCE, 0x13, // length of the curve ID 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, // curve identifier 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, DER_BITSTRING, 0x42, // length 0x00, ], Curve::Ed25519 => vec![ DER_SEQUENCE, 0x2a, //length DER_SEQUENCE, 0x5, // length of the curve ID 0x6, 0x3, 0x2b, 0x65, 0x70, // curve identifier DER_BITSTRING, 0x21, 0x0, ], Curve::X25519 => vec![ DER_SEQUENCE, 0x2a, // length of the signature DER_SEQUENCE, 0x5, // length of the curve ID 0x6, 0x3, 0x2b, 0x65, 0x6e, // curve identifier DER_BITSTRING, 0x21, // length 0x0, ], _ => return Err(EcError::UnsupportedCurve), } }; let mut key = key.clone(); // Not supported if public_key_len(curve) == 0 { return Err(EcError::EcKeyInvalidKeyData); } // if the key length is bigger than the expected -> wrong key if key.len() > public_key_len(curve) { // checking if the first two bytes are the encoding if key[0] != DER_OCTETSTRING && key[1] as usize != public_key_len(curve) && key.len() != public_key_len(curve) + 2 { return Err(EcError::EcKeyInvalidKeyData); } else { let (_, key) = key.split_at(2); return build_spki_from_raw_public_key(key.to_vec(), curve); } } // if the key length is shorted than expected -> extend the key if key.len() < public_key_len(curve) { let mut zeros = vec![0 as u8; public_key_len(curve) - key.len()]; zeros.append(&mut key); lh.append(&mut zeros); } else { lh.append(&mut key); } Ok(lh) } pub fn pub_key_from_uncompressed(bytes: Vec, curve: Curve) -> Result { let z = build_spki_from_raw_public_key(bytes, curve).unwrap(); match curve { Curve::P256 => match nss_gk_api::ec::import_ec_public_key_from_spki(&z) { Ok(key) => return Ok(EcPublicKey::P256(key)), Err(_) => return Err(EcError::EcKeyInvalidKeyData), }, Curve::Ed25519 => match nss_gk_api::ec::import_ec_public_key_from_spki(&z) { Ok(key) => return Ok(EcPublicKey::Ed25519(key)), Err(_) => return Err(EcError::EcKeyInvalidKeyData), }, Curve::X25519 => match nss_gk_api::ec::import_ec_public_key_from_spki(&z) { Ok(key) => return Ok(EcPublicKey::X25519(key)), Err(_) => return Err(EcError::EcKeyInvalidKeyData), }, _ => Err(EcError::UnsupportedCurve), } } pub fn pub_key_to_uncompressed(key: EcPublicKey) -> Result, EcError> { match key { EcPublicKey::Ed25519(key) | EcPublicKey::X25519(key) => { let k0 = key.key_data_alt().unwrap(); Ok(k0.to_vec()) } EcPublicKey::P256(key) => { let k0 = key.key_data_alt().unwrap(); if k0.len() == public_key_len(Curve::P256) { return Ok(k0.to_vec()); }; // The key is encoded if k0[0] != DER_OCTETSTRING { return Err(EcError::EcKeyInvalidKeyData); } if k0[1] as usize != public_key_len(Curve::P256) { return Err(EcError::EcKeyInvalidKeyData); } let (_, key) = k0.split_at(2); Ok(key.to_vec()) } } } pub fn generate_private_key(curve: Curve) -> Result { let key_pair = nss_gk_api::ec::ecdh_keygen(nss_gk_api::ec::EcCurve::P256).unwrap(); match curve { Curve::P256 => return Ok(EcPrivateKey::P256(key_pair.private)), Curve::X25519 => return Ok(EcPrivateKey::X25519(key_pair.private)), Curve::Ed25519 => return Ok(EcPrivateKey::Ed25519(key_pair.private)), _ => Err(EcError::UnsupportedCurve), } } // I think that instead of raw bytes, we should use pkcs8/spki #[allow(dead_code)] pub fn private_key_from_pkcs8(bytes: &[u8], curve: Curve) -> Result { let private_key = nss_gk_api::ec::import_ec_private_key_pkcs8(bytes).unwrap(); match curve { Curve::P256 => return Ok(EcPrivateKey::P256(private_key)), Curve::Ed25519 => return Ok(EcPrivateKey::Ed25519(private_key)), Curve::X25519 => return Ok(EcPrivateKey::X25519(private_key)), _ => Err(EcError::UnsupportedCurve), } } #[allow(dead_code)] pub fn private_key_to_pkcs8(key: EcPrivateKey) -> Result, EcError> { match key { EcPrivateKey::P256(key) | EcPrivateKey::Ed25519(key) | EcPrivateKey::X25519(key) => { match nss_gk_api::ec::export_ec_private_key_pkcs8(key) { Ok(key) => return Ok(key), Err(_) => return Err(EcError::EcKeyInvalidKeyData), } } } } fn build_pkcs8_from_raw_private_key(key: Vec, curve: Curve) -> Result, EcError> { let mut lh = { match curve { Curve::P256 => vec![ DER_SEQUENCE, 0x41, // length DER_INTEGER, 0x1, 0x0, // Key type DER_SEQUENCE, 0x13, 0x6, 0x7, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x2, 0x1, 0x6, 0x8, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x3, 0x1, 0x7, // Curve // See P256 encoding 0x4, 0x27, 0x30, 0x25, 0x2, 0x1, 0x1, 0x4, 0x20, ], Curve::Ed25519 => vec![ DER_SEQUENCE, 0x2e, DER_INTEGER, 0x01, 0x00, // Key type DER_SEQUENCE, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, //Curve DER_OCTETSTRING, 0x22, DER_OCTETSTRING, 0x20, ], Curve::X25519 => vec![ DER_SEQUENCE, 0x2e, DER_INTEGER, 0x01, 0x00, // Key type DER_SEQUENCE, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, // Curve DER_OCTETSTRING, 0x22, DER_OCTETSTRING, 0x20, ], _ => return Err(EcError::UnsupportedCurve), } }; let mut key = key.clone(); if private_key_len(curve) == 0 { return Err(EcError::EcKeyInvalidKeyData); } // if the key length is bigger than the expected -> wrong key if key.len() > private_key_len(curve) { return Err(EcError::EcKeyInvalidKeyData); } // if the key length is shorted than expected -> extend the key if key.len() < private_key_len(curve) { let mut zeros = vec![0 as u8; private_key_len(curve) - key.len()]; zeros.append(&mut key); lh.append(&mut zeros); } else { lh.append(&mut key); } Ok(lh) } fn private_key_from_bytes_helper(bytes: Vec, curve: Curve) -> Vec { if is_secret_key_contains_public_key(bytes.clone(), curve) { let (test, _) = bytes.split_at(private_key_len(curve)); return test.to_vec(); } return bytes; } pub fn private_key_from_bytes(bytes: Vec, curve: Curve) -> Result { let private_key = private_key_from_bytes_helper(bytes, curve); let private_key_pkcs8 = build_pkcs8_from_raw_private_key(private_key, curve).unwrap(); let private_key_imported = nss_gk_api::ec::import_ec_private_key_pkcs8(&private_key_pkcs8).unwrap(); match curve { Curve::P256 => Ok(EcPrivateKey::P256(private_key_imported)), Curve::Ed25519 => Ok(EcPrivateKey::Ed25519(private_key_imported)), Curve::X25519 => Ok(EcPrivateKey::X25519(private_key_imported)), _ => Err(EcError::UnsupportedCurve), } } pub fn private_key_to_bytes(key: EcPrivateKey) -> Result, EcError> { match key { EcPrivateKey::Ed25519(key) | EcPrivateKey::P256(key) | EcPrivateKey::X25519(key) => { Ok(key.key_data().unwrap()) } } } pub fn private_key_to_public(private_key: &EcPrivateKey) -> Result { match private_key { EcPrivateKey::X25519(key) => Ok(EcPublicKey::X25519( nss_gk_api::ec::convert_to_public(key.clone()).unwrap(), )), EcPrivateKey::Ed25519(key) => Ok(EcPublicKey::Ed25519( nss_gk_api::ec::convert_to_public(key.clone()).unwrap(), )), EcPrivateKey::P256(key) => Ok(EcPublicKey::P256( nss_gk_api::ec::convert_to_public(key.clone()).unwrap(), )), } } pub fn private_key_ecdh( private_key: &EcPrivateKey, remote_public: &EcPublicKey, ) -> Result, EcError> { let shared_secret = match private_key { EcPrivateKey::X25519(private_key) => match remote_public { EcPublicKey::X25519(public) => { let r = nss_gk_api::ec::ecdh(private_key.clone(), public.clone()).unwrap(); Ok(r) } _ => Err(EcError::EcdhKeyTypeMismatch), }, EcPrivateKey::Ed25519(_) => Err(EcError::EcKeyNotEcdh), EcPrivateKey::P256(private_key) => match remote_public { EcPublicKey::P256(public) => { let r = nss_gk_api::ec::ecdh(private_key.clone(), public.clone()).unwrap(); Ok(r) } _ => Err(EcError::EcdhKeyTypeMismatch), }, }?; Ok(shared_secret) } pub fn sign_p256(private_key: PrivateKey, data: &[u8]) -> Result, EcError> { let mut hashed_data = Hash::hash(&Hash::Sha256, data); let signature = nss_gk_api::ec::sign_ecdsa(private_key, hashed_data.as_mut()).unwrap(); Ok(signature) } // Removes not needed zeros/ expands the signature until the required length if required // This function could be easily extended to P384/etc if changed the input to also a curve // And providing this curve to the each length functions fn format_ecdsa_p256(buffer: &[u8]) -> Result, EcError> { if buffer.len() > max_size_ecdsa_part(Curve::P256).unwrap() + 1 { return Err(EcError::EcKeyInvalidKeyData); } // Correct size, no modification needed if buffer.len() == max_size_ecdsa_part(Curve::P256).unwrap() { return Ok(buffer.to_vec()); } // It's possible that the buffer was padded (for one byte) with 0 // In this case the first byte should be equal to 0 if buffer.len() == max_size_ecdsa_part(Curve::P256).unwrap() + 1 { if buffer[0] != 0x00 { return Err(EcError::EcKeyInvalidKeyData); } if buffer[1] < 0b1000000 { return Err(EcError::EcKeyInvalidKeyData); } // Removing the zero if found let (_, rest) = buffer.split_at(1); return Ok(rest.to_vec()); } // If the signature is shorter, we extend it with 0 until get_max_size_ecdsa_part let mut buffer = buffer.to_vec(); let mut zeros = vec![0 as u8; max_size_ecdsa_part(Curve::P256).unwrap() - buffer.len()]; zeros.append(&mut buffer); Ok(zeros) } // ECDSA signature is packed the following way // Signature ::= SEQUENCE { // r INTEGER, // s INTEGER, // } fn parse_ecdsa_p256(signature: &[u8]) -> Result, EcError> { let signature_vec = signature.to_vec(); if signature[0] != DER_SEQUENCE { // Not a correct encoding return Err(EcError::EcKeyInvalidKeyData); } // the first byte is the length of the rest of the buffer let len_buffer = signature[1]; if (len_buffer + 2) as usize != signature.len() { // Wrong length return Err(EcError::EcKeyInvalidKeyData); } if signature[2] != DER_INTEGER { // Both R and S are integers return Err(EcError::EcKeyInvalidKeyData); } let len_r = signature[3]; // R could be padded with 0x0 at the start if the most significant // bit of the signature is 1. if len_r as usize > max_size_ecdsa_part(Curve::P256).unwrap() + 1 { return Err(EcError::EcKeyInvalidKeyData); } // Dividing the signature into introduction (Sequence; Len; Integer; Len) // and the rest // Intro is already checked for correctness let (_, rs) = signature_vec.split_at(4); // Reading len_r bytes of signature // It will divide the buffer into R and the rest let skip_until_r = 3; let (r, intro_s) = rs.split_at(len_r as usize); // The first byte after R is the type identifier of the next element // For ecdsa, it should be integer if signature[(skip_until_r + len_r + 1) as usize] != DER_INTEGER { return Err(EcError::EcKeyInvalidKeyData); } // The next byte is the length let len_s = signature_vec[(skip_until_r + len_r + 2) as usize]; if len_s as usize > max_size_ecdsa_part(Curve::P256).unwrap() + 1 { return Err(EcError::EcKeyInvalidKeyData); } // Now the intro_s buffer consists of Type identifier and its length (2) // Intro buffer is already checked // The rest is the S part of the signature let (_, s) = intro_s.split_at(2); // Formatting R and S (removing padded zeros, extending the size if necessary) let mut r = format_ecdsa_p256(r).unwrap(); let s = format_ecdsa_p256(s).unwrap(); // Concatenation of R and S r.extend(s); Ok(r) } pub fn verify_p256_( public_key: PublicKey, signature: Vec, data: &[u8], ) -> Result { let mut hashed_data = Hash::hash(&Hash::Sha256, data); let result = nss_gk_api::ec::verify_ecdsa(public_key, hashed_data.as_mut(), &signature).unwrap(); Ok(result) } pub fn verify_p256(public_key: PublicKey, signature: &[u8], data: &[u8]) -> Result { if signature.len() != max_size_ecdsa_part(Curve::P256).unwrap() * 2 { let signature = parse_ecdsa_p256(signature).unwrap(); return verify_p256_(public_key, signature, data); } return verify_p256_(public_key, signature.to_vec(), data); } pub fn sign_ed25519(private_key: PrivateKey, data: &[u8]) -> Result, EcError> { let signature = nss_gk_api::ec::sign_eddsa(private_key, &data).unwrap(); Ok(signature) } #[allow(dead_code)] fn encode_ecdsa_p256(signature: Vec) -> Result, EcError> { if signature.len() != max_size_ecdsa_part(Curve::P256).unwrap() * 2 { return Err(EcError::EcKeyInvalidKeyData); } let (r, s) = signature.split_at(max_size_ecdsa_part(Curve::P256).unwrap()); let mut signature = vec![DER_SEQUENCE]; let r_len = max_size_ecdsa_part(Curve::P256).unwrap() + { if r[0] < 0b10000000 { 0 } else { 1 } }; let s_len = max_size_ecdsa_part(Curve::P256).unwrap() + { if s[0] < 0b10000000 { 0 } else { 1 } }; signature.push((4 + r_len + s_len) as u8); signature.push(DER_INTEGER); signature.push(r_len as u8); if r[0] >= 0b10000000 { signature.push(0); } signature.append(&mut r.to_vec()); signature.push(DER_INTEGER); signature.push(s_len as u8); if s[0] >= 0b10000000 { signature.push(0); } signature.append(&mut s.to_vec()); Ok(signature) } pub fn verify_ed25519( public_key: PublicKey, signature: &[u8], data: &[u8], ) -> Result { let result = nss_gk_api::ec::verify_eddsa(public_key, &data, signature).unwrap(); Ok(result) } pub fn generate_keypair(curve: Curve) -> Result { match curve { Curve::P256 => { let key = nss_gk_api::ec::ecdh_keygen(nss_gk_api::ec::EcCurve::P256).unwrap(); let secret: Vec = private_key_to_bytes(EcPrivateKey::P256(key.private))?; let public: Vec = pub_key_to_uncompressed(EcPublicKey::P256(key.public))?; return Ok(KeyPair { public, secret }); } Curve::Ed25519 => { let key = nss_gk_api::ec::ecdh_keygen(nss_gk_api::ec::EcCurve::Ed25519).unwrap(); let secret: Vec = private_key_to_bytes(EcPrivateKey::Ed25519(key.private))?; let public: Vec = pub_key_to_uncompressed(EcPublicKey::Ed25519(key.public))?; return Ok(KeyPair { public, secret }); } Curve::X25519 => { let key = nss_gk_api::ec::ecdh_keygen(nss_gk_api::ec::EcCurve::X25519).unwrap(); let secret: Vec = private_key_to_bytes(EcPrivateKey::X25519(key.private))?; let public: Vec = pub_key_to_uncompressed(EcPublicKey::X25519(key.public))?; return Ok(KeyPair { public, secret }); } _ => { let secret = generate_private_key(curve)?; let public = private_key_to_public(&secret)?; let secret = private_key_to_bytes(secret)?; let public = pub_key_to_uncompressed(public)?; Ok(KeyPair { public, secret }) } } } #[derive(Clone, Default)] pub struct KeyPair { pub public: Vec, pub secret: Vec, } impl Debug for KeyPair { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("KeyPair") .field("public", &mls_rs_core::debug::pretty_bytes(&self.public)) .field("secret", &mls_rs_core::debug::pretty_bytes(&self.secret)) .finish() } } fn is_secret_key_contains_public_key(secret_key: Vec, curve: Curve) -> bool { let private_key_len = private_key_len(curve); let public_key_len = public_key_len(curve); if secret_key.len() == private_key_len + public_key_len { return true; } return false; } pub fn private_key_bytes_to_public(secret_key: Vec, curve: Curve) -> Result, EcError> { if !is_secret_key_contains_public_key(secret_key.clone(), curve) { let secret_key = private_key_from_bytes(secret_key.clone(), curve)?; let public_key = private_key_to_public(&secret_key)?; pub_key_to_uncompressed(public_key) } else { let (_, public_key) = secret_key.split_at(private_key_len(curve)); Ok(public_key.to_vec()) } } #[cfg(test)] pub(crate) mod test_utils { use serde::Deserialize; use super::Curve; use alloc::vec::Vec; #[derive(Deserialize)] pub(crate) struct TestKeys { #[serde(with = "hex::serde")] p256: Vec, #[serde(with = "hex::serde")] x25519: Vec, #[serde(with = "hex::serde")] ed25519: Vec, } impl TestKeys { pub(crate) fn get_key_from_curve(&self, curve: Curve) -> Vec { match curve { Curve::P256 => self.p256.clone(), Curve::X25519 => self.x25519.clone(), Curve::Ed25519 => self.ed25519.clone(), _ => Vec::new(), } } } pub(crate) fn get_test_public_keys() -> TestKeys { let test_case_file = include_str!("../test_data/test_public_keys.json"); serde_json::from_str(test_case_file).unwrap() } pub(crate) fn get_test_secret_keys() -> TestKeys { let test_case_file = include_str!("../test_data/test_private_keys.json"); serde_json::from_str(test_case_file).unwrap() } pub fn is_curve_25519(curve: Curve) -> bool { curve == Curve::X25519 || curve == Curve::Ed25519 } #[allow(dead_code)] pub fn byte_equal(curve: Curve, other: Curve) -> bool { if curve == other { return true; } if is_curve_25519(curve) && is_curve_25519(other) { return true; } false } } #[cfg(test)] mod tests { use assert_matches::assert_matches; use super::{ generate_keypair, generate_private_key, private_key_bytes_to_public, private_key_from_bytes, private_key_from_pkcs8, private_key_to_bytes, private_key_to_pkcs8, pub_key_from_uncompressed, pub_key_to_uncompressed, sign_ed25519, sign_p256, test_utils::{get_test_public_keys, get_test_secret_keys}, verify_ed25519, verify_p256, Curve, EcPublicKey, }; const SUPPORTED_CURVES: [Curve; 3] = [Curve::Ed25519, Curve::P256, Curve::X25519]; #[test] fn private_key_can_be_generated() { SUPPORTED_CURVES.iter().copied().for_each(|curve| { let one_key = generate_private_key(curve) .unwrap_or_else(|e| panic!("Failed to generate private key for {curve:?} : {e:?}")); let another_key = generate_private_key(curve) .unwrap_or_else(|e| panic!("Failed to generate private key for {curve:?} : {e:?}")); assert_ne!( private_key_to_bytes(one_key).unwrap(), private_key_to_bytes(another_key).unwrap(), "Same key generated twice for {curve:?}" ); }); } #[test] fn key_pair_can_be_generated() { SUPPORTED_CURVES.iter().copied().for_each(|curve| { assert_matches!( generate_keypair(curve), Ok(_), "Failed to generate key pair for {curve:?}" ); }); } #[test] fn private_key_can_be_imported_and_exported() { SUPPORTED_CURVES.iter().copied().for_each(|curve| { let key_bytes = get_test_secret_keys().get_key_from_curve(curve); let imported_key = private_key_from_bytes(key_bytes.clone(), curve) .unwrap_or_else(|e| panic!("Failed to import private key for {curve:?} : {e:?}")); let exported_bytes = private_key_to_bytes(imported_key) .unwrap_or_else(|e| panic!("Failed to export private key for {curve:?} : {e:?}")); assert_eq!(exported_bytes, key_bytes); }); } #[test] fn private_key_pkcs8_can_be_imported_and_exported() { SUPPORTED_CURVES.iter().copied().for_each(|curve| { let key = generate_private_key(curve) .unwrap_or_else(|e| panic!("Failed to generate private key for {curve:?} : {e:?}")); let exported_bytes = private_key_to_pkcs8(key) .unwrap_or_else(|e| panic!("Failed to export private key for {curve:?} : {e:?}")); let imported_key = private_key_from_pkcs8(&exported_bytes, curve) .unwrap_or_else(|e| panic!("Failed to import private key for {curve:?} : {e:?}")); let exported_bytes_2 = private_key_to_pkcs8(imported_key) .unwrap_or_else(|e| panic!("Failed to export private key for {curve:?} : {e:?}")); assert_eq!(exported_bytes_2, exported_bytes); }); } #[test] fn public_key_can_be_imported_and_exported() { SUPPORTED_CURVES.iter().copied().for_each(|curve| { let key_bytes = get_test_public_keys().get_key_from_curve(curve); let imported_key = pub_key_from_uncompressed(key_bytes.clone(), curve) .unwrap_or_else(|e| panic!("Failed to import public key for {curve:?} : {e:?}")); let exported_bytes = pub_key_to_uncompressed(imported_key) .unwrap_or_else(|e| panic!("Failed to export public key for {curve:?} : {e:?}")); assert_eq!(exported_bytes, key_bytes); }); } #[test] fn secret_to_public() { let test_public_keys = get_test_public_keys(); let test_secret_keys = get_test_secret_keys(); for curve in SUPPORTED_CURVES.iter().copied() { let secret_key = test_secret_keys.get_key_from_curve(curve); let public_key = private_key_bytes_to_public(secret_key, curve).unwrap(); let expected_public_key = test_public_keys.get_key_from_curve(curve); assert_eq!(public_key, expected_public_key); } } // #[test] // fn mismatched_curve_import() { // for curve in SUPPORTED_CURVES.iter().copied() { // for other_curve in SUPPORTED_CURVES // .iter() // .copied() // .filter(|c| !byte_equal(*c, curve)) // { // let public_key = get_test_public_keys().get_key_from_curve(curve); // let res = pub_key_from_uncompressed(public_key, other_curve); // assert!(res.is_err()); // } // } // } // TODO: discuss if we need this test // TODO: if yes, we need to introduce secure cmp // #[test] // fn test_order_range_enforcement() { // let p256_order = // hex::decode("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551") // .unwrap(); // // Keys must be <= to order // let p256_res = private_key_from_bytes(p256_order, Curve::P256); // assert_matches!(p256_res, Err(EcError::EcKeyInvalidKeyData)); // let nist_curves = [Curve::P256]; // // Keys must not be 0 // for curve in nist_curves { // assert_matches!( // private_key_from_bytes(vec![0u8; curve.secret_key_size()], curve), // Err(EcError::EcKeyInvalidKeyData) // ); // } // } use serde::Deserialize; #[derive(Deserialize)] struct TestCaseSignature { pub algorithm: String, #[serde(with = "hex::serde")] pub private_key: Vec, #[serde(with = "hex::serde")] pub public_key: Vec, #[serde(with = "hex::serde")] pub hash: Vec, #[serde(with = "hex::serde")] pub signature: Vec, } fn test_sign_p256(private_key: Vec, public_key: Vec, data: Vec) { let curve = Curve::P256; let private_key = private_key_from_bytes(private_key, curve).unwrap(); let public_key = pub_key_from_uncompressed(public_key, curve).unwrap(); match private_key { super::EcPrivateKey::P256(private_key) => { let signature = sign_p256(private_key, &data).unwrap(); match public_key { EcPublicKey::P256(public_key) => { let verify = verify_p256(public_key, &signature, &data).unwrap(); assert_eq!(verify, true) } _ => assert!(false), } } _ => assert!(false), } } fn test_sign_ed25519( private_key: Vec, public_key: Vec, data: Vec, expected_signature: Vec, ) { let curve = Curve::Ed25519; let private_key = private_key_from_bytes(private_key, curve).unwrap(); let public_key = pub_key_from_uncompressed(public_key, curve).unwrap(); match private_key { super::EcPrivateKey::Ed25519(private_key) => { let signature = sign_ed25519(private_key, &data).unwrap(); assert_eq!(signature, expected_signature); match public_key { EcPublicKey::Ed25519(public_key) => { let verify = verify_ed25519(public_key, &signature, &data).unwrap(); assert_eq!(verify, true) } _ => assert!(false), } } _ => assert!(false), } } #[test] fn test_ecdsa_eddsa() { let test_case_file = include_str!("../test_data/test_ecdsa_eddsa.json"); let test_cases: Vec = serde_json::from_str(test_case_file).unwrap(); for case in test_cases { match case.algorithm.as_str() { "ECDSA" => test_sign_p256(case.private_key, case.public_key, case.hash), "EDDSA" => { test_sign_ed25519(case.private_key, case.public_key, case.hash, case.signature) } _ => assert!(false), } } } }