ts_crypto/
elliptic.rs

1//! Elliptic curve cryptography
2
3use der::{
4    Decode,
5    asn1::{SequenceOf, UintRef},
6};
7use digest::array::{Array, ArraySize};
8use ecdsa::{
9    EcdsaCurve, EncodedPoint, Signature, SignatureSize, SigningKey as EcSigningKey,
10    VerifyingKey as EcVerifyingKey, hazmat::DigestAlgorithm,
11};
12use elliptic_curve::{
13    AffinePoint, CurveArithmetic, FieldBytes, FieldBytesSize,
14    point::AffineCoordinates,
15    sec1::{FromEncodedPoint, ToEncodedPoint},
16};
17use sec1::point::ModulusSize;
18use signature::{Signer, Verifier};
19
20use crate::{SignMessage, ToVerifier, VerifySignature};
21
22/// An elliptic curve for `ECDSA`
23pub trait Curve
24where
25    // Curve has point operations
26    AffinePoint<Self::Curve>: FromEncodedPoint<Self::Curve> + ToEncodedPoint<Self::Curve>,
27    FieldBytesSize<Self::Curve>: ModulusSize,
28    // Curve be used in a key
29    EcSigningKey<Self::Curve>: Signer<Signature<Self::Curve>>,
30    EcVerifyingKey<Self::Curve>: Verifier<Signature<Self::Curve>>,
31    SignatureSize<Self::Curve>: ArraySize,
32{
33    /// The inner curve
34    type Curve: EcdsaCurve + CurveArithmetic + DigestAlgorithm;
35}
36
37/// Parameters for an `ECDSA` key
38pub trait Parameters: VerifySignature {
39    /// Get the x coordinate for this key
40    fn x(&self) -> Vec<u8>;
41
42    /// Get the y coordinate for this key
43    fn y(&self) -> Vec<u8>;
44}
45
46impl<C> Curve for C
47where
48    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
49    // Curve has point operations
50    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
51    FieldBytesSize<C>: ModulusSize,
52    // Curve be used in a key
53    EcSigningKey<C>: Signer<Signature<C>>,
54    EcVerifyingKey<C>: Verifier<Signature<C>>,
55    SignatureSize<C>: ArraySize,
56{
57    type Curve = C;
58}
59
60/// An `ECDSA` verifying key
61pub struct VerifyingKey<C: Curve> {
62    /// The inner verifying key
63    pub(crate) key: EcVerifyingKey<C::Curve>,
64}
65
66/// An `ECDSA` signing key
67pub struct SigningKey<C: Curve> {
68    /// The inner signing key
69    pub(crate) key: EcSigningKey<C::Curve>,
70}
71
72impl<C: Curve> VerifyingKey<C> {
73    /// Create a key from coordinates on the curve
74    pub fn from_coordinates(x: &[u8], y: &[u8]) -> Option<Self> {
75        let x = Array::try_from(x).ok()?;
76        let y = Array::try_from(y).ok()?;
77        let point = EncodedPoint::<C::Curve>::from_affine_coordinates(&x, &y, false);
78        let point = AffinePoint::<C::Curve>::from_encoded_point(&point).into_option()?;
79        let key = EcVerifyingKey::from_affine(point).ok()?;
80        Some(Self { key })
81    }
82}
83
84impl<C: Curve> Parameters for VerifyingKey<C> {
85    fn x(&self) -> Vec<u8> {
86        self.key.as_affine().x().to_vec()
87    }
88
89    fn y(&self) -> Vec<u8> {
90        self.key.as_affine().y().to_vec()
91    }
92}
93
94impl<C: Curve> VerifySignature for VerifyingKey<C> {
95    fn verifies_signature(&self, signature: &[u8], message: &[u8]) -> bool {
96        if let Ok(signature) = Signature::from_slice(signature) {
97            self.key.verify(message, &signature).is_ok()
98        } else if let Ok(sequence) = SequenceOf::<UintRef, 2>::from_der(signature) {
99            // ```text
100            // ECDSA-Sig-Value ::= SEQUENCE {
101            //   r  INTEGER,
102            //   s  INTEGER
103            // }
104            // ```
105            let Some(r) = sequence.get(0) else {
106                return false;
107            };
108            let Some(s) = sequence.get(1) else {
109                return false;
110            };
111
112            // Pad `r` and `s` to the required length
113            let mut r_array = Array::<u8, FieldBytesSize<C::Curve>>::default();
114            if r.as_bytes().len() > r_array.len() {
115                return false;
116            }
117            let offset = r_array.len().saturating_sub(r.as_bytes().len());
118            #[allow(clippy::indexing_slicing, reason = "offest is always in bounds")]
119            r_array[offset..].copy_from_slice(r.as_bytes());
120
121            let mut s_array = Array::<u8, FieldBytesSize<C::Curve>>::default();
122            if s.as_bytes().len() > s_array.len() {
123                return false;
124            }
125            let offset = r_array.len().saturating_sub(s.as_bytes().len());
126            #[allow(clippy::indexing_slicing, reason = "offest is always in bounds")]
127            s_array[offset..].copy_from_slice(s.as_bytes());
128
129            let Ok(r) = FieldBytes::<C::Curve>::try_from(r_array.as_slice()) else {
130                return false;
131            };
132            let Ok(s) = FieldBytes::<C::Curve>::try_from(s_array.as_slice()) else {
133                return false;
134            };
135            let Ok(signature) = Signature::<C::Curve>::from_scalars(r, s) else {
136                return false;
137            };
138            // C::Curve::FieldBytesSize::USIZE
139
140            self.key.verify(message, &signature).is_ok()
141        } else {
142            false
143        }
144    }
145}
146
147impl<C: Curve> SignMessage for SigningKey<C> {
148    fn sign(&self, message: &[u8]) -> Vec<u8> {
149        let signature: Signature<C::Curve> = self.key.sign(message);
150        signature.to_vec()
151    }
152}
153
154impl<C: Curve> ToVerifier for SigningKey<C> {
155    type Key = VerifyingKey<C>;
156
157    fn verifying_key(&self) -> Self::Key {
158        let key = *self.key.verifying_key();
159        VerifyingKey { key }
160    }
161}