dryoc/sign.rs
1//! # Public-key signatures
2//!
3//! This module implements libsodium's public-key signature functions. The
4//! signatures are based on Ed25519 (EdDSA). It provides both a
5//! [single-part](SigningKeyPair::sign) and [multi-part](IncrementalSigner)
6//! interface.
7//!
8//! The single-part interface is convenient for short
9//! messages, such as those small enough to fit in memory. The multi-part
10//! interface may be more appropriate for lengthy messages, those which don't
11//! fit in memory, or those for which the entire message isn't known at once
12//! (i.e., during network communication, or reading a large file).
13//!
14//! The single-part and multi-part variants use slightly different algorithms,
15//! and thus they are not compatible with each other.
16//!
17//! Use this module when you want to:
18//!
19//! * share a message with other parties, and provide a proof that the message
20//! is authentic
21//! * verify that the message from another party was signed using their secret
22//! key, without having knowledge of the original secret
23//!
24//! The public key of the signer must be known to the verifier.
25//!
26//! Keep signing and encryption keys separate. Although Ed25519 keys can be
27//! converted to X25519 keys or derived from the same seed, doing so couples two
28//! distinct security roles.
29//!
30//! Signing secret keys include both the seed and public key. Use
31//! [`secret_key_to_seed`], [`secret_key_to_public_key`],
32//! [`SigningKeyPair::to_seed`], or [`SigningKeyPair::to_public_key`] to extract
33//! those parts when interoperating with libsodium-style key storage.
34//!
35//! ## Rustaceous API example, single-part
36//!
37//! ```
38//! use dryoc::sign::*;
39//!
40//! // Generate a random keypair, using default types
41//! let keypair = SigningKeyPair::<PublicKey, SecretKey>::generate();
42//! let message = b"Fair is foul, and foul is fair: Hover through the fog and filthy air.";
43//!
44//! // Sign the message, using default types (stack-allocated byte array, Vec<u8>)
45//! let signed_message = keypair.sign_with_defaults(message).expect("signing failed");
46//!
47//! // Verify the message signature
48//! signed_message
49//! .verify(&keypair.public_key)
50//! .expect("verification failed");
51//! ```
52//!
53//! ## Extracting key material
54//!
55//! ```
56//! use dryoc::sign::*;
57//!
58//! let seed = Seed::from([7u8; dryoc::constants::CRYPTO_SIGN_SEEDBYTES]);
59//! let keypair = SigningKeyPair::<PublicKey, SecretKey>::from_seed(&seed);
60//!
61//! let extracted_seed: Seed = keypair.to_seed();
62//! let extracted_public_key: PublicKey = keypair.to_public_key();
63//!
64//! assert_eq!(extracted_seed, seed);
65//! assert_eq!(extracted_public_key, keypair.public_key);
66//! ```
67//!
68//! ## Incremental (multi-part) interface
69//!
70//! ```
71//! use dryoc::sign::*;
72//!
73//! // Generate a random keypair, using default types
74//! let keypair = SigningKeyPair::<PublicKey, SecretKey>::generate();
75//!
76//! // Initialize the incremental signer interface
77//! let mut signer = IncrementalSigner::new();
78//! signer.update(b"This above all: to thine ownself be true.");
79//! signer.update(b"And it must follow, as the night the day,");
80//! signer.update(b"Thou canst not then be false to any man.");
81//!
82//! let signature: Signature = signer
83//! .finalize(&keypair.secret_key)
84//! .expect("signing failed");
85//! ```
86//!
87//! ## Additional resources
88//!
89//! * See <https://libsodium.gitbook.io/doc/public-key_cryptography/public-key_signatures>
90//! for additional details on public-key signatures
91//! * For secret-key based encryption, see
92//! [`DryocSecretBox`](crate::dryocsecretbox)
93//! * For stream encryption, see [`DryocStream`](crate::dryocstream)
94//! * See the [protected] mod for an example using the protected memory features
95
96use std::fmt;
97
98#[cfg(feature = "serde")]
99use serde::{Deserialize, Serialize};
100use subtle::ConstantTimeEq;
101use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
102
103use crate::classic::crypto_sign::{
104 SignerState, crypto_sign_detached, crypto_sign_ed25519_sk_to_pk,
105 crypto_sign_ed25519_sk_to_seed, crypto_sign_final_create, crypto_sign_final_verify,
106 crypto_sign_init, crypto_sign_keypair_inplace, crypto_sign_seed_keypair_inplace,
107 crypto_sign_update, crypto_sign_verify_detached,
108};
109use crate::constants::{
110 CRYPTO_SIGN_BYTES, CRYPTO_SIGN_PUBLICKEYBYTES, CRYPTO_SIGN_SECRETKEYBYTES,
111 CRYPTO_SIGN_SEEDBYTES,
112};
113use crate::error::Error;
114use crate::types::*;
115
116/// Stack-allocated public key for message signing.
117pub type PublicKey = StackByteArray<CRYPTO_SIGN_PUBLICKEYBYTES>;
118/// Stack-allocated secret key for message signing.
119pub type SecretKey = StackByteArray<CRYPTO_SIGN_SECRETKEYBYTES>;
120/// Stack-allocated seed for message signing.
121pub type Seed = StackByteArray<CRYPTO_SIGN_SEEDBYTES>;
122/// Stack-allocated signature for message signing.
123pub type Signature = StackByteArray<CRYPTO_SIGN_BYTES>;
124/// Heap-allocated message for message signing.
125pub type Message = Vec<u8>;
126
127/// Extracts the Ed25519 seed from a signing secret key.
128pub fn secret_key_to_seed<
129 SeedOut: NewByteArray<CRYPTO_SIGN_SEEDBYTES>,
130 SigningSecretKey: ByteArray<CRYPTO_SIGN_SECRETKEYBYTES>,
131>(
132 secret_key: &SigningSecretKey,
133) -> SeedOut {
134 let mut seed = SeedOut::new_byte_array();
135 crypto_sign_ed25519_sk_to_seed(seed.as_mut_array(), secret_key.as_array());
136 seed
137}
138
139/// Extracts the Ed25519 public key from a signing secret key.
140pub fn secret_key_to_public_key<
141 PublicKeyOut: NewByteArray<CRYPTO_SIGN_PUBLICKEYBYTES>,
142 SigningSecretKey: ByteArray<CRYPTO_SIGN_SECRETKEYBYTES>,
143>(
144 secret_key: &SigningSecretKey,
145) -> PublicKeyOut {
146 let mut public_key = PublicKeyOut::new_byte_array();
147 crypto_sign_ed25519_sk_to_pk(public_key.as_mut_array(), secret_key.as_array());
148 public_key
149}
150
151#[cfg_attr(
152 feature = "serde",
153 derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize, Clone)
154)]
155#[cfg_attr(not(feature = "serde"), derive(Zeroize, ZeroizeOnDrop, Clone))]
156/// An Ed25519 keypair for public-key signatures
157pub struct SigningKeyPair<
158 PublicKey: ByteArray<CRYPTO_SIGN_PUBLICKEYBYTES> + Zeroize,
159 SecretKey: ByteArray<CRYPTO_SIGN_SECRETKEYBYTES> + Zeroize,
160> {
161 /// Public key
162 pub public_key: PublicKey,
163 /// Secret key
164 pub secret_key: SecretKey,
165}
166
167impl<
168 PublicKey: ByteArray<CRYPTO_SIGN_PUBLICKEYBYTES> + Zeroize,
169 SecretKey: ByteArray<CRYPTO_SIGN_SECRETKEYBYTES> + Zeroize,
170> fmt::Debug for SigningKeyPair<PublicKey, SecretKey>
171{
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 f.debug_struct("SigningKeyPair")
174 .field("public_key", &"[REDACTED]")
175 .field("secret_key", &"[REDACTED]")
176 .finish()
177 }
178}
179
180impl<
181 PublicKey: NewByteArray<CRYPTO_SIGN_PUBLICKEYBYTES> + Zeroize,
182 SecretKey: NewByteArray<CRYPTO_SIGN_SECRETKEYBYTES> + Zeroize,
183> SigningKeyPair<PublicKey, SecretKey>
184{
185 /// Creates a new, empty signing keypair.
186 pub fn new() -> Self {
187 Self {
188 public_key: PublicKey::new_byte_array(),
189 secret_key: SecretKey::new_byte_array(),
190 }
191 }
192
193 /// Generates a random signing keypair.
194 pub fn generate() -> Self {
195 let mut public_key = PublicKey::new_byte_array();
196 let mut secret_key = SecretKey::new_byte_array();
197 crypto_sign_keypair_inplace(public_key.as_mut_array(), secret_key.as_mut_array());
198 Self {
199 public_key,
200 secret_key,
201 }
202 }
203
204 /// Generates a random signing keypair.
205 ///
206 /// Prefer [`generate`](Self::generate). `gen` is retained for compatibility
207 /// with older Rust editions.
208 #[deprecated(note = "use generate() instead")]
209 pub fn r#gen() -> Self {
210 Self::generate()
211 }
212
213 /// Derives a signing keypair from `secret_key`, and consumes it, returning
214 /// a new keypair.
215 pub fn from_secret_key(secret_key: SecretKey) -> Self {
216 let mut seed = Zeroizing::new([0u8; 32]);
217 seed.copy_from_slice(&secret_key.as_slice()[..32]);
218
219 Self::from_seed(&*seed)
220 }
221
222 /// Derives a signing keypair from `seed`, returning
223 /// a new keypair.
224 pub fn from_seed<Seed: ByteArray<CRYPTO_SIGN_SEEDBYTES>>(seed: &Seed) -> Self {
225 let mut public_key = PublicKey::new_byte_array();
226 let mut secret_key = SecretKey::new_byte_array();
227
228 crypto_sign_seed_keypair_inplace(
229 public_key.as_mut_array(),
230 secret_key.as_mut_array(),
231 seed.as_array(),
232 );
233
234 Self {
235 public_key,
236 secret_key,
237 }
238 }
239}
240
241impl<
242 PublicKey: ByteArray<CRYPTO_SIGN_PUBLICKEYBYTES> + Zeroize,
243 SecretKey: ByteArray<CRYPTO_SIGN_SECRETKEYBYTES> + Zeroize,
244> SigningKeyPair<PublicKey, SecretKey>
245{
246 /// Extracts the Ed25519 seed from this keypair's secret key.
247 pub fn to_seed<SeedOut: NewByteArray<CRYPTO_SIGN_SEEDBYTES>>(&self) -> SeedOut {
248 secret_key_to_seed(&self.secret_key)
249 }
250
251 /// Extracts the Ed25519 public key embedded in this keypair's secret key.
252 pub fn to_public_key<PublicKeyOut: NewByteArray<CRYPTO_SIGN_PUBLICKEYBYTES>>(
253 &self,
254 ) -> PublicKeyOut {
255 secret_key_to_public_key(&self.secret_key)
256 }
257}
258
259impl
260 SigningKeyPair<
261 StackByteArray<CRYPTO_SIGN_PUBLICKEYBYTES>,
262 StackByteArray<CRYPTO_SIGN_SECRETKEYBYTES>,
263 >
264{
265 /// Randomly generates a new signing keypair, using default types
266 /// (stack-allocated byte arrays). Provided for convenience.
267 pub fn generate_with_defaults() -> Self {
268 Self::generate()
269 }
270
271 /// Randomly generates a new signing keypair, using default types
272 /// (stack-allocated byte arrays). Provided for convenience.
273 ///
274 /// Prefer [`generate_with_defaults`](Self::generate_with_defaults). This
275 /// method is retained for compatibility.
276 #[deprecated(note = "use generate_with_defaults() instead")]
277 pub fn gen_with_defaults() -> Self {
278 Self::generate_with_defaults()
279 }
280}
281
282impl<
283 'a,
284 PublicKey: ByteArray<CRYPTO_SIGN_PUBLICKEYBYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
285 SecretKey: ByteArray<CRYPTO_SIGN_SECRETKEYBYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
286> SigningKeyPair<PublicKey, SecretKey>
287{
288 /// Constructs a new signing keypair from key slices, consuming them. Does
289 /// not check validity or authenticity of keypair.
290 ///
291 /// # Errors
292 ///
293 /// Returns an error if either slice has the wrong length for its key type,
294 /// or if the target key type rejects the key bytes.
295 pub fn from_slices(public_key: &'a [u8], secret_key: &'a [u8]) -> Result<Self, Error> {
296 validate_length!(
297 exact CRYPTO_SIGN_PUBLICKEYBYTES,
298 public_key.len(),
299 crate::ErrorContext::PublicKey
300 );
301 validate_length!(
302 exact CRYPTO_SIGN_SECRETKEYBYTES,
303 secret_key.len(),
304 crate::ErrorContext::SecretKey
305 );
306
307 Ok(Self {
308 public_key: PublicKey::try_from(public_key)
309 .map_err(|_| Error::invalid_key(crate::ErrorContext::PublicKey))?,
310 secret_key: SecretKey::try_from(secret_key)
311 .map_err(|_| Error::invalid_key(crate::ErrorContext::SecretKey))?,
312 })
313 }
314}
315
316#[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
317#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
318pub mod protected {
319 //! # Protected memory for [`SigningKeyPair`] and [`SignedMessage`]
320 //!
321 //! ## Example
322 //! ```
323 //! use dryoc::sign::SigningKeyPair;
324 //! use dryoc::sign::protected::*;
325 //!
326 //! // Generate a random keypair, using default types
327 //! let keypair = SigningKeyPair::generate_locked_keypair().expect("keypair generate failed");
328 //! let message = Message::from_slice_into_locked(
329 //! b"Fair is foul, and foul is fair: Hover through the fog and filthy air.",
330 //! )
331 //! .expect("message lock failed");
332 //!
333 //! // Sign the message, using default types (stack-allocated byte array, Vec<u8>)
334 //! let signed_message: LockedSignedMessage = keypair.sign(message).expect("signing failed");
335 //!
336 //! // Verify the message signature
337 //! signed_message
338 //! .verify(&keypair.public_key)
339 //! .expect("verification failed");
340 //! ```
341 use super::*;
342 pub use crate::protected::*;
343
344 /// Heap-allocated, page-aligned public-key for signed messages,
345 /// for use with protected memory.
346 pub type PublicKey = HeapByteArray<CRYPTO_SIGN_PUBLICKEYBYTES>;
347 /// Heap-allocated, page-aligned secret-key for signed messages,
348 /// for use with protected memory.
349 pub type SecretKey = HeapByteArray<CRYPTO_SIGN_SECRETKEYBYTES>;
350 /// Heap-allocated, page-aligned seed for signed messages,
351 /// for use with protected memory.
352 pub type Seed = HeapByteArray<CRYPTO_SIGN_SEEDBYTES>;
353 /// Heap-allocated, page-aligned signature for signed messages,
354 /// for use with protected memory.
355 pub type Signature = HeapByteArray<CRYPTO_SIGN_BYTES>;
356 /// Heap-allocated, page-aligned message for signed messages,
357 /// for use with protected memory.
358 pub type Message = HeapBytes;
359
360 /// Heap-allocated, page-aligned public/secret keypair for message signing,
361 /// for use with protected memory.
362 pub type LockedSigningKeyPair = SigningKeyPair<Locked<PublicKey>, Locked<SecretKey>>;
363 /// Heap-allocated, page-aligned signed message, for use with protected
364 /// memory.
365 pub type LockedSignedMessage = SignedMessage<Locked<Signature>, Locked<Message>>;
366
367 impl
368 SigningKeyPair<
369 Locked<HeapByteArray<CRYPTO_SIGN_PUBLICKEYBYTES>>,
370 Locked<HeapByteArray<CRYPTO_SIGN_SECRETKEYBYTES>>,
371 >
372 {
373 /// Returns a new locked signing keypair.
374 ///
375 /// # Errors
376 ///
377 /// Returns [`Error::Io`] if either allocation cannot be locked.
378 ///
379 /// # Panics
380 ///
381 /// Panics if either page-aligned allocation cannot be created or its
382 /// size cannot be represented with guard pages.
383 pub fn new_locked_keypair() -> Result<Self, Error> {
384 Ok(Self {
385 public_key: HeapByteArray::<CRYPTO_SIGN_PUBLICKEYBYTES>::new_locked()?,
386 secret_key: HeapByteArray::<CRYPTO_SIGN_SECRETKEYBYTES>::new_locked()?,
387 })
388 }
389
390 /// Returns a new randomly generated locked signing keypair.
391 ///
392 /// # Errors
393 ///
394 /// Returns [`Error::Io`] if either allocation cannot be locked.
395 ///
396 /// # Panics
397 ///
398 /// Panics if either page-aligned allocation cannot be created, its
399 /// size cannot be represented with guard pages, or the operating
400 /// system's random number generator fails.
401 pub fn generate_locked_keypair() -> Result<Self, Error> {
402 let mut res = Self::new_locked_keypair()?;
403
404 crypto_sign_keypair_inplace(
405 res.public_key.as_mut_array(),
406 res.secret_key.as_mut_array(),
407 );
408
409 Ok(res)
410 }
411
412 /// Returns a new randomly generated locked signing keypair.
413 ///
414 /// Prefer [`generate_locked_keypair`](Self::generate_locked_keypair).
415 /// This method is retained for compatibility.
416 ///
417 /// # Errors
418 ///
419 /// Returns the same errors as
420 /// [`generate_locked_keypair`](Self::generate_locked_keypair).
421 ///
422 /// # Panics
423 ///
424 /// Panics under the same conditions as
425 /// [`generate_locked_keypair`](Self::generate_locked_keypair).
426 #[deprecated(note = "use generate_locked_keypair() instead")]
427 pub fn gen_locked_keypair() -> Result<Self, Error> {
428 Self::generate_locked_keypair()
429 }
430 }
431
432 impl
433 SigningKeyPair<
434 LockedRO<HeapByteArray<CRYPTO_SIGN_PUBLICKEYBYTES>>,
435 LockedRO<HeapByteArray<CRYPTO_SIGN_SECRETKEYBYTES>>,
436 >
437 {
438 /// Returns a new randomly generated locked, read-only signing keypair.
439 ///
440 /// # Errors
441 ///
442 /// Returns [`Error::Io`] if either allocation cannot be locked or its
443 /// page permissions cannot be changed to read-only.
444 ///
445 /// # Panics
446 ///
447 /// Panics if either page-aligned allocation cannot be created, its
448 /// size cannot be represented with guard pages, or the operating
449 /// system's random number generator fails.
450 pub fn generate_readonly_locked_keypair() -> Result<Self, Error> {
451 let mut public_key = HeapByteArray::<CRYPTO_SIGN_PUBLICKEYBYTES>::new_locked()?;
452 let mut secret_key = HeapByteArray::<CRYPTO_SIGN_SECRETKEYBYTES>::new_locked()?;
453
454 crypto_sign_keypair_inplace(public_key.as_mut_array(), secret_key.as_mut_array());
455
456 let public_key = public_key.mprotect_readonly()?;
457 let secret_key = secret_key.mprotect_readonly()?;
458
459 Ok(Self {
460 public_key,
461 secret_key,
462 })
463 }
464
465 /// Returns a new randomly generated locked, read-only signing keypair.
466 ///
467 /// Prefer
468 /// [`generate_readonly_locked_keypair`](Self::generate_readonly_locked_keypair).
469 /// This method is retained for compatibility.
470 ///
471 /// # Errors
472 ///
473 /// Returns the same errors as
474 /// [`generate_readonly_locked_keypair`](Self::generate_readonly_locked_keypair).
475 ///
476 /// # Panics
477 ///
478 /// Panics under the same conditions as
479 /// [`generate_readonly_locked_keypair`](Self::generate_readonly_locked_keypair).
480 #[deprecated(note = "use generate_readonly_locked_keypair() instead")]
481 pub fn gen_readonly_locked_keypair() -> Result<Self, Error> {
482 Self::generate_readonly_locked_keypair()
483 }
484 }
485}
486
487#[cfg_attr(
488 feature = "serde",
489 derive(Zeroize, Clone, Debug, Serialize, Deserialize)
490)]
491#[cfg_attr(not(feature = "serde"), derive(Zeroize, Clone, Debug))]
492/// A signed message, for use with [`SigningKeyPair`].
493pub struct SignedMessage<
494 Signature: ByteArray<CRYPTO_SIGN_BYTES> + Zeroize,
495 Message: Bytes + Zeroize,
496> {
497 signature: Signature,
498 message: Message,
499}
500
501/// [Vec]-based signed message.
502pub type VecSignedMessage = SignedMessage<Signature, Vec<u8>>;
503
504impl<
505 PublicKey: ByteArray<CRYPTO_SIGN_PUBLICKEYBYTES> + Zeroize,
506 SecretKey: ByteArray<CRYPTO_SIGN_SECRETKEYBYTES> + Zeroize,
507> SigningKeyPair<PublicKey, SecretKey>
508{
509 /// Signs `message` using this keypair, consuming the message, and returning
510 /// a new [`SignedMessage`]. The type of `message` should match that of the
511 /// target signed message.
512 ///
513 /// # Errors
514 ///
515 /// The fixed-size signature and secret-key types satisfy the current
516 /// implementation's requirements, so this function does not return an
517 /// error for valid type implementations. The [`Result`] is retained for
518 /// compatibility with the underlying signing API.
519 pub fn sign<Signature: NewByteArray<CRYPTO_SIGN_BYTES> + Zeroize, Message: Bytes + Zeroize>(
520 &self,
521 message: Message,
522 ) -> Result<SignedMessage<Signature, Message>, Error> {
523 let mut signature = Signature::new_byte_array();
524 crypto_sign_detached(
525 signature.as_mut_array(),
526 message.as_slice(),
527 self.secret_key.as_array(),
528 )?;
529
530 Ok(SignedMessage::<Signature, Message> { signature, message })
531 }
532
533 /// Signs `message`, putting the result into a [`Vec`]. Convenience wrapper
534 /// for [`SigningKeyPair::sign`].
535 ///
536 /// # Errors
537 ///
538 /// The default fixed-size types satisfy the current implementation's
539 /// requirements, so this function does not return an error in normal use.
540 /// The [`Result`] is retained for API compatibility.
541 pub fn sign_with_defaults<Message: Bytes>(
542 &self,
543 message: Message,
544 ) -> Result<SignedMessage<StackByteArray<CRYPTO_SIGN_BYTES>, Vec<u8>>, Error> {
545 self.sign(Vec::from(message.as_slice()))
546 }
547}
548
549impl Default for SigningKeyPair<PublicKey, SecretKey> {
550 fn default() -> Self {
551 Self::new()
552 }
553}
554
555/// Multi-part (incremental) interface for [`SigningKeyPair`].
556pub struct IncrementalSigner {
557 state: SignerState,
558}
559
560impl IncrementalSigner {
561 /// Returns a new incremental signer instance.
562 pub fn new() -> Self {
563 Self {
564 state: crypto_sign_init(),
565 }
566 }
567
568 /// Updates the state for this incremental signer with `message`.
569 pub fn update<Message: Bytes>(&mut self, message: &Message) {
570 crypto_sign_update(&mut self.state, message.as_slice())
571 }
572
573 /// Finalizes this incremental signer, returning the signature upon
574 /// success.
575 ///
576 /// # Errors
577 ///
578 /// The fixed-size signature and secret-key types satisfy the current
579 /// implementation's requirements, so this function does not return an
580 /// error for valid type implementations. The [`Result`] is retained for
581 /// compatibility with the underlying signing API.
582 pub fn finalize<
583 Signature: NewByteArray<CRYPTO_SIGN_BYTES>,
584 SecretKey: ByteArray<CRYPTO_SIGN_SECRETKEYBYTES>,
585 >(
586 self,
587 secret_key: &SecretKey,
588 ) -> Result<Signature, Error> {
589 let mut signature = Signature::new_byte_array();
590
591 crypto_sign_final_create(self.state, signature.as_mut_array(), secret_key.as_array())?;
592
593 Ok(signature)
594 }
595
596 /// Verifies `signature` as a valid signature for this signer.
597 ///
598 /// # Errors
599 ///
600 /// Returns an error if `signature` is not valid for the accumulated
601 /// message and `public_key`.
602 pub fn verify<
603 Signature: ByteArray<CRYPTO_SIGN_BYTES>,
604 PublicKey: ByteArray<CRYPTO_SIGN_PUBLICKEYBYTES>,
605 >(
606 self,
607 signature: &Signature,
608 public_key: &PublicKey,
609 ) -> Result<(), Error> {
610 crypto_sign_final_verify(self.state, signature.as_array(), public_key.as_array())?;
611
612 Ok(())
613 }
614}
615
616impl Default for IncrementalSigner {
617 fn default() -> Self {
618 Self::new()
619 }
620}
621
622impl<Signature: ByteArray<CRYPTO_SIGN_BYTES> + Zeroize, Message: Bytes + Zeroize>
623 SignedMessage<Signature, Message>
624{
625 /// Verifies that this signed message is valid for `public_key`.
626 ///
627 /// # Errors
628 ///
629 /// Returns an error if the signature is not valid for the message and
630 /// `public_key`.
631 pub fn verify<PublicKey: ByteArray<CRYPTO_SIGN_PUBLICKEYBYTES>>(
632 &self,
633 public_key: &PublicKey,
634 ) -> Result<(), Error> {
635 crypto_sign_verify_detached(
636 self.signature.as_array(),
637 self.message.as_slice(),
638 public_key.as_array(),
639 )
640 }
641}
642
643impl<
644 'a,
645 Signature: ByteArray<CRYPTO_SIGN_BYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
646 Message: Bytes + From<&'a [u8]> + Zeroize,
647> SignedMessage<Signature, Message>
648{
649 /// Initializes a [`SignedMessage`] from a slice. Expects the first
650 /// [`CRYPTO_SIGN_BYTES`] bytes to contain the message signature,
651 /// with the remaining bytes containing the message.
652 ///
653 /// # Errors
654 ///
655 /// Returns an error if `bytes` is shorter than a signature or the
656 /// signature cannot be converted to the requested output type.
657 pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
658 if bytes.len() < CRYPTO_SIGN_BYTES {
659 Err(
660 length_error!(crate::ErrorContext::SignedMessage, bytes.len(), min CRYPTO_SIGN_BYTES),
661 )
662 } else {
663 let (signature, message) = bytes.split_at(CRYPTO_SIGN_BYTES);
664 Ok(Self {
665 signature: Signature::try_from(signature)
666 .map_err(|_| Error::invalid_encoding(crate::ErrorContext::Signature))?,
667 message: Message::from(message),
668 })
669 }
670 }
671}
672
673impl<Signature: ByteArray<CRYPTO_SIGN_BYTES> + Zeroize, Message: Bytes + Zeroize>
674 SignedMessage<Signature, Message>
675{
676 /// Returns a new box with `tag`, `data` and (optional) `ephemeral_pk`,
677 /// consuming each.
678 pub fn from_parts(signature: Signature, message: Message) -> Self {
679 Self { signature, message }
680 }
681
682 /// Copies `self` into a new [`Vec`]
683 pub fn to_vec(&self) -> Vec<u8> {
684 self.to_bytes()
685 }
686
687 /// Moves the tag, data, and (optional) ephemeral public key out of this
688 /// instance, returning them as a tuple.
689 pub fn into_parts(self) -> (Signature, Message) {
690 (self.signature, self.message)
691 }
692
693 /// Copies `self` into the target. Can be used with protected memory.
694 pub fn to_bytes<Bytes: NewBytes + ResizableBytes>(&self) -> Bytes {
695 let mut data = Bytes::new_bytes();
696
697 data.resize(self.signature.len() + self.message.len(), 0);
698 let s = data.as_mut_slice();
699 s[..CRYPTO_SIGN_BYTES].copy_from_slice(self.signature.as_slice());
700 s[CRYPTO_SIGN_BYTES..].copy_from_slice(self.message.as_slice());
701
702 data
703 }
704}
705
706impl<
707 PublicKey: ByteArray<CRYPTO_SIGN_PUBLICKEYBYTES> + Zeroize,
708 SecretKey: ByteArray<CRYPTO_SIGN_SECRETKEYBYTES> + Zeroize,
709> PartialEq<SigningKeyPair<PublicKey, SecretKey>> for SigningKeyPair<PublicKey, SecretKey>
710{
711 fn eq(&self, other: &Self) -> bool {
712 self.public_key
713 .as_slice()
714 .ct_eq(other.public_key.as_slice())
715 .unwrap_u8()
716 == 1
717 && self
718 .secret_key
719 .as_slice()
720 .ct_eq(other.secret_key.as_slice())
721 .unwrap_u8()
722 == 1
723 }
724}
725
726impl<Signature: ByteArray<CRYPTO_SIGN_BYTES> + Zeroize, Message: Bytes + Zeroize>
727 PartialEq<SignedMessage<Signature, Message>> for SignedMessage<Signature, Message>
728{
729 fn eq(&self, other: &Self) -> bool {
730 self.signature
731 .as_slice()
732 .ct_eq(other.signature.as_slice())
733 .unwrap_u8()
734 == 1
735 && self
736 .message
737 .as_slice()
738 .ct_eq(other.message.as_slice())
739 .unwrap_u8()
740 == 1
741 }
742}
743
744#[cfg(test)]
745mod tests {
746 use super::*;
747
748 #[test]
749 fn signing_keypair_debug_redacts_keys_and_secret_key_reconstructs_keypair() {
750 let keypair = SigningKeyPair::<PublicKey, SecretKey>::generate();
751 let debug = format!("{keypair:?}");
752 let reconstructed = SigningKeyPair::from_secret_key(keypair.secret_key.clone());
753
754 assert_eq!(
755 debug,
756 "SigningKeyPair { public_key: \"[REDACTED]\", secret_key: \"[REDACTED]\" }"
757 );
758 assert_eq!(reconstructed, keypair);
759 }
760
761 #[test]
762 fn test_message_signing() {
763 let keypair = SigningKeyPair::generate_with_defaults();
764 let message = b"hello my frens";
765
766 let signed_message = keypair.sign_with_defaults(message).expect("signing failed");
767
768 signed_message
769 .verify(&keypair.public_key)
770 .expect("verification failed");
771 }
772
773 #[test]
774 fn test_secret_key_extraction() {
775 let seed = Seed::generate();
776 let keypair = SigningKeyPair::<PublicKey, SecretKey>::from_seed(&seed);
777
778 let extracted_seed: Seed = keypair.to_seed();
779 let extracted_public_key: PublicKey = keypair.to_public_key();
780 assert_eq!(extracted_seed, seed);
781 assert_eq!(extracted_public_key, keypair.public_key);
782
783 let extracted_seed_vec: Vec<u8> = secret_key_to_seed(&keypair.secret_key);
784 let extracted_public_key_vec: Vec<u8> = secret_key_to_public_key(&keypair.secret_key);
785 assert_eq!(extracted_seed_vec.as_slice(), seed.as_slice());
786 assert_eq!(
787 extracted_public_key_vec.as_slice(),
788 keypair.public_key.as_slice()
789 );
790 }
791}