ssb_crypto/
lib.rs

1//! This crate provides the cryptographic functionality needed to implement
2//! the Secure Scuttlebutt networking protocols and content signing and encryption.
3//!
4//! There are two implementations of the crypto operations available; one that uses
5//! [libsodium] C library (via the [sodiumoxide] crate), and a pure-rust implementation
6//! that uses [dalek] and [RustCrypto] crates (which is the default). You can select which
7//! implementation to use via Cargo.toml feature flags (see below).
8//!
9//! # Features
10//!
11//! If you only need the struct definitions and basic operations,
12//! disable default features, and (optionally) enable b64.
13//!
14//! ```toml
15//! [dependencies.ssb-crypto]
16//! version = "0.2"
17//! default_features = false
18//! features = ["b64"]
19//! ```
20//!
21//! ### `dalek`
22//! On by default. Use the dalek/RustCrypto implementation of the crypto operations.
23//! The crypto functionality is exposed via convenient methods, eg [`Keypair::sign`] and
24//! [`PublicKey::verify`]. If neither `dalek` nor `sodium` features are enabled,
25//! these methods won't be available.
26//!
27//! ### `rand`
28//! On by default. Provide functions to generate keys and nonces with user-specified
29//! cryptographically-secure random number generator (Eg. [`Nonce::generate_with_rng`]).
30//! These functions can be used in `no_std` environments that aren't supported by `getrandom`.
31//! Enabled if `dalek` is enabled.
32//!
33//! ### `getrandom`
34//! On by default. Provide functions to generate keys and nonces using the OS-provided
35//! cryptographically-secure random number generator (via the [getrandom] crate).
36//! For environments that aren't supported by getrandom, disable this feature and
37//! use the `generate_with_rng()` functions instead.
38//!
39//! ### `b64`
40//! On by default. Enable `from_base64` functions for [`Keypair`], [`PublicKey`], [`Signature`], [`Hash`], and [`NetworkKey`].
41//! Also enabled by `alloc`.
42//!
43//! ### `alloc`
44//! On by default. Enable `as_base64() -> String` functions for [`Keypair`], [`PublicKey`], [`Signature`], and [`Hash`].
45//!
46//! ### `sodium`
47//! Use the libsodium/sodiumoxide implementation of the crypto operations.
48//! If the `sodium` and `dalek` features are both enabled, struct methods (eg. [`Keypair::sign`])
49//! will use the dalek implementation. Note that this can happen if multiple dependencies
50//! use ssb-crypto, some preferring `sodium`, and others preferring `dalek`.
51//! To force the methods to use the sodium implementation, enable the `force_sodium` feature.
52//!
53//! WARNING: if you use the sodium implementation, you must call ssb_crypto::sodium::init().
54//! If you don't, libsodium's random-number generation and key-generation functions are not thread-safe.
55//!
56//! ```toml
57//! [dependencies.ssb-crypto]
58//! version = "0.2"
59//! default_features = false
60//! features = ["sodium", "b64"]
61//! ```
62//!
63//! ### `sodium_module`
64//! Enable the `sodium` module, which contains standalone functions
65//! for all the crypto operations, implemented using libsodium/sodiumoxide.
66//! This is mostly useful for testing; eg. `cargo test --features sodium_module`
67//! will test the dalek and sodium implementations for compatibility.
68//! Note that the sodium and dalek modules are hidden from the docs; you'll have
69//! to look at the code if you want to use them directly.
70//!
71//! [getrandom]: https://crates.io/crates/getrandom
72//! [libsodium]: https://libsodium.gitbook.io
73//! [sodiumoxide]: https://crates.io/crates/sodiumoxide
74//! [RustCrypto]: https://github.com/RustCrypto/
75//! [dalek]: https://dalek.rs
76//! [`Hash`]: ./struct.Hash.html
77//! [`Keypair`]: ./struct.Keypair.html
78//! [`Keypair::sign`]: ./struct.Keypair.html#method.sign
79//! [`NetworkKey`]: ./struct.NetworkKey.html
80//! [`PublicKey`]: ./struct.PublicKey.html
81//! [`PublicKey::verify`]: ./struct.PublicKey.html#method.verify
82//! [`Nonce::generate_with_rng`]: ./struct.Nonce.html#method.generate_with_rng
83//! [`Signature`]: ./struct.Signature.html
84//!
85//! # `no_std` support
86//! To build for an embedded (aka `no_std`) environment, disable default features,
87//! enable `dalek` and optionally `b64`.
88//! For example:
89//! ```sh
90//! cargo build --no-default-features --features dalek,b64 --target thumbv7em-none-eabihf
91//! ```
92#![no_std]
93#![warn(missing_docs)]
94
95#[cfg(feature = "alloc")]
96extern crate alloc;
97
98#[cfg(test)]
99#[macro_use]
100extern crate std;
101
102#[cfg(feature = "b64")]
103mod b64;
104
105#[cfg(feature = "dalek_module")]
106#[doc(hidden)]
107pub mod dalek;
108#[cfg(feature = "sodium_module")]
109#[doc(hidden)]
110pub mod sodium;
111
112mod hash;
113pub use hash::*;
114
115mod auth;
116pub use auth::*;
117
118mod sign;
119pub use sign::*;
120
121pub mod ephemeral;
122pub mod secretbox;
123pub mod utils;
124
125pub use zerocopy::{AsBytes, FromBytes};
126
127#[cfg(all(test, any(feature = "sodium", feature = "dalek")))]
128mod tests {
129    #[allow(unused_imports)]
130    use crate::{ephemeral::*, Keypair, PublicKey};
131
132    #[test]
133    #[cfg(any(feature = "sodium", feature = "getrandom"))]
134    fn shared_secret_with_zero() {
135        let (c_eph_pk, _) = generate_ephemeral_keypair();
136        let c_keys = Keypair::generate();
137
138        let (_, s_eph_sk) = generate_ephemeral_keypair();
139        let s_keys = Keypair::generate();
140
141        assert!(derive_shared_secret(&s_eph_sk, &c_eph_pk).is_some());
142        // let zero_eph_pk = EphPublicKey([0; EphPublicKey::SIZE]);
143        // assert!(derive_shared_secret(&s_eph_sk, &zero_eph_pk).is_none());
144
145        assert!(derive_shared_secret_pk(&s_eph_sk, &c_keys.public).is_some());
146        let zero_pk = PublicKey([0; PublicKey::SIZE]);
147        assert!(derive_shared_secret_pk(&s_eph_sk, &zero_pk).is_none());
148
149        assert!(derive_shared_secret_sk(&s_keys.secret, &c_eph_pk).is_some());
150        // assert!(derive_shared_secret_sk(&s_keys.secret, &zero_eph_pk).is_none());
151    }
152}
153
154#[cfg(all(
155    test,
156    any(feature = "sodium", feature = "dalek"),
157    all(feature = "dalek_module", feature = "sodium_module")
158))]
159mod dalek_vs_sodium {
160    #[cfg(any(feature = "force_sodium", not(feature = "dalek")))]
161    use crate::dalek as other;
162    #[cfg(all(feature = "dalek", not(feature = "force_sodium")))]
163    use crate::sodium as other;
164
165    use crate::dalek;
166    use crate::sodium;
167    use crate::Keypair;
168
169    #[test]
170    #[cfg(any(feature = "sodium", feature = "getrandom"))]
171    fn auth() {
172        use crate::{ephemeral::*, NetworkKey};
173
174        let (pk, _sk) = generate_ephemeral_keypair();
175        let netkey = NetworkKey::SSB_MAIN_NET;
176        let auth = netkey.authenticate(&pk.0);
177        let auth2 = other::auth::authenticate(&netkey, &pk.0);
178        assert_eq!(auth.0, auth2.0);
179
180        assert!(netkey.verify(&auth, &pk.0));
181        assert!(other::auth::verify(&netkey, &auth, &pk.0));
182    }
183
184    #[test]
185    fn auth_test_vecs() {
186        use crate::NetworkKey;
187        use hmac::{Hmac, Mac, NewMac};
188        use sha2::Sha512;
189
190        let key = NetworkKey([
191            74, 101, 102, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
192            0, 0, 0, 0, 0,
193        ]);
194        let c = [
195            0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20, 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e,
196            0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x3f,
197        ];
198
199        let a_expected = [
200            0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2, 0xe3, 0x95, 0xfb, 0xe7, 0x3b, 0x56,
201            0xe0, 0xa3, 0x87, 0xbd, 0x64, 0x22, 0x2e, 0x83, 0x1f, 0xd6, 0x10, 0x27, 0x0c, 0xd7,
202            0xea, 0x25, 0x05, 0x54,
203        ];
204        let a1 = sodium::auth::authenticate(&key, &c);
205        assert_eq!(a1.0, a_expected);
206
207        let mut mac = Hmac::<Sha512>::new_varkey(&key.0).unwrap();
208        mac.update(&c);
209        assert_eq!(mac.finalize().into_bytes().as_ref()[..32], a_expected);
210
211        let a2 = dalek::auth::authenticate(&key, &c);
212        assert_eq!(a2.0, a_expected);
213    }
214    #[test]
215    fn hash() {
216        let m = "hello this is a message".as_bytes();
217        assert_eq!(crate::hash::hash(m), other::hash::hash(m));
218    }
219
220    #[test]
221    #[cfg(any(feature = "sodium", feature = "getrandom"))]
222    fn sign() {
223        let m = "hello this is a message".as_bytes();
224        let kp = Keypair::generate();
225        let sig1 = kp.sign(m);
226        let sig2 = other::sign::sign(&kp, m);
227
228        assert_eq!(sig1, sig2);
229        assert!(kp.public.verify(&sig1, m));
230        assert!(other::sign::verify(&kp.public, &sig1, m));
231    }
232
233    #[test]
234    fn sign_test_vecs() {
235        use hex::decode;
236
237        let vecs = [("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
238		     "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
239		     "",
240		     "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"),
241		    ("833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42",
242		     "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf",
243		     "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
244		     "dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704")];
245
246        for (sk, pk, msg, sig) in &vecs {
247            let sk = decode(sk).unwrap();
248            let pk = decode(pk).unwrap();
249            let msg = decode(msg).unwrap();
250            let sig = decode(sig).unwrap();
251
252            let s_k = sodium::sign::keypair_from_seed(&sk).unwrap();
253            let d_k = dalek::sign::keypair_from_seed(&sk).unwrap();
254
255            assert_eq!(s_k.public, d_k.public);
256            assert_eq!(&s_k.public.0, &pk[..]);
257            assert_eq!(s_k.secret.0, d_k.secret.0);
258
259            let sig1 = s_k.sign(&msg);
260            let sig2 = d_k.sign(&msg);
261            dbg!(sig1);
262            dbg!(sig2);
263            assert_eq!(sig1, sig2);
264            assert_eq!(&sig[..], &sig1.0[..]);
265            assert!(s_k.public.verify(&sig1, &msg));
266            assert!(d_k.public.verify(&sig1, &msg));
267        }
268    }
269
270    #[test]
271    fn ephemeral() {
272        use dalek::ephemeral as dal;
273        use sodium::ephemeral as sod;
274        use x25519_dalek as x25519;
275
276        let (xp, xs) = sod::generate_ephemeral_keypair();
277        let (yp, ys) = sod::generate_ephemeral_keypair();
278
279        let xs2 = x25519::StaticSecret::from(xs.0);
280        let xp2 = x25519::PublicKey::from(&xs2);
281        assert_eq!(&xp.0, xp2.as_bytes());
282
283        let ys2 = x25519::StaticSecret::from(ys.0);
284        let yp2 = x25519::PublicKey::from(&ys2);
285        let yp3 = x25519::PublicKey::from(yp.0);
286        assert_eq!(&yp.0, yp2.as_bytes());
287        assert_eq!(&yp.0, yp3.as_bytes());
288
289        let dh = xs2.diffie_hellman(&yp2);
290        let sod_secret = sod::derive_shared_secret(&xs, &yp).unwrap();
291        let dal_secret = dal::derive_shared_secret(&xs, &yp).unwrap();
292        assert_eq!(&sod_secret.0, dh.as_bytes());
293        assert_eq!(&dal_secret.0, dh.as_bytes());
294
295        let dh = ys2.diffie_hellman(&xp2);
296        let sod_secret = sod::derive_shared_secret(&ys, &xp).unwrap();
297        let dal_secret = dal::derive_shared_secret(&ys, &xp).unwrap();
298        assert_eq!(&sod_secret.0, dh.as_bytes());
299        assert_eq!(&dal_secret.0, dh.as_bytes());
300    }
301}