Coverage Report

Created: 2025-07-18 06:08

/rust/registry/src/index.crates.io-6f17d22bba15001f/ring-0.17.14/src/agreement.rs
Line
Count
Source (jump to first uncovered line)
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 AUTHOR DISCLAIMS ALL WARRANTIES
8
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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
0
    fn eq(&self, other: &Self) -> bool {
86
0
        self.curve.id == other.curve.id
87
0
    }
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
0
    pub fn generate(
107
0
        alg: &'static Algorithm,
108
0
        rng: &dyn rand::SecureRandom,
109
0
    ) -> Result<Self, error::Unspecified> {
110
0
        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
0
        let private_key = ec::Seed::generate(alg.curve, rng, cpu_features)?;
117
0
        Ok(Self {
118
0
            private_key,
119
0
            algorithm: alg,
120
0
        })
121
0
    }
122
123
    /// Computes the public key from the private key.
124
    #[inline(always)]
125
0
    pub fn compute_public_key(&self) -> Result<PublicKey, error::Unspecified> {
126
0
        // NSA Guide Step 1.
127
0
        //
128
0
        // Obviously, this only handles the part of Step 1 between the private
129
0
        // key generation and the sending of the public key to the peer. `out`
130
0
        // is what should be sent to the peer.
131
0
        self.private_key
132
0
            .compute_public_key(cpu::features())
133
0
            .map(|public_key| PublicKey {
134
0
                algorithm: self.algorithm,
135
0
                bytes: public_key,
136
0
            })
137
0
    }
138
139
    /// The algorithm for the private key.
140
    #[inline]
141
0
    pub fn algorithm(&self) -> &'static Algorithm {
142
0
        self.algorithm
143
0
    }
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
0
    fn as_ref(&self) -> &[u8] {
167
0
        self.bytes.as_ref()
168
0
    }
169
}
170
171
impl core::fmt::Debug for PublicKey {
172
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
173
0
        f.debug_struct("PublicKey")
174
0
            .field("algorithm", &self.algorithm)
175
0
            .field("bytes", &debug::HexStr(self.as_ref()))
176
0
            .finish()
177
0
    }
178
}
179
180
impl PublicKey {
181
    /// The algorithm for the public key.
182
    #[inline]
183
0
    pub fn algorithm(&self) -> &'static Algorithm {
184
0
        self.algorithm
185
0
    }
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
0
    fn as_ref(&self) -> &[u8] {
200
0
        self.bytes.as_ref()
201
0
    }
202
}
203
204
impl<B: core::fmt::Debug> core::fmt::Debug for UnparsedPublicKey<B>
205
where
206
    B: AsRef<[u8]>,
207
{
208
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
209
0
        f.debug_struct("UnparsedPublicKey")
210
0
            .field("algorithm", &self.algorithm)
211
0
            .field("bytes", &debug::HexStr(self.bytes.as_ref()))
212
0
            .finish()
213
0
    }
214
}
215
216
impl<B> UnparsedPublicKey<B> {
217
    /// Constructs a new `UnparsedPublicKey`.
218
0
    pub fn new(algorithm: &'static Algorithm, bytes: B) -> Self {
219
0
        Self { algorithm, bytes }
220
0
    }
221
222
    /// The algorithm for the public key.
223
    #[inline]
224
0
    pub fn algorithm(&self) -> &'static Algorithm {
225
0
        self.algorithm
226
0
    }
227
228
    /// TODO: doc
229
    #[inline]
230
0
    pub fn bytes(&self) -> &B {
231
0
        &self.bytes
232
0
    }
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
0
pub fn agree_ephemeral<B: AsRef<[u8]>, R>(
254
0
    my_private_key: EphemeralPrivateKey,
255
0
    peer_public_key: &UnparsedPublicKey<B>,
256
0
    kdf: impl FnOnce(&[u8]) -> R,
257
0
) -> Result<R, error::Unspecified> {
258
0
    let peer_public_key = UnparsedPublicKey {
259
0
        algorithm: peer_public_key.algorithm,
260
0
        bytes: peer_public_key.bytes.as_ref(),
261
0
    };
262
0
    agree_ephemeral_(my_private_key, peer_public_key, kdf, cpu::features())
263
0
}
264
265
0
fn agree_ephemeral_<R>(
266
0
    my_private_key: EphemeralPrivateKey,
267
0
    peer_public_key: UnparsedPublicKey<&[u8]>,
268
0
    kdf: impl FnOnce(&[u8]) -> R,
269
0
    cpu: cpu::Features,
270
0
) -> Result<R, error::Unspecified> {
271
0
    // NSA Guide Prerequisite 1.
272
0
    //
273
0
    // The domain parameters are hard-coded. This check verifies that the
274
0
    // peer's public key's domain parameters match the domain parameters of
275
0
    // this private key.
276
0
    if peer_public_key.algorithm != my_private_key.algorithm {
277
0
        return Err(error::Unspecified);
278
0
    }
279
0
280
0
    let alg = &my_private_key.algorithm;
281
0
282
0
    // NSA Guide Prerequisite 2, regarding which KDFs are allowed, is delegated
283
0
    // to the caller.
284
0
285
0
    // NSA Guide Prerequisite 3, "Prior to or during the key-agreement process,
286
0
    // each party shall obtain the identifier associated with the other party
287
0
    // during the key-agreement scheme," is delegated to the caller.
288
0
289
0
    // NSA Guide Step 1 is handled by `EphemeralPrivateKey::generate()` and
290
0
    // `EphemeralPrivateKey::compute_public_key()`.
291
0
292
0
    let mut shared_key = [0u8; ec::ELEM_MAX_BYTES];
293
0
    let shared_key = &mut shared_key[..alg.curve.elem_scalar_seed_len];
294
0
295
0
    // NSA Guide Steps 2, 3, and 4.
296
0
    //
297
0
    // We have a pretty liberal interpretation of the NIST's spec's "Destroy"
298
0
    // that doesn't meet the NSA requirement to "zeroize."
299
0
    (alg.ecdh)(
300
0
        shared_key,
301
0
        &my_private_key.private_key,
302
0
        untrusted::Input::from(peer_public_key.bytes),
303
0
        cpu,
304
0
    )?;
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
0
    Ok(kdf(shared_key))
311
0
}