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
//! Elliptic Curve Diffie-Hellman (ECDH) on P-256.
//!
//! BLE uses ECDH on P-256 for pairing. This module provides an interface for plugging in different
//! implementations of the P-256 operations. The main consumer of this module is the [`security`]
//! module; refer to that for more info about pairing and encryption in BLE.
//!
//! The primary trait in this module is [`EcdhProvider`]. Rubble comes with 2 built-in
//! implementations of that trait:
//!
//! * [`P256Provider`] and [`P256SecretKey`]: These use the pure-Rust [`p256`] crate and are always
//! available.
//! * [`RingProvider`] and [`RingSecretKey`] (behind the **`ring`** Cargo feature): These use the
//! [*ring*][ring] library to provide the operations. Note that *ring* does not support
//! `#![no_std]` operation, so this is mostly useful for tests and other non-embedded usage.
//!
//! [`security`]: crate::security
//! [ring]: https://github.com/briansmith/ring
//! [`p256`]: https://docs.rs/p256
mod p256;
pub use self::p256::*;
#[cfg(feature = "ring")]
mod ring;
#[cfg(feature = "ring")]
pub use self::ring::*;
use core::fmt;
use rand_core::{CryptoRng, RngCore};
/// A P-256 public key (point on the curve) in uncompressed format.
///
/// The encoding is as specified in *[SEC 1: Elliptic Curve Cryptography]*, but without the leading
/// `0x04` byte: The first 32 Bytes are the big-endian encoding of the point's X coordinate, and the
/// remaining 32 Bytes are the Y coordinate, encoded the same way.
///
/// Note that this type does not provide any validity guarantees (unlike [`PrivateKey`]
/// implementors): It is possible to represent invalid public P-256 keys, such as the point at
/// infinity, with this type. The other APIs in this module are designed to take that into account.
///
/// [SEC 1: Elliptic Curve Cryptography]: http://www.secg.org/sec1-v2.pdf
/// [`PrivateKey`]: trait.PrivateKey.html
pub struct PublicKey(pub [u8; 64]);
/// A shared secret resulting from an ECDH key agreement.
///
/// This is returned by implementations of [`SecretKey::agree`].
///
/// [`SecretKey::agree`]: trait.SecretKey.html#tymethod.agree
pub struct SharedSecret(pub [u8; 32]);
/// Error returned by [`SecretKey::agree`] when the public key of the other party is invalid.
///
/// [`SecretKey::agree`]: trait.SecretKey.html#tymethod.agree
#[derive(Debug)]
pub struct InvalidPublicKey {}
impl InvalidPublicKey {
/// Creates a new `InvalidPublicKey` error.
pub fn new() -> Self {
Self {}
}
}
impl fmt::Display for InvalidPublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("invalid public key")
}
}
/// Trait for ECDH providers.
pub trait EcdhProvider {
/// Provider-defined secret key type.
type SecretKey: SecretKey;
/// Generates a P-256 key pair using cryptographically strong randomness.
///
/// Implementors must ensure that they only return valid private/public key pairs from this
/// method.
///
/// Rubble will pass a cryptographically secure random number generator `rng` to this function
/// that may be used to obtain entropy for key generation. Implementations may also use their
/// own RNG if they so choose.
fn generate_keypair<R>(&mut self, rng: &mut R) -> (Self::SecretKey, PublicKey)
where
R: RngCore + CryptoRng;
}
/// Secret key operations required by Rubble.
///
/// This API imposes no requirements on the representation or location of secret keys. This means
/// that it should be possible to implement this trait even for keys stored in some secure key
/// storage like a smartcard.
pub trait SecretKey: Sized {
/// Performs ECDH key agreement using an ephemeral secret key `self` and the public key of the
/// other party.
///
/// Here, "ephemeral" just means that this method takes `self` by value. This allows
/// implementing `SecretKey` for providers that enforce single-use keys using Rust ownership
/// (like *ring*).
///
/// # Errors
///
/// If `foreign_key` is an invalid public key, implementors must return an error.
fn agree(self, foreign_key: &PublicKey) -> Result<SharedSecret, InvalidPublicKey>;
}
/// Runs Rubble's P-256 provider testsuite against `provider`.
///
/// Note that this is just a quick smoke test that does not provide any assurance about security
/// properties. The P-256 provider should have a dedicated test suite.
pub fn run_tests(mut provider: impl EcdhProvider) {
static RNG: &[u8] = &[
0x1e, 0x66, 0x81, 0xb6, 0xa3, 0x4e, 0x06, 0x97, 0x75, 0xbe, 0xd4, 0x5c, 0xf9, 0x52, 0x3f,
0xf1, 0x5b, 0x6a, 0x72, 0xe2, 0xb8, 0x35, 0xb3, 0x29, 0x5e, 0xe0, 0xbb, 0x92, 0x35, 0xa5,
0xb9, 0x60, 0xc9, 0xaf, 0xe2, 0x72, 0x12, 0xf1, 0xc4, 0xfc, 0x10, 0x2d, 0x63, 0x2f, 0x05,
0xd6, 0xe5, 0x0a, 0xbf, 0x2c, 0xb9, 0x02, 0x3a, 0x67, 0x23, 0x63, 0x36, 0x7a, 0x62, 0xe6,
0x63, 0xce, 0x28, 0x98,
];
// Pretend-RNG that returns a fixed sequence of pregenerated numbers. Do not do this outside of
// tests.
struct Rng(&'static [u8]);
impl RngCore for Rng {
fn next_u32(&mut self) -> u32 {
rand_core::impls::next_u32_via_fill(self)
}
fn next_u64(&mut self) -> u64 {
rand_core::impls::next_u64_via_fill(self)
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
if self.0.len() < dest.len() {
panic!("p256::run_tests: ran out of pregenerated entropy");
}
for chunk in dest.chunks_mut(self.0.len()) {
chunk.copy_from_slice(&self.0[..chunk.len()]);
self.0 = &self.0[chunk.len()..];
}
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
self.fill_bytes(dest);
Ok(())
}
}
impl CryptoRng for Rng {}
// Test that different key pairs will be generated:
let mut rng = Rng(RNG);
let (secret1, public1) = provider.generate_keypair(&mut rng);
let (secret2, public2) = provider.generate_keypair(&mut rng);
assert_ne!(&public1.0[..], &public2.0[..]);
// Test that ECDH agreement results in the same shared secret:
let shared1 = secret1.agree(&public2).unwrap();
let shared2 = secret2.agree(&public1).unwrap();
assert_eq!(shared1.0, shared2.0);
// Now, test that ECDH agreement with invalid public keys fails correctly.
// Point at infinity is an invalid public key:
let infty = PublicKey([0; 64]);
let (secret, _) = provider.generate_keypair(&mut Rng(RNG));
assert!(secret.agree(&infty).is_err());
// Malicious public key not on the curve:
// (taken from https://web-in-security.blogspot.com/2015/09/practical-invalid-curve-attacks.html)
let x = [
0xb7, 0x0b, 0xf0, 0x43, 0xc1, 0x44, 0x93, 0x57, 0x56, 0xf8, 0xf4, 0x57, 0x8c, 0x36, 0x9c,
0xf9, 0x60, 0xee, 0x51, 0x0a, 0x5a, 0x0f, 0x90, 0xe9, 0x3a, 0x37, 0x3a, 0x21, 0xf0, 0xd1,
0x39, 0x7f,
];
let y = [
0x4a, 0x2e, 0x0d, 0xed, 0x57, 0xa5, 0x15, 0x6b, 0xb8, 0x2e, 0xb4, 0x31, 0x4c, 0x37, 0xfd,
0x41, 0x55, 0x39, 0x5a, 0x7e, 0x51, 0x98, 0x8a, 0xf2, 0x89, 0xcc, 0xe5, 0x31, 0xb9, 0xc1,
0x71, 0x92,
];
let mut key = [0; 64];
key[..32].copy_from_slice(&x);
key[32..].copy_from_slice(&y);
let (secret, _) = provider.generate_keypair(&mut Rng(RNG));
assert!(secret.agree(&PublicKey(key)).is_err());
}