Skip to main content

rns_crypto/
identity.rs

1use alloc::vec::Vec;
2use core::fmt;
3
4use crate::ed25519::{Ed25519PrivateKey, Ed25519PublicKey};
5use crate::hkdf;
6use crate::sha256;
7use crate::token::{Token, TokenError};
8use crate::x25519::{X25519PrivateKey, X25519PublicKey};
9use crate::Rng;
10
11pub const KEYSIZE: usize = 512; // bits
12pub const DERIVED_KEY_LENGTH: usize = 64; // bytes
13pub const TRUNCATED_HASHLENGTH: usize = 128; // bits (16 bytes)
14
15#[derive(Debug)]
16pub enum CryptoError {
17    NoPrivateKey,
18    NoPublicKey,
19    TokenError(TokenError),
20    HkdfError(hkdf::HkdfError),
21    InvalidCiphertext,
22    InvalidPublicKey,
23}
24
25impl fmt::Display for CryptoError {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        match self {
28            CryptoError::NoPrivateKey => write!(f, "No private key"),
29            CryptoError::NoPublicKey => write!(f, "No public key"),
30            CryptoError::TokenError(e) => write!(f, "Token error: {}", e),
31            CryptoError::HkdfError(e) => write!(f, "HKDF error: {}", e),
32            CryptoError::InvalidCiphertext => write!(f, "Invalid ciphertext"),
33            CryptoError::InvalidPublicKey => write!(f, "Invalid public key"),
34        }
35    }
36}
37
38#[cfg(feature = "std")]
39impl std::error::Error for CryptoError {}
40
41pub use crate::stamp_value::{stamp_value, StampCost, StampValue};
42
43/// Generate an X25519 keypair whose public key meets `stamp_cost` over
44/// `material`, mirroring upstream `generate_stamped_key`.
45pub fn generate_stamped_key(
46    rng: &mut dyn Rng,
47    material: &[u8],
48    stamp_cost: StampCost,
49) -> (X25519PrivateKey, X25519PublicKey, StampValue) {
50    generate_stamped_key_until(rng, material, stamp_cost, &mut || false)
51        .expect("stamped key generation without a stop condition always completes")
52}
53
54/// Like [`generate_stamped_key`], but gives up and returns `None` as soon as
55/// `should_stop` reports true. The search is unbounded, so callers that run
56/// it on a latency-sensitive path must supply a deadline or cancellation flag,
57/// as upstream does with `LINK_STAMP_TIMEOUT` and `cancel_work`.
58pub fn generate_stamped_key_until(
59    rng: &mut dyn Rng,
60    material: &[u8],
61    stamp_cost: StampCost,
62    should_stop: &mut dyn FnMut() -> bool,
63) -> Option<(X25519PrivateKey, X25519PublicKey, StampValue)> {
64    loop {
65        if should_stop() {
66            return None;
67        }
68        let prv = X25519PrivateKey::generate(rng);
69        let public = prv.public_key();
70        let mut input = Vec::with_capacity(material.len() + 32);
71        input.extend_from_slice(material);
72        input.extend_from_slice(&public.public_bytes());
73        let product = crate::sha256::sha256(&input);
74        if stamp_cost.admits(&product) {
75            let value = crate::stamp_value::value_of_product(&product);
76            return Some((prv, public, value));
77        }
78    }
79}
80
81pub struct Identity {
82    prv: Option<X25519PrivateKey>,
83    sig_prv: Option<Ed25519PrivateKey>,
84    pub_key: Option<X25519PublicKey>,
85    sig_pub: Option<Ed25519PublicKey>,
86    hash: [u8; 16],
87}
88
89impl Identity {
90    pub fn new(rng: &mut dyn Rng) -> Self {
91        let prv = X25519PrivateKey::generate(rng);
92        let sig_prv = Ed25519PrivateKey::generate(rng);
93
94        let pub_key = prv.public_key();
95        let sig_pub = sig_prv.public_key();
96
97        let mut pub_bytes = [0u8; 64];
98        pub_bytes[..32].copy_from_slice(&pub_key.public_bytes());
99        pub_bytes[32..].copy_from_slice(&sig_pub.public_bytes());
100
101        let hash = truncated_hash(&pub_bytes);
102
103        Identity {
104            prv: Some(prv),
105            sig_prv: Some(sig_prv),
106            pub_key: Some(pub_key),
107            sig_pub: Some(sig_pub),
108            hash,
109        }
110    }
111
112    /// Create an identity whose X25519 public key carries a stamp of at least
113    /// `value` over the Ed25519 public key, mirroring upstream
114    /// `Identity(value=n)`.
115    pub fn new_stamped(rng: &mut dyn Rng, value: StampCost) -> Self {
116        let sig_prv = Ed25519PrivateKey::generate(rng);
117        let sig_pub = sig_prv.public_key();
118        let (prv, pub_key, _value) = generate_stamped_key(rng, &sig_pub.public_bytes(), value);
119
120        let mut pub_bytes = [0u8; 64];
121        pub_bytes[..32].copy_from_slice(&pub_key.public_bytes());
122        pub_bytes[32..].copy_from_slice(&sig_pub.public_bytes());
123        let hash = truncated_hash(&pub_bytes);
124
125        Identity {
126            prv: Some(prv),
127            sig_prv: Some(sig_prv),
128            pub_key: Some(pub_key),
129            sig_pub: Some(sig_pub),
130            hash,
131        }
132    }
133
134    /// Stamp value carried by this identity's public key, if available.
135    pub fn get_value(&self) -> Option<StampValue> {
136        match (&self.sig_pub, &self.pub_key) {
137            (Some(sig_pub), Some(pub_key)) => Some(stamp_value(
138                &sig_pub.public_bytes(),
139                &pub_key.public_bytes(),
140            )),
141            _ => None,
142        }
143    }
144
145    /// Load a complete private identity from its fixed-width key material.
146    ///
147    /// Valid input is represented by the 64-byte type itself, so successful
148    /// construction returns `Self` directly instead of a fall-through status.
149    pub fn from_private_key(prv_bytes: &[u8; 64]) -> Self {
150        let x_prv_bytes: [u8; 32] = prv_bytes[..32].try_into().unwrap();
151        let ed_seed: [u8; 32] = prv_bytes[32..].try_into().unwrap();
152
153        let prv = X25519PrivateKey::from_bytes(&x_prv_bytes);
154        let sig_prv = Ed25519PrivateKey::from_bytes(&ed_seed);
155
156        let pub_key = prv.public_key();
157        let sig_pub = sig_prv.public_key();
158
159        let mut pub_bytes = [0u8; 64];
160        pub_bytes[..32].copy_from_slice(&pub_key.public_bytes());
161        pub_bytes[32..].copy_from_slice(&sig_pub.public_bytes());
162
163        let hash = truncated_hash(&pub_bytes);
164
165        Identity {
166            prv: Some(prv),
167            sig_prv: Some(sig_prv),
168            pub_key: Some(pub_key),
169            sig_pub: Some(sig_pub),
170            hash,
171        }
172    }
173
174    /// Load a public-only identity from fixed-width public key material.
175    ///
176    /// Any 32 bytes form a valid X25519 public key, but the Ed25519 half must
177    /// decode to a valid curve point; otherwise this returns
178    /// `CryptoError::InvalidPublicKey`.
179    pub fn from_public_key(pub_bytes: &[u8; 64]) -> Result<Self, CryptoError> {
180        let x_pub_bytes: [u8; 32] = pub_bytes[..32].try_into().unwrap();
181        let ed_pub_bytes: [u8; 32] = pub_bytes[32..].try_into().unwrap();
182
183        let pub_key = X25519PublicKey::from_bytes(&x_pub_bytes);
184        let sig_pub = Ed25519PublicKey::from_bytes(&ed_pub_bytes)
185            .map_err(|_| CryptoError::InvalidPublicKey)?;
186
187        let hash = truncated_hash(pub_bytes);
188
189        Ok(Identity {
190            prv: None,
191            sig_prv: None,
192            pub_key: Some(pub_key),
193            sig_pub: Some(sig_pub),
194            hash,
195        })
196    }
197
198    pub fn get_private_key(&self) -> Option<[u8; 64]> {
199        match (&self.prv, &self.sig_prv) {
200            (Some(prv), Some(sig_prv)) => {
201                let mut result = [0u8; 64];
202                result[..32].copy_from_slice(&prv.private_bytes());
203                result[32..].copy_from_slice(&sig_prv.private_bytes());
204                Some(result)
205            }
206            _ => None,
207        }
208    }
209
210    pub fn get_public_key(&self) -> Option<[u8; 64]> {
211        match (&self.pub_key, &self.sig_pub) {
212            (Some(pub_key), Some(sig_pub)) => {
213                let mut result = [0u8; 64];
214                result[..32].copy_from_slice(&pub_key.public_bytes());
215                result[32..].copy_from_slice(&sig_pub.public_bytes());
216                Some(result)
217            }
218            _ => None,
219        }
220    }
221
222    pub fn hash(&self) -> &[u8; 16] {
223        &self.hash
224    }
225
226    pub fn encrypt(&self, plaintext: &[u8], rng: &mut dyn Rng) -> Result<Vec<u8>, CryptoError> {
227        let pub_key = self.pub_key.as_ref().ok_or(CryptoError::NoPublicKey)?;
228        self.encrypt_to_public_key(plaintext, pub_key, rng)
229    }
230
231    pub fn encrypt_with_ratchet(
232        &self,
233        plaintext: &[u8],
234        ratchet: Option<&[u8; 32]>,
235        rng: &mut dyn Rng,
236    ) -> Result<Vec<u8>, CryptoError> {
237        match ratchet {
238            Some(ratchet_pub_bytes) => {
239                let ratchet_pub = X25519PublicKey::from_bytes(ratchet_pub_bytes);
240                self.encrypt_to_public_key(plaintext, &ratchet_pub, rng)
241            }
242            None => self.encrypt(plaintext, rng),
243        }
244    }
245
246    fn encrypt_to_public_key(
247        &self,
248        plaintext: &[u8],
249        target_public_key: &X25519PublicKey,
250        rng: &mut dyn Rng,
251    ) -> Result<Vec<u8>, CryptoError> {
252        encrypt_to(&self.hash, target_public_key, plaintext, rng)
253    }
254
255    /// [`encrypt_with_ratchet`](Self::encrypt_with_ratchet) for the identity
256    /// whose 64-byte public key is `public`, without building the identity:
257    /// encrypting needs only the X25519 half and the identity hash, so the
258    /// Ed25519 half is not decompressed.
259    pub fn encrypt_for_public_key(
260        public: &[u8; 64],
261        plaintext: &[u8],
262        ratchet: Option<&[u8; 32]>,
263        rng: &mut dyn Rng,
264    ) -> Result<Vec<u8>, CryptoError> {
265        let x_pub: [u8; 32] = public[..32].try_into().unwrap();
266        let target = X25519PublicKey::from_bytes(ratchet.unwrap_or(&x_pub));
267        encrypt_to(&truncated_hash(public), &target, plaintext, rng)
268    }
269
270    /// Encrypt with a specific ephemeral key and IV for deterministic testing
271    pub fn encrypt_deterministic(
272        &self,
273        plaintext: &[u8],
274        ephemeral_prv: &[u8; 32],
275        iv: &[u8; 16],
276    ) -> Result<Vec<u8>, CryptoError> {
277        let pub_key = self.pub_key.as_ref().ok_or(CryptoError::NoPublicKey)?;
278
279        let ephemeral = X25519PrivateKey::from_bytes(ephemeral_prv);
280        let ephemeral_pub_bytes = ephemeral.public_key().public_bytes();
281        let shared_key = ephemeral.exchange(pub_key);
282
283        let derived_key = hkdf::hkdf(DERIVED_KEY_LENGTH, &shared_key, Some(&self.hash), None)
284            .map_err(CryptoError::HkdfError)?;
285
286        let token = Token::new(&derived_key).map_err(CryptoError::TokenError)?;
287        let ciphertext = token.encrypt_with_iv(plaintext, iv);
288
289        let mut result = Vec::with_capacity(32 + ciphertext.len());
290        result.extend_from_slice(&ephemeral_pub_bytes);
291        result.extend_from_slice(&ciphertext);
292        Ok(result)
293    }
294
295    pub fn decrypt(&self, ciphertext_token: &[u8]) -> Result<Vec<u8>, CryptoError> {
296        let prv = self.prv.as_ref().ok_or(CryptoError::NoPrivateKey)?;
297        self.decrypt_with_key(ciphertext_token, prv)
298    }
299
300    /// Try newest-first destination keys, optionally forbidding identity-key fallback.
301    /// The identity hash remains the HKDF salt, including for ratchet encryption.
302    pub fn decrypt_with_ratchets(
303        &self,
304        ciphertext: &[u8],
305        ratchets: &crate::ratchet::RatchetRing,
306        enforce: bool,
307    ) -> Result<crate::ratchet::Decrypted, CryptoError> {
308        // Token length is independent of the candidate key. Do not scan retained
309        // secrets when no candidate can possibly decrypt this input. Keep the
310        // existing enforced/fallback error paths below.
311        if ciphertext.len() > 32 + crate::token::TOKEN_OVERHEAD {
312            for key in ratchets.keys() {
313                let private = X25519PrivateKey::from_bytes(key);
314                if let Ok(plaintext) = self.decrypt_with_key(ciphertext, &private) {
315                    return Ok(crate::ratchet::Decrypted {
316                        plaintext,
317                        ratchet_id: Some(crate::ratchet::ratchet_id(
318                            &private.public_key().public_bytes(),
319                        )),
320                    });
321                }
322            }
323        }
324        if enforce {
325            return Err(CryptoError::InvalidCiphertext);
326        }
327        Ok(crate::ratchet::Decrypted {
328            plaintext: self.decrypt(ciphertext)?,
329            ratchet_id: None,
330        })
331    }
332
333    fn decrypt_with_key(
334        &self,
335        ciphertext_token: &[u8],
336        private: &X25519PrivateKey,
337    ) -> Result<Vec<u8>, CryptoError> {
338        if ciphertext_token.len() <= 32 {
339            return Err(CryptoError::InvalidCiphertext);
340        }
341        if ciphertext_token.len() <= 32 + crate::token::TOKEN_OVERHEAD {
342            return Err(CryptoError::TokenError(TokenError::InvalidToken));
343        }
344        let peer_pub_bytes: [u8; 32] = ciphertext_token[..32].try_into().unwrap();
345        let peer_pub = X25519PublicKey::from_bytes(&peer_pub_bytes);
346        let shared_key = zeroize::Zeroizing::new(private.exchange(&peer_pub));
347        let derived_key = zeroize::Zeroizing::new(
348            hkdf::hkdf(DERIVED_KEY_LENGTH, &*shared_key, Some(&self.hash), None)
349                .map_err(CryptoError::HkdfError)?,
350        );
351        let token = Token::new(&derived_key).map_err(CryptoError::TokenError)?;
352        token
353            .decrypt(&ciphertext_token[32..])
354            .map_err(CryptoError::TokenError)
355    }
356
357    /// The Ed25519 key this identity signs with, when it has its private keys.
358    pub fn signing_key(&self) -> Option<&Ed25519PrivateKey> {
359        self.sig_prv.as_ref()
360    }
361
362    pub fn sign(&self, message: &[u8]) -> Result<[u8; 64], CryptoError> {
363        let sig_prv = self.sig_prv.as_ref().ok_or(CryptoError::NoPrivateKey)?;
364        Ok(sig_prv.sign(message))
365    }
366
367    pub fn verify(&self, signature: &[u8; 64], message: &[u8]) -> bool {
368        match &self.sig_pub {
369            Some(sig_pub) => sig_pub.verify(signature, message),
370            None => false,
371        }
372    }
373}
374
375/// Encrypt `plaintext` for `target`, salting the key derivation with the
376/// receiving identity's `hash`: an ephemeral public key, then the token.
377fn encrypt_to(
378    hash: &[u8; 16],
379    target: &X25519PublicKey,
380    plaintext: &[u8],
381    rng: &mut dyn Rng,
382) -> Result<Vec<u8>, CryptoError> {
383    let ephemeral = X25519PrivateKey::generate(rng);
384    let ephemeral_pub_bytes = ephemeral.public_key().public_bytes();
385    let shared_key = ephemeral.exchange(target);
386
387    let derived_key = hkdf::hkdf(DERIVED_KEY_LENGTH, &shared_key, Some(hash), None)
388        .map_err(CryptoError::HkdfError)?;
389
390    let token = Token::new(&derived_key).map_err(CryptoError::TokenError)?;
391    let ciphertext = token.encrypt(plaintext, rng);
392
393    let mut result = Vec::with_capacity(32 + ciphertext.len());
394    result.extend_from_slice(&ephemeral_pub_bytes);
395    result.extend_from_slice(&ciphertext);
396    Ok(result)
397}
398
399fn truncated_hash(data: &[u8]) -> [u8; 16] {
400    let full = sha256::sha256(data);
401    let mut result = [0u8; 16];
402    result.copy_from_slice(&full[..16]);
403    result
404}
405
406#[cfg(test)]
407mod tests {
408    use super::*;
409    use crate::FixedRng;
410
411    #[test]
412    fn from_public_key_with_invalid_signing_key_returns_error() {
413        let identity = Identity::from_private_key(&[0x37; 64]);
414        let mut public_key = identity.get_public_key().unwrap();
415        // y = 2 is not a valid compressed Ed25519 point encoding.
416        public_key[32..].copy_from_slice(&[0; 32]);
417        public_key[32] = 2;
418        assert!(matches!(
419            Identity::from_public_key(&public_key),
420            Err(CryptoError::InvalidPublicKey)
421        ));
422    }
423
424    #[test]
425    fn encrypting_from_key_bytes_matches_the_identity() {
426        let identity = Identity::from_private_key(&[0x37; 64]);
427        let public_key = identity.get_public_key().unwrap();
428        let public = Identity::from_public_key(&public_key).unwrap();
429        let ratchet = crate::ratchet::RatchetRing::from_keys(vec![[0x42; 32]]).unwrap();
430        let ratchet_public = ratchet.current_public().unwrap();
431        for ratchet_key in [None, Some(&ratchet_public)] {
432            let seed = [0x63; 64];
433            let expected = public
434                .encrypt_with_ratchet(b"payload", ratchet_key, &mut FixedRng::new(&seed))
435                .unwrap();
436            let actual = Identity::encrypt_for_public_key(
437                &public_key,
438                b"payload",
439                ratchet_key,
440                &mut FixedRng::new(&seed),
441            )
442            .unwrap();
443            assert_eq!(actual, expected);
444            let plaintext = identity
445                .decrypt_with_ratchets(&actual, &ratchet, false)
446                .unwrap()
447                .plaintext;
448            assert_eq!(plaintext, b"payload");
449        }
450    }
451
452    #[test]
453    fn from_public_key_accepts_valid_public_key() {
454        let identity = Identity::from_private_key(&[0x37; 64]);
455        let public_key = identity.get_public_key().unwrap();
456        let public = Identity::from_public_key(&public_key).unwrap();
457        assert_eq!(public.get_public_key(), Some(public_key));
458        assert_eq!(public.hash(), identity.hash());
459    }
460
461    #[test]
462    fn short_ratchet_ciphertexts_preserve_error_and_fallback_policy() {
463        use crate::ratchet::RatchetRing;
464        let identity = Identity::from_private_key(&[0x37; 64]);
465        let public = Identity::from_public_key(&identity.get_public_key().unwrap()).unwrap();
466        for retained in [0, 1, 32] {
467            let ring = RatchetRing::from_keys(vec![[0x42; 32]; retained]).unwrap();
468            for length in [0, 1, 31, 32, 33, 48, 79, 80] {
469                let ciphertext = vec![0x63; length];
470                for owner in [&identity, &public] {
471                    assert!(matches!(
472                        owner.decrypt_with_ratchets(&ciphertext, &ring, true),
473                        Err(CryptoError::InvalidCiphertext)
474                    ));
475                    let expected = owner.decrypt(&ciphertext).unwrap_err();
476                    let actual = owner
477                        .decrypt_with_ratchets(&ciphertext, &ring, false)
478                        .err()
479                        .unwrap();
480                    assert_eq!(format!("{actual:?}"), format!("{expected:?}"));
481                }
482                assert!(matches!(
483                    public.decrypt(&ciphertext),
484                    Err(CryptoError::NoPrivateKey)
485                ));
486                if length <= 32 {
487                    assert!(matches!(
488                        identity.decrypt(&ciphertext),
489                        Err(CryptoError::InvalidCiphertext)
490                    ));
491                } else {
492                    assert!(matches!(
493                        identity.decrypt(&ciphertext),
494                        Err(CryptoError::TokenError(TokenError::InvalidToken))
495                    ));
496                }
497            }
498        }
499    }
500
501    #[test]
502    fn minimum_valid_ratchet_ciphertext_still_authenticates() {
503        use crate::ratchet::{ratchet_id, RatchetRing};
504        let identity = Identity::from_private_key(&[0x37; 64]);
505        let public_identity =
506            Identity::from_public_key(&identity.get_public_key().unwrap()).unwrap();
507        let ring = RatchetRing::from_keys(vec![[0x42; 32]]).unwrap();
508        let key = ring.current_public().unwrap();
509        let mut ciphertext = identity
510            .encrypt_with_ratchet(b"", Some(&key), &mut crate::FixedRng::new(&[0x63; 64]))
511            .unwrap();
512        assert_eq!(ciphertext.len(), 96);
513        let result = public_identity
514            .decrypt_with_ratchets(&ciphertext, &ring, true)
515            .unwrap();
516        assert!(result.plaintext.is_empty());
517        assert_eq!(result.ratchet_id, Some(ratchet_id(&key)));
518        *ciphertext.last_mut().unwrap() ^= 1;
519        assert!(public_identity
520            .decrypt_with_ratchets(&ciphertext, &ring, true)
521            .is_err());
522    }
523
524    #[test]
525    fn test_identity_key_roundtrip() {
526        let mut rng = FixedRng::new(&(0..64).collect::<Vec<u8>>());
527        let id = Identity::new(&mut rng);
528        let prv_bytes = id.get_private_key().unwrap();
529        let id2 = Identity::from_private_key(&prv_bytes);
530        assert_eq!(id.get_public_key().unwrap(), id2.get_public_key().unwrap());
531    }
532
533    #[test]
534    fn test_identity_hash() {
535        let mut rng = FixedRng::new(&(0..64).collect::<Vec<u8>>());
536        let id = Identity::new(&mut rng);
537        let pub_key = id.get_public_key().unwrap();
538        let expected_hash = truncated_hash(&pub_key);
539        assert_eq!(*id.hash(), expected_hash);
540    }
541
542    #[test]
543    fn test_identity_encrypt_decrypt_roundtrip() {
544        let mut rng = FixedRng::new(&(0..128).collect::<Vec<u8>>());
545        let id = Identity::new(&mut rng);
546        let plaintext = b"Hello, Reticulum! This is a test of the encrypt/decrypt pipeline.";
547        let mut rng2 = FixedRng::new(&(128..255).collect::<Vec<u8>>());
548        let ciphertext = id.encrypt(plaintext, &mut rng2).unwrap();
549        let decrypted = id.decrypt(&ciphertext).unwrap();
550        assert_eq!(decrypted, plaintext);
551    }
552
553    #[test]
554    fn test_identity_encrypt_with_ratchet_targets_ratchet_key() {
555        let mut rng = FixedRng::new(&(0..128).collect::<Vec<u8>>());
556        let remote_identity = Identity::new(&mut rng);
557        let ratchet_prv = X25519PrivateKey::from_bytes(&[0x42; 32]);
558        let ratchet_pub = ratchet_prv.public_key().public_bytes();
559        let plaintext = b"ratcheted";
560
561        let mut encrypt_rng = FixedRng::new(&(128..255).collect::<Vec<u8>>());
562        let ciphertext = remote_identity
563            .encrypt_with_ratchet(plaintext, Some(&ratchet_pub), &mut encrypt_rng)
564            .unwrap();
565
566        let peer_pub_bytes: [u8; 32] = ciphertext[..32].try_into().unwrap();
567        let peer_pub = X25519PublicKey::from_bytes(&peer_pub_bytes);
568        let shared_key = ratchet_prv.exchange(&peer_pub);
569        let derived_key = hkdf::hkdf(
570            DERIVED_KEY_LENGTH,
571            &shared_key,
572            Some(remote_identity.hash()),
573            None,
574        )
575        .unwrap();
576        let token = Token::new(&derived_key).unwrap();
577        let decrypted = token.decrypt(&ciphertext[32..]).unwrap();
578
579        assert_eq!(decrypted, plaintext);
580        assert!(remote_identity.decrypt(&ciphertext).is_err());
581    }
582
583    #[test]
584    fn test_identity_sign_verify() {
585        let mut rng = FixedRng::new(&(0..64).collect::<Vec<u8>>());
586        let id = Identity::new(&mut rng);
587        let msg = b"Sign this message";
588        let sig = id.sign(msg).unwrap();
589        assert!(id.verify(&sig, msg));
590        assert!(!id.verify(&sig, b"Wrong message"));
591    }
592
593    #[test]
594    fn stamped_key_generation_until_meets_cost_when_never_stopped() {
595        let mut rng = crate::OsRng;
596        let material = b"link request material";
597        let (prv, public, value) =
598            generate_stamped_key_until(&mut rng, material, StampCost::from_whole(4), &mut || false)
599                .unwrap();
600        assert!(value.meets(StampCost::from_whole(4)));
601        assert_eq!(public.public_bytes(), prv.public_key().public_bytes());
602        assert_eq!(stamp_value(material, &public.public_bytes()), value);
603    }
604
605    #[test]
606    fn stamped_key_generation_stops_when_asked() {
607        let mut rng = crate::OsRng;
608        let mut polls = 0u32;
609        let result = generate_stamped_key_until(
610            &mut rng,
611            b"material",
612            StampCost::from_whole(100),
613            &mut || {
614                polls += 1;
615                polls > 3
616            },
617        );
618        assert!(result.is_none());
619        assert_eq!(polls, 4);
620    }
621
622    #[test]
623    fn stamped_identity_carries_required_value() {
624        let mut rng = crate::OsRng;
625        let id = Identity::new_stamped(&mut rng, StampCost::from_whole(4));
626        let value = id.get_value().unwrap();
627        assert!(value.meets(StampCost::from_whole(4)));
628
629        let public = id.get_public_key().unwrap();
630        assert_eq!(stamp_value(&public[32..], &public[..32]), value);
631    }
632}