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 cose::CoseSerializable,
17 error::{EncodingError, Result},
18 keys::KeyId,
19 CryptoKey,
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 _: () = {
41 fn assert_zeroize_on_drop<T: zeroize::ZeroizeOnDrop>() {}
42 fn assert_all() {
43 assert_zeroize_on_drop::<ed25519_dalek::SigningKey>();
44 }
45};
46impl zeroize::ZeroizeOnDrop for SigningKey {}
47impl CryptoKey for SigningKey {}
48
49impl SigningKey {
50 pub fn make(algorithm: SignatureAlgorithm) -> Self {
52 match algorithm {
53 SignatureAlgorithm::Ed25519 => SigningKey {
54 id: KeyId::make(),
55 inner: RawSigningKey::Ed25519(Box::pin(ed25519_dalek::SigningKey::generate(
56 &mut rand::thread_rng(),
57 ))),
58 },
59 }
60 }
61
62 pub(super) fn cose_algorithm(&self) -> Algorithm {
63 match &self.inner {
64 RawSigningKey::Ed25519(_) => Algorithm::EdDSA,
65 }
66 }
67
68 pub fn to_verifying_key(&self) -> VerifyingKey {
71 match &self.inner {
72 RawSigningKey::Ed25519(key) => VerifyingKey {
73 id: self.id.clone(),
74 inner: RawVerifyingKey::Ed25519(key.verifying_key()),
75 },
76 }
77 }
78
79 pub(super) fn sign_raw(&self, data: &[u8]) -> Vec<u8> {
83 match &self.inner {
84 RawSigningKey::Ed25519(key) => key.sign(data).to_bytes().to_vec(),
85 }
86 }
87}
88
89impl CoseSerializable for SigningKey {
90 fn to_cose(&self) -> Vec<u8> {
92 match &self.inner {
93 RawSigningKey::Ed25519(key) => {
94 coset::CoseKeyBuilder::new_okp_key()
95 .key_id((&self.id).into())
96 .algorithm(Algorithm::EdDSA)
97 .param(
98 OkpKeyParameter::D.to_i64(), Value::Bytes(key.to_bytes().into()),
100 )
101 .param(
102 OkpKeyParameter::Crv.to_i64(), Value::Integer(Integer::from(EllipticCurve::Ed25519.to_i64())),
104 )
105 .add_key_op(KeyOperation::Sign)
106 .add_key_op(KeyOperation::Verify)
107 .build()
108 .to_vec()
109 .expect("Signing key is always serializable")
110 }
111 }
112 }
113
114 fn from_cose(bytes: &[u8]) -> Result<Self, EncodingError> {
116 let cose_key =
117 CoseKey::from_slice(bytes).map_err(|_| EncodingError::InvalidCoseEncoding)?;
118
119 match (&cose_key.alg, &cose_key.kty) {
120 (
121 Some(RegisteredLabelWithPrivate::Assigned(Algorithm::EdDSA)),
122 RegisteredLabel::Assigned(KeyType::OKP),
123 ) => Ok(SigningKey {
124 id: key_id(&cose_key)?,
125 inner: RawSigningKey::Ed25519(Box::pin(ed25519_signing_key(&cose_key)?)),
126 }),
127 _ => Err(EncodingError::UnsupportedValue(
128 "COSE key type or algorithm",
129 )),
130 }
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn test_cose_roundtrip_encode_signing() {
140 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
141 let cose = signing_key.to_cose();
142 let parsed_key = SigningKey::from_cose(&cose).unwrap();
143
144 assert_eq!(signing_key.to_cose(), parsed_key.to_cose());
145 }
146
147 #[test]
148 fn test_sign_rountrip() {
149 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
150 let signature = signing_key.sign_raw("Test message".as_bytes());
151 let verifying_key = signing_key.to_verifying_key();
152 assert!(verifying_key
153 .verify_raw(&signature, "Test message".as_bytes())
154 .is_ok());
155 }
156}