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