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