mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-03 20:13:48 +00:00
ed: Expose hazmat::raw_sign_byupdate() for streamed signing (#765)
* Added raw_sign_byupdate to hazmat; refactored other funcs to use that directly
This commit is contained in:
parent
6dc7a1c7c5
commit
08d7176d58
2 changed files with 101 additions and 5 deletions
|
|
@ -106,7 +106,7 @@ impl TryFrom<&[u8]> for ExpandedSecretKey {
|
|||
/// calculate the pseudorandomness needed for signing. According to the Ed25519 spec, `CtxDigest =
|
||||
/// Sha512`.
|
||||
///
|
||||
/// # ⚠️ Unsafe
|
||||
/// # ⚠️ Cryptographically Unsafe
|
||||
///
|
||||
/// Do NOT use this function unless you absolutely must. Using the wrong values in
|
||||
/// `ExpandedSecretKey` can leak your signing key. See
|
||||
|
|
@ -127,7 +127,7 @@ where
|
|||
/// `CtxDigest` is the digest function used to calculate the pseudorandomness needed for signing.
|
||||
/// According to the Ed25519 spec, `MsgDigest = CtxDigest = Sha512`.
|
||||
///
|
||||
/// # ⚠️ Unsafe
|
||||
/// # ⚠️ Cryptographically Unsafe
|
||||
//
|
||||
/// Do NOT use this function unless you absolutely must. Using the wrong values in
|
||||
/// `ExpandedSecretKey` can leak your signing key. See
|
||||
|
|
@ -169,6 +169,33 @@ where
|
|||
esk.raw_sign_prehashed::<CtxDigest, MsgDigest>(prehashed_message, verifying_key, context)
|
||||
}
|
||||
|
||||
/// Compute an ordinary Ed25519 signature, with the message contents provided incrementally by
|
||||
/// updating a digest instance.
|
||||
///
|
||||
/// The `msg_update` closure provides the message content, updating a hasher argument. It will be
|
||||
/// called twice. This closure MUST leave its hasher in the same state (i.e., must hash the same
|
||||
/// values) after both calls. Otherwise it will produce an invalid signature.
|
||||
///
|
||||
/// `CtxDigest` is the digest used to calculate the pseudorandomness needed for signing. According
|
||||
/// to the Ed25519 spec, `CtxDigest = Sha512`.
|
||||
///
|
||||
/// # ⚠️ Cryptographically Unsafe
|
||||
///
|
||||
/// Do NOT use this function unless you absolutely must. Using the wrong values in
|
||||
/// `ExpandedSecretKey` can leak your signing key. See
|
||||
/// [here](https://github.com/MystenLabs/ed25519-unsafe-libs) for more details on this attack.
|
||||
pub fn raw_sign_byupdate<CtxDigest, F>(
|
||||
esk: &ExpandedSecretKey,
|
||||
msg_update: F,
|
||||
verifying_key: &VerifyingKey,
|
||||
) -> Result<Signature, SignatureError>
|
||||
where
|
||||
CtxDigest: Digest<OutputSize = U64>,
|
||||
F: Fn(&mut CtxDigest) -> Result<(), SignatureError>,
|
||||
{
|
||||
esk.raw_sign_byupdate::<CtxDigest, F>(msg_update, verifying_key)
|
||||
}
|
||||
|
||||
/// The ordinary non-batched Ed25519 verification check, rejecting non-canonical R
|
||||
/// values.`CtxDigest` is the digest used to calculate the pseudorandomness needed for signing.
|
||||
/// According to the Ed25519 spec, `CtxDigest = Sha512`.
|
||||
|
|
@ -263,4 +290,47 @@ mod test {
|
|||
.unwrap();
|
||||
raw_verify_prehashed::<CtxDigest, MsgDigest>(&vk, h, Some(ctx_str), &sig).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_byupdate() {
|
||||
// Generate the keypair
|
||||
let mut rng = OsRng;
|
||||
let esk = ExpandedSecretKey::random(&mut rng);
|
||||
let vk = VerifyingKey::from(&esk);
|
||||
|
||||
let msg = b"realistic";
|
||||
// signatures are deterministic so we can compare with a good one
|
||||
let good_sig = raw_sign::<CtxDigest>(&esk, msg, &vk);
|
||||
|
||||
let sig = raw_sign_byupdate::<CtxDigest, _>(
|
||||
&esk,
|
||||
|h| {
|
||||
h.update(msg);
|
||||
Ok(())
|
||||
},
|
||||
&vk,
|
||||
);
|
||||
assert!(sig.unwrap() == good_sig, "sign byupdate matches");
|
||||
|
||||
let sig = raw_sign_byupdate::<CtxDigest, _>(
|
||||
&esk,
|
||||
|h| {
|
||||
h.update(msg);
|
||||
Err(SignatureError::new())
|
||||
},
|
||||
&vk,
|
||||
);
|
||||
assert!(sig.is_err(), "sign byupdate failure propagates");
|
||||
|
||||
let sig = raw_sign_byupdate::<CtxDigest, _>(
|
||||
&esk,
|
||||
|h| {
|
||||
h.update(&msg[..1]);
|
||||
h.update(&msg[1..]);
|
||||
Ok(())
|
||||
},
|
||||
&vk,
|
||||
);
|
||||
assert!(sig.unwrap() == good_sig, "sign byupdate two part");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -833,6 +833,7 @@ impl ExpandedSecretKey {
|
|||
/// This definition is loose in its parameters so that end-users of the `hazmat` module can
|
||||
/// change how the `ExpandedSecretKey` is calculated and which hash function to use.
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[inline(always)]
|
||||
pub(crate) fn raw_sign<CtxDigest>(
|
||||
&self,
|
||||
|
|
@ -841,11 +842,36 @@ impl ExpandedSecretKey {
|
|||
) -> Signature
|
||||
where
|
||||
CtxDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
// OK unwrap, update can't fail.
|
||||
self.raw_sign_byupdate(
|
||||
|h: &mut CtxDigest| {
|
||||
h.update(message);
|
||||
Ok(())
|
||||
},
|
||||
verifying_key,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Sign a message provided in parts. The `msg_update` closure will be called twice to hash the
|
||||
/// message parts. This closure MUST leave its hasher in the same state (i.e., must hash the
|
||||
/// same values) after both calls. Otherwise it will produce an invalid signature.
|
||||
#[allow(non_snake_case)]
|
||||
#[inline(always)]
|
||||
pub(crate) fn raw_sign_byupdate<CtxDigest, F>(
|
||||
&self,
|
||||
msg_update: F,
|
||||
verifying_key: &VerifyingKey,
|
||||
) -> Result<Signature, SignatureError>
|
||||
where
|
||||
CtxDigest: Digest<OutputSize = U64>,
|
||||
F: Fn(&mut CtxDigest) -> Result<(), SignatureError>,
|
||||
{
|
||||
let mut h = CtxDigest::new();
|
||||
|
||||
h.update(self.hash_prefix);
|
||||
h.update(message);
|
||||
msg_update(&mut h)?;
|
||||
|
||||
let r = Scalar::from_hash(h);
|
||||
let R: CompressedEdwardsY = EdwardsPoint::mul_base(&r).compress();
|
||||
|
|
@ -853,12 +879,12 @@ impl ExpandedSecretKey {
|
|||
h = CtxDigest::new();
|
||||
h.update(R.as_bytes());
|
||||
h.update(verifying_key.as_bytes());
|
||||
h.update(message);
|
||||
msg_update(&mut h)?;
|
||||
|
||||
let k = Scalar::from_hash(h);
|
||||
let s: Scalar = (k * self.scalar) + r;
|
||||
|
||||
InternalSignature { R, s }.into()
|
||||
Ok(InternalSignature { R, s }.into())
|
||||
}
|
||||
|
||||
/// The prehashed signing function for Ed25519 (i.e., Ed25519ph). `CtxDigest` is the digest
|
||||
|
|
|
|||
Loading…
Reference in a new issue