mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-04 20:24:10 +00:00
Add Scalar::from_bits_clamped (#498)
As discussed in #497, adds a function which "clamps" a 256-bit input into a valid scalar by clearing and setting bits, as used by Ed25519 and X25519
This commit is contained in:
parent
83f6b149d3
commit
fedb1450de
1 changed files with 65 additions and 0 deletions
|
|
@ -277,6 +277,34 @@ impl Scalar {
|
|||
|
||||
s
|
||||
}
|
||||
|
||||
/// Construct a `Scalar` from the low 255 bits of a little-endian 256-bit integer
|
||||
/// `clamping` it's value to be in range
|
||||
///
|
||||
/// **n ∈ 2^254 + 8\*{0, 1, 2, 3, . . ., 2^251 − 1}**
|
||||
///
|
||||
/// # Explanation of `clamping`
|
||||
///
|
||||
/// For Curve25519, h = 8, and multiplying by 8 is the same as a binary left-shift by 3 bits.
|
||||
/// If you take a secret scalar value between 2^251 and 2^252 – 1 and left-shift by 3 bits
|
||||
/// then you end up with a 255-bit number with the most significant bit set to 1 and
|
||||
/// the least-significant three bits set to 0.
|
||||
///
|
||||
/// The Curve25519 clamping operation takes **an arbitrary 256-bit random value** and
|
||||
/// clears the most-significant bit (making it a 255-bit number), sets the next bit, and then
|
||||
/// clears the 3 least-significant bits. In other words, it directly creates a scalar value that is
|
||||
/// in the right form and pre-multiplied by the cofactor.
|
||||
///
|
||||
/// See <https://neilmadden.blog/2020/05/28/whats-the-curve25519-clamping-all-about/> for details
|
||||
pub const fn from_bits_clamped(bytes: [u8; 32]) -> Scalar {
|
||||
let mut s = Scalar { bytes };
|
||||
|
||||
s.bytes[0] &= 0b1111_1000;
|
||||
s.bytes[31] &= 0b0111_1111;
|
||||
s.bytes[31] |= 0b0100_0000;
|
||||
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Scalar {
|
||||
|
|
@ -1868,4 +1896,41 @@ mod test {
|
|||
// One byte short
|
||||
read_le_u64_into(&[0xFE, 0xEF, 0x10, 0x01, 0x1F, 0xF1, 0x0F], &mut dst);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scalar_clamp() {
|
||||
let input = A_SCALAR.bytes;
|
||||
let expected = Scalar {
|
||||
bytes: [
|
||||
0x18, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d, 0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8,
|
||||
0x26, 0x4d, 0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1, 0x58, 0x9e, 0x7b, 0x7f,
|
||||
0x23, 0x76, 0xef, 0x49,
|
||||
],
|
||||
};
|
||||
let actual = Scalar::from_bits_clamped(input);
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let expected = Scalar {
|
||||
bytes: [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0x40,
|
||||
],
|
||||
};
|
||||
let actual = Scalar::from_bits_clamped([0; 32]);
|
||||
assert_eq!(expected, actual);
|
||||
let expected = Scalar {
|
||||
bytes: [
|
||||
0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0x7f,
|
||||
],
|
||||
};
|
||||
let actual = Scalar::from_bits_clamped([0xff; 32]);
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
assert_eq!(
|
||||
LARGEST_ED25519_S.bytes,
|
||||
Scalar::from_bits_clamped(LARGEST_ED25519_S.bytes).bytes
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue