bitwarden_crypto/signing/
signing_key.rs1use std::pin::Pin;
2
3use ciborium::{value::Integer, Value};
4use coset::{
5 iana::{Algorithm, EllipticCurve, EnumI64, KeyOperation, KeyType, OkpKeyParameter},
6 CborSerializable, CoseKey, RegisteredLabel, RegisteredLabelWithPrivate,
7};
8use ed25519_dalek::Signer;
9
10use super::{
11 ed25519_signing_key, key_id,
12 verifying_key::{RawVerifyingKey, VerifyingKey},
13 SignatureAlgorithm,
14};
15use crate::{
16 content_format::CoseKeyContentFormat,
17 cose::CoseSerializable,
18 error::{EncodingError, Result},
19 keys::KeyId,
20 CoseKeyBytes, CryptoKey,
21};
22
23#[derive(Clone)]
26enum RawSigningKey {
27 Ed25519(Pin<Box<ed25519_dalek::SigningKey>>),
28}
29
30#[derive(Clone)]
33pub struct SigningKey {
34 pub(super) id: KeyId,
35 inner: RawSigningKey,
36}
37
38const _: () = {
42 fn assert_zeroize_on_drop<T: zeroize::ZeroizeOnDrop>() {}
43 fn assert_all() {
44 assert_zeroize_on_drop::<ed25519_dalek::SigningKey>();
45 }
46};
47impl zeroize::ZeroizeOnDrop for SigningKey {}
48impl CryptoKey for SigningKey {}
49
50impl SigningKey {
51 pub fn make(algorithm: SignatureAlgorithm) -> Self {
53 match algorithm {
54 SignatureAlgorithm::Ed25519 => SigningKey {
55 id: KeyId::make(),
56 inner: RawSigningKey::Ed25519(Box::pin(ed25519_dalek::SigningKey::generate(
57 &mut rand::thread_rng(),
58 ))),
59 },
60 }
61 }
62
63 pub(super) fn cose_algorithm(&self) -> Algorithm {
64 match &self.inner {
65 RawSigningKey::Ed25519(_) => Algorithm::EdDSA,
66 }
67 }
68
69 pub fn to_verifying_key(&self) -> VerifyingKey {
72 match &self.inner {
73 RawSigningKey::Ed25519(key) => VerifyingKey {
74 id: self.id.clone(),
75 inner: RawVerifyingKey::Ed25519(key.verifying_key()),
76 },
77 }
78 }
79
80 pub(super) fn sign_raw(&self, data: &[u8]) -> Vec<u8> {
84 match &self.inner {
85 RawSigningKey::Ed25519(key) => key.sign(data).to_bytes().to_vec(),
86 }
87 }
88}
89
90impl CoseSerializable<CoseKeyContentFormat> for SigningKey {
91 fn to_cose(&self) -> CoseKeyBytes {
93 match &self.inner {
94 RawSigningKey::Ed25519(key) => {
95 coset::CoseKeyBuilder::new_okp_key()
96 .key_id((&self.id).into())
97 .algorithm(Algorithm::EdDSA)
98 .param(
99 OkpKeyParameter::D.to_i64(), Value::Bytes(key.to_bytes().into()),
101 )
102 .param(
103 OkpKeyParameter::Crv.to_i64(), Value::Integer(Integer::from(EllipticCurve::Ed25519.to_i64())),
105 )
106 .add_key_op(KeyOperation::Sign)
107 .add_key_op(KeyOperation::Verify)
108 .build()
109 .to_vec()
110 .expect("Signing key is always serializable")
111 .into()
112 }
113 }
114 }
115
116 fn from_cose(bytes: &CoseKeyBytes) -> Result<Self, EncodingError> {
118 let cose_key =
119 CoseKey::from_slice(bytes.as_ref()).map_err(|_| EncodingError::InvalidCoseEncoding)?;
120
121 match (&cose_key.alg, &cose_key.kty) {
122 (
123 Some(RegisteredLabelWithPrivate::Assigned(Algorithm::EdDSA)),
124 RegisteredLabel::Assigned(KeyType::OKP),
125 ) => Ok(SigningKey {
126 id: key_id(&cose_key)?,
127 inner: RawSigningKey::Ed25519(Box::pin(ed25519_signing_key(&cose_key)?)),
128 }),
129 _ => Err(EncodingError::UnsupportedValue(
130 "COSE key type or algorithm",
131 )),
132 }
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn test_cose_roundtrip_encode_signing() {
142 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
143 let cose = signing_key.to_cose();
144 let parsed_key = SigningKey::from_cose(&cose).unwrap();
145
146 assert_eq!(signing_key.to_cose(), parsed_key.to_cose());
147 }
148
149 #[test]
150 fn test_sign_rountrip() {
151 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
152 let signature = signing_key.sign_raw("Test message".as_bytes());
153 let verifying_key = signing_key.to_verifying_key();
154 assert!(verifying_key
155 .verify_raw(&signature, "Test message".as_bytes())
156 .is_ok());
157 }
158}