httpsig/crypto/
asymmetric.rs

1use super::AlgorithmName;
2use crate::{
3  error::{HttpSigError, HttpSigResult},
4  trace::*,
5};
6use ecdsa::{
7  elliptic_curve::{sec1::ToEncodedPoint, PublicKey as EcPublicKey, SecretKey as EcSecretKey},
8  signature::{DigestSigner, DigestVerifier},
9};
10use ed25519_compact::{PublicKey as Ed25519PublicKey, SecretKey as Ed25519SecretKey};
11use p256::NistP256;
12use p384::NistP384;
13use pkcs8::{der::Decode, Document, PrivateKeyInfo};
14use sha2::{Digest, Sha256, Sha384};
15use spki::SubjectPublicKeyInfoRef;
16
17#[allow(non_upper_case_globals, dead_code)]
18/// Algorithm OIDs
19mod algorithm_oids {
20  /// OID for `id-ecPublicKey`, if you're curious
21  pub const EC: &str = "1.2.840.10045.2.1";
22  /// OID for `id-Ed25519`, if you're curious
23  pub const Ed25519: &str = "1.3.101.112";
24}
25#[allow(non_upper_case_globals, dead_code)]
26/// Params OIDs
27mod params_oids {
28  // OID for the NIST P-256 elliptic curve.
29  pub const Secp256r1: &str = "1.2.840.10045.3.1.7";
30  // OID for the NIST P-384 elliptic curve.
31  pub const Secp384r1: &str = "1.3.132.0.34";
32}
33
34/* -------------------------------- */
35#[derive(Debug, Clone)]
36/// Secret key for http signature
37/// Name conventions follow [Section-6.2.2, RFC9421](https://datatracker.ietf.org/doc/html/rfc9421#section-6.2.2)
38pub enum SecretKey {
39  /// ecdsa-p384-sha384
40  EcdsaP384Sha384(EcSecretKey<NistP384>),
41  /// ecdsa-p256-sha256
42  EcdsaP256Sha256(EcSecretKey<NistP256>),
43  /// ed25519
44  Ed25519(Ed25519SecretKey),
45}
46
47impl SecretKey {
48  /// from plain bytes
49  pub fn from_bytes(alg: AlgorithmName, bytes: &[u8]) -> HttpSigResult<Self> {
50    match alg {
51      AlgorithmName::EcdsaP256Sha256 => {
52        debug!("Read P256 private key");
53        let sk = EcSecretKey::from_bytes(bytes.into()).map_err(|e| HttpSigError::ParsePrivateKeyError(e.to_string()))?;
54        Ok(Self::EcdsaP256Sha256(sk))
55      }
56      AlgorithmName::EcdsaP384Sha384 => {
57        debug!("Read P384 private key");
58        let sk = EcSecretKey::from_bytes(bytes.into()).map_err(|e| HttpSigError::ParsePrivateKeyError(e.to_string()))?;
59        Ok(Self::EcdsaP384Sha384(sk))
60      }
61      AlgorithmName::Ed25519 => {
62        debug!("Read Ed25519 private key");
63        let mut seed = [0u8; 32];
64        seed.copy_from_slice(bytes);
65        let sk = ed25519_compact::KeyPair::from_seed(ed25519_compact::Seed::new(seed)).sk;
66        Ok(Self::Ed25519(sk))
67      }
68      _ => Err(HttpSigError::ParsePrivateKeyError("Unsupported algorithm".to_string())),
69    }
70  }
71  /// parse der
72  /// Derive secret key from der bytes
73  pub fn from_der(der: &[u8]) -> HttpSigResult<Self> {
74    let pki = PrivateKeyInfo::from_der(der).map_err(|e| HttpSigError::ParsePrivateKeyError(e.to_string()))?;
75
76    let (algorithm_name, sk_bytes) = match pki.algorithm.oid.to_string().as_ref() {
77      // ec
78      algorithm_oids::EC => {
79        let param = pki
80          .algorithm
81          .parameters_oid()
82          .map_err(|e| HttpSigError::ParsePrivateKeyError(e.to_string()))?;
83        let algorithm_name = match param.to_string().as_ref() {
84          params_oids::Secp256r1 => AlgorithmName::EcdsaP256Sha256,
85          params_oids::Secp384r1 => AlgorithmName::EcdsaP384Sha384,
86          _ => return Err(HttpSigError::ParsePrivateKeyError("Unsupported curve".to_string())),
87        };
88        let sk_bytes = sec1::EcPrivateKey::try_from(pki.private_key)
89          .map_err(|e| HttpSigError::ParsePrivateKeyError(format!("Error decoding EcPrivateKey: {e}")))?
90          .private_key;
91        (algorithm_name, sk_bytes)
92      }
93      // ed25519
94      algorithm_oids::Ed25519 => (AlgorithmName::Ed25519, &pki.private_key[2..]),
95      _ => return Err(HttpSigError::ParsePrivateKeyError("Unsupported algorithm".to_string())),
96    };
97    let sk = Self::from_bytes(algorithm_name, sk_bytes)?;
98    Ok(sk)
99  }
100
101  /// Derive secret key from pem string
102  pub fn from_pem(pem: &str) -> HttpSigResult<Self> {
103    let (tag, doc) = Document::from_pem(pem).map_err(|e| HttpSigError::ParsePrivateKeyError(e.to_string()))?;
104    if tag != "PRIVATE KEY" {
105      return Err(HttpSigError::ParsePrivateKeyError("Invalid tag".to_string()));
106    };
107    Self::from_der(doc.as_bytes())
108  }
109
110  /// Get public key from secret key
111  pub fn public_key(&self) -> PublicKey {
112    match &self {
113      Self::EcdsaP256Sha256(key) => PublicKey::EcdsaP256Sha256(key.public_key()),
114      Self::EcdsaP384Sha384(key) => PublicKey::EcdsaP384Sha384(key.public_key()),
115      Self::Ed25519(key) => PublicKey::Ed25519(key.public_key()),
116    }
117  }
118}
119
120impl super::SigningKey for SecretKey {
121  /// Sign data
122  fn sign(&self, data: &[u8]) -> HttpSigResult<Vec<u8>> {
123    match &self {
124      Self::EcdsaP256Sha256(sk) => {
125        debug!("Sign EcdsaP256Sha256");
126        let sk = ecdsa::SigningKey::from(sk);
127        let mut digest = <Sha256 as Digest>::new();
128        digest.update(data);
129        let sig: ecdsa::Signature<NistP256> = sk.sign_digest(digest);
130        Ok(sig.to_bytes().to_vec())
131      }
132      Self::EcdsaP384Sha384(sk) => {
133        debug!("Sign EcdsaP384Sha384");
134        let sk = ecdsa::SigningKey::from(sk);
135        let mut digest = <Sha384 as Digest>::new();
136        digest.update(data);
137        let sig: ecdsa::Signature<NistP384> = sk.sign_digest(digest);
138        Ok(sig.to_bytes().to_vec())
139      }
140      Self::Ed25519(sk) => {
141        debug!("Sign Ed25519");
142        let sig = sk.sign(data, Some(ed25519_compact::Noise::default()));
143        Ok(sig.as_ref().to_vec())
144      }
145    }
146  }
147
148  fn key_id(&self) -> String {
149    use super::VerifyingKey;
150    self.public_key().key_id()
151  }
152
153  fn alg(&self) -> AlgorithmName {
154    use super::VerifyingKey;
155    self.public_key().alg()
156  }
157}
158
159impl super::VerifyingKey for SecretKey {
160  fn verify(&self, data: &[u8], signature: &[u8]) -> HttpSigResult<()> {
161    self.public_key().verify(data, signature)
162  }
163
164  fn key_id(&self) -> String {
165    self.public_key().key_id()
166  }
167
168  fn alg(&self) -> AlgorithmName {
169    self.public_key().alg()
170  }
171}
172
173/* -------------------------------- */
174#[derive(Debug, Clone)]
175/// Public key for http signature, only for asymmetric algorithm
176/// Name conventions follow [Section 6.2.2, RFC9421](https://datatracker.ietf.org/doc/html/rfc9421#section-6.2.2)
177pub enum PublicKey {
178  /// ecdsa-p256-sha256
179  EcdsaP256Sha256(EcPublicKey<NistP256>),
180  /// ecdsa-p384-sha384
181  EcdsaP384Sha384(EcPublicKey<NistP384>),
182  /// ed25519
183  Ed25519(Ed25519PublicKey),
184}
185
186impl PublicKey {
187  /// from plain bytes
188  pub fn from_bytes(alg: AlgorithmName, bytes: &[u8]) -> HttpSigResult<Self> {
189    match alg {
190      AlgorithmName::EcdsaP256Sha256 => {
191        debug!("Read P256 public key");
192        let pk = EcPublicKey::from_sec1_bytes(bytes).map_err(|e| HttpSigError::ParsePublicKeyError(e.to_string()))?;
193        Ok(Self::EcdsaP256Sha256(pk))
194      }
195      AlgorithmName::EcdsaP384Sha384 => {
196        debug!("Read P384 public key");
197        let pk = EcPublicKey::from_sec1_bytes(bytes).map_err(|e| HttpSigError::ParsePublicKeyError(e.to_string()))?;
198        Ok(Self::EcdsaP384Sha384(pk))
199      }
200      AlgorithmName::Ed25519 => {
201        debug!("Read Ed25519 public key");
202        let pk = ed25519_compact::PublicKey::from_slice(bytes).map_err(|e| HttpSigError::ParsePublicKeyError(e.to_string()))?;
203        Ok(Self::Ed25519(pk))
204      }
205      _ => Err(HttpSigError::ParsePublicKeyError("Unsupported algorithm".to_string())),
206    }
207  }
208
209  #[allow(dead_code)]
210  /// Convert from pem string
211  pub fn from_pem(pem: &str) -> HttpSigResult<Self> {
212    let (tag, doc) = Document::from_pem(pem).map_err(|e| HttpSigError::ParsePublicKeyError(e.to_string()))?;
213    if tag != "PUBLIC KEY" {
214      return Err(HttpSigError::ParsePublicKeyError("Invalid tag".to_string()));
215    };
216
217    let spki_ref = SubjectPublicKeyInfoRef::from_der(doc.as_bytes())
218      .map_err(|e| HttpSigError::ParsePublicKeyError(format!("Error decoding SubjectPublicKeyInfo: {e}").to_string()))?;
219
220    let (algorithm_name, pk_bytes) = match spki_ref.algorithm.oid.to_string().as_ref() {
221      // ec
222      algorithm_oids::EC => {
223        let param = spki_ref
224          .algorithm
225          .parameters_oid()
226          .map_err(|e| HttpSigError::ParsePublicKeyError(e.to_string()))?;
227        let algorithm_name = match param.to_string().as_ref() {
228          params_oids::Secp256r1 => AlgorithmName::EcdsaP256Sha256,
229          params_oids::Secp384r1 => AlgorithmName::EcdsaP384Sha384,
230          _ => return Err(HttpSigError::ParsePublicKeyError("Unsupported curve".to_string())),
231        };
232        let pk_bytes = spki_ref
233          .subject_public_key
234          .as_bytes()
235          .ok_or(HttpSigError::ParsePublicKeyError("Invalid public key".to_string()))?;
236        (algorithm_name, pk_bytes)
237      }
238      // ed25519
239      algorithm_oids::Ed25519 => (
240        AlgorithmName::Ed25519,
241        spki_ref
242          .subject_public_key
243          .as_bytes()
244          .ok_or(HttpSigError::ParsePublicKeyError("Invalid public key".to_string()))?,
245      ),
246      _ => return Err(HttpSigError::ParsePublicKeyError("Unsupported algorithm".to_string())),
247    };
248    Self::from_bytes(algorithm_name, pk_bytes)
249  }
250}
251
252impl super::VerifyingKey for PublicKey {
253  /// Verify signature
254  fn verify(&self, data: &[u8], signature: &[u8]) -> HttpSigResult<()> {
255    match self {
256      Self::EcdsaP256Sha256(pk) => {
257        debug!("Verify EcdsaP256Sha256");
258        let signature = ecdsa::Signature::<NistP256>::from_bytes(signature.into())
259          .map_err(|e| HttpSigError::ParseSignatureError(e.to_string()))?;
260        let vk = ecdsa::VerifyingKey::from(pk);
261        let mut digest = <Sha256 as Digest>::new();
262        digest.update(data);
263        vk.verify_digest(digest, &signature)
264          .map_err(|e| HttpSigError::InvalidSignature(e.to_string()))
265      }
266      Self::EcdsaP384Sha384(pk) => {
267        debug!("Verify EcdsaP384Sha384");
268        let signature = ecdsa::Signature::<NistP384>::from_bytes(signature.into())
269          .map_err(|e| HttpSigError::ParseSignatureError(e.to_string()))?;
270        let vk = ecdsa::VerifyingKey::from(pk);
271        let mut digest = <Sha384 as Digest>::new();
272        digest.update(data);
273        vk.verify_digest(digest, &signature)
274          .map_err(|e| HttpSigError::InvalidSignature(e.to_string()))
275      }
276      Self::Ed25519(pk) => {
277        debug!("Verify Ed25519");
278        let sig =
279          ed25519_compact::Signature::from_slice(signature).map_err(|e| HttpSigError::ParseSignatureError(e.to_string()))?;
280        pk.verify(data, &sig)
281          .map_err(|e| HttpSigError::InvalidSignature(e.to_string()))
282      }
283    }
284  }
285
286  /// Create key id
287  fn key_id(&self) -> String {
288    use base64::{engine::general_purpose, Engine as _};
289
290    let bytes = match self {
291      Self::EcdsaP256Sha256(vk) => vk.to_encoded_point(true).as_bytes().to_vec(),
292      Self::EcdsaP384Sha384(vk) => vk.to_encoded_point(true).as_bytes().to_vec(),
293      Self::Ed25519(vk) => vk.as_ref().to_vec(),
294    };
295    let mut hasher = <Sha256 as Digest>::new();
296    hasher.update(&bytes);
297    let hash = hasher.finalize();
298    general_purpose::STANDARD.encode(hash)
299  }
300
301  /// Get the algorithm name
302  fn alg(&self) -> AlgorithmName {
303    match self {
304      Self::EcdsaP256Sha256(_) => AlgorithmName::EcdsaP256Sha256,
305      Self::EcdsaP384Sha384(_) => AlgorithmName::EcdsaP384Sha384,
306      Self::Ed25519(_) => AlgorithmName::Ed25519,
307    }
308  }
309}
310
311#[cfg(test)]
312mod tests {
313  use p256::elliptic_curve::group::GroupEncoding;
314
315  use super::*;
316  use std::matches;
317
318  const P256_SECERT_KEY: &str = r##"-----BEGIN PRIVATE KEY-----
319MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgv7zxW56ojrWwmSo1
3204uOdbVhUfj9Jd+5aZIB9u8gtWnihRANCAARGYsMe0CT6pIypwRvoJlLNs4+cTh2K
321L7fUNb5i6WbKxkpAoO+6T3pMBG5Yw7+8NuGTvvtrZAXduA2giPxQ8zCf
322-----END PRIVATE KEY-----
323"##;
324  const P256_PUBLIC_KEY: &str = r##"-----BEGIN PUBLIC KEY-----
325MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERmLDHtAk+qSMqcEb6CZSzbOPnE4d
326ii+31DW+YulmysZKQKDvuk96TARuWMO/vDbhk777a2QF3bgNoIj8UPMwnw==
327-----END PUBLIC KEY-----
328"##;
329  const P384_SECERT_KEY: &str = r##"-----BEGIN PRIVATE KEY-----
330MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCPYbeLLlIQKUzVyVGH
331MeuFp/9o2Lr+4GrI3bsbHuViMMceiuM+8xqzFCSm4Ltl5UyhZANiAARKg3yM+Ltx
332n4ZptF3hI6Q167crEtPRklCEsRTyWUqy+VrrnM5LU/+fqxVbyniBZHd4vmQVYtjF
333xsv8P3DpjvpKJZqFfVdIr2ZR+kYDKHwIruIF9fCPawAH2tnbuc3xEzQ=
334-----END PRIVATE KEY-----
335"##;
336  const P384_PUBLIC_KEY: &str = r##"-----BEGIN PUBLIC KEY-----
337MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAESoN8jPi7cZ+GabRd4SOkNeu3KxLT0ZJQ
338hLEU8llKsvla65zOS1P/n6sVW8p4gWR3eL5kFWLYxcbL/D9w6Y76SiWahX1XSK9m
339UfpGAyh8CK7iBfXwj2sAB9rZ27nN8RM0
340-----END PUBLIC KEY-----
341"##;
342
343  const EDDSA_SECRET_KEY: &str = r##"-----BEGIN PRIVATE KEY-----
344MC4CAQAwBQYDK2VwBCIEIDSHAE++q1BP7T8tk+mJtS+hLf81B0o6CFyWgucDFN/C
345-----END PRIVATE KEY-----
346"##;
347  const EDDSA_PUBLIC_KEY: &str = r##"-----BEGIN PUBLIC KEY-----
348MCowBQYDK2VwAyEA1ixMQcxO46PLlgQfYS46ivFd+n0CcDHSKUnuhm3i1O0=
349-----END PUBLIC KEY-----
350"##;
351
352  #[test]
353  fn test_from_bytes() {
354    let ed25519_kp = ed25519_compact::KeyPair::from_seed(ed25519_compact::Seed::default());
355    let ed25519_sk = ed25519_kp.sk.seed().to_vec();
356    let ed25519_pk = ed25519_kp.pk.as_ref();
357    let sk = SecretKey::from_bytes(AlgorithmName::Ed25519, &ed25519_sk).unwrap();
358    assert!(matches!(sk, SecretKey::Ed25519(_)));
359    let pk = PublicKey::from_bytes(AlgorithmName::Ed25519, ed25519_pk).unwrap();
360    assert!(matches!(pk, PublicKey::Ed25519(_)));
361
362    let mut rng = rand_085::thread_rng();
363    let es256_sk = p256::ecdsa::SigningKey::random(&mut rng);
364    let es256_pk = es256_sk.verifying_key();
365    let sk = SecretKey::from_bytes(AlgorithmName::EcdsaP256Sha256, es256_sk.to_bytes().as_ref()).unwrap();
366    assert!(matches!(sk, SecretKey::EcdsaP256Sha256(_)));
367    let pk_bytes = es256_pk.as_affine().to_bytes();
368    let pk = PublicKey::from_bytes(AlgorithmName::EcdsaP256Sha256, pk_bytes.as_ref()).unwrap();
369    assert!(matches!(pk, PublicKey::EcdsaP256Sha256(_)));
370  }
371
372  #[test]
373  fn test_from_pem() {
374    let sk = SecretKey::from_pem(P256_SECERT_KEY).unwrap();
375    assert!(matches!(sk, SecretKey::EcdsaP256Sha256(_)));
376    let pk = PublicKey::from_pem(P256_PUBLIC_KEY).unwrap();
377    assert!(matches!(pk, PublicKey::EcdsaP256Sha256(_)));
378
379    let sk = SecretKey::from_pem(P384_SECERT_KEY).unwrap();
380    assert!(matches!(sk, SecretKey::EcdsaP384Sha384(_)));
381    let pk = PublicKey::from_pem(P384_PUBLIC_KEY).unwrap();
382    assert!(matches!(pk, PublicKey::EcdsaP384Sha384(_)));
383
384    let sk = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
385    assert!(matches!(sk, SecretKey::Ed25519(_)));
386    let pk = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
387    assert!(matches!(pk, PublicKey::Ed25519(_)));
388  }
389
390  #[test]
391  fn test_sign_verify() {
392    use super::super::{SigningKey, VerifyingKey};
393    let sk = SecretKey::from_pem(P256_SECERT_KEY).unwrap();
394    let pk = PublicKey::from_pem(P256_PUBLIC_KEY).unwrap();
395    let data = b"hello world";
396    let signature = sk.sign(data).unwrap();
397    pk.verify(data, &signature).unwrap();
398    assert!(pk.verify(b"hello", &signature).is_err());
399
400    let sk = SecretKey::from_pem(P384_SECERT_KEY).unwrap();
401    let pk = PublicKey::from_pem(P384_PUBLIC_KEY).unwrap();
402    let data = b"hello world";
403    let signature = sk.sign(data).unwrap();
404    pk.verify(data, &signature).unwrap();
405    assert!(pk.verify(b"hello", &signature).is_err());
406
407    let sk = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
408    let pk = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
409    let data = b"hello world";
410    let signature = sk.sign(data).unwrap();
411    pk.verify(data, &signature).unwrap();
412    assert!(pk.verify(b"hello", &signature).is_err());
413  }
414
415  #[test]
416  fn test_kid() -> HttpSigResult<()> {
417    use super::super::VerifyingKey;
418    let sk = SecretKey::from_pem(P256_SECERT_KEY)?;
419    let pk = PublicKey::from_pem(P256_PUBLIC_KEY)?;
420    assert_eq!(sk.public_key().key_id(), pk.key_id());
421    assert_eq!(pk.key_id(), "k34r3Nqfak67bhJSXTjTRo5tCIr1Bsre1cPoJ3LJ9xE=");
422
423    let sk = SecretKey::from_pem(P384_SECERT_KEY)?;
424    let pk = PublicKey::from_pem(P384_PUBLIC_KEY)?;
425    assert_eq!(sk.public_key().key_id(), pk.key_id());
426    assert_eq!(pk.key_id(), "JluSJKLaQsbGcgg1Ves4FfP/Kf7qS11RT88TvU0eNSo=");
427
428    let sk = SecretKey::from_pem(EDDSA_SECRET_KEY)?;
429    let pk = PublicKey::from_pem(EDDSA_PUBLIC_KEY)?;
430    assert_eq!(sk.public_key().key_id(), pk.key_id());
431    assert_eq!(pk.key_id(), "gjrE7ACMxgzYfFHgabgf4kLTg1eKIdsJ94AiFTFj1is=");
432    Ok(())
433  }
434}