codas/types/
cryptography.rs

1/// Cryptographic data types, like hashes and signatures.
2///
3/// # Unstable
4///
5/// These types may be split out into a separate crate in the future,
6/// and have experimental APIs.
7use argon2::Argon2;
8use chacha20poly1305::{
9    aead::{Aead, Payload},
10    AeadCore, ChaCha20Poly1305, Key, KeyInit, Nonce,
11};
12use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey};
13use rand_core::OsRng;
14use snafu::Snafu;
15
16use crate::{
17    codec::{CodecError, Decodable, Encodable, Format, WritesEncodable},
18    sized_byte_array,
19    stream::Writes,
20    types::binary::hex_from_bytes,
21};
22
23use super::Coda;
24
25sized_byte_array!(
26    /// Byte array containing a Blake3 hash.
27    HashBytes,
28    32
29);
30
31sized_byte_array!(
32    /// Byte array containing an Ed25519 private key.
33    PrivateKeyBytes,
34    32
35);
36
37sized_byte_array!(
38    /// Byte array containing an Ed25519 public key.
39    PublicKeyBytes,
40    32
41);
42
43sized_byte_array!(
44    /// Byte array containing an Ed25519 signature.
45    SignatureBytes,
46    64
47);
48
49/// A hasher which creates [`HashBytes`].
50#[derive(Default)]
51pub struct CryptoHasher {
52    hasher: blake3::Hasher,
53}
54
55impl CryptoHasher {
56    /// Writes `bytes` to the in-progress hash.
57    pub fn write(&mut self, bytes: &[u8]) {
58        self.hasher.update(bytes);
59    }
60
61    /// Completes the hash and consumes `self`, returning it as [HashBytes].
62    pub fn finalize(self) -> HashBytes {
63        HashBytes::from(*self.hasher.finalize().as_bytes())
64    }
65
66    /// Completes the hash and consumes `self`, writing it into `bytes`.
67    pub fn finalize_into_bytes(self, bytes: &mut HashBytes) {
68        bytes.0 = *self.hasher.finalize().as_bytes();
69    }
70}
71
72impl Writes for CryptoHasher {
73    fn write(&mut self, buf: &[u8]) -> Result<usize, crate::stream::StreamError> {
74        self.hasher.update(buf);
75        Ok(buf.len())
76    }
77
78    fn write_all(&mut self, buf: &[u8]) -> Result<(), crate::stream::StreamError> {
79        self.hasher.update(buf);
80        Ok(())
81    }
82}
83
84/// Signing (private) and verifying (public)
85/// key pair which can create and verify
86/// [`SignatureBytes`].
87pub struct CryptoKeys {
88    signer: CryptoSigner,
89    verifier: CryptoVerifier,
90}
91
92impl CryptoKeys {
93    /// Generates and returns a new pair of keys.
94    pub fn generate() -> Self {
95        let mut rng = OsRng;
96        let signer = SigningKey::generate(&mut rng);
97        let verifier = signer.verifying_key();
98        CryptoKeys {
99            signer: CryptoSigner {
100                private_key: signer,
101            },
102            verifier: CryptoVerifier {
103                public_key: verifier,
104            },
105        }
106    }
107
108    /// Tries to load a pair of keys from
109    /// `private_key`.
110    pub fn from_private(private_key: PrivateKeyBytes) -> Result<Self, CryptoError> {
111        let signer = SigningKey::from_bytes(&private_key.0);
112        let verifier = signer.verifying_key();
113        Ok(CryptoKeys {
114            signer: CryptoSigner {
115                private_key: signer,
116            },
117            verifier: CryptoVerifier {
118                public_key: verifier,
119            },
120        })
121    }
122
123    /// Consumes these keys, returning _only_
124    /// their private key.
125    pub fn into_private(self) -> PrivateKeyBytes {
126        let mut bytes = PrivateKeyBytes::default();
127        let private_key = &self.signer.private_key.to_keypair_bytes()[0..PrivateKeyBytes::SIZE];
128        bytes.copy_from_slice(private_key);
129        bytes
130    }
131}
132
133/// Signing (private) key which
134/// creates [`SignatureBytes`].
135pub struct CryptoSigner {
136    private_key: SigningKey,
137}
138
139/// Verifying (public) key which
140/// verifies [`SignatureBytes`].
141#[derive(Copy, Clone, Debug)]
142pub struct CryptoVerifier {
143    public_key: VerifyingKey,
144}
145
146impl TryFrom<&PublicKeyBytes> for CryptoVerifier {
147    type Error = CryptoError;
148
149    fn try_from(public_key: &PublicKeyBytes) -> Result<Self, Self::Error> {
150        let public_key =
151            VerifyingKey::from_bytes(&public_key.0).map_err(|_| CryptoError::InvalidPublicKey {
152                pub_key: *public_key,
153            })?;
154
155        Ok(CryptoVerifier { public_key })
156    }
157}
158
159/// A cryptographic certificate, containing
160/// [`SignatureBytes`] accompanied by the
161/// [`PublicKeyBytes`] of the entity that
162/// created the signature.
163#[derive(Copy, Clone, Debug, Default)]
164pub struct CryptoCert {
165    /// The public key of the entity
166    /// that created [`Self::signature`].
167    pub public_key: PublicKeyBytes,
168
169    /// The signature.
170    pub signature: SignatureBytes,
171}
172
173impl CryptoCert {
174    /// Signs `data` with `signer`, replacing
175    /// `self`'s current signature with the result.
176    pub fn sign(
177        &mut self,
178        signer: &impl CryptoSigns,
179        data: &[&[u8]],
180    ) -> core::result::Result<(), CryptoError> {
181        self.public_key = signer.public_key_bytes();
182        self.signature = signer.sign(data)?;
183
184        Ok(())
185    }
186
187    /// Returns `Ok` iff this certificate's
188    /// signature is valid and matches `data`.
189    pub fn verify(&self, data: &[&[u8]]) -> core::result::Result<(), CryptoError> {
190        let key = CryptoVerifier::try_from(&self.public_key)?;
191        key.verify(data, &self.signature)
192    }
193}
194
195impl Eq for CryptoCert {}
196impl PartialEq for CryptoCert {
197    fn eq(&self, other: &Self) -> bool {
198        self.public_key == other.public_key && self.signature == other.signature
199    }
200}
201
202impl Ord for CryptoCert {
203    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
204        self.signature.cmp(&other.signature)
205    }
206}
207
208impl PartialOrd for CryptoCert {
209    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
210        Some(self.cmp(other))
211    }
212}
213
214impl core::hash::Hash for CryptoCert {
215    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
216        self.public_key.hash(state);
217        self.signature.hash(state);
218    }
219}
220
221impl Encodable for CryptoCert {
222    const FORMAT: Format = PublicKeyBytes::FORMAT.with(SignatureBytes::FORMAT);
223
224    fn encode(&self, writer: &mut (impl WritesEncodable + ?Sized)) -> Result<(), CodecError> {
225        writer.write_data(&self.public_key)?;
226        writer.write_data(&self.signature)?;
227        Ok(())
228    }
229}
230
231impl Decodable for CryptoCert {
232    fn decode(
233        &mut self,
234        reader: &mut (impl crate::codec::ReadsDecodable + ?Sized),
235        header: Option<crate::codec::DataHeader>,
236    ) -> Result<(), CodecError> {
237        Self::ensure_no_header(header)?;
238        reader.read_data_into(&mut self.public_key)?;
239        reader.read_data_into(&mut self.signature)?;
240        Ok(())
241    }
242}
243
244/// A thing that can be represented as a cryptographic hash.
245pub trait HasCryptoHash {
246    /// Writes `self`'s cryptographhically hashable
247    /// data to `hasher`.
248    fn crypto_hash_into(&self, hasher: &mut CryptoHasher);
249
250    /// Returns a new [`CryptoHasher`] containing `self`'s
251    /// cryptographically hashable data.
252    ///
253    /// The hashable data is not guaranteed to contain the
254    /// entirety of `self`'s data. For example, some data
255    /// structures may contain a certificate that indirectly
256    /// contains all of `self`'s data; in these cases,
257    /// the hashable data returned by this method may be
258    /// the raw bytes of the certificate's signature.
259    fn crypto_hasher(&self) -> CryptoHasher {
260        let mut hasher = CryptoHasher::default();
261        self.crypto_hash_into(&mut hasher);
262        hasher
263    }
264}
265
266impl HasCryptoHash for CryptoCert {
267    fn crypto_hash_into(&self, hasher: &mut CryptoHasher) {
268        hasher.write(&self.signature);
269    }
270}
271
272impl HasCryptoHash for Coda {
273    fn crypto_hash_into(&self, hasher: &mut CryptoHasher) {
274        let _ = self.encode(hasher);
275    }
276}
277
278/// A thing that has associated [`PublicKeyBytes`].
279pub trait HasCryptoPublicKey {
280    /// Returns this thing's public key.
281    fn public_key_bytes(&self) -> PublicKeyBytes;
282}
283
284impl HasCryptoPublicKey for CryptoKeys {
285    fn public_key_bytes(&self) -> PublicKeyBytes {
286        self.verifier.public_key_bytes()
287    }
288}
289
290impl HasCryptoPublicKey for CryptoSigner {
291    fn public_key_bytes(&self) -> PublicKeyBytes {
292        (*self.private_key.verifying_key().as_bytes()).into()
293    }
294}
295
296impl HasCryptoPublicKey for CryptoVerifier {
297    fn public_key_bytes(&self) -> PublicKeyBytes {
298        (*self.public_key.as_bytes()).into()
299    }
300}
301
302/// A thing that creates [`SignatureBytes`].
303pub trait CryptoSigns: HasCryptoPublicKey {
304    /// Signs `message` with this signer's private key,
305    /// returning `Ok(signature)` iff signing was successful.
306    fn sign(&self, message: &[&[u8]]) -> Result<SignatureBytes, CryptoError>;
307}
308
309impl CryptoSigns for CryptoKeys {
310    fn sign(&self, message: &[&[u8]]) -> Result<SignatureBytes, CryptoError> {
311        self.signer.sign(message)
312    }
313}
314
315impl CryptoSigns for CryptoSigner {
316    fn sign(&self, message: &[&[u8]]) -> Result<SignatureBytes, CryptoError> {
317        let signature = self
318            .private_key
319            .try_sign(message.concat().as_slice())
320            .expect("signing failure");
321        Ok(signature.to_bytes().into())
322    }
323}
324
325/// A thing that verifies [`SignatureBytes`].
326pub trait CryptoVerifies: HasCryptoPublicKey {
327    /// Verifies `signature` against `message`,
328    /// returning `Ok` iff the `signature` is
329    /// valid and corresponds to this verifier's
330    /// public key.
331    fn verify(&self, message: &[&[u8]], signature: &SignatureBytes) -> Result<(), CryptoError>;
332}
333
334impl CryptoVerifies for CryptoKeys {
335    fn verify(&self, message: &[&[u8]], signature: &SignatureBytes) -> Result<(), CryptoError> {
336        self.verifier.verify(message, signature)
337    }
338}
339
340impl CryptoVerifies for CryptoVerifier {
341    fn verify(&self, message: &[&[u8]], signature: &SignatureBytes) -> Result<(), CryptoError> {
342        let message = message.concat();
343        let message = message.as_slice();
344        let sig = Signature::from_bytes(&signature.0);
345        self.public_key
346            .verify_strict(message, &sig)
347            .map_err(|_| CryptoError::InvalidSignature {
348                signature: *signature,
349            })
350    }
351}
352
353/// Data encrypted with `ChaCha20-Poly1305`
354/// by a key derived via `Argon2`.
355#[derive(Default)]
356pub struct EncryptedData {
357    /// Nonce used during key derivation,
358    /// encryption, and decryption.
359    nonce: [u8; 12],
360
361    /// Encrypted data.
362    data: alloc::vec::Vec<u8>,
363}
364
365impl EncryptedData {
366    /// Encrypts `data` with `key`, returning a new encrypted data.
367    pub fn new(key: &[u8], data: &[u8]) -> Result<Self, CryptoError> {
368        // Generate a nonce for key derivation and encryption.
369        let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng);
370
371        // Derive key.
372        let mut derived_key = [0u8; 32];
373        Argon2::default().hash_password_into(key, nonce.as_ref(), &mut derived_key)?;
374        let cipher = ChaCha20Poly1305::new(&Key::from(derived_key));
375
376        // Encrypt the data, attaching the nonce as unencrypted additional associated data.
377        let encrypted = cipher.encrypt(
378            &nonce,
379            Payload {
380                msg: data,
381                aad: &nonce,
382            },
383        )?;
384
385        Ok(Self {
386            nonce: nonce.into(),
387            data: encrypted,
388        })
389    }
390
391    /// Decrypts this data with `key`, returning
392    /// the decrypted data.
393    pub fn decrypt(&self, key: &[u8]) -> Result<alloc::vec::Vec<u8>, CryptoError> {
394        // Derive key.
395        let mut derived_key = [0u8; 32];
396        Argon2::default().hash_password_into(key, self.nonce.as_ref(), &mut derived_key)?;
397        let cipher = ChaCha20Poly1305::new(&Key::from(derived_key));
398
399        // Decrypt the data.
400        let decrypted = cipher.decrypt(
401            Nonce::from_slice(&self.nonce),
402            Payload {
403                msg: &self.data,
404                aad: &self.nonce,
405            },
406        )?;
407
408        Ok(decrypted)
409    }
410
411    /// Returns a string containing the nonce and
412    /// encrypted data in HEX format, separated by
413    /// a `-` character.
414    pub fn to_hex(&self) -> alloc::string::String {
415        alloc::format!(
416            "{}-{}",
417            hex_from_bytes(&self.nonce),
418            hex_from_bytes(&self.data)
419        )
420    }
421
422    /// Returns a new encrypted data by decoding a
423    /// string containing a `nonce-data` pair, where
424    /// the `nonce` and `data` are HEX-encoded.
425    pub fn from_hex(hex: &str) -> Result<Self, CryptoError> {
426        let (nonce, key) = hex.split_once('-').ok_or(CryptoError::Malformed)?;
427        let nonce = super::binary::bytes_from_hex(nonce).map_err(|_| CryptoError::Malformed)?;
428        let key = super::binary::bytes_from_hex(key).map_err(|_| CryptoError::Malformed)?;
429
430        Ok(EncryptedData {
431            nonce: nonce.try_into().map_err(|_| CryptoError::Malformed)?,
432            data: key,
433        })
434    }
435}
436
437impl Encodable for EncryptedData {
438    const FORMAT: Format = Format::data(0)
439        .with(<[u8; 12]>::FORMAT)
440        .with(alloc::vec::Vec::<u8>::FORMAT);
441
442    fn encode(&self, writer: &mut (impl WritesEncodable + ?Sized)) -> Result<(), CodecError> {
443        writer.write_data(&self.nonce)?;
444        writer.write_data(&self.data)?;
445        Ok(())
446    }
447}
448
449impl Decodable for EncryptedData {
450    fn decode(
451        &mut self,
452        reader: &mut (impl crate::codec::ReadsDecodable + ?Sized),
453        header: Option<crate::codec::DataHeader>,
454    ) -> Result<(), CodecError> {
455        Self::ensure_header(header, &[0])?;
456        reader.read_data_into(&mut self.nonce)?;
457        reader.read_data_into(&mut self.data)?;
458        Ok(())
459    }
460}
461
462/// An error that may occur when interacting with cryptographic data.
463#[derive(Debug, Snafu, Clone)]
464pub enum CryptoError {
465    #[snafu(display("the private key could not be loaded as an Ed25519 private key"))]
466    InvalidPrivateKey,
467
468    #[snafu(display("{pub_key} could not be loaded as an Ed25519 public key"))]
469    InvalidPublicKey { pub_key: PublicKeyBytes },
470
471    #[snafu(display("{signature} was not a valid Ed25519 signature for the provided message"))]
472    InvalidSignature { signature: SignatureBytes },
473
474    #[snafu(display("deriving a cryptographic key failed: {message}"))]
475    KeyDerivationFailure { message: alloc::string::String },
476
477    #[snafu(display("encrypting or decrypting data failed: {message}"))]
478    CipherFailure { message: alloc::string::String },
479
480    #[snafu(display("the provided input was malformed or corrupt"))]
481    Malformed,
482}
483
484impl From<argon2::Error> for CryptoError {
485    fn from(value: argon2::Error) -> Self {
486        Self::KeyDerivationFailure {
487            message: <argon2::Error as crate::alloc::string::ToString>::to_string(&value),
488        }
489    }
490}
491
492impl From<chacha20poly1305::Error> for CryptoError {
493    fn from(value: chacha20poly1305::Error) -> Self {
494        Self::CipherFailure {
495            message: <chacha20poly1305::Error as crate::alloc::string::ToString>::to_string(&value),
496        }
497    }
498}
499
500#[cfg(test)]
501mod tests {
502
503    use crate::codec::ReadsDecodable;
504
505    use super::*;
506
507    #[test]
508    fn encrypted_data() {
509        let key = b"cupc4k3s";
510        let message = b"i'm so secret.";
511
512        // Test encryption happy-path.
513        let mut encrypted = EncryptedData::new(key, message).unwrap();
514        let decrypted = encrypted.decrypt(key).unwrap();
515        assert_eq!(message, decrypted.as_slice());
516
517        // Test that different encryptions use different nonces.
518        let mut encrypted_too = EncryptedData::new(key, message).unwrap();
519        assert_ne!(encrypted_too.data, encrypted.data);
520        assert_ne!(encrypted_too.nonce, encrypted.nonce);
521
522        // Test that mutating the nonce breaks decryption.
523        encrypted.nonce.fill(0u8);
524        assert!(encrypted.decrypt(key).is_err());
525
526        // Test that mutating the payload breaks decryption.
527        encrypted_too.data.fill(0u8);
528        assert!(encrypted_too.decrypt(key).is_err());
529    }
530
531    #[test]
532    fn encrypted_data_codas_codec() {
533        let key = b"p4nc4k3s";
534        let message = b"i'm pretty secret.";
535
536        // Encrypt a message.
537        let encrypted = EncryptedData::new(key, message).unwrap();
538
539        // Encode the message payload.
540        let mut encoded = vec![];
541        encoded.write_data(&encrypted).unwrap();
542
543        // Decode the message payload.
544        let decoded: EncryptedData = encoded.as_slice().read_data().unwrap();
545
546        // Test that the decoded data is still well-formatted.
547        let decrypted = decoded.decrypt(key).unwrap();
548        assert_eq!(message, decrypted.as_slice());
549    }
550
551    #[test]
552    fn encrypted_data_hex_codec() {
553        let key = b"p4nc4k3s";
554        let message = b"i'm pretty secret.";
555
556        // Encrypt a message.
557        let encrypted = EncryptedData::new(key, message).unwrap();
558
559        // Convert the message to hexadecimal.
560        let encoded = encrypted.to_hex();
561
562        let mut bytes = vec![];
563        bytes.write_data(&encrypted).unwrap();
564        eprintln!("raw hex: {encoded}");
565        eprintln!("\n\ncoda hx: {}", hex_from_bytes(&bytes));
566
567        // Decode the message contents.
568        let decoded = EncryptedData::from_hex(&encoded).unwrap();
569        assert_eq!(encrypted.nonce, decoded.nonce);
570        assert_eq!(encrypted.data, decoded.data);
571    }
572}