Skip to main content

bitwarden_crypto/keys/
signed_public_key.rs

1//! A public encryption key alone is not authenticated. It needs to be tied to a cryptographic
2//! identity, which is provided by a signature keypair. This is done by signing the public key, and
3//! requiring consumers to verify the public key before consumption by using unwrap_and_verify.
4
5use std::{borrow::Cow, str::FromStr};
6
7use bitwarden_encoding::{B64, FromStrVisitor};
8use serde::{Deserialize, Serialize};
9use serde_bytes::ByteBuf;
10use serde_repr::{Deserialize_repr, Serialize_repr};
11
12use super::PublicKey;
13use crate::{
14    CoseSign1Bytes, CryptoError, PublicKeyEncryptionAlgorithm, RawPublicKey, SignedObject,
15    SigningKey, SigningNamespace, SpkiPublicKeyBytes, VerifyingKey, cose::CoseSerializable,
16    error::EncodingError,
17};
18
19#[cfg(feature = "wasm")]
20#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
21const TS_CUSTOM_TYPES: &'static str = r#"
22export type SignedPublicKey = Tagged<string, "SignedPublicKey">;
23"#;
24
25/// [`PublicKeyFormat`] defines the format of the public key in a [`SignedPublicKeyMessage`].
26/// Currently, only ASN.1 Subject Public Key Info (SPKI) is used, but CoseKey may become another
27/// option in the future.
28#[derive(Serialize_repr, Deserialize_repr)]
29#[repr(u8)]
30enum PublicKeyFormat {
31    Spki = 0,
32}
33
34/// [`SignedPublicKeyMessage`] is a message that once signed, makes a claim towards owning a
35/// public encryption key.
36#[derive(Serialize, Deserialize)]
37#[serde(rename_all = "camelCase")]
38pub struct SignedPublicKeyMessage {
39    /// The algorithm/crypto system used with this public key.
40    algorithm: PublicKeyEncryptionAlgorithm,
41    /// The format of the public key.
42    content_format: PublicKeyFormat,
43    /// The public key, serialized and formatted in the content format specified in
44    /// `content_format`.
45    ///
46    /// Note: [ByteBuf] is used here to ensure efficient serialization. Using [`Vec<u8>`] would
47    /// lead to an incompatible encoding of individual bytes, instead of a contiguous byte
48    /// buffer.
49    public_key: ByteBuf,
50}
51
52impl SignedPublicKeyMessage {
53    /// Creates a new [`SignedPublicKeyMessage`] from a [`PublicKey`]. This message
54    /// can then be signed using a [`SigningKey`] to create a [`SignedPublicKey`].
55    pub fn from_public_key(public_key: &PublicKey) -> Result<Self, CryptoError> {
56        match public_key.inner() {
57            RawPublicKey::RsaOaepSha1(_) => Ok(SignedPublicKeyMessage {
58                algorithm: PublicKeyEncryptionAlgorithm::RsaOaepSha1,
59                content_format: PublicKeyFormat::Spki,
60                public_key: ByteBuf::from(public_key.to_der()?.as_ref()),
61            }),
62        }
63    }
64
65    /// Signs the [`SignedPublicKeyMessage`] using the provided [`SigningKey`], and returns a
66    /// [`SignedPublicKey`].
67    pub fn sign(&self, signing_key: &SigningKey) -> Result<SignedPublicKey, CryptoError> {
68        Ok(SignedPublicKey(
69            signing_key.sign(self, &SigningNamespace::SignedPublicKey)?,
70        ))
71    }
72}
73
74/// [`SignedPublicKey`] is a public encryption key, signed by the owner of the encryption
75/// keypair. This wrapping ensures that the consumer of the public key MUST verify the identity of
76/// the Signer before they can use the public key for encryption.
77#[derive(Clone, PartialEq)]
78pub struct SignedPublicKey(pub(crate) SignedObject);
79
80impl std::fmt::Debug for SignedPublicKey {
81    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82        let mut debug_struct = f.debug_struct("SignedPublicKey");
83
84        if let Ok(key_id) = self.0.signed_by_id() {
85            debug_struct.field("signed_by", &key_id);
86        }
87
88        if let Some(msg) = self
89            .0
90            .dangerous_unverified_decode_do_not_use_except_for_debug_logs::<SignedPublicKeyMessage>(
91            )
92        {
93            debug_struct.field("algorithm", &msg.algorithm);
94            debug_struct.field("public_key", &hex::encode(&msg.public_key));
95        }
96
97        debug_struct.finish()
98    }
99}
100
101impl From<SignedPublicKey> for CoseSign1Bytes {
102    fn from(val: SignedPublicKey) -> Self {
103        val.0.to_cose()
104    }
105}
106
107impl TryFrom<CoseSign1Bytes> for SignedPublicKey {
108    type Error = EncodingError;
109    fn try_from(bytes: CoseSign1Bytes) -> Result<Self, EncodingError> {
110        Ok(SignedPublicKey(SignedObject::from_cose(&bytes)?))
111    }
112}
113
114impl From<SignedPublicKey> for String {
115    fn from(val: SignedPublicKey) -> Self {
116        let bytes: CoseSign1Bytes = val.into();
117        B64::from(bytes.as_ref()).to_string()
118    }
119}
120
121impl SignedPublicKey {
122    /// Verifies the signature of the public key against the provided [`VerifyingKey`], and returns
123    /// the [`PublicKey`] if the verification is successful.
124    pub fn verify_and_unwrap(self, verifying_key: &VerifyingKey) -> Result<PublicKey, CryptoError> {
125        let public_key_message: SignedPublicKeyMessage = self
126            .0
127            .verify_and_unwrap(verifying_key, &SigningNamespace::SignedPublicKey)?;
128        match (
129            public_key_message.algorithm,
130            public_key_message.content_format,
131        ) {
132            (PublicKeyEncryptionAlgorithm::RsaOaepSha1, PublicKeyFormat::Spki) => {
133                Ok(PublicKey::from_der(&SpkiPublicKeyBytes::from(
134                    public_key_message.public_key.into_vec(),
135                ))
136                .map_err(|_| EncodingError::InvalidValue("public key"))?)
137            }
138        }
139    }
140}
141
142impl FromStr for SignedPublicKey {
143    type Err = EncodingError;
144
145    fn from_str(s: &str) -> Result<Self, Self::Err> {
146        let bytes = B64::try_from(s).map_err(|_| EncodingError::InvalidCborSerialization)?;
147        Self::try_from(CoseSign1Bytes::from(&bytes))
148    }
149}
150
151impl<'de> Deserialize<'de> for SignedPublicKey {
152    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
153    where
154        D: serde::Deserializer<'de>,
155    {
156        deserializer.deserialize_str(FromStrVisitor::new())
157    }
158}
159
160impl serde::Serialize for SignedPublicKey {
161    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
162    where
163        S: serde::Serializer,
164    {
165        let b64_serialized_signed_public_key: String = self.clone().into();
166        serializer.serialize_str(&b64_serialized_signed_public_key)
167    }
168}
169
170impl schemars::JsonSchema for SignedPublicKey {
171    fn schema_name() -> Cow<'static, str> {
172        "SignedPublicKey".into()
173    }
174
175    fn json_schema(generator: &mut schemars::generate::SchemaGenerator) -> schemars::Schema {
176        generator.subschema_for::<String>()
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183    use crate::{PrivateKey, PublicKeyEncryptionAlgorithm, SignatureAlgorithm};
184
185    #[test]
186    #[ignore = "Manual test to verify debug format"]
187    fn test_debug() {
188        let public_key =
189            PrivateKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1).to_public_key();
190        let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
191        let message = SignedPublicKeyMessage::from_public_key(&public_key).unwrap();
192        let signed_public_key = message.sign(&signing_key).unwrap();
193        println!("{:?}", signed_public_key);
194    }
195
196    #[test]
197    fn test_signed_asymmetric_public_key() {
198        let public_key =
199            PrivateKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1).to_public_key();
200        let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
201        let message = SignedPublicKeyMessage::from_public_key(&public_key).unwrap();
202        let signed_public_key = message.sign(&signing_key).unwrap();
203        let verifying_key = signing_key.to_verifying_key();
204        let verified_public_key = signed_public_key.verify_and_unwrap(&verifying_key).unwrap();
205        assert_eq!(
206            public_key.to_der().unwrap(),
207            verified_public_key.to_der().unwrap()
208        );
209    }
210}