dryoc/classic/
crypto_sign.rs

1//! # Public-key signatures
2//!
3//! This module implements libsodium's public-key signatures, based on Ed25519.
4//!
5//! ## Classic API example
6//!
7//! ```
8//! use dryoc::classic::crypto_sign::*;
9//! use dryoc::constants::CRYPTO_SIGN_BYTES;
10//!
11//! // Generate a random signing keypair
12//! let (public_key, secret_key) = crypto_sign_keypair();
13//! let message = b"These violent delights have violent ends...";
14//!
15//! // Signed message buffer needs to be correct length
16//! let mut signed_message = vec![0u8; message.len() + CRYPTO_SIGN_BYTES];
17//!
18//! // Sign the message, placing the result into `signed_message`
19//! crypto_sign(&mut signed_message, message, &secret_key).expect("sign failed");
20//!
21//! // Allocate a new buffer for opening the message
22//! let mut opened_message = vec![0u8; message.len()];
23//!
24//! // Open the signed message, verifying the signature
25//! crypto_sign_open(&mut opened_message, &signed_message, &public_key).expect("verify failed");
26//!
27//! assert_eq!(&opened_message, message);
28//!
29//! // Create an invalid message
30//! let mut invalid_signed_message = signed_message.clone();
31//! invalid_signed_message[5] = !invalid_signed_message[5];
32//!
33//! // An invalid message can't be verified
34//! crypto_sign_open(&mut opened_message, &invalid_signed_message, &public_key)
35//!     .expect_err("open should not succeed");
36//! ```
37//!
38//! ## Classic API example, detached mode
39//!
40//! ```
41//! use dryoc::classic::crypto_sign::*;
42//! use dryoc::constants::CRYPTO_SIGN_BYTES;
43//!
44//! // Generate a random signing keypair
45//! let (public_key, secret_key) = crypto_sign_keypair();
46//! let message = b"Brevity is the soul of wit.";
47//! let mut signature = [0u8; CRYPTO_SIGN_BYTES];
48//!
49//! // Sign our message
50//! crypto_sign_detached(&mut signature, message, &secret_key).expect("sign failed");
51//!
52//! // Verify the signature
53//! crypto_sign_verify_detached(&signature, message, &public_key).expect("verify failed");
54//! ```
55
56use super::crypto_sign_ed25519::*;
57pub use super::crypto_sign_ed25519::{PublicKey, SecretKey};
58use crate::constants::CRYPTO_SIGN_BYTES;
59use crate::error::Error;
60
61/// In-place variant of [`crypto_sign_keypair`].
62pub fn crypto_sign_keypair_inplace(public_key: &mut PublicKey, secret_key: &mut SecretKey) {
63    crypto_sign_ed25519_keypair_inplace(public_key, secret_key)
64}
65
66/// In-place variant of [`crypto_sign_seed_keypair`].
67pub fn crypto_sign_seed_keypair_inplace(
68    public_key: &mut PublicKey,
69    secret_key: &mut SecretKey,
70    seed: &[u8; 32],
71) {
72    crypto_sign_ed25519_seed_keypair_inplace(public_key, secret_key, seed)
73}
74
75/// Randomly generates a new Ed25519 `(PublicKey, SecretKey)` keypair that can
76/// be used for message signing.
77pub fn crypto_sign_keypair() -> (PublicKey, SecretKey) {
78    crypto_sign_ed25519_keypair()
79}
80
81/// Returns a keypair derived from `seed`, which can be used for message
82/// signing.
83pub fn crypto_sign_seed_keypair(seed: &[u8; 32]) -> (PublicKey, SecretKey) {
84    crypto_sign_ed25519_seed_keypair(seed)
85}
86
87/// Signs `message`, placing the result into `signed_message`. The length of
88/// `signed_message` should be the length of the message plus
89/// [`CRYPTO_SIGN_BYTES`].
90///
91/// This function is compatible with libsodium`s `crypto_sign`, however the
92/// `ED25519_NONDETERMINISTIC` feature is not supported.
93pub fn crypto_sign(
94    signed_message: &mut [u8],
95    message: &[u8],
96    secret_key: &SecretKey,
97) -> Result<(), Error> {
98    if signed_message.len() != message.len() + CRYPTO_SIGN_BYTES {
99        Err(dryoc_error!(format!(
100            "signed_message length incorrect (expect {}, got {})",
101            message.len() + CRYPTO_SIGN_BYTES,
102            signed_message.len()
103        )))
104    } else {
105        crypto_sign_ed25519(signed_message, message, secret_key)
106    }
107}
108
109/// Verifies the signature of `signed_message`, placing the result into
110/// `message`. The length of `message` should be the length of the signed
111/// message minus [`CRYPTO_SIGN_BYTES`].
112///
113/// This function is compatible with libsodium`s `crypto_sign_open`, however the
114/// `ED25519_NONDETERMINISTIC` feature is not supported.
115pub fn crypto_sign_open(
116    message: &mut [u8],
117    signed_message: &[u8],
118    public_key: &PublicKey,
119) -> Result<(), Error> {
120    if signed_message.len() < CRYPTO_SIGN_BYTES {
121        Err(dryoc_error!(format!(
122            "signed_message length invalid ({} < {})",
123            signed_message.len(),
124            CRYPTO_SIGN_BYTES,
125        )))
126    } else if message.len() != signed_message.len() - CRYPTO_SIGN_BYTES {
127        Err(dryoc_error!(format!(
128            "message length incorrect (expect {}, got {})",
129            signed_message.len() - CRYPTO_SIGN_BYTES,
130            message.len()
131        )))
132    } else {
133        crypto_sign_ed25519_open(message, signed_message, public_key)
134    }
135}
136
137/// Signs `message`, placing the signature into `signature` upon success.
138/// Detached variant of [`crypto_sign_open`].
139///
140/// This function is compatible with libsodium`s `crypto_sign_detached`, however
141/// the `ED25519_NONDETERMINISTIC` feature is not supported.
142pub fn crypto_sign_detached(
143    signature: &mut Signature,
144    message: &[u8],
145    secret_key: &SecretKey,
146) -> Result<(), Error> {
147    crypto_sign_ed25519_detached(signature, message, secret_key)
148}
149
150/// Verifies that `signature` is a valid signature for `message` using the given
151/// `public_key`.
152///
153/// This function is compatible with libsodium`s `crypto_sign_verify_detached`,
154/// however the `ED25519_NONDETERMINISTIC` feature is not supported.
155pub fn crypto_sign_verify_detached(
156    signature: &Signature,
157    message: &[u8],
158    public_key: &PublicKey,
159) -> Result<(), Error> {
160    crypto_sign_ed25519_verify_detached(signature, message, public_key)
161}
162
163/// State for incremental signing interface.
164pub struct SignerState {
165    state: Ed25519SignerState,
166}
167
168/// Initializes the incremental signing interface.
169pub fn crypto_sign_init() -> SignerState {
170    SignerState {
171        state: crypto_sign_ed25519ph_init(),
172    }
173}
174
175/// Updates the signature for `state` with `message`.
176pub fn crypto_sign_update(state: &mut SignerState, message: &[u8]) {
177    crypto_sign_ed25519ph_update(&mut state.state, message)
178}
179
180/// Finalizes the incremental signature for `state`, using `secret_key`, copying
181/// the result into `signature` upon success, and consuming the state.
182pub fn crypto_sign_final_create(
183    state: SignerState,
184    signature: &mut Signature,
185    secret_key: &SecretKey,
186) -> Result<(), Error> {
187    crypto_sign_ed25519ph_final_create(state.state, signature, secret_key)
188}
189
190/// Verifies the computed signature for `state` and `public_key` matches
191/// `signature`, consuming the state.
192pub fn crypto_sign_final_verify(
193    state: SignerState,
194    signature: &Signature,
195    public_key: &PublicKey,
196) -> Result<(), Error> {
197    crypto_sign_ed25519ph_final_verify(state.state, signature, public_key)
198}
199
200#[cfg(test)]
201mod tests {
202    use super::*;
203
204    #[test]
205    fn test_crypto_sign() {
206        use base64::Engine as _;
207        use base64::engine::general_purpose;
208        use sodiumoxide::crypto::sign;
209
210        for _ in 0..10 {
211            let (public_key, secret_key) = crypto_sign_keypair();
212            let message = b"important message";
213            let mut signed_message = vec![0u8; message.len() + CRYPTO_SIGN_BYTES];
214            crypto_sign(&mut signed_message, message, &secret_key).expect("sign failed");
215
216            let so_signed_message = sign::sign(
217                message,
218                &sign::SecretKey::from_slice(&secret_key).expect("secret key failed"),
219            );
220
221            assert_eq!(
222                general_purpose::STANDARD.encode(&signed_message),
223                general_purpose::STANDARD.encode(&so_signed_message)
224            );
225
226            let so_m = sign::verify(
227                &signed_message,
228                &sign::PublicKey::from_slice(&public_key).expect("public key failed"),
229            )
230            .expect("verify failed");
231
232            assert_eq!(so_m, message);
233        }
234    }
235
236    #[test]
237    fn test_crypto_sign_open() {
238        use base64::Engine as _;
239        use base64::engine::general_purpose;
240        use sodiumoxide::crypto::sign;
241
242        for _ in 0..10 {
243            let (public_key, secret_key) = crypto_sign_keypair();
244            let message = b"important message";
245            let mut signed_message = vec![0u8; message.len() + CRYPTO_SIGN_BYTES];
246            crypto_sign(&mut signed_message, message, &secret_key).expect("sign failed");
247
248            let so_signed_message = sign::sign(
249                message,
250                &sign::SecretKey::from_slice(&secret_key).expect("secret key failed"),
251            );
252
253            assert_eq!(
254                general_purpose::STANDARD.encode(&signed_message),
255                general_purpose::STANDARD.encode(&so_signed_message)
256            );
257
258            let so_m = sign::verify(
259                &signed_message,
260                &sign::PublicKey::from_slice(&public_key).expect("public key failed"),
261            )
262            .expect("verify failed");
263
264            assert_eq!(so_m, message);
265
266            let mut opened_message = vec![0u8; message.len()];
267
268            crypto_sign_open(&mut opened_message, &signed_message, &public_key)
269                .expect("verify failed");
270
271            assert_eq!(opened_message, message);
272        }
273    }
274
275    #[test]
276    fn test_crypto_sign_detached() {
277        use sodiumoxide::crypto::sign;
278
279        for _ in 0..10 {
280            let (public_key, secret_key) = crypto_sign_keypair();
281            let message = b"important message";
282            let mut signature = [0u8; CRYPTO_SIGN_BYTES];
283            crypto_sign_detached(&mut signature, message, &secret_key).expect("sign failed");
284
285            assert!(sign::verify_detached(
286                &sign::ed25519::Signature::from_bytes(&signature).expect("secret key failed"),
287                message,
288                &sign::PublicKey::from_slice(&public_key).expect("public key failed"),
289            ));
290
291            crypto_sign_verify_detached(&signature, message, &public_key).expect("verify failed");
292        }
293    }
294
295    #[test]
296    fn test_crypto_sign_incremental() {
297        use sodiumoxide::crypto::sign;
298
299        use crate::rng::copy_randombytes;
300
301        for _ in 0..10 {
302            let (public_key, secret_key) = crypto_sign_keypair();
303            let mut signer = crypto_sign_init();
304            let mut verifier = crypto_sign_init();
305
306            let mut so_signer = sign::State::init();
307            let mut so_verifier = sign::State::init();
308
309            for _ in 0..3 {
310                let mut randos = vec![0u8; 100];
311                copy_randombytes(&mut randos);
312
313                crypto_sign_update(&mut signer, &randos);
314                crypto_sign_update(&mut verifier, &randos);
315
316                so_signer.update(&randos);
317                so_verifier.update(&randos);
318            }
319
320            let mut signature = [0u8; CRYPTO_SIGN_BYTES];
321            crypto_sign_final_create(signer, &mut signature, &secret_key)
322                .expect("final create failed");
323
324            let so_signature = so_signer
325                .finalize(&sign::SecretKey::from_slice(&secret_key).expect("secret key failed"));
326
327            assert_eq!(signature, so_signature.to_bytes());
328
329            crypto_sign_final_verify(verifier, &so_signature.to_bytes(), &public_key)
330                .expect("verify failed");
331
332            assert!(so_signer.verify(
333                &sign::ed25519::Signature::from_bytes(&signature).expect("secret key failed"),
334                &sign::PublicKey::from_slice(&public_key).expect("public key failed"),
335            ));
336        }
337    }
338}