cbor: prepare for publishing as standalone crate

- Add an example of usage
- Add a minimal README, including the example code
- Document public items
- Add more info to Cargo.toml
This commit is contained in:
David Drysdale
2021-06-09 10:12:55 +01:00
committed by kaczmarczyck
parent 7719078d46
commit 3aca5fbc74
7 changed files with 200 additions and 2 deletions

View File

@@ -5,8 +5,14 @@ authors = [
"Fabian Kaczmarczyck <kaczmarczyck@google.com>", "Fabian Kaczmarczyck <kaczmarczyck@google.com>",
"Guillaume Endignoux <guillaumee@google.com>", "Guillaume Endignoux <guillaumee@google.com>",
"Jean-Michel Picod <jmichel@google.com>", "Jean-Michel Picod <jmichel@google.com>",
"David Drysdale <drysdale@google.com>",
] ]
license = "Apache-2.0" license = "Apache-2.0"
edition = "2018" edition = "2018"
description = "CBOR parsing library"
homepage = "https://github.com/google/OpenSK"
repository = "https://github.com/google/OpenSK"
keywords = ["cbor", "serialization", "no_std"]
categories = ["encoding"]
[dependencies] [dependencies]

63
libraries/cbor/README.md Normal file
View File

@@ -0,0 +1,63 @@
# CBOR Parsing Library
This crate implements Concise Binary Object Representation (CBOR) from [RFC
8949](https://datatracker.ietf.org/doc/html/rfc8949).
## Usage
```rust
fn main() {
// Build a CBOR object with various different types included. Note that this
// object is not built in canonical order.
let manual_object = Value::Map(vec![
(
Value::Unsigned(1),
Value::Array(vec![Value::Unsigned(2), Value::Unsigned(3)]),
),
(
Value::TextString("tstr".to_owned()),
Value::ByteString(vec![1, 2, 3]),
),
(Value::Negative(-2), Value::Simple(SimpleValue::NullValue)),
(Value::Unsigned(3), Value::Simple(SimpleValue::TrueValue)),
]);
// Build the same object using the crate's convenience macros.
let macro_object = cbor_map! {
1 => cbor_array![2, 3],
"tstr" => cbor_bytes!(vec![1, 2, 3]),
-2 => cbor_null!(),
3 => cbor_true!(),
};
assert_eq!(manual_object, macro_object);
println!("Object {:?}", manual_object);
// Serialize to bytes.
let mut manual_data = vec![];
sk_cbor::writer::write(manual_object, &mut manual_data);
let hex_manual_data = hexify(&manual_data);
// Serialized version is in canonical order.
println!("Serializes to {}", hex_manual_data);
assert_eq!(
hex_manual_data,
concat!(
"a4", // 4-map
"01", // int(1) =>
"820203", // 2-array [2, 3],
"03", // int(3) =>
"f5", // true,
"21", // nint(-2) =>
"f6", // null,
"6474737472", // 4-tstr "tstr" =>
"43010203" // 3-bstr
)
);
// Convert back to an object. This is different than the original object,
// because the map is now in canonical order.
let recovered_object = sk_cbor::reader::read(&manual_data).unwrap();
println!("Deserializes to {:?}", recovered_object);
}
```

View File

@@ -0,0 +1,90 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
//! Example program demonstrating cbor usage.
extern crate alloc;
use sk_cbor::values::{SimpleValue, Value};
use sk_cbor::{cbor_array, cbor_bytes, cbor_map, cbor_null, cbor_true};
fn hexify(data: &[u8]) -> String {
let mut s = String::new();
for b in data {
s.push_str(&format!("{:02x}", b));
}
s
}
fn main() {
// Build a CBOR object with various different types included. Note that this
// object is not built in canonical order.
let manual_object = Value::Map(vec![
(
Value::Unsigned(1),
Value::Array(vec![Value::Unsigned(2), Value::Unsigned(3)]),
),
(
Value::TextString("tstr".to_owned()),
Value::ByteString(vec![1, 2, 3]),
),
(Value::Negative(-2), Value::Simple(SimpleValue::NullValue)),
(Value::Unsigned(3), Value::Simple(SimpleValue::TrueValue)),
]);
// Build the same object using the crate's convenience macros.
let macro_object = cbor_map! {
1 => cbor_array![2, 3],
"tstr" => cbor_bytes!(vec![1, 2, 3]),
-2 => cbor_null!(),
3 => cbor_true!(),
};
assert_eq!(manual_object, macro_object);
println!("Object {:?}", manual_object);
// Serialize to bytes.
let mut manual_data = vec![];
sk_cbor::writer::write(manual_object, &mut manual_data);
let hex_manual_data = hexify(&manual_data);
let mut macro_data = vec![];
sk_cbor::writer::write(macro_object, &mut macro_data);
let hex_macro_data = hexify(&macro_data);
assert_eq!(hex_manual_data, hex_macro_data);
// Serialized version is in canonical order.
println!("Serializes to {}", hex_manual_data);
assert_eq!(
hex_manual_data,
concat!(
"a4", // 4-map
"01", // int(1) =>
"820203", // 2-array [2, 3],
"03", // int(3) =>
"f5", // true,
"21", // nint(-2) =>
"f6", // null,
"6474737472", // 4-tstr "tstr" =>
"43010203" // 3-bstr
)
);
// Convert back to an object. This is different than the original object,
// because the map is now in canonical order.
let recovered_object = sk_cbor::reader::read(&manual_data).unwrap();
println!("Deserializes to {:?}", recovered_object);
}

View File

@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
//! Convenience macros for working with CBOR values.
use crate::values::Value; use crate::values::Value;
use alloc::vec; use alloc::vec;
use core::cmp::Ordering; use core::cmp::Ordering;
@@ -110,6 +112,7 @@ pub fn destructure_cbor_map_peek_value(
} }
} }
/// Assert that the keys in a vector of key-value pairs are in canonical order.
#[macro_export] #[macro_export]
macro_rules! assert_sorted_keys { macro_rules! assert_sorted_keys {
// Last key // Last key

View File

@@ -12,11 +12,14 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
//! Functionality for deserializing CBOR data into values.
use super::values::{Constants, SimpleValue, Value}; use super::values::{Constants, SimpleValue, Value};
use crate::{cbor_array_vec, cbor_bytes_lit, cbor_map_collection, cbor_text, cbor_unsigned}; use crate::{cbor_array_vec, cbor_bytes_lit, cbor_map_collection, cbor_text, cbor_unsigned};
use alloc::str; use alloc::str;
use alloc::vec::Vec; use alloc::vec::Vec;
/// Possible errors from a deserialization operation.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum DecoderError { pub enum DecoderError {
UnsupportedMajorType, UnsupportedMajorType,
@@ -32,6 +35,8 @@ pub enum DecoderError {
OutOfRangeIntegerValue, OutOfRangeIntegerValue,
} }
/// Deserialize CBOR binary data to produce a single [`Value`], expecting that there
/// is no additional data.
pub fn read(encoded_cbor: &[u8]) -> Result<Value, DecoderError> { pub fn read(encoded_cbor: &[u8]) -> Result<Value, DecoderError> {
let mut reader = Reader::new(encoded_cbor); let mut reader = Reader::new(encoded_cbor);
let value = reader.decode_complete_data_item(Reader::MAX_NESTING_DEPTH)?; let value = reader.decode_complete_data_item(Reader::MAX_NESTING_DEPTH)?;

View File

@@ -12,24 +12,34 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
//! Types for expressing CBOR values.
use super::writer::write; use super::writer::write;
use alloc::string::{String, ToString}; use alloc::string::{String, ToString};
use alloc::vec::Vec; use alloc::vec::Vec;
use core::cmp::Ordering; use core::cmp::Ordering;
/// Possible CBOR values.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Value { pub enum Value {
/// Unsigned integer value (uint).
Unsigned(u64), Unsigned(u64),
// We only use 63 bits of information here. /// Signed integer value (nint). Only 63 bits of information are used here.
Negative(i64), Negative(i64),
/// Byte string (bstr).
ByteString(Vec<u8>), ByteString(Vec<u8>),
/// Text string (tstr).
TextString(String), TextString(String),
/// Array/tuple of values.
Array(Vec<Value>), Array(Vec<Value>),
/// Map of key-value pairs.
Map(Vec<(Value, Value)>), Map(Vec<(Value, Value)>),
// TAG is omitted // TAG is omitted
/// Simple value.
Simple(SimpleValue), Simple(SimpleValue),
} }
/// Specific simple CBOR values.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum SimpleValue { pub enum SimpleValue {
FalseValue = 20, FalseValue = 20,
@@ -38,20 +48,30 @@ pub enum SimpleValue {
Undefined = 23, Undefined = 23,
} }
/// Constant values required for CBOR encoding.
pub struct Constants {} pub struct Constants {}
impl Constants { impl Constants {
/// Number of bits used to shift left the major type of a CBOR type byte.
pub const MAJOR_TYPE_BIT_SHIFT: u8 = 5; pub const MAJOR_TYPE_BIT_SHIFT: u8 = 5;
/// Mask to retrieve the additional information held in a CBOR type bytes,
/// ignoring the major type.
pub const ADDITIONAL_INFORMATION_MASK: u8 = 0x1F; pub const ADDITIONAL_INFORMATION_MASK: u8 = 0x1F;
/// Additional information value that indicates the largest inline value.
pub const ADDITIONAL_INFORMATION_MAX_INT: u8 = 23; pub const ADDITIONAL_INFORMATION_MAX_INT: u8 = 23;
/// Additional information value indicating that a 1-byte length follows.
pub const ADDITIONAL_INFORMATION_1_BYTE: u8 = 24; pub const ADDITIONAL_INFORMATION_1_BYTE: u8 = 24;
/// Additional information value indicating that a 2-byte length follows.
pub const ADDITIONAL_INFORMATION_2_BYTES: u8 = 25; pub const ADDITIONAL_INFORMATION_2_BYTES: u8 = 25;
/// Additional information value indicating that a 4-byte length follows.
pub const ADDITIONAL_INFORMATION_4_BYTES: u8 = 26; pub const ADDITIONAL_INFORMATION_4_BYTES: u8 = 26;
/// Additional information value indicating that an 8-byte length follows.
pub const ADDITIONAL_INFORMATION_8_BYTES: u8 = 27; pub const ADDITIONAL_INFORMATION_8_BYTES: u8 = 27;
} }
impl Value { impl Value {
// For simplicity, this only takes i64. Construct directly for the last bit. /// Create an appropriate CBOR integer value (uint/nint).
/// For simplicity, this only takes i64. Construct directly for the last bit.
pub fn integer(int: i64) -> Value { pub fn integer(int: i64) -> Value {
if int >= 0 { if int >= 0 {
Value::Unsigned(int as u64) Value::Unsigned(int as u64)
@@ -60,6 +80,7 @@ impl Value {
} }
} }
/// Create a CBOR boolean simple value.
pub fn bool_value(b: bool) -> Value { pub fn bool_value(b: bool) -> Value {
if b { if b {
Value::Simple(SimpleValue::TrueValue) Value::Simple(SimpleValue::TrueValue)
@@ -68,6 +89,7 @@ impl Value {
} }
} }
/// Return the major type for the [`Value`].
pub fn type_label(&self) -> u8 { pub fn type_label(&self) -> u8 {
// TODO use enum discriminant instead when stable // TODO use enum discriminant instead when stable
// https://github.com/rust-lang/rust/issues/60553 // https://github.com/rust-lang/rust/issues/60553
@@ -128,6 +150,7 @@ impl PartialEq for Value {
} }
impl SimpleValue { impl SimpleValue {
/// Create a simple value from its encoded value.
pub fn from_integer(int: u64) -> Option<SimpleValue> { pub fn from_integer(int: u64) -> Option<SimpleValue> {
match int { match int {
20 => Some(SimpleValue::FalseValue), 20 => Some(SimpleValue::FalseValue),
@@ -193,7 +216,9 @@ impl From<bool> for Value {
} }
} }
/// Trait that indicates that a type can be converted to a CBOR [`Value`].
pub trait IntoCborValue { pub trait IntoCborValue {
/// Convert `self` into a CBOR [`Value`], consuming it along the way.
fn into_cbor_value(self) -> Value; fn into_cbor_value(self) -> Value;
} }
@@ -206,7 +231,9 @@ where
} }
} }
/// Trait that indicates that a type can be converted to a CBOR [`Option<Value>`].
pub trait IntoCborValueOption { pub trait IntoCborValueOption {
/// Convert `self` into a CBOR [`Option<Value>`], consuming it along the way.
fn into_cbor_value_option(self) -> Option<Value>; fn into_cbor_value_option(self) -> Option<Value>;
} }

View File

@@ -12,9 +12,13 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
//! Functionality for serializing CBOR values into bytes.
use super::values::{Constants, Value}; use super::values::{Constants, Value};
use alloc::vec::Vec; use alloc::vec::Vec;
/// Convert a [`Value`] to serialized CBOR data, consuming it along the way and appending to the provided vector.
/// Returns a `bool` indicating whether conversion succeeded.
pub fn write(value: Value, encoded_cbor: &mut Vec<u8>) -> bool { pub fn write(value: Value, encoded_cbor: &mut Vec<u8>) -> bool {
let mut writer = Writer::new(encoded_cbor); let mut writer = Writer::new(encoded_cbor);
writer.encode_cbor(value, Writer::MAX_NESTING_DEPTH) writer.encode_cbor(value, Writer::MAX_NESTING_DEPTH)