bitwarden_crypto/signing/
cose.rs1use coset::{
4 CoseKey, Label, ProtectedHeader, RegisteredLabel,
5 iana::{AkpKeyParameter, EllipticCurve, EnumI64, OkpKeyParameter},
6};
7use ml_dsa::B32;
8
9use super::SigningNamespace;
10use crate::{
11 CryptoError, KEY_ID_SIZE,
12 cose::SIGNING_NAMESPACE,
13 error::{EncodingError, SignatureError},
14 keys::KeyId,
15 signing::signing_key::ML_DSA_SEED_SIZE,
16};
17
18pub(super) fn namespace(
21 protected_header: &ProtectedHeader,
22) -> Result<SigningNamespace, CryptoError> {
23 let namespace = protected_header
24 .header
25 .rest
26 .iter()
27 .find_map(|(key, value)| {
28 if let Label::Int(key) = key
29 && *key == SIGNING_NAMESPACE
30 {
31 return value.as_integer();
32 }
33 None
34 })
35 .ok_or(SignatureError::InvalidNamespace)?;
36
37 SigningNamespace::try_from(i128::from(namespace))
38}
39
40pub(super) fn content_type(
44 protected_header: &ProtectedHeader,
45) -> Result<coset::iana::CoapContentFormat, CryptoError> {
46 protected_header
47 .header
48 .content_type
49 .as_ref()
50 .and_then(|ct| match ct {
51 RegisteredLabel::Assigned(content_format) => Some(*content_format),
52 _ => None,
53 })
54 .ok_or_else(|| SignatureError::InvalidSignature.into())
55}
56
57pub(super) fn key_id(cose_key: &CoseKey) -> Result<KeyId, EncodingError> {
60 let key_id: [u8; KEY_ID_SIZE] = cose_key
61 .key_id
62 .as_slice()
63 .try_into()
64 .map_err(|_| EncodingError::InvalidValue("key id length"))?;
65 let key_id: KeyId = key_id.into();
66 Ok(key_id)
67}
68
69pub(super) fn ed25519_signing_key(
71 cose_key: &CoseKey,
72) -> Result<ed25519_dalek::SigningKey, EncodingError> {
73 let d = okp_d(cose_key)?;
75 let crv = okp_curve(cose_key)?;
76 if crv == EllipticCurve::Ed25519.to_i64().into() {
77 Ok(ed25519_dalek::SigningKey::from_bytes(
78 d.try_into()
79 .map_err(|_| EncodingError::InvalidCoseEncoding)?,
80 ))
81 } else {
82 Err(EncodingError::UnsupportedValue("OKP curve"))
83 }
84}
85
86pub(super) fn ed25519_verifying_key(
88 cose_key: &CoseKey,
89) -> Result<ed25519_dalek::VerifyingKey, EncodingError> {
90 let x = okp_x(cose_key)?;
92 let crv = okp_curve(cose_key)?;
93 if crv == EllipticCurve::Ed25519.to_i64().into() {
94 ed25519_dalek::VerifyingKey::from_bytes(
95 x.try_into()
96 .map_err(|_| EncodingError::InvalidValue("ed25519 OKP verifying key"))?,
97 )
98 .map_err(|_| EncodingError::InvalidValue("ed25519 OKP verifying key"))
99 } else {
100 Err(EncodingError::UnsupportedValue("OKP curve"))
101 }
102}
103
104fn cose_param(
106 cose_key: &CoseKey,
107 param: impl EnumI64 + Copy,
108) -> Option<&coset::cbor::value::Value> {
109 cose_key.params.iter().find_map(|(key, value)| match key {
110 Label::Int(i) if EnumI64::from_i64(*i) == Some(param) => Some(value),
111 _ => None,
112 })
113}
114
115fn okp_d(cose_key: &CoseKey) -> Result<&[u8], EncodingError> {
117 cose_param(cose_key, OkpKeyParameter::D)
119 .and_then(|v| v.as_bytes().map(Vec::as_slice))
120 .ok_or(EncodingError::MissingValue("OKP private key"))
121}
122
123fn okp_x(cose_key: &CoseKey) -> Result<&[u8], EncodingError> {
125 cose_param(cose_key, OkpKeyParameter::X)
127 .and_then(|v| v.as_bytes().map(Vec::as_slice))
128 .ok_or(EncodingError::MissingValue("OKP public key"))
129}
130
131fn okp_curve(cose_key: &CoseKey) -> Result<i128, EncodingError> {
133 cose_param(cose_key, OkpKeyParameter::Crv)
135 .and_then(|v| v.as_integer().map(i128::from))
136 .ok_or(EncodingError::MissingValue("OKP curve"))
137}
138
139fn akp_priv(cose_key: &CoseKey) -> Result<&[u8], EncodingError> {
141 cose_param(cose_key, AkpKeyParameter::Priv)
142 .and_then(|v| v.as_bytes().map(Vec::as_slice))
143 .ok_or(EncodingError::MissingValue("AKP private key"))
144}
145
146fn akp_pub(cose_key: &CoseKey) -> Result<&[u8], EncodingError> {
148 cose_param(cose_key, AkpKeyParameter::Pub)
149 .and_then(|v| v.as_bytes().map(Vec::as_slice))
150 .ok_or(EncodingError::MissingValue("AKP public key"))
151}
152
153pub(super) fn mldsa_seed(cose_key: &CoseKey) -> Result<B32, EncodingError> {
156 let priv_bytes = akp_priv(cose_key)?;
157 let seed: [u8; ML_DSA_SEED_SIZE] = priv_bytes
158 .try_into()
159 .map_err(|_| EncodingError::InvalidValue("ML-DSA seed length"))?;
160 Ok(seed.into())
161}
162
163pub(super) fn mldsa44_verifying_key(
165 cose_key: &CoseKey,
166) -> Result<ml_dsa::VerifyingKey<ml_dsa::MlDsa44>, EncodingError> {
167 let pub_bytes = akp_pub(cose_key)?;
168 let vk_encoded = ml_dsa::EncodedVerifyingKey::<ml_dsa::MlDsa44>::try_from(pub_bytes)
169 .map_err(|_| EncodingError::InvalidValue("ML-DSA-44 verifying key length"))?;
170 Ok(ml_dsa::VerifyingKey::<ml_dsa::MlDsa44>::decode(&vk_encoded))
171}