1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
//! This crate provides the cryptographic functionality needed to implement
//! the Secure Scuttlebutt networking protocols and content signing and encryption.
//!
//! There are two implementations of the crypto operations available; one that uses
//! [libsodium] C library (via the [sodiumoxide] crate), and a pure-rust implementation
//! that uses [dalek] and [RustCrypto] crates (which is the default). You can select which
//! implementation to use via Cargo.toml feature flags (see below).
//!
//! # Features
//!
//! If you only need the struct definitions and basic operations,
//! disable default features, and (optionally) enable b64.
//!
//! ```toml
//! [dependencies.ssb-crypto]
//! version = "0.2"
//! default_features = false
//! features = ["b64"]
//! ```
//!
//! ### `dalek`
//! On by default. Use the dalek/RustCrypto implementation of the crypto operations.
//! The crypto functionality is exposed via convenient methods, eg [`Keypair::sign`] and
//! [`PublicKey::verify`]. If neither `dalek` nor `sodium` features are enabled,
//! these methods won't be available.
//!
//! ### `rand`
//! On by default. Provide functions to generate keys and nonces with user-specified
//! cryptographically-secure random number generator (Eg. [`Nonce::generate_with_rng`]).
//! These functions can be used in `no_std` environments that aren't supported by `getrandom`.
//! Enabled if `dalek` is enabled.
//!
//! ### `getrandom`
//! On by default. Provide functions to generate keys and nonces using the OS-provided
//! cryptographically-secure random number generator (via the [getrandom] crate).
//! For environments that aren't supported by getrandom, disable this feature and
//! use the `generate_with_rng()` functions instead.
//!
//! ### `b64`
//! On by default. Enable `from_base64` functions for [`Keypair`], [`PublicKey`], [`Signature`], [`Hash`], and [`NetworkKey`].
//! Also enabled by `alloc`.
//!
//! ### `alloc`
//! On by default. Enable `as_base64() -> String` functions for [`Keypair`], [`PublicKey`], [`Signature`], and [`Hash`].
//!
//! ### `sodium`
//! Use the libsodium/sodiumoxide implementation of the crypto operations.
//! If the `sodium` and `dalek` features are both enabled, struct methods (eg. [`Keypair::sign`])
//! will use the dalek implementation. Note that this can happen if multiple dependencies
//! use ssb-crypto, some preferring `sodium`, and others preferring `dalek`.
//! To force the methods to use the sodium implementation, enable the `force_sodium` feature.
//!
//! WARNING: if you use the sodium implementation, you must call ssb_crypto::sodium::init().
//! If you don't, libsodium's random-number generation and key-generation functions are not thread-safe.
//!
//! ```toml
//! [dependencies.ssb-crypto]
//! version = "0.2"
//! default_features = false
//! features = ["sodium", "b64"]
//! ```
//!
//! ### `sodium_module`
//! Enable the `sodium` module, which contains standalone functions
//! for all the crypto operations, implemented using libsodium/sodiumoxide.
//! This is mostly useful for testing; eg. `cargo test --features sodium_module`
//! will test the dalek and sodium implementations for compatibility.
//! Note that the sodium and dalek modules are hidden from the docs; you'll have
//! to look at the code if you want to use them directly.
//!
//! [getrandom]: https://crates.io/crates/getrandom
//! [libsodium]: https://libsodium.gitbook.io
//! [sodiumoxide]: https://crates.io/crates/sodiumoxide
//! [RustCrypto]: https://github.com/RustCrypto/
//! [dalek]: https://dalek.rs
//! [`Hash`]: ./struct.Hash.html
//! [`Keypair`]: ./struct.Keypair.html
//! [`Keypair::sign`]: ./struct.Keypair.html#method.sign
//! [`NetworkKey`]: ./struct.NetworkKey.html
//! [`PublicKey`]: ./struct.PublicKey.html
//! [`PublicKey::verify`]: ./struct.PublicKey.html#method.verify
//! [`Nonce::generate_with_rng`]: ./struct.Nonce.html#method.generate_with_rng
//! [`Signature`]: ./struct.Signature.html
//!
//! # `no_std` support
//! To build for an embedded (aka `no_std`) environment, disable default features,
//! enable `dalek` and optionally `b64`.
//! For example:
//! ```sh
//! cargo build --no-default-features --features dalek,b64 --target thumbv7em-none-eabihf
//! ```
#![no_std]
#![warn(missing_docs)]

