diff --git a/Cargo.toml b/Cargo.toml index 271e3c85..14982da9 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] @@ -27,8 +27,9 @@ 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_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 } @@ -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/README.md b/README.md index 304d1813..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. @@ -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. @@ -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 9571be72..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. @@ -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 ]`. @@ -359,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 diff --git a/src/parser.rs b/src/parser.rs index 275c6aad..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 { @@ -1419,8 +1425,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 +1434,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/src/validator/cbor.rs b/src/validator/cbor.rs index 5fca640c..6ab86453 100644 --- a/src/validator/cbor.rs +++ b/src/validator/cbor.rs @@ -452,39 +452,57 @@ 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(()); } } } 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 +510,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 +900,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 @@ -978,45 +997,64 @@ 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(()); } } } 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)); @@ -1210,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 { @@ -1587,31 +1630,40 @@ 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(()); } } } 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 +1671,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)); @@ -2126,12 +2188,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()))?; @@ -2187,45 +2254,64 @@ 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(()); } } } 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 d58640df..483e075e 100644 --- a/src/validator/json.rs +++ b/src/validator/json.rs @@ -452,45 +452,64 @@ 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(()); } } } 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 +920,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 @@ -968,45 +987,64 @@ 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(()); } } } 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)); @@ -1044,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; @@ -1156,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 { @@ -1335,46 +1378,64 @@ 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(()); } } } 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)); @@ -1623,12 +1684,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()))?; @@ -1684,39 +1750,57 @@ 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(()); } } } 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) { @@ -1724,6 +1808,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)); @@ -1801,12 +1886,9 @@ 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#"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/src/validator/mod.rs b/src/validator/mod.rs index f59fc3f6..9e5c0631 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -614,10 +614,41 @@ 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, /// 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); + 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) && c != 'd' { + unescape.push(format!("\\{}", c)); + } + } + } + } + + for replace in unescape.iter() { + formatted_regex = + formatted_regex.replace(replace, &replace.chars().nth(1).unwrap().to_string()); + } + + for find in ["?=", "?!", "?<=", "? Result<(), parser::Error> { @@ -35,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(()) -// } diff --git a/tests/did.rs b/tests/did.rs new file mode 100644 index 00000000..78bdfd21 --- /dev/null +++ b/tests/did.rs @@ -0,0 +1,111 @@ +#![cfg(feature = "std")] +#![cfg(not(target_arch = "wasm32"))] + +use cddl::{validate_cbor_from_slice, validate_json_from_str}; +use std::{ + error::Error, + ffi::OsStr, + fs::{self, File}, + io::Read, +}; + +static KNOWN_BAD: &'static [&'static str] = &[ + "bad_context.json", + "example2.json", + "example2.cbor", + "example3.cbor", + "example12.cbor", + "example1a.cbor", + "service_example1.json", +]; + +#[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" { + 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); + } + + // 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 { + assert!(r.is_ok()); + } + } + } + } + } + + 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)?; + 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); + } + + // 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 { + assert!(r.is_ok()); + } + } + } + } + } + + 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 @@ +oassertionMethodbidx@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 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 @@ +nauthenticationbidx@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 @@ +nauthenticationbidx%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 @@ +tcapabilityDelegationbidx@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 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 @@ +tcapabilityInvocationbidx@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..cde76407 --- /dev/null +++ b/tests/fixtures/did/capabilityInvocation/capabilityInvocation_example10.json @@ -0,0 +1,11 @@ +{ + "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..bdb126e7 --- /dev/null +++ b/tests/fixtures/did/contentType/contentType.json @@ -0,0 +1,3 @@ +{ + "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 new file mode 100644 index 00000000..6f3eda46 --- /dev/null +++ b/tests/fixtures/did/context/bad_context.json @@ -0,0 +1,3 @@ +{ + "@context": "http://example.com" +} \ No newline at end of file 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..0ba3e3bd --- /dev/null +++ b/tests/fixtures/did/context/context_eample1b.json @@ -0,0 +1,7 @@ +{ + "@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 new file mode 100644 index 00000000..981842d4 --- /dev/null +++ b/tests/fixtures/did/context/context_example1.json @@ -0,0 +1,3 @@ +{ + "@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 new file mode 100644 index 00000000..88936ebd --- /dev/null +++ b/tests/fixtures/did/context/context_example1a.json @@ -0,0 +1,6 @@ +{ + "@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/example1.cbor b/tests/fixtures/did/context/example1.cbor new file mode 100644 index 00000000..a8d8dfc0 --- /dev/null +++ b/tests/fixtures/did/context/example1.cbor @@ -0,0 +1 @@ +h@contextxhttps://www.w3.org/ns/did/v1x*https://example.com/blockchain-identity/v1 \ No newline at end of file diff --git a/tests/fixtures/did/context/example1.json b/tests/fixtures/did/context/example1.json new file mode 100644 index 00000000..4291581d --- /dev/null +++ b/tests/fixtures/did/context/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/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@contextxhttps://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..07fe617c --- /dev/null +++ b/tests/fixtures/did/context/good_context.json @@ -0,0 +1,6 @@ +{ + "@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.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..e5103910 --- /dev/null +++ b/tests/fixtures/did/controller/controller_example3.json @@ -0,0 +1,3 @@ +{ + "controller": "did:example:123" +} \ No newline at end of file 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..14124b12 --- /dev/null +++ b/tests/fixtures/did/created/created.json @@ -0,0 +1,3 @@ +{ + "created": "2002-10-10T17:00:00Z" +} \ No newline at end of file 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..cd747772 --- /dev/null +++ b/tests/fixtures/did/did-url/did-url_example1.json @@ -0,0 +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.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..96f1790f --- /dev/null +++ b/tests/fixtures/did/did/did_example1.json @@ -0,0 +1 @@ +"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 new file mode 100644 index 00000000..2c3618f8 --- /dev/null +++ b/tests/fixtures/did/did/did_example2.json @@ -0,0 +1 @@ +"did:example:lajlajfaifjelfjlieahflahf" \ No newline at end of file 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/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 @@ +rverificationMethodbidtdid: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..c0fe705d --- /dev/null +++ b/tests/fixtures/did/error/error_example1.json @@ -0,0 +1,3 @@ +{ + "error": "not-found" +} \ No newline at end of file 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..5a9d5bdd --- /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 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..4dc9f9da --- /dev/null +++ b/tests/fixtures/did/id/id_example2.json @@ -0,0 +1,3 @@ +{ + "id": "did:ipid:abc123" +} \ No newline at end of file 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 @@ +lkeyAgreementbidx>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..364b8b66 --- /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" +} \ No newline at end of file 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..aac60416 --- /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-----" +} \ No newline at end of file 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..e1e9a273 --- /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:123lpublicKeyJwkaxx+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:4JlpublicKeyJwkaxnpleurapophysisayhscaleletccrveP-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 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 @@ +rverificationMethodx%did:example:123456789abcdefghi#keys-1bidx$did:example:123456789abcdefghi#key-1dtypex#SchnorrSecp256k1VerificationKey2019jcontrollerx did:example:123456789abcdefghijkopublicKeyBase58x,H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPVbidxdid:example:123456789abcdefghidtypexEd25519VerificationKey2018jcontrollerxdid:example:123456789abcdefghilpublicKeyJwkakx yRnYf2JWv3QFnKwYlgv1ztrxiP43AL0fcalgeHS256cktycoctbidxdid: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