mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-07 20:50:39 +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 =
|
/// calculate the pseudorandomness needed for signing. According to the Ed25519 spec, `CtxDigest =
|
||||||
/// Sha512`.
|
/// Sha512`.
|
||||||
///
|
///
|
||||||
/// # ⚠️ Unsafe
|
/// # ⚠️ Cryptographically Unsafe
|
||||||
///
|
///
|
||||||
/// Do NOT use this function unless you absolutely must. Using the wrong values in
|
/// Do NOT use this function unless you absolutely must. Using the wrong values in
|
||||||
/// `ExpandedSecretKey` can leak your signing key. See
|
/// `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.
|
/// `CtxDigest` is the digest function used to calculate the pseudorandomness needed for signing.
|
||||||
/// According to the Ed25519 spec, `MsgDigest = CtxDigest = Sha512`.
|
/// 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
|
/// Do NOT use this function unless you absolutely must. Using the wrong values in
|
||||||
/// `ExpandedSecretKey` can leak your signing key. See
|
/// `ExpandedSecretKey` can leak your signing key. See
|
||||||
|
|
@ -169,6 +169,33 @@ where
|
||||||
esk.raw_sign_prehashed::<CtxDigest, MsgDigest>(prehashed_message, verifying_key, context)
|
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
|
/// The ordinary non-batched Ed25519 verification check, rejecting non-canonical R
|
||||||
/// values.`CtxDigest` is the digest used to calculate the pseudorandomness needed for signing.
|
/// values.`CtxDigest` is the digest used to calculate the pseudorandomness needed for signing.
|
||||||
/// According to the Ed25519 spec, `CtxDigest = Sha512`.
|
/// According to the Ed25519 spec, `CtxDigest = Sha512`.
|
||||||
|
|
@ -263,4 +290,47 @@ mod test {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
raw_verify_prehashed::<CtxDigest, MsgDigest>(&vk, h, Some(ctx_str), &sig).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
|
/// 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.
|
/// change how the `ExpandedSecretKey` is calculated and which hash function to use.
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn raw_sign<CtxDigest>(
|
pub(crate) fn raw_sign<CtxDigest>(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -841,11 +842,36 @@ impl ExpandedSecretKey {
|
||||||
) -> Signature
|
) -> Signature
|
||||||
where
|
where
|
||||||
CtxDigest: Digest<OutputSize = U64>,
|
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();
|
let mut h = CtxDigest::new();
|
||||||
|
|
||||||
h.update(self.hash_prefix);
|
h.update(self.hash_prefix);
|
||||||
h.update(message);
|
msg_update(&mut h)?;
|
||||||
|
|
||||||
let r = Scalar::from_hash(h);
|
let r = Scalar::from_hash(h);
|
||||||
let R: CompressedEdwardsY = EdwardsPoint::mul_base(&r).compress();
|
let R: CompressedEdwardsY = EdwardsPoint::mul_base(&r).compress();
|
||||||
|
|
@ -853,12 +879,12 @@ impl ExpandedSecretKey {
|
||||||
h = CtxDigest::new();
|
h = CtxDigest::new();
|
||||||
h.update(R.as_bytes());
|
h.update(R.as_bytes());
|
||||||
h.update(verifying_key.as_bytes());
|
h.update(verifying_key.as_bytes());
|
||||||
h.update(message);
|
msg_update(&mut h)?;
|
||||||
|
|
||||||
let k = Scalar::from_hash(h);
|
let k = Scalar::from_hash(h);
|
||||||
let s: Scalar = (k * self.scalar) + r;
|
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
|
/// The prehashed signing function for Ed25519 (i.e., Ed25519ph). `CtxDigest` is the digest
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue