exonum_crypto/
lib.rs

1// Copyright 2020 The Exonum Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//   http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Cryptography related types, constants, traits and functions. The functions
16//! in this library are used for key generation, hashing, signing and signature
17//! verification.
18//!
19//! The Crypto library makes it possible to potentially change the type of
20//! cryptography applied in the system and add abstractions best
21//! suited for Exonum.
22
23#![warn(
24    missing_debug_implementations,
25    missing_docs,
26    unsafe_code,
27    bare_trait_objects
28)]
29#![warn(clippy::pedantic, clippy::nursery)]
30#![allow(
31    // Next `cast_*` lints don't give alternatives.
32    clippy::cast_possible_wrap, clippy::cast_possible_truncation, clippy::cast_sign_loss,
33    // Next lints produce too much noise/false positives.
34    clippy::module_name_repetitions, clippy::similar_names, clippy::must_use_candidate,
35    clippy::pub_enum_variant_names,
36    // '... may panic' lints.
37    clippy::indexing_slicing,
38    // Too much work to fix.
39    clippy::missing_errors_doc, clippy::missing_const_for_fn
40)]
41
42#[macro_use]
43extern crate serde_derive; // Required for Protobuf.
44
45#[doc(inline)]
46pub use self::crypto_impl::{
47    HASH_SIZE, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SEED_LENGTH, SIGNATURE_LENGTH,
48};
49#[cfg(feature = "sodiumoxide-crypto")]
50pub use self::crypto_lib::sodiumoxide::x25519;
51
52#[cfg(feature = "with-protobuf")]
53#[doc(hidden)]
54pub mod proto;
55
56use hex::{encode as encode_hex, FromHex, FromHexError, ToHex};
57use serde::{
58    de::{self, Deserialize, Deserializer, Visitor},
59    Serialize, Serializer,
60};
61
62use std::{
63    default::Default,
64    fmt,
65    ops::{Index, Range, RangeFrom, RangeFull, RangeTo},
66};
67
68// A way to set an active cryptographic backend is to export it as `crypto_impl`.
69#[cfg(feature = "sodiumoxide-crypto")]
70use self::crypto_lib::sodiumoxide as crypto_impl;
71
72#[macro_use]
73mod macros;
74
75pub(crate) mod crypto_lib;
76
77/// The size to crop the string in debug messages.
78const BYTES_IN_DEBUG: usize = 4;
79
80fn write_short_hex(f: &mut fmt::Formatter<'_>, slice: &[u8]) -> fmt::Result {
81    for byte in slice.iter().take(BYTES_IN_DEBUG) {
82        write!(f, "{:02x}", byte)?;
83    }
84    if slice.len() > BYTES_IN_DEBUG {
85        write!(f, "...")?;
86    }
87    Ok(())
88}
89
90/// Signs a slice of bytes using the signer's secret key and returns the
91/// resulting `Signature`.
92///
93/// # Examples
94///
95/// The example below generates a pair of secret and public keys, indicates
96/// certain data, signs the data using the secret key and with the help of
97/// the public key verifies that the data have been signed with the corresponding
98/// secret key.
99///
100/// ```
101/// # exonum_crypto::init();
102/// let (public_key, secret_key) = exonum_crypto::gen_keypair();
103/// let data = [1, 2, 3];
104/// let signature = exonum_crypto::sign(&data, &secret_key);
105/// assert!(exonum_crypto::verify(&signature, &data, &public_key));
106/// ```
107pub fn sign(data: &[u8], secret_key: &SecretKey) -> Signature {
108    let impl_signature = crypto_impl::sign(data, &secret_key.0);
109    Signature(impl_signature)
110}
111
112/// Computes a secret key and a corresponding public key from a `Seed`.
113///
114/// # Examples
115///
116/// The example below generates a keypair that depends on the indicated seed.
117/// Indicating the same seed value always results in the same keypair.
118///
119/// ```
120/// use exonum_crypto::{SEED_LENGTH, Seed};
121///
122/// # exonum_crypto::init();
123/// let (public_key, secret_key) = exonum_crypto::gen_keypair_from_seed(&Seed::new([1; SEED_LENGTH]));
124/// ```
125pub fn gen_keypair_from_seed(seed: &Seed) -> (PublicKey, SecretKey) {
126    let (impl_pub_key, impl_secret_key) = crypto_impl::gen_keypair_from_seed(&seed.0);
127    (PublicKey(impl_pub_key), SecretKey(impl_secret_key))
128}
129
130/// Generates a secret key and a corresponding public key using a cryptographically secure
131/// pseudo-random number generator.
132///
133/// # Examples
134///
135/// The example below generates a unique keypair.
136///
137/// ```
138/// # exonum_crypto::init();
139/// let (public_key, secret_key) = exonum_crypto::gen_keypair();
140/// ```
141pub fn gen_keypair() -> (PublicKey, SecretKey) {
142    let (pubkey, secret_key) = crypto_impl::gen_keypair();
143    (PublicKey(pubkey), SecretKey(secret_key))
144}
145
146/// Verifies that `data` is signed with a secret key corresponding to the
147/// given public key.
148///
149/// # Examples
150///
151/// The example below generates a pair of secret and public keys, indicates
152/// certain data, signs the data using the secret key and with the help of the public key
153/// verifies that the data have been signed with the corresponding secret key.
154///
155/// ```
156/// # exonum_crypto::init();
157/// let (public_key, secret_key) = exonum_crypto::gen_keypair();
158/// let data = [1, 2, 3];
159/// let signature = exonum_crypto::sign(&data, &secret_key);
160/// assert!(exonum_crypto::verify(&signature, &data, &public_key));
161/// ```
162pub fn verify(sig: &Signature, data: &[u8], pubkey: &PublicKey) -> bool {
163    crypto_impl::verify(&sig.0, data, &pubkey.0)
164}
165
166/// Calculates a hash of a bytes slice.
167///
168/// Type of a hash depends on a chosen crypto backend (via `...-crypto` cargo feature).
169///
170/// # Examples
171///
172/// The example below calculates the hash of the indicated data.
173///
174/// ```
175/// # exonum_crypto::init();
176/// let data = [1, 2, 3];
177/// let hash = exonum_crypto::hash(&data);
178/// ```
179pub fn hash(data: &[u8]) -> Hash {
180    let dig = crypto_impl::hash(data);
181    Hash(dig)
182}
183
184/// Initializes the cryptographic backend.
185///
186/// # Panics
187///
188/// Panics if backend initialization is failed.
189///
190/// # Examples
191///
192/// ```
193/// exonum_crypto::init();
194/// ```
195pub fn init() {
196    if !crypto_impl::init() {
197        panic!("Cryptographic library initialization failed.");
198    }
199}
200
201/// This structure provides a possibility to calculate a hash digest
202/// for a stream of data. Unlike the
203/// [`Hash` structure](struct.Hash.html),
204/// the given structure lets the code process several data chunks without
205/// the need to copy them into a single buffer.
206///
207/// # Examples
208///
209/// The example below indicates the data the code is working with; runs the
210/// system hash update as many times as required to process all the data chunks
211/// and calculates the resulting hash of the system.
212///
213/// ```rust
214/// use exonum_crypto::HashStream;
215///
216/// let data: Vec<[u8; 5]> = vec![[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]];
217/// let mut hash_stream = HashStream::new();
218/// for chunk in data {
219///     hash_stream = hash_stream.update(&chunk);
220/// }
221/// let _ = hash_stream.hash();
222/// ```
223#[derive(Debug, Default)]
224pub struct HashStream(crypto_impl::HashState);
225
226impl HashStream {
227    /// Creates a new instance of `HashStream`.
228    pub fn new() -> Self {
229        Self(crypto_impl::HashState::init())
230    }
231
232    /// Processes a chunk of stream and returns a `HashStream` with the updated internal state.
233    pub fn update(mut self, chunk: &[u8]) -> Self {
234        self.0.update(chunk);
235        self
236    }
237
238    /// Returns the resulting hash of the system calculated upon the commit
239    /// of currently supplied data.
240    pub fn hash(self) -> Hash {
241        let dig = self.0.finalize();
242        Hash(dig)
243    }
244}
245
246/// This structure provides a possibility to create and/or verify
247/// digital signatures for a stream of data. If the data are split into several
248/// chunks, the indicated chunks are added to the system and when adding is
249/// complete, the data is signed.
250///
251/// # Examples
252///
253/// The example below adds several data chunks to the system, generates a pair
254/// of random public and secret keys, signs the data and verifies the signature.
255///
256/// ```rust
257/// use exonum_crypto::{SignStream, gen_keypair};
258///
259/// let data: Vec<[u8; 5]> = vec![[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]];
260/// let (public_key, secret_key) = gen_keypair();
261/// let mut create_stream = SignStream::new();
262/// let mut verify_stream = SignStream::new();
263/// for chunk in data {
264///     create_stream = create_stream.update(&chunk);
265///     verify_stream = verify_stream.update(&chunk);
266/// }
267/// let file_sign = create_stream.sign(&secret_key);
268/// assert!(verify_stream.verify(&file_sign, &public_key));
269/// ```
270#[derive(Debug, Default)]
271pub struct SignStream(crypto_impl::SignState);
272
273impl SignStream {
274    /// Creates a new instance of `SignStream`.
275    ///
276    /// # Examples
277    ///
278    /// ```
279    /// use exonum_crypto::SignStream;
280    ///
281    /// let stream = SignStream::new();
282    /// ```
283    pub fn new() -> Self {
284        Self(crypto_impl::SignState::init())
285    }
286
287    /// Adds a new `chunk` to the message that will eventually be signed and/or verified.
288    ///
289    /// # Examples
290    ///
291    /// ```
292    /// use exonum_crypto::SignStream;
293    ///
294    /// let mut stream = SignStream::new();
295    ///
296    /// let data = &[[1, 2, 3], [4, 5, 6], [7, 8, 9]];
297    /// for chunk in data.iter() {
298    ///     stream = stream.update(chunk);
299    /// }
300    /// ```
301    pub fn update(mut self, chunk: &[u8]) -> Self {
302        self.0.update(chunk);
303        self
304    }
305
306    /// Computes and returns a signature for the previously supplied message
307    /// using the given `secret_key`.
308    ///
309    /// # Examples
310    ///
311    /// ```
312    /// use exonum_crypto::{SignStream, gen_keypair};
313    ///
314    /// let mut stream = SignStream::new();
315    ///
316    /// let data = &[[1, 2, 3], [4, 5, 6], [7, 8, 9]];
317    /// for chunk in data.iter() {
318    ///     stream = stream.update(chunk);
319    /// }
320    ///
321    /// let (public_key, secret_key) = gen_keypair();
322    /// let signature = stream.sign(&secret_key);
323    /// ```
324    pub fn sign(&mut self, secret_key: &SecretKey) -> Signature {
325        Signature(self.0.finalize(&secret_key.0))
326    }
327
328    /// Verifies that `sig` is a valid signature for the previously supplied message
329    /// using the given `public_key`.
330    ///
331    /// # Examples
332    ///
333    /// ```
334    /// use exonum_crypto::{SignStream, gen_keypair};
335    ///
336    /// let mut stream = SignStream::new();
337    /// let mut verify_stream = SignStream::new();
338    ///
339    /// let data = &[[1, 2, 3], [4, 5, 6], [7, 8, 9]];
340    /// for chunk in data.iter() {
341    ///     stream = stream.update(chunk);
342    ///     verify_stream = verify_stream.update(chunk);
343    /// }
344    ///
345    /// let (public_key, secret_key) = gen_keypair();
346    /// let signature = stream.sign(&secret_key);
347    /// assert!(verify_stream.verify(&signature, &public_key));
348    /// ```
349    pub fn verify(&mut self, sig: &Signature, public_key: &PublicKey) -> bool {
350        self.0.verify(&sig.0, &public_key.0)
351    }
352}
353
354implement_public_crypto_wrapper! {
355/// Ed25519 public key used to verify digital signatures.
356///
357/// In public-key cryptography, the system uses a a mathematically related pair
358/// of keys: a public key, which is openly distributed, and a secret key,
359/// which should remain confidential. For more information, refer to
360/// [Public-key cryptography](https://en.wikipedia.org/wiki/Public-key_cryptography).
361///
362/// Ed25519 is a signature system that ensures fast signing and key generation,
363/// as well as security and collision resilience.
364///
365/// # Examples
366///
367/// In the example below, the function generates a pair of random public and
368/// secret keys.
369///
370/// ```
371/// # exonum_crypto::init();
372/// let (public_key, _) = exonum_crypto::gen_keypair();
373/// ```
374    struct PublicKey, PUBLIC_KEY_LENGTH
375}
376
377implement_private_crypto_wrapper! {
378/// Ed25519 secret key used to create digital signatures over messages.
379///
380/// In public-key cryptography, the system uses a a mathematically related pair
381/// of keys: a public key, which is openly distributed, and a secret key,
382/// which should remain confidential. For more information, refer to
383/// [Public-key cryptography](https://en.wikipedia.org/wiki/Public-key_cryptography).
384///
385/// Ed25519 is a signature system that ensures fast signing and key generation,
386/// as well as security and collision resilience.
387///
388/// # Examples
389///
390/// In the example below, the function generates a pair of random public and
391/// secret keys.
392///
393/// ```
394/// # exonum_crypto::init();
395/// let (_, secret_key) = exonum_crypto::gen_keypair();
396/// ```
397    struct SecretKey, SECRET_KEY_LENGTH
398}
399
400implement_public_crypto_wrapper! {
401/// The result of applying the SHA-256 hash function to data.
402///
403/// This function splits the input data into blocks and runs each block
404/// through a cycle of 64 iterations. The result of the function is a hash
405/// 256 bits or 32 bytes in length.
406///
407/// # Examples
408///
409/// The example below generates the hash of the indicated data.
410///
411/// ```
412/// use exonum_crypto::Hash;
413///
414/// let data = [1, 2, 3];
415/// let hash_from_data = exonum_crypto::hash(&data);
416/// let default_hash = Hash::default();
417/// ```
418    struct Hash, HASH_SIZE
419}
420
421implement_public_crypto_wrapper! {
422/// Ed25519 digital signature. This structure creates a signature over data
423/// using a secret key. Later it is possible to verify, using the corresponding
424/// public key, that the data have indeed been signed with that secret key.
425///
426/// Ed25519 is a signature system that ensures fast signing and key generation,
427/// as well as security and collision resilience.
428///
429/// # Examples
430///
431/// The example below generates a pair of random public and secret keys,
432/// adds certain data, signs the data using the secret key and verifies
433/// that the data have been signed with that secret key.
434///
435/// ```
436/// # exonum_crypto::init();
437/// let (public_key, secret_key) = exonum_crypto::gen_keypair();
438/// let data = [1, 2, 3];
439/// let signature = exonum_crypto::sign(&data, &secret_key);
440/// assert!(exonum_crypto::verify(&signature, &data, &public_key));
441/// ```
442    struct Signature, SIGNATURE_LENGTH
443}
444
445implement_private_crypto_wrapper! {
446/// Ed25519 seed representing a succession of bytes that can be used for
447/// deterministic keypair generation. If the same seed is indicated in the
448/// generator multiple times, the generated keys will be the same each time.
449///
450/// Note that this is not the seed added to Exonum transactions for additional
451/// security, this is a separate entity. This structure is useful for testing,
452/// to receive repeatable results. The seed in this structure is either set
453/// manually or selected using the methods below.
454///
455/// # Examples
456///
457/// The example below generates a pair of public and secret keys taking
458/// into account the selected seed. The same seed will always lead to
459/// generation of the same keypair.
460///
461/// ```
462/// use exonum_crypto::{SEED_LENGTH, Seed};
463///
464/// # exonum_crypto::init();
465/// let (public_key, secret_key) = exonum_crypto::gen_keypair_from_seed(&Seed::new([1; SEED_LENGTH]));
466/// ```
467    struct Seed, SEED_LENGTH
468}
469
470implement_serde! {Hash}
471implement_serde! {PublicKey}
472implement_serde! {SecretKey}
473implement_serde! {Seed}
474implement_serde! {Signature}
475
476implement_index_traits! {Hash}
477implement_index_traits! {PublicKey}
478implement_index_traits! {SecretKey}
479implement_index_traits! {Seed}
480implement_index_traits! {Signature}
481
482/// Pair of matching secret and public keys.
483///
484/// Prefer using this struct to `(PublicKey, SecretKey)`, since it asserts that public
485/// and secret keys necessarily match.
486#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
487pub struct KeyPair {
488    public_key: PublicKey,
489    secret_key: SecretKey,
490}
491
492impl KeyPair {
493    /// Creates a keypair from the provided keys, checking that they correspond to each other.
494    ///
495    /// # Panics
496    ///
497    /// - If the keys do not match.
498    pub fn from_keys(public_key: PublicKey, secret_key: SecretKey) -> Self {
499        assert!(
500            verify_keys_match(&public_key, &secret_key),
501            "Public key does not match the secret key."
502        );
503
504        Self {
505            public_key,
506            secret_key,
507        }
508    }
509
510    /// Generates a random keypair using the random number generator provided by the crypto backend.
511    pub fn random() -> Self {
512        let (public_key, secret_key) = gen_keypair();
513        Self {
514            public_key,
515            secret_key,
516        }
517    }
518
519    /// Generates a keypair from the provided seed.
520    pub fn from_seed(seed: &Seed) -> Self {
521        let (public_key, secret_key) = gen_keypair_from_seed(seed);
522        Self {
523            public_key,
524            secret_key,
525        }
526    }
527
528    /// Gets the public key.
529    pub fn public_key(&self) -> PublicKey {
530        self.public_key
531    }
532
533    /// Gets a reference to the secret key.
534    pub fn secret_key(&self) -> &SecretKey {
535        &self.secret_key
536    }
537}
538
539impl From<(PublicKey, SecretKey)> for KeyPair {
540    fn from(keys: (PublicKey, SecretKey)) -> Self {
541        Self::from_keys(keys.0, keys.1)
542    }
543}
544
545fn verify_keys_match(public_key: &PublicKey, secret_key: &SecretKey) -> bool {
546    crypto_impl::verify_keys_match(&public_key.0, &secret_key.0)
547}
548
549#[cfg(test)]
550mod tests {
551    use super::*;
552
553    use serde::de::DeserializeOwned;
554
555    use hex::FromHex;
556
557    #[test]
558    fn to_from_hex_hash() {
559        let original = hash(&[]);
560        let from_hex = Hash::from_hex(original.to_hex()).unwrap();
561        assert_eq!(original, from_hex);
562    }
563
564    #[test]
565    fn zero_hash() {
566        let hash = Hash::zero();
567        assert_eq!(hash.as_ref(), [0; HASH_SIZE]);
568    }
569
570    #[test]
571    fn to_from_hex_keys() {
572        let (p, s) = gen_keypair();
573
574        let ph = PublicKey::from_hex(p.to_hex()).unwrap();
575        assert_eq!(p, ph);
576
577        let sh = SecretKey::from_hex(s.to_hex()).unwrap();
578        assert_eq!(s, sh);
579    }
580
581    #[test]
582    fn serialize_deserialize_hash() {
583        assert_serialize_deserialize(&Hash::new([207; HASH_SIZE]));
584    }
585
586    #[test]
587    fn serialize_deserialize_public_key() {
588        assert_serialize_deserialize(&PublicKey::new([208; PUBLIC_KEY_LENGTH]));
589    }
590
591    #[test]
592    fn serialize_deserialize_signature() {
593        assert_serialize_deserialize(&Signature::new([209; SIGNATURE_LENGTH]));
594    }
595
596    #[test]
597    fn serialize_deserialize_seed() {
598        assert_serialize_deserialize(&Seed::new([210; SEED_LENGTH]));
599    }
600
601    #[test]
602    fn serialize_deserialize_secret_key() {
603        assert_serialize_deserialize(&SecretKey::new([211; SECRET_KEY_LENGTH]));
604    }
605
606    #[test]
607    fn debug_format() {
608        // Check zero padding.
609        let hash = Hash::new([1; HASH_SIZE]);
610        assert_eq!(format!("{:?}", &hash), "Hash(01010101...)");
611
612        let pk = PublicKey::new([15; PUBLIC_KEY_LENGTH]);
613        assert_eq!(format!("{:?}", &pk), "PublicKey(0f0f0f0f...)");
614        let sk = SecretKey::new([8; SECRET_KEY_LENGTH]);
615        assert_eq!(format!("{:?}", &sk), "SecretKey(08080808...)");
616        let signature = Signature::new([10; SIGNATURE_LENGTH]);
617        assert_eq!(format!("{:?}", &signature), "Signature(0a0a0a0a...)");
618        let seed = Seed::new([4; SEED_LENGTH]);
619        assert_eq!(format!("{:?}", &seed), "Seed(04040404...)");
620
621        // Check no padding.
622        let hash = Hash::new([128; HASH_SIZE]);
623        assert_eq!(format!("{:?}", &hash), "Hash(80808080...)");
624        let sk = SecretKey::new([255; SECRET_KEY_LENGTH]);
625        assert_eq!(format!("{:?}", &sk), "SecretKey(ffffffff...)");
626    }
627
628    // Note that only public values have Display impl.
629    #[test]
630    fn display_format() {
631        // Check zero padding.
632        let hash = Hash::new([1; HASH_SIZE]);
633        assert_eq!(format!("{}", &hash), "01010101...");
634
635        let pk = PublicKey::new([15; PUBLIC_KEY_LENGTH]);
636        assert_eq!(format!("{}", &pk), "0f0f0f0f...");
637        let signature = Signature::new([10; SIGNATURE_LENGTH]);
638        assert_eq!(format!("{}", &signature), "0a0a0a0a...");
639
640        // Check no padding.
641        let hash = Hash::new([128; HASH_SIZE]);
642        assert_eq!(format!("{}", &hash), "80808080...");
643    }
644
645    #[test]
646    fn hash_streaming_zero() {
647        let h1 = hash(&[]);
648        let state = HashStream::new();
649        let h2 = state.update(&[]).hash();
650        assert_eq!(h1, h2);
651    }
652
653    #[test]
654    fn hash_streaming_chunks() {
655        let data: [u8; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
656        let h1 = hash(&data);
657        let state = HashStream::new();
658        let h2 = state.update(&data[..5]).update(&data[5..]).hash();
659        assert_eq!(h1, h2);
660    }
661
662    #[test]
663    fn sign_streaming_zero() {
664        let (pk, sk) = gen_keypair();
665        let mut creation_stream = SignStream::new().update(&[]);
666        let sig = creation_stream.sign(&sk);
667        let mut verified_stream = SignStream::new().update(&[]);
668        assert!(verified_stream.verify(&sig, &pk));
669    }
670
671    #[test]
672    fn sign_streaming_chunks() {
673        let data: [u8; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
674        let (pk, sk) = gen_keypair();
675        let mut creation_stream = SignStream::new().update(&data[..5]).update(&data[5..]);
676        let sig = creation_stream.sign(&sk);
677        let mut verified_stream = SignStream::new().update(&data[..5]).update(&data[5..]);
678        assert!(verified_stream.verify(&sig, &pk));
679    }
680
681    fn assert_serialize_deserialize<T>(original_value: &T)
682    where
683        T: Serialize + DeserializeOwned + PartialEq + fmt::Debug,
684    {
685        let json = serde_json::to_string(original_value).unwrap();
686        let deserialized_value: T = serde_json::from_str(&json).unwrap();
687        assert_eq!(*original_value, deserialized_value);
688    }
689
690    #[test]
691    fn valid_keypair() {
692        let (pk, sk) = gen_keypair();
693        let _ = KeyPair::from_keys(pk, sk);
694    }
695
696    #[test]
697    #[should_panic]
698    fn not_valid_keypair() {
699        let (pk, _) = gen_keypair();
700        let (_, sk) = gen_keypair();
701        let _ = KeyPair::from_keys(pk, sk);
702    }
703}