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