1use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE;
33use curve25519_dalek::edwards::EdwardsPoint;
34use curve25519_dalek::scalar::Scalar;
35use zeroize::Zeroize;
36
37use super::crypto_core::decompress_canonical_ed25519_point;
38use crate::constants::{
39 CRYPTO_HASH_SHA512_BYTES, CRYPTO_SCALARMULT_CURVE25519_BYTES,
40 CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES, CRYPTO_SIGN_ED25519_BYTES,
41 CRYPTO_SIGN_ED25519_PUBLICKEYBYTES, CRYPTO_SIGN_ED25519_SECRETKEYBYTES,
42 CRYPTO_SIGN_ED25519_SEEDBYTES,
43};
44use crate::error::Error;
45use crate::sha512::Sha512;
46
47pub type PublicKey = [u8; CRYPTO_SIGN_ED25519_PUBLICKEYBYTES];
49pub type SecretKey = [u8; CRYPTO_SIGN_ED25519_SECRETKEYBYTES];
51pub type Signature = [u8; CRYPTO_SIGN_ED25519_BYTES];
53
54const DOM2PREFIX: &[u8] = b"SigEd25519 no Ed25519 collisions\x01\x00";
55
56#[inline]
58pub(crate) fn crypto_sign_ed25519_seed_keypair_inplace(
59 public_key: &mut PublicKey,
60 secret_key: &mut SecretKey,
61 seed: &[u8; CRYPTO_SIGN_ED25519_SEEDBYTES],
62) {
63 let mut hash: [u8; CRYPTO_HASH_SHA512_BYTES] = Sha512::compute(seed);
64
65 let mut clamped = clamp_hash(&mut hash);
66 let mut sk = Scalar::from_bytes_mod_order(clamped);
67 clamped.zeroize();
68
69 let pk = (ED25519_BASEPOINT_TABLE * &sk).compress();
70 secret_key[..CRYPTO_SIGN_ED25519_SEEDBYTES].copy_from_slice(seed);
71 secret_key[CRYPTO_SIGN_ED25519_SEEDBYTES..].copy_from_slice(pk.as_bytes());
72
73 public_key.copy_from_slice(pk.as_bytes());
74
75 sk.zeroize();
76}
77
78pub(crate) fn crypto_sign_ed25519_seed_keypair(
81 seed: &[u8; CRYPTO_SIGN_ED25519_SEEDBYTES],
82) -> (PublicKey, SecretKey) {
83 let mut public_key = PublicKey::default();
84 let mut secret_key = [0u8; CRYPTO_SIGN_ED25519_SECRETKEYBYTES];
85
86 crypto_sign_ed25519_seed_keypair_inplace(&mut public_key, &mut secret_key, seed);
87
88 (public_key, secret_key)
89}
90
91#[inline]
93pub(crate) fn crypto_sign_ed25519_keypair_inplace(
94 public_key: &mut PublicKey,
95 secret_key: &mut SecretKey,
96) {
97 use crate::rng::copy_randombytes;
98 let mut seed = [0u8; CRYPTO_SIGN_ED25519_SEEDBYTES];
99 copy_randombytes(&mut seed);
100 crypto_sign_ed25519_seed_keypair_inplace(public_key, secret_key, &seed);
101 seed.zeroize();
102}
103
104pub(crate) fn crypto_sign_ed25519_keypair() -> (PublicKey, SecretKey) {
107 let mut public_key = PublicKey::default();
108 let mut secret_key = [0u8; CRYPTO_SIGN_ED25519_SECRETKEYBYTES];
109 crypto_sign_ed25519_keypair_inplace(&mut public_key, &mut secret_key);
110
111 (public_key, secret_key)
112}
113
114fn clamp_hash(
115 hash: &mut [u8; CRYPTO_HASH_SHA512_BYTES],
116) -> [u8; CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES] {
117 let mut scalar = [0u8; CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES];
118 scalar.copy_from_slice(&hash[..CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES]);
119 hash.zeroize();
120 scalar[0] &= 248;
121 scalar[31] &= 127;
122 scalar[31] |= 64;
123 scalar
124}
125
126pub fn crypto_sign_ed25519_pk_to_curve25519(
136 x25519_public_key: &mut [u8; CRYPTO_SCALARMULT_CURVE25519_BYTES],
137 ed25519_public_key: &PublicKey,
138) -> Result<(), Error> {
139 let ep = decompress_canonical_ed25519_point(ed25519_public_key)
140 .filter(|point| !point.is_small_order() && point.is_torsion_free())
141 .ok_or(Error::invalid_key(crate::ErrorContext::Ed25519PublicKey))?;
142 x25519_public_key.copy_from_slice(ep.to_montgomery().as_bytes());
143
144 Ok(())
145}
146
147pub fn crypto_sign_ed25519_sk_to_curve25519(
152 x25519_secret_key: &mut [u8; CRYPTO_SCALARMULT_CURVE25519_BYTES],
153 ed25519_secret_key: &SecretKey,
154) {
155 let mut hash: [u8; CRYPTO_HASH_SHA512_BYTES] = Sha512::compute(&ed25519_secret_key[..32]);
156 let mut scalar = clamp_hash(&mut hash);
157 x25519_secret_key.copy_from_slice(&scalar);
158 scalar.zeroize()
159}
160
161pub fn crypto_sign_ed25519_sk_to_seed(
165 seed: &mut [u8; CRYPTO_SIGN_ED25519_SEEDBYTES],
166 secret_key: &SecretKey,
167) {
168 seed.copy_from_slice(&secret_key[..CRYPTO_SIGN_ED25519_SEEDBYTES]);
169}
170
171pub fn crypto_sign_ed25519_sk_to_pk(public_key: &mut PublicKey, secret_key: &SecretKey) {
176 public_key.copy_from_slice(
177 &secret_key[CRYPTO_SIGN_ED25519_SEEDBYTES..CRYPTO_SIGN_ED25519_SECRETKEYBYTES],
178 );
179}
180
181pub(crate) fn crypto_sign_ed25519(
182 signed_message: &mut [u8],
183 message: &[u8],
184 secret_key: &SecretKey,
185) -> Result<(), Error> {
186 if signed_message.len() != message.len() + CRYPTO_SIGN_ED25519_BYTES {
187 Err(length_error!(
188 crate::ErrorContext::SignedMessage,
189 signed_message.len(),
190 exact message.len() + CRYPTO_SIGN_ED25519_BYTES
191 ))
192 } else {
193 let (sig, sm) = signed_message.split_at_mut(CRYPTO_SIGN_ED25519_BYTES);
194 let sig: &mut [u8; CRYPTO_SIGN_ED25519_BYTES] =
195 <&mut [u8; CRYPTO_SIGN_ED25519_BYTES]>::try_from(sig).unwrap();
196 sm.copy_from_slice(message);
197 crypto_sign_ed25519_detached(sig, message, secret_key)
198 }
199}
200
201pub(crate) fn crypto_sign_ed25519_detached(
202 signature: &mut Signature,
203 message: &[u8],
204 secret_key: &SecretKey,
205) -> Result<(), Error> {
206 crypto_sign_ed25519_detached_impl(signature, message, secret_key, false)
207}
208
209#[inline]
210fn crypto_sign_ed25519_detached_impl(
211 signature: &mut Signature,
212 message: &[u8],
213 secret_key: &SecretKey,
214 prehashed: bool,
215) -> Result<(), Error> {
216 if signature.len() != CRYPTO_SIGN_ED25519_BYTES {
217 Err(length_error!(
218 crate::ErrorContext::Signature,
219 signature.len(),
220 exact CRYPTO_SIGN_ED25519_BYTES
221 ))
222 } else {
223 let mut az: [u8; CRYPTO_HASH_SHA512_BYTES] = Sha512::compute(&secret_key[..32]);
224
225 let mut hasher = Sha512::new();
226 if prehashed {
227 hasher.update(DOM2PREFIX);
228 }
229 hasher.update(&az[32..]);
230 hasher.update(message);
231 let mut nonce: [u8; CRYPTO_HASH_SHA512_BYTES] = hasher.finalize();
232
233 signature[32..].copy_from_slice(&secret_key[32..]);
234
235 let mut r = Scalar::from_bytes_mod_order_wide(&nonce);
236 let big_r = (ED25519_BASEPOINT_TABLE * &r).compress();
237
238 signature[..32].copy_from_slice(big_r.as_bytes());
239
240 let mut hasher = Sha512::new();
241 if prehashed {
242 hasher.update(DOM2PREFIX);
243 }
244 hasher.update(signature);
245 hasher.update(message);
246 let mut hram: [u8; CRYPTO_HASH_SHA512_BYTES] = hasher.finalize();
247
248 let mut k = Scalar::from_bytes_mod_order_wide(&hram);
249 let mut clamped = clamp_hash(&mut az);
250 let mut signing_scalar = Scalar::from_bytes_mod_order(clamped);
251 clamped.zeroize();
252 let mut sig = (k * signing_scalar) + r;
253
254 signature[32..].copy_from_slice(sig.as_bytes());
255
256 az.zeroize();
257 nonce.zeroize();
258 hram.zeroize();
259 r.zeroize();
260 k.zeroize();
261 signing_scalar.zeroize();
262 sig.zeroize();
263
264 Ok(())
265 }
266}
267
268pub(crate) fn crypto_sign_ed25519_verify_detached(
269 signature: &Signature,
270 message: &[u8],
271 public_key: &PublicKey,
272) -> Result<(), Error> {
273 crypto_sign_ed25519_verify_detached_impl(signature, message, public_key, false)
274}
275
276fn crypto_sign_ed25519_verify_detached_impl(
277 signature: &Signature,
278 message: &[u8],
279 public_key: &PublicKey,
280 prehashed: bool,
281) -> Result<(), Error> {
282 let s_bytes = *<&[u8; CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES]>::try_from(&signature[32..])
283 .map_err(|_| Error::AuthenticationFailed)?;
284 let s = Option::<Scalar>::from(Scalar::from_canonical_bytes(s_bytes))
285 .ok_or(Error::AuthenticationFailed)?;
286 let r_bytes = <&[u8; CRYPTO_SIGN_ED25519_PUBLICKEYBYTES]>::try_from(&signature[..32])
287 .map_err(|_| Error::AuthenticationFailed)?;
288 let big_r = decompress_canonical_ed25519_point(r_bytes).ok_or(Error::AuthenticationFailed)?;
289 if big_r.is_small_order() {
290 return Err(Error::AuthenticationFailed);
291 }
292 let pk = decompress_canonical_ed25519_point(public_key)
293 .ok_or(Error::invalid_key(crate::ErrorContext::Ed25519PublicKey))?;
294 if pk.is_small_order() {
295 return Err(Error::invalid_key(crate::ErrorContext::Ed25519PublicKey));
296 }
297
298 let mut hasher = Sha512::new();
299 if prehashed {
300 hasher.update(DOM2PREFIX);
301 }
302 hasher.update(&signature[..32]);
303 hasher.update(public_key);
304 hasher.update(message);
305 let h: [u8; CRYPTO_HASH_SHA512_BYTES] = hasher.finalize();
306
307 let k = Scalar::from_bytes_mod_order_wide(&h);
308
309 let sig_r = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(-pk), &s);
310
311 if sig_r == big_r {
312 Ok(())
313 } else {
314 Err(Error::AuthenticationFailed)
315 }
316}
317
318pub(crate) fn crypto_sign_ed25519_open(
319 message: &mut [u8],
320 signed_message: &[u8],
321 public_key: &PublicKey,
322) -> Result<(), Error> {
323 if signed_message.len() < CRYPTO_SIGN_ED25519_BYTES {
324 Err(length_error!(
325 crate::ErrorContext::SignedMessage,
326 signed_message.len(),
327 min CRYPTO_SIGN_ED25519_BYTES
328 ))
329 } else if message.len() != signed_message.len() - CRYPTO_SIGN_ED25519_BYTES {
330 Err(length_error!(
331 crate::ErrorContext::Message,
332 message.len(),
333 exact signed_message.len() - CRYPTO_SIGN_ED25519_BYTES
334 ))
335 } else {
336 let (sig, sm) = signed_message.split_at(CRYPTO_SIGN_ED25519_BYTES);
337 let sig: &[u8; CRYPTO_SIGN_ED25519_BYTES] =
338 <&[u8; CRYPTO_SIGN_ED25519_BYTES]>::try_from(sig).unwrap();
339 crypto_sign_ed25519_verify_detached(sig, sm, public_key)?;
340 message.copy_from_slice(sm);
341 Ok(())
342 }
343}
344
345pub(crate) struct Ed25519SignerState {
346 hasher: Sha512,
347}
348
349pub(crate) fn crypto_sign_ed25519ph_init() -> Ed25519SignerState {
350 Ed25519SignerState {
351 hasher: Sha512::new(),
352 }
353}
354
355pub(crate) fn crypto_sign_ed25519ph_update(state: &mut Ed25519SignerState, message: &[u8]) {
356 state.hasher.update(message)
357}
358
359pub(crate) fn crypto_sign_ed25519ph_final_create(
360 state: Ed25519SignerState,
361 signature: &mut Signature,
362 secret_key: &SecretKey,
363) -> Result<(), Error> {
364 let mut hash: [u8; CRYPTO_HASH_SHA512_BYTES] = state.hasher.finalize();
365 let res = crypto_sign_ed25519_detached_impl(signature, &hash, secret_key, true);
366 hash.zeroize();
367 res
368}
369
370pub(crate) fn crypto_sign_ed25519ph_final_verify(
371 state: Ed25519SignerState,
372 signature: &Signature,
373 public_key: &PublicKey,
374) -> Result<(), Error> {
375 let mut hash: [u8; CRYPTO_HASH_SHA512_BYTES] = state.hasher.finalize();
376 let res = crypto_sign_ed25519_verify_detached_impl(signature, &hash, public_key, true);
377 hash.zeroize();
378 res
379}
380
381#[cfg(test)]
382mod regression_tests {
383 use super::*;
384
385 const ED25519_GROUP_ORDER: [u8; 32] = [
386 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde,
387 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
388 0x00, 0x10,
389 ];
390
391 pub(super) fn add_group_order_to_s(signature: &mut Signature) {
392 let mut carry = 0u16;
393 for (s, order) in signature[32..].iter_mut().zip(ED25519_GROUP_ORDER) {
394 let sum = u16::from(*s) + u16::from(order) + carry;
395 *s = sum as u8;
396 carry = sum >> 8;
397 }
398 assert_eq!(carry, 0, "a reduced Ed25519 scalar plus L fits in 256 bits");
399 }
400
401 #[test]
402 fn verification_rejects_s_plus_group_order() {
403 let message = b"malleability regression";
404 let (public_key, secret_key) = crypto_sign_ed25519_seed_keypair(&[7u8; 32]);
405
406 let mut signature = [0u8; CRYPTO_SIGN_ED25519_BYTES];
407 crypto_sign_ed25519_detached(&mut signature, message, &secret_key).unwrap();
408 crypto_sign_ed25519_verify_detached(&signature, message, &public_key).unwrap();
409 add_group_order_to_s(&mut signature);
410 assert!(matches!(
411 crypto_sign_ed25519_verify_detached(&signature, message, &public_key),
412 Err(Error::AuthenticationFailed)
413 ));
414
415 let mut signed_message = vec![0u8; message.len() + CRYPTO_SIGN_ED25519_BYTES];
416 crypto_sign_ed25519(&mut signed_message, message, &secret_key).unwrap();
417 let embedded_signature =
418 <&mut Signature>::try_from(&mut signed_message[..CRYPTO_SIGN_ED25519_BYTES]).unwrap();
419 add_group_order_to_s(embedded_signature);
420 let mut opened_message = vec![0u8; message.len()];
421 assert!(matches!(
422 crypto_sign_ed25519_open(&mut opened_message, &signed_message, &public_key),
423 Err(Error::AuthenticationFailed)
424 ));
425
426 let mut signer = crypto_sign_ed25519ph_init();
427 crypto_sign_ed25519ph_update(&mut signer, message);
428 let mut prehash_signature = [0u8; CRYPTO_SIGN_ED25519_BYTES];
429 crypto_sign_ed25519ph_final_create(signer, &mut prehash_signature, &secret_key).unwrap();
430 add_group_order_to_s(&mut prehash_signature);
431
432 let mut verifier = crypto_sign_ed25519ph_init();
433 crypto_sign_ed25519ph_update(&mut verifier, message);
434 assert!(matches!(
435 crypto_sign_ed25519ph_final_verify(verifier, &prehash_signature, &public_key),
436 Err(Error::AuthenticationFailed)
437 ));
438 }
439
440 #[test]
441 fn public_key_conversion_rejects_invalid_edwards_points() {
442 let identity = {
443 let mut point = [0u8; 32];
444 point[0] = 1;
445 point
446 };
447 let noncanonical_identity = {
448 let mut point = [0xff; 32];
449 point[0] = 0xee;
450 point[31] = 0x7f;
451 point
452 };
453 let mixed_order = (curve25519_dalek::constants::ED25519_BASEPOINT_POINT
454 + curve25519_dalek::constants::EIGHT_TORSION[1])
455 .compress()
456 .to_bytes();
457 let mixed_point = decompress_canonical_ed25519_point(&mixed_order).unwrap();
458 assert!(!mixed_point.is_small_order());
459 assert!(!mixed_point.is_torsion_free());
460
461 for invalid_key in [identity, noncanonical_identity, mixed_order] {
462 let mut output = [0xa5; CRYPTO_SCALARMULT_CURVE25519_BYTES];
463 assert!(crypto_sign_ed25519_pk_to_curve25519(&mut output, &invalid_key).is_err());
464 assert_eq!(
465 output, [0xa5; CRYPTO_SCALARMULT_CURVE25519_BYTES],
466 "conversion failure must not modify the output"
467 );
468 }
469 }
470
471 #[test]
472 fn public_key_conversion_accepts_valid_high_sign_bit() {
473 let basepoint = curve25519_dalek::constants::ED25519_BASEPOINT_COMPRESSED.to_bytes();
474 let mut negative_basepoint = basepoint;
475 negative_basepoint[31] |= 0x80;
476
477 let mut positive_output = [0u8; CRYPTO_SCALARMULT_CURVE25519_BYTES];
478 let mut negative_output = [0u8; CRYPTO_SCALARMULT_CURVE25519_BYTES];
479 crypto_sign_ed25519_pk_to_curve25519(&mut positive_output, &basepoint).unwrap();
480 crypto_sign_ed25519_pk_to_curve25519(&mut negative_output, &negative_basepoint).unwrap();
481 assert_eq!(positive_output, negative_output);
482 }
483}
484
485#[cfg(all(test, dryoc_native_tests))]
486mod tests {
487 use base64::Engine as _;
488 use base64::engine::general_purpose;
489
490 use super::*;
491 use crate::rng::copy_randombytes;
492
493 #[test]
494 fn test_keypair_seed() {
495 use sodiumoxide::crypto::sign;
496
497 for _ in 0..10 {
498 let mut seed = [0u8; CRYPTO_SIGN_ED25519_SEEDBYTES];
499 copy_randombytes(&mut seed);
500
501 let (pk, sk) = crypto_sign_ed25519_seed_keypair(&seed);
502
503 let (so_pk, so_sk) =
504 sign::keypair_from_seed(&sign::Seed::from_slice(&seed).expect("seed failed"));
505
506 assert_eq!(
507 general_purpose::STANDARD.encode(pk),
508 general_purpose::STANDARD.encode(so_pk.0)
509 );
510 assert_eq!(
511 general_purpose::STANDARD.encode(sk),
512 general_purpose::STANDARD.encode(so_sk.0)
513 );
514 }
515 }
516
517 #[test]
518 fn test_key_conversion() {
519 use libsodium_sys::{
520 crypto_sign_ed25519_pk_to_curve25519 as so_crypto_sign_ed25519_pk_to_curve25519,
521 crypto_sign_ed25519_sk_to_curve25519 as so_crypto_sign_ed25519_sk_to_curve25519,
522 };
523
524 for _ in 0..10 {
525 let (pk, sk) = crypto_sign_ed25519_keypair();
526 let mut xpk = [0u8; CRYPTO_SCALARMULT_CURVE25519_BYTES];
527 let mut xsk = [0u8; CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES];
528 crypto_sign_ed25519_pk_to_curve25519(&mut xpk, &pk).expect("pk failed");
529 crypto_sign_ed25519_sk_to_curve25519(&mut xsk, &sk);
530
531 let mut so_xpk = [0u8; CRYPTO_SCALARMULT_CURVE25519_BYTES];
532 let mut so_xsk = [0u8; CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES];
533
534 unsafe {
535 so_crypto_sign_ed25519_pk_to_curve25519(so_xpk.as_mut_ptr(), pk.as_ptr());
536 so_crypto_sign_ed25519_sk_to_curve25519(so_xsk.as_mut_ptr(), sk.as_ptr());
537 }
538
539 assert_eq!(
540 general_purpose::STANDARD.encode(xpk),
541 general_purpose::STANDARD.encode(so_xpk)
542 );
543 assert_eq!(
544 general_purpose::STANDARD.encode(xsk),
545 general_purpose::STANDARD.encode(so_xsk)
546 );
547 }
548 }
549
550 #[test]
551 fn test_invalid_public_key_conversion_compatibility() {
552 use libsodium_sys::crypto_sign_ed25519_pk_to_curve25519 as sodium_convert;
553
554 let identity = {
555 let mut point = [0u8; 32];
556 point[0] = 1;
557 point
558 };
559 let noncanonical_identity = {
560 let mut point = [0xff; 32];
561 point[0] = 0xee;
562 point[31] = 0x7f;
563 point
564 };
565 let mixed_order = (curve25519_dalek::constants::ED25519_BASEPOINT_POINT
566 + curve25519_dalek::constants::EIGHT_TORSION[1])
567 .compress()
568 .to_bytes();
569
570 for invalid_key in [identity, noncanonical_identity, mixed_order] {
571 let mut output = [0u8; CRYPTO_SCALARMULT_CURVE25519_BYTES];
572 let dryoc_result = crypto_sign_ed25519_pk_to_curve25519(&mut output, &invalid_key);
573 let sodium_result =
574 unsafe { sodium_convert(output.as_mut_ptr(), invalid_key.as_ptr()) };
575 assert!(dryoc_result.is_err());
576 assert_eq!(sodium_result, -1);
577 }
578 }
579
580 #[test]
581 fn test_noncanonical_signature_scalar_compatibility() {
582 use libsodium_sys::crypto_sign_verify_detached as sodium_verify;
583
584 let message = b"malleability regression";
585 let (public_key, secret_key) = crypto_sign_ed25519_seed_keypair(&[7u8; 32]);
586 let mut signature = [0u8; CRYPTO_SIGN_ED25519_BYTES];
587 crypto_sign_ed25519_detached(&mut signature, message, &secret_key).unwrap();
588 super::regression_tests::add_group_order_to_s(&mut signature);
589
590 assert!(crypto_sign_ed25519_verify_detached(&signature, message, &public_key).is_err());
591 let sodium_result = unsafe {
592 sodium_verify(
593 signature.as_ptr(),
594 message.as_ptr(),
595 message.len() as u64,
596 public_key.as_ptr(),
597 )
598 };
599 assert_eq!(sodium_result, -1);
600 }
601
602 #[test]
603 fn test_secret_key_extraction() {
604 use libsodium_sys::{
605 crypto_sign_ed25519_sk_to_pk as so_crypto_sign_ed25519_sk_to_pk,
606 crypto_sign_ed25519_sk_to_seed as so_crypto_sign_ed25519_sk_to_seed,
607 };
608
609 for _ in 0..10 {
610 let (pk, sk) = crypto_sign_ed25519_keypair();
611 let mut seed = [0u8; CRYPTO_SIGN_ED25519_SEEDBYTES];
612 let mut extracted_pk = [0u8; CRYPTO_SIGN_ED25519_PUBLICKEYBYTES];
613 crypto_sign_ed25519_sk_to_seed(&mut seed, &sk);
614 crypto_sign_ed25519_sk_to_pk(&mut extracted_pk, &sk);
615
616 let mut so_seed = [0u8; CRYPTO_SIGN_ED25519_SEEDBYTES];
617 let mut so_pk = [0u8; CRYPTO_SIGN_ED25519_PUBLICKEYBYTES];
618
619 unsafe {
620 so_crypto_sign_ed25519_sk_to_seed(so_seed.as_mut_ptr(), sk.as_ptr());
621 so_crypto_sign_ed25519_sk_to_pk(so_pk.as_mut_ptr(), sk.as_ptr());
622 }
623
624 assert_eq!(seed, so_seed);
625 assert_eq!(extracted_pk, pk);
626 assert_eq!(extracted_pk, so_pk);
627 }
628 }
629}