#[cfg(feature = "alloc")]
extern crate alloc;

#[cfg(test)]
#[macro_use]
extern crate std;

#[cfg(feature = "b64")]
mod b64;

#[cfg(feature = "dalek_module")]
#[doc(hidden)]
pub mod dalek;
#[cfg(feature = "sodium_module")]
#[doc(hidden)]
pub mod sodium;

mod hash;
pub use hash::*;

mod auth;
pub use auth::*;

mod sign;
pub use sign::*;

pub mod ephemeral;
pub mod secretbox;
pub mod utils;

pub use zerocopy::{AsBytes, FromBytes};

#[cfg(all(test, any(feature = "sodium", feature = "dalek")))]
mod tests {
    #[allow(unused_imports)]
    use crate::{ephemeral::*, Keypair, PublicKey};

    #[test]
    #[cfg(any(feature = "sodium", feature = "getrandom"))]
    fn shared_secret_with_zero() {
        let (c_eph_pk, _) = generate_ephemeral_keypair();
        let c_keys = Keypair::generate();

        let (_, s_eph_sk) = generate_ephemeral_keypair();
        let s_keys = Keypair::generate();

        assert!(derive_shared_secret(&s_eph_sk, &c_eph_pk).is_some());
        // let zero_eph_pk = EphPublicKey([0; EphPublicKey::SIZE]);
        // assert!(derive_shared_secret(&s_eph_sk, &zero_eph_pk).is_none());

        assert!(derive_shared_secret_pk(&s_eph_sk, &c_keys.public).is_some());
        let zero_pk = PublicKey([0; PublicKey::SIZE]);
        assert!(derive_shared_secret_pk(&s_eph_sk, &zero_pk).is_none());

        assert!(derive_shared_secret_sk(&s_keys.secret, &c_eph_pk).is_some());
        // assert!(derive_shared_secret_sk(&s_keys.secret, &zero_eph_pk).is_none());
    }
}

#[cfg(all(
    test,
    any(feature = "sodium", feature = "dalek"),
    all(feature = "dalek_module", feature = "sodium_module")
))]
mod dalek_vs_sodium {
    #[cfg(any(feature = "force_sodium", not(feature = "dalek")))]
    use crate::dalek as other;
    #[cfg(all(feature = "dalek", not(feature = "force_sodium")))]
    use crate::sodium as other;

    use crate::dalek;
    use crate::sodium;
    use crate::Keypair;

    #[test]
    #[cfg(any(feature = "sodium", feature = "getrandom"))]
    fn auth() {
        use crate::{ephemeral::*, NetworkKey};

        let (pk, _sk) = generate_ephemeral_keypair();
        let netkey = NetworkKey::SSB_MAIN_NET;
        let auth = netkey.authenticate(&pk.0);
        let auth2 = other::auth::authenticate(&netkey, &pk.0);
        assert_eq!(auth.0, auth2.0);

        assert!(netkey.verify(&auth, &pk.0));
        assert!(other::auth::verify(&netkey, &auth, &pk.0));
    }

