Skip to main content

bitwarden_crypto/signing/
verifying_key.rs

1//! A verifying key is the public part of a signature key pair. It is used to verify signatures.
2//!
3//! This implements the lowest layer of the signature module, verifying signatures on raw byte
4//! arrays.
5
6use ciborium::{Value, value::Integer};
7use coset::{
8    CborSerializable, MlDsaVariant, RegisteredLabel, RegisteredLabelWithPrivate,
9    iana::{Algorithm, EllipticCurve, EnumI64, KeyOperation, KeyType, OkpKeyParameter},
10};
11use ml_dsa::{MlDsa44, signature::Verifier};
12
13use super::{SignatureAlgorithm, ed25519_verifying_key, key_id, mldsa44_verifying_key};
14use crate::{
15    CoseKeyBytes, CryptoError,
16    content_format::CoseKeyContentFormat,
17    cose::CoseSerializable,
18    error::{EncodingError, SignatureError},
19    keys::KeyId,
20};
21
22/// A `VerifyingKey` without the key id. This enum contains a variant for each supported signature
23/// scheme.
24pub(super) enum RawVerifyingKey {
25    Ed25519(ed25519_dalek::VerifyingKey),
26    MlDsa44(Box<ml_dsa::VerifyingKey<MlDsa44>>),
27}
28
29/// A verifying key is a public key used for verifying signatures. It can be published to other
30/// users, who can use it to verify that messages were signed by the holder of the corresponding
31/// `SigningKey`.
32pub struct VerifyingKey {
33    pub(super) id: KeyId,
34    pub(super) inner: RawVerifyingKey,
35}
36
37impl std::fmt::Debug for VerifyingKey {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        let key_suffix = match &self.inner {
40            RawVerifyingKey::Ed25519(_) => "Ed25519",
41            RawVerifyingKey::MlDsa44(_) => "MlDsa44",
42        };
43        let mut debug_struct = f.debug_struct(format!("VerifyingKey::{}", key_suffix).as_str());
44        debug_struct.field("id", &self.id);
45        match &self.inner {
46            RawVerifyingKey::Ed25519(key) => {
47                debug_struct.field("key", &hex::encode(key.to_bytes()));
48            }
49            RawVerifyingKey::MlDsa44(key) => {
50                let encoded = key.encode();
51                debug_struct.field("key", &hex::encode(encoded));
52            }
53        }
54        debug_struct.finish()
55    }
56}
57
58impl VerifyingKey {
59    /// Returns the signature scheme used by the verifying key.
60    pub fn algorithm(&self) -> SignatureAlgorithm {
61        match &self.inner {
62            RawVerifyingKey::Ed25519(_) => SignatureAlgorithm::Ed25519,
63            RawVerifyingKey::MlDsa44(_) => SignatureAlgorithm::MlDsa44,
64        }
65    }
66
67    /// Verifies the signature of the given data, for the given namespace.
68    /// This should never be used directly, but only through the `verify` method, to enforce
69    /// strong domain separation of the signatures.
70    pub(super) fn verify_raw(&self, signature: &[u8], data: &[u8]) -> Result<(), CryptoError> {
71        match &self.inner {
72            RawVerifyingKey::Ed25519(key) => {
73                let sig = ed25519_dalek::Signature::from_bytes(
74                    signature
75                        .try_into()
76                        .map_err(|_| SignatureError::InvalidSignature)?,
77                );
78                key.verify_strict(data, &sig)
79                    .map_err(|_| SignatureError::InvalidSignature.into())
80            }
81            RawVerifyingKey::MlDsa44(key) => {
82                let sig = ml_dsa::Signature::<MlDsa44>::try_from(signature)
83                    .map_err(|_| SignatureError::InvalidSignature)?;
84                key.verify(data, &sig)
85                    .map_err(|_| SignatureError::InvalidSignature.into())
86            }
87        }
88    }
89}
90
91impl CoseSerializable<CoseKeyContentFormat> for VerifyingKey {
92    fn to_cose(&self) -> CoseKeyBytes {
93        match &self.inner {
94            RawVerifyingKey::Ed25519(key) => coset::CoseKeyBuilder::new_okp_key()
95                .key_id((&self.id).into())
96                .algorithm(Algorithm::EdDSA)
97                .param(
98                    OkpKeyParameter::Crv.to_i64(), // Elliptic curve identifier
99                    Value::Integer(Integer::from(EllipticCurve::Ed25519.to_i64())),
100                )
101                // Note: X does not refer to the X coordinate of the public key curve point, but
102                // to the verifying key (signature public key), as represented by the curve spec. In
103                // the case of Ed25519, this is the compressed Y coordinate. This
104                // was ill-defined in earlier drafts of the standard. https://www.rfc-editor.org/rfc/rfc9053.html#name-octet-key-pair
105                .param(
106                    OkpKeyParameter::X.to_i64(), // Verifying key (digital signature public key)
107                    Value::Bytes(key.to_bytes().to_vec()),
108                )
109                .add_key_op(KeyOperation::Verify)
110                .build()
111                .to_vec()
112                .expect("Verifying key is always serializable")
113                .into(),
114            RawVerifyingKey::MlDsa44(key) => coset::CoseKeyBuilder::new_mldsa_pub_key(
115                MlDsaVariant::MlDsa44,
116                key.encode().to_vec(),
117            )
118            .key_id((&self.id).into())
119            .add_key_op(KeyOperation::Verify)
120            .build()
121            .to_vec()
122            .expect("Verifying key is always serializable")
123            .into(),
124        }
125    }
126
127    fn from_cose(bytes: &CoseKeyBytes) -> Result<Self, EncodingError>
128    where
129        Self: Sized,
130    {
131        let cose_key = coset::CoseKey::from_slice(bytes.as_ref())
132            .map_err(|_| EncodingError::InvalidCoseEncoding)?;
133
134        let algorithm = cose_key
135            .alg
136            .as_ref()
137            .ok_or(EncodingError::MissingValue("COSE key algorithm"))?;
138        match (&cose_key.kty, algorithm) {
139            (
140                RegisteredLabel::Assigned(KeyType::OKP),
141                RegisteredLabelWithPrivate::Assigned(Algorithm::EdDSA),
142            ) => Ok(VerifyingKey {
143                id: key_id(&cose_key)?,
144                inner: RawVerifyingKey::Ed25519(ed25519_verifying_key(&cose_key)?),
145            }),
146            (
147                RegisteredLabel::Assigned(KeyType::AKP),
148                RegisteredLabelWithPrivate::Assigned(Algorithm::ML_DSA_44),
149            ) => Ok(VerifyingKey {
150                id: key_id(&cose_key)?,
151                inner: RawVerifyingKey::MlDsa44(Box::new(mldsa44_verifying_key(&cose_key)?)),
152            }),
153            _ => Err(EncodingError::UnsupportedValue(
154                "COSE key type or algorithm",
155            )),
156        }
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    const VERIFYING_KEY: &[u8] = &[
165        166, 1, 1, 2, 80, 55, 131, 40, 191, 230, 137, 76, 182, 184, 139, 94, 152, 45, 63, 13, 71,
166        3, 39, 4, 129, 2, 32, 6, 33, 88, 32, 93, 213, 35, 177, 81, 219, 226, 241, 147, 140, 238,
167        32, 34, 183, 213, 107, 227, 92, 75, 84, 208, 47, 198, 80, 18, 188, 172, 145, 184, 154, 26,
168        170,
169    ];
170    const SIGNED_DATA_RAW: &[u8] = &[
171        247, 239, 74, 181, 75, 54, 137, 225, 2, 158, 14, 0, 61, 210, 254, 208, 255, 16, 8, 81, 173,
172        33, 59, 67, 204, 31, 45, 38, 147, 118, 228, 84, 235, 252, 104, 38, 194, 173, 62, 52, 9,
173        184, 1, 22, 113, 134, 154, 108, 24, 83, 78, 2, 23, 235, 80, 22, 57, 110, 100, 24, 151, 33,
174        186, 12,
175    ];
176
177    #[test]
178    fn test_cose_roundtrip_encode_verifying() {
179        let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY)).unwrap();
180        let cose = verifying_key.to_cose();
181        let parsed_key = VerifyingKey::from_cose(&cose).unwrap();
182
183        assert_eq!(verifying_key.to_cose(), parsed_key.to_cose());
184    }
185
186    #[test]
187    fn test_testvector() {
188        let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY)).unwrap();
189        assert_eq!(verifying_key.algorithm(), SignatureAlgorithm::Ed25519);
190
191        verifying_key
192            .verify_raw(SIGNED_DATA_RAW, b"Test message")
193            .unwrap();
194    }
195
196    #[test]
197    fn test_invalid_testvector() {
198        let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY)).unwrap();
199        assert_eq!(verifying_key.algorithm(), SignatureAlgorithm::Ed25519);
200
201        // This should fail, as the signed object is not valid for the given verifying key.
202        assert!(
203            verifying_key
204                .verify_raw(SIGNED_DATA_RAW, b"Invalid message")
205                .is_err()
206        );
207    }
208}