| 1 | // Copyright 2015-2017 Brian Smith. |
| 2 | // |
| 3 | // Permission to use, copy, modify, and/or distribute this software for any |
| 4 | // purpose with or without fee is hereby granted, provided that the above |
| 5 | // copyright notice and this permission notice appear in all copies. |
| 6 | // |
| 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
| 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY |
| 10 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| 12 | // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| 13 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 14 | |
| 15 | //! Key Agreement: ECDH, including X25519. |
| 16 | //! |
| 17 | //! # Example |
| 18 | //! |
| 19 | //! Note that this example uses X25519, but ECDH using NIST P-256/P-384 is done |
| 20 | //! exactly the same way, just substituting |
| 21 | //! `agreement::ECDH_P256`/`agreement::ECDH_P384` for `agreement::X25519`. |
| 22 | //! |
| 23 | //! ``` |
| 24 | //! use ring::{agreement, rand}; |
| 25 | //! |
| 26 | //! let rng = rand::SystemRandom::new(); |
| 27 | //! |
| 28 | //! let my_private_key = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)?; |
| 29 | //! |
| 30 | //! // Make `my_public_key` a byte slice containing my public key. In a real |
| 31 | //! // application, this would be sent to the peer in an encoded protocol |
| 32 | //! // message. |
| 33 | //! let my_public_key = my_private_key.compute_public_key()?; |
| 34 | //! |
| 35 | //! let peer_public_key_bytes = { |
| 36 | //! // In a real application, the peer public key would be parsed out of a |
| 37 | //! // protocol message. Here we just generate one. |
| 38 | //! let peer_private_key = |
| 39 | //! agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)?; |
| 40 | //! peer_private_key.compute_public_key()? |
| 41 | //! }; |
| 42 | //! |
| 43 | //! let peer_public_key = agreement::UnparsedPublicKey::new( |
| 44 | //! &agreement::X25519, |
| 45 | //! peer_public_key_bytes); |
| 46 | //! |
| 47 | //! agreement::agree_ephemeral( |
| 48 | //! my_private_key, |
| 49 | //! &peer_public_key, |
| 50 | //! |_key_material| { |
| 51 | //! // In a real application, we'd apply a KDF to the key material and the |
| 52 | //! // public keys (as recommended in RFC 7748) and then derive session |
| 53 | //! // keys from the result. We omit all that here. |
| 54 | //! }, |
| 55 | //! )?; |
| 56 | //! |
| 57 | //! # Ok::<(), ring::error::Unspecified>(()) |
| 58 | //! ``` |
| 59 | |
| 60 | // The "NSA Guide" steps here are from from section 3.1, "Ephemeral Unified |
| 61 | // Model." |
| 62 | |
| 63 | use crate::{cpu, debug, ec, error, rand}; |
| 64 | |
| 65 | pub use crate::ec::{ |
| 66 | curve25519::x25519::X25519, |
| 67 | suite_b::ecdh::{ECDH_P256, ECDH_P384}, |
| 68 | }; |
| 69 | |
| 70 | /// A key agreement algorithm. |
| 71 | pub struct Algorithm { |
| 72 | pub(crate) curve: &'static ec::Curve, |
| 73 | pub(crate) ecdh: fn( |
| 74 | out: &mut [u8], |
| 75 | private_key: &ec::Seed, |
| 76 | peer_public_key: untrusted::Input, |
| 77 | cpu: cpu::Features, |
| 78 | ) -> Result<(), error::Unspecified>, |
| 79 | } |
| 80 | |
| 81 | derive_debug_via_field!(Algorithm, curve); |
| 82 | |
| 83 | impl Eq for Algorithm {} |
| 84 | impl PartialEq for Algorithm { |
| 85 | fn eq(&self, other: &Self) -> bool { |
| 86 | self.curve.id == other.curve.id |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | /// An ephemeral private key for use (only) with `agree_ephemeral`. The |
| 91 | /// signature of `agree_ephemeral` ensures that an `EphemeralPrivateKey` can be |
| 92 | /// used for at most one key agreement. |
| 93 | pub struct EphemeralPrivateKey { |
| 94 | private_key: ec::Seed, |
| 95 | algorithm: &'static Algorithm, |
| 96 | } |
| 97 | |
| 98 | derive_debug_via_field!( |
| 99 | EphemeralPrivateKey, |
| 100 | stringify!(EphemeralPrivateKey), |
| 101 | algorithm |
| 102 | ); |
| 103 | |
| 104 | impl EphemeralPrivateKey { |
| 105 | /// Generate a new ephemeral private key for the given algorithm. |
| 106 | pub fn generate( |
| 107 | alg: &'static Algorithm, |
| 108 | rng: &dyn rand::SecureRandom, |
| 109 | ) -> Result<Self, error::Unspecified> { |
| 110 | let cpu_features = cpu::features(); |
| 111 | |
| 112 | // NSA Guide Step 1. |
| 113 | // |
| 114 | // This only handles the key generation part of step 1. The rest of |
| 115 | // step one is done by `compute_public_key()`. |
| 116 | let private_key = ec::Seed::generate(alg.curve, rng, cpu_features)?; |
| 117 | Ok(Self { |
| 118 | private_key, |
| 119 | algorithm: alg, |
| 120 | }) |
| 121 | } |
| 122 | |
| 123 | /// Computes the public key from the private key. |
| 124 | #[inline (always)] |
| 125 | pub fn compute_public_key(&self) -> Result<PublicKey, error::Unspecified> { |
| 126 | // NSA Guide Step 1. |
| 127 | // |
| 128 | // Obviously, this only handles the part of Step 1 between the private |
| 129 | // key generation and the sending of the public key to the peer. `out` |
| 130 | // is what should be sent to the peer. |
| 131 | self.private_key |
| 132 | .compute_public_key(cpu::features()) |
| 133 | .map(|public_key| PublicKey { |
| 134 | algorithm: self.algorithm, |
| 135 | bytes: public_key, |
| 136 | }) |
| 137 | } |
| 138 | |
| 139 | /// The algorithm for the private key. |
| 140 | #[inline ] |
| 141 | pub fn algorithm(&self) -> &'static Algorithm { |
| 142 | self.algorithm |
| 143 | } |
| 144 | |
| 145 | /// Do not use. |
| 146 | #[deprecated ] |
| 147 | #[cfg (test)] |
| 148 | pub fn bytes(&self) -> &[u8] { |
| 149 | self.bytes_for_test() |
| 150 | } |
| 151 | |
| 152 | #[cfg (test)] |
| 153 | pub(super) fn bytes_for_test(&self) -> &[u8] { |
| 154 | self.private_key.bytes_less_safe() |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | /// A public key for key agreement. |
| 159 | #[derive (Clone)] |
| 160 | pub struct PublicKey { |
| 161 | algorithm: &'static Algorithm, |
| 162 | bytes: ec::PublicKey, |
| 163 | } |
| 164 | |
| 165 | impl AsRef<[u8]> for PublicKey { |
| 166 | fn as_ref(&self) -> &[u8] { |
| 167 | self.bytes.as_ref() |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | impl core::fmt::Debug for PublicKey { |
| 172 | fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { |
| 173 | f&mut DebugStruct<'_, '_>.debug_struct("PublicKey" ) |
| 174 | .field("algorithm" , &self.algorithm) |
| 175 | .field(name:"bytes" , &debug::HexStr(self.as_ref())) |
| 176 | .finish() |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | impl PublicKey { |
| 181 | /// The algorithm for the public key. |
| 182 | #[inline ] |
| 183 | pub fn algorithm(&self) -> &'static Algorithm { |
| 184 | self.algorithm |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | /// An unparsed, possibly malformed, public key for key agreement. |
| 189 | #[derive (Clone, Copy)] |
| 190 | pub struct UnparsedPublicKey<B> { |
| 191 | algorithm: &'static Algorithm, |
| 192 | bytes: B, |
| 193 | } |
| 194 | |
| 195 | impl<B> AsRef<[u8]> for UnparsedPublicKey<B> |
| 196 | where |
| 197 | B: AsRef<[u8]>, |
| 198 | { |
| 199 | fn as_ref(&self) -> &[u8] { |
| 200 | self.bytes.as_ref() |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | impl<B: core::fmt::Debug> core::fmt::Debug for UnparsedPublicKey<B> |
| 205 | where |
| 206 | B: AsRef<[u8]>, |
| 207 | { |
| 208 | fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { |
| 209 | f&mut DebugStruct<'_, '_>.debug_struct("UnparsedPublicKey" ) |
| 210 | .field("algorithm" , &self.algorithm) |
| 211 | .field(name:"bytes" , &debug::HexStr(self.bytes.as_ref())) |
| 212 | .finish() |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | impl<B> UnparsedPublicKey<B> { |
| 217 | /// Constructs a new `UnparsedPublicKey`. |
| 218 | pub fn new(algorithm: &'static Algorithm, bytes: B) -> Self { |
| 219 | Self { algorithm, bytes } |
| 220 | } |
| 221 | |
| 222 | /// The algorithm for the public key. |
| 223 | #[inline ] |
| 224 | pub fn algorithm(&self) -> &'static Algorithm { |
| 225 | self.algorithm |
| 226 | } |
| 227 | |
| 228 | /// TODO: doc |
| 229 | #[inline ] |
| 230 | pub fn bytes(&self) -> &B { |
| 231 | &self.bytes |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | /// Performs a key agreement with an ephemeral private key and the given public |
| 236 | /// key. |
| 237 | /// |
| 238 | /// `my_private_key` is the ephemeral private key to use. Since it is moved, it |
| 239 | /// will not be usable after calling `agree_ephemeral`, thus guaranteeing that |
| 240 | /// the key is used for only one key agreement. |
| 241 | /// |
| 242 | /// `peer_public_key` is the peer's public key. `agree_ephemeral` will return |
| 243 | /// `Err(error_value)` if it does not match `my_private_key's` algorithm/curve. |
| 244 | /// `agree_ephemeral` verifies that it is encoded in the standard form for the |
| 245 | /// algorithm and that the key is *valid*; see the algorithm's documentation for |
| 246 | /// details on how keys are to be encoded and what constitutes a valid key for |
| 247 | /// that algorithm. |
| 248 | /// |
| 249 | /// After the key agreement is done, `agree_ephemeral` calls `kdf` with the raw |
| 250 | /// key material from the key agreement operation and then returns what `kdf` |
| 251 | /// returns. |
| 252 | #[inline ] |
| 253 | pub fn agree_ephemeral<B: AsRef<[u8]>, R>( |
| 254 | my_private_key: EphemeralPrivateKey, |
| 255 | peer_public_key: &UnparsedPublicKey<B>, |
| 256 | kdf: impl FnOnce(&[u8]) -> R, |
| 257 | ) -> Result<R, error::Unspecified> { |
| 258 | let peer_public_key: UnparsedPublicKey<&[u8]> = UnparsedPublicKey { |
| 259 | algorithm: peer_public_key.algorithm, |
| 260 | bytes: peer_public_key.bytes.as_ref(), |
| 261 | }; |
| 262 | agree_ephemeral_(my_private_key, peer_public_key, kdf, cpu:cpu::features()) |
| 263 | } |
| 264 | |
| 265 | fn agree_ephemeral_<R>( |
| 266 | my_private_key: EphemeralPrivateKey, |
| 267 | peer_public_key: UnparsedPublicKey<&[u8]>, |
| 268 | kdf: impl FnOnce(&[u8]) -> R, |
| 269 | cpu: cpu::Features, |
| 270 | ) -> Result<R, error::Unspecified> { |
| 271 | // NSA Guide Prerequisite 1. |
| 272 | // |
| 273 | // The domain parameters are hard-coded. This check verifies that the |
| 274 | // peer's public key's domain parameters match the domain parameters of |
| 275 | // this private key. |
| 276 | if peer_public_key.algorithm != my_private_key.algorithm { |
| 277 | return Err(error::Unspecified); |
| 278 | } |
| 279 | |
| 280 | let alg = &my_private_key.algorithm; |
| 281 | |
| 282 | // NSA Guide Prerequisite 2, regarding which KDFs are allowed, is delegated |
| 283 | // to the caller. |
| 284 | |
| 285 | // NSA Guide Prerequisite 3, "Prior to or during the key-agreement process, |
| 286 | // each party shall obtain the identifier associated with the other party |
| 287 | // during the key-agreement scheme," is delegated to the caller. |
| 288 | |
| 289 | // NSA Guide Step 1 is handled by `EphemeralPrivateKey::generate()` and |
| 290 | // `EphemeralPrivateKey::compute_public_key()`. |
| 291 | |
| 292 | let mut shared_key = [0u8; ec::ELEM_MAX_BYTES]; |
| 293 | let shared_key = &mut shared_key[..alg.curve.elem_scalar_seed_len]; |
| 294 | |
| 295 | // NSA Guide Steps 2, 3, and 4. |
| 296 | // |
| 297 | // We have a pretty liberal interpretation of the NIST's spec's "Destroy" |
| 298 | // that doesn't meet the NSA requirement to "zeroize." |
| 299 | (alg.ecdh)( |
| 300 | shared_key, |
| 301 | &my_private_key.private_key, |
| 302 | untrusted::Input::from(peer_public_key.bytes), |
| 303 | cpu, |
| 304 | )?; |
| 305 | |
| 306 | // NSA Guide Steps 5 and 6. |
| 307 | // |
| 308 | // Again, we have a pretty liberal interpretation of the NIST's spec's |
| 309 | // "Destroy" that doesn't meet the NSA requirement to "zeroize." |
| 310 | Ok(kdf(shared_key)) |
| 311 | } |
| 312 | |