cose/utils.rs
1//! Utilities to help the process of encoding/decoding a COSE message.
2//!
3//! # Examples
4//!
5//! Examples on how to encode a COSE message by providing the respective
6//! parameters and cose-key in JSON format.
7//!
8//! The functions `decode_json_key` and `decode_json` used in this examples are from the optional feature `json` of this crate.
9//!
10//! ## cose-sign1
11//! ```
12//! use cose::utils;
13//! use cose::message::{CoseMessage, SIG1_TAG};
14//!
15//! fn main() {
16//!
17//! // cose-sign1 message in JSON format
18//! let data: &str = r#"
19//! {
20//! "protected": {"alg": "EDDSA" },
21//! "unprotected": {"kid": "11" },
22//! "payload": "signed message"
23//! }"#;
24//!
25//! // cose-key in JSON format
26//! let key_json: &str = r#"
27//! {
28//! "kty": "OKP",
29//! "alg": "EDDSA",
30//! "crv": "Ed25519",
31//! "x": "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
32//! "d": "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
33//! "key ops": ["sign", "verify"]
34//! }"#;
35//!
36//! // Decode the cose-key JSON to CoseKey structure
37//! let key = utils::decode_json_key(key_json).unwrap();
38//! // Encode the cose-sign1 with the decoded cose-key and cose-sign1 JSON
39//! let res = utils::decode_json(data, &key, SIG1_TAG).unwrap();
40//!
41//! // Verify the signature
42//! let mut verify = CoseMessage::new_sign();
43//! verify.bytes = res;
44//! verify.init_decoder(None).unwrap();
45//! verify.key(&key).unwrap();
46//! verify.decode(None, None).unwrap();
47//! }
48//!
49//! ```
50//! ## cose-encrypt0
51//!
52//! ```
53//! use cose::utils;
54//! use cose::message::{CoseMessage, ENC0_TAG};
55//!
56//! fn main() {
57//! // cose-encrypt0 message in JSON format
58//! let data: &str = r#"
59//! {
60//! "protected": {"alg": 24 },
61//! "unprotected": {"kid": "11", "iv": "000102030405060700010203" },
62//! "payload": "This is the content."
63//! }"#;
64//! // cose-key in JSON format
65//! let key_json: &str = r#"
66//! {
67//! "kty": 4,
68//! "alg": 24,
69//! "k": "849b57219dae48de646d07dbb533566e976686457c1491be3a76dcea6c427188",
70//! "key ops": [3, 4]
71//! }"#;
72//!
73//! // Decode the cose-key JSON to CoseKey structure
74//! let key = utils::decode_json_key(key_json).unwrap();
75//! // Encode the cose-encrypt0 with the decoded cose-key and cose-encrypt0 JSON
76//! let res = utils::decode_json(data, &key, ENC0_TAG).unwrap();
77//!
78//! // Decrypt and verify
79//! let mut dec0 = CoseMessage::new_encrypt();
80//! dec0.bytes = res;
81//! dec0.init_decoder(None).unwrap();
82//!
83//! dec0.key(&key).unwrap();
84//! let resp = dec0.decode(None, None).unwrap();
85//! assert_eq!(resp, b"This is the content.".to_vec());
86//! }
87//! ```
88//! ## cose-mac0
89//!
90//! ```
91//! use cose::utils;
92//! use cose::message::{CoseMessage, MAC0_TAG};
93//!
94//! pub fn mac0_json() {
95//! // cose-mac0 message in JSON format
96//! let data: &str = r#"
97//! {
98//! "protected": {"crit": [1, 2], "alg": 26 },
99//! "unprotected": {"kid": "11"},
100//! "payload": "This is the content."
101//! }"#;
102//!
103//! // cose-key in JSON format
104//! let key_json: &str = r#"
105//! {
106//! "kty": 4,
107//! "alg": 26,
108//! "k": "849b57219dae48de646d07dbb533566e976686457c1491be3a76dcea6c427188",
109//! "key ops": [9, 10]
110//! }"#;
111//!
112//! // Decode the cose-key JSON to CoseKey structure
113//! let key = utils::decode_json_key(key_json).unwrap();
114//! // Encode the cose-mac0 with the decoded cose-key and cose-mac0 JSON
115//! let res = utils::decode_json(data, &key, MAC0_TAG).unwrap();
116//!
117//! // Verify the MAC tag
118//! let mut verify = CoseMessage::new_mac();
119//! verify.bytes = res;
120//! verify.init_decoder(None).unwrap();
121//!
122//! verify.key(&key).unwrap();
123//! verify.decode(None, None).unwrap();
124//! }
125//!
126//! ```
127
128use crate::common;
129#[cfg(feature = "json")]
130use crate::errors::CoseResult;
131use crate::errors::{CoseError, CoseResultWithRet};
132use crate::headers;
133#[cfg(feature = "json")]
134use crate::keys;
135use crate::message;
136#[cfg(feature = "json")]
137use crate::message::CoseMessage;
138use cbor::{types::Tag, Config, Decoder};
139#[cfg(feature = "json")]
140use hex;
141#[cfg(feature = "json")]
142use serde_json::Value;
143use std::io::Cursor;
144
145/// Function that with a given COSE message bytes, identifies the corresponding message type.
146///
147/// This only works if the message is properly tagged.
148pub fn cose_type_finder(bytes: &Vec<u8>) -> CoseResultWithRet<String> {
149 let input = Cursor::new(bytes);
150 let mut decoder = Decoder::new(Config::default(), input);
151 let tag = decoder.tag()?;
152 match tag {
153 Tag::Unassigned(message::ENC0_TAG) => Ok(message::ENC0_TYPE.to_string()),
154 Tag::Unassigned(message::MAC0_TAG) => Ok(message::MAC0_TYPE.to_string()),
155 Tag::Unassigned(message::SIG1_TAG) => Ok(message::SIG1_TYPE.to_string()),
156 Tag::Unassigned(message::ENC_TAG) => Ok(message::ENC_TYPE.to_string()),
157 Tag::Unassigned(message::MAC_TAG) => Ok(message::MAC_TYPE.to_string()),
158 Tag::Unassigned(message::SIG_TAG) => Ok(message::SIG_TYPE.to_string()),
159 _ => Err(CoseError::InvalidCoseStructure()),
160 }
161}
162
163/// Function that encodes a json object to a COSE message and applies the respective cryptographic
164/// operations with the given cose-key.
165///
166/// `tag` parameter is the COSE message type identifier.
167#[cfg(feature = "json")]
168pub fn decode_json(
169 message_json: &str,
170 key: &keys::CoseKey,
171 tag: u64,
172) -> CoseResultWithRet<Vec<u8>> {
173 let message_value: Value = serde_json::from_str(message_json)?;
174 let mut header = headers::CoseHeader::new();
175 decode_json_header(&mut header, &message_value["protected"].to_string(), true)?;
176 decode_json_header(
177 &mut header,
178 &message_value["unprotected"].to_string(),
179 false,
180 )?;
181 let payload = match &message_value["payload"] {
182 Value::String(r) => r.as_bytes().to_vec(),
183 _ => Vec::new(),
184 };
185
186 if tag == message::SIG1_TAG {
187 let mut sign = CoseMessage::new_sign();
188 sign.payload(payload);
189 sign.add_header(header);
190 sign.key(&key)?;
191 sign.secure_content(None)?;
192 sign.encode(true)?;
193 Ok(sign.bytes)
194 } else if tag == message::ENC0_TAG {
195 let mut enc = CoseMessage::new_encrypt();
196 enc.payload(payload);
197 enc.add_header(header);
198 enc.key(&key)?;
199 enc.secure_content(None)?;
200 enc.encode(true)?;
201 Ok(enc.bytes)
202 } else if tag == message::MAC0_TAG {
203 let mut mac = CoseMessage::new_mac();
204 mac.payload(payload);
205 mac.add_header(header);
206 mac.key(&key)?;
207 mac.secure_content(None)?;
208 mac.encode(true)?;
209 Ok(mac.bytes)
210 } else {
211 Ok(Vec::new())
212 }
213}
214
215#[cfg(feature = "json")]
216fn decode_json_header(
217 header: &mut headers::CoseHeader,
218 json_header: &str,
219 prot: bool,
220) -> CoseResult {
221 let json_value: Value = serde_json::from_str(json_header)?;
222 if json_value.get("crit").is_some() {
223 for i in json_value["crit"].as_array().unwrap().to_vec() {
224 header.crit.push(i.as_i64().unwrap() as i32);
225 }
226 }
227 if json_value.get("alg").is_some() {
228 let alg = match &json_value["alg"] {
229 Value::String(r) => common::get_alg_id(r.to_string())?,
230 Value::Number(v) => v.as_i64().unwrap() as i32,
231 _ => 0,
232 };
233 header.alg(alg, prot, false);
234 }
235 if json_value.get("kid").is_some() {
236 header.kid(
237 json_value["kid"].as_str().unwrap().as_bytes().to_vec(),
238 prot,
239 false,
240 );
241 }
242 if json_value.get("iv").is_some() {
243 header.iv(
244 hex::decode(json_value["iv"].as_str().unwrap().to_string())?,
245 prot,
246 false,
247 );
248 }
249 if json_value.get("partial iv").is_some() {
250 header.partial_iv(
251 hex::decode(json_value["partial iv"].as_str().unwrap().to_string())?,
252 prot,
253 false,
254 );
255 }
256 if json_value.get("content type").is_some() {
257 match &json_value["content_type"] {
258 Value::String(r) => {
259 header.content_type(
260 headers::ContentTypeTypes::Tstr(r.as_str().to_string()),
261 prot,
262 false,
263 );
264 }
265 Value::Number(v) => {
266 header.content_type(
267 headers::ContentTypeTypes::Uint(v.as_u64().unwrap() as u32),
268 prot,
269 false,
270 );
271 }
272 _ => (),
273 };
274 }
275 Ok(())
276}
277
278/// Function to decode a json object to a cose-key structure.
279#[cfg(feature = "json")]
280pub fn decode_json_key(json_key: &str) -> CoseResultWithRet<keys::CoseKey> {
281 let json_value: Value = serde_json::from_str(json_key)?;
282 let mut key = keys::CoseKey::new();
283 if json_value.get("alg").is_some() {
284 let alg = match &json_value["alg"] {
285 Value::String(r) => common::get_alg_id(r.to_string())?,
286 Value::Number(v) => v.as_i64().unwrap() as i32,
287 _ => 0,
288 };
289 key.alg(alg);
290 }
291 if json_value.get("kty").is_some() {
292 let kty = match &json_value["kty"] {
293 Value::String(r) => common::get_kty_id(r.to_string())?,
294 Value::Number(v) => v.as_i64().unwrap() as i32,
295 _ => 0,
296 };
297 key.kty(kty);
298 }
299 if json_value.get("crv").is_some() {
300 let crv = match &json_value["crv"] {
301 Value::String(r) => common::get_crv_id(r.to_string())?,
302 Value::Number(v) => v.as_i64().unwrap() as i32,
303 _ => 0,
304 };
305 key.crv(crv);
306 }
307 if json_value.get("key ops").is_some() {
308 let mut key_ops = Vec::new();
309 for i in json_value["key ops"].as_array().unwrap().to_vec() {
310 key_ops.push(match i {
311 Value::String(r) => common::get_key_op_id(r.to_string())?,
312 Value::Number(v) => v.as_i64().unwrap() as i32,
313 _ => 0,
314 });
315 }
316 key.key_ops(key_ops);
317 }
318 if json_value.get("x").is_some() {
319 key.x(hex::decode(json_value["x"].as_str().unwrap().to_string())?);
320 }
321 if json_value.get("y").is_some() {
322 match &json_value["y"] {
323 Value::String(r) => {
324 key.y(hex::decode(r.as_str().to_string())?);
325 }
326 _ => {}
327 };
328 }
329 if json_value.get("d").is_some() {
330 key.d(hex::decode(json_value["d"].as_str().unwrap().to_string())?);
331 }
332 if json_value.get("k").is_some() {
333 key.k(hex::decode(json_value["k"].as_str().unwrap().to_string())?);
334 }
335 Ok(key)
336}