Skip to content

Commit

Permalink
Merge pull request #7 from KtorZ/master
Browse files Browse the repository at this point in the history
Add support for .cbor control operator
  • Loading branch information
ericseppanen committed Oct 1, 2023
2 parents e16d5c1 + 42a7651 commit 154798e
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Supported CDDL features:
- Turn a group into a choice (`&`)
- Map keys with cut syntax (`^ =>`)
- Generic types
- Control operators `.size` and `.regexp`
- Control operators `.cbor`, `.size` and `.regexp`

Unimplemented CDDL features:
- Extend type with `/=`
Expand Down
20 changes: 20 additions & 0 deletions src/flatten.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ fn flatten_control(ctl: &ast::TypeControl) -> FlattenResult<Node> {
let ctl_result = match ctl.op.as_str() {
"size" => control_size(ctl)?,
"regexp" => control_regex(ctl)?,
"cbor" => control_cbor(ctl)?,
_ => return Err(ValidateError::Unsupported("control operator".into())),
};

Expand Down Expand Up @@ -172,6 +173,25 @@ fn control_regex(ctl: &ast::TypeControl) -> FlattenResult<Control> {
}
}

// Handle the "cbor" control operator:
// <target> .cbor <rule>
// The only allowed targets are bstr.
//
fn control_cbor(ctl: &ast::TypeControl) -> FlattenResult<Control> {
let target = flatten_type2(&ctl.target)?;
let node = Box::new(flatten_type2(&ctl.arg)?);

// The target type must be a byte string.
match target {
Node::PreludeType(PreludeType::Bstr) => {}
_ => {
return Err(ValidateError::Structural("bad .cbor target type".into()));
}
}

Ok(Control::Cbor(CtlOpCbor { node }))
}

// The only way a range start or end can be specified is with a literal
// value, or with a typename. We will accept either of those, and throw
// an error otherwise. Let the validator worry about whether a typename
Expand Down
15 changes: 15 additions & 0 deletions src/ivt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,8 @@ pub enum Control {
Size(CtlOpSize),
/// Apply a regular expression to a text string.
Regexp(CtlOpRegexp),
/// Validate a nested CBOR bytestring
Cbor(CtlOpCbor),
}

/// Control Operator `.size`
Expand Down Expand Up @@ -416,6 +418,19 @@ impl PartialEq for CtlOpRegexp {
}
}

/// Control Operator `.cbor`
///
/// `.cbor` is defined in RFC 8610 3.8.4
///
/// A ".cbor" control on a byte string indicates that the byte string
/// carries a CBOR-encoded data item. Decoded, the data item matches the
/// type given as the right-hand-side argument.
#[derive(Debug, Clone, PartialEq)]
pub struct CtlOpCbor {
/// The nested node to satisfy
pub(crate) node: Box<Node>,
}

/// Any node in the Intermediate Validation Tree.
#[derive(Debug, Clone, PartialEq, IntoStaticStr)]
#[allow(missing_docs)]
Expand Down
26 changes: 26 additions & 0 deletions src/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,32 @@ fn validate_control(ctl: &Control, value: &Value, ctx: &Context) -> ValidateResu
match ctl {
Control::Size(ctl_size) => validate_control_size(ctl_size, value, ctx),
Control::Regexp(re) => validate_control_regexp(re, value),
Control::Cbor(ctl_cbor) => validate_control_cbor(ctl_cbor, value, ctx),
}
}

#[cfg(not(feature = "serde_cbor"))]
fn validate_control_cbor(_ctl_cbor: &CtlOpCbor, _value: &Value, _ctx: &Context) -> ValidateResult {
Err(ValidateError::Unsupported(
"'.cbor' control operator; enable serde_cbor feature to support.".into(),
))
}

#[cfg(feature = "serde_cbor")]
fn validate_control_cbor(ctl_cbor: &CtlOpCbor, value: &Value, ctx: &Context) -> ValidateResult {
use serde_cbor::Value as CBOR_Value;
use std::convert::TryFrom;

match value {
Value::Bytes(bytes) => {
let cbor_value: CBOR_Value = serde_cbor::from_slice(bytes)
.map_err(|e| ValidateError::ValueError(format!("{}", e)))?;

let nested_value = Value::try_from(cbor_value)?;

validate(&nested_value, ctl_cbor.node.as_ref(), ctx)
}
_ => Err::<(), ValidateError>(mismatch("Bytes")),
}
}

Expand Down
19 changes: 19 additions & 0 deletions tests/cbor_cddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub mod cbor {
pub const BYTES_EMPTY: &[u8] = b"\x40";
pub const BYTES_1234: &[u8] = b"\x44\x01\x02\x03\x04"; // hex 01020304

pub const CBOR_INT_23: &[u8] = b"\x41\x17"; // cbor(23)
}

#[test]
Expand Down Expand Up @@ -692,6 +693,24 @@ fn cbor_control_size() {
validate_cbor_bytes("thing", cddl_input, cbor::BYTES_EMPTY).unwrap_err();
}

#[test]
fn cbor_control_cbor() {
let cddl_input = r#"thing = bytes .cbor uint"#;
validate_cbor_bytes("thing", cddl_input, cbor::CBOR_INT_23).unwrap();

let cddl_input = r#"
thing = bytes .cbor foo
foo = uint
"#;
validate_cbor_bytes("thing", cddl_input, cbor::CBOR_INT_23).unwrap();

let cddl_input = r#"
thing = bytes .cbor foo<uint>
foo<t> = t
"#;
validate_cbor_bytes("thing", cddl_input, cbor::CBOR_INT_23).unwrap();
}

#[track_caller]
fn validate_cbor_tstr(name: &str, cddl: &str, input: &str) -> ValidateResult {
let cbor_bytes = serde_cbor::to_vec(&input).unwrap();
Expand Down

0 comments on commit 154798e

Please sign in to comment.