1use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE;
12use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint};
13use curve25519_dalek::scalar::Scalar;
14use zeroize::Zeroize;
15
16use crate::constants::{
17 CRYPTO_HASH_SHA512_BYTES, CRYPTO_SCALARMULT_CURVE25519_BYTES,
18 CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES, CRYPTO_SIGN_ED25519_BYTES,
19 CRYPTO_SIGN_ED25519_PUBLICKEYBYTES, CRYPTO_SIGN_ED25519_SECRETKEYBYTES,
20 CRYPTO_SIGN_ED25519_SEEDBYTES,
21};
22use crate::error::Error;
23use crate::sha512::Sha512;
24
25pub type PublicKey = [u8; CRYPTO_SIGN_ED25519_PUBLICKEYBYTES];
27pub type SecretKey = [u8; CRYPTO_SIGN_ED25519_SECRETKEYBYTES];
29pub type Signature = [u8; CRYPTO_SIGN_ED25519_BYTES];
31
32const DOM2PREFIX: &[u8] = b"SigEd25519 no Ed25519 collisions\x01\x00";
33
34#[inline]
36pub(crate) fn crypto_sign_ed25519_seed_keypair_inplace(
37 public_key: &mut PublicKey,
38 secret_key: &mut SecretKey,
39 seed: &[u8; CRYPTO_SIGN_ED25519_SEEDBYTES],
40) {
41 let hash: [u8; CRYPTO_HASH_SHA512_BYTES] = Sha512::compute(seed);
42
43 let mut sk = Scalar::from_bytes_mod_order(clamp_hash(hash));
44
45 let pk = (ED25519_BASEPOINT_TABLE * &sk).compress();
46 secret_key[..CRYPTO_SIGN_ED25519_SEEDBYTES].copy_from_slice(seed);
47 secret_key[CRYPTO_SIGN_ED25519_SEEDBYTES..].copy_from_slice(pk.as_bytes());
48
49 public_key.copy_from_slice(pk.as_bytes());
50
51 sk.zeroize();
52}
53
54pub(crate) fn crypto_sign_ed25519_seed_keypair(
57 seed: &[u8; CRYPTO_SIGN_ED25519_SEEDBYTES],
58) -> (PublicKey, SecretKey) {
59 let mut public_key = PublicKey::default();
60 let mut secret_key = [0u8; CRYPTO_SIGN_ED25519_SECRETKEYBYTES];
61
62 crypto_sign_ed25519_seed_keypair_inplace(&mut public_key, &mut secret_key, seed);
63
64 (public_key, secret_key)
65}
66
67#[inline]
69pub(crate) fn crypto_sign_ed25519_keypair_inplace(
70 public_key: &mut PublicKey,
71 secret_key: &mut SecretKey,
72) {
73 use crate::rng::copy_randombytes;
74 let mut seed = [0u8; CRYPTO_SIGN_ED25519_SEEDBYTES];
75 copy_randombytes(&mut seed);
76 crypto_sign_ed25519_seed_keypair_inplace(public_key, secret_key, &seed);
77}
78
79pub(crate) fn crypto_sign_ed25519_keypair() -> (PublicKey, SecretKey) {
82 let mut public_key = PublicKey::default();
83 let mut secret_key = [0u8; CRYPTO_SIGN_ED25519_SECRETKEYBYTES];
84 crypto_sign_ed25519_keypair_inplace(&mut public_key, &mut secret_key);
85
86 (public_key, secret_key)
87}
88
89fn clamp_hash(
90 mut hash: [u8; CRYPTO_HASH_SHA512_BYTES],
91) -> [u8; CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES] {
92 let mut scalar = [0u8; CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES];
93 scalar.copy_from_slice(&hash[..CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES]);
94 hash.zeroize();
95 scalar[0] &= 248;
96 scalar[31] &= 127;
97 scalar[31] |= 64;
98 scalar
99}
100
101pub fn crypto_sign_ed25519_pk_to_curve25519(
106 x25519_public_key: &mut [u8; CRYPTO_SCALARMULT_CURVE25519_BYTES],
107 ed25519_public_key: &PublicKey,
108) -> Result<(), Error> {
109 let ep = CompressedEdwardsY(*ed25519_public_key)
110 .decompress()
111 .ok_or_else(|| dryoc_error!("failed to convert to Edwards point"))?;
112 x25519_public_key.copy_from_slice(ep.to_montgomery().as_bytes());
113
114 Ok(())
115}
116
117pub fn crypto_sign_ed25519_sk_to_curve25519(
122 x25519_secret_key: &mut [u8; CRYPTO_SCALARMULT_CURVE25519_BYTES],
123 ed25519_secret_key: &SecretKey,
124) {
125 let hash: [u8; CRYPTO_HASH_SHA512_BYTES] = Sha512::compute(&ed25519_secret_key[..32]);
126 let mut scalar = clamp_hash(hash);
127 x25519_secret_key.copy_from_slice(&scalar);
128 scalar.zeroize()
129}
130
131pub(crate) fn crypto_sign_ed25519(
132 signed_message: &mut [u8],
133 message: &[u8],
134 secret_key: &SecretKey,
135) -> Result<(), Error> {
136 if signed_message.len() != message.len() + CRYPTO_SIGN_ED25519_BYTES {
137 Err(dryoc_error!(format!(
138 "signed_message length incorrect (expect {}, got {})",
139 message.len() + CRYPTO_SIGN_ED25519_BYTES,
140 signed_message.len()
141 )))
142 } else {
143 let (sig, sm) = signed_message.split_at_mut(CRYPTO_SIGN_ED25519_BYTES);
144 let sig: &mut [u8; CRYPTO_SIGN_ED25519_BYTES] =
145 <&mut [u8; CRYPTO_SIGN_ED25519_BYTES]>::try_from(sig).unwrap();
146 sm.copy_from_slice(message);
147 crypto_sign_ed25519_detached(sig, message, secret_key)
148 }
149}
150
151pub(crate) fn crypto_sign_ed25519_detached(
152 signature: &mut Signature,
153 message: &[u8],
154 secret_key: &SecretKey,
155) -> Result<(), Error> {
156 crypto_sign_ed25519_detached_impl(signature, message, secret_key, false)
157}
158
159#[inline]
160fn crypto_sign_ed25519_detached_impl(
161 signature: &mut Signature,
162 message: &[u8],
163 secret_key: &SecretKey,
164 prehashed: bool,
165) -> Result<(), Error> {
166 if signature.len() != CRYPTO_SIGN_ED25519_BYTES {
167 Err(dryoc_error!(format!(
168 "signature length incorrect (expect {}, got {})",
169 CRYPTO_SIGN_ED25519_BYTES,
170 signature.len()
171 )))
172 } else {
173 let mut az: [u8; CRYPTO_HASH_SHA512_BYTES] = Sha512::compute(&secret_key[..32]);
174
175 let mut hasher = Sha512::new();
176 if prehashed {
177 hasher.update(DOM2PREFIX);
178 }
179 hasher.update(&az[32..]);
180 hasher.update(message);
181 let mut nonce: [u8; CRYPTO_HASH_SHA512_BYTES] = hasher.finalize();
182
183 signature[32..].copy_from_slice(&secret_key[32..]);
184
185 let r = Scalar::from_bytes_mod_order_wide(&nonce);
186 let big_r = (ED25519_BASEPOINT_TABLE * &r).compress();
187
188 signature[..32].copy_from_slice(big_r.as_bytes());
189
190 let mut hasher = Sha512::new();
191 if prehashed {
192 hasher.update(DOM2PREFIX);
193 }
194 hasher.update(signature);
195 hasher.update(message);
196 let hram: [u8; CRYPTO_HASH_SHA512_BYTES] = hasher.finalize();
197
198 let k = Scalar::from_bytes_mod_order_wide(&hram);
199 let clamped = clamp_hash(az);
200 let sig = (k * Scalar::from_bytes_mod_order(clamped)) + r;
201
202 signature[32..].copy_from_slice(sig.as_bytes());
203
204 az.zeroize();
205 nonce.zeroize();
206
207 Ok(())
208 }
209}
210
211pub(crate) fn crypto_sign_ed25519_verify_detached(
212 signature: &Signature,
213 message: &[u8],
214 public_key: &PublicKey,
215) -> Result<(), Error> {
216 crypto_sign_ed25519_verify_detached_impl(signature, message, public_key, false)
217}
218
219fn crypto_sign_ed25519_verify_detached_impl(
220 signature: &Signature,
221 message: &[u8],
222 public_key: &PublicKey,
223 prehashed: bool,
224) -> Result<(), Error> {
225 let s = Scalar::from_bytes_mod_order(
226 *<&[u8; CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES]>::try_from(&signature[32..])
227 .map_err(|_| dryoc_error!("bad signature"))?,
228 );
229 let big_r = CompressedEdwardsY::from_slice(&signature[..32])?
230 .decompress()
231 .ok_or_else(|| dryoc_error!("bad signature"))?;
232 if big_r.is_small_order() {
233 return Err(dryoc_error!("bad signature"));
234 }
235 let pk = CompressedEdwardsY::from_slice(public_key)?
236 .decompress()
237 .ok_or_else(|| dryoc_error!("bad public key"))?;
238 if pk.is_small_order() {
239 return Err(dryoc_error!("bad public key"));
240 }
241
242 let mut hasher = Sha512::new();
243 if prehashed {
244 hasher.update(DOM2PREFIX);
245 }
246 hasher.update(&signature[..32]);
247 hasher.update(public_key);
248 hasher.update(message);
249 let h: [u8; CRYPTO_HASH_SHA512_BYTES] = hasher.finalize();
250
251 let k = Scalar::from_bytes_mod_order_wide(&h);
252
253 let sig_r = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(-pk), &s);
254
255 if sig_r == big_r {
256 Ok(())
257 } else {
258 Err(dryoc_error!("bad signature"))
259 }
260}
261
262pub(crate) fn crypto_sign_ed25519_open(
263 message: &mut [u8],
264 signed_message: &[u8],
265 public_key: &PublicKey,
266) -> Result<(), Error> {
267 if signed_message.len() < CRYPTO_SIGN_ED25519_BYTES {
268 Err(dryoc_error!(format!(
269 "signed_message length invalid ({} < {})",
270 signed_message.len(),
271 CRYPTO_SIGN_ED25519_BYTES,
272 )))
273 } else if message.len() != signed_message.len() - CRYPTO_SIGN_ED25519_BYTES {
274 Err(dryoc_error!(format!(
275 "message length incorrect (expect {}, got {})",
276 signed_message.len() - CRYPTO_SIGN_ED25519_BYTES,
277 message.len()
278 )))
279 } else {
280 let (sig, sm) = signed_message.split_at(CRYPTO_SIGN_ED25519_BYTES);
281 let sig: &[u8; CRYPTO_SIGN_ED25519_BYTES] =
282 <&[u8; CRYPTO_SIGN_ED25519_BYTES]>::try_from(sig).unwrap();
283 crypto_sign_ed25519_verify_detached(sig, sm, public_key)?;
284 message.copy_from_slice(sm);
285 Ok(())
286 }
287}
288
289pub(crate) struct Ed25519SignerState {
290 hasher: Sha512,
291}
292
293pub(crate) fn crypto_sign_ed25519ph_init() -> Ed25519SignerState {
294 Ed25519SignerState {
295 hasher: Sha512::new(),
296 }
297}
298
299pub(crate) fn crypto_sign_ed25519ph_update(state: &mut Ed25519SignerState, message: &[u8]) {
300 state.hasher.update(message)
301}
302
303pub(crate) fn crypto_sign_ed25519ph_final_create(
304 state: Ed25519SignerState,
305 signature: &mut Signature,
306 secret_key: &SecretKey,
307) -> Result<(), Error> {
308 let mut hash: [u8; CRYPTO_HASH_SHA512_BYTES] = state.hasher.finalize();
309 let res = crypto_sign_ed25519_detached_impl(signature, &hash, secret_key, true);
310 hash.zeroize();
311 res
312}
313
314pub(crate) fn crypto_sign_ed25519ph_final_verify(
315 state: Ed25519SignerState,
316 signature: &Signature,
317 public_key: &PublicKey,
318) -> Result<(), Error> {
319 let mut hash: [u8; CRYPTO_HASH_SHA512_BYTES] = state.hasher.finalize();
320 let res = crypto_sign_ed25519_verify_detached_impl(signature, &hash, public_key, true);
321 hash.zeroize();
322 res
323}
324
325#[cfg(test)]
326mod tests {
327 use base64::Engine as _;
328 use base64::engine::general_purpose;
329
330 use super::*;
331 use crate::rng::copy_randombytes;
332
333 #[test]
334 fn test_keypair_seed() {
335 use sodiumoxide::crypto::sign;
336
337 for _ in 0..10 {
338 let mut seed = [0u8; CRYPTO_SIGN_ED25519_SEEDBYTES];
339 copy_randombytes(&mut seed);
340
341 let (pk, sk) = crypto_sign_ed25519_seed_keypair(&seed);
342
343 let (so_pk, so_sk) =
344 sign::keypair_from_seed(&sign::Seed::from_slice(&seed).expect("seed failed"));
345
346 assert_eq!(
347 general_purpose::STANDARD.encode(pk),
348 general_purpose::STANDARD.encode(so_pk.0)
349 );
350 assert_eq!(
351 general_purpose::STANDARD.encode(sk),
352 general_purpose::STANDARD.encode(so_sk.0)
353 );
354 }
355 }
356
357 #[test]
358 fn test_() {
359 use libsodium_sys::{
360 crypto_sign_ed25519_pk_to_curve25519 as so_crypto_sign_ed25519_pk_to_curve25519,
361 crypto_sign_ed25519_sk_to_curve25519 as so_crypto_sign_ed25519_sk_to_curve25519,
362 };
363
364 for _ in 0..10 {
365 let (pk, sk) = crypto_sign_ed25519_keypair();
366 let mut xpk = [0u8; CRYPTO_SCALARMULT_CURVE25519_BYTES];
367 let mut xsk = [0u8; CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES];
368 crypto_sign_ed25519_pk_to_curve25519(&mut xpk, &pk).expect("pk failed");
369 crypto_sign_ed25519_sk_to_curve25519(&mut xsk, &sk);
370
371 let mut so_xpk = [0u8; CRYPTO_SCALARMULT_CURVE25519_BYTES];
372 let mut so_xsk = [0u8; CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES];
373
374 unsafe {
375 so_crypto_sign_ed25519_pk_to_curve25519(so_xpk.as_mut_ptr(), pk.as_ptr());
376 so_crypto_sign_ed25519_sk_to_curve25519(so_xsk.as_mut_ptr(), sk.as_ptr());
377 }
378
379 assert_eq!(
380 general_purpose::STANDARD.encode(xpk),
381 general_purpose::STANDARD.encode(so_xpk)
382 );
383 assert_eq!(
384 general_purpose::STANDARD.encode(xsk),
385 general_purpose::STANDARD.encode(so_xsk)
386 );
387 }
388 }
389}