atrium_crypto/
keypair.rs

1//! Keypair structs for signing, and utility trait implementations.
2use crate::Algorithm;
3use crate::did::prefix_did_key;
4use crate::error::Result;
5use ecdsa::elliptic_curve::{
6    AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, Scalar,
7    generic_array::ArrayLength,
8    ops::Invert,
9    sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint},
10    subtle::CtOption,
11};
12use ecdsa::hazmat::{DigestPrimitive, SignPrimitive};
13use ecdsa::signature::{Signer, rand_core::CryptoRngCore};
14use ecdsa::{Signature, SignatureSize, SigningKey};
15use k256::Secp256k1;
16use p256::NistP256;
17
18/// A keypair for signing messages.
19pub struct Keypair<C>
20where
21    C: PrimeCurve + CurveArithmetic,
22    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
23    SignatureSize<C>: ArrayLength<u8>,
24{
25    signing_key: SigningKey<C>,
26}
27
28impl<C> Keypair<C>
29where
30    C: PrimeCurve + CurveArithmetic,
31    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
32    SignatureSize<C>: ArrayLength<u8>,
33{
34    /// Generate a cryptographically random [`SigningKey`].
35    ///
36    /// ```
37    /// use atrium_crypto::keypair::Keypair;
38    ///
39    /// let keypair = Keypair::<k256::Secp256k1>::create(&mut rand::thread_rng());
40    /// ```
41    pub fn create(rng: &mut impl CryptoRngCore) -> Self {
42        Self { signing_key: SigningKey::<C>::random(rng) }
43    }
44    /// Initialize signing key from a raw scalar serialized as a byte slice.
45    pub fn import(bytes: &[u8]) -> Result<Self> {
46        Ok(Self { signing_key: SigningKey::from_slice(bytes)? })
47    }
48}
49
50impl<C> Keypair<C>
51where
52    C: PrimeCurve + CurveArithmetic,
53    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
54    SignatureSize<C>: ArrayLength<u8>,
55    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
56    FieldBytesSize<C>: ModulusSize,
57{
58    fn compressed_public_key(&self) -> Box<[u8]> {
59        self.signing_key.verifying_key().to_encoded_point(true).to_bytes()
60    }
61}
62
63impl<C> Keypair<C>
64where
65    C: PrimeCurve + CurveArithmetic + DigestPrimitive,
66    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
67    SignatureSize<C>: ArrayLength<u8>,
68{
69    /// Sign a message with the keypair.
70    ///
71    /// Returns the signature as a byte vector of the "low-S" form.
72    ///
73    /// Details:
74    /// [https://atproto.com/specs/cryptography#ecdsa-signature-malleability](https://atproto.com/specs/cryptography#ecdsa-signature-malleability)
75    pub fn sign(&self, msg: &[u8]) -> Result<Vec<u8>> {
76        let signature: Signature<_> = self.signing_key.try_sign(msg)?;
77        Ok(signature.normalize_s().unwrap_or(signature).to_bytes().to_vec())
78    }
79}
80
81/// Generate a DID key string from a keypair.
82pub trait Did<C> {
83    fn did(&self) -> String;
84}
85
86/// Export a keypair as a byte vector.
87pub trait Export<C> {
88    fn export(&self) -> Vec<u8>;
89}
90
91impl<C> Export<C> for Keypair<C>
92where
93    C: PrimeCurve + CurveArithmetic,
94    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
95    SignatureSize<C>: ArrayLength<u8>,
96{
97    fn export(&self) -> Vec<u8> {
98        self.signing_key.to_bytes().to_vec()
99    }
100}
101
102/// Type alias for a P-256 keypair.
103pub type P256Keypair = Keypair<NistP256>;
104
105impl Did<NistP256> for P256Keypair {
106    fn did(&self) -> String {
107        prefix_did_key(&Algorithm::P256.format_mulikey_compressed(&self.compressed_public_key()))
108    }
109}
110
111/// Type alias for a secp256k1 keypair.
112pub type Secp256k1Keypair = Keypair<Secp256k1>;
113
114impl Did<Secp256k1> for Secp256k1Keypair {
115    fn did(&self) -> String {
116        prefix_did_key(
117            &Algorithm::Secp256k1.format_mulikey_compressed(&self.compressed_public_key()),
118        )
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::{P256Keypair, Secp256k1Keypair};
125    use crate::Algorithm;
126    use crate::did::{format_did_key, parse_did_key};
127    use crate::verify::Verifier;
128    use rand::rngs::ThreadRng;
129
130    #[test]
131    fn p256_did() {
132        let keypair = P256Keypair::create(&mut ThreadRng::default());
133        let did = {
134            use super::Did;
135            keypair.did()
136        };
137        let formatted =
138            format_did_key(Algorithm::P256, &keypair.signing_key.verifying_key().to_sec1_bytes())
139                .expect("formatting to did key should succeed");
140        assert_eq!(did, formatted);
141
142        let (alg, public_key) = parse_did_key(&did).expect("parsing did key should succeed");
143        assert_eq!(alg, Algorithm::P256);
144        assert_eq!(
145            public_key,
146            keypair.signing_key.verifying_key().to_encoded_point(false).as_bytes()
147        );
148    }
149
150    #[test]
151    fn secp256k1_did() {
152        let keypair = Secp256k1Keypair::create(&mut ThreadRng::default());
153        let did = {
154            use super::Did;
155            keypair.did()
156        };
157        let formatted = format_did_key(
158            Algorithm::Secp256k1,
159            &keypair.signing_key.verifying_key().to_sec1_bytes(),
160        )
161        .expect("formatting to did key should succeed");
162        assert_eq!(did, formatted);
163
164        let (alg, public_key) = parse_did_key(&did).expect("parsing did key should succeed");
165        assert_eq!(alg, Algorithm::Secp256k1);
166        assert_eq!(
167            public_key,
168            keypair.signing_key.verifying_key().to_encoded_point(false).as_bytes()
169        );
170    }
171
172    #[test]
173    fn p256_export() {
174        let keypair = P256Keypair::create(&mut ThreadRng::default());
175        let exported = {
176            use super::Export;
177            keypair.export()
178        };
179        let imported = P256Keypair::import(&exported).expect("importing keypair should succeed");
180        {
181            use super::Did;
182            assert_eq!(keypair.did(), imported.did());
183        }
184    }
185
186    #[test]
187    fn secp256k1_export() {
188        let keypair = Secp256k1Keypair::create(&mut ThreadRng::default());
189        let exported = {
190            use super::Export;
191            keypair.export()
192        };
193        let imported =
194            Secp256k1Keypair::import(&exported).expect("importing keypair should succeed");
195        {
196            use super::Did;
197            assert_eq!(keypair.did(), imported.did());
198        }
199    }
200
201    #[test]
202    fn p256_verify() {
203        let keypair = P256Keypair::create(&mut ThreadRng::default());
204        let did = {
205            use super::Did;
206            keypair.did()
207        };
208        let (alg, public_key) = parse_did_key(&did).expect("parsing did key should succeed");
209        assert_eq!(alg, Algorithm::P256);
210
211        let verifier = Verifier::default();
212        let msg = [1, 2, 3, 4, 5, 6, 7, 8];
213        let signature = keypair.sign(&msg).expect("signing should succeed");
214        let mut corrupted_signature = signature.clone();
215        corrupted_signature[0] = corrupted_signature[0].wrapping_add(1);
216        assert!(
217            verifier.verify(alg, &public_key, &msg, &signature).is_ok(),
218            "verifying signature should succeed"
219        );
220        assert!(
221            verifier.verify(alg, &public_key, &msg[..7], &signature).is_err(),
222            "verifying signature should fail with incorrect message"
223        );
224        assert!(
225            verifier.verify(alg, &public_key, &msg, &corrupted_signature).is_err(),
226            "verifying signature should fail with incorrect signature"
227        );
228        assert!(
229            verifier.verify(Algorithm::Secp256k1, &public_key, &msg, &signature).is_err(),
230            "verifying signature should fail with incorrect algorithm"
231        );
232    }
233
234    #[test]
235    fn secp256k1_verify() {
236        let keypair = Secp256k1Keypair::create(&mut ThreadRng::default());
237        let did = {
238            use super::Did;
239            keypair.did()
240        };
241        let (alg, public_key) = parse_did_key(&did).expect("parsing did key should succeed");
242        assert_eq!(alg, Algorithm::Secp256k1);
243
244        let verifier = Verifier::default();
245        let msg = [1, 2, 3, 4, 5, 6, 7, 8];
246        let signature = keypair.sign(&msg).expect("signing should succeed");
247        let mut corrupted_signature = signature.clone();
248        corrupted_signature[0] = corrupted_signature[0].wrapping_add(1);
249        assert!(
250            verifier.verify(alg, &public_key, &msg, &signature).is_ok(),
251            "verifying signature should succeed"
252        );
253        assert!(
254            verifier.verify(alg, &public_key, &msg[..7], &signature).is_err(),
255            "verifying signature should fail with incorrect message"
256        );
257        assert!(
258            verifier.verify(alg, &public_key, &msg, &corrupted_signature).is_err(),
259            "verifying signature should fail with incorrect signature"
260        );
261        assert!(
262            verifier.verify(Algorithm::P256, &public_key, &msg, &signature).is_err(),
263            "verifying signature should fail with incorrect algorithm"
264        );
265    }
266}