    #[test]
    fn auth_test_vecs() {
        use crate::NetworkKey;
        use hmac::{Hmac, Mac, NewMac};
        use sha2::Sha512;

        let key = NetworkKey([
            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,
            0, 0, 0, 0, 0,
        ]);
        let c = [
            0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20, 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e,
            0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x3f,
        ];

        let a_expected = [
            0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2, 0xe3, 0x95, 0xfb, 0xe7, 0x3b, 0x56,
            0xe0, 0xa3, 0x87, 0xbd, 0x64, 0x22, 0x2e, 0x83, 0x1f, 0xd6, 0x10, 0x27, 0x0c, 0xd7,
            0xea, 0x25, 0x05, 0x54,
        ];
        let a1 = sodium::auth::authenticate(&key, &c);
        assert_eq!(a1.0, a_expected);

        let mut mac = Hmac::<Sha512>::new_varkey(&key.0).unwrap();
        mac.update(&c);
        assert_eq!(mac.finalize().into_bytes().as_ref()[..32], a_expected);

        let a2 = dalek::auth::authenticate(&key, &c);
        assert_eq!(a2.0, a_expected);
    }
    #[test]
    fn hash() {
        let m = "hello this is a message".as_bytes();
        assert_eq!(crate::hash::hash(m), other::hash::hash(m));
    }

    #[test]
    #[cfg(any(feature = "sodium", feature = "getrandom"))]
    fn sign() {
        let m = "hello this is a message".as_bytes();
        let kp = Keypair::generate();
        let sig1 = kp.sign(m);
        let sig2 = other::sign::sign(&kp, m);

        assert_eq!(sig1, sig2);
        assert!(kp.public.verify(&sig1, m));
        assert!(other::sign::verify(&kp.public, &sig1, m));
    }

    #[test]
    fn sign_test_vecs() {
        use hex::decode;

        let vecs = [("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
		     "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
		     "",
		     "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"),
		    ("833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42",
		     "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf",
		     "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
		     "dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704")];

        for (sk, pk, msg, sig) in &vecs {
            let sk = decode(sk).unwrap();
            let pk = decode(pk).unwrap();
            let msg = decode(msg).unwrap();
            let sig = decode(sig).unwrap();

            let s_k = sodium::sign::keypair_from_seed(&sk).unwrap();
            let d_k = dalek::sign::keypair_from_seed(&sk).unwrap();

            assert_eq!(s_k.public, d_k.public);
            assert_eq!(&s_k.public.0, &pk[..]);
            assert_eq!(s_k.secret.0, d_k.secret.0);

            let sig1 = s_k.sign(&msg);
            let sig2 = d_k.sign(&msg);
            dbg!(sig1);
            dbg!(sig2);
            assert_eq!(sig1, sig2);
            assert_eq!(&sig[..], &sig1.0[..]);
            assert!(s_k.public.verify(&sig1, &msg));
            assert!(d_k.public.verify(&sig1, &msg));
        }
    }

    #[test]
    fn ephemeral() {
        use dalek::ephemeral as dal;
        use sodium::ephemeral as sod;
        use x25519_dalek as x25519;

        let (xp, xs) = sod::generate_ephemeral_keypair();
        let (yp, ys) = sod::generate_ephemeral_keypair();

        let xs2 = x25519::StaticSecret::from(xs.0);
        let xp2 = x25519::PublicKey::from(&xs2);
        assert_eq!(&xp.0, xp2.as_bytes());

        let ys2 = x25519::StaticSecret::from(ys.0);
        let yp2 = x25519::PublicKey::from(&ys2);
        let yp3 = x25519::PublicKey::from(yp.0);
        assert_eq!(&yp.0, yp2.as_bytes());
        assert_eq!(&yp.0, yp3.as_bytes());

        let dh = xs2.diffie_hellman(&yp2);
        let sod_secret = sod::derive_shared_secret(&xs, &yp).unwrap();
        let dal_secret = dal::derive_shared_secret(&xs, &yp).unwrap();
        assert_eq!(&sod_secret.0, dh.as_bytes());
        assert_eq!(&dal_secret.0, dh.as_bytes());

        let dh = ys2.diffie_hellman(&xp2);
        let sod_secret = sod::derive_shared_secret(&ys, &xp).unwrap();
        let dal_secret = dal::derive_shared_secret(&ys, &xp).unwrap();
        assert_eq!(&sod_secret.0, dh.as_bytes());
        assert_eq!(&dal_secret.0, dh.as_bytes());
    }
}