Add domain restricted sign/verify to ed25519 interface, modeled after p384 version.

This commit is contained in:
Adam Ierymenko
2023-11-15 12:02:39 -05:00
parent 5d634d4714
commit 49cc32b79d
2 changed files with 60 additions and 12 deletions
+13 -10
View File
@@ -70,7 +70,7 @@ pub struct P384PublicKey {
unsafe impl Send for P384PublicKey {}
unsafe impl Sync for P384PublicKey {}
fn create_digest(domain: &[u8], data: &[&[u8]]) -> [u8; SHA384_HASH_SIZE] {
fn create_domain_restricted_digest(domain: &[u8], data: &[&[u8]]) -> [u8; SHA384_HASH_SIZE] {
debug_assert!(domain.len() <= u16::MAX as usize);
let mut hasher = SHA384::new();
for msg in data {
@@ -149,7 +149,7 @@ impl P384PublicKey {
mem::forget(r);
mem::forget(s);
// Digest the message.
let digest = create_digest(domain, data);
let digest = create_domain_restricted_digest(domain, data);
let key = self.key.lock().unwrap();
// Actually perform the verification.
@@ -301,7 +301,7 @@ impl P384KeyPair {
/// purpose the signer did not intend.
#[allow(unused_assignments)]
pub fn sign_all(&self, domain: &[u8], data: &[&[u8]]) -> [u8; P384_ECDSA_SIGNATURE_SIZE] {
let digest = create_digest(domain, data);
let digest = create_domain_restricted_digest(domain, data);
unsafe {
let keypair = self.pair.lock().unwrap();
// Actually create the signature with ECDSA.
@@ -355,13 +355,16 @@ impl P384KeyPair {
let other_key = other_public.key.lock().unwrap();
unsafe {
// Ask OpenSSL to perform DH between the keypair and the other key's public key object.
assert_eq!(ECDH_compute_key(
output.as_mut_ptr(),
P384_ECDH_SHARED_SECRET_SIZE as c_ulong,
ffi::EC_KEY_get0_public_key(other_key.0),
keypair.0,
ptr::null(),
), P384_ECDH_SHARED_SECRET_SIZE as c_int)
assert_eq!(
ECDH_compute_key(
output.as_mut_ptr(),
P384_ECDH_SHARED_SECRET_SIZE as c_ulong,
ffi::EC_KEY_get0_public_key(other_key.0),
keypair.0,
ptr::null(),
),
P384_ECDH_SHARED_SECRET_SIZE as c_int
)
}
}
}
+47 -2
View File
@@ -19,6 +19,18 @@ pub const ED25519_PUBLIC_KEY_SIZE: usize = 32;
pub const ED25519_SECRET_KEY_SIZE: usize = 32;
pub const ED25519_SIGNATURE_SIZE: usize = 64;
fn create_domain_restricted_digest(domain: &[u8], data: &[u8]) -> ed25519_dalek::Sha512 {
debug_assert!(domain.len() <= u16::MAX as usize);
let mut hasher = ed25519_dalek::Sha512::new();
hasher.update(data);
// We hash the domain last to mitigate some of the weaknesses of merkle-damgard.
if domain.len() > 0 {
hasher.update(domain);
hasher.update(&(domain.len() as u16).to_be_bytes());
}
hasher
}
/// Curve25519 key pair for ECDH key agreement.
#[derive(Clone)]
pub struct X25519KeyPair(x25519_dalek::StaticSecret, x25519_dalek::PublicKey);
@@ -115,14 +127,23 @@ impl Ed25519KeyPair {
*output = self.0.to_bytes();
}
/// Sign a hash of the message (no domain parameter).
pub fn sign(&self, msg: &[u8]) -> [u8; ED25519_SIGNATURE_SIZE] {
let mut h = ed25519_dalek::Sha512::new();
let _ = h.update(msg);
self.0.sign_prehashed(h.clone(), None).unwrap().to_bytes()
self.0.sign_prehashed(h, None).unwrap().to_bytes()
}
/// Sign a hash of the message that includes a domain parameter.
pub fn sign_domain_restricted(&self, domain: &[u8], msg: &[u8]) -> [u8; ED25519_SIGNATURE_SIZE] {
self.0
.sign_prehashed(create_domain_restricted_digest(domain, msg), None)
.unwrap()
.to_bytes()
}
/// Create a signature with the first 32 bytes of the SHA512 hash appended.
/// ZeroTier does this for legacy reasons, but it's ignored in newer versions.
/// This is for legacy ZeroTier V1 compatibility requirements. It's not used in newer code.
pub fn sign_zt(&self, msg: &[u8]) -> [u8; 96] {
let mut h = ed25519_dalek::Sha512::new();
let _ = h.update(msg);
@@ -136,6 +157,7 @@ impl Ed25519KeyPair {
}
}
/// Verify a non-domain-restricted signature.
#[must_use]
pub fn ed25519_verify(public_key: &[u8; ED25519_PUBLIC_KEY_SIZE], signature: &[u8], msg: &[u8]) -> bool {
if signature.len() >= 64 {
@@ -150,3 +172,26 @@ pub fn ed25519_verify(public_key: &[u8; ED25519_PUBLIC_KEY_SIZE], signature: &[u
false
}
}
/// Verify a domain restricted signature.
#[must_use]
pub fn ed25519_verify_domain_restricted(
public_key: &[u8; ED25519_PUBLIC_KEY_SIZE],
signature: &[u8],
domain: &[u8],
msg: &[u8],
) -> bool {
if signature.len() >= 64 {
ed25519_dalek::VerifyingKey::from_bytes(public_key.try_into().unwrap()).map_or(false, |pk| {
let sig: [u8; 64] = signature[0..64].try_into().unwrap();
pk.verify_prehashed(
create_domain_restricted_digest(domain, msg),
None,
&ed25519_dalek::Signature::from(sig),
)
.is_ok()
})
} else {
false
}
}