1 // Copyright 2021 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 //////////////////////////////////////////////////////////////////////////////// 16 17 //! COSE_Key functionality. 18 19 use crate::{ 20 cbor::value::Value, 21 common::{AsCborValue, CborOrdering}, 22 iana, 23 iana::EnumI64, 24 util::{to_cbor_array, ValueTryAs}, 25 Algorithm, CoseError, Label, Result, 26 }; 27 use alloc::{collections::BTreeSet, vec, vec::Vec}; 28 29 #[cfg(test)] 30 mod tests; 31 32 /// Key type. 33 pub type KeyType = crate::RegisteredLabel<iana::KeyType>; 34 35 impl Default for KeyType { default() -> Self36 fn default() -> Self { 37 KeyType::Assigned(iana::KeyType::Reserved) 38 } 39 } 40 41 /// Key operation. 42 pub type KeyOperation = crate::RegisteredLabel<iana::KeyOperation>; 43 44 /// A collection of [`CoseKey`] objects. 45 #[derive(Clone, Debug, Default, PartialEq)] 46 pub struct CoseKeySet(pub Vec<CoseKey>); 47 48 impl crate::CborSerializable for CoseKeySet {} 49 50 impl AsCborValue for CoseKeySet { from_cbor_value(value: Value) -> Result<Self>51 fn from_cbor_value(value: Value) -> Result<Self> { 52 Ok(Self( 53 value.try_as_array_then_convert(CoseKey::from_cbor_value)?, 54 )) 55 } 56 to_cbor_value(self) -> Result<Value>57 fn to_cbor_value(self) -> Result<Value> { 58 to_cbor_array(self.0) 59 } 60 } 61 62 /// Structure representing a cryptographic key. 63 /// 64 /// ```cddl 65 /// COSE_Key = { 66 /// 1 => tstr / int, ; kty 67 /// ? 2 => bstr, ; kid 68 /// ? 3 => tstr / int, ; alg 69 /// ? 4 => [+ (tstr / int) ], ; key_ops 70 /// ? 5 => bstr, ; Base IV 71 /// * label => values 72 /// } 73 /// ``` 74 #[derive(Clone, Debug, Default, PartialEq)] 75 pub struct CoseKey { 76 /// Key type identification. 77 pub kty: KeyType, 78 /// Key identification. 79 pub key_id: Vec<u8>, 80 /// Key use restriction to this algorithm. 81 pub alg: Option<Algorithm>, 82 /// Restrict set of possible operations. 83 pub key_ops: BTreeSet<KeyOperation>, 84 /// Base IV to be xor-ed with partial IVs. 85 pub base_iv: Vec<u8>, 86 /// Any additional parameter (label,value) pairs. If duplicate labels are present, 87 /// CBOR-encoding will fail. 88 pub params: Vec<(Label, Value)>, 89 } 90 91 impl CoseKey { 92 /// Re-order the contents of the key so that the contents will be emitted in one of the standard 93 /// CBOR sorted orders. canonicalize(&mut self, ordering: CborOrdering)94 pub fn canonicalize(&mut self, ordering: CborOrdering) { 95 // The keys that are represented as named fields CBOR-encode as single bytes 0x01 - 0x05, 96 // which sort before any other CBOR values (other than 0x00) in either sorting scheme: 97 // - In length-first sorting, a single byte sorts before anything multi-byte and 1-5 sorts 98 // before any other value. 99 // - In encoded-lexicographic sorting, there are no valid CBOR-encoded single values that 100 // start with a byte in the range 0x01 - 0x05 other than the values 1-5. 101 // So we only need to sort the `params`. 102 match ordering { 103 CborOrdering::Lexicographic => self.params.sort_by(|l, r| l.0.cmp(&r.0)), 104 CborOrdering::LengthFirstLexicographic => { 105 self.params.sort_by(|l, r| l.0.cmp_canonical(&r.0)) 106 } 107 } 108 } 109 } 110 111 impl crate::CborSerializable for CoseKey {} 112 113 const KTY: Label = Label::Int(iana::KeyParameter::Kty as i64); 114 const KID: Label = Label::Int(iana::KeyParameter::Kid as i64); 115 const ALG: Label = Label::Int(iana::KeyParameter::Alg as i64); 116 const KEY_OPS: Label = Label::Int(iana::KeyParameter::KeyOps as i64); 117 const BASE_IV: Label = Label::Int(iana::KeyParameter::BaseIv as i64); 118 119 impl AsCborValue for CoseKey { from_cbor_value(value: Value) -> Result<Self>120 fn from_cbor_value(value: Value) -> Result<Self> { 121 let m = value.try_as_map()?; 122 let mut key = Self::default(); 123 let mut seen = BTreeSet::new(); 124 for (l, value) in m.into_iter() { 125 // The `ciborium` CBOR library does not police duplicate map keys. 126 // RFC 8152 section 14 requires that COSE does police duplicates, so do it here. 127 let label = Label::from_cbor_value(l)?; 128 if seen.contains(&label) { 129 return Err(CoseError::DuplicateMapKey); 130 } 131 seen.insert(label.clone()); 132 match label { 133 KTY => key.kty = KeyType::from_cbor_value(value)?, 134 135 KID => { 136 key.key_id = value.try_as_nonempty_bytes()?; 137 } 138 139 ALG => key.alg = Some(Algorithm::from_cbor_value(value)?), 140 141 KEY_OPS => { 142 let key_ops = value.try_as_array()?; 143 for key_op in key_ops.into_iter() { 144 if !key.key_ops.insert(KeyOperation::from_cbor_value(key_op)?) { 145 return Err(CoseError::UnexpectedItem( 146 "repeated array entry", 147 "unique array label", 148 )); 149 } 150 } 151 if key.key_ops.is_empty() { 152 return Err(CoseError::UnexpectedItem("empty array", "non-empty array")); 153 } 154 } 155 156 BASE_IV => { 157 key.base_iv = value.try_as_nonempty_bytes()?; 158 } 159 160 label => key.params.push((label, value)), 161 } 162 } 163 // Check that key type has been set. 164 if key.kty == KeyType::Assigned(iana::KeyType::Reserved) { 165 return Err(CoseError::UnexpectedItem( 166 "no kty label", 167 "mandatory kty label", 168 )); 169 } 170 171 Ok(key) 172 } 173 to_cbor_value(self) -> Result<Value>174 fn to_cbor_value(self) -> Result<Value> { 175 let mut map: Vec<(Value, Value)> = vec![(KTY.to_cbor_value()?, self.kty.to_cbor_value()?)]; 176 if !self.key_id.is_empty() { 177 map.push((KID.to_cbor_value()?, Value::Bytes(self.key_id))); 178 } 179 if let Some(alg) = self.alg { 180 map.push((ALG.to_cbor_value()?, alg.to_cbor_value()?)); 181 } 182 if !self.key_ops.is_empty() { 183 map.push((KEY_OPS.to_cbor_value()?, to_cbor_array(self.key_ops)?)); 184 } 185 if !self.base_iv.is_empty() { 186 map.push((BASE_IV.to_cbor_value()?, Value::Bytes(self.base_iv))); 187 } 188 let mut seen = BTreeSet::new(); 189 for (label, value) in self.params { 190 if seen.contains(&label) { 191 return Err(CoseError::DuplicateMapKey); 192 } 193 seen.insert(label.clone()); 194 map.push((label.to_cbor_value()?, value)); 195 } 196 Ok(Value::Map(map)) 197 } 198 } 199 200 /// Builder for [`CoseKey`] objects. 201 #[derive(Debug, Default)] 202 pub struct CoseKeyBuilder(CoseKey); 203 204 impl CoseKeyBuilder { 205 builder! {CoseKey} 206 builder_set! {kty: KeyType} 207 builder_set! {key_id: Vec<u8>} 208 builder_set! {base_iv: Vec<u8>} 209 210 /// Constructor for an elliptic curve public key specified by `x` and `y` coordinates. new_ec2_pub_key(curve: iana::EllipticCurve, x: Vec<u8>, y: Vec<u8>) -> Self211 pub fn new_ec2_pub_key(curve: iana::EllipticCurve, x: Vec<u8>, y: Vec<u8>) -> Self { 212 Self(CoseKey { 213 kty: KeyType::Assigned(iana::KeyType::EC2), 214 params: vec![ 215 ( 216 Label::Int(iana::Ec2KeyParameter::Crv as i64), 217 Value::from(curve as u64), 218 ), 219 (Label::Int(iana::Ec2KeyParameter::X as i64), Value::Bytes(x)), 220 (Label::Int(iana::Ec2KeyParameter::Y as i64), Value::Bytes(y)), 221 ], 222 ..Default::default() 223 }) 224 } 225 226 /// Constructor for an elliptic curve public key specified by `x` coordinate plus sign of `y` 227 /// coordinate. new_ec2_pub_key_y_sign(curve: iana::EllipticCurve, x: Vec<u8>, y_sign: bool) -> Self228 pub fn new_ec2_pub_key_y_sign(curve: iana::EllipticCurve, x: Vec<u8>, y_sign: bool) -> Self { 229 Self(CoseKey { 230 kty: KeyType::Assigned(iana::KeyType::EC2), 231 params: vec![ 232 ( 233 Label::Int(iana::Ec2KeyParameter::Crv as i64), 234 Value::from(curve as u64), 235 ), 236 (Label::Int(iana::Ec2KeyParameter::X as i64), Value::Bytes(x)), 237 ( 238 Label::Int(iana::Ec2KeyParameter::Y as i64), 239 Value::Bool(y_sign), 240 ), 241 ], 242 ..Default::default() 243 }) 244 } 245 246 /// Constructor for an elliptic curve private key specified by `d`, together with public `x` and 247 /// `y` coordinates. new_ec2_priv_key( curve: iana::EllipticCurve, x: Vec<u8>, y: Vec<u8>, d: Vec<u8>, ) -> Self248 pub fn new_ec2_priv_key( 249 curve: iana::EllipticCurve, 250 x: Vec<u8>, 251 y: Vec<u8>, 252 d: Vec<u8>, 253 ) -> Self { 254 let mut builder = Self::new_ec2_pub_key(curve, x, y); 255 builder 256 .0 257 .params 258 .push((Label::Int(iana::Ec2KeyParameter::D as i64), Value::Bytes(d))); 259 builder 260 } 261 262 /// Constructor for a symmetric key specified by `k`. new_symmetric_key(k: Vec<u8>) -> Self263 pub fn new_symmetric_key(k: Vec<u8>) -> Self { 264 Self(CoseKey { 265 kty: KeyType::Assigned(iana::KeyType::Symmetric), 266 params: vec![( 267 Label::Int(iana::SymmetricKeyParameter::K as i64), 268 Value::Bytes(k), 269 )], 270 ..Default::default() 271 }) 272 } 273 274 /// Constructor for a octet keypair key. new_okp_key() -> Self275 pub fn new_okp_key() -> Self { 276 Self(CoseKey { 277 kty: KeyType::Assigned(iana::KeyType::OKP), 278 ..Default::default() 279 }) 280 } 281 282 /// Set the key type. 283 #[must_use] key_type(mut self, key_type: iana::KeyType) -> Self284 pub fn key_type(mut self, key_type: iana::KeyType) -> Self { 285 self.0.kty = KeyType::Assigned(key_type); 286 self 287 } 288 289 /// Set the algorithm. 290 #[must_use] algorithm(mut self, alg: iana::Algorithm) -> Self291 pub fn algorithm(mut self, alg: iana::Algorithm) -> Self { 292 self.0.alg = Some(Algorithm::Assigned(alg)); 293 self 294 } 295 296 /// Add a key operation. 297 #[must_use] add_key_op(mut self, op: iana::KeyOperation) -> Self298 pub fn add_key_op(mut self, op: iana::KeyOperation) -> Self { 299 self.0.key_ops.insert(KeyOperation::Assigned(op)); 300 self 301 } 302 303 /// Set a parameter value. 304 /// 305 /// # Panics 306 /// 307 /// This function will panic if it used to set a parameter label from the [`iana::KeyParameter`] 308 /// range. 309 #[must_use] param(mut self, label: i64, value: Value) -> Self310 pub fn param(mut self, label: i64, value: Value) -> Self { 311 if iana::KeyParameter::from_i64(label).is_some() { 312 panic!("param() method used to set KeyParameter"); // safe: invalid input 313 } 314 self.0.params.push((Label::Int(label), value)); 315 self 316 } 317 } 318