Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial stab at the schema #3

Merged
merged 14 commits into from
Mar 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: CI

on: [push]

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: install cddl
run: gem install --user-install cddl
- name: set up PATH
run: echo "$(gem env gempath | cut -d':' -f1)/bin" >> $GITHUB_PATH
- uses: actions/checkout@v2
- name: test schema against test vectors
run: cddl=cddl mdspell=unused make tests
- name: test schema against any extra document
run: cddl=cddl mdspell=unused make extra
23 changes: 23 additions & 0 deletions .spelling
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# markdown-spellcheck spelling configuration file
# Format - lines beginning # are comments
# global dictionary is at the start, file overrides afterwards
# one word per line, to define a file override use ' - filename'
# where filename is relative to this configuration file
GlueCOSE
CDDL
Sign1
go-cose
JWK
COSE
base16
COSE_Sign1
CBOR
boolean
PRNG
e.g.
RFC9052
serialised
i.e.
- README.md
18
4.4
21 changes: 21 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.DEFAULT_GOAL := all

SCHEMA := gluecose-schema.cddl

TESTS := sign1-verify-0000.json
TESTS += sign1-sign-0000.json

EXTRA := misc/gocose-result-sign1-verify-0000.json

include tools.mk

.PHONY: tests
tests: ; for f in $(TESTS) ; do $(cddl) $(SCHEMA) v $$f ; done

.PHONY: extra
extra: ; for f in $(EXTRA) ; do $(cddl) $(SCHEMA) v $$f ; done

.PHONY: spell
spell: ; $(mdspell) --en-us README.md

all: tests extra spell
133 changes: 131 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,131 @@
# test-vectors
Repository to hold the various test inputs
# GlueCOSE Validation Suite

**Caution: this is a work in progress in its very early stages.**

This is a repository of GlueCOSE test cases. Eventually, this will morph in to
the GlueCOSE validation suite.

Here's a first stab at the [CDDL schema](gluecose-schema.cddl) for specifying
test cases as well as their results.

And here's the first test case for a [Sign1 verify](sign1-verify-0000.json) that
has been [validated](misc/gocose-result-sign1-verify-0000.json) using the
go-cose implementation.

## Sign1 Tests

Signing and verification test cases depend on the key material and signature
scheme specified in the corresponding `TestCaseInput`.

All tests shall:

* Load and decode the [JWK](https://www.rfc-editor.org/info/rfc7517) contained
in the `key` object

* Set the signature scheme to the algorithm specified in the `alg` string. The
label is the same as the corresponding *Name* in the [COSE Algorithms
Registry](https://www.iana.org/assignments/cose/cose.xhtml#algorithms).

### Verify

This section describes how the GlueCOSE test writer consumes a `Sign1_verify`
object to create the test driver for verifying COSE Sign1 payloads.

All the keys in the remainder of this section are relative to the
`Sign1_verify` object associated with the `"sign1::verify"` key.

* The `cborHex` string in the `taggedCOSESign1` object contains the
[Base16](https://www.rfc-editor.org/info/rfc4648) encoding of the tagged (18)
COSE_Sign1 data that needs to be verified. Load it and Base16 decode it into
a byte buffer. (Note that a CBOR diagnostic version of the same exact content
is optionally provided in the `cborDiag` string within the same `input`. This
is only intended for human consumption and has no bearing on the test logic.)

* If the `external` key is present, load it and Base16 decode it into a byte
buffer. This represents what COSE calls "Externally Supplied Data".

* If the `detachedPayload` key is present, load it and Base16 decode it into a
byte buffer. This is to be used when constructing the COSE `Sig_Structure` in
lieu of the `nil` payload in the `taggedCOSESign1`.

* Call the verify API exposed by your implementation passing the key, the
signature scheme, the tagged COSE_Sign1 data, and the optional externally
supplied data.

* Check that the result is compatible with the boolean carried in the
`shouldVerify` key. If so, set `Result` to `"pass"` in the `TestCaseOutput`
payload for this test case. Otherwise set `"fail"`.

### Sign

This section describes how the GlueCOSE test writer consumes a `Sign1_sign`
object to create the test driver for signing data into COSE Sign1 payloads.

All the keys in the remainder of this section are relative to the
`Sign1_sign` object associated with the `"sign1::sign"` key.

There are two types of platforms / implementations considered here:

1. Those that expose an interface to the PRNG
1. Those that don't

The first type MUST use a "zero reader" (e.g., `/dev/zero` on UNIX-like OSes) as
PRNG to make the randomized tests deterministic, and shall implement the
[deterministic](#deterministic) version of the test.

For the second type, an [alternative](#non-deterministic) to the deterministic
test is specified.

The assumption is that the sign API exposed by the implementation under test
will use the fields in the `Sign1_sign` object to construct its input. However,
we assume that implementations will vary the way in which they consume the test
case input data: for example, one could assemble all parameters in one single
object before passing it to the sign interface, another could supply each piece
separately, etc. Therefore, here we will only describe the semantics of the
`Sign1_sign` object fields and let each implementation deal with the details of
deriving their input parameter(s). An implementation will then need to go
through the steps specified in [Section 4.4 of
RFC9052](https://www.rfc-editor.org/authors/rfc9052.html#section-4.4) to produce
the signature and the resulting COSE_Sign1 object.

* The `payload` key is a Base16 encoded string corresponding to the payload to
be signed. If the `detached` key is `true`, the resulting COSE_Sign1 will
have a `nil` payload. Otherwise (`detached` key missing or `true`), the
resulting COSE_Sign1 has it as its value.

* If present, the `protectedHeaders` key contains the protected headers as a
serialized CBOR map Base16 encoded.

* If present, the `unprotectedHeaders` key contains the unprotected headers as a
serialized CBOR map Base16 encoded.

* If present, the `external` key contains the base16 encoded string with any
externally supplied data.

* The `tbsHex` key is a Base16 encoded string corresponding to the resulting
COSE `Sig_Structure` canonically serialised as per [Section 9 of
RFC9052](https://www.rfc-editor.org/authors/rfc9052.html#section-9). This is
an intermediate value that is normally invisible to the API caller, therefore
it is not expected to be used directly by the test driver. It serves as an
aid for the developer.

* The `expectedOutput` key contains a Base16 encoded string corresponding to the
tagged (18) CBOR encoded COSE_Sign1 message.

#### Deterministic

It is expected that the output of the sign API is compared to the full value
contained in the `cborHex` field of the `expectedOutput`. If the two values
match, set `Result` to `"pass"` in the `TestCaseOutput` payload for this test
case. Otherwise set `"fail"`.

#### Non-deterministic

It is expected that the output of the sign API and the value contained in the
`cborHex` field of the `expectedOutput` are compared up the the 3rd entry of the
COSE_Sign1 array, i.e., excluding the 4th (signature) field. The
`fixedOutputLength` field contains the number of bytes of non-randomized output.
If the two values truncated to `fixedOutputLength` bytes match, set `Result` to
`"pass"` in the `TestCaseOutput` payload for this test case. Otherwise set
`"fail"`.

85 changes: 85 additions & 0 deletions gluecose-schema.cddl
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
start = TestCaseInput / TestCaseOutput

TestCaseInput = {
uuid: text ; unique identifier for the test case
title: text
description: text

key: JWK
alg: COSEAlgo

operation
}

operation //= ( "sign1::sign" => Sign1_sign )
operation //= ( "sign1::verify" => Sign1_verify )
; operation //= ( "sign::sign" => TODO )
; operation //= ( "sign::verify" => TODO )
; operation //= ( "encrypt::encrypt" => TODO )
; operation //= ( "encrypt::decrypt" => TODO )
; operation //= ( "encrypt0::encrypt" => TODO )
; operation //= ( "encrypt0::decrypt" => TODO )
; operation //= ( "mac::mac" => TODO )
; operation //= ( "mac::verify" => TODO )
; operation //= ( "mac0::mac" => TODO )
; operation //= ( "mac0::verify" => TODO )

TODO = "todo"

Sign1_sign = {
payload: HexString
thomas-fossati marked this conversation as resolved.
Show resolved Hide resolved
? protectedHeaders: CBORBlob
? unprotectedHeaders: CBORBlob
? external: HexString ; externally supplied data
? detached: bool .default false
tbsHex: CBORBlob ; CBOR encoded Sig_structure
expectedOutput: CBORBlob
fixedOutputLength: uint ; used only for non-deterministic testers
}

Sign1_verify = {
taggedCOSESign1: CBORBlob ; tagged COSE Sign1
? external: HexString ; optional external data
thomas-fossati marked this conversation as resolved.
Show resolved Hide resolved
; optional detached payload (only present if payload==nil in taggedCOSESign1)
? detachedPayload: HexString
shouldVerify: bool ; whether a successful validation is expected
}

CBORBlob = {
cborHex: HexString ; base16 encoded byte buffer
? cborDiag: text ; optional string containing the CBOR diagnostic
; representation of cborHex
}

HexString = text .regexp "([a-f0-9]{2})+"

; See https://www.iana.org/assignments/cose/cose.xhtml#algorithms
COSEAlgo /= "ES256"
COSEAlgo /= "ES384"
; COSEAlgo /= TODO

JWK = {
+ text => text
}

TestCaseOutput = {
uuid: text ; unique identifier for the test case
implementation: Implementation
executionTs: text ; RFC3339 date-time
result: Result
}

Result /= "pass"
Result /= "skip"
Result /= "fail"

Implementation = {
name: text
version: text
author: [ + text ]
homepage: text
license: text
sourceLang: [ + text ]
? targetLang: [ + text ]
deterministic: bool
}
17 changes: 17 additions & 0 deletions misc/gocose-result-sign1-verify-0000.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"uuid": "66584A57-390B-4A52-B7B6-B7CA4FC4204F",
"implementation": {
"name": "go-cose",
"version": "0.0.1",
"author": [
"Greg Guthe",
"Contributors to the Veraison project"
],
"homepage": "https://github.com/veraison/go-cose",
"license": "MPL 2.0",
"sourceLang": [ "go" ],
"deterministic": true
},
"executionTs": "2022-03-09T14:10:42Z",
"result": "pass"
}
34 changes: 34 additions & 0 deletions sign1-sign-0000.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"uuid": "D55A49BD-53D9-42B1-9E76-E0CF2AD33E9D",
"title": "And another one...",
"description": "Second attempt at a Sign1 test case (sign)",
"key": {
"kty": "EC",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
"d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM"
},
"alg": "ES256",
"sign1::sign": {
"payload": "546869732069732074686520636f6e74656e742e",
"protectedHeaders": {
"cborHex": "a10126",
"cborDiag": "{1: -7}"
},
"unprotectedHeaders": {
"cborHex": "a104423131",
"cborDiag": "{4: h'3131'}"
},
"tbsHex": {
"cborHex": "846a5369676e61747572653143a101264c11aa22bb33cc44dd5500669954546869732069732074686520636f6e74656e742e",
"cborDiag": "['Signature1', h'A10126', h'11AA22BB33CC44DD55006699', h'546869732069732074686520636F6E74656E742E']"
},
"detached": false,
"expectedOutput": {
"cborHex": "d28443a10126a10442313154546869732069732074686520636f6e74656e742e58403a7487d9a528cb61dd8e99bd652c12577fc47d70ee5af2e703c420584f060fc7a8d61e4a35862b2b531a8447030ab966aeed8dd45ebc507c761431e349995770",
"cborDiag": "18([h'A10126', {4: h'3131'}, h'546869732069732074686520636F6E74656E742E', h'3A7487D9A528CB61DD8E99BD652C12577FC47D70EE5AF2E703C420584F060FC7A8D61E4A35862B2B531A8447030AB966AEED8DD45EBC507C761431E349995770'])"
},
"fixedOutputLength": 32
}
}
21 changes: 21 additions & 0 deletions sign1-verify-0000.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"uuid": "66584A57-390B-4A52-B7B6-B7CA4FC4204F",
"title": "Let's go",
"description": "First attempt at drafting a Sign1 test case",
"key": {
"kty": "EC",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
"d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM"
},
"alg": "ES256",
"sign1::verify": {
"taggedCOSESign1": {
"cborHex": "d28443a10126a10442313154546869732069732074686520636f6e74656e742e58403a7487d9a528cb61dd8e99bd652c12577fc47d70ee5af2e703c420584f060fc7a8d61e4a35862b2b531a8447030ab966aeed8dd45ebc507c761431e349995770",
"cborDiag": "18([h'A10126', {4: h'3131'}, h'546869732069732074686520636F6E74656E742E', h'3A7487D9A528CB61DD8E99BD652C12577FC47D70EE5AF2E703C420584F060FC7A8D61E4A35862B2B531A8447030AB966AEED8DD45EBC507C761431E349995770'])"
},
"external": "11aa22bb33cc44dd55006699",
"shouldVerify": true
}
}
13 changes: 13 additions & 0 deletions tools.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# cddl and curl are prerequisite
# fail hard if they are not found

cddl ?= $(shell command -v cddl)
ifeq ($(strip $(cddl)),)
$(error cddl not found. To install cddl: 'gem install cddl')
endif

mdspell ?= $(shell command -v mdspell)
ifeq ($(strip $(mdspell)),)
$(error mdspell not found. To install mdspell: 'npm i markdown-spellcheck -g')
endif