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