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    if tag == Tag::Unassigned(message::ENC0_TAG) {
153        Ok(message::ENC0_TYPE.to_string())
154    } else if tag == Tag::Unassigned(message::MAC0_TAG) {
155        Ok(message::MAC0_TYPE.to_string())
156    } else if tag == Tag::Unassigned(message::SIG1_TAG) {
157        Ok(message::SIG1_TYPE.to_string())
158    } else if tag == Tag::Unassigned(message::ENC_TAG) {
159        Ok(message::ENC_TYPE.to_string())
160    } else if tag == Tag::Unassigned(message::MAC_TAG) {
161        Ok(message::MAC_TYPE.to_string())
162    } else if tag == Tag::Unassigned(message::SIG_TAG) {
163        Ok(message::SIG_TYPE.to_string())
164    } else {
165        Err(CoseError::InvalidCoseStructure())
166    }
167}
168
169/// Function that encodes a json object to a COSE message and applies the respective cryptographic
170/// operations with the given cose-key.
171///
172/// `tag` parameter is the COSE message type identifier.
173#[cfg(feature = "json")]
174pub fn decode_json(
175    message_json: &str,
176    key: &keys::CoseKey,
177    tag: u64,
178) -> CoseResultWithRet<Vec<u8>> {
179    let message_value: Value = serde_json::from_str(message_json)?;
180    let mut header = headers::CoseHeader::new();
181    decode_json_header(&mut header, &message_value["protected"].to_string(), true)?;
182    decode_json_header(
183        &mut header,
184        &message_value["unprotected"].to_string(),
185        false,
186    )?;
187    let payload = match &message_value["payload"] {
188        Value::String(r) => r.as_bytes().to_vec(),
189        _ => Vec::new(),
190    };
191
192    if tag == message::SIG1_TAG {
193        let mut sign = CoseMessage::new_sign();
194        sign.payload(payload);
195        sign.add_header(header);
196        sign.key(&key)?;
197        sign.secure_content(None)?;
198        sign.encode(true)?;
199        Ok(sign.bytes)
200    } else if tag == message::ENC0_TAG {
201        let mut enc = CoseMessage::new_encrypt();
202        enc.payload(payload);
203        enc.add_header(header);
204        enc.key(&key)?;
205        enc.secure_content(None)?;
206        enc.encode(true)?;
207        Ok(enc.bytes)
208    } else if tag == message::MAC0_TAG {
209        let mut mac = CoseMessage::new_mac();
210        mac.payload(payload);
211        mac.add_header(header);
212        mac.key(&key)?;
213        mac.secure_content(None)?;
214        mac.encode(true)?;
215        Ok(mac.bytes)
216    } else {
217        Ok(Vec::new())
218    }
219}
220
221#[cfg(feature = "json")]
222fn decode_json_header(
223    header: &mut headers::CoseHeader,
224    json_header: &str,
225    prot: bool,
226) -> CoseResult {
227    let json_value: Value = serde_json::from_str(json_header)?;
228    if json_value.get("crit") != None {
229        for i in json_value["crit"].as_array().unwrap().to_vec() {
230            header.crit.push(i.as_i64().unwrap() as i32);
231        }
232    }
233    if json_value.get("alg") != None {
234        let alg = match &json_value["alg"] {
235            Value::String(r) => common::get_alg_id(r.to_string())?,
236            Value::Number(v) => v.as_i64().unwrap() as i32,
237            _ => 0,
238        };
239        header.alg(alg, prot, false);
240    }
241    if json_value.get("kid") != None {
242        header.kid(
243            json_value["kid"].as_str().unwrap().as_bytes().to_vec(),
244            prot,
245            false,
246        );
247    }
248    if json_value.get("iv") != None {
249        header.iv(
250            hex::decode(json_value["iv"].as_str().unwrap().to_string())?,
251            prot,
252            false,
253        );
254    }
255    if json_value.get("partial iv") != None {
256        header.partial_iv(
257            hex::decode(json_value["partial iv"].as_str().unwrap().to_string())?,
258            prot,
259            false,
260        );
261    }
262    if json_value.get("content type") != None {
263        match &json_value["content_type"] {
264            Value::String(r) => {
265                header.content_type(
266                    headers::ContentTypeTypes::Tstr(r.as_str().to_string()),
267                    prot,
268                    false,
269                );
270            }
271            Value::Number(v) => {
272                header.content_type(
273                    headers::ContentTypeTypes::Uint(v.as_u64().unwrap() as u32),
274                    prot,
275                    false,
276                );
277            }
278            _ => (),
279        };
280    }
281    Ok(())
282}
283
284/// Function to decode a json object to a cose-key structure.
285#[cfg(feature = "json")]
286pub fn decode_json_key(json_key: &str) -> CoseResultWithRet<keys::CoseKey> {
287    let json_value: Value = serde_json::from_str(json_key)?;
288    let mut key = keys::CoseKey::new();
289    if json_value.get("alg") != None {
290        let alg = match &json_value["alg"] {
291            Value::String(r) => common::get_alg_id(r.to_string())?,
292            Value::Number(v) => v.as_i64().unwrap() as i32,
293            _ => 0,
294        };
295        key.alg(alg);
296    }
297    if json_value.get("kty") != None {
298        let kty = match &json_value["kty"] {
299            Value::String(r) => common::get_kty_id(r.to_string())?,
300            Value::Number(v) => v.as_i64().unwrap() as i32,
301            _ => 0,
302        };
303        key.kty(kty);
304    }
305    if json_value.get("crv") != None {
306        let crv = match &json_value["crv"] {
307            Value::String(r) => common::get_crv_id(r.to_string())?,
308            Value::Number(v) => v.as_i64().unwrap() as i32,
309            _ => 0,
310        };
311        key.crv(crv);
312    }
313    if json_value.get("key ops") != None {
314        let mut key_ops = Vec::new();
315        for i in json_value["key ops"].as_array().unwrap().to_vec() {
316            key_ops.push(match i {
317                Value::String(r) => common::get_key_op_id(r.to_string())?,
318                Value::Number(v) => v.as_i64().unwrap() as i32,
319                _ => 0,
320            });
321        }
322        key.key_ops(key_ops);
323    }
324    if json_value.get("x") != None {
325        key.x(hex::decode(json_value["x"].as_str().unwrap().to_string())?);
326    }
327    if json_value.get("y") != None {
328        match &json_value["y"] {
329            Value::String(r) => {
330                key.y(hex::decode(r.as_str().to_string())?);
331            }
332            _ => {}
333        };
334    }
335    if json_value.get("d") != None {
336        key.d(hex::decode(json_value["d"].as_str().unwrap().to_string())?);
337    }
338    if json_value.get("k") != None {
339        key.k(hex::decode(json_value["k"].as_str().unwrap().to_string())?);
340    }
341    Ok(key)
342}