static_dh_ecdh/ecdh/
ecdh.rs

1// #![allow(warnings)]
2
3use core::ops::{Mul};
4use core::{convert::TryInto};
5
6use num_bigint_dig::{BigInt, BigUint, Sign};
7use rand_chacha::rand_core::{RngCore, SeedableRng};
8use rand_chacha::ChaCha20Rng;
9
10use generic_array::{
11    typenum::{self, Unsigned},
12    ArrayLength, GenericArray,
13};
14
15use elliptic_curve::sec1::EncodedPoint as PubKey;
16use elliptic_curve::{sec1::UncompressedPointSize, Curve};
17use p256::{AffinePoint, NistP256, NonZeroScalar, PublicKey, Scalar};
18use p384::{NistP384, SecretKey as P384Secret};
19
20use super::affine_math::{APTypes, EncodedTypes, MyAffinePoint};
21
22use crate::{constants, dh::dh};
23use crate::{CryptoError, Result};
24
25/// Implemented by types that have a fixed-length byte representation
26pub trait ToBytes {
27    /// An associated type to represent the serialized form of the implementing type.
28    type OutputSize: ArrayLength<u8>;
29
30    /// Types implementing this method are serializable
31    fn to_bytes(&self) -> GenericArray<u8, Self::OutputSize>;
32
33    /// Returns the size (in bytes) of this type when serialized
34    fn size() -> usize {
35        Self::OutputSize::to_usize()
36    }
37}
38/// Implemented by types that can be deserialized from byte representation
39pub trait FromBytes: ToBytes + Sized {
40    /// Types implementing this method are de-serializable
41    fn from_bytes(bytes: &[u8]) -> Result<Self>;
42}
43/// An ECDH-P256 private key is simply a scalar in the NIST P-256 field.
44#[derive(Clone)]
45pub struct SkP256(NonZeroScalar);
46/// An ECDH-P256 public key. This is derived from the private key using scalar point multiplication.
47#[derive(Debug, Clone, PartialEq)]
48pub struct PkP256(p256::PublicKey);
49
50// Everything is serialized and deserialized in uncompressed form
51impl ToBytes for PkP256 {
52    // A fancy way of saying "65 bytes"
53    type OutputSize = UncompressedPointSize<NistP256>;
54
55    fn to_bytes(&self) -> GenericArray<u8, Self::OutputSize> {
56        // Get the uncompressed pubkey encoding
57        let bytes = p256::EncodedPoint::encode(self.0, false);
58        GenericArray::clone_from_slice(bytes.as_bytes())
59    }
60}
61
62// Everything is serialized and deserialized in uncompressed form
63impl FromBytes for PkP256 {
64    fn from_bytes(bytes: &[u8]) -> Result<Self> {
65        // In order to parse as an uncompressed curve point, we first make sure the input length is
66        // correct. This also ensures we're receiving the uncompressed representation.
67        if bytes.len() != Self::OutputSize::to_usize() {
68            return Err(CryptoError::InvalidEncoding);
69        }
70        // Now just call the routine exposed by the p256 crate. This preserves the
71        // invariant that public keys can't be the point at infinity, since the point at infinity
72        // has no representation as a SEC1 bytestring.
73        let parsed =
74            p256::PublicKey::from_sec1_bytes(bytes).map_err(|_| CryptoError::InvalidEncoding)?;
75        Ok(PkP256(parsed))
76    }
77}
78
79impl ToBytes for SkP256 {
80    // A fancy way of saying "32 bytes"
81    type OutputSize = <NistP256 as Curve>::FieldSize;
82
83    fn to_bytes(&self) -> GenericArray<u8, Self::OutputSize> {
84        // Many of p256 types like `NonZeroScalar` require `GenericArrays` and to my
85        // knowledge, there isnt an easy way way to convert from `Scalars` directly to bytes.
86        // So, we still need this. It would be great, if we could switch to const-generics
87        self.0.into()
88        // arr.as_slice().try_into().expect("Secret Key serialization failed")
89    }
90}
91
92impl FromBytes for SkP256 {
93    fn from_bytes(bytes: &[u8]) -> Result<Self> {
94        // Check the length
95        if bytes.len() != Self::OutputSize::to_usize() {
96            return Err(CryptoError::InvalidEncoding);
97        }
98        // Copy the bytes into a fixed-size array
99        let arr = GenericArray::<u8, Self::OutputSize>::clone_from_slice(bytes);
100        // We do not allow private keys to be 0. This is so that we can avoid checking the output
101        // of the P256::kex() function (see docs there for more detail)
102        let scalar = Scalar::from_bytes_reduced(&arr);
103        let nonzero_scalar = NonZeroScalar::new(scalar).ok_or(CryptoError::InvalidEncoding)?;
104
105        Ok(SkP256(nonzero_scalar))
106    }
107}
108
109/// A struct to hold the computed p-256 shared secret
110#[derive(Debug, Clone, PartialEq)]
111pub struct SharedSecretP256(pub AffinePoint);
112
113/// We only need the x co-ordinate from the result (i.e. 32 bytes of a coordinate from an Affine Point.)
114impl ToBytes for SharedSecretP256 {
115    type OutputSize = typenum::U32;
116
117    fn to_bytes(&self) -> GenericArray<u8, Self::OutputSize> {
118        // §4.1: Representation of the KEX result is the serialization of the x-coordinate
119        let bytes = p256::EncodedPoint::from(self.0);
120        GenericArray::<u8, Self::OutputSize>::clone_from_slice(bytes.x().unwrap())
121    }
122}
123/// A trait to describe the types, methods and functions of a key-exhange for a curve
124pub trait KeyExchange {
125    /// Secret key type
126    type SKey: Clone + ToBytes + FromBytes;
127    /// Public key type
128    type PubKey: Clone + ToBytes + FromBytes;
129    /// Shared Secret type
130    type CompSecret: ToBytes;
131
132    /// A function to generate a random private key, given a 32 byte seed value. 
133    fn generate_private_key(seed: [u8; 32]) -> Self::SKey;
134    /// A method to generate the public key, given a private key. 
135    fn generate_public_key(sk: &Self::SKey) -> Self::PubKey;
136    /// A method to compute the shared secret, given a private key and public key.
137    fn generate_shared_secret(sk: &Self::SKey, pk: &Self::PubKey) -> Result<Self::CompSecret>;
138}
139/// A struct that represents the ECDH implementation for the p-256 curve 
140pub struct ECDHNISTP256;
141
142impl KeyExchange for ECDHNISTP256 {
143    type SKey = SkP256;
144    type PubKey = PkP256;
145    type CompSecret = SharedSecretP256;
146
147    fn generate_private_key(seed: [u8; 32]) -> Self::SKey {
148        let mut rng = ChaCha20Rng::from_seed(seed); // test seed value.
149        let mut dest = [0; 32];
150        rng.fill_bytes(&mut dest);
151        let arr = GenericArray::<u8, _>::clone_from_slice(&dest);
152        SkP256(NonZeroScalar::from_repr(arr).expect("Private scalar value initialization failed"))
153    }
154
155    fn generate_public_key(sk: &Self::SKey) -> Self::PubKey {
156        let affine_pub_key = AffinePoint::generator().mul(sk.0);
157        PkP256(PublicKey::from_affine(affine_pub_key).expect("Failed to derive public key"))
158    }
159
160    fn generate_shared_secret(
161        sk: &Self::SKey,
162        others_pk: &Self::PubKey,
163    ) -> Result<Self::CompSecret> {
164        let shared_secret = others_pk.0.as_affine().mul(sk.0);
165        Ok(SharedSecretP256(shared_secret))
166    }
167}
168
169/// An ECDH-P384 private key is simply a scalar in the NIST P-384 field.
170#[derive(Debug, Clone)]
171pub struct SkP384(P384Secret);
172/// An ECDH-P384 public key. This is derived from the private key using scalar point multiplication.
173#[derive(Debug, Clone, PartialEq)]
174pub struct PkP384(pub PubKey<NistP384>);
175/// A struct to hold the computed p-384 shared secret
176#[derive(Debug, Clone, PartialEq)]
177pub struct SharedSecretP384(pub PubKey<NistP384>);
178
179// Everything is serialized and deserialized in uncompressed form
180impl ToBytes for PkP384 {
181    // A fancy way of saying "97 bytes"
182    type OutputSize = UncompressedPointSize<NistP384>;
183
184    fn to_bytes(&self) -> GenericArray<u8, Self::OutputSize> {
185        // Get the uncompressed pubkey encoding
186        GenericArray::clone_from_slice(self.0.as_bytes())
187    }
188}
189
190// Everything is serialized and deserialized in uncompressed form
191impl FromBytes for PkP384 {
192    fn from_bytes(bytes: &[u8]) -> Result<Self> {
193        // In order to parse as an uncompressed curve point, we first make sure the input length is
194        // correct. This also ensures we're receiving the uncompressed representation.
195        if bytes.len() != Self::OutputSize::to_usize() {
196            return Err(CryptoError::InvalidEncoding);
197        }
198        // Now just call the routine exposed by the p256 crate. This preserves the
199        // invariant that public keys can't be the point at infinity, since the point at infinity
200        // has no representation as a SEC1 bytestring.
201        let parsed = PubKey::from_bytes(bytes).map_err(|_| CryptoError::InvalidEncoding)?;
202        Ok(PkP384(parsed))
203    }
204}
205
206impl ToBytes for SkP384 {
207    // A fancy way of saying "48 bytes"
208    type OutputSize = <NistP384 as Curve>::FieldSize;
209
210    fn to_bytes(&self) -> GenericArray<u8, Self::OutputSize> {
211        // Many of p256 types like `NonZeroScalar` require `GenericArrays` and to my
212        // knowledge, there isnt an easy way way to convert from `Scalars` directly to bytes.
213        // So, we still need this. It would be great, if we could switch to const-generics
214        self.0.to_bytes()
215        // arr.as_slice().try_into().expect("Secret Key serialization failed")
216    }
217}
218
219impl FromBytes for SkP384 {
220    fn from_bytes(bytes: &[u8]) -> Result<Self> {
221        // Check the length
222        if bytes.len() != Self::OutputSize::to_usize() {
223            return Err(CryptoError::InvalidEncoding);
224        }
225
226        Ok(SkP384(
227            P384Secret::from_bytes(bytes).expect("Failed to deserialize raw private scalar"),
228        ))
229    }
230}
231
232/// We only need the x co-ordinate from the result (i.e. 48 bytes of a coordinate from an Affine Point.)
233impl ToBytes for SharedSecretP384 {
234    type OutputSize = typenum::U48;
235
236    fn to_bytes(&self) -> GenericArray<u8, Self::OutputSize> {
237        // §4.1: Representation of the KEX result is the serialization of the x-coordinate
238        let bytes = p384::EncodedPoint::from(self.0);
239        GenericArray::<u8, Self::OutputSize>::clone_from_slice(bytes.x().unwrap())
240    }
241}
242
243/// A struct that represents the ECDH implementation for the p-256 curve 
244pub struct ECDHNISTP384<const N: usize>;
245
246impl<const N: usize> KeyExchange for ECDHNISTP384<N> {
247    type SKey = SkP384;
248    type PubKey = PkP384;
249    type CompSecret = SharedSecretP384;
250
251    fn generate_private_key(seed: [u8; 32]) -> Self::SKey {
252        let mut rng = ChaCha20Rng::from_seed(seed); // test seed value.
253        let mut dest = [0; N];
254        rng.fill_bytes(&mut dest);
255        SkP384(P384Secret::from_bytes(&dest).expect("Failed to generate a `P384` private key"))
256    }
257
258    fn generate_public_key(sk: &Self::SKey) -> Self::PubKey {
259        let mod_prime =
260            dh::unhexlify_to_bytearray::<N>(&constants::ECDH_NIST_384_MODP.replace("0x", ""));
261        let b_val =
262            dh::unhexlify_to_bytearray::<N>(&constants::ECDH_NIST_384_B_VAL.replace("0x", ""));
263
264        let a = BigInt::from(-3);
265        let b = BigInt::from_bytes_be(Sign::Plus, &b_val);
266        let modp = BigInt::from_bytes_be(Sign::Plus, &mod_prime);
267
268        let gen = MyAffinePoint::<N>::generator();
269        let pk = match gen {
270            APTypes::P384(gen) => {
271                let pub_key = MyAffinePoint::<48>::double_and_add(
272                    gen,
273                    BigUint::from_bytes_be(sk.clone().to_bytes().as_slice()),
274                    &a,
275                    &b,
276                    &modp,
277                );
278                if let EncodedTypes::EncodedTypeP384(pubkey) = pub_key.to_uncompressed_bytes(false) {
279                    pubkey
280                } else {
281                    unreachable!() // technically, should be unreachable
282                }
283            }
284            _ => unreachable!(),
285        };
286        pk
287    }
288
289    fn generate_shared_secret(
290        sk: &Self::SKey,
291        others_pk: &Self::PubKey,
292    ) -> Result<Self::CompSecret> {
293        let mod_prime =
294            dh::unhexlify_to_bytearray::<N>(&constants::ECDH_NIST_384_MODP.replace("0x", ""));
295        let b_val =
296            dh::unhexlify_to_bytearray::<N>(&constants::ECDH_NIST_384_B_VAL.replace("0x", ""));
297
298        let a = BigInt::from(-3);
299        let b = BigInt::from_bytes_be(Sign::Plus, &b_val);
300        let modp = BigInt::from_bytes_be(Sign::Plus, &mod_prime);
301
302        if others_pk.0.as_bytes().len() != 97 {
303            panic!()
304        };
305        let pk: [u8; 97] = others_pk
306            .0
307            .as_bytes()
308            .try_into()
309            .expect("failed to serialize `EncodedPoint`");
310        let affine_pt = MyAffinePoint {
311            x: BigInt::from_bytes_be(Sign::Plus, &pk[1..N + 1]),
312            y: BigInt::from_bytes_be(Sign::Plus, &pk[N + 1..97]),
313            infinity: false,
314        };
315
316        let shared_secret = MyAffinePoint::<48>::double_and_add(
317            affine_pt,
318            BigUint::from_bytes_be(sk.clone().to_bytes().as_slice()),
319            &a,
320            &b,
321            &modp,
322        );
323        if let EncodedTypes::EncodedTypeP384_SS(sharedsecret) = shared_secret.to_uncompressed_bytes(true)
324        {
325            Ok(sharedsecret)
326        } else {
327            unreachable!() // technically, should be unreachable
328        }
329    }
330}
331