From 60e391c8582338f54b1eff8f842f1c149289178e Mon Sep 17 00:00:00 2001 From: anweiss <2326106+anweiss@users.noreply.github.com> Date: Wed, 28 Oct 2020 12:15:34 -0400 Subject: [PATCH 01/11] did test fixtures and regex escaping fixes --- src/validator/cbor.rs | 17 ++- src/validator/json.rs | 17 ++- src/validator/mod.rs | 20 +++ tests/cddl.rs | 5 +- tests/did.rs | 76 +++++++++++ tests/fixtures/cddl/diddoc.cddl | 29 +++++ tests/fixtures/did/accept/accept.cddl | 3 + .../fixtures/did/accept/accept_example1.cbor | 1 + .../fixtures/did/accept/accept_example1.json | 3 + .../fixtures/did/accept/accept_example2.cbor | 1 + .../fixtures/did/accept/accept_example2.json | 3 + .../fixtures/did/accept/accept_example3.cbor | 1 + .../fixtures/did/accept/accept_example3.json | 3 + .../did/assertionMethod/assertionMethod.cbor | 1 + .../did/assertionMethod/assertionMethod.cddl | 121 ++++++++++++++++++ .../assertionMethod_example7.json | 11 ++ 16 files changed, 296 insertions(+), 16 deletions(-) create mode 100644 tests/did.rs create mode 100644 tests/fixtures/cddl/diddoc.cddl create mode 100644 tests/fixtures/did/accept/accept.cddl create mode 100644 tests/fixtures/did/accept/accept_example1.cbor create mode 100644 tests/fixtures/did/accept/accept_example1.json create mode 100644 tests/fixtures/did/accept/accept_example2.cbor create mode 100644 tests/fixtures/did/accept/accept_example2.json create mode 100644 tests/fixtures/did/accept/accept_example3.cbor create mode 100644 tests/fixtures/did/accept/accept_example3.json create mode 100644 tests/fixtures/did/assertionMethod/assertionMethod.cbor create mode 100644 tests/fixtures/did/assertionMethod/assertionMethod.cddl create mode 100644 tests/fixtures/did/assertionMethod/assertionMethod_example7.json diff --git a/src/validator/cbor.rs b/src/validator/cbor.rs index 5fca640c..9e9b0acb 100644 --- a/src/validator/cbor.rs +++ b/src/validator/cbor.rs @@ -2126,12 +2126,17 @@ impl<'a> Visitor<'a, ValidationError> for CBORValidator<'a> { } Some(Token::REGEXP) | Some(Token::PCRE) => { let re = regex::Regex::new( - serde_json::from_str::(&format!("\"{}\"", t)) - .map_err(|e| ValidationError::from_validator(self, e.to_string()))? - .as_str() - .ok_or_else(|| { - ValidationError::from_validator(self, "malformed regex".to_string()) - })?, + &format_regex( + serde_json::from_str::(&format!("\"{}\"", t)) + .map_err(|e| ValidationError::from_validator(self, e.to_string()))? + .as_str() + .ok_or_else(|| { + ValidationError::from_validator(self, "malformed regex".to_string()) + })?, + ) + .ok_or_else(|| { + ValidationError::from_validator(self, "malformed regex".to_string()) + })?, ) .map_err(|e| ValidationError::from_validator(self, e.to_string()))?; diff --git a/src/validator/json.rs b/src/validator/json.rs index d58640df..fd970d35 100644 --- a/src/validator/json.rs +++ b/src/validator/json.rs @@ -1623,12 +1623,17 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { } Some(Token::REGEXP) | Some(Token::PCRE) => { let re = regex::Regex::new( - serde_json::from_str::(&format!("\"{}\"", t)) - .map_err(|e| ValidationError::from_validator(self, e.to_string()))? - .as_str() - .ok_or_else(|| { - ValidationError::from_validator(self, "malformed regex".to_string()) - })?, + &format_regex( + serde_json::from_str::(&format!("\"{}\"", t)) + .map_err(|e| ValidationError::from_validator(self, e.to_string()))? + .as_str() + .ok_or_else(|| { + ValidationError::from_validator(self, "malformed regex".to_string()) + })?, + ) + .ok_or_else(|| { + ValidationError::from_validator(self, "malformed regex".to_string()) + })?, ) .map_err(|e| ValidationError::from_validator(self, e.to_string()))?; diff --git a/src/validator/mod.rs b/src/validator/mod.rs index f59fc3f6..4fe5fc14 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -621,3 +621,23 @@ pub struct EntryCount { /// Optional occurrence pub entry_occurrence: Option, } + +/// Regex needs to be formatted in a certain way so it can be parsed. See +/// https://github.com/anweiss/cddl/issues/67 +pub fn format_regex(input: &str) -> Option { + let mut formatted_regex = String::from(input); + for replace in ["\\:", "\\%", "\\_", "\\/", "\\="].iter() { + formatted_regex = + formatted_regex.replace(replace, &replace.chars().nth(1).unwrap().to_string()); + } + + for find in ["?=", "?!", "?<=", "? Result<(), parser::Error> { diff --git a/tests/did.rs b/tests/did.rs new file mode 100644 index 00000000..e5d3c48a --- /dev/null +++ b/tests/did.rs @@ -0,0 +1,76 @@ +use cddl::{validate_cbor_from_slice, validate_json_from_str}; +use std::{ + error::Error, + ffi::OsStr, + fs::{self, File}, + io::Read, +}; + +#[test] +fn validate_did_json_examples() -> Result<(), Box> { + for entry in fs::read_dir("tests/fixtures/did/")? { + let entry = entry?; + if entry.file_type()?.is_dir() { + let mut cddl = String::new(); + + for file in fs::read_dir(entry.path())? { + let file = file?; + if file.path().extension().and_then(OsStr::to_str).unwrap() == "cddl" { + cddl = fs::read_to_string(file.path())?; + break; + } + } + + if cddl.is_empty() { + return Err(format!("missing cddl file at {:?}", entry.path()).into()); + } + + for file in fs::read_dir(entry.path())? { + let file = file?; + if file.path().extension().and_then(OsStr::to_str).unwrap() == "json" { + if let Err(e) = validate_json_from_str(&cddl, &fs::read_to_string(file.path())?) { + println!("{}", e); + } + } + } + } + } + + Ok(()) +} + +#[test] +fn validate_did_cbor_examples() -> Result<(), Box> { + for entry in fs::read_dir("tests/fixtures/did/")? { + let entry = entry?; + if entry.file_type()?.is_dir() { + let mut cddl = String::new(); + + for file in fs::read_dir(entry.path())? { + let file = file?; + if file.path().extension().and_then(OsStr::to_str).unwrap() == "cddl" { + cddl = fs::read_to_string(file.path())?; + break; + } + } + + if cddl.is_empty() { + return Err(format!("missing cddl file at {:?}", entry.path()).into()); + } + + for file in fs::read_dir(entry.path())? { + let file = file?; + if file.path().extension().and_then(OsStr::to_str).unwrap() == "cbor" { + let mut f = File::open(file.path())?; + let mut data = Vec::new(); + f.read_to_end(&mut data)?; + if let Err(e) = validate_cbor_from_slice(&cddl, &data) { + println!("{}", e); + } + } + } + } + } + + Ok(()) +} diff --git a/tests/fixtures/cddl/diddoc.cddl b/tests/fixtures/cddl/diddoc.cddl new file mode 100644 index 00000000..0b7dd961 --- /dev/null +++ b/tests/fixtures/cddl/diddoc.cddl @@ -0,0 +1,29 @@ +DID-document = { + ? @context : uri + id : did + ? publicKey : [* publicKey ] + ? authentication : [ *did // *publicKey // *tstr ] + ? service : [ + service ] + ? controller : did / [ *did ] + ? created : time + ? updated : time + proof : any +} + +publicKey = { + id : did + type : text + controller : uri +} + +did = tstr .pcre "^did\\:(?[a-z0-9]{2,})\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)" + +did-url = tstr .pcre "^did\\:(?[a-z0-9]{2,})\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\;(?[A-Za-z0-9\\/)(?\\?[a-z0-9\\=\\&])#(?.+)" + +service = { + id : did-url + type : text + serviceEndpoint : uri + ? description : text + * tstr => any +} \ No newline at end of file diff --git a/tests/fixtures/did/accept/accept.cddl b/tests/fixtures/did/accept/accept.cddl new file mode 100644 index 00000000..397bfa0e --- /dev/null +++ b/tests/fixtures/did/accept/accept.cddl @@ -0,0 +1,3 @@ +did-document-resolution-request = { + "accept" : "application/did+json" / "application/did+ld+json" / "application/did+cbor" / "application/did+dag+cbor" +} \ No newline at end of file diff --git a/tests/fixtures/did/accept/accept_example1.cbor b/tests/fixtures/did/accept/accept_example1.cbor new file mode 100644 index 00000000..6d71d838 --- /dev/null +++ b/tests/fixtures/did/accept/accept_example1.cbor @@ -0,0 +1 @@ +¡facceptwapplication/did+ld+json \ No newline at end of file diff --git a/tests/fixtures/did/accept/accept_example1.json b/tests/fixtures/did/accept/accept_example1.json new file mode 100644 index 00000000..0c4f2c4f --- /dev/null +++ b/tests/fixtures/did/accept/accept_example1.json @@ -0,0 +1,3 @@ +{ + "accept": "application/did+ld+json" +} \ No newline at end of file diff --git a/tests/fixtures/did/accept/accept_example2.cbor b/tests/fixtures/did/accept/accept_example2.cbor new file mode 100644 index 00000000..6d71d838 --- /dev/null +++ b/tests/fixtures/did/accept/accept_example2.cbor @@ -0,0 +1 @@ +¡facceptwapplication/did+ld+json \ No newline at end of file diff --git a/tests/fixtures/did/accept/accept_example2.json b/tests/fixtures/did/accept/accept_example2.json new file mode 100644 index 00000000..1424ef8d --- /dev/null +++ b/tests/fixtures/did/accept/accept_example2.json @@ -0,0 +1,3 @@ +{ + "accept": "application/did+cbor" +} \ No newline at end of file diff --git a/tests/fixtures/did/accept/accept_example3.cbor b/tests/fixtures/did/accept/accept_example3.cbor new file mode 100644 index 00000000..65e0f4ee --- /dev/null +++ b/tests/fixtures/did/accept/accept_example3.cbor @@ -0,0 +1 @@ +¡faccepttapplication/did+json \ No newline at end of file diff --git a/tests/fixtures/did/accept/accept_example3.json b/tests/fixtures/did/accept/accept_example3.json new file mode 100644 index 00000000..3ac70ca6 --- /dev/null +++ b/tests/fixtures/did/accept/accept_example3.json @@ -0,0 +1,3 @@ +{ + "accept": "application/did+json" +} \ No newline at end of file diff --git a/tests/fixtures/did/assertionMethod/assertionMethod.cbor b/tests/fixtures/did/assertionMethod/assertionMethod.cbor new file mode 100644 index 00000000..94bead7f --- /dev/null +++ b/tests/fixtures/did/assertionMethod/assertionMethod.cbor @@ -0,0 +1 @@ +¡oassertionMethod‚¤bidx@did:example:123#z6MkpzW2izkFjNwMBwwvKqmELaQcH8t54QL5xmBdJg9Xh1y4dtypexEd25519VerificationKey2018jcontrollerodid:example:123opublicKeyBase58x,BYEz8kVpPqSt5T7DeGoPVUrcTZcDeX5jGkGhUQBWmoBgx;did:example:123#WjKgJV7VRw3hmgU6--4v15c0Aewbcvat1BsRFTIqa5Q \ No newline at end of file diff --git a/tests/fixtures/did/assertionMethod/assertionMethod.cddl b/tests/fixtures/did/assertionMethod/assertionMethod.cddl new file mode 100644 index 00000000..0b2e44d5 --- /dev/null +++ b/tests/fixtures/did/assertionMethod/assertionMethod.cddl @@ -0,0 +1,121 @@ +DID-document = { + + "assertionMethod" : [ 1* publicKeyHex / ethereumAddress / publicKeyJwk / publicKeyBase58 / publicKeyGpg / did-url / relative-did-url ] + +} + +did = text .regexp "did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)" + +did-url = text .regexp "did\\:(?[a-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\/?(?[A-Za-z0-9\\/]+)\\??(?[A-Za-z0-9\\=\\&\\%\\-\\:]+)?\\#?(?[A-Za-z0-9\\-]+)?" + +;; base URI value is the DID that is associated with the DID subject +relative-did-url = text .regexp "^#.+$" ; fragment identifier of self relative DID url, i.e. #key-1 in DID document with `id` = did:example:123456789abcdefghi becomes `did:example:123456789abcdefghi#key-1` + + +;;; +; verificationMethod types included below for convenience +;;; + +publicKeyJwk = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyJwk" : publicKeyEC / publicKeyoct / publicKeyed25519 / publicKeyRSA + +} + + +;;; public key Hex is a type of verification method with public key encoded as base16 +publicKeyHex = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyHex" : text .regexp "[a-zA-F0-9]+" ; only allows for hexadecimal representations +} + +publicKeyGpg = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyGpg" : text ; This could probbaly be better to capture -----BEGIN PGP PUBLIC KEY BLOCK +} + +;;; public key Hex is a type of verification method with the public key encoded as base58 +publicKeyBase58 = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyBase58" : text .regexp "[a-zA-Z0-9]+" ; only allows for base58 representations +} + + +;;; ethereeum address is a type of verification method with ethereum address as public key information +ethereumAddress = { + "id" : did-url + "type" : "EcdsaSecp256k1RecoveryMethod2020" ; this is the only type known for ethereum address + "controller" : did + "ethereumAddress" : text .regexp "0x[a-zA-F0-9]{40,40}" ; only allows for hexadecimal representations +} + + + +;;; +; below is a partial list of properties of various well-known JWK public key types. +; as per https://github.com/w3c/did-core/issues/240, the is an active area of discussion +; additional constraints are necessary to finalize the data definition all all JWK types +; below is a partial list of properties of various well-known JWK public key types. +;;;; + + +;;; +publicKeyEC = { + "crv" : "P-256" / "P-384" / "P-521" + "kty" : "EC" + "x" : text + "y" : text + ? "kid" : text ; should be sha256 fingerprint +} + + +;;; Octet sequence key for representing secret keys +publicKeyoct = { + "kty" : "oct" + ? "kid" : text ; this should be the sha256 fingerprint + "alg" : "HS256" / "HS384" / "HS512" + "k" : text .size 32 ; for shared secret +} + + +;; Octet key pair key like ed25519 +publicKeyed25519 = { + "kty" : "OKP" + "crv" : "Ed25519" + "x" : text .size 32 + "use" : "sig" / "enc" + ? "kid" : text ; should be sha256 fingerprint +} + + + +;;; RSA key type +publicKeyRSA = { + "alg" : "RSA" + "mod" : text ; + "exp" : text ; + ? "kid" : text ; should be sha256 fingerprint +} + + +; current list of all registered Verification Method classes +; These are classes not a properties - in other words, use them for the value of type in a verification method object. +VerificationMethodTypes = + "JsonWebKey2020" / + "Ed25519VerificationKey2018" / + "EcdsaSecp256k1VerificationKey2019" / + "SchnorrSecp256k1VerificationKey2019" / + "Ed25519VerificationKey2018" / + "GpgVerificationKey2020" / + "RsaVerificationKey2018" / + "X25519KeyAgreementKey2019" / + "EcdsaSecp256k1RecoveryMethod2020" + \ No newline at end of file diff --git a/tests/fixtures/did/assertionMethod/assertionMethod_example7.json b/tests/fixtures/did/assertionMethod/assertionMethod_example7.json new file mode 100644 index 00000000..c3975462 --- /dev/null +++ b/tests/fixtures/did/assertionMethod/assertionMethod_example7.json @@ -0,0 +1,11 @@ +{ + "assertionMethod": [ + { + "id": "did:example:123#z6MkpzW2izkFjNwMBwwvKqmELaQcH8t54QL5xmBdJg9Xh1y4", + "type": "Ed25519VerificationKey2018", + "controller": "did:example:123", + "publicKeyBase58": "BYEz8kVpPqSt5T7DeGoPVUrcTZcDeX5jGkGhUQBWmoBg" + }, + "did:example:123#WjKgJV7VRw3hmgU6--4v15c0Aewbcvat1BsRFTIqa5Q" + ] +} \ No newline at end of file From cc295e70462cdf6d7e821d2f54a2b429410e87ca Mon Sep 17 00:00:00 2001 From: anweiss <2326106+anweiss@users.noreply.github.com> Date: Thu, 29 Oct 2020 13:53:21 -0400 Subject: [PATCH 02/11] regex fixes --- Cargo.toml | 3 ++- src/validator/mod.rs | 13 ++++++++++++- tests/did.rs | 16 ++++++++++++++-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 271e3c85..6bb1e918 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ codespan-reporting = "0.9" itertools = "0.9" lexical-core = "0.7" regex = { version = "1.4", default-features = false, features = ["std"] } +regex-syntax = { version = "0.6", optional = true } serde = { version = "1.0", optional = true, features = ["derive"] } serde_cbor = { version = "0.11", optional = true } serde_json = { version = "1.0", optional = true, default-features = false } @@ -50,7 +51,7 @@ wasm-bindgen-test = "0.3" [features] default = ["std"] -std = ["serde_json", "serde_cbor", "serde", "chrono", "wasm-bindgen", "clap", "crossterm", "uriparse", "base64-url"] +std = ["serde_json", "serde_cbor", "serde", "chrono", "wasm-bindgen", "clap", "crossterm", "uriparse", "base64-url", "regex-syntax"] lsp = ["std"] [[bin]] diff --git a/src/validator/mod.rs b/src/validator/mod.rs index 4fe5fc14..da58e05f 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -626,7 +626,18 @@ pub struct EntryCount { /// https://github.com/anweiss/cddl/issues/67 pub fn format_regex(input: &str) -> Option { let mut formatted_regex = String::from(input); - for replace in ["\\:", "\\%", "\\_", "\\/", "\\="].iter() { + let mut replace = Vec::new(); + for (idx, c) in formatted_regex.char_indices() { + if c == '\\' { + if let Some(c) = formatted_regex.chars().nth(idx + 1) { + if !regex_syntax::is_meta_character(c) { + replace.push(format!("\\{}", c)); + } + } + } + } + + for replace in replace.iter() { formatted_regex = formatted_regex.replace(replace, &replace.chars().nth(1).unwrap().to_string()); } diff --git a/tests/did.rs b/tests/did.rs index e5d3c48a..2337f339 100644 --- a/tests/did.rs +++ b/tests/did.rs @@ -28,7 +28,16 @@ fn validate_did_json_examples() -> Result<(), Box> { for file in fs::read_dir(entry.path())? { let file = file?; if file.path().extension().and_then(OsStr::to_str).unwrap() == "json" { - if let Err(e) = validate_json_from_str(&cddl, &fs::read_to_string(file.path())?) { + let r = validate_json_from_str(&cddl, &fs::read_to_string(file.path())?); + // Files with known validation errors + if ["assertionMethod_example7.json"].contains(&file.file_name().to_str().unwrap()) { + assert!(r.is_err()); + } else { + assert!(r.is_ok()); + } + + if let Err(e) = r { + println!("error validating {:?}\n", file.path()); println!("{}", e); } } @@ -64,7 +73,10 @@ fn validate_did_cbor_examples() -> Result<(), Box> { let mut f = File::open(file.path())?; let mut data = Vec::new(); f.read_to_end(&mut data)?; - if let Err(e) = validate_cbor_from_slice(&cddl, &data) { + let r = validate_cbor_from_slice(&cddl, &data); + + if let Err(e) = r { + println!("error validating {:?}\n", file.path()); println!("{}", e); } } From 0eec10f188d3ef055adf9a83a475496f1e9dc029 Mon Sep 17 00:00:00 2001 From: anweiss <2326106+anweiss@users.noreply.github.com> Date: Thu, 29 Oct 2020 17:22:38 -0400 Subject: [PATCH 03/11] homogenous array type choice validation fixes and additional did text fixtures --- src/validator/cbor.rs | 56 +++++++- src/validator/json.rs | 74 +++++++++-- src/validator/mod.rs | 8 +- tests/did.rs | 24 ++-- .../did/authentication/authentication.cddl | 121 ++++++++++++++++++ .../authentication_example8.cbor | 1 + .../authentication_example8.json | 11 ++ .../authentication_example8b.cbor | 1 + .../authentication_example8b.json | 10 ++ .../capabilityDelegation.cddl | 121 ++++++++++++++++++ .../capabilityDelegation_example9.cbor | 1 + .../capabilityDelegation_example9.json | 11 ++ 12 files changed, 414 insertions(+), 25 deletions(-) create mode 100644 tests/fixtures/did/authentication/authentication.cddl create mode 100644 tests/fixtures/did/authentication/authentication_example8.cbor create mode 100644 tests/fixtures/did/authentication/authentication_example8.json create mode 100644 tests/fixtures/did/authentication/authentication_example8b.cbor create mode 100644 tests/fixtures/did/authentication/authentication_example8b.json create mode 100644 tests/fixtures/did/capabilityDelegation/capabilityDelegation.cddl create mode 100644 tests/fixtures/did/capabilityDelegation/capabilityDelegation_example9.cbor create mode 100644 tests/fixtures/did/capabilityDelegation/capabilityDelegation_example9.json diff --git a/src/validator/cbor.rs b/src/validator/cbor.rs index 9e9b0acb..c94bfd25 100644 --- a/src/validator/cbor.rs +++ b/src/validator/cbor.rs @@ -474,17 +474,29 @@ impl<'a> Visitor<'a, ValidationError> for CBORValidator<'a> { } if iter_items { + let mut errors = Vec::new(); + let mut type_choice_success = false; for (idx, v) in a.iter().enumerate() { let mut cv = CBORValidator::new(self.cddl, v.clone()); cv.generic_rules = self.generic_rules.clone(); cv.eval_generic_rule = self.eval_generic_rule; + cv.ctrl = self.ctrl.clone(); cv.is_multi_type_choice = self.is_multi_type_choice; cv.cbor_location .push_str(&format!("{}/{}", self.cbor_location, idx)); cv.visit_range(lower, upper, is_inclusive)?; - self.errors.append(&mut cv.errors); + if self.is_multi_type_choice && cv.errors.is_empty() { + type_choice_success = true; + break; + } + + errors.append(&mut cv.errors); + } + + if !type_choice_success { + self.errors.append(&mut errors); } } else if let Some(idx) = self.group_entry_idx.take() { if let Some(v) = a.get(idx) { @@ -492,6 +504,7 @@ impl<'a> Visitor<'a, ValidationError> for CBORValidator<'a> { cv.generic_rules = self.generic_rules.clone(); cv.eval_generic_rule = self.eval_generic_rule; cv.is_multi_type_choice = self.is_multi_type_choice; + cv.ctrl = self.ctrl.clone(); cv.cbor_location .push_str(&format!("{}/{}", self.cbor_location, idx)); @@ -881,7 +894,7 @@ impl<'a> Visitor<'a, ValidationError> for CBORValidator<'a> { match target { Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => { match self.cbor { - Value::Text(_) => self.visit_type2(controller)?, + Value::Text(_) | Value::Array(_) => self.visit_type2(controller)?, _ => self.add_error(format!( ".regexp/.pcre control can only be matched against cbor string, got {:?}", self.cbor @@ -1000,23 +1013,36 @@ impl<'a> Visitor<'a, ValidationError> for CBORValidator<'a> { } if iter_items { + let mut errors = Vec::new(); + let mut type_choice_success = false; for (idx, v) in a.iter().enumerate() { let mut cv = CBORValidator::new(self.cddl, v.clone()); cv.generic_rules = self.generic_rules.clone(); cv.eval_generic_rule = self.eval_generic_rule; + cv.ctrl = self.ctrl.clone(); cv.is_multi_type_choice = self.is_multi_type_choice; cv.cbor_location .push_str(&format!("{}/{}", self.cbor_location, idx)); cv.visit_group(group)?; + if self.is_multi_type_choice && cv.errors.is_empty() { + type_choice_success = true; + break; + } + self.errors.append(&mut cv.errors); } + + if !type_choice_success { + self.errors.append(&mut errors); + } } else if let Some(idx) = self.group_entry_idx.take() { if let Some(v) = a.get(idx) { let mut cv = CBORValidator::new(self.cddl, v.clone()); cv.generic_rules = self.generic_rules.clone(); cv.eval_generic_rule = self.eval_generic_rule; + cv.ctrl = self.ctrl.clone(); cv.is_multi_type_choice = self.is_multi_type_choice; cv.cbor_location .push_str(&format!("{}/{}", self.cbor_location, idx)); @@ -1609,9 +1635,12 @@ impl<'a> Visitor<'a, ValidationError> for CBORValidator<'a> { } if iter_items { + let mut errors = Vec::new(); + let mut type_choice_success = false; for (idx, v) in a.iter().enumerate() { let mut cv = CBORValidator::new(self.cddl, v.clone()); cv.generic_rules = self.generic_rules.clone(); + cv.ctrl = self.ctrl.clone(); cv.eval_generic_rule = self.eval_generic_rule; cv.is_multi_type_choice = self.is_multi_type_choice; cv.cbor_location @@ -1619,14 +1648,24 @@ impl<'a> Visitor<'a, ValidationError> for CBORValidator<'a> { cv.visit_identifier(ident)?; + if self.is_multi_type_choice && cv.errors.is_empty() { + type_choice_success = true; + break; + } + self.errors.append(&mut cv.errors); } + + if !type_choice_success { + self.errors.append(&mut errors); + } } else if let Some(idx) = self.group_entry_idx.take() { if let Some(v) = a.get(idx) { let mut cv = CBORValidator::new(self.cddl, v.clone()); cv.generic_rules = self.generic_rules.clone(); cv.eval_generic_rule = self.eval_generic_rule; cv.is_multi_type_choice = self.is_multi_type_choice; + cv.ctrl = self.ctrl.clone(); cv.cbor_location .push_str(&format!("{}/{}", self.cbor_location, idx)); @@ -2214,23 +2253,36 @@ impl<'a> Visitor<'a, ValidationError> for CBORValidator<'a> { } if iter_items { + let mut errors = Vec::new(); + let mut type_choice_success = false; for (idx, v) in a.iter().enumerate() { let mut cv = CBORValidator::new(self.cddl, v.clone()); cv.generic_rules = self.generic_rules.clone(); cv.eval_generic_rule = self.eval_generic_rule; cv.is_multi_type_choice = self.is_multi_type_choice; + cv.ctrl = self.ctrl.clone(); cv.cbor_location .push_str(&format!("{}/{}", self.cbor_location, idx)); cv.visit_value(value)?; + if self.is_multi_type_choice && cv.errors.is_empty() { + type_choice_success = true; + break; + } + self.errors.append(&mut cv.errors); } + + if !type_choice_success { + self.errors.append(&mut errors); + } } else if let Some(idx) = self.group_entry_idx.take() { if let Some(v) = a.get(idx) { let mut cv = CBORValidator::new(self.cddl, v.clone()); cv.generic_rules = self.generic_rules.clone(); cv.eval_generic_rule = self.eval_generic_rule; + cv.ctrl = self.ctrl.clone(); cv.is_multi_type_choice = self.is_multi_type_choice; cv.cbor_location .push_str(&format!("{}/{}", self.cbor_location, idx)); diff --git a/src/validator/json.rs b/src/validator/json.rs index fd970d35..0e12b1d8 100644 --- a/src/validator/json.rs +++ b/src/validator/json.rs @@ -474,23 +474,36 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { } if iter_items { + let mut errors = Vec::new(); + let mut type_choice_success = false; for (idx, v) in a.iter().enumerate() { let mut jv = JSONValidator::new(self.cddl, v.clone()); jv.generic_rules = self.generic_rules.clone(); jv.eval_generic_rule = self.eval_generic_rule; + jv.ctrl = self.ctrl.clone(); jv.is_multi_type_choice = self.is_multi_type_choice; jv.json_location .push_str(&format!("{}/{}", self.json_location, idx)); jv.visit_range(lower, upper, is_inclusive)?; - self.errors.append(&mut jv.errors); + if self.is_multi_type_choice && jv.errors.is_empty() { + type_choice_success = true; + break; + } + + errors.append(&mut jv.errors); + } + + if !type_choice_success { + self.errors.append(&mut errors); } } else if let Some(idx) = self.group_entry_idx.take() { if let Some(v) = a.get(idx) { let mut jv = JSONValidator::new(self.cddl, v.clone()); jv.generic_rules = self.generic_rules.clone(); jv.eval_generic_rule = self.eval_generic_rule; + jv.ctrl = self.ctrl.clone(); jv.is_multi_type_choice = self.is_multi_type_choice; jv.json_location .push_str(&format!("{}/{}", self.json_location, idx)); @@ -901,7 +914,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { match target { Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => { match self.json { - Value::String(_) => self.visit_type2(controller)?, + Value::String(_) | Value::Array(_) => self.visit_type2(controller)?, _ => self.add_error(format!( ".regexp/.pcre control can only be matched against JSON string, got {}", self.json @@ -990,23 +1003,36 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { } if iter_items { + let mut errors = Vec::new(); + let mut type_choice_success = false; for (idx, v) in a.iter().enumerate() { let mut jv = JSONValidator::new(self.cddl, v.clone()); jv.generic_rules = self.generic_rules.clone(); jv.eval_generic_rule = self.eval_generic_rule; + jv.ctrl = self.ctrl.clone(); jv.is_multi_type_choice = self.is_multi_type_choice; jv.json_location .push_str(&format!("{}/{}", self.json_location, idx)); jv.visit_group(group)?; - self.errors.append(&mut jv.errors); + if self.is_multi_type_choice && jv.errors.is_empty() { + type_choice_success = true; + break; + } + + errors.append(&mut jv.errors); + } + + if !type_choice_success { + self.errors.append(&mut errors); } } else if let Some(idx) = self.group_entry_idx.take() { if let Some(v) = a.get(idx) { let mut jv = JSONValidator::new(self.cddl, v.clone()); jv.generic_rules = self.generic_rules.clone(); jv.eval_generic_rule = self.eval_generic_rule; + jv.ctrl = self.ctrl.clone(); jv.is_multi_type_choice = self.is_multi_type_choice; jv.json_location .push_str(&format!("{}/{}", self.json_location, idx)); @@ -1358,23 +1384,36 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { } if iter_items { + let mut errors = Vec::new(); + let mut type_choice_success = false; for (idx, v) in a.iter().enumerate() { let mut jv = JSONValidator::new(self.cddl, v.clone()); jv.generic_rules = self.generic_rules.clone(); jv.eval_generic_rule = self.eval_generic_rule; jv.is_multi_type_choice = self.is_multi_type_choice; + jv.ctrl = self.ctrl.clone(); jv.json_location .push_str(&format!("{}/{}", self.json_location, idx)); jv.visit_identifier(ident)?; - self.errors.append(&mut jv.errors); + if self.is_multi_type_choice && jv.errors.is_empty() { + type_choice_success = true; + break; + } + + errors.append(&mut jv.errors); + } + + if !type_choice_success { + self.errors.append(&mut errors); } } else if let Some(idx) = self.group_entry_idx.take() { if let Some(v) = a.get(idx) { let mut jv = JSONValidator::new(self.cddl, v.clone()); jv.generic_rules = self.generic_rules.clone(); jv.eval_generic_rule = self.eval_generic_rule; + jv.ctrl = self.ctrl.clone(); jv.is_multi_type_choice = self.is_multi_type_choice; jv.json_location .push_str(&format!("{}/{}", self.json_location, idx)); @@ -1711,17 +1750,29 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { } if iter_items { + let mut errors = Vec::new(); + let mut type_choice_success = false; for (idx, v) in a.iter().enumerate() { let mut jv = JSONValidator::new(self.cddl, v.clone()); jv.generic_rules = self.generic_rules.clone(); jv.eval_generic_rule = self.eval_generic_rule; jv.is_multi_type_choice = self.is_multi_type_choice; + jv.ctrl = self.ctrl.clone(); jv.json_location .push_str(&format!("{}/{}", self.json_location, idx)); jv.visit_value(value)?; - self.errors.append(&mut jv.errors); + if self.is_multi_type_choice && jv.errors.is_empty() { + type_choice_success = true; + break; + } + + errors.append(&mut jv.errors); + } + + if !type_choice_success { + self.errors.append(&mut errors); } } else if let Some(idx) = self.group_entry_idx.take() { if let Some(v) = a.get(idx) { @@ -1729,6 +1780,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { jv.generic_rules = self.generic_rules.clone(); jv.eval_generic_rule = self.eval_generic_rule; jv.is_multi_type_choice = self.is_multi_type_choice; + jv.ctrl = self.ctrl.clone(); jv.json_location .push_str(&format!("{}/{}", self.json_location, idx)); @@ -1806,12 +1858,12 @@ mod tests { #[test] fn validate() -> std::result::Result<(), Box> { - let cddl = r#"document = { - @context: "https://www.example.com/ns/v1" / [ "https://www.example.com/ns/v1", 1* ~uri ], - }"#; - let json = r#"{ - "@context": [ "https://www.example.com/ns/v1", "telnet://192.0.2.16:80/", "http://test.com" ] - }"#; + let cddl = r#"document = [ + 1* "https://www.example.com/ns/v1" / integer + ]"#; + let json = r#"[ + 1 + ]"#; let mut lexer = lexer_from_str(cddl); let cddl = cddl_from_str(&mut lexer, cddl, true).map_err(json::Error::CDDLParsing)?; diff --git a/src/validator/mod.rs b/src/validator/mod.rs index da58e05f..5b075048 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -626,18 +626,18 @@ pub struct EntryCount { /// https://github.com/anweiss/cddl/issues/67 pub fn format_regex(input: &str) -> Option { let mut formatted_regex = String::from(input); - let mut replace = Vec::new(); + let mut unescape = Vec::new(); for (idx, c) in formatted_regex.char_indices() { if c == '\\' { if let Some(c) = formatted_regex.chars().nth(idx + 1) { if !regex_syntax::is_meta_character(c) { - replace.push(format!("\\{}", c)); + unescape.push(format!("\\{}", c)); } } } } - for replace in replace.iter() { + for replace in unescape.iter() { formatted_regex = formatted_regex.replace(replace, &replace.chars().nth(1).unwrap().to_string()); } @@ -648,7 +648,7 @@ pub fn format_regex(input: &str) -> Option { } } - formatted_regex = formatted_regex.replace("?<", "P?<"); + formatted_regex = formatted_regex.replace("?<", "?P<"); Some(formatted_regex) } diff --git a/tests/did.rs b/tests/did.rs index 2337f339..c56c56b1 100644 --- a/tests/did.rs +++ b/tests/did.rs @@ -6,6 +6,8 @@ use std::{ io::Read, }; +static KNOWN_BAD: &'static [&'static str] = &[]; + #[test] fn validate_did_json_examples() -> Result<(), Box> { for entry in fs::read_dir("tests/fixtures/did/")? { @@ -29,17 +31,17 @@ fn validate_did_json_examples() -> Result<(), Box> { let file = file?; if file.path().extension().and_then(OsStr::to_str).unwrap() == "json" { let r = validate_json_from_str(&cddl, &fs::read_to_string(file.path())?); + if let Err(e) = &r { + println!("error validating {:?}\n", file.path()); + println!("{}", e); + } + // Files with known validation errors - if ["assertionMethod_example7.json"].contains(&file.file_name().to_str().unwrap()) { + if KNOWN_BAD.contains(&file.file_name().to_str().unwrap()) { assert!(r.is_err()); } else { assert!(r.is_ok()); } - - if let Err(e) = r { - println!("error validating {:?}\n", file.path()); - println!("{}", e); - } } } } @@ -74,11 +76,17 @@ fn validate_did_cbor_examples() -> Result<(), Box> { let mut data = Vec::new(); f.read_to_end(&mut data)?; let r = validate_cbor_from_slice(&cddl, &data); - - if let Err(e) = r { + if let Err(e) = &r { println!("error validating {:?}\n", file.path()); println!("{}", e); } + + // Files with known validation errors + if KNOWN_BAD.contains(&file.file_name().to_str().unwrap()) { + assert!(r.is_err()); + } else { + assert!(r.is_ok()); + } } } } diff --git a/tests/fixtures/did/authentication/authentication.cddl b/tests/fixtures/did/authentication/authentication.cddl new file mode 100644 index 00000000..2544a951 --- /dev/null +++ b/tests/fixtures/did/authentication/authentication.cddl @@ -0,0 +1,121 @@ +DID-document = { + + "authentication" : [ 1* publicKeyHex / ethereumAddress / publicKeyJwk / publicKeyBase58 / publicKeyGpg / did-url / relative-did-url ] + +} + +did = text .regexp "did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)" + +did-url = text .regexp "did\\:(?[a-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\/?(?[A-Za-z0-9\\/]+)\\??(?[A-Za-z0-9\\=\\&\\%\\-\\:]+)?\\#?(?[A-Za-z0-9\\-]+)?" + +;; base URI value is the DID that is associated with the DID subject +relative-did-url = text .regexp "^#.+$" ; fragment identifier of self relative DID url, i.e. #key-1 in DID document with `id` = did:example:123456789abcdefghi becomes `did:example:123456789abcdefghi#key-1` + + +;;; +; verificationMethod types included below for convenience +;;; + +publicKeyJwk = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyJwk" : publicKeyEC / publicKeyoct / publicKeyed25519 / publicKeyRSA + +} + + +;;; public key Hex is a type of verification method with public key encoded as base16 +publicKeyHex = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyHex" : text .regexp "[a-zA-F0-9]+" ; only allows for hexadecimal representations +} + +publicKeyGpg = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyGpg" : text ; This could probbaly be better to capture -----BEGIN PGP PUBLIC KEY BLOCK +} + +;;; public key Hex is a type of verification method with the public key encoded as base58 +publicKeyBase58 = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyBase58" : text .regexp "[a-zA-Z0-9]+" ; only allows for base58 representations +} + + +;;; ethereeum address is a type of verification method with ethereum address as public key information +ethereumAddress = { + "id" : did-url + "type" : "EcdsaSecp256k1RecoveryMethod2020" ; this is the only type known for ethereum address + "controller" : did + "ethereumAddress" : text .regexp "0x[a-zA-F0-9]{40,40}" ; only allows for hexadecimal representations +} + + + +;;; +; below is a partial list of properties of various well-known JWK public key types. +; as per https://github.com/w3c/did-core/issues/240, the is an active area of discussion +; additional constraints are necessary to finalize the data definition all all JWK types +; below is a partial list of properties of various well-known JWK public key types. +;;;; + + +;;; +publicKeyEC = { + "crv" : "P-256" / "P-384" / "P-521" + "kty" : "EC" + "x" : text + "y" : text + ? "kid" : text ; should be sha256 fingerprint +} + + +;;; Octet sequence key for representing secret keys +publicKeyoct = { + "kty" : "oct" + ? "kid" : text ; this should be the sha256 fingerprint + "alg" : "HS256" / "HS384" / "HS512" + "k" : text .size 32 ; for shared secret +} + + +;; Octet key pair key like ed25519 +publicKeyed25519 = { + "kty" : "OKP" + "crv" : "Ed25519" + "x" : text .size 32 + "use" : "sig" / "enc" + ? "kid" : text ; should be sha256 fingerprint +} + + + +;;; RSA key type +publicKeyRSA = { + "alg" : "RSA" + "mod" : text ; + "exp" : text ; + ? "kid" : text ; should be sha256 fingerprint +} + + +; current list of all registered Verification Method classes +; These are classes not a properties - in other words, use them for the value of type in a verification method object. +VerificationMethodTypes = + "JsonWebKey2020" / + "Ed25519VerificationKey2018" / + "EcdsaSecp256k1VerificationKey2019" / + "SchnorrSecp256k1VerificationKey2019" / + "Ed25519VerificationKey2018" / + "GpgVerificationKey2020" / + "RsaVerificationKey2018" / + "X25519KeyAgreementKey2019" / + "EcdsaSecp256k1RecoveryMethod2020" + \ No newline at end of file diff --git a/tests/fixtures/did/authentication/authentication_example8.cbor b/tests/fixtures/did/authentication/authentication_example8.cbor new file mode 100644 index 00000000..a09858e4 --- /dev/null +++ b/tests/fixtures/did/authentication/authentication_example8.cbor @@ -0,0 +1 @@ +¡nauthentication‚¤bidx@did:example:123#z6MkpzW2izkFjNwMBwwvKqmELaQcH8t54QL5xmBdJg9Xh1y4dtypexEd25519VerificationKey2018jcontrollerodid:example:123opublicKeyBase58x,BYEz8kVpPqSt5T7DeGoPVUrcTZcDeX5jGkGhUQBWmoBgx;did:example:123#WjKgJV7VRw3hmgU6--4v15c0Aewbcvat1BsRFTIqa5Q \ No newline at end of file diff --git a/tests/fixtures/did/authentication/authentication_example8.json b/tests/fixtures/did/authentication/authentication_example8.json new file mode 100644 index 00000000..9ca3d2da --- /dev/null +++ b/tests/fixtures/did/authentication/authentication_example8.json @@ -0,0 +1,11 @@ +{ + "authentication": [ + { + "id": "did:example:123#z6MkpzW2izkFjNwMBwwvKqmELaQcH8t54QL5xmBdJg9Xh1y4", + "type": "Ed25519VerificationKey2018", + "controller": "did:example:123", + "publicKeyBase58": "BYEz8kVpPqSt5T7DeGoPVUrcTZcDeX5jGkGhUQBWmoBg" + }, + "did:example:123#WjKgJV7VRw3hmgU6--4v15c0Aewbcvat1BsRFTIqa5Q" + ] +} \ No newline at end of file diff --git a/tests/fixtures/did/authentication/authentication_example8b.cbor b/tests/fixtures/did/authentication/authentication_example8b.cbor new file mode 100644 index 00000000..59375f0d --- /dev/null +++ b/tests/fixtures/did/authentication/authentication_example8b.cbor @@ -0,0 +1 @@ +¡nauthentication¤bidx%did:example:123456789abcdefghi#keys-1dtypexEd25519VerificationKey2018jcontrollerxdid:example:123456789abcdefghiopublicKeyBase58x,H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV \ No newline at end of file diff --git a/tests/fixtures/did/authentication/authentication_example8b.json b/tests/fixtures/did/authentication/authentication_example8b.json new file mode 100644 index 00000000..aeed4da6 --- /dev/null +++ b/tests/fixtures/did/authentication/authentication_example8b.json @@ -0,0 +1,10 @@ +{ + "authentication": [ + { + "id": "did:example:123456789abcdefghi#keys-1", + "type": "Ed25519VerificationKey2018", + "controller": "did:example:123456789abcdefghi", + "publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/did/capabilityDelegation/capabilityDelegation.cddl b/tests/fixtures/did/capabilityDelegation/capabilityDelegation.cddl new file mode 100644 index 00000000..f3e683c2 --- /dev/null +++ b/tests/fixtures/did/capabilityDelegation/capabilityDelegation.cddl @@ -0,0 +1,121 @@ +DID-document = { + + "capabilityDelegation" : [ 1* publicKeyHex / ethereumAddress / publicKeyJwk / publicKeyBase58 / publicKeyGpg / did-url / relative-did-url ] + +} + +did = text .regexp "did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)" + +did-url = text .regexp "did\\:(?[a-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\/?(?[A-Za-z0-9\\/]+)\\??(?[A-Za-z0-9\\=\\&\\%\\-\\:]+)?\\#?(?[A-Za-z0-9\\-]+)?" + +;; base URI value is the DID that is associated with the DID subject +relative-did-url = text .regexp "^#.+$" ; fragment identifier of self relative DID url, i.e. #key-1 in DID document with `id` = did:example:123456789abcdefghi becomes `did:example:123456789abcdefghi#key-1` + + +;;; +; verificationMethod types included below for convenience +;;; + +publicKeyJwk = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyJwk" : publicKeyEC / publicKeyoct / publicKeyed25519 / publicKeyRSA + +} + + +;;; public key Hex is a type of verification method with public key encoded as base16 +publicKeyHex = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyHex" : text .regexp "[a-zA-F0-9]+" ; only allows for hexadecimal representations +} + +publicKeyGpg = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyGpg" : text ; This could probbaly be better to capture -----BEGIN PGP PUBLIC KEY BLOCK +} + +;;; public key Hex is a type of verification method with the public key encoded as base58 +publicKeyBase58 = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyBase58" : text .regexp "[a-zA-Z0-9]+" ; only allows for base58 representations +} + + +;;; ethereeum address is a type of verification method with ethereum address as public key information +ethereumAddress = { + "id" : did-url + "type" : "EcdsaSecp256k1RecoveryMethod2020" ; this is the only type known for ethereum address + "controller" : did + "ethereumAddress" : text .regexp "0x[a-zA-F0-9]{40,40}" ; only allows for hexadecimal representations +} + + + +;;; +; below is a partial list of properties of various well-known JWK public key types. +; as per https://github.com/w3c/did-core/issues/240, the is an active area of discussion +; additional constraints are necessary to finalize the data definition all all JWK types +; below is a partial list of properties of various well-known JWK public key types. +;;;; + + +;;; +publicKeyEC = { + "crv" : "P-256" / "P-384" / "P-521" + "kty" : "EC" + "x" : text + "y" : text + ? "kid" : text ; should be sha256 fingerprint +} + + +;;; Octet sequence key for representing secret keys +publicKeyoct = { + "kty" : "oct" + ? "kid" : text ; this should be the sha256 fingerprint + "alg" : "HS256" / "HS384" / "HS512" + "k" : text .size 32 ; for shared secret +} + + +;; Octet key pair key like ed25519 +publicKeyed25519 = { + "kty" : "OKP" + "crv" : "Ed25519" + "x" : text .size 32 + "use" : "sig" / "enc" + ? "kid" : text ; should be sha256 fingerprint +} + + + +;;; RSA key type +publicKeyRSA = { + "alg" : "RSA" + "mod" : text ; + "exp" : text ; + ? "kid" : text ; should be sha256 fingerprint +} + + +; current list of all registered Verification Method classes +; These are classes not a properties - in other words, use them for the value of type in a verification method object. +VerificationMethodTypes = + "JsonWebKey2020" / + "Ed25519VerificationKey2018" / + "EcdsaSecp256k1VerificationKey2019" / + "SchnorrSecp256k1VerificationKey2019" / + "Ed25519VerificationKey2018" / + "GpgVerificationKey2020" / + "RsaVerificationKey2018" / + "X25519KeyAgreementKey2019" / + "EcdsaSecp256k1RecoveryMethod2020" + \ No newline at end of file diff --git a/tests/fixtures/did/capabilityDelegation/capabilityDelegation_example9.cbor b/tests/fixtures/did/capabilityDelegation/capabilityDelegation_example9.cbor new file mode 100644 index 00000000..9e4bd966 --- /dev/null +++ b/tests/fixtures/did/capabilityDelegation/capabilityDelegation_example9.cbor @@ -0,0 +1 @@ +¡tcapabilityDelegation‚¤bidx@did:example:123#z6MkpzW2izkFjNwMBwwvKqmELaQcH8t54QL5xmBdJg9Xh1y4dtypexEd25519VerificationKey2018jcontrollerodid:example:123opublicKeyBase58x,BYEz8kVpPqSt5T7DeGoPVUrcTZcDeX5jGkGhUQBWmoBgx;did:example:123#WjKgJV7VRw3hmgU6--4v15c0Aewbcvat1BsRFTIqa5Q \ No newline at end of file diff --git a/tests/fixtures/did/capabilityDelegation/capabilityDelegation_example9.json b/tests/fixtures/did/capabilityDelegation/capabilityDelegation_example9.json new file mode 100644 index 00000000..e714c4c5 --- /dev/null +++ b/tests/fixtures/did/capabilityDelegation/capabilityDelegation_example9.json @@ -0,0 +1,11 @@ +{ + "capabilityDelegation": [ + { + "id": "did:example:123#z6MkpzW2izkFjNwMBwwvKqmELaQcH8t54QL5xmBdJg9Xh1y4", + "type": "Ed25519VerificationKey2018", + "controller": "did:example:123", + "publicKeyBase58": "BYEz8kVpPqSt5T7DeGoPVUrcTZcDeX5jGkGhUQBWmoBg" + }, + "did:example:123#WjKgJV7VRw3hmgU6--4v15c0Aewbcvat1BsRFTIqa5Q" + ] +} \ No newline at end of file From 846818a97070c7af39fda18c9b75cd1e782b72b1 Mon Sep 17 00:00:00 2001 From: anweiss <2326106+anweiss@users.noreply.github.com> Date: Thu, 29 Oct 2020 17:25:41 -0400 Subject: [PATCH 04/11] integration test updates --- Cargo.toml | 2 +- tests/did.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6bb1e918..943f7301 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ version = "0.8.2" authors = ["Andrew Weiss "] readme = "README.md" edition = "2018" -exclude = ["cddl-lsp/**/*", "www/**/*", ".github/**/*", ".devcontainer/**/*", "pkg/**/*", ".dockerignore", "Dockerfile"] +exclude = ["cddl-lsp/**/*", "www/**/*", ".github/**/*", ".devcontainer/**/*", "pkg/**/*", ".dockerignore", "Dockerfile", "tests/**/*"] # Temporary workaround for https://github.com/rustwasm/wasm-pack/issues/886 [package.metadata.wasm-pack.profile.release] diff --git a/tests/did.rs b/tests/did.rs index c56c56b1..83c91fab 100644 --- a/tests/did.rs +++ b/tests/did.rs @@ -1,3 +1,6 @@ +#![cfg(feature = "std")] +#![cfg(not(target_arch = "wasm32"))] + use cddl::{validate_cbor_from_slice, validate_json_from_str}; use std::{ error::Error, From ffeed12d97acbfbfd03dce260fb2415b078ebe74 Mon Sep 17 00:00:00 2001 From: anweiss <2326106+anweiss@users.noreply.github.com> Date: Thu, 29 Oct 2020 17:49:06 -0400 Subject: [PATCH 05/11] non-homogenous array validation fixes --- src/validator/cbor.rs | 48 ++++++++++++++++++++++++++++++----------- src/validator/json.rs | 50 ++++++++++++++++++++++++++++++++----------- src/validator/mod.rs | 2 +- tests/cddl.rs | 11 ---------- 4 files changed, 74 insertions(+), 37 deletions(-) diff --git a/src/validator/cbor.rs b/src/validator/cbor.rs index c94bfd25..b5d982fc 100644 --- a/src/validator/cbor.rs +++ b/src/validator/cbor.rs @@ -452,22 +452,28 @@ impl<'a> Visitor<'a, ValidationError> for CBORValidator<'a> { } if !iter_items && !allow_empty_array { - if let Some(entry_counts) = self.entry_counts.take() { + if let Some(entry_counts) = &self.entry_counts { + let mut errors = Vec::new(); let len = a.len(); if !validate_entry_count(&entry_counts, len) { for ec in entry_counts.iter() { if let Some(occur) = &ec.entry_occurrence { - self.add_error(format!( + errors.push(format!( "expecting array with length per occurrence {}", occur, )); } else { - self.add_error(format!( + errors.push(format!( "expecting array with length {}, got {}", ec.count, len )); } } + + for error in errors.into_iter() { + self.add_error(error); + } + return Ok(()); } } @@ -991,22 +997,28 @@ impl<'a> Visitor<'a, ValidationError> for CBORValidator<'a> { } if !iter_items && !allow_empty_array { - if let Some(entry_counts) = self.entry_counts.take() { + if let Some(entry_counts) = &self.entry_counts { + let mut errors = Vec::new(); let len = a.len(); if !validate_entry_count(&entry_counts, len) { for ec in entry_counts.iter() { if let Some(occur) = &ec.entry_occurrence { - self.add_error(format!( + errors.push(format!( "expecting array with length per occurrence {}", occur, )); } else { - self.add_error(format!( + errors.push(format!( "expecting array with length {}, got {}", ec.count, len )); } } + + for error in errors.into_iter() { + self.add_error(error); + } + return Ok(()); } } @@ -1613,22 +1625,28 @@ impl<'a> Visitor<'a, ValidationError> for CBORValidator<'a> { } if !iter_items && !allow_empty_array { - if let Some(entry_counts) = self.entry_counts.take() { + if let Some(entry_counts) = &self.entry_counts { + let mut errors = Vec::new(); let len = a.len(); if !validate_entry_count(&entry_counts, len) { for ec in entry_counts.iter() { if let Some(occur) = &ec.entry_occurrence { - self.add_error(format!( + errors.push(format!( "expecting array with length per occurrence {}", occur, )); } else { - self.add_error(format!( + errors.push(format!( "expecting array with length {}, got {}", ec.count, len )); } } + + for error in errors.into_iter() { + self.add_error(error); + } + return Ok(()); } } @@ -2231,22 +2249,28 @@ impl<'a> Visitor<'a, ValidationError> for CBORValidator<'a> { } if !iter_items && !allow_empty_array { - if let Some(entry_counts) = self.entry_counts.take() { + if let Some(entry_counts) = &self.entry_counts { + let mut errors = Vec::new(); let len = a.len(); if !validate_entry_count(&entry_counts, len) { for ec in entry_counts.iter() { if let Some(occur) = &ec.entry_occurrence { - self.add_error(format!( + errors.push(format!( "expecting array with length per occurrence {}", occur, )); } else { - self.add_error(format!( + errors.push(format!( "expecting array with length {}, got {}", ec.count, len )); } } + + for error in errors.into_iter() { + self.add_error(error); + } + return Ok(()); } } diff --git a/src/validator/json.rs b/src/validator/json.rs index 0e12b1d8..49b8d596 100644 --- a/src/validator/json.rs +++ b/src/validator/json.rs @@ -452,22 +452,28 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { } if !iter_items && !allow_empty_array { - if let Some(entry_counts) = self.entry_counts.take() { + if let Some(entry_counts) = &self.entry_counts { + let mut errors = Vec::new(); let len = a.len(); if !validate_entry_count(&entry_counts, len) { for ec in entry_counts.iter() { if let Some(occur) = &ec.entry_occurrence { - self.add_error(format!( + errors.push(format!( "expecting array with length per occurrence {}", occur, )); } else { - self.add_error(format!( + errors.push(format!( "expecting array with length {}, got {}", ec.count, len )); } } + + for error in errors.into_iter() { + self.add_error(error); + } + return Ok(()); } } @@ -981,22 +987,28 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { } if !iter_items && !allow_empty_array { - if let Some(entry_counts) = self.entry_counts.take() { + if let Some(entry_counts) = &self.entry_counts { + let mut errors = Vec::new(); let len = a.len(); if !validate_entry_count(&entry_counts, len) { for ec in entry_counts.iter() { if let Some(occur) = &ec.entry_occurrence { - self.add_error(format!( + errors.push(format!( "expecting array with length per occurrence {}", occur, )); } else { - self.add_error(format!( + errors.push(format!( "expecting array with length {}, got {}", ec.count, len )); } } + + for error in errors.into_iter() { + self.add_error(error); + } + return Ok(()); } } @@ -1070,6 +1082,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { let count = entry_counts_from_group_choice(self.cddl, gc); entry_counts.push(count); } + self.entry_counts = Some(entry_counts); self.visit_group(group)?; self.entry_counts = None; @@ -1361,23 +1374,28 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { } if !iter_items && !allow_empty_array { - if let Some(entry_counts) = self.entry_counts.take() { + if let Some(entry_counts) = &self.entry_counts { + let mut errors = Vec::new(); let len = a.len(); if !validate_entry_count(&entry_counts, len) { for ec in entry_counts.iter() { if let Some(occur) = &ec.entry_occurrence { - self.add_error(format!( + errors.push(format!( "expecting array with length per occurrence {}", occur, )); } else { - self.add_error(format!( + errors.push(format!( "expecting array with length {}, got {}", ec.count, len )); } } + for error in errors.into_iter() { + self.add_error(error); + } + return Ok(()); } } @@ -1728,22 +1746,28 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { } if !iter_items && !allow_empty_array { - if let Some(entry_counts) = self.entry_counts.take() { + if let Some(entry_counts) = &self.entry_counts { + let mut errors = Vec::new(); let len = a.len(); if !validate_entry_count(&entry_counts, len) { for ec in entry_counts.iter() { if let Some(occur) = &ec.entry_occurrence { - self.add_error(format!( + errors.push(format!( "expecting array with length per occurrence {}", occur, )); } else { - self.add_error(format!( + errors.push(format!( "expecting array with length {}, got {}", ec.count, len )); } } + + for error in errors.into_iter() { + self.add_error(error); + } + return Ok(()); } } @@ -1862,7 +1886,7 @@ mod tests { 1* "https://www.example.com/ns/v1" / integer ]"#; let json = r#"[ - 1 + 1, 2 ]"#; let mut lexer = lexer_from_str(cddl); diff --git a/src/validator/mod.rs b/src/validator/mod.rs index 5b075048..bd5aa410 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -614,7 +614,7 @@ pub fn validate_entry_count(valid_entry_counts: &[EntryCount], num_entries: usiz } /// Entry count -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct EntryCount { /// Count pub count: u64, diff --git a/tests/cddl.rs b/tests/cddl.rs index 575e97af..90b213d8 100644 --- a/tests/cddl.rs +++ b/tests/cddl.rs @@ -32,14 +32,3 @@ fn verify_json_validation() -> json::Result { &fs::read_to_string("tests/fixtures/json/reputon.json").unwrap(), ) } - -// #[test] -// fn verify_ast_correctness() -> Result<(), Box> { -// let c = parser::cddl_from_str(std::str::from_utf8(include_bytes!( -// "data/cddl/reputon.cddl" -// ))?)?; - -// assert_eq!(c, data::reputon()); - -// Ok(()) -// } From 3ac0d4645fc9fb165207a8b107ea7983b4dfc2d5 Mon Sep 17 00:00:00 2001 From: anweiss <2326106+anweiss@users.noreply.github.com> Date: Fri, 30 Oct 2020 14:30:03 -0400 Subject: [PATCH 06/11] more did fixtures, regex escaping fix and update serde_cbor dep --- Cargo.toml | 2 +- src/validator/mod.rs | 2 +- tests/did.rs | 15 +- .../capabilityInvocation.cddl | 121 +++++++++++++ .../capabilityInvocation_example10.cbor | 1 + .../capabilityInvocation_example10.json | 10 ++ .../fixtures/did/contentType/contentType.cbor | 1 + .../fixtures/did/contentType/contentType.cddl | 3 + .../fixtures/did/contentType/contentType.json | 1 + tests/fixtures/did/context/bad_context.json | 1 + tests/fixtures/did/context/context.cddl | 3 + .../did/context/context_eample1b.json | 1 + .../did/context/context_example1.json | 1 + .../did/context/context_example1a.json | 1 + tests/fixtures/did/context/good_context.cbor | 1 + tests/fixtures/did/context/good_context.json | 1 + tests/fixtures/did/controller/controller.cddl | 6 + .../did/controller/controller_example3.cbor | 1 + .../did/controller/controller_example3.json | 3 + tests/fixtures/did/created/created.cbor | 1 + tests/fixtures/did/created/created.cddl | 3 + tests/fixtures/did/created/created.json | 3 + tests/fixtures/did/did-url/did-url.cddl | 4 + .../did/did-url/did-url_example1.json | 4 + tests/fixtures/did/did/did.cddl | 1 + tests/fixtures/did/did/did_example1.json | 1 + tests/fixtures/did/did/did_example2.json | 1 + .../did/didDocument/did-document.cddl | 167 ++++++++++++++++++ tests/fixtures/did/didDocument/example1.cbor | 1 + tests/fixtures/did/didDocument/example1.json | 6 + tests/fixtures/did/didDocument/example12.cbor | 1 + tests/fixtures/did/didDocument/example1a.cbor | 1 + tests/fixtures/did/didDocument/example2.cbor | 1 + tests/fixtures/did/didDocument/example2.json | 3 + tests/fixtures/did/didDocument/example3.cbor | 1 + tests/fixtures/did/error/error.cddl | 3 + tests/fixtures/did/error/error_example1.json | 3 + .../did/ethereumAddress/ethereumAddress.cddl | 14 ++ .../ethereumAddress_example12.cbor | 1 + .../ethereumAddress_example12.json | 6 + 40 files changed, 398 insertions(+), 3 deletions(-) create mode 100644 tests/fixtures/did/capabilityInvocation/capabilityInvocation.cddl create mode 100644 tests/fixtures/did/capabilityInvocation/capabilityInvocation_example10.cbor create mode 100644 tests/fixtures/did/capabilityInvocation/capabilityInvocation_example10.json create mode 100644 tests/fixtures/did/contentType/contentType.cbor create mode 100644 tests/fixtures/did/contentType/contentType.cddl create mode 100644 tests/fixtures/did/contentType/contentType.json create mode 100644 tests/fixtures/did/context/bad_context.json create mode 100644 tests/fixtures/did/context/context.cddl create mode 100644 tests/fixtures/did/context/context_eample1b.json create mode 100644 tests/fixtures/did/context/context_example1.json create mode 100644 tests/fixtures/did/context/context_example1a.json create mode 100644 tests/fixtures/did/context/good_context.cbor create mode 100644 tests/fixtures/did/context/good_context.json create mode 100644 tests/fixtures/did/controller/controller.cddl create mode 100644 tests/fixtures/did/controller/controller_example3.cbor create mode 100644 tests/fixtures/did/controller/controller_example3.json create mode 100644 tests/fixtures/did/created/created.cbor create mode 100644 tests/fixtures/did/created/created.cddl create mode 100644 tests/fixtures/did/created/created.json create mode 100644 tests/fixtures/did/did-url/did-url.cddl create mode 100644 tests/fixtures/did/did-url/did-url_example1.json create mode 100644 tests/fixtures/did/did/did.cddl create mode 100644 tests/fixtures/did/did/did_example1.json create mode 100644 tests/fixtures/did/did/did_example2.json create mode 100644 tests/fixtures/did/didDocument/did-document.cddl create mode 100644 tests/fixtures/did/didDocument/example1.cbor create mode 100644 tests/fixtures/did/didDocument/example1.json create mode 100644 tests/fixtures/did/didDocument/example12.cbor create mode 100644 tests/fixtures/did/didDocument/example1a.cbor create mode 100644 tests/fixtures/did/didDocument/example2.cbor create mode 100644 tests/fixtures/did/didDocument/example2.json create mode 100644 tests/fixtures/did/didDocument/example3.cbor create mode 100644 tests/fixtures/did/error/error.cddl create mode 100644 tests/fixtures/did/error/error_example1.json create mode 100644 tests/fixtures/did/ethereumAddress/ethereumAddress.cddl create mode 100644 tests/fixtures/did/ethereumAddress/ethereumAddress_example12.cbor create mode 100644 tests/fixtures/did/ethereumAddress/ethereumAddress_example12.json diff --git a/Cargo.toml b/Cargo.toml index 943f7301..14982da9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ lexical-core = "0.7" regex = { version = "1.4", default-features = false, features = ["std"] } regex-syntax = { version = "0.6", optional = true } serde = { version = "1.0", optional = true, features = ["derive"] } -serde_cbor = { version = "0.11", optional = true } +serde_cbor = { version = "0.11.1", optional = true, default-features = false, features = ["std", "tags"] } serde_json = { version = "1.0", optional = true, default-features = false } uriparse = { version = "0.6", optional = true } base64-url = { version = "1.4", optional = true } diff --git a/src/validator/mod.rs b/src/validator/mod.rs index bd5aa410..9e5c0631 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -630,7 +630,7 @@ pub fn format_regex(input: &str) -> Option { for (idx, c) in formatted_regex.char_indices() { if c == '\\' { if let Some(c) = formatted_regex.chars().nth(idx + 1) { - if !regex_syntax::is_meta_character(c) { + if !regex_syntax::is_meta_character(c) && c != 'd' { unescape.push(format!("\\{}", c)); } } diff --git a/tests/did.rs b/tests/did.rs index 83c91fab..ee6dd2f0 100644 --- a/tests/did.rs +++ b/tests/did.rs @@ -9,7 +9,20 @@ use std::{ io::Read, }; -static KNOWN_BAD: &'static [&'static str] = &[]; +static KNOWN_BAD: &'static [&'static str] = &[ + "bad_context.json", + "example1.json", + "example1.cbor", + "example2.json", + "example2.cbor", + "example3.cbor", + "example12.cbor", + "example1a.cbor", + // Awaiting reply to + // https://github.com/w3c/did-spec-registries/pull/138#issuecomment-719710486 + // to clarify validation semantics when unwrapping tags + "good_context.cbor", +]; #[test] fn validate_did_json_examples() -> Result<(), Box> { diff --git a/tests/fixtures/did/capabilityInvocation/capabilityInvocation.cddl b/tests/fixtures/did/capabilityInvocation/capabilityInvocation.cddl new file mode 100644 index 00000000..ee5cbff4 --- /dev/null +++ b/tests/fixtures/did/capabilityInvocation/capabilityInvocation.cddl @@ -0,0 +1,121 @@ +DID-document = { + + "capabilityInvocation" : [ 1* publicKeyHex / ethereumAddress / publicKeyJwk / publicKeyBase58 / publicKeyGpg / did-url / relative-did-url ] + +} + +did = text .regexp "did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)" + +did-url = text .regexp "did\\:(?[a-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\/?(?[A-Za-z0-9\\/]+)\\??(?[A-Za-z0-9\\=\\&\\%\\-\\:]+)?\\#?(?[A-Za-z0-9\\-]+)?" + +;; base URI value is the DID that is associated with the DID subject +relative-did-url = text .regexp "^#.+$" ; fragment identifier of self relative DID url, i.e. #key-1 in DID document with `id` = did:example:123456789abcdefghi becomes `did:example:123456789abcdefghi#key-1` + + +;;; +; verificationMethod types included below for convenience +;;; + +publicKeyJwk = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyJwk" : publicKeyEC / publicKeyoct / publicKeyed25519 / publicKeyRSA + +} + + +;;; public key Hex is a type of verification method with public key encoded as base16 +publicKeyHex = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyHex" : text .regexp "[a-zA-F0-9]+" ; only allows for hexadecimal representations +} + +publicKeyGpg = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyGpg" : text ; This could probbaly be better to capture -----BEGIN PGP PUBLIC KEY BLOCK +} + +;;; public key Hex is a type of verification method with the public key encoded as base58 +publicKeyBase58 = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyBase58" : text .regexp "[a-zA-Z0-9]+" ; only allows for base58 representations +} + + +;;; ethereeum address is a type of verification method with ethereum address as public key information +ethereumAddress = { + "id" : did-url + "type" : "EcdsaSecp256k1RecoveryMethod2020" ; this is the only type known for ethereum address + "controller" : did + "ethereumAddress" : text .regexp "0x[a-zA-F0-9]{40,40}" ; only allows for hexadecimal representations +} + + + +;;; +; below is a partial list of properties of various well-known JWK public key types. +; as per https://github.com/w3c/did-core/issues/240, the is an active area of discussion +; additional constraints are necessary to finalize the data definition all all JWK types +; below is a partial list of properties of various well-known JWK public key types. +;;;; + + +;;; +publicKeyEC = { + "crv" : "P-256" / "P-384" / "P-521" + "kty" : "EC" + "x" : text + "y" : text + ? "kid" : text ; should be sha256 fingerprint +} + + +;;; Octet sequence key for representing secret keys +publicKeyoct = { + "kty" : "oct" + ? "kid" : text ; this should be the sha256 fingerprint + "alg" : "HS256" / "HS384" / "HS512" + "k" : text .size 32 ; for shared secret +} + + +;; Octet key pair key like ed25519 +publicKeyed25519 = { + "kty" : "OKP" + "crv" : "Ed25519" + "x" : text .size 32 + "use" : "sig" / "enc" + ? "kid" : text ; should be sha256 fingerprint +} + + + +;;; RSA key type +publicKeyRSA = { + "alg" : "RSA" + "mod" : text ; + "exp" : text ; + ? "kid" : text ; should be sha256 fingerprint +} + + +; current list of all registered Verification Method classes +; These are classes not a properties - in other words, use them for the value of type in a verification method object. +VerificationMethodTypes = + "JsonWebKey2020" / + "Ed25519VerificationKey2018" / + "EcdsaSecp256k1VerificationKey2019" / + "SchnorrSecp256k1VerificationKey2019" / + "Ed25519VerificationKey2018" / + "GpgVerificationKey2020" / + "RsaVerificationKey2018" / + "X25519KeyAgreementKey2019" / + "EcdsaSecp256k1RecoveryMethod2020" + \ No newline at end of file diff --git a/tests/fixtures/did/capabilityInvocation/capabilityInvocation_example10.cbor b/tests/fixtures/did/capabilityInvocation/capabilityInvocation_example10.cbor new file mode 100644 index 00000000..a7e21347 --- /dev/null +++ b/tests/fixtures/did/capabilityInvocation/capabilityInvocation_example10.cbor @@ -0,0 +1 @@ +¡tcapabilityInvocation‚¤bidx@did:example:123#z6MkpzW2izkFjNwMBwwvKqmELaQcH8t54QL5xmBdJg9Xh1y4dtypexEd25519VerificationKey2018jcontrollerodid:example:123opublicKeyBase58x,BYEz8kVpPqSt5T7DeGoPVUrcTZcDeX5jGkGhUQBWmoBgx;did:example:123#WjKgJV7VRw3hmgU6--4v15c0Aewbcvat1BsRFTIqa5Q \ No newline at end of file diff --git a/tests/fixtures/did/capabilityInvocation/capabilityInvocation_example10.json b/tests/fixtures/did/capabilityInvocation/capabilityInvocation_example10.json new file mode 100644 index 00000000..e2dc1de3 --- /dev/null +++ b/tests/fixtures/did/capabilityInvocation/capabilityInvocation_example10.json @@ -0,0 +1,10 @@ +{ + "capabilityInvocation": [{ + "id": "did:example:123#z6MkpzW2izkFjNwMBwwvKqmELaQcH8t54QL5xmBdJg9Xh1y4", + "type": "Ed25519VerificationKey2018", + "controller": "did:example:123", + "publicKeyBase58": "BYEz8kVpPqSt5T7DeGoPVUrcTZcDeX5jGkGhUQBWmoBg" + }, + "did:example:123#WjKgJV7VRw3hmgU6--4v15c0Aewbcvat1BsRFTIqa5Q" + ] +} \ No newline at end of file diff --git a/tests/fixtures/did/contentType/contentType.cbor b/tests/fixtures/did/contentType/contentType.cbor new file mode 100644 index 00000000..e355c532 --- /dev/null +++ b/tests/fixtures/did/contentType/contentType.cbor @@ -0,0 +1 @@ +¡lcontent-typewapplication/did+ld+json \ No newline at end of file diff --git a/tests/fixtures/did/contentType/contentType.cddl b/tests/fixtures/did/contentType/contentType.cddl new file mode 100644 index 00000000..be45d5d8 --- /dev/null +++ b/tests/fixtures/did/contentType/contentType.cddl @@ -0,0 +1,3 @@ +did-document-resolution-response = { + "content-type": "application/did+json" / "application/did+ld+json" / "application/did+cbor" / "application/did+dag+cbor" +} \ No newline at end of file diff --git a/tests/fixtures/did/contentType/contentType.json b/tests/fixtures/did/contentType/contentType.json new file mode 100644 index 00000000..86cf6555 --- /dev/null +++ b/tests/fixtures/did/contentType/contentType.json @@ -0,0 +1 @@ +{ "content-type" : "application/did+ld+json" } diff --git a/tests/fixtures/did/context/bad_context.json b/tests/fixtures/did/context/bad_context.json new file mode 100644 index 00000000..80f2229c --- /dev/null +++ b/tests/fixtures/did/context/bad_context.json @@ -0,0 +1 @@ +{ "@context" : "http://example.com" } diff --git a/tests/fixtures/did/context/context.cddl b/tests/fixtures/did/context/context.cddl new file mode 100644 index 00000000..7c4e7bfd --- /dev/null +++ b/tests/fixtures/did/context/context.cddl @@ -0,0 +1,3 @@ +DID-document = { + @context : "https://www.w3.org/ns/did/v1" / ["https://www.w3.org/ns/did/v1", 1*~uri ] +} diff --git a/tests/fixtures/did/context/context_eample1b.json b/tests/fixtures/did/context/context_eample1b.json new file mode 100644 index 00000000..0ed6b0a3 --- /dev/null +++ b/tests/fixtures/did/context/context_eample1b.json @@ -0,0 +1 @@ +{"@context" : [ "https://www.w3.org/ns/did/v1" , "https://w3id.org/security/v1", "https://example.com/v2" ] } diff --git a/tests/fixtures/did/context/context_example1.json b/tests/fixtures/did/context/context_example1.json new file mode 100644 index 00000000..c63f14ac --- /dev/null +++ b/tests/fixtures/did/context/context_example1.json @@ -0,0 +1 @@ +{"@context" : "https://www.w3.org/ns/did/v1"} diff --git a/tests/fixtures/did/context/context_example1a.json b/tests/fixtures/did/context/context_example1a.json new file mode 100644 index 00000000..1d02f2aa --- /dev/null +++ b/tests/fixtures/did/context/context_example1a.json @@ -0,0 +1 @@ +{"@context" : [ "https://www.w3.org/ns/did/v1" , "https://w3id.org/security/v1" ] } diff --git a/tests/fixtures/did/context/good_context.cbor b/tests/fixtures/did/context/good_context.cbor new file mode 100644 index 00000000..1c12e30d --- /dev/null +++ b/tests/fixtures/did/context/good_context.cbor @@ -0,0 +1 @@ +¡h@context‚xhttps://www.w3.org/ns/did/v1rhttp://example.com \ No newline at end of file diff --git a/tests/fixtures/did/context/good_context.json b/tests/fixtures/did/context/good_context.json new file mode 100644 index 00000000..26602163 --- /dev/null +++ b/tests/fixtures/did/context/good_context.json @@ -0,0 +1 @@ +{ "@context" : ["https://www.w3.org/ns/did/v1" , "http://example.com" ] } diff --git a/tests/fixtures/did/controller/controller.cddl b/tests/fixtures/did/controller/controller.cddl new file mode 100644 index 00000000..8ef663dc --- /dev/null +++ b/tests/fixtures/did/controller/controller.cddl @@ -0,0 +1,6 @@ +DID-document = { + "controller" : did / [1*did] +} + + +did = text .regexp "^did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)$" \ No newline at end of file diff --git a/tests/fixtures/did/controller/controller_example3.cbor b/tests/fixtures/did/controller/controller_example3.cbor new file mode 100644 index 00000000..e44e6a34 --- /dev/null +++ b/tests/fixtures/did/controller/controller_example3.cbor @@ -0,0 +1 @@ +¡jcontrollerodid:example:123 \ No newline at end of file diff --git a/tests/fixtures/did/controller/controller_example3.json b/tests/fixtures/did/controller/controller_example3.json new file mode 100644 index 00000000..075adca9 --- /dev/null +++ b/tests/fixtures/did/controller/controller_example3.json @@ -0,0 +1,3 @@ +{ + "controller": "did:example:123" +} diff --git a/tests/fixtures/did/created/created.cbor b/tests/fixtures/did/created/created.cbor new file mode 100644 index 00000000..81fbedab --- /dev/null +++ b/tests/fixtures/did/created/created.cbor @@ -0,0 +1 @@ +¡gcreatedt2002-10-10T17:00:00Z \ No newline at end of file diff --git a/tests/fixtures/did/created/created.cddl b/tests/fixtures/did/created/created.cddl new file mode 100644 index 00000000..94da4a62 --- /dev/null +++ b/tests/fixtures/did/created/created.cddl @@ -0,0 +1,3 @@ +did-document-metadata = { + "created" : text .regexp "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$" +} diff --git a/tests/fixtures/did/created/created.json b/tests/fixtures/did/created/created.json new file mode 100644 index 00000000..181c8117 --- /dev/null +++ b/tests/fixtures/did/created/created.json @@ -0,0 +1,3 @@ +{ + "created": "2002-10-10T17:00:00Z" +} diff --git a/tests/fixtures/did/did-url/did-url.cddl b/tests/fixtures/did/did-url/did-url.cddl new file mode 100644 index 00000000..0a38caa8 --- /dev/null +++ b/tests/fixtures/did/did-url/did-url.cddl @@ -0,0 +1,4 @@ +; +did-url = text .regexp "^did\\:(?[a-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\/?(?[A-Za-z0-9\\/]+)\\??(?[A-Za-z0-9\\=\\&\\%\\-\\:]+)?\\#?(?[A-Za-z0-9\\-\\_]+)?" + + diff --git a/tests/fixtures/did/did-url/did-url_example1.json b/tests/fixtures/did/did-url/did-url_example1.json new file mode 100644 index 00000000..29836259 --- /dev/null +++ b/tests/fixtures/did/did-url/did-url_example1.json @@ -0,0 +1,4 @@ +"did:example:123?service=files&relative-ref=%2Fmyresume%2Fdoc%3Fversion%3Dlatest%23intro&foo=bar&version-time=2016-10-17T02:41:00Z#id-1" + + + diff --git a/tests/fixtures/did/did/did.cddl b/tests/fixtures/did/did/did.cddl new file mode 100644 index 00000000..28380973 --- /dev/null +++ b/tests/fixtures/did/did/did.cddl @@ -0,0 +1 @@ +did = text .regexp "^did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)$" \ No newline at end of file diff --git a/tests/fixtures/did/did/did_example1.json b/tests/fixtures/did/did/did_example1.json new file mode 100644 index 00000000..b7e79349 --- /dev/null +++ b/tests/fixtures/did/did/did_example1.json @@ -0,0 +1 @@ +"did:example:abc123" diff --git a/tests/fixtures/did/did/did_example2.json b/tests/fixtures/did/did/did_example2.json new file mode 100644 index 00000000..f642ff2a --- /dev/null +++ b/tests/fixtures/did/did/did_example2.json @@ -0,0 +1 @@ +"did:example:lajlajfaifjelfjlieahflahf" diff --git a/tests/fixtures/did/didDocument/did-document.cddl b/tests/fixtures/did/didDocument/did-document.cddl new file mode 100644 index 00000000..415dbed4 --- /dev/null +++ b/tests/fixtures/did/didDocument/did-document.cddl @@ -0,0 +1,167 @@ +did-document = { + ? @context : "https://www.w3.org/ns/did/v1" / ["https://www.w3.org/ns/did/v1", 1*~uri ] + "id" : did + "controller" : did / [1*did] + ? "verificationMethod" : [ 1* verificationMethod ] ; A public key can be used as a verification method. + ? "publicKey" : [ 1* verificationMethod ] ; A public key can be used as a verification method. + ? "authentication" : [ 1* verificationMethod ] ; The associated value MUST be an ordered set of one or more verification methods. Each verification method MAY be embedded or referenced. + ? "assertionMethod" : [1* verificationMethod] ; The associated value MUST be an ordered set of one or more verification methods. Each verification method MAY be embedded or referenced. + ? "keyAgreement" : [1* verificationMethod] ; The associated value MUST be an ordered set of one or more verification methods. Each verification method MAY be embedded or referenced. + ? "capabilityDelegation" : [1* verificationMethod] ; The associated value MUST be an ordered set of one or more verification methods. Each verification method MAY be embedded or referenced. + ? "capabilityInvocation" : [1* verificationMethod] ; The associated value MUST be an ordered set of one or more verification methods. Each verification method MAY be embedded or referenced. + ? "service" : [1* serviceEndpoint ] ; The optional service parameter describes one or more service endpoints. + } + + +;; DID syntax according to did-core specification, see: https://www.w3.org/TR/did-core/#did-syntax. method-name must be registered and match method-name below +did = text .regexp "^did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)$" + + +;; DID url syntax including path, query and fragment +did-url = text .regexp "^did\\:(?[a-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\/?(?[A-Za-z0-9\\/]+)\\??(?[A-Za-z0-9\\=\\&\\%\\-\\:]+)?\\#?(?[A-Za-z0-9\\-\\_]+)?" + + +;; base URI value is the DID that is associated with the DID subject +relative-did-url = text .regexp "^#.+$" ; fragment identifier of self relative DID url, i.e. #key-1 in DID document with `id` = did:example:123456789abcdefghi becomes `did:example:123456789abcdefghi#key-1` + + +;; The associated value MUST be an ordered set of one or more verification methods. Each verification method MAY be embedded or referenced. +verificationMethod = publicKeyHex / ethereumAddress / publicKeyPem / publicKeyJwk / publicKeyMultiformat / publicKeyBase58 / did-url / relative-did-url + + +;; this is the list of all registered DID methods including `example` for testing +;; did methods are limited to method-name = 1*method-char +method-name = + "abt" / "btcr" / "stack" / "erc725" / "example" / "ipid" / + "life" / "sov" / "uport" / "ethr" / "v1" / "com" / "dom" / + "ont" / "vvo" / "aergo" / "icon" / "is" / "iwt" / "ockam" / + "ala" / "op" / "jlinc" / "ion" / "jolo" / "bryk" / "peer" / + "selfkey" / "meta" / "tys" / "git" / "tangle" / "emtrust" / + "ttm" / "wlk" / "pistis" / "holo" / "web" / "io" / "vaultie" / + "moac" / "omn" / "work" / "vid" / "ccp" / "jnctn" / "evan" / + "elastos" / "kilt" / "elem" / "github" / "bid" / "ptn" / "echo" / + "trustbloc" / "san" / "gatc" / "factom" / "signor" / "hedera" / + "sirius" / "dock" / "twit" / "near" / "vaa" / "bba" / "morpheus" / + "etho" / "bnb" / "celo" / "klay" / "trx" / "grg" / "schema" / "key" + + + +;;; +; verificationMethod types +;;; + +publicKeyJwk = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyJwk" : publicKeyEC / publicKeyoct / publicKeyed25519 / publicKeyRSA + +} + + +;;; public key Hex is a type of verification method with public key encoded as base16 +publicKeyHex = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyHex" : text .regexp "[a-zA-F0-9]+" ; only allows for hexadecimal representations +} + + +publicKeyGpg = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyGpg" : text ; This could probbaly be better to capture -----BEGIN PGP PUBLIC KEY BLOCK +} + +;;; public key Hex is a type of verification method with the public key encoded as base58 +publicKeyBase58 = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyBase58" : text .regexp "[a-zA-Z0-9]+" ; only allows for base58 representations +} + + +;;; ethereeum address is a type of verification method with ethereum address as public key information +ethereumAddress = { + "id" : did-url + "type" : "EcdsaSecp256k1RecoveryMethod2020" ; this is the only type known for ethereum address + "controller" : did + "ethereumAddress" : text .regexp "0x[a-zA-F0-9]{40,40}" ; only allows for hexadecimal representations +} + + + +;;; +; below is a partial list of properties of various well-known JWK public key types. +; as per https://github.com/w3c/did-core/issues/240, the is an active area of discussion +; additional constraints are necessary to finalize the data definition all all JWK types +; below is a partial list of properties of various well-known JWK public key types. +;;;; + + +;;; +publicKeyEC = { + "crv" : "P-256" / "P-384" / "P-521" + "kty" : "EC" + "x" : text + "y" : text + ? "kid" : text ; should be sha256 fingerprint +} + + +;;; Octet sequence key for representing secret keys +publicKeyoct = { + "kty" : "oct" + ? "kid" : text ; this should be the sha256 fingerprint + "alg" : "HS256" / "HS384" / "HS512" + "k" : text .size 32 ; for shared secret +} + + +;; Octet key pair key like ed25519 +publicKeyed25519 = { + "kty" : "OKP" + "crv" : "Ed25519" + "x" : text .size 32 + "use" : "sig" / "enc" + ? "kid" : text ; should be sha256 fingerprint +} + + + + + +;;; RSA key type +publicKeyRSA = { + "alg" : "RSA" + "mod" : text ; + "exp" : text ; + ? "kid" : text ; should be sha256 fingerprint +} + + +; current list of all registered Verification Method classes +; These are classes not a properties - in other words, use them for the value of type in a verification method object. +VerificationMethodTypes = + "JsonWebKey2020" / + "Ed25519VerificationKey2018" / + "EcdsaSecp256k1VerificationKey2019" / + "SchnorrSecp256k1VerificationKey2019" / + "Ed25519VerificationKey2018" / + "GpgVerificationKey2020" / + "RsaVerificationKey2018" / + "X25519KeyAgreementKey2019" / + "EcdsaSecp256k1RecoveryMethod2020" + + + +serviceEndpoint = { + ? "@content" : ~uri + "id" : did-url + "type" : text + ? "description" : text + "serviceEndpoint" : ~uri +} \ No newline at end of file diff --git a/tests/fixtures/did/didDocument/example1.cbor b/tests/fixtures/did/didDocument/example1.cbor new file mode 100644 index 00000000..a8d8dfc0 --- /dev/null +++ b/tests/fixtures/did/didDocument/example1.cbor @@ -0,0 +1 @@ +¡h@context‚xhttps://www.w3.org/ns/did/v1x*https://example.com/blockchain-identity/v1 \ No newline at end of file diff --git a/tests/fixtures/did/didDocument/example1.json b/tests/fixtures/did/didDocument/example1.json new file mode 100644 index 00000000..4291581d --- /dev/null +++ b/tests/fixtures/did/didDocument/example1.json @@ -0,0 +1,6 @@ +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://example.com/blockchain-identity/v1" + ] +} \ No newline at end of file diff --git a/tests/fixtures/did/didDocument/example12.cbor b/tests/fixtures/did/didDocument/example12.cbor new file mode 100644 index 00000000..c8671476 --- /dev/null +++ b/tests/fixtures/did/didDocument/example12.cbor @@ -0,0 +1 @@ +¡rverificationMethod£bidtdid:example:123#vm-3dtypex EcdsaSecp256k1RecoveryMethod2020jcontrollerodid:example:123 \ No newline at end of file diff --git a/tests/fixtures/did/didDocument/example1a.cbor b/tests/fixtures/did/didDocument/example1a.cbor new file mode 100644 index 00000000..7bec35a1 --- /dev/null +++ b/tests/fixtures/did/didDocument/example1a.cbor @@ -0,0 +1 @@ +¡i@context2xhttps://www.w3.org/ns/did/v1 \ No newline at end of file diff --git a/tests/fixtures/did/didDocument/example2.cbor b/tests/fixtures/did/didDocument/example2.cbor new file mode 100644 index 00000000..79bde230 --- /dev/null +++ b/tests/fixtures/did/didDocument/example2.cbor @@ -0,0 +1 @@ +¡bidodid:example:123 \ No newline at end of file diff --git a/tests/fixtures/did/didDocument/example2.json b/tests/fixtures/did/didDocument/example2.json new file mode 100644 index 00000000..5d57da91 --- /dev/null +++ b/tests/fixtures/did/didDocument/example2.json @@ -0,0 +1,3 @@ +{ + "id": "did:example:123" +} \ No newline at end of file diff --git a/tests/fixtures/did/didDocument/example3.cbor b/tests/fixtures/did/didDocument/example3.cbor new file mode 100644 index 00000000..e44e6a34 --- /dev/null +++ b/tests/fixtures/did/didDocument/example3.cbor @@ -0,0 +1 @@ +¡jcontrollerodid:example:123 \ No newline at end of file diff --git a/tests/fixtures/did/error/error.cddl b/tests/fixtures/did/error/error.cddl new file mode 100644 index 00000000..505310a1 --- /dev/null +++ b/tests/fixtures/did/error/error.cddl @@ -0,0 +1,3 @@ +did-document-resolution-response = { + "error": text ; metadata property response codes not yet determined +} \ No newline at end of file diff --git a/tests/fixtures/did/error/error_example1.json b/tests/fixtures/did/error/error_example1.json new file mode 100644 index 00000000..ca527afa --- /dev/null +++ b/tests/fixtures/did/error/error_example1.json @@ -0,0 +1,3 @@ +{ + "error": "not-found" +} diff --git a/tests/fixtures/did/ethereumAddress/ethereumAddress.cddl b/tests/fixtures/did/ethereumAddress/ethereumAddress.cddl new file mode 100644 index 00000000..81011a0d --- /dev/null +++ b/tests/fixtures/did/ethereumAddress/ethereumAddress.cddl @@ -0,0 +1,14 @@ +verificationMethod = { + "id" : did-url + "controller" : did + "type" : "EcdsaSecp256k1RecoveryMethod2020" ; this is the only type known for ethereum address + "ethereumAddress" : text .regexp "0x[a-zA-F0-9]{40,40}" ; only allows for hexadecimal representations +} + + +did = text .regexp "^did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)$" + +did-url = text .regexp "^did\\:(?[a-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\/?(?[A-Za-z0-9\\/]+)\\??(?[A-Za-z0-9\\=\\&\\%\\-\\:]+)?\\#?(?[A-Za-z0-9\\-\\_]+)?" + + + diff --git a/tests/fixtures/did/ethereumAddress/ethereumAddress_example12.cbor b/tests/fixtures/did/ethereumAddress/ethereumAddress_example12.cbor new file mode 100644 index 00000000..c41fee25 --- /dev/null +++ b/tests/fixtures/did/ethereumAddress/ethereumAddress_example12.cbor @@ -0,0 +1 @@ +¤bidtdid:example:123#vm-3dtypex EcdsaSecp256k1RecoveryMethod2020jcontrollerodid:example:123oethereumAddressx*0xF3beAC30C498D9E26865F34fCAa57dBB935b0D74 \ No newline at end of file diff --git a/tests/fixtures/did/ethereumAddress/ethereumAddress_example12.json b/tests/fixtures/did/ethereumAddress/ethereumAddress_example12.json new file mode 100644 index 00000000..83ccb1f3 --- /dev/null +++ b/tests/fixtures/did/ethereumAddress/ethereumAddress_example12.json @@ -0,0 +1,6 @@ +{ + "id": "did:example:123#vm-3", + "controller": "did:example:123", + "type": "EcdsaSecp256k1RecoveryMethod2020", + "ethereumAddress": "0xF3beAC30C498D9E26865F34fCAa57dBB935b0D74" + } \ No newline at end of file From ed914153848186b4e1145686ef0384304c1f57cd Mon Sep 17 00:00:00 2001 From: anweiss <2326106+anweiss@users.noreply.github.com> Date: Fri, 30 Oct 2020 14:38:10 -0400 Subject: [PATCH 07/11] readme updates --- README.md | 2 +- src/lib.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 304d1813..18d00dab 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,7 @@ Below is the table of supported control operators: | `.ne` | âœ”ï¸ | | `.default` | âœ”ï¸ | -1: When groups with multiple group entries are used to validate arrays, those entries with occurrence indicators are ignored due to complexities involved with processing these ambiguities. For proper JSON validation, avoid writing CDDL that looks like the following: `[ * a: int, b: tstr, ? c: int ]`. +1: When groups with multiple group entries are used to validate arrays, occurrence indicators are "greedy" in that only the first occurrence indicator that is come across is used in the validation. Subsequent entries with occurrence indicators are ignored due to complexities involved with processing these ambiguities. For proper JSON validation, avoid writing CDDL that looks like the following: `[ * a: int, b: tstr, ? c: int ]`. 2: While JSON itself does not distinguish between integers and floating-point numbers, this crate does provide the ability to validate numbers against a more specific numerical CBOR type, provided that its equivalent representation is allowed by JSON. Refer to [Appendix E.](https://tools.ietf.org/html/rfc8610#appendix-E) of the standard for more details on the implications of using CDDL with JSON numbers. diff --git a/src/lib.rs b/src/lib.rs index 9571be72..79a62b28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -282,7 +282,9 @@ //! | `.default` | âœ”ï¸ | //! //! 1: When groups with multiple group entries are used to -//! validate arrays, those entries with occurrence indicators are ignored due to +//! validate arrays, occurrence indicators are "greedy" in that only the first +//! occurrence indicator that is come across is used in the validation. +//! Subsequent entries with occurrence indicators are ignored due to //! complexities involved with processing these ambiguities. For proper JSON //! validation, avoid writing CDDL that looks like the following: `[ * a: int, //! b: tstr, ? c: int ]`. From a67dc50efc02bc0526a44953ee15515c44296652 Mon Sep 17 00:00:00 2001 From: anweiss <2326106+anweiss@users.noreply.github.com> Date: Fri, 30 Oct 2020 14:39:52 -0400 Subject: [PATCH 08/11] readme updates --- README.md | 2 ++ src/lib.rs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/README.md b/README.md index 18d00dab..eadd4200 100644 --- a/README.md +++ b/README.md @@ -265,6 +265,8 @@ The following tags are supported when validating CBOR: | `mime-message = #6.36(tstr)` | âœ”ï¸ | | `cbor-any = #6.55799(any)` | âœ”ï¸ | +The `.bits`, `.cbor` and `.cborseq` control operators are not yet implemented. + ## `no_std` support Only the lexer and parser can be used in a `no_std` context provided that a heap allocator is available. This can be enabled by opting out of the default features in your `Cargo.toml` file as follows: diff --git a/src/lib.rs b/src/lib.rs index 79a62b28..b2893198 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -361,6 +361,9 @@ //! | `mime-message = #6.36(tstr)` | âœ”ï¸ | //! | `cbor-any = #6.55799(any)` | âœ”ï¸ | //! +//! The `.bits`, `.cbor` and `.cborseq` control operators are not yet +//! implemented. +//! //! ## `no_std` support //! //! Only the lexer and parser can be used in a `no_std` context provided that a From b12ef17b8e973aeb0691f30fc5eedf9e79979c36 Mon Sep 17 00:00:00 2001 From: anweiss <2326106+anweiss@users.noreply.github.com> Date: Mon, 2 Nov 2020 10:46:02 -0500 Subject: [PATCH 09/11] fix tag unwrapping semantics --- README.md | 30 +++++++++++++++--------------- src/lib.rs | 30 +++++++++++++++--------------- src/validator/cbor.rs | 7 ++++++- src/validator/json.rs | 10 +++++++--- tests/did.rs | 8 ++++---- 5 files changed, 47 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index eadd4200..3027c1e4 100644 --- a/README.md +++ b/README.md @@ -178,21 +178,21 @@ The first non-group rule defined by a CDDL data structure definition determines The following types and features of CDDL are supported by this crate for validating JSON: -| CDDL | JSON | -| ---------------------- | ---------------------------------------------------------------------------------------------------------- | -| structs | objects | -| arrays | arrays[1](#arrays) | -| `text / tstr` | string | -| `uri` | string (valid RFC3986 URI) | -| `tdate` | string (valid RFC3339 date/time) | -| `b64url` | string (base64url-encoded) | -| `time` | number (valid UNIX timestamp integer in seconds) | -| `number / int / float` | number[2](#number) | -| `bool / true / false` | boolean | -| `null / nil` | null | -| `any` | any valid JSON | -| byte strings | not yet implemented | -| unwrap (`~`) | any JSON that matches unwrapped type from map, array or supported tag (`uri`, `tdate`, `b64url` or `time`) | +| CDDL | JSON | +| ---------------------- | ----------------------------------------------------------- | +| structs | objects | +| arrays | arrays[1](#arrays) | +| `text / tstr` | string | +| `uri` | string (valid RFC3986 URI) | +| `tdate` | string (valid RFC3339 date/time) | +| `b64url` | string (base64url-encoded) | +| `time` | number (valid UNIX timestamp integer in seconds) | +| `number / int / float` | number[2](#number) | +| `bool / true / false` | boolean | +| `null / nil` | null | +| `any` | any valid JSON | +| byte strings | not yet implemented | +| unwrap (`~`) | any JSON that matches unwrapped type from map, array or tag | CDDL groups, generics, sockets/plugs and group-to-choice enumerations can all be used when validating JSON. diff --git a/src/lib.rs b/src/lib.rs index b2893198..c060e0d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -231,21 +231,21 @@ //! The following types and features of CDDL are supported by this crate for //! validating JSON: //! -//! | CDDL | JSON | -//! | ---------------------- | ---------------------------------------------------------------------------------------------------------- | -//! | structs | objects | -//! | arrays | arrays[1](#arrays) | -//! | `text / tstr` | string | -//! | `uri` | string (valid RFC3986 URI) | -//! | `tdate` | string (valid RFC3339 date/time) | -//! | `b64url` | string (base64url-encoded) | -//! | `time` | number (valid UNIX timestamp integer in seconds) | -//! | `number / int / float` | number[2](#number) | -//! | `bool / true / false` | boolean | -//! | `null / nil` | null | -//! | `any` | any valid JSON | -//! | byte strings | not yet implemented | -//! | unwrap (`~`) | any JSON that matches unwrapped type from map, array or supported tag (`uri`, `tdate`, `b64url` or `time`) | +//! | CDDL | JSON | +//! | ---------------------- | ----------------------------------------------------------- | +//! | structs | objects | +//! | arrays | arrays[1](#arrays) | +//! | `text / tstr` | string | +//! | `uri` | string (valid RFC3986 URI) | +//! | `tdate` | string (valid RFC3339 date/time) | +//! | `b64url` | string (base64url-encoded) | +//! | `time` | number (valid UNIX timestamp integer in seconds) | +//! | `number / int / float` | number[2](#number) | +//! | `bool / true / false` | boolean | +//! | `null / nil` | null | +//! | `any` | any valid JSON | +//! | byte strings | not yet implemented | +//! | unwrap (`~`) | any JSON that matches unwrapped type from map, array or tag | //! //! CDDL groups, generics, sockets/plugs and group-to-choice enumerations can //! all be used when validating JSON. diff --git a/src/validator/cbor.rs b/src/validator/cbor.rs index b5d982fc..6ab86453 100644 --- a/src/validator/cbor.rs +++ b/src/validator/cbor.rs @@ -1248,7 +1248,12 @@ impl<'a> Visitor<'a, ValidationError> for CBORValidator<'a> { .. } => { if let Some(tag) = tag_from_token(&lookup_ident(ident.ident)) { - return self.visit_type2(&tag); + // Per + // https://github.com/w3c/did-spec-registries/pull/138#issuecomment-719739215, + // strip tag and validate underlying type + if let Type2::TaggedData { t, .. } = tag { + return self.visit_type(&t); + } } if let Some(ga) = generic_args { diff --git a/src/validator/json.rs b/src/validator/json.rs index 49b8d596..e1dc18bc 100644 --- a/src/validator/json.rs +++ b/src/validator/json.rs @@ -1195,9 +1195,13 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { generic_args, .. } => { - // Disregard the tag when validating JSON - if tag_from_token(&lookup_ident(ident.ident)).is_some() { - return self.visit_identifier(ident); + if let Some(tag) = tag_from_token(&lookup_ident(ident.ident)) { + // Per + // https://github.com/w3c/did-spec-registries/pull/138#issuecomment-719739215, + // strip tag and validate underlying type + if let Type2::TaggedData { t, .. } = tag { + return self.visit_type(&t); + } } if let Some(ga) = generic_args { diff --git a/tests/did.rs b/tests/did.rs index ee6dd2f0..c75386a1 100644 --- a/tests/did.rs +++ b/tests/did.rs @@ -18,10 +18,6 @@ static KNOWN_BAD: &'static [&'static str] = &[ "example3.cbor", "example12.cbor", "example1a.cbor", - // Awaiting reply to - // https://github.com/w3c/did-spec-registries/pull/138#issuecomment-719710486 - // to clarify validation semantics when unwrapping tags - "good_context.cbor", ]; #[test] @@ -54,8 +50,10 @@ fn validate_did_json_examples() -> Result<(), Box> { // Files with known validation errors if KNOWN_BAD.contains(&file.file_name().to_str().unwrap()) { + println!("assert error {:?}", file.path()); assert!(r.is_err()); } else { + println!("assert ok {:?}", file.path()); assert!(r.is_ok()); } } @@ -99,8 +97,10 @@ fn validate_did_cbor_examples() -> Result<(), Box> { // Files with known validation errors if KNOWN_BAD.contains(&file.file_name().to_str().unwrap()) { + println!("assert error {:?}", file.path()); assert!(r.is_err()); } else { + println!("assert ok {:?}", file.path()); assert!(r.is_ok()); } } From 76922d92d6682883c3ebe7be85fa8c13f3c6ee94 Mon Sep 17 00:00:00 2001 From: anweiss <2326106+anweiss@users.noreply.github.com> Date: Mon, 2 Nov 2020 11:38:44 -0500 Subject: [PATCH 10/11] parser fix and more did examples --- src/parser.rs | 7 +- tests/did.rs | 6 +- .../{didDocument => context}/example1.cbor | 0 .../{didDocument => context}/example1.json | 0 tests/fixtures/did/id/id.cddl | 6 + tests/fixtures/did/id/id_example2.cbor | 1 + tests/fixtures/did/id/id_example2.json | 1 + .../did/keyAgreement/keyAgreement.cddl | 121 ++++++++++++++++++ .../keyAgreement/keyAgreement_example11.cbor | 1 + .../keyAgreement/keyAgreement_example11.json | 10 ++ .../did/publicKeyBase58/publicKeyBase58.cddl | 26 ++++ .../publicKeyBase58_example.cbor | 1 + .../publicKeyBase58_example.json | 6 + .../did/publicKeyGpg/publicKeyGpg.cddl | 26 ++++ .../publicKeyGpg/publicKeyGpg_example18.cbor | 1 + .../publicKeyGpg/publicKeyGpg_example18.json | 6 + .../did/publicKeyHex/publicKeyHex.cddl | 25 ++++ .../publicKeyHex/publicKeyHex_example13.json | 6 + .../did/publicKeyJwk/publicKeyJwk.cddl | 75 +++++++++++ .../publicKeyJwk/publicKeyJwk_example14.cbor | 1 + .../publicKeyJwk/publicKeyJwk_example14.json | 12 ++ .../publicKeyJwk/publicKeyJwk_example14b.cbor | 1 + .../publicKeyJwk/publicKeyJwk_example14b.json | 11 ++ 23 files changed, 344 insertions(+), 6 deletions(-) rename tests/fixtures/did/{didDocument => context}/example1.cbor (100%) rename tests/fixtures/did/{didDocument => context}/example1.json (100%) create mode 100644 tests/fixtures/did/id/id.cddl create mode 100644 tests/fixtures/did/id/id_example2.cbor create mode 100644 tests/fixtures/did/id/id_example2.json create mode 100644 tests/fixtures/did/keyAgreement/keyAgreement.cddl create mode 100644 tests/fixtures/did/keyAgreement/keyAgreement_example11.cbor create mode 100644 tests/fixtures/did/keyAgreement/keyAgreement_example11.json create mode 100644 tests/fixtures/did/publicKeyBase58/publicKeyBase58.cddl create mode 100644 tests/fixtures/did/publicKeyBase58/publicKeyBase58_example.cbor create mode 100644 tests/fixtures/did/publicKeyBase58/publicKeyBase58_example.json create mode 100644 tests/fixtures/did/publicKeyGpg/publicKeyGpg.cddl create mode 100644 tests/fixtures/did/publicKeyGpg/publicKeyGpg_example18.cbor create mode 100644 tests/fixtures/did/publicKeyGpg/publicKeyGpg_example18.json create mode 100644 tests/fixtures/did/publicKeyHex/publicKeyHex.cddl create mode 100644 tests/fixtures/did/publicKeyHex/publicKeyHex_example13.json create mode 100644 tests/fixtures/did/publicKeyJwk/publicKeyJwk.cddl create mode 100644 tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14.cbor create mode 100644 tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14.json create mode 100644 tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14b.cbor create mode 100644 tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14b.json diff --git a/src/parser.rs b/src/parser.rs index 275c6aad..f42b8001 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1419,8 +1419,8 @@ where return Ok(grpchoice); } - // Don't advance the token if it is a member key, comma or an opening or - // closing map/group delimiter. Otherwise, advance + // Don't advance the token if it is part of a member key, comma or an + // opening or closing map/group delimiter. Otherwise, advance if !self.cur_token_is(Token::RPAREN) && !self.cur_token_is(Token::RBRACE) && !self.cur_token_is(Token::RBRACKET) @@ -1428,6 +1428,9 @@ where && !self.cur_token_is(Token::LBRACE) && !self.cur_token_is(Token::LBRACKET) && !self.cur_token_is(Token::COMMA) + && !self.cur_token_is(Token::OPTIONAL) + && !self.cur_token_is(Token::ONEORMORE) + && !self.cur_token_is(Token::ASTERISK) && !self.peek_token_is(&Token::COLON) && !self.peek_token_is(&Token::ARROWMAP) && !self.cur_token_is(Token::EOF) diff --git a/tests/did.rs b/tests/did.rs index c75386a1..b252bd08 100644 --- a/tests/did.rs +++ b/tests/did.rs @@ -11,8 +11,6 @@ use std::{ static KNOWN_BAD: &'static [&'static str] = &[ "bad_context.json", - "example1.json", - "example1.cbor", "example2.json", "example2.cbor", "example3.cbor", @@ -43,6 +41,7 @@ fn validate_did_json_examples() -> Result<(), Box> { let file = file?; if file.path().extension().and_then(OsStr::to_str).unwrap() == "json" { let r = validate_json_from_str(&cddl, &fs::read_to_string(file.path())?); + println!("assert ok {:?}", file.path()); if let Err(e) = &r { println!("error validating {:?}\n", file.path()); println!("{}", e); @@ -53,7 +52,6 @@ fn validate_did_json_examples() -> Result<(), Box> { println!("assert error {:?}", file.path()); assert!(r.is_err()); } else { - println!("assert ok {:?}", file.path()); assert!(r.is_ok()); } } @@ -90,6 +88,7 @@ fn validate_did_cbor_examples() -> Result<(), Box> { let mut data = Vec::new(); f.read_to_end(&mut data)?; let r = validate_cbor_from_slice(&cddl, &data); + println!("assert ok {:?}", file.path()); if let Err(e) = &r { println!("error validating {:?}\n", file.path()); println!("{}", e); @@ -100,7 +99,6 @@ fn validate_did_cbor_examples() -> Result<(), Box> { println!("assert error {:?}", file.path()); assert!(r.is_err()); } else { - println!("assert ok {:?}", file.path()); assert!(r.is_ok()); } } diff --git a/tests/fixtures/did/didDocument/example1.cbor b/tests/fixtures/did/context/example1.cbor similarity index 100% rename from tests/fixtures/did/didDocument/example1.cbor rename to tests/fixtures/did/context/example1.cbor diff --git a/tests/fixtures/did/didDocument/example1.json b/tests/fixtures/did/context/example1.json similarity index 100% rename from tests/fixtures/did/didDocument/example1.json rename to tests/fixtures/did/context/example1.json diff --git a/tests/fixtures/did/id/id.cddl b/tests/fixtures/did/id/id.cddl new file mode 100644 index 00000000..2847b6bf --- /dev/null +++ b/tests/fixtures/did/id/id.cddl @@ -0,0 +1,6 @@ +DID-document = { + "id" : did +} + +did = text .regexp "^did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)$" + diff --git a/tests/fixtures/did/id/id_example2.cbor b/tests/fixtures/did/id/id_example2.cbor new file mode 100644 index 00000000..e08cd4ba --- /dev/null +++ b/tests/fixtures/did/id/id_example2.cbor @@ -0,0 +1 @@ +¡bidodid:ipid:abc123 \ No newline at end of file diff --git a/tests/fixtures/did/id/id_example2.json b/tests/fixtures/did/id/id_example2.json new file mode 100644 index 00000000..2bff7c96 --- /dev/null +++ b/tests/fixtures/did/id/id_example2.json @@ -0,0 +1 @@ +{ "id" : "did:ipid:abc123" } diff --git a/tests/fixtures/did/keyAgreement/keyAgreement.cddl b/tests/fixtures/did/keyAgreement/keyAgreement.cddl new file mode 100644 index 00000000..384ee9d4 --- /dev/null +++ b/tests/fixtures/did/keyAgreement/keyAgreement.cddl @@ -0,0 +1,121 @@ +DID-document = { + + "keyAgreement" : [ 1* publicKeyHex / ethereumAddress / publicKeyJwk / publicKeyBase58 / publicKeyGpg / did-url / relative-did-url ] + +} + +did = text .regexp "did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)" + +did-url = text .regexp "did\\:(?[a-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\/?(?[A-Za-z0-9\\/]+)\\??(?[A-Za-z0-9\\=\\&\\%\\-\\:]+)?\\#?(?[A-Za-z0-9\\-]+)?" + +;; base URI value is the DID that is associated with the DID subject +relative-did-url = text .regexp "^#.+$" ; fragment identifier of self relative DID url, i.e. #key-1 in DID document with `id` = did:example:123456789abcdefghi becomes `did:example:123456789abcdefghi#key-1` + + +;;; +; verificationMethod types included below for convenience +;;; + +publicKeyJwk = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyJwk" : publicKeyEC / publicKeyoct / publicKeyed25519 / publicKeyRSA + +} + + +;;; public key Hex is a type of verification method with public key encoded as base16 +publicKeyHex = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyHex" : text .regexp "[a-zA-F0-9]+" ; only allows for hexadecimal representations +} + +publicKeyGpg = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyGpg" : text ; This could probbaly be better to capture -----BEGIN PGP PUBLIC KEY BLOCK +} + +;;; public key Hex is a type of verification method with the public key encoded as base58 +publicKeyBase58 = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyBase58" : text .regexp "[a-zA-Z0-9]+" ; only allows for base58 representations +} + + +;;; ethereeum address is a type of verification method with ethereum address as public key information +ethereumAddress = { + "id" : did-url + "type" : "EcdsaSecp256k1RecoveryMethod2020" ; this is the only type known for ethereum address + "controller" : did + "ethereumAddress" : text .regexp "0x[a-zA-F0-9]{40,40}" ; only allows for hexadecimal representations +} + + + +;;; +; below is a partial list of properties of various well-known JWK public key types. +; as per https://github.com/w3c/did-core/issues/240, the is an active area of discussion +; additional constraints are necessary to finalize the data definition all all JWK types +; below is a partial list of properties of various well-known JWK public key types. +;;;; + + +;;; +publicKeyEC = { + "crv" : "P-256" / "P-384" / "P-521" + "kty" : "EC" + "x" : text + "y" : text + ? "kid" : text ; should be sha256 fingerprint +} + + +;;; Octet sequence key for representing secret keys +publicKeyoct = { + "kty" : "oct" + ? "kid" : text ; this should be the sha256 fingerprint + "alg" : "HS256" / "HS384" / "HS512" + "k" : text .size 32 ; for shared secret +} + + +;; Octet key pair key like ed25519 +publicKeyed25519 = { + "kty" : "OKP" + "crv" : "Ed25519" + "x" : text .size 32 + "use" : "sig" / "enc" + ? "kid" : text ; should be sha256 fingerprint +} + + + +;;; RSA key type +publicKeyRSA = { + "alg" : "RSA" + "mod" : text ; + "exp" : text ; + ? "kid" : text ; should be sha256 fingerprint +} + + +; current list of all registered Verification Method classes +; These are classes not a properties - in other words, use them for the value of type in a verification method object. +VerificationMethodTypes = + "JsonWebKey2020" / + "Ed25519VerificationKey2018" / + "EcdsaSecp256k1VerificationKey2019" / + "SchnorrSecp256k1VerificationKey2019" / + "Ed25519VerificationKey2018" / + "GpgVerificationKey2020" / + "RsaVerificationKey2018" / + "X25519KeyAgreementKey2019" / + "EcdsaSecp256k1RecoveryMethod2020" + \ No newline at end of file diff --git a/tests/fixtures/did/keyAgreement/keyAgreement_example11.cbor b/tests/fixtures/did/keyAgreement/keyAgreement_example11.cbor new file mode 100644 index 00000000..722ce2ae --- /dev/null +++ b/tests/fixtures/did/keyAgreement/keyAgreement_example11.cbor @@ -0,0 +1 @@ +¡lkeyAgreement¤bidx>did:example:123#zC9ByQ8aJs8vrNXyDhPHHNNMSHPcaSgNpjjsBYpMMjsTdSdtypexX25519KeyAgreementKey2019jcontrollerodid:example:123opublicKeyBase58x,9hFgmPVfmBZwRvFEyniQDBkz9LmV7gDEqytWyGZLmDXE \ No newline at end of file diff --git a/tests/fixtures/did/keyAgreement/keyAgreement_example11.json b/tests/fixtures/did/keyAgreement/keyAgreement_example11.json new file mode 100644 index 00000000..34e50088 --- /dev/null +++ b/tests/fixtures/did/keyAgreement/keyAgreement_example11.json @@ -0,0 +1,10 @@ +{ + "keyAgreement": [ + { + "id": "did:example:123#zC9ByQ8aJs8vrNXyDhPHHNNMSHPcaSgNpjjsBYpMMjsTdS", + "type": "X25519KeyAgreementKey2019", + "controller": "did:example:123", + "publicKeyBase58": "9hFgmPVfmBZwRvFEyniQDBkz9LmV7gDEqytWyGZLmDXE" + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/did/publicKeyBase58/publicKeyBase58.cddl b/tests/fixtures/did/publicKeyBase58/publicKeyBase58.cddl new file mode 100644 index 00000000..42192e1e --- /dev/null +++ b/tests/fixtures/did/publicKeyBase58/publicKeyBase58.cddl @@ -0,0 +1,26 @@ +publicKey = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyBase58" : text .regexp "[a-zA-Z0-9]+" ; only allows for base58 representations +} + + +did = text .regexp "^did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)$" + +did-url = text .regexp "^did\\:(?[a-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\/?(?[A-Za-z0-9\\/]+)\\??(?[A-Za-z0-9\\=\\&\\%\\-\\:]+)?\\#?(?[A-Za-z0-9\\-]+)?" + + + +; current list of all registered Verification Method types +VerificationMethodTypes = + "JsonWebKey2020" / + "Ed25519VerificationKey2018" / + "EcdsaSecp256k1VerificationKey2019" / + "SchnorrSecp256k1VerificationKey2019" / + "Ed25519VerificationKey2018" / + "GpgVerificationKey2020" / + "RsaVerificationKey2018" / + "X25519KeyAgreementKey2019" / + "EcdsaSecp256k1RecoveryMethod2020" + diff --git a/tests/fixtures/did/publicKeyBase58/publicKeyBase58_example.cbor b/tests/fixtures/did/publicKeyBase58/publicKeyBase58_example.cbor new file mode 100644 index 00000000..9ca18c57 --- /dev/null +++ b/tests/fixtures/did/publicKeyBase58/publicKeyBase58_example.cbor @@ -0,0 +1 @@ +¤bidx%did:example:123456789abcdefghi#keys-1dtypexEd25519VerificationKey2018jcontrollerxdid:example:123456789abcdefghiopublicKeyBase58x,H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV \ No newline at end of file diff --git a/tests/fixtures/did/publicKeyBase58/publicKeyBase58_example.json b/tests/fixtures/did/publicKeyBase58/publicKeyBase58_example.json new file mode 100644 index 00000000..ca51efed --- /dev/null +++ b/tests/fixtures/did/publicKeyBase58/publicKeyBase58_example.json @@ -0,0 +1,6 @@ +{ + "id": "did:example:123456789abcdefghi#keys-1", + "type": "Ed25519VerificationKey2018", + "controller": "did:example:123456789abcdefghi", + "publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" + } diff --git a/tests/fixtures/did/publicKeyGpg/publicKeyGpg.cddl b/tests/fixtures/did/publicKeyGpg/publicKeyGpg.cddl new file mode 100644 index 00000000..9aa6c374 --- /dev/null +++ b/tests/fixtures/did/publicKeyGpg/publicKeyGpg.cddl @@ -0,0 +1,26 @@ +verificationMethod = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyGpg" : text ; This could probbaly be better to capture -----BEGIN PGP PUBLIC KEY BLOCK +} + + +did = text .regexp "^did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)$" + +did-url = text .regexp "^did\\:(?[a-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\/?(?[A-Za-z0-9\\/]+)\\??(?[A-Za-z0-9\\=\\&\\%\\-\\:]+)?\\#?(?[A-Za-z0-9\\-]+)?" + + + +; current list of all registered Verification Method types +VerificationMethodTypes = + "JsonWebKey2020" / + "Ed25519VerificationKey2018" / + "EcdsaSecp256k1VerificationKey2019" / + "SchnorrSecp256k1VerificationKey2019" / + "Ed25519VerificationKey2018" / + "GpgVerificationKey2020" / + "RsaVerificationKey2018" / + "X25519KeyAgreementKey2019" / + "EcdsaSecp256k1RecoveryMethod2020" + diff --git a/tests/fixtures/did/publicKeyGpg/publicKeyGpg_example18.cbor b/tests/fixtures/did/publicKeyGpg/publicKeyGpg_example18.cbor new file mode 100644 index 00000000..584f5f72 --- /dev/null +++ b/tests/fixtures/did/publicKeyGpg/publicKeyGpg_example18.cbor @@ -0,0 +1 @@ +¤bidx8did:example:123#989ed1057a294c8a3665add842e784c4d08de1e2dtypevGpgVerificationKey2020jcontrollerodid:example:123lpublicKeyGpgxÒ-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: OpenPGP.js v4.9.0\nComment: https://openpgpjs.org\nxjMEXkm5LRYJKwYBBAHaRw8BAQdASmfrjYr7vrjwHNiBsdcImK397Vc3t4BLE8rnNv6Dw===wSoi\n-----END PGP PUBLIC KEY BLOCK----- \ No newline at end of file diff --git a/tests/fixtures/did/publicKeyGpg/publicKeyGpg_example18.json b/tests/fixtures/did/publicKeyGpg/publicKeyGpg_example18.json new file mode 100644 index 00000000..cea236fa --- /dev/null +++ b/tests/fixtures/did/publicKeyGpg/publicKeyGpg_example18.json @@ -0,0 +1,6 @@ +{ + "id": "did:example:123#989ed1057a294c8a3665add842e784c4d08de1e2", + "type": "GpgVerificationKey2020", + "controller": "did:example:123", + "publicKeyGpg": "-----BEGIN PGP PUBLIC KEY BLOCK-----\\nVersion: OpenPGP.js v4.9.0\\nComment: https://openpgpjs.org\\nxjMEXkm5LRYJKwYBBAHaRw8BAQdASmfrjYr7vrjwHNiBsdcImK397Vc3t4BLE8rnNv6Dw===wSoi\\n-----END PGP PUBLIC KEY BLOCK-----" + } diff --git a/tests/fixtures/did/publicKeyHex/publicKeyHex.cddl b/tests/fixtures/did/publicKeyHex/publicKeyHex.cddl new file mode 100644 index 00000000..b767ee63 --- /dev/null +++ b/tests/fixtures/did/publicKeyHex/publicKeyHex.cddl @@ -0,0 +1,25 @@ +verificationMethod = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyHex" : text .regexp "[a-zA-F0-9]+" ; only allows for hexadecimal representations +} + +did = text .regexp "^did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)$" + +did-url = text .regexp "^did\\:(?[a-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\/?(?[A-Za-z0-9\\/]+)\\??(?[A-Za-z0-9\\=\\&\\%\\-\\:]+)?\\#?(?[A-Za-z0-9\\-\\_]+)?" + + +; current list of all registered Verification Method classes +; These are classes not a properties - in other words, use them for the value of type in a verification method object. +VerificationMethodTypes = + "JsonWebKey2020" / + "Ed25519VerificationKey2018" / + "EcdsaSecp256k1VerificationKey2019" / + "SchnorrSecp256k1VerificationKey2019" / + "Ed25519VerificationKey2018" / + "GpgVerificationKey2020" / + "RsaVerificationKey2018" / + "X25519KeyAgreementKey2019" / + "EcdsaSecp256k1RecoveryMethod2020" + \ No newline at end of file diff --git a/tests/fixtures/did/publicKeyHex/publicKeyHex_example13.json b/tests/fixtures/did/publicKeyHex/publicKeyHex_example13.json new file mode 100644 index 00000000..1b9f9314 --- /dev/null +++ b/tests/fixtures/did/publicKeyHex/publicKeyHex_example13.json @@ -0,0 +1,6 @@ +{ + "id": "did:example:123#vm-2", + "controller": "did:example:123", + "type": "EcdsaSecp256k1RecoveryMethod2020", + "publicKeyHex": "027560af3387d375e3342a6968179ef3c6d04f5d33b2b611cf326d4708badd7770" + } \ No newline at end of file diff --git a/tests/fixtures/did/publicKeyJwk/publicKeyJwk.cddl b/tests/fixtures/did/publicKeyJwk/publicKeyJwk.cddl new file mode 100644 index 00000000..2909eb2e --- /dev/null +++ b/tests/fixtures/did/publicKeyJwk/publicKeyJwk.cddl @@ -0,0 +1,75 @@ +publicKey = { + + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyJwk" : publicKeyEC / publicKeyoct / publicKeyed25519 / publicKeyRSA + + +} + +did = text .regexp "did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)" + +did-url = text .regexp "did\\:(?[a-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\/?(?[A-Za-z0-9\\/]+)\\??(?[A-Za-z0-9\\=\\&\\%\\-\\:]+)?\\#?(?[A-Za-z0-9\\-\\_]+)" + + +;;; +; below is a partial list of properties of various well-known JWK public key types. +; as per https://github.com/w3c/did-core/issues/240, the is an active area of discussion +; additional constraints are necessary to finalize the data definition all all JWK types +; below is a partial list of properties of various well-known JWK public key types. +;;;; + + +;;; +publicKeyEC = { + "crv" : "P-256" / "P-384" / "P-521" + "kty" : "EC" + "x" : text + "y" : text + ? "kid" : text ; should be sha256 fingerprint +} + + +;;; Octet sequence key for representing secret keys +publicKeyoct = { + "kty" : "oct" + ? "kid" : text ; this should be the sha256 fingerprint + "alg" : "HS256" / "HS384" / "HS512" + "k" : text .size 32 ; for shared secret +} + + +;; Octet key pair key like ed25519 +publicKeyed25519 = { + "kty" : "OKP" + "crv" : "Ed25519" + "x" : text .size 32 + "use" : "sig" / "enc" + ? "kid" : text ; should be sha256 fingerprint +} + + + +;;; RSA key type +publicKeyRSA = { + "alg" : "RSA" + "mod" : text ; + "exp" : text ; + ? "kid" : text ; should be sha256 fingerprint +} + + +; current list of all registered Verification Method classes +; These are classes not a properties - in other words, use them for the value of type in a verification method object. +VerificationMethodTypes = + "JsonWebKey2020" / + "Ed25519VerificationKey2018" / + "EcdsaSecp256k1VerificationKey2019" / + "SchnorrSecp256k1VerificationKey2019" / + "Ed25519VerificationKey2018" / + "GpgVerificationKey2020" / + "RsaVerificationKey2018" / + "X25519KeyAgreementKey2019" / + "EcdsaSecp256k1RecoveryMethod2020" + \ No newline at end of file diff --git a/tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14.cbor b/tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14.cbor new file mode 100644 index 00000000..20bba58f --- /dev/null +++ b/tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14.cbor @@ -0,0 +1 @@ +¤bidx;did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkwdtypenJsonWebKey2020jcontrollerodid:example:123lpublicKeyJwk¥axx+38M1FDts7Oea7urmseiugGW7tWc3mLpJh6rKe7xINZ8ayx+nDQW6XZ7b_u2Sy9slofYLlG03sOEoug3I0aAPQ0exs4ccrveP-256ckidx+_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkwcktybEC \ No newline at end of file diff --git a/tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14.json b/tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14.json new file mode 100644 index 00000000..967866cc --- /dev/null +++ b/tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14.json @@ -0,0 +1,12 @@ +{ + "id": "did:example:123#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw", + "type": "JsonWebKey2020", + "controller": "did:example:123", + "publicKeyJwk": { + "crv": "P-256", + "x": "38M1FDts7Oea7urmseiugGW7tWc3mLpJh6rKe7xINZ8", + "y": "nDQW6XZ7b_u2Sy9slofYLlG03sOEoug3I0aAPQ0exs4", + "kty": "EC", + "kid": "_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw" + } +} \ No newline at end of file diff --git a/tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14b.cbor b/tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14b.cbor new file mode 100644 index 00000000..1c7b0b55 --- /dev/null +++ b/tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14b.cbor @@ -0,0 +1 @@ +¤bidtdid:r2:YPRm/k3e3#vwydtypex!EcdsaSecp256k1VerificationKey2019jcontrollerkdid:MuGg:4JlpublicKeyJwk¤axnpleurapophysisayhscaleletccrveP-256cktybEC \ No newline at end of file diff --git a/tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14b.json b/tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14b.json new file mode 100644 index 00000000..4e1dcb8f --- /dev/null +++ b/tests/fixtures/did/publicKeyJwk/publicKeyJwk_example14b.json @@ -0,0 +1,11 @@ +{ + "id": "did:r2:YPRm/k3e3#vwy", + "type": "EcdsaSecp256k1VerificationKey2019", + "controller": "did:MuGg:4J", + "publicKeyJwk": { + "crv": "P-256", + "kty": "EC", + "x": "pleurapophysis", + "y": "scalelet" + } +} \ No newline at end of file From 09af76c065cfe1fbbdbb24c9a051cb1ebd00be9f Mon Sep 17 00:00:00 2001 From: anweiss <2326106+anweiss@users.noreply.github.com> Date: Mon, 2 Nov 2020 11:56:15 -0500 Subject: [PATCH 11/11] parser fix and more did examples --- src/parser.rs | 6 + src/validator/json.rs | 9 +- tests/did.rs | 1 + .../capabilityInvocation_example10.json | 15 ++- .../fixtures/did/contentType/contentType.json | 4 +- tests/fixtures/did/context/bad_context.json | 4 +- .../did/context/context_eample1b.json | 8 +- .../did/context/context_example1.json | 4 +- .../did/context/context_example1a.json | 7 +- tests/fixtures/did/context/good_context.json | 7 +- .../did/controller/controller_example3.json | 2 +- tests/fixtures/did/created/created.json | 2 +- .../did/did-url/did-url_example1.json | 5 +- tests/fixtures/did/did/did_example1.json | 2 +- tests/fixtures/did/did/did_example2.json | 2 +- tests/fixtures/did/error/error_example1.json | 2 +- .../ethereumAddress_example12.json | 10 +- tests/fixtures/did/id/id_example2.json | 4 +- .../publicKeyBase58_example.json | 10 +- .../publicKeyGpg/publicKeyGpg_example18.json | 10 +- .../publicKeyHex/publicKeyHex_example13.json | 10 +- .../did/publicKeyPem/publicKeyPem.cddl | 29 +++++ .../publicKeyPem/publicKeyPem_example.cbor | 3 + .../publicKeyPem/publicKeyPem_example.json | 6 + tests/fixtures/did/service/service.cddl | 22 ++++ .../did/service/service_example1.json | 5 + .../did/serviceEndpoint/serviceEndpoint.cddl | 1 + .../did/serviceEndpoint/serviceEndpoint.json | 1 + tests/fixtures/did/updated/updated.cbor | 1 + tests/fixtures/did/updated/updated.cddl | 3 + tests/fixtures/did/updated/updated.json | 3 + .../verificationMethod.cddl | 121 ++++++++++++++++++ .../verificationMethod_example.cbor | 1 + .../verificationMethod_example.json | 27 ++++ .../did/version-time/version-time.cddl | 1 + .../version-time/version-time_example1.cbor | 1 + .../version-time/version-time_example1.json | 1 + 37 files changed, 301 insertions(+), 49 deletions(-) create mode 100644 tests/fixtures/did/publicKeyPem/publicKeyPem.cddl create mode 100644 tests/fixtures/did/publicKeyPem/publicKeyPem_example.cbor create mode 100644 tests/fixtures/did/publicKeyPem/publicKeyPem_example.json create mode 100644 tests/fixtures/did/service/service.cddl create mode 100644 tests/fixtures/did/service/service_example1.json create mode 100644 tests/fixtures/did/serviceEndpoint/serviceEndpoint.cddl create mode 100644 tests/fixtures/did/serviceEndpoint/serviceEndpoint.json create mode 100644 tests/fixtures/did/updated/updated.cbor create mode 100644 tests/fixtures/did/updated/updated.cddl create mode 100644 tests/fixtures/did/updated/updated.json create mode 100644 tests/fixtures/did/verificationMethod/verificationMethod.cddl create mode 100644 tests/fixtures/did/verificationMethod/verificationMethod_example.cbor create mode 100644 tests/fixtures/did/verificationMethod/verificationMethod_example.json create mode 100644 tests/fixtures/did/version-time/version-time.cddl create mode 100644 tests/fixtures/did/version-time/version-time_example1.cbor create mode 100644 tests/fixtures/did/version-time/version-time_example1.json diff --git a/src/parser.rs b/src/parser.rs index f42b8001..a9a7e4af 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -589,8 +589,14 @@ where }) } _ => { + // If type rule is an unwrap type, advance token after parsing type + let advance_token = matches!(self.cur_token, Token::UNWRAP); let mut t = self.parse_type(None)?; + if advance_token { + self.next_token()?; + } + let comments_after_rule = if let Some(comments) = t.comments_after_type() { Some(comments) } else { diff --git a/src/validator/json.rs b/src/validator/json.rs index e1dc18bc..483e075e 100644 --- a/src/validator/json.rs +++ b/src/validator/json.rs @@ -1886,12 +1886,9 @@ mod tests { #[test] fn validate() -> std::result::Result<(), Box> { - let cddl = r#"document = [ - 1* "https://www.example.com/ns/v1" / integer - ]"#; - let json = r#"[ - 1, 2 - ]"#; + let cddl = r#"serivceEndpoint = ~uri + "#; + let json = r#""test""#; let mut lexer = lexer_from_str(cddl); let cddl = cddl_from_str(&mut lexer, cddl, true).map_err(json::Error::CDDLParsing)?; diff --git a/tests/did.rs b/tests/did.rs index b252bd08..78bdfd21 100644 --- a/tests/did.rs +++ b/tests/did.rs @@ -16,6 +16,7 @@ static KNOWN_BAD: &'static [&'static str] = &[ "example3.cbor", "example12.cbor", "example1a.cbor", + "service_example1.json", ]; #[test] diff --git a/tests/fixtures/did/capabilityInvocation/capabilityInvocation_example10.json b/tests/fixtures/did/capabilityInvocation/capabilityInvocation_example10.json index e2dc1de3..cde76407 100644 --- a/tests/fixtures/did/capabilityInvocation/capabilityInvocation_example10.json +++ b/tests/fixtures/did/capabilityInvocation/capabilityInvocation_example10.json @@ -1,10 +1,11 @@ { - "capabilityInvocation": [{ - "id": "did:example:123#z6MkpzW2izkFjNwMBwwvKqmELaQcH8t54QL5xmBdJg9Xh1y4", - "type": "Ed25519VerificationKey2018", - "controller": "did:example:123", - "publicKeyBase58": "BYEz8kVpPqSt5T7DeGoPVUrcTZcDeX5jGkGhUQBWmoBg" - }, - "did:example:123#WjKgJV7VRw3hmgU6--4v15c0Aewbcvat1BsRFTIqa5Q" + "capabilityInvocation": [ + { + "id": "did:example:123#z6MkpzW2izkFjNwMBwwvKqmELaQcH8t54QL5xmBdJg9Xh1y4", + "type": "Ed25519VerificationKey2018", + "controller": "did:example:123", + "publicKeyBase58": "BYEz8kVpPqSt5T7DeGoPVUrcTZcDeX5jGkGhUQBWmoBg" + }, + "did:example:123#WjKgJV7VRw3hmgU6--4v15c0Aewbcvat1BsRFTIqa5Q" ] } \ No newline at end of file diff --git a/tests/fixtures/did/contentType/contentType.json b/tests/fixtures/did/contentType/contentType.json index 86cf6555..bdb126e7 100644 --- a/tests/fixtures/did/contentType/contentType.json +++ b/tests/fixtures/did/contentType/contentType.json @@ -1 +1,3 @@ -{ "content-type" : "application/did+ld+json" } +{ + "content-type": "application/did+ld+json" +} \ No newline at end of file diff --git a/tests/fixtures/did/context/bad_context.json b/tests/fixtures/did/context/bad_context.json index 80f2229c..6f3eda46 100644 --- a/tests/fixtures/did/context/bad_context.json +++ b/tests/fixtures/did/context/bad_context.json @@ -1 +1,3 @@ -{ "@context" : "http://example.com" } +{ + "@context": "http://example.com" +} \ No newline at end of file diff --git a/tests/fixtures/did/context/context_eample1b.json b/tests/fixtures/did/context/context_eample1b.json index 0ed6b0a3..0ba3e3bd 100644 --- a/tests/fixtures/did/context/context_eample1b.json +++ b/tests/fixtures/did/context/context_eample1b.json @@ -1 +1,7 @@ -{"@context" : [ "https://www.w3.org/ns/did/v1" , "https://w3id.org/security/v1", "https://example.com/v2" ] } +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3id.org/security/v1", + "https://example.com/v2" + ] +} \ No newline at end of file diff --git a/tests/fixtures/did/context/context_example1.json b/tests/fixtures/did/context/context_example1.json index c63f14ac..981842d4 100644 --- a/tests/fixtures/did/context/context_example1.json +++ b/tests/fixtures/did/context/context_example1.json @@ -1 +1,3 @@ -{"@context" : "https://www.w3.org/ns/did/v1"} +{ + "@context": "https://www.w3.org/ns/did/v1" +} \ No newline at end of file diff --git a/tests/fixtures/did/context/context_example1a.json b/tests/fixtures/did/context/context_example1a.json index 1d02f2aa..88936ebd 100644 --- a/tests/fixtures/did/context/context_example1a.json +++ b/tests/fixtures/did/context/context_example1a.json @@ -1 +1,6 @@ -{"@context" : [ "https://www.w3.org/ns/did/v1" , "https://w3id.org/security/v1" ] } +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3id.org/security/v1" + ] +} \ No newline at end of file diff --git a/tests/fixtures/did/context/good_context.json b/tests/fixtures/did/context/good_context.json index 26602163..07fe617c 100644 --- a/tests/fixtures/did/context/good_context.json +++ b/tests/fixtures/did/context/good_context.json @@ -1 +1,6 @@ -{ "@context" : ["https://www.w3.org/ns/did/v1" , "http://example.com" ] } +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + "http://example.com" + ] +} \ No newline at end of file diff --git a/tests/fixtures/did/controller/controller_example3.json b/tests/fixtures/did/controller/controller_example3.json index 075adca9..e5103910 100644 --- a/tests/fixtures/did/controller/controller_example3.json +++ b/tests/fixtures/did/controller/controller_example3.json @@ -1,3 +1,3 @@ { "controller": "did:example:123" -} +} \ No newline at end of file diff --git a/tests/fixtures/did/created/created.json b/tests/fixtures/did/created/created.json index 181c8117..14124b12 100644 --- a/tests/fixtures/did/created/created.json +++ b/tests/fixtures/did/created/created.json @@ -1,3 +1,3 @@ { "created": "2002-10-10T17:00:00Z" -} +} \ No newline at end of file diff --git a/tests/fixtures/did/did-url/did-url_example1.json b/tests/fixtures/did/did-url/did-url_example1.json index 29836259..cd747772 100644 --- a/tests/fixtures/did/did-url/did-url_example1.json +++ b/tests/fixtures/did/did-url/did-url_example1.json @@ -1,4 +1 @@ -"did:example:123?service=files&relative-ref=%2Fmyresume%2Fdoc%3Fversion%3Dlatest%23intro&foo=bar&version-time=2016-10-17T02:41:00Z#id-1" - - - +"did:example:123?service=files&relative-ref=%2Fmyresume%2Fdoc%3Fversion%3Dlatest%23intro&foo=bar&version-time=2016-10-17T02:41:00Z#id-1" \ No newline at end of file diff --git a/tests/fixtures/did/did/did_example1.json b/tests/fixtures/did/did/did_example1.json index b7e79349..96f1790f 100644 --- a/tests/fixtures/did/did/did_example1.json +++ b/tests/fixtures/did/did/did_example1.json @@ -1 +1 @@ -"did:example:abc123" +"did:example:abc123" \ No newline at end of file diff --git a/tests/fixtures/did/did/did_example2.json b/tests/fixtures/did/did/did_example2.json index f642ff2a..2c3618f8 100644 --- a/tests/fixtures/did/did/did_example2.json +++ b/tests/fixtures/did/did/did_example2.json @@ -1 +1 @@ -"did:example:lajlajfaifjelfjlieahflahf" +"did:example:lajlajfaifjelfjlieahflahf" \ No newline at end of file diff --git a/tests/fixtures/did/error/error_example1.json b/tests/fixtures/did/error/error_example1.json index ca527afa..c0fe705d 100644 --- a/tests/fixtures/did/error/error_example1.json +++ b/tests/fixtures/did/error/error_example1.json @@ -1,3 +1,3 @@ { "error": "not-found" -} +} \ No newline at end of file diff --git a/tests/fixtures/did/ethereumAddress/ethereumAddress_example12.json b/tests/fixtures/did/ethereumAddress/ethereumAddress_example12.json index 83ccb1f3..5a9d5bdd 100644 --- a/tests/fixtures/did/ethereumAddress/ethereumAddress_example12.json +++ b/tests/fixtures/did/ethereumAddress/ethereumAddress_example12.json @@ -1,6 +1,6 @@ { - "id": "did:example:123#vm-3", - "controller": "did:example:123", - "type": "EcdsaSecp256k1RecoveryMethod2020", - "ethereumAddress": "0xF3beAC30C498D9E26865F34fCAa57dBB935b0D74" - } \ No newline at end of file + "id": "did:example:123#vm-3", + "controller": "did:example:123", + "type": "EcdsaSecp256k1RecoveryMethod2020", + "ethereumAddress": "0xF3beAC30C498D9E26865F34fCAa57dBB935b0D74" +} \ No newline at end of file diff --git a/tests/fixtures/did/id/id_example2.json b/tests/fixtures/did/id/id_example2.json index 2bff7c96..4dc9f9da 100644 --- a/tests/fixtures/did/id/id_example2.json +++ b/tests/fixtures/did/id/id_example2.json @@ -1 +1,3 @@ -{ "id" : "did:ipid:abc123" } +{ + "id": "did:ipid:abc123" +} \ No newline at end of file diff --git a/tests/fixtures/did/publicKeyBase58/publicKeyBase58_example.json b/tests/fixtures/did/publicKeyBase58/publicKeyBase58_example.json index ca51efed..364b8b66 100644 --- a/tests/fixtures/did/publicKeyBase58/publicKeyBase58_example.json +++ b/tests/fixtures/did/publicKeyBase58/publicKeyBase58_example.json @@ -1,6 +1,6 @@ { - "id": "did:example:123456789abcdefghi#keys-1", - "type": "Ed25519VerificationKey2018", - "controller": "did:example:123456789abcdefghi", - "publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" - } + "id": "did:example:123456789abcdefghi#keys-1", + "type": "Ed25519VerificationKey2018", + "controller": "did:example:123456789abcdefghi", + "publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" +} \ No newline at end of file diff --git a/tests/fixtures/did/publicKeyGpg/publicKeyGpg_example18.json b/tests/fixtures/did/publicKeyGpg/publicKeyGpg_example18.json index cea236fa..aac60416 100644 --- a/tests/fixtures/did/publicKeyGpg/publicKeyGpg_example18.json +++ b/tests/fixtures/did/publicKeyGpg/publicKeyGpg_example18.json @@ -1,6 +1,6 @@ { - "id": "did:example:123#989ed1057a294c8a3665add842e784c4d08de1e2", - "type": "GpgVerificationKey2020", - "controller": "did:example:123", - "publicKeyGpg": "-----BEGIN PGP PUBLIC KEY BLOCK-----\\nVersion: OpenPGP.js v4.9.0\\nComment: https://openpgpjs.org\\nxjMEXkm5LRYJKwYBBAHaRw8BAQdASmfrjYr7vrjwHNiBsdcImK397Vc3t4BLE8rnNv6Dw===wSoi\\n-----END PGP PUBLIC KEY BLOCK-----" - } + "id": "did:example:123#989ed1057a294c8a3665add842e784c4d08de1e2", + "type": "GpgVerificationKey2020", + "controller": "did:example:123", + "publicKeyGpg": "-----BEGIN PGP PUBLIC KEY BLOCK-----\\nVersion: OpenPGP.js v4.9.0\\nComment: https://openpgpjs.org\\nxjMEXkm5LRYJKwYBBAHaRw8BAQdASmfrjYr7vrjwHNiBsdcImK397Vc3t4BLE8rnNv6Dw===wSoi\\n-----END PGP PUBLIC KEY BLOCK-----" +} \ No newline at end of file diff --git a/tests/fixtures/did/publicKeyHex/publicKeyHex_example13.json b/tests/fixtures/did/publicKeyHex/publicKeyHex_example13.json index 1b9f9314..e1e9a273 100644 --- a/tests/fixtures/did/publicKeyHex/publicKeyHex_example13.json +++ b/tests/fixtures/did/publicKeyHex/publicKeyHex_example13.json @@ -1,6 +1,6 @@ { - "id": "did:example:123#vm-2", - "controller": "did:example:123", - "type": "EcdsaSecp256k1RecoveryMethod2020", - "publicKeyHex": "027560af3387d375e3342a6968179ef3c6d04f5d33b2b611cf326d4708badd7770" - } \ No newline at end of file + "id": "did:example:123#vm-2", + "controller": "did:example:123", + "type": "EcdsaSecp256k1RecoveryMethod2020", + "publicKeyHex": "027560af3387d375e3342a6968179ef3c6d04f5d33b2b611cf326d4708badd7770" +} \ No newline at end of file diff --git a/tests/fixtures/did/publicKeyPem/publicKeyPem.cddl b/tests/fixtures/did/publicKeyPem/publicKeyPem.cddl new file mode 100644 index 00000000..4b7d06f4 --- /dev/null +++ b/tests/fixtures/did/publicKeyPem/publicKeyPem.cddl @@ -0,0 +1,29 @@ +publicKey = { + + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyPem" : text ; this could be improved + +} + + + + +did = text .regexp "did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)" + +did-url = text .regexp "did\\:(?[a-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\/?(?[A-Za-z0-9\\/]+)\\??(?[A-Za-z0-9\\=\\&\\%\\-\\:]+)?\\#?(?[A-Za-z0-9\\-\\_]+)" + + +; current list of all registered Verification Method classes +; These are classes not a properties - in other words, use them for the value of type in a verification method object. +VerificationMethodTypes = + "JsonWebKey2020" / + "Ed25519VerificationKey2018" / + "EcdsaSecp256k1VerificationKey2019" / + "SchnorrSecp256k1VerificationKey2019" / + "Ed25519VerificationKey2018" / + "GpgVerificationKey2020" / + "RsaVerificationKey2018" / + "X25519KeyAgreementKey2019" + diff --git a/tests/fixtures/did/publicKeyPem/publicKeyPem_example.cbor b/tests/fixtures/did/publicKeyPem/publicKeyPem_example.cbor new file mode 100644 index 00000000..f8ee2087 --- /dev/null +++ b/tests/fixtures/did/publicKeyPem/publicKeyPem_example.cbor @@ -0,0 +1,3 @@ +¤bidtdid:example:123#vm-2dtypevRsaVerificationKey2018jcontrollerodid:example:123lpublicKeyPemxD-----BEGIN PUBLIC KEY----- +MIIBG0BAOClDQAB +-----END PUBLIC KEY----- diff --git a/tests/fixtures/did/publicKeyPem/publicKeyPem_example.json b/tests/fixtures/did/publicKeyPem/publicKeyPem_example.json new file mode 100644 index 00000000..cef5a3e2 --- /dev/null +++ b/tests/fixtures/did/publicKeyPem/publicKeyPem_example.json @@ -0,0 +1,6 @@ +{ + "id": "did:example:123#vm-2", + "controller": "did:example:123", + "type": "RsaVerificationKey2018", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBG0BAOClDQAB\n-----END PUBLIC KEY-----\n" +} \ No newline at end of file diff --git a/tests/fixtures/did/service/service.cddl b/tests/fixtures/did/service/service.cddl new file mode 100644 index 00000000..9050c9e5 --- /dev/null +++ b/tests/fixtures/did/service/service.cddl @@ -0,0 +1,22 @@ +did-document = { + "service" = serviceEndpoint + +} + +serviceEndpoint = { + ? "@content" : ~uri + "id" : did-url + "type" : text + ? "description" : text + "serviceEndpoint" : ~uri +} + + +did-url = text .regexp "^did\\:(?[a-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\/?(?[A-Za-z0-9\\/]+)\\??(?[A-Za-z0-9\\=\\&\\%\\-\\:]+)?\\#?(?[A-Za-z0-9\\-\\_]+)?" + + + + + + + diff --git a/tests/fixtures/did/service/service_example1.json b/tests/fixtures/did/service/service_example1.json new file mode 100644 index 00000000..8cfa43c6 --- /dev/null +++ b/tests/fixtures/did/service/service_example1.json @@ -0,0 +1,5 @@ +{ + "id": "did:example:123456789abcdefghi#vcs", + "type": "VerifiableCredentialService", + "serviceEndpoint": "https://example.com/vc/" +} \ No newline at end of file diff --git a/tests/fixtures/did/serviceEndpoint/serviceEndpoint.cddl b/tests/fixtures/did/serviceEndpoint/serviceEndpoint.cddl new file mode 100644 index 00000000..2278bfc0 --- /dev/null +++ b/tests/fixtures/did/serviceEndpoint/serviceEndpoint.cddl @@ -0,0 +1 @@ +serivceEndpoint = ~uri diff --git a/tests/fixtures/did/serviceEndpoint/serviceEndpoint.json b/tests/fixtures/did/serviceEndpoint/serviceEndpoint.json new file mode 100644 index 00000000..7d869bf7 --- /dev/null +++ b/tests/fixtures/did/serviceEndpoint/serviceEndpoint.json @@ -0,0 +1 @@ +"https://repository.example.com/service/8377464" \ No newline at end of file diff --git a/tests/fixtures/did/updated/updated.cbor b/tests/fixtures/did/updated/updated.cbor new file mode 100644 index 00000000..d3f37e52 --- /dev/null +++ b/tests/fixtures/did/updated/updated.cbor @@ -0,0 +1 @@ +¡gupdatedt2002-10-10T17:00:00Z \ No newline at end of file diff --git a/tests/fixtures/did/updated/updated.cddl b/tests/fixtures/did/updated/updated.cddl new file mode 100644 index 00000000..1be8b34e --- /dev/null +++ b/tests/fixtures/did/updated/updated.cddl @@ -0,0 +1,3 @@ +did-document-metadata = { + "updated" : text .regexp "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$" +} diff --git a/tests/fixtures/did/updated/updated.json b/tests/fixtures/did/updated/updated.json new file mode 100644 index 00000000..411e3d43 --- /dev/null +++ b/tests/fixtures/did/updated/updated.json @@ -0,0 +1,3 @@ +{ + "updated": "2002-10-10T17:00:00Z" +} \ No newline at end of file diff --git a/tests/fixtures/did/verificationMethod/verificationMethod.cddl b/tests/fixtures/did/verificationMethod/verificationMethod.cddl new file mode 100644 index 00000000..fff8f38b --- /dev/null +++ b/tests/fixtures/did/verificationMethod/verificationMethod.cddl @@ -0,0 +1,121 @@ +DID-document = { + + "verificationMethod" : [ 1* publicKeyHex / ethereumAddress / publicKeyJwk / publicKeyBase58 / publicKeyGpg / did-url / relative-did-url ] + +} + +did = text .regexp "did\\:(?[A-Za-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)" + +did-url = text .regexp "did\\:(?[a-z0-9]+)\\:(?[A-Za-z0-9\\.\\-\\:\\_]+)\\/?(?[A-Za-z0-9\\/]+)\\??(?[A-Za-z0-9\\=\\&\\%\\-\\:]+)?\\#?(?[A-Za-z0-9\\-]+)?" + +;; base URI value is the DID that is associated with the DID subject +relative-did-url = text .regexp "^#.+$" ; fragment identifier of self relative DID url, i.e. #key-1 in DID document with `id` = did:example:123456789abcdefghi becomes `did:example:123456789abcdefghi#key-1` + + +;;; +; verificationMethod types included below for convenience +;;; + +publicKeyJwk = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyJwk" : publicKeyEC / publicKeyoct / publicKeyed25519 / publicKeyRSA + +} + + +;;; public key Hex is a type of verification method with public key encoded as base16 +publicKeyHex = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyHex" : text .regexp "[a-zA-F0-9]+" ; only allows for hexadecimal representations +} + +publicKeyGpg = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyGpg" : text ; This could probbaly be better to capture -----BEGIN PGP PUBLIC KEY BLOCK +} + +;;; public key Hex is a type of verification method with the public key encoded as base58 +publicKeyBase58 = { + "id" : did-url + "type" : VerificationMethodTypes + "controller" : did + "publicKeyBase58" : text .regexp "[a-zA-Z0-9]+" ; only allows for base58 representations +} + + +;;; ethereeum address is a type of verification method with ethereum address as public key information +ethereumAddress = { + "id" : did-url + "type" : "EcdsaSecp256k1RecoveryMethod2020" ; this is the only type known for ethereum address + "controller" : did + "ethereumAddress" : text .regexp "0x[a-zA-F0-9]{40,40}" ; only allows for hexadecimal representations +} + + + +;;; +; below is a partial list of properties of various well-known JWK public key types. +; as per https://github.com/w3c/did-core/issues/240, the is an active area of discussion +; additional constraints are necessary to finalize the data definition all all JWK types +; below is a partial list of properties of various well-known JWK public key types. +;;;; + + +;;; +publicKeyEC = { + "crv" : "P-256" / "P-384" / "P-521" + "kty" : "EC" + "x" : text + "y" : text + ? "kid" : text ; should be sha256 fingerprint +} + + +;;; Octet sequence key for representing secret keys +publicKeyoct = { + "kty" : "oct" + ? "kid" : text ; this should be the sha256 fingerprint + "alg" : "HS256" / "HS384" / "HS512" + "k" : text .size 32 ; for shared secret +} + + +;; Octet key pair key like ed25519 +publicKeyed25519 = { + "kty" : "OKP" + "crv" : "Ed25519" + "x" : text .size 32 + "use" : "sig" / "enc" + ? "kid" : text ; should be sha256 fingerprint +} + + + +;;; RSA key type +publicKeyRSA = { + "alg" : "RSA" + "mod" : text ; + "exp" : text ; + ? "kid" : text ; should be sha256 fingerprint +} + + +; current list of all registered Verification Method classes +; These are classes not a properties - in other words, use them for the value of type in a verification method object. +VerificationMethodTypes = + "JsonWebKey2020" / + "Ed25519VerificationKey2018" / + "EcdsaSecp256k1VerificationKey2019" / + "SchnorrSecp256k1VerificationKey2019" / + "Ed25519VerificationKey2018" / + "GpgVerificationKey2020" / + "RsaVerificationKey2018" / + "X25519KeyAgreementKey2019" / + "EcdsaSecp256k1RecoveryMethod2020" + \ No newline at end of file diff --git a/tests/fixtures/did/verificationMethod/verificationMethod_example.cbor b/tests/fixtures/did/verificationMethod/verificationMethod_example.cbor new file mode 100644 index 00000000..1899fa33 --- /dev/null +++ b/tests/fixtures/did/verificationMethod/verificationMethod_example.cbor @@ -0,0 +1 @@ +¡rverificationMethod„x%did:example:123456789abcdefghi#keys-1¤bidx$did:example:123456789abcdefghi#key-1dtypex#SchnorrSecp256k1VerificationKey2019jcontrollerx did:example:123456789abcdefghijkopublicKeyBase58x,H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV¤bidxdid:example:123456789abcdefghidtypexEd25519VerificationKey2018jcontrollerxdid:example:123456789abcdefghilpublicKeyJwk£akx yRnYf2JWv3QFnKwYlgv1ztrxiP43AL0fcalgeHS256cktycoct¤bidxdid:example:123456789abcdefghidtypex EcdsaSecp256k1RecoveryMethod2020jcontrollerxdid:example:123456789abcdefghioethereumAddressx*0x0137639710732B2B30bD70dDb89c35e794038062 \ No newline at end of file diff --git a/tests/fixtures/did/verificationMethod/verificationMethod_example.json b/tests/fixtures/did/verificationMethod/verificationMethod_example.json new file mode 100644 index 00000000..971b28a9 --- /dev/null +++ b/tests/fixtures/did/verificationMethod/verificationMethod_example.json @@ -0,0 +1,27 @@ +{ + "verificationMethod": [ + "did:example:123456789abcdefghi#keys-1", + { + "id": "did:example:123456789abcdefghi#key-1", + "type": "SchnorrSecp256k1VerificationKey2019", + "controller": "did:example:123456789abcdefghijk", + "publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" + }, + { + "id": "did:example:123456789abcdefghi", + "type": "Ed25519VerificationKey2018", + "controller": "did:example:123456789abcdefghi", + "publicKeyJwk": { + "kty": "oct", + "alg": "HS256", + "k": "yRnYf2JWv3QFnKwYlgv1ztrxiP43AL0f" + } + }, + { + "id": "did:example:123456789abcdefghi", + "type": "EcdsaSecp256k1RecoveryMethod2020", + "controller": "did:example:123456789abcdefghi", + "ethereumAddress": "0x0137639710732B2B30bD70dDb89c35e794038062" + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/did/version-time/version-time.cddl b/tests/fixtures/did/version-time/version-time.cddl new file mode 100644 index 00000000..e694810a --- /dev/null +++ b/tests/fixtures/did/version-time/version-time.cddl @@ -0,0 +1 @@ +version-time = text .regexp "^version-time=(?\\d{4}\\-\\d{2}\\-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}Z)$" \ No newline at end of file diff --git a/tests/fixtures/did/version-time/version-time_example1.cbor b/tests/fixtures/did/version-time/version-time_example1.cbor new file mode 100644 index 00000000..2deb41b0 --- /dev/null +++ b/tests/fixtures/did/version-time/version-time_example1.cbor @@ -0,0 +1 @@ +x!version-time=2016-10-17T02:41:00Z \ No newline at end of file diff --git a/tests/fixtures/did/version-time/version-time_example1.json b/tests/fixtures/did/version-time/version-time_example1.json new file mode 100644 index 00000000..fa64961f --- /dev/null +++ b/tests/fixtures/did/version-time/version-time_example1.json @@ -0,0 +1 @@ +"version-time=2016-10-17T02:41:00Z" \ No newline at end of file