pax_global_header 0000666 0000000 0000000 00000000064 14551116371 0014516 g ustar 00root root 0000000 0000000 52 comment=98633907748013a171fc7663d83df75f5a4c96cf
golang-github-veraison-go-cose-1.2.1/ 0000775 0000000 0000000 00000000000 14551116371 0017424 5 ustar 00root root 0000000 0000000 golang-github-veraison-go-cose-1.2.1/.github/ 0000775 0000000 0000000 00000000000 14551116371 0020764 5 ustar 00root root 0000000 0000000 golang-github-veraison-go-cose-1.2.1/.github/.codecov.yml 0000664 0000000 0000000 00000000131 14551116371 0023202 0 ustar 00root root 0000000 0000000 coverage:
status:
patch: off
project:
default:
target: 89%
golang-github-veraison-go-cose-1.2.1/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 14551116371 0023147 5 ustar 00root root 0000000 0000000 golang-github-veraison-go-cose-1.2.1/.github/ISSUE_TEMPLATE/bug-or-issue.yaml 0000664 0000000 0000000 00000006306 14551116371 0026361 0 ustar 00root root 0000000 0000000 # Copyright The Veraison Go-COSE Authors.
# Licensed under the Mozilla Public License 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.mozilla.org/en-US/MPL/2.0/
# Permissions of this weak copyleft license are conditioned on making available
# source code of licensed files and modifications of those files under the same license
# (or in certain cases, one of the GNU licenses). Copyright and license notices must be preserved.
# Contributors provide an express grant of patent rights. However, a larger work using
# the licensed work may be distributed under different terms and without source code for
# files added in the larger work.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Original template copied from Notary Project https://github.com/notaryproject
name: π Bug or Issue
description: Something is not working as expected or not working at all! Report it here!
labels: [bug, triage]
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to fill out this issue report. π Please check existing issues first before continuing: https://github.com/veraison/go-cose/issues
- type: dropdown
id: area
validations:
required: true
attributes:
label: "What is the areas you experience the issue in?"
options:
- Go-COSE Library
- type: textarea
id: verbatim
validations:
required: true
attributes:
label: "What is not working as expected?"
description: "In your own words, describe what the issue is."
- type: textarea
id: expect
validations:
required: true
attributes:
label: "What did you expect to happen?"
description: "A clear and concise description of what you expected to happen."
- type: textarea
id: reproduce
validations:
required: true
attributes:
label: "How can we reproduce it?"
description: "Detailed steps to reproduce the behavior. Commands and their outputs are always helpful. If the bug is in a library, code snippets work as well."
- type: textarea
id: environment
validations:
required: true
attributes:
label: Describe your environment
description: "Installation method (e.g. wget, curl, brew, apt-get, yum, chocolate, MSI) if applicable / OS version / Shell type (e.g. zsh, bash, cmd.exe, Bash on Windows) / Golang version if applicable"
- type: textarea
id: version
validations:
required: true
attributes:
label: What is the version of your Go-COSE Library?
description: "For the libraries check the `go.mod` file."
- type: markdown
attributes:
value: |
If you want to contribute to this project, we will be happy to guide you through out contribution process especially when you already have a good proposal or understanding of how to fix this issue. Join us at https://veraison.zulipchat.com/.
golang-github-veraison-go-cose-1.2.1/.github/ISSUE_TEMPLATE/config.yml 0000664 0000000 0000000 00000002222 14551116371 0025135 0 ustar 00root root 0000000 0000000 # Copyright The Veraison Go-COSE Authors.
# Licensed under the Mozilla Public License 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.mozilla.org/en-US/MPL/2.0/
# Permissions of this weak copyleft license are conditioned on making available
# source code of licensed files and modifications of those files under the same license
# (or in certain cases, one of the GNU licenses). Copyright and license notices must be preserved.
# Contributors provide an express grant of patent rights. However, a larger work using
# the licensed work may be distributed under different terms and without source code for
# files added in the larger work.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Original template copied from Notary Project https://github.com/notaryproject
blank_issues_enabled: true
golang-github-veraison-go-cose-1.2.1/.github/ISSUE_TEMPLATE/feature-request.yaml 0000664 0000000 0000000 00000005454 14551116371 0027164 0 ustar 00root root 0000000 0000000 # Copyright The Veraison Go-COSE Authors.
# Licensed under the Mozilla Public License 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.mozilla.org/en-US/MPL/2.0/
# Permissions of this weak copyleft license are conditioned on making available
# source code of licensed files and modifications of those files under the same license
# (or in certain cases, one of the GNU licenses). Copyright and license notices must be preserved.
# Contributors provide an express grant of patent rights. However, a larger work using
# the licensed work may be distributed under different terms and without source code for
# files added in the larger work.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Original template copied from Notary Project https://github.com/notaryproject
name: π Feature Request
description: Suggest an idea for this project.
labels: [enhancement, triage]
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to suggest a useful feature for the project!
- type: dropdown
id: area
validations:
required: true
attributes:
label: "What is the areas you would like to add the new feature to?"
options:
- Go-COSE Library
- type: textarea
id: problem
validations:
required: true
attributes:
label: "Is your feature request related to a problem?"
description: "A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]"
- type: textarea
id: solution
validations:
required: true
attributes:
label: "What solution do you propose?"
description: "A clear and concise description of what you want to happen."
- type: textarea
id: alternatives
validations:
required: true
attributes:
label: "What alternatives have you considered?"
description: "A clear and concise description of any alternative solutions or features you've considered."
- type: textarea
id: context
validations:
required: false
attributes:
label: "Any additional context?"
description: "Add any other context or screenshots about the feature request here."
- type: markdown
attributes:
value: |
If you want to contribute to this project, we will be happy to guide you through out contribution process especially when you already have a good proposal or understanding of how to improve the functionality. Join us at https://veraison.zulipchat.com/.
golang-github-veraison-go-cose-1.2.1/.github/workflows/ 0000775 0000000 0000000 00000000000 14551116371 0023021 5 ustar 00root root 0000000 0000000 golang-github-veraison-go-cose-1.2.1/.github/workflows/ci.yml 0000664 0000000 0000000 00000001376 14551116371 0024146 0 ustar 00root root 0000000 0000000 # GitHub Actions - CI for Go to build & test. See ci-go-cover.yml and linters.yml for code coverage and linters.
# Taken from: https://github.com/fxamacker/cbor/workflows/ci.yml (thanks!)
name: ci
on: [push, pull_request]
jobs:
# Test on various OS with default Go version.
tests:
strategy:
fail-fast: false
matrix:
go-version: [1.18, 1.19]
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
check-latest: true
- name: Checkout code
uses: actions/checkout@v3
- name: Run tests
run: go test -race -v -coverprofile=coverage.txt .
- name: Upload coverage to codecov.io
uses: codecov/codecov-action@v3
golang-github-veraison-go-cose-1.2.1/.gitignore 0000664 0000000 0000000 00000000615 14551116371 0021416 0 ustar 00root root 0000000 0000000 # Binaries for programs and plugins
*.exe
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
# test deps
test/cose-rust/
test/cose-wg-examples/
# go deps
vendor/
# OSX fs files
.DS_Store
/cose-fuzz.zip
/workdir/
golang-github-veraison-go-cose-1.2.1/.golangci.yml 0000664 0000000 0000000 00000003035 14551116371 0022011 0 ustar 00root root 0000000 0000000 # Do not delete linter settings. Linters like gocritic can be enabled on the command line.
linters-settings:
dupl:
threshold: 100
funlen:
lines: 100
statements: 50
goconst:
min-len: 2
min-occurrences: 3
gocritic:
enabled-tags:
- diagnostic
- experimental
- opinionated
- performance
- style
disabled-checks:
- dupImport # https://github.com/go-critic/go-critic/issues/845
- ifElseChain
- octalLiteral
- paramTypeCombine
- whyNoLint
- wrapperFunc
gofmt:
simplify: false
goimports:
local-prefixes: github.com/fxamacker/cbor
golint:
min-confidence: 0
govet:
check-shadowing: true
lll:
line-length: 140
maligned:
suggest-new: true
misspell:
locale: US
linters:
disable-all: true
enable:
- deadcode
- errcheck
- goconst
- gocyclo
- gofmt
- goimports
- golint
- gosec
- govet
- ineffassign
- maligned
- misspell
- staticcheck
- structcheck
- typecheck
- unconvert
- unused
- varcheck
issues:
# max-issues-per-linter default is 50. Set to 0 to disable limit.
max-issues-per-linter: 0
# max-same-issues default is 3. Set to 0 to disable limit.
max-same-issues: 0
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
- path: _test\.go
linters:
- goconst
- dupl
- gomnd
- lll
- path: doc\.go
linters:
- goimports
- gomnd
- lll
golang-github-veraison-go-cose-1.2.1/CODEOWNERS 0000664 0000000 0000000 00000000477 14551116371 0021027 0 ustar 00root root 0000000 0000000 # To be kept in sync with: [community/OWNERS](https://github.com/veraison/community/blob/main/OWNERS)
# and the GitHub Team: [go-cose-maintainers](https://github.com/orgs/veraison/teams/go-cose-maintainers)
* henkbirkholz qmuntal roywill setrofim shizhMSFT simonfrost-arm SteveLasker thomas-fossati yogeshbdeshpande
golang-github-veraison-go-cose-1.2.1/LICENSE 0000664 0000000 0000000 00000040525 14551116371 0020437 0 ustar 00root root 0000000 0000000 Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
golang-github-veraison-go-cose-1.2.1/README.md 0000664 0000000 0000000 00000016432 14551116371 0020711 0 ustar 00root root 0000000 0000000 # go-cose
[](https://pkg.go.dev/github.com/veraison/go-cose)
[](https://github.com/veraison/go-cose/actions?query=workflow%3Aci)
[](https://codecov.io/gh/veraison/go-cose)
A golang library for the [COSE specification][cose-spec]
## Project Status
**Current Release**: [go-cose v1.0.0][current-release]
The project was *initially* forked from the upstream [mozilla-services/go-cose][mozilla-go-cose] project, however the Veraison and Mozilla maintainers have agreed to retire the mozilla-services/go-cose project and focus on [veraison/go-cose][veraison-go-cose] as the active project.
We thank the [Mozilla maintainers and contributors][mozilla-contributors] for their great work that formed the base of the [veraison/go-cose][veraison-go-cose] project.
## Community
The [veraison/go-cose](https://github.com/veraison/go-cose) project is an open source community effort.
You can reach the go-cose community via::
- [Mailing List](veraison-project@confidentialcomputing.io)
- Bi-weekly meetings: 08:00-09:00 Pacific
- [Zoom meeting link](https://us02web.zoom.us/j/81054434992?pwd=YjNBU21seU5VcGdtVXY3VHVjS251Zz09)
- [Calendar ics link](https://zoom.us/meeting/tZUtcu2srT8jE9YFubXn-lC9upuwUiiev52G/ics)
- [Meeting Notes](https://veraison.zulipchat.com/#narrow/stream/317999-go-cose-meetings)
- [Meeting Recordings](https://www.youtube.com/@go-cose-community3000)
Participation in the go-cose community is governed by the Veraison [CODE_OF_CONDUCT.md](https://github.com/veraison/.github/blob/main/CODE_OF_CONDUCT.md) and [GOVERNANCE.md](https://github.com/veraison/community/blob/main/GOVERNANCE.md)
## Code of Conduct
This project has adopted the [Contributor Covenant Code of Conduct](https://github.com/veraison/.github/blob/main/CODE_OF_CONDUCT.md).
## Installation
go-cose is compatible with modern Go releases in module mode, with Go installed:
```bash
go get github.com/veraison/go-cose
```
will resolve and add the package to the current development module, along with its dependencies.
Alternatively the same can be achieved if you use import in a package:
```go
import "github.com/veraison/go-cose"
```
and run `go get` without parameters.
Finally, to use the top-of-trunk version of this repo, use the following command:
```bash
go get github.com/veraison/go-cose@main
```
## Usage
### Signing and Verification
```go
import "github.com/veraison/go-cose"
```
Construct a new COSE_Sign1_Tagged message, then sign it using ECDSA w/ SHA-256 and finally marshal it. For example:
```go
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
_ "crypto/sha256"
"github.com/veraison/go-cose"
)
func SignP256(data []byte) ([]byte, error) {
// create a signer
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
signer, err := cose.NewSigner(cose.AlgorithmES256, privateKey)
if err != nil {
return nil, err
}
// create message header
headers := cose.Headers{
Protected: cose.ProtectedHeader{
cose.HeaderLabelAlgorithm: cose.AlgorithmES256,
},
}
// sign and marshal message
return cose.Sign1(rand.Reader, signer, headers, data, nil)
}
```
Verify a raw COSE_Sign1_Tagged message. For example:
```go
package main
import (
"crypto"
_ "crypto/sha256"
"github.com/veraison/go-cose"
)
func VerifyP256(publicKey crypto.PublicKey, sig []byte) error {
// create a verifier from a trusted private key
verifier, err := cose.NewVerifier(cose.AlgorithmES256, publicKey)
if err != nil {
return err
}
// create a sign message from a raw COSE_Sign1 payload
var msg cose.Sign1Message
if err = msg.UnmarshalCBOR(sig); err != nil {
return err
}
return msg.Verify(nil, verifier)
}
```
See [example_test.go](./example_test.go) for more examples.
#### Untagged Signing and Verification
Untagged COSE_Sign1 messages can be signed and verified as above, using
`cose.UntaggedSign1Message` instead of `cose.Sign1Message`.
### About hashing
`go-cose` does not import any hash package by its own to avoid linking unnecessary algorithms to the final binary.
It is the the responsibility of the `go-cose` user to make the necessary hash functions available at runtime, i.e.,
by using a blank import:
```go
import (
_ "crypto/sha256"
_ "crypto/sha512"
)
```
These are the required packages for each built-in cose.Algorithm:
- cose.AlgorithmPS256, cose.AlgorithmES256: `crypto/sha256`
- cose.AlgorithmPS384, cose.AlgorithmPS512, cose.AlgorithmES384, cose.AlgorithmES512: `crypto/sha512`
- cose.AlgorithmEd25519: none
## Features
### Signing and Verifying Objects
go-cose supports two different signature structures:
- [cose.Sign1Message](https://pkg.go.dev/github.com/veraison/go-cose#Sign1Message) implements [COSE_Sign1](https://datatracker.ietf.org/doc/html/rfc8152#section-4.2).
- [cose.SignMessage](https://pkg.go.dev/github.com/veraison/go-cose#SignMessage) implements [COSE_Sign](https://datatracker.ietf.org/doc/html/rfc8152#section-4.1).
> :warning: The COSE_Sign API is currently **EXPERIMENTAL** and may be changed or removed in a later release. In addition, the amount of functional and security testing it has received so far is significantly lower than the COSE_Sign1 API.
### Built-in Algorithms
go-cose has built-in supports the following algorithms:
- PS{256,384,512}: RSASSA-PSS w/ SHA as defined in RFC 8230.
- ES{256,384,512}: ECDSA w/ SHA as defined in RFC 8152.
- Ed25519: PureEdDSA as defined in RFC 8152.
### Custom Algorithms
The supported algorithms can be extended at runtime by using [cose.RegisterAlgorithm](https://pkg.go.dev/github.com/veraison/go-cose#RegisterAlgorithm).
[API docs](https://pkg.go.dev/github.com/veraison/go-cose)
### Integer Ranges
CBOR supports integers in the range [-264, -1] βͺ [0, 264 - 1].
This does not map onto a single Go integer type.
`go-cose` uses `int64` to encompass both positive and negative values to keep data sizes smaller and easy to use.
The main effect is that integer label values in the [-264, -263 - 1] and the [263, 264 - 1] ranges, which are nominally valid
per RFC 8152, are rejected by the go-cose library.
### Conformance Tests
`go-cose` runs the [GlueCOSE](https://github.com/gluecose/test-vectors) test suite on every local `go test` execution.
These are also executed on every CI job.
### Fuzz Tests
`go-cose` implements several fuzz tests using [Go's native fuzzing](https://go.dev/doc/fuzz).
Fuzzing requires Go 1.18 or higher, and can be executed as follows:
```bash
go test -fuzz=FuzzSign1
```
### Security Reviews
`go-cose` undergoes periodic security review. The security review reports are located [here](./reports)
[cose-spec]: https://datatracker.ietf.org/doc/rfc9052/
[mozilla-contributors]: https://github.com/mozilla-services/go-cose/graphs/contributors
[mozilla-go-cose]: http://github.com/mozilla-services/go-cose
[veraison-go-cose]: https://github.com/veraison/go-cose
[current-release]: https://github.com/veraison/go-cose/releases/tag/v1.0.0
golang-github-veraison-go-cose-1.2.1/SECURITY.md 0000664 0000000 0000000 00000012442 14551116371 0021220 0 ustar 00root root 0000000 0000000 # Security Process and Policy
This document provides the details on the veraison/go-cose security policy and details the processes surrounding security handling.
## Supported Versions
The current stable release of [go-cose][go-cose] is [v1.0.0][v1.0.0-release]. Please upgrade to [v1.0.0][v1.0.0-release] if you are using a pre-release version.
| Version | Supported |
| ------- | ------------------ |
| [v1.0.0][v1.0.0-release] | Yes |
## Report A Vulnerability
Weβre extremely grateful for security researchers and users who report vulnerabilities
to the [veraison/go-cose][go-cose] community. All reports are thoroughly investigated by a set of [veraison/go-cose maintainers][go-cose-maintainers].
To make a report please email the private security list at go-cose-security@googlegroups.com with details using the following template:
### Reporting Template
```console
[TO:]: go-cose-security@googlegroups.com
[SUBJECT]: go-cose Security Notification
[BODY]:
Release: v1.0.0
Summary:
A quick summary of the issue
Impact:
Details on how to reproduce the security issue.
Contact:
Information on who to contact for additional information
```
### When To Send a Report
You think you have found a vulnerability in the [veraison/go-cose][go-cose] project.
### Security Vulnerability Response
Each report will be reviewed and receipt acknowledged in a timely manner. This will set off the security review process detailed below.
Any vulnerability information shared with the security team stays within the [veraison/go-cose][go-cose] project and will not be shared with others unless it is necessary to fix the issue. Information is shared only on a need to know basis.
We ask that vulnerability reporter(s) act in good faith by not disclosing the issue to others. And we strive to act in good faith by acting swiftly, and by justly crediting the vulnerability reporter(s) in writing (see [Public Disclosure](#public-disclosure)).
As the security issue moves through triage, identification, and release the reporter of the security vulnerability will be notified. Additional questions about the vulnerability map may also be asked from the reporter.
### Public Disclosure
A public disclosure of security vulnerabilities is released alongside release updates or details that fix the vulnerability. We try to fully disclose vulnerabilities once a mitigation strategy is available. Our goal is to perform a release and public disclosure quickly and in a timetable that works well for users. For example, a release may be ready on a Friday but for the sake of users may be delayed to a Monday.
When needed, CVEs will be assigned to vulnerabilities. Due to the process and time it takes to obtain a CVE ID, disclosures will happen first. Once the disclosure is public the process will begin to obtain a CVE ID. Once the ID has been assigned the disclosure will be updated.
If the vulnerability reporter would like their name and details shared as part of the disclosure process we are happy to. We will ask permission and for the way the reporter would like to be identified. We appreciate vulnerability reports and would like to credit reporters if they would like the credit.
## Security Team Membership
The security team is made up of a subset of the Veraison project maintainers who are willing and able to respond to vulnerability reports.
### Responsibilities
* Members MUST be active project maintainers on active (non-deprecated) Veraison projects as defined in the [governance](https://github.com/veraison/community/blob/main/GOVERNANCE.md)
* Members SHOULD engage in each reported vulnerability, at a minimum to make sure it is being handled
* Members MUST keep the vulnerability details private and only share on a need to know basis
### Membership
New members are required to be active maintainers of Veraison projects who are willing to perform the responsibilities outlined above. The security team is a subset of the maintainers across Veraison sub-projects. Members can step down at any time and may join at any time.
If at any time a security team member is found to be no longer an active maintainer on active Veraison sub-projects, this individual will be removed from the security team.
## Patch and Release Team
When a vulnerability comes in and is acknowledged, a team - including maintainers of the Veraison project affected - will be assembled to patch the vulnerability, release an update, and publish the vulnerability disclosure. This may expand beyond the security team as needed but will stay within the pool of Veraison project maintainers.
## Disclosures
Vulnerability disclosures are published to [security-advisories][security-advisories]. The disclosures will contain an overview, details about the vulnerability, a fix for the vulnerability that will typically be an update, and optionally a workaround if one is available.
Disclosures will be published on the same day as a release fixing the vulnerability after the release is published.
[go-cose]: https://github.com/veraison/go-cose
[security-advisories]: https://github.com/veraison/go-cose/security/advisories
[v1.0.0-release]: https://github.com/veraison/go-cose/releases/tag/v1.0.0
[go-cose-maintainers]: https://github.com/veraison/community/blob/main/OWNERS
golang-github-veraison-go-cose-1.2.1/algorithm.go 0000664 0000000 0000000 00000006605 14551116371 0021750 0 ustar 00root root 0000000 0000000 package cose
import (
"crypto"
"fmt"
"strconv"
)
// Algorithms supported by this library.
//
// When using an algorithm which requires hashing,
// make sure the associated hash function is linked to the binary.
const (
// RSASSA-PSS w/ SHA-256 by RFC 8230.
// Requires an available crypto.SHA256.
AlgorithmPS256 Algorithm = -37
// RSASSA-PSS w/ SHA-384 by RFC 8230.
// Requires an available crypto.SHA384.
AlgorithmPS384 Algorithm = -38
// RSASSA-PSS w/ SHA-512 by RFC 8230.
// Requires an available crypto.SHA512.
AlgorithmPS512 Algorithm = -39
// ECDSA w/ SHA-256 by RFC 8152.
// Requires an available crypto.SHA256.
AlgorithmES256 Algorithm = -7
// ECDSA w/ SHA-384 by RFC 8152.
// Requires an available crypto.SHA384.
AlgorithmES384 Algorithm = -35
// ECDSA w/ SHA-512 by RFC 8152.
// Requires an available crypto.SHA512.
AlgorithmES512 Algorithm = -36
// PureEdDSA by RFC 8152.
AlgorithmEd25519 Algorithm = -8
// An invalid/unrecognised algorithm.
AlgorithmInvalid Algorithm = 0
)
// Algorithm represents an IANA algorithm entry in the COSE Algorithms registry.
//
// # See Also
//
// COSE Algorithms: https://www.iana.org/assignments/cose/cose.xhtml#algorithms
//
// RFC 8152 16.4: https://datatracker.ietf.org/doc/html/rfc8152#section-16.4
type Algorithm int64
// String returns the name of the algorithm
func (a Algorithm) String() string {
switch a {
case AlgorithmPS256:
return "PS256"
case AlgorithmPS384:
return "PS384"
case AlgorithmPS512:
return "PS512"
case AlgorithmES256:
return "ES256"
case AlgorithmES384:
return "ES384"
case AlgorithmES512:
return "ES512"
case AlgorithmEd25519:
// As stated in RFC 8152 8.2, only the pure EdDSA version is used for
// COSE.
return "EdDSA"
default:
return "unknown algorithm value " + strconv.Itoa(int(a))
}
}
// MarshalCBOR marshals the Algorithm as a CBOR int.
func (a Algorithm) MarshalCBOR() ([]byte, error) {
return encMode.Marshal(int64(a))
}
// UnmarshalCBOR populates the Algorithm from the provided CBOR value (must be
// int or tstr).
func (a *Algorithm) UnmarshalCBOR(data []byte) error {
var raw intOrStr
if err := raw.UnmarshalCBOR(data); err != nil {
return fmt.Errorf("invalid algorithm value: %w", err)
}
if raw.IsString() {
v := algorithmFromString(raw.String())
if v == AlgorithmInvalid {
return fmt.Errorf("unknown algorithm value %q", raw.String())
}
*a = v
} else {
v := raw.Int()
*a = Algorithm(v)
}
return nil
}
// hashFunc returns the hash associated with the algorithm supported by this
// library.
func (a Algorithm) hashFunc() crypto.Hash {
switch a {
case AlgorithmPS256, AlgorithmES256:
return crypto.SHA256
case AlgorithmPS384, AlgorithmES384:
return crypto.SHA384
case AlgorithmPS512, AlgorithmES512:
return crypto.SHA512
default:
return 0
}
}
// computeHash computes the digest using the hash specified in the algorithm.
func (a Algorithm) computeHash(data []byte) ([]byte, error) {
return computeHash(a.hashFunc(), data)
}
// computeHash computes the digest using the given hash.
func computeHash(h crypto.Hash, data []byte) ([]byte, error) {
if !h.Available() {
return nil, ErrUnavailableHashFunc
}
hh := h.New()
if _, err := hh.Write(data); err != nil {
return nil, err
}
return hh.Sum(nil), nil
}
// NOTE: there are currently no registered string values for an algorithm.
func algorithmFromString(v string) Algorithm {
return AlgorithmInvalid
}
golang-github-veraison-go-cose-1.2.1/algorithm_test.go 0000664 0000000 0000000 00000010570 14551116371 0023003 0 ustar 00root root 0000000 0000000 package cose
import (
"crypto"
"crypto/sha256"
"hash"
"io"
"reflect"
"testing"
)
func TestAlgorithm_String(t *testing.T) {
// run tests
tests := []struct {
name string
alg Algorithm
want string
}{
{
name: "unknown algorithm",
alg: 0,
want: "unknown algorithm value 0",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.alg.String(); got != tt.want {
t.Errorf("Algorithm.String() = %v, want %v", got, tt.want)
}
})
}
}
func TestAlgorithm_CBOR(t *testing.T) {
tvs2 := []struct {
Data []byte
ExpectedError string
}{
{[]byte{0x63, 0x66, 0x6f, 0x6f}, "unknown algorithm value \"foo\""},
{[]byte{0x40}, "invalid algorithm value: must be int or string, found []uint8"},
}
for _, tv := range tvs2 {
var a Algorithm
err := a.UnmarshalCBOR(tv.Data)
assertEqualError(t, err, tv.ExpectedError)
}
}
func TestAlgorithm_computeHash(t *testing.T) {
// run tests
data := []byte("hello world")
tests := []struct {
name string
alg Algorithm
want []byte
wantErr error
}{
{
name: "PS256",
alg: AlgorithmPS256,
want: []byte{
0xb9, 0x4d, 0x27, 0xb9, 0x93, 0x4d, 0x3e, 0x08, 0xa5, 0x2e, 0x52, 0xd7, 0xda, 0x7d, 0xab, 0xfa,
0xc4, 0x84, 0xef, 0xe3, 0x7a, 0x53, 0x80, 0xee, 0x90, 0x88, 0xf7, 0xac, 0xe2, 0xef, 0xcd, 0xe9,
},
},
{
name: "PS384",
alg: AlgorithmPS384,
want: []byte{
0xfd, 0xbd, 0x8e, 0x75, 0xa6, 0x7f, 0x29, 0xf7, 0x01, 0xa4, 0xe0, 0x40, 0x38, 0x5e, 0x2e, 0x23,
0x98, 0x63, 0x03, 0xea, 0x10, 0x23, 0x92, 0x11, 0xaf, 0x90, 0x7f, 0xcb, 0xb8, 0x35, 0x78, 0xb3,
0xe4, 0x17, 0xcb, 0x71, 0xce, 0x64, 0x6e, 0xfd, 0x08, 0x19, 0xdd, 0x8c, 0x08, 0x8d, 0xe1, 0xbd,
},
},
{
name: "PS512",
alg: AlgorithmPS512,
want: []byte{
0x30, 0x9e, 0xcc, 0x48, 0x9c, 0x12, 0xd6, 0xeb, 0x4c, 0xc4, 0x0f, 0x50, 0xc9, 0x02, 0xf2, 0xb4,
0xd0, 0xed, 0x77, 0xee, 0x51, 0x1a, 0x7c, 0x7a, 0x9b, 0xcd, 0x3c, 0xa8, 0x6d, 0x4c, 0xd8, 0x6f,
0x98, 0x9d, 0xd3, 0x5b, 0xc5, 0xff, 0x49, 0x96, 0x70, 0xda, 0x34, 0x25, 0x5b, 0x45, 0xb0, 0xcf,
0xd8, 0x30, 0xe8, 0x1f, 0x60, 0x5d, 0xcf, 0x7d, 0xc5, 0x54, 0x2e, 0x93, 0xae, 0x9c, 0xd7, 0x6f,
},
},
{
name: "ES256",
alg: AlgorithmES256,
want: []byte{
0xb9, 0x4d, 0x27, 0xb9, 0x93, 0x4d, 0x3e, 0x08, 0xa5, 0x2e, 0x52, 0xd7, 0xda, 0x7d, 0xab, 0xfa,
0xc4, 0x84, 0xef, 0xe3, 0x7a, 0x53, 0x80, 0xee, 0x90, 0x88, 0xf7, 0xac, 0xe2, 0xef, 0xcd, 0xe9,
},
},
{
name: "ES384",
alg: AlgorithmES384,
want: []byte{
0xfd, 0xbd, 0x8e, 0x75, 0xa6, 0x7f, 0x29, 0xf7, 0x01, 0xa4, 0xe0, 0x40, 0x38, 0x5e, 0x2e, 0x23,
0x98, 0x63, 0x03, 0xea, 0x10, 0x23, 0x92, 0x11, 0xaf, 0x90, 0x7f, 0xcb, 0xb8, 0x35, 0x78, 0xb3,
0xe4, 0x17, 0xcb, 0x71, 0xce, 0x64, 0x6e, 0xfd, 0x08, 0x19, 0xdd, 0x8c, 0x08, 0x8d, 0xe1, 0xbd,
},
},
{
name: "ES512",
alg: AlgorithmES512,
want: []byte{
0x30, 0x9e, 0xcc, 0x48, 0x9c, 0x12, 0xd6, 0xeb, 0x4c, 0xc4, 0x0f, 0x50, 0xc9, 0x02, 0xf2, 0xb4,
0xd0, 0xed, 0x77, 0xee, 0x51, 0x1a, 0x7c, 0x7a, 0x9b, 0xcd, 0x3c, 0xa8, 0x6d, 0x4c, 0xd8, 0x6f,
0x98, 0x9d, 0xd3, 0x5b, 0xc5, 0xff, 0x49, 0x96, 0x70, 0xda, 0x34, 0x25, 0x5b, 0x45, 0xb0, 0xcf,
0xd8, 0x30, 0xe8, 0x1f, 0x60, 0x5d, 0xcf, 0x7d, 0xc5, 0x54, 0x2e, 0x93, 0xae, 0x9c, 0xd7, 0x6f,
},
},
{
name: "Ed25519",
alg: AlgorithmEd25519,
wantErr: ErrUnavailableHashFunc,
},
{
name: "unknown algorithm",
alg: 0,
wantErr: ErrUnavailableHashFunc,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.alg.computeHash(data)
if err != tt.wantErr {
t.Errorf("Algorithm.computeHash() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Algorithm.computeHash() = %v, want %v", got, tt.want)
}
})
}
}
type badHash struct{}
func badHashNew() hash.Hash {
return &badHash{}
}
func (*badHash) Write(p []byte) (n int, err error) {
return 0, io.EOF
}
func (*badHash) Sum(b []byte) []byte {
return b
}
func (*badHash) Reset() {}
func (*badHash) Size() int {
return 0
}
func (*badHash) BlockSize() int {
return 0
}
func Test_computeHash(t *testing.T) {
crypto.RegisterHash(crypto.SHA256, badHashNew)
defer crypto.RegisterHash(crypto.SHA256, sha256.New)
_, err := computeHash(crypto.SHA256, nil)
if err != io.EOF {
t.Fatalf("computeHash() error = %v, wantErr %v", err, io.EOF)
}
}
golang-github-veraison-go-cose-1.2.1/bench_test.go 0000664 0000000 0000000 00000003335 14551116371 0022075 0 ustar 00root root 0000000 0000000 package cose_test
import (
"io"
"testing"
"github.com/veraison/go-cose"
)
func newSign1Message() *cose.Sign1Message {
return &cose.Sign1Message{
Headers: cose.Headers{
Protected: cose.ProtectedHeader{
cose.HeaderLabelAlgorithm: cose.AlgorithmES256,
},
Unprotected: cose.UnprotectedHeader{
cose.HeaderLabelKeyID: []byte{0x01},
},
},
Payload: make([]byte, 100),
Signature: make([]byte, 32),
}
}
type noSigner struct{}
func (noSigner) Algorithm() cose.Algorithm {
return cose.AlgorithmES256
}
func (noSigner) Sign(_ io.Reader, digest []byte) ([]byte, error) {
return digest, nil
}
func (noSigner) Verify(_, _ []byte) error {
return nil
}
func BenchmarkSign1Message_MarshalCBOR(b *testing.B) {
msg := newSign1Message()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := msg.MarshalCBOR()
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkSign1Message_UnmarshalCBOR(b *testing.B) {
data, err := newSign1Message().MarshalCBOR()
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var m cose.Sign1Message
err = m.UnmarshalCBOR(data)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkSign1Message_Sign(b *testing.B) {
msg := newSign1Message()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
msg.Signature = nil
err := msg.Sign(zeroSource{}, nil, noSigner{})
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkSign1Message_Verify(b *testing.B) {
msg := newSign1Message()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
err := msg.Verify(nil, noSigner{})
if err != nil {
b.Fatal(err)
}
}
}
golang-github-veraison-go-cose-1.2.1/cbor.go 0000664 0000000 0000000 00000006341 14551116371 0020704 0 ustar 00root root 0000000 0000000 package cose
import (
"bytes"
"errors"
"io"
"github.com/fxamacker/cbor/v2"
)
// CBOR Tags for COSE signatures registered in the IANA "CBOR Tags" registry.
//
// Reference: https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml#tags
const (
CBORTagSignMessage = 98
CBORTagSign1Message = 18
)
// Pre-configured modes for CBOR encoding and decoding.
var (
encMode cbor.EncMode
decMode cbor.DecMode
decModeWithTagsForbidden cbor.DecMode
)
func init() {
var err error
// init encode mode
encOpts := cbor.EncOptions{
Sort: cbor.SortCoreDeterministic, // sort map keys
IndefLength: cbor.IndefLengthForbidden, // no streaming
}
encMode, err = encOpts.EncMode()
if err != nil {
panic(err)
}
// init decode mode
decOpts := cbor.DecOptions{
DupMapKey: cbor.DupMapKeyEnforcedAPF, // duplicated key not allowed
IndefLength: cbor.IndefLengthForbidden, // no streaming
IntDec: cbor.IntDecConvertSigned, // decode CBOR uint/int to Go int64
}
decMode, err = decOpts.DecMode()
if err != nil {
panic(err)
}
decOpts.TagsMd = cbor.TagsForbidden
decModeWithTagsForbidden, err = decOpts.DecMode()
if err != nil {
panic(err)
}
}
// byteString represents a "bstr / nil" type.
type byteString []byte
// UnmarshalCBOR decodes data into a "bstr / nil" type.
// It also ensures the data is of major type 2 since []byte can be alternatively
// interpreted as an array of bytes.
//
// Note: `github.com/fxamacker/cbor/v2` considers the primitive value
// `undefined` (major type 7, value 23) as nil, which is not recognized by COSE.
//
// Related Code: https://github.com/fxamacker/cbor/blob/v2.4.0/decode.go#L709
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-1.3
func (s *byteString) UnmarshalCBOR(data []byte) error {
if s == nil {
return errors.New("cbor: UnmarshalCBOR on nil byteString pointer")
}
if len(data) == 0 {
return io.EOF // same error as returned by cbor.Unmarshal()
}
if bytes.Equal(data, []byte{0xf6}) {
*s = nil
return nil
}
if data[0]>>5 != 2 { // major type 2: bstr
return errors.New("cbor: require bstr type")
}
return decModeWithTagsForbidden.Unmarshal(data, (*[]byte)(s))
}
// deterministicBinaryString converts a bstr into the deterministic encoding.
//
// Reference: https://www.rfc-editor.org/rfc/rfc9052.html#section-9
func deterministicBinaryString(data cbor.RawMessage) (cbor.RawMessage, error) {
if len(data) == 0 {
return nil, io.EOF
}
if data[0]>>5 != 2 { // major type 2: bstr
return nil, errors.New("cbor: require bstr type")
}
// fast path: return immediately if bstr is already deterministic
if err := decModeWithTagsForbidden.Valid(data); err != nil {
return nil, err
}
ai := data[0] & 0x1f
if ai < 24 {
return data, nil
}
switch ai {
case 24:
if data[1] >= 24 {
return data, nil
}
case 25:
if data[1] != 0 {
return data, nil
}
case 26:
if data[1] != 0 || data[2] != 0 {
return data, nil
}
case 27:
if data[1] != 0 || data[2] != 0 || data[3] != 0 || data[4] != 0 {
return data, nil
}
}
// slow path: convert by re-encoding
// error checking is not required since `data` has been validataed
var s []byte
_ = decModeWithTagsForbidden.Unmarshal(data, &s)
return encMode.Marshal(s)
}
golang-github-veraison-go-cose-1.2.1/cbor_test.go 0000664 0000000 0000000 00000011416 14551116371 0021742 0 ustar 00root root 0000000 0000000 package cose
import (
"bytes"
"reflect"
"testing"
"github.com/fxamacker/cbor/v2"
)
func Test_byteString_UnmarshalCBOR(t *testing.T) {
// test nil pointer
t.Run("nil byteString pointer", func(t *testing.T) {
var s *byteString
data := []byte{0x40}
if err := s.UnmarshalCBOR(data); err == nil {
t.Errorf("want error on nil *byteString")
}
})
// test others
tests := []struct {
name string
data []byte
want byteString
wantErr string
}{
{
name: "valid string",
data: []byte{0x43, 0x66, 0x6f, 0x6f},
want: []byte{0x66, 0x6f, 0x6f},
},
{
name: "empty string",
data: []byte{0x40},
want: []byte{},
},
{
name: "nil string",
data: []byte{0xf6},
want: nil,
},
{
name: "undefined string",
data: []byte{0xf7},
wantErr: "cbor: require bstr type",
},
{
name: "nil CBOR data",
data: nil,
wantErr: "EOF",
},
{
name: "empty CBOR data",
data: []byte{},
wantErr: "EOF",
},
{
name: "tagged string",
data: []byte{0xc2, 0x40},
wantErr: "cbor: require bstr type",
},
{
name: "array of bytes", // issue #46
data: []byte{0x82, 0x00, 0x1},
wantErr: "cbor: require bstr type",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var got byteString
err := got.UnmarshalCBOR(tt.data)
if err != nil && (err.Error() != tt.wantErr) {
t.Errorf("byteString.UnmarshalCBOR() error = %v, wantErr %v", err, tt.wantErr)
} else if err == nil && (tt.wantErr != "") {
t.Errorf("byteString.UnmarshalCBOR() error = %v, wantErr %v", err, tt.wantErr)
}
if !bytes.Equal(got, tt.want) {
t.Errorf("byteString.UnmarshalCBOR() = %v, want %v", got, tt.want)
}
})
}
}
func Test_deterministicBinaryString(t *testing.T) {
gen := func(initial []byte, size int) []byte {
data := make([]byte, size+len(initial))
copy(data, initial)
return data
}
tests := []struct {
name string
data cbor.RawMessage
want cbor.RawMessage
wantErr bool
}{
{
name: "empty input",
data: nil,
wantErr: true,
},
{
name: "not bstr",
data: []byte{0x00},
wantErr: true,
},
{
name: "short length",
data: gen([]byte{0x57}, 23),
want: gen([]byte{0x57}, 23),
},
{
name: "optimal uint8 length",
data: gen([]byte{0x58, 0x18}, 24),
want: gen([]byte{0x58, 0x18}, 24),
},
{
name: "non-optimal uint8 length",
data: gen([]byte{0x58, 0x17}, 23),
want: gen([]byte{0x57}, 23),
},
{
name: "optimal uint16 length",
data: gen([]byte{0x59, 0x01, 0x00}, 256),
want: gen([]byte{0x59, 0x01, 0x00}, 256),
},
{
name: "non-optimal uint16 length, target short",
data: gen([]byte{0x59, 0x00, 0x17}, 23),
want: gen([]byte{0x57}, 23),
},
{
name: "non-optimal uint16 length, target uint8",
data: gen([]byte{0x59, 0x00, 0x18}, 24),
want: gen([]byte{0x58, 0x18}, 24),
},
{
name: "optimal uint32 length",
data: gen([]byte{0x5a, 0x00, 0x01, 0x00, 0x00}, 65536),
want: gen([]byte{0x5a, 0x00, 0x01, 0x00, 0x00}, 65536),
},
{
name: "non-optimal uint32 length, target short",
data: gen([]byte{0x5a, 0x00, 0x00, 0x00, 0x17}, 23),
want: gen([]byte{0x57}, 23),
},
{
name: "non-optimal uint32 length, target uint8",
data: gen([]byte{0x5a, 0x00, 0x00, 0x00, 0x18}, 24),
want: gen([]byte{0x58, 0x18}, 24),
},
{
name: "non-optimal uint32 length, target uint16",
data: gen([]byte{0x5a, 0x00, 0x00, 0x01, 0x00}, 256),
want: gen([]byte{0x59, 0x01, 0x00}, 256),
},
{
name: "non-optimal uint64 length, target short",
data: gen([]byte{0x5b,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x17,
}, 23),
want: gen([]byte{0x57}, 23),
},
{
name: "non-optimal uint64 length, target uint8",
data: gen([]byte{0x5b,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18,
}, 24),
want: gen([]byte{0x58, 0x18}, 24),
},
{
name: "non-optimal uint64 length, target uint16",
data: gen([]byte{0x5b,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00,
}, 256),
want: gen([]byte{0x59, 0x01, 0x00}, 256),
},
{
name: "non-optimal uint64 length, target uint32",
data: gen([]byte{0x5b,
0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
}, 65536),
want: gen([]byte{0x5a, 0x00, 0x01, 0x00, 0x00}, 65536),
},
{
name: "early EOF",
data: gen([]byte{0x5b,
0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
}, 42),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := deterministicBinaryString(tt.data)
if (err != nil) != tt.wantErr {
t.Errorf("deterministicBinaryString() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("deterministicBinaryString() = %v, want %v", got, tt.want)
}
})
}
}
golang-github-veraison-go-cose-1.2.1/common.go 0000664 0000000 0000000 00000003377 14551116371 0021255 0 ustar 00root root 0000000 0000000 package cose
import (
"errors"
"fmt"
)
// intOrStr is a value that can be either an int or a tstr when serialized to
// CBOR.
type intOrStr struct {
intVal int64
strVal string
isString bool
}
func newIntOrStr(v interface{}) *intOrStr {
var ios intOrStr
if err := ios.Set(v); err != nil {
return nil
}
return &ios
}
func (ios intOrStr) Int() int64 {
return ios.intVal
}
func (ios intOrStr) String() string {
if ios.IsString() {
return ios.strVal
}
return fmt.Sprint(ios.intVal)
}
func (ios intOrStr) IsInt() bool {
return !ios.isString
}
func (ios intOrStr) IsString() bool {
return ios.isString
}
func (ios intOrStr) Value() interface{} {
if ios.IsInt() {
return ios.intVal
}
return ios.strVal
}
func (ios *intOrStr) Set(v interface{}) error {
switch t := v.(type) {
case int64:
ios.intVal = t
ios.strVal = ""
ios.isString = false
case int:
ios.intVal = int64(t)
ios.strVal = ""
ios.isString = false
case string:
ios.strVal = t
ios.intVal = 0
ios.isString = true
default:
return fmt.Errorf("must be int or string, found %T", t)
}
return nil
}
// MarshalCBOR returns the encoded CBOR representation of the intOrString, as
// either int or tstr, depending on the value. If no value has been set,
// intOrStr is encoded as a zero-length tstr.
func (ios intOrStr) MarshalCBOR() ([]byte, error) {
if ios.IsInt() {
return encMode.Marshal(ios.intVal)
}
return encMode.Marshal(ios.strVal)
}
// UnmarshalCBOR unmarshals the provided CBOR encoded data (must be an int,
// uint, or tstr).
func (ios *intOrStr) UnmarshalCBOR(data []byte) error {
if len(data) == 0 {
return errors.New("zero length buffer")
}
var val interface{}
if err := decMode.Unmarshal(data, &val); err != nil {
return err
}
return ios.Set(val)
}
golang-github-veraison-go-cose-1.2.1/common_test.go 0000664 0000000 0000000 00000006346 14551116371 0022313 0 ustar 00root root 0000000 0000000 package cose
import (
"bytes"
"reflect"
"testing"
"github.com/fxamacker/cbor/v2"
)
func Test_intOrStr(t *testing.T) {
ios := newIntOrStr(3)
assertEqual(t, true, ios.IsInt())
assertEqual(t, false, ios.IsString())
assertEqual(t, 3, ios.Int())
assertEqual(t, "3", ios.String())
ios = newIntOrStr("foo")
assertEqual(t, false, ios.IsInt())
assertEqual(t, true, ios.IsString())
assertEqual(t, 0, ios.Int())
assertEqual(t, "foo", ios.String())
ios = newIntOrStr(3.5)
if ios != nil {
t.Errorf("Expected nil, got %v", ios)
}
}
func Test_intOrStr_CBOR(t *testing.T) {
ios := newIntOrStr(3)
data, err := ios.MarshalCBOR()
requireNoError(t, err)
assertEqual(t, []byte{0x03}, data)
ios = &intOrStr{}
err = ios.UnmarshalCBOR(data)
requireNoError(t, err)
assertEqual(t, true, ios.IsInt())
assertEqual(t, 3, ios.Int())
ios = newIntOrStr("foo")
data, err = ios.MarshalCBOR()
requireNoError(t, err)
assertEqual(t, []byte{0x63, 0x66, 0x6f, 0x6f}, data)
ios = &intOrStr{}
err = ios.UnmarshalCBOR(data)
requireNoError(t, err)
assertEqual(t, true, ios.IsString())
assertEqual(t, "foo", ios.String())
// empty value as field
s := struct {
Field1 intOrStr `cbor:"1,keyasint"`
Field2 int `cbor:"2,keyasint"`
}{Field1: intOrStr{}, Field2: 7}
data, err = cbor.Marshal(s)
requireNoError(t, err)
assertEqual(t, []byte{0xa2, 0x1, 0x00, 0x2, 0x7}, data)
ios = &intOrStr{}
data = []byte{0x22}
err = ios.UnmarshalCBOR(data)
requireNoError(t, err)
assertEqual(t, true, ios.IsInt())
assertEqual(t, -3, ios.Int())
data = []byte{}
err = ios.UnmarshalCBOR(data)
assertEqualError(t, err, "zero length buffer")
data = []byte{0x40}
err = ios.UnmarshalCBOR(data)
assertEqualError(t, err, "must be int or string, found []uint8")
data = []byte{0xff, 0xff}
err = ios.UnmarshalCBOR(data)
assertEqualError(t, err, "cbor: unexpected \"break\" code")
}
func requireNoError(t *testing.T, err error) {
if err != nil {
t.Errorf("Unexpected error: %q", err)
t.Fail()
}
}
func assertEqualError(t *testing.T, err error, expected string) {
if err == nil || err.Error() != expected {
t.Errorf("Unexpected error: want %q, got %q", expected, err)
}
}
func assertEqual(t *testing.T, expected, actual interface{}) {
if !objectsAreEqualValues(expected, actual) {
t.Errorf("Unexpected value: want %v, got %v", expected, actual)
}
}
// taken from github.com/stretchr/testify
func objectsAreEqualValues(expected, actual interface{}) bool {
if objectsAreEqual(expected, actual) {
return true
}
actualType := reflect.TypeOf(actual)
if actualType == nil {
return false
}
expectedValue := reflect.ValueOf(expected)
if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
// Attempt comparison after type conversion
return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual)
}
return false
}
// taken from github.com/stretchr/testify
func objectsAreEqual(expected, actual interface{}) bool {
if expected == nil || actual == nil {
return expected == actual
}
exp, ok := expected.([]byte)
if !ok {
return reflect.DeepEqual(expected, actual)
}
act, ok := actual.([]byte)
if !ok {
return false
}
if exp == nil || act == nil {
return exp == nil && act == nil
}
return bytes.Equal(exp, act)
}
golang-github-veraison-go-cose-1.2.1/conformance_test.go 0000664 0000000 0000000 00000020013 14551116371 0023300 0 ustar 00root root 0000000 0000000 package cose_test
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
_ "crypto/sha256"
_ "crypto/sha512"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"math/big"
"os"
"path/filepath"
"strings"
"testing"
"github.com/fxamacker/cbor/v2"
"github.com/veraison/go-cose"
)
type TestCase struct {
UUID string `json:"uuid"`
Title string `json:"title"`
Description string `json:"description"`
Key Key `json:"key"`
Alg string `json:"alg"`
Sign1 *Sign1 `json:"sign1::sign"`
Verify1 *Verify1 `json:"sign1::verify"`
}
type Key map[string]string
type Sign1 struct {
Payload string `json:"payload"`
ProtectedHeaders *CBOR `json:"protectedHeaders"`
UnprotectedHeaders *CBOR `json:"unprotectedHeaders"`
External string `json:"external"`
Detached bool `json:"detached"`
TBS CBOR `json:"tbsHex"`
Output CBOR `json:"expectedOutput"`
OutputLength int `json:"fixedOutputLength"`
}
type Verify1 struct {
TaggedCOSESign1 CBOR `json:"taggedCOSESign1"`
External string `json:"external"`
Verify bool `json:"shouldVerify"`
}
type CBOR struct {
CBORHex string `json:"cborHex"`
CBORDiag string `json:"cborDiag"`
}
// Conformance samples are taken from
// https://github.com/gluecose/test-vectors.
var testCases = []struct {
name string
deterministic bool
err string
skip bool
}{
{name: "sign1-sign-0000"},
{name: "sign1-sign-0001"},
{name: "sign1-sign-0002"},
{name: "sign1-sign-0003"},
{name: "sign1-sign-0004", deterministic: true},
{name: "sign1-sign-0005", deterministic: true},
{name: "sign1-sign-0006", deterministic: true},
{name: "sign1-verify-0000"},
{name: "sign1-verify-0001"},
{name: "sign1-verify-0002"},
{name: "sign1-verify-0003"},
{name: "sign1-verify-0004"},
{name: "sign1-verify-0005"},
{name: "sign1-verify-0006"},
{name: "sign1-verify-negative-0000", err: "cbor: invalid protected header: cbor: require bstr type"},
{name: "sign1-verify-negative-0001", err: "cbor: invalid protected header: cbor: protected header: require map type"},
{name: "sign1-verify-negative-0002", err: "cbor: invalid protected header: cbor: found duplicate map key \"1\" at map element index 1"},
{name: "sign1-verify-negative-0003", err: "cbor: invalid unprotected header: cbor: found duplicate map key \"4\" at map element index 1"},
}
func TestConformance(t *testing.T) {
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
if tt.skip {
t.SkipNow()
}
data, err := os.ReadFile(filepath.Join("testdata", tt.name+".json"))
if err != nil {
t.Fatal(err)
}
var tc TestCase
err = json.Unmarshal(data, &tc)
if err != nil {
t.Fatal(err)
}
if tc.Sign1 != nil {
testSign1(t, &tc, tt.deterministic)
} else if tc.Verify1 != nil {
testVerify1(t, &tc, tt.err)
} else {
t.Fatal("test case not supported")
}
})
}
}
func testVerify1(t *testing.T, tc *TestCase, wantErr string) {
var err error
defer func() {
if tc.Verify1.Verify && err != nil {
t.Fatal(err)
} else if !tc.Verify1.Verify {
if err == nil {
t.Fatal("Verify1 should have failed")
}
if wantErr != "" {
if got := err.Error(); !strings.Contains(got, wantErr) {
t.Fatalf("error mismatch; want %q, got %q", wantErr, got)
}
}
}
}()
var verifier cose.Verifier
_, verifier, err = getSigner(tc, false)
if err != nil {
return
}
var sigMsg cose.Sign1Message
err = sigMsg.UnmarshalCBOR(mustHexToBytes(tc.Verify1.TaggedCOSESign1.CBORHex))
if err != nil {
return
}
var external []byte
if tc.Verify1.External != "" {
external = mustHexToBytes(tc.Verify1.External)
}
err = sigMsg.Verify(external, verifier)
if tc.Verify1.Verify && err != nil {
t.Fatal(err)
} else if !tc.Verify1.Verify && err == nil {
t.Fatal("Verify1 should have failed")
}
}
func testSign1(t *testing.T, tc *TestCase, deterministic bool) {
signer, verifier, err := getSigner(tc, true)
if err != nil {
t.Fatal(err)
}
sig := tc.Sign1
sigMsg := cose.NewSign1Message()
sigMsg.Payload = mustHexToBytes(sig.Payload)
sigMsg.Headers, err = decodeHeaders(mustHexToBytes(sig.ProtectedHeaders.CBORHex), mustHexToBytes(sig.UnprotectedHeaders.CBORHex))
if err != nil {
t.Fatal(err)
}
var external []byte
if sig.External != "" {
external = mustHexToBytes(sig.External)
}
err = sigMsg.Sign(new(zeroSource), external, signer)
if err != nil {
t.Fatal(err)
}
err = sigMsg.Verify(external, verifier)
if err != nil {
t.Fatal(err)
}
got, err := sigMsg.MarshalCBOR()
if err != nil {
t.Fatal(err)
}
want := mustHexToBytes(sig.Output.CBORHex)
if !deterministic {
got = got[:sig.OutputLength]
want = want[:sig.OutputLength]
}
if !bytes.Equal(want, got) {
t.Fatalf("unexpected output:\nwant: %x\n got: %x", want, got)
}
}
func getSigner(tc *TestCase, private bool) (cose.Signer, cose.Verifier, error) {
pkey, err := getKey(tc.Key, private)
if err != nil {
return nil, nil, err
}
alg := mustNameToAlg(tc.Alg)
signer, err := cose.NewSigner(alg, pkey)
if err != nil {
return nil, nil, err
}
verifier, err := cose.NewVerifier(alg, pkey.Public())
if err != nil {
return nil, nil, err
}
return signer, verifier, nil
}
func getKey(key Key, private bool) (crypto.Signer, error) {
switch key["kty"] {
case "RSA":
pkey := &rsa.PrivateKey{
PublicKey: rsa.PublicKey{
N: mustBase64ToBigInt(key["n"]),
E: mustBase64ToInt(key["e"]),
},
}
if private {
pkey.D = mustBase64ToBigInt(key["d"])
pkey.Primes = []*big.Int{mustBase64ToBigInt(key["p"]), mustBase64ToBigInt(key["q"])}
pkey.Precomputed = rsa.PrecomputedValues{
Dp: mustBase64ToBigInt(key["dp"]),
Dq: mustBase64ToBigInt(key["dq"]),
Qinv: mustBase64ToBigInt(key["qi"]),
CRTValues: make([]rsa.CRTValue, 0),
}
}
return pkey, nil
case "EC":
var c elliptic.Curve
switch key["crv"] {
case "P-224":
c = elliptic.P224()
case "P-256":
c = elliptic.P256()
case "P-384":
c = elliptic.P384()
case "P-521":
c = elliptic.P521()
default:
return nil, errors.New("unsupported EC curve: " + key["crv"])
}
pkey := &ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
X: mustBase64ToBigInt(key["x"]),
Y: mustBase64ToBigInt(key["y"]),
Curve: c,
},
}
if private {
pkey.D = mustBase64ToBigInt(key["d"])
}
return pkey, nil
}
return nil, errors.New("unsupported key type: " + key["kty"])
}
// zeroSource is an io.Reader that returns an unlimited number of zero bytes.
type zeroSource struct{}
func (zeroSource) Read(b []byte) (n int, err error) {
for i := range b {
b[i] = 0
}
return len(b), nil
}
var encMode, _ = cbor.CanonicalEncOptions().EncMode()
func decodeHeaders(protected, unprotected []byte) (hdr cose.Headers, err error) {
// test-vectors encodes the protected header as a map instead of a map wrapped in a bstr.
// UnmarshalFromRaw expects the former, so wrap the map here before passing it to UnmarshalFromRaw.
hdr.RawProtected, err = encMode.Marshal(protected)
if err != nil {
return
}
hdr.RawUnprotected = unprotected
err = hdr.UnmarshalFromRaw()
return hdr, err
}
func mustBase64ToInt(s string) int {
return int(mustBase64ToBigInt(s).Int64())
}
func mustHexToBytes(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return b
}
func mustBase64ToBigInt(s string) *big.Int {
val, err := base64.RawURLEncoding.DecodeString(s)
if err != nil {
panic(err)
}
return new(big.Int).SetBytes(val)
}
// mustNameToAlg returns the algorithm associated to name.
// The content of name is not defined in any RFC,
// but it's what the test cases use to identify algorithms.
func mustNameToAlg(name string) cose.Algorithm {
switch name {
case "PS256":
return cose.AlgorithmPS256
case "PS384":
return cose.AlgorithmPS384
case "PS512":
return cose.AlgorithmPS512
case "ES256":
return cose.AlgorithmES256
case "ES384":
return cose.AlgorithmES384
case "ES512":
return cose.AlgorithmES512
}
panic("algorithm name not found: " + name)
}
golang-github-veraison-go-cose-1.2.1/ecdsa.go 0000664 0000000 0000000 00000011213 14551116371 0021030 0 ustar 00root root 0000000 0000000 package cose
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"encoding/asn1"
"errors"
"fmt"
"io"
"math/big"
)
// I2OSP - Integer-to-Octet-String primitive converts a nonnegative integer to
// an octet string of a specified length `len(buf)`, and stores it in `buf`.
// I2OSP is used for encoding ECDSA signature (r, s) into byte strings.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8017#section-4.1
func I2OSP(x *big.Int, buf []byte) error {
if x.Sign() < 0 {
return errors.New("I2OSP: negative integer")
}
if x.BitLen() > len(buf)*8 {
return errors.New("I2OSP: integer too large")
}
x.FillBytes(buf)
return nil
}
// OS2IP - Octet-String-to-Integer primitive converts an octet string to a
// nonnegative integer.
// OS2IP is used for decoding ECDSA signature (r, s) from byte strings.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8017#section-4.2
func OS2IP(x []byte) *big.Int {
return new(big.Int).SetBytes(x)
}
// ecdsaKeySigner is a ECDSA-based signer with golang built-in keys.
type ecdsaKeySigner struct {
alg Algorithm
key *ecdsa.PrivateKey
}
// Algorithm returns the signing algorithm associated with the private key.
func (es *ecdsaKeySigner) Algorithm() Algorithm {
return es.alg
}
// Sign signs message content with the private key using entropy from rand.
// The resulting signature should follow RFC 8152 section 8.1,
// although it does not follow the recommendation of being deterministic.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-8.1
func (es *ecdsaKeySigner) Sign(rand io.Reader, content []byte) ([]byte, error) {
digest, err := es.alg.computeHash(content)
if err != nil {
return nil, err
}
r, s, err := ecdsa.Sign(rand, es.key, digest)
if err != nil {
return nil, err
}
return encodeECDSASignature(es.key.Curve, r, s)
}
// ecdsaKeySigner is a ECDSA based signer with a generic crypto.Signer.
type ecdsaCryptoSigner struct {
alg Algorithm
key *ecdsa.PublicKey
signer crypto.Signer
}
// Algorithm returns the signing algorithm associated with the private key.
func (es *ecdsaCryptoSigner) Algorithm() Algorithm {
return es.alg
}
// Sign signs message content with the private key, possibly using entropy from
// rand.
// The resulting signature should follow RFC 8152 section 8.1.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-8.1
func (es *ecdsaCryptoSigner) Sign(rand io.Reader, content []byte) ([]byte, error) {
digest, err := es.alg.computeHash(content)
if err != nil {
return nil, err
}
sigASN1, err := es.signer.Sign(rand, digest, nil)
if err != nil {
return nil, err
}
// decode ASN.1 decoded signature
var sig struct {
R, S *big.Int
}
if _, err := asn1.Unmarshal(sigASN1, &sig); err != nil {
return nil, err
}
// encode signature in the COSE form
return encodeECDSASignature(es.key.Curve, sig.R, sig.S)
}
// encodeECDSASignature encodes (r, s) into a signature binary string using the
// method specified by RFC 8152 section 8.1.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-8.1
func encodeECDSASignature(curve elliptic.Curve, r, s *big.Int) ([]byte, error) {
n := (curve.Params().N.BitLen() + 7) / 8
sig := make([]byte, n*2)
if err := I2OSP(r, sig[:n]); err != nil {
return nil, err
}
if err := I2OSP(s, sig[n:]); err != nil {
return nil, err
}
return sig, nil
}
// decodeECDSASignature decodes (r, s) from a signature binary string using the
// method specified by RFC 8152 section 8.1.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-8.1
func decodeECDSASignature(curve elliptic.Curve, sig []byte) (r, s *big.Int, err error) {
n := (curve.Params().N.BitLen() + 7) / 8
if len(sig) != n*2 {
return nil, nil, fmt.Errorf("invalid signature length: %d", len(sig))
}
return OS2IP(sig[:n]), OS2IP(sig[n:]), nil
}
// ecdsaVerifier is a ECDSA based verifier with golang built-in keys.
type ecdsaVerifier struct {
alg Algorithm
key *ecdsa.PublicKey
}
// Algorithm returns the signing algorithm associated with the public key.
func (ev *ecdsaVerifier) Algorithm() Algorithm {
return ev.alg
}
// Verify verifies message content with the public key, returning nil for
// success.
// Otherwise, it returns ErrVerification.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-8.1
func (ev *ecdsaVerifier) Verify(content []byte, signature []byte) error {
// compute digest
digest, err := ev.alg.computeHash(content)
if err != nil {
return err
}
// verify signature
r, s, err := decodeECDSASignature(ev.key.Curve, signature)
if err != nil {
return ErrVerification
}
if verified := ecdsa.Verify(ev.key, digest, r, s); !verified {
return ErrVerification
}
return nil
}
golang-github-veraison-go-cose-1.2.1/ecdsa_test.go 0000664 0000000 0000000 00000025272 14551116371 0022101 0 ustar 00root root 0000000 0000000 package cose
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"encoding/asn1"
"errors"
"io"
"math/big"
"reflect"
"testing"
)
func TestI2OSP(t *testing.T) {
tests := []struct {
name string
x *big.Int
buf []byte
want []byte
wantErr string
}{
{
name: "negative int",
x: big.NewInt(-1),
buf: make([]byte, 2),
wantErr: "I2OSP: negative integer",
},
{
name: "integer too large #1",
x: big.NewInt(1),
buf: make([]byte, 0),
wantErr: "I2OSP: integer too large",
},
{
name: "integer too large #2",
x: big.NewInt(256),
buf: make([]byte, 0),
wantErr: "I2OSP: integer too large",
},
{
name: "integer too large #3",
x: big.NewInt(1 << 24),
buf: make([]byte, 3),
wantErr: "I2OSP: integer too large",
},
{
name: "zero length string",
x: big.NewInt(0),
buf: make([]byte, 0),
want: []byte{},
},
{
name: "zero length string with nil buffer",
x: big.NewInt(0),
buf: nil,
want: nil,
},
{
name: "I2OSP(0, 2)",
x: big.NewInt(0),
buf: make([]byte, 2),
want: []byte{0x00, 0x00},
},
{
name: "I2OSP(1, 2)",
x: big.NewInt(1),
buf: make([]byte, 2),
want: []byte{0x00, 0x01},
},
{
name: "I2OSP(255, 2)",
x: big.NewInt(255),
buf: make([]byte, 2),
want: []byte{0x00, 0xff},
},
{
name: "I2OSP(256, 2)",
x: big.NewInt(256),
buf: make([]byte, 2),
want: []byte{0x01, 0x00},
},
{
name: "I2OSP(65535, 2)",
x: big.NewInt(65535),
buf: make([]byte, 2),
want: []byte{0xff, 0xff},
},
{
name: "I2OSP(1234, 5)",
x: big.NewInt(1234),
buf: make([]byte, 5),
want: []byte{0x00, 0x00, 0x00, 0x04, 0xd2},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := I2OSP(tt.x, tt.buf)
if err != nil && (err.Error() != tt.wantErr) {
t.Errorf("I2OSP() error = %v, wantErr %v", err, tt.wantErr)
return
} else if err == nil && (tt.wantErr != "") {
t.Errorf("I2OSP() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got := tt.buf; (tt.wantErr == "") && !reflect.DeepEqual(got, tt.want) {
t.Errorf("I2OSP() = %v, want %v", got, tt.want)
}
})
}
}
func TestOS2IP(t *testing.T) {
tests := []struct {
name string
x []byte
want *big.Int
}{
{
name: "zero length string",
x: []byte{},
want: big.NewInt(0),
},
{
name: "OS2IP(I2OSP(0, 2))",
x: []byte{0x00, 0x00},
want: big.NewInt(0),
},
{
name: "OS2IP(I2OSP(1, 2))",
x: []byte{0x00, 0x01},
want: big.NewInt(1),
},
{
name: "OS2IP(I2OSP(255, 2))",
x: []byte{0x00, 0xff},
want: big.NewInt(255),
},
{
name: "OS2IP(I2OSP(256, 2))",
x: []byte{0x01, 0x00},
want: big.NewInt(256),
},
{
name: "OS2IP(I2OSP(65535, 2))",
x: []byte{0xff, 0xff},
want: big.NewInt(65535),
},
{
name: "OS2IP(I2OSP(1234, 5))",
x: []byte{0x00, 0x00, 0x00, 0x04, 0xd2},
want: big.NewInt(1234),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := OS2IP(tt.x); tt.want.Cmp(got) != 0 {
t.Errorf("OS2IP() = %v, want %v", got, tt.want)
}
})
}
}
func generateTestECDSAKey(t *testing.T) *ecdsa.PrivateKey {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("ecdsa.GenerateKey() error = %v", err)
}
return key
}
func Test_customCurveKeySigner(t *testing.T) {
// https://github.com/veraison/go-cose/issues/59
pCustom := *elliptic.P256().Params()
pCustom.Name = "P-custom"
pCustom.BitSize /= 2
key, err := ecdsa.GenerateKey(&pCustom, rand.Reader)
if err != nil {
t.Fatalf("ecdsa.GenerateKey() error = %v", err)
}
testSignVerify(t, AlgorithmES256, key, false)
}
func Test_ecdsaKeySigner(t *testing.T) {
key := generateTestECDSAKey(t)
testSignVerify(t, AlgorithmES256, key, false)
}
func Test_ecdsaCryptoSigner(t *testing.T) {
wrappedKey := struct {
crypto.Signer
}{
Signer: generateTestECDSAKey(t),
}
testSignVerify(t, AlgorithmES256, wrappedKey, true)
}
func testSignVerify(t *testing.T, alg Algorithm, key crypto.Signer, isCryptoSigner bool) {
// set up signer
signer, err := NewSigner(alg, key)
if err != nil {
t.Fatalf("NewSigner() error = %v", err)
}
if isCryptoSigner {
if _, ok := signer.(*ecdsaCryptoSigner); !ok {
t.Fatalf("NewSigner() type = %v, want *ecdsaCryptoSigner", reflect.TypeOf(signer))
}
} else {
if _, ok := signer.(*ecdsaKeySigner); !ok {
t.Fatalf("NewSigner() type = %v, want *ecdsaKeySigner", reflect.TypeOf(signer))
}
}
if got := signer.Algorithm(); got != alg {
t.Fatalf("Algorithm() = %v, want %v", got, alg)
}
// sign / verify round trip
// see also conformance_test.go for strict tests.
content := []byte("hello world")
sig, err := signer.Sign(rand.Reader, content)
if err != nil {
t.Fatalf("Sign() error = %v", err)
}
verifier, err := NewVerifier(alg, key.Public())
if err != nil {
t.Fatalf("NewVerifier() error = %v", err)
}
if err := verifier.Verify(content, sig); err != nil {
t.Fatalf("Verifier.Verify() error = %v", err)
}
}
type ecdsaBadCryptoSigner struct {
crypto.Signer
signature []byte
err error
}
func (s *ecdsaBadCryptoSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) {
return s.signature, s.err
}
func Test_ecdsaBadCryptoSigner_SignFailure(t *testing.T) {
badSigner := &ecdsaBadCryptoSigner{
Signer: generateTestECDSAKey(t),
err: errors.New("sign failure"),
}
testSignFailure(t, AlgorithmES256, badSigner)
}
func Test_ecdsaBadCryptoSigner_BadSignature(t *testing.T) {
key := generateTestECDSAKey(t)
// nil signature
badSigner := &ecdsaBadCryptoSigner{
Signer: key,
signature: nil,
}
testSignFailure(t, AlgorithmES256, badSigner)
// malformed signature: bad r
sig, err := asn1.Marshal(struct {
R, S *big.Int
}{
R: big.NewInt(-1),
S: big.NewInt(1),
})
if err != nil {
t.Fatalf("asn1.Marshal() error = %v", err)
}
badSigner = &ecdsaBadCryptoSigner{
Signer: key,
signature: sig,
}
testSignFailure(t, AlgorithmES256, badSigner)
// malformed signature: bad s
sig, err = asn1.Marshal(struct {
R, S *big.Int
}{
R: big.NewInt(1),
S: big.NewInt(-1),
})
if err != nil {
t.Fatalf("asn1.Marshal() error = %v", err)
}
badSigner = &ecdsaBadCryptoSigner{
Signer: key,
signature: sig,
}
testSignFailure(t, AlgorithmES256, badSigner)
}
func Test_ecdsaKeySigner_SignHashFailure(t *testing.T) {
key := generateTestECDSAKey(t)
crypto.RegisterHash(crypto.SHA256, badHashNew)
defer crypto.RegisterHash(crypto.SHA256, sha256.New)
testSignFailure(t, AlgorithmES256, key)
}
func Test_ecdsaCryptoSigner_SignHashFailure(t *testing.T) {
wrappedKey := struct {
crypto.Signer
}{
Signer: generateTestECDSAKey(t),
}
crypto.RegisterHash(crypto.SHA256, badHashNew)
defer crypto.RegisterHash(crypto.SHA256, sha256.New)
testSignFailure(t, AlgorithmES256, wrappedKey)
}
func testSignFailure(t *testing.T, alg Algorithm, key crypto.Signer) {
signer, err := NewSigner(alg, key)
if err != nil {
t.Fatalf("NewSigner() error = %v", err)
}
content := []byte("hello world")
if _, err = signer.Sign(rand.Reader, content); err == nil {
t.Fatalf("Sign() error = nil, wantErr true")
}
}
func Test_ecdsaVerifier_Verify_Success(t *testing.T) {
// generate key
alg := AlgorithmES256
key := generateTestECDSAKey(t)
// generate a valid signature
content, sig := signTestData(t, alg, key)
// set up verifier
verifier, err := NewVerifier(alg, key.Public())
if err != nil {
t.Fatalf("NewVerifier() error = %v", err)
}
if _, ok := verifier.(*ecdsaVerifier); !ok {
t.Fatalf("NewVerifier() type = %v, want *ecdsaVerifier", reflect.TypeOf(verifier))
}
if got := verifier.Algorithm(); got != alg {
t.Fatalf("Algorithm() = %v, want %v", got, alg)
}
// verify round trip
if err := verifier.Verify(content, sig); err != nil {
t.Fatalf("ecdsaVerifier.Verify() error = %v", err)
}
}
func Test_ecdsaVerifier_Verify_AlgorithmMismatch(t *testing.T) {
// generate key
alg := AlgorithmES256
key := generateTestECDSAKey(t)
// generate a valid signature
content, sig := signTestData(t, alg, key)
// set up verifier with a different algorithm
verifier := &ecdsaVerifier{
alg: AlgorithmES512,
key: &key.PublicKey,
}
// verification should fail on algorithm mismatch
if err := verifier.Verify(content, sig); err != ErrVerification {
t.Fatalf("ecdsaVerifier.Verify() error = %v, wantErr %v", err, ErrVerification)
}
}
func Test_ecdsaVerifier_Verify_KeyMismatch(t *testing.T) {
// generate key
alg := AlgorithmES256
key := generateTestECDSAKey(t)
// generate a valid signature
content, sig := signTestData(t, alg, key)
// set up verifier with a different key / new key
key = generateTestECDSAKey(t)
verifier := &ecdsaVerifier{
alg: alg,
key: &key.PublicKey,
}
// verification should fail on key mismatch
if err := verifier.Verify(content, sig); err != ErrVerification {
t.Fatalf("ecdsaVerifier.Verify() error = %v, wantErr %v", err, ErrVerification)
}
}
func Test_ecdsaVerifier_Verify_InvalidSignature(t *testing.T) {
// generate key
alg := AlgorithmES256
key := generateTestECDSAKey(t)
// generate a valid signature with a tampered one
content, sig := signTestData(t, alg, key)
tamperedSig := make([]byte, len(sig))
copy(tamperedSig, sig)
tamperedSig[0]++
// set up verifier with a different algorithm
verifier := &ecdsaVerifier{
alg: alg,
key: &key.PublicKey,
}
// verification should fail on invalid signature
tests := []struct {
name string
signature []byte
}{
{
name: "nil signature",
signature: nil,
},
{
name: "empty signature",
signature: []byte{},
},
{
name: "incomplete signature",
signature: sig[:len(sig)-2],
},
{
name: "tampered signature",
signature: tamperedSig,
},
{
name: "too many signature bytes",
signature: append(sig, 0),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := verifier.Verify(content, tt.signature); err != ErrVerification {
t.Errorf("ecdsaVerifier.Verify() error = %v, wantErr %v", err, ErrVerification)
}
})
}
}
func Test_ecdsaVerifier_Verify_HashFailure(t *testing.T) {
// generate key
alg := AlgorithmES256
key := generateTestECDSAKey(t)
// generate a valid signature
content, sig := signTestData(t, alg, key)
// set up verifier
verifier, err := NewVerifier(alg, key.Public())
if err != nil {
t.Fatalf("NewVerifier() error = %v", err)
}
// verify with bad hash implementation
crypto.RegisterHash(crypto.SHA256, badHashNew)
defer crypto.RegisterHash(crypto.SHA256, sha256.New)
if err := verifier.Verify(content, sig); err == nil {
t.Fatalf("ecdsaVerifier.Verify() error = nil, wantErr true")
}
}
golang-github-veraison-go-cose-1.2.1/ed25519.go 0000664 0000000 0000000 00000002725 14551116371 0020757 0 ustar 00root root 0000000 0000000 package cose
import (
"crypto"
"crypto/ed25519"
"io"
)
// ed25519Signer is a Pure EdDSA based signer with a generic crypto.Signer.
type ed25519Signer struct {
key crypto.Signer
}
// Algorithm returns the signing algorithm associated with the private key.
func (es *ed25519Signer) Algorithm() Algorithm {
return AlgorithmEd25519
}
// Sign signs message content with the private key, possibly using entropy from
// rand.
// The resulting signature should follow RFC 8152 section 8.2.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-8.2
func (es *ed25519Signer) Sign(rand io.Reader, content []byte) ([]byte, error) {
// crypto.Hash(0) must be passed as an option.
// Reference: https://pkg.go.dev/crypto/ed25519#PrivateKey.Sign
return es.key.Sign(rand, content, crypto.Hash(0))
}
// ed25519Verifier is a Pure EdDSA based verifier with golang built-in keys.
type ed25519Verifier struct {
key ed25519.PublicKey
}
// Algorithm returns the signing algorithm associated with the public key.
func (ev *ed25519Verifier) Algorithm() Algorithm {
return AlgorithmEd25519
}
// Verify verifies message content with the public key, returning nil for
// success.
// Otherwise, it returns ErrVerification.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-8.2
func (ev *ed25519Verifier) Verify(content []byte, signature []byte) error {
if verified := ed25519.Verify(ev.key, content, signature); !verified {
return ErrVerification
}
return nil
}
golang-github-veraison-go-cose-1.2.1/ed25519_test.go 0000664 0000000 0000000 00000007033 14551116371 0022013 0 ustar 00root root 0000000 0000000 package cose
import (
"crypto/ed25519"
"crypto/rand"
"reflect"
"testing"
)
func generateTestEd25519Key(t *testing.T) (ed25519.PublicKey, ed25519.PrivateKey) {
vk, sk, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("ed25519.GenerateKey() error = %v", err)
}
return vk, sk
}
func Test_ed25519Signer(t *testing.T) {
// generate key
alg := AlgorithmEd25519
_, key := generateTestEd25519Key(t)
// set up signer
signer, err := NewSigner(alg, key)
if err != nil {
t.Fatalf("NewSigner() error = %v", err)
}
if _, ok := signer.(*ed25519Signer); !ok {
t.Fatalf("NewSigner() type = %v, want *ed25519Signer", reflect.TypeOf(signer))
}
if got := signer.Algorithm(); got != alg {
t.Fatalf("Algorithm() = %v, want %v", got, alg)
}
// sign / verify round trip
// see also conformance_test.go for strict tests.
content := []byte("hello world")
sig, err := signer.Sign(rand.Reader, content)
if err != nil {
t.Fatalf("Sign() error = %v", err)
}
verifier, err := NewVerifier(alg, key.Public())
if err != nil {
t.Fatalf("NewVerifier() error = %v", err)
}
if err := verifier.Verify(content, sig); err != nil {
t.Fatalf("Verifier.Verify() error = %v", err)
}
}
func Test_ed25519Verifier_Verify_Success(t *testing.T) {
// generate key
alg := AlgorithmEd25519
_, key := generateTestEd25519Key(t)
// generate a valid signature
content, sig := signTestData(t, alg, key)
// set up verifier
verifier, err := NewVerifier(alg, key.Public())
if err != nil {
t.Fatalf("NewVerifier() error = %v", err)
}
if _, ok := verifier.(*ed25519Verifier); !ok {
t.Fatalf("NewVerifier() type = %v, want *ed25519Verifier", reflect.TypeOf(verifier))
}
if got := verifier.Algorithm(); got != alg {
t.Fatalf("Algorithm() = %v, want %v", got, alg)
}
// verify round trip
if err := verifier.Verify(content, sig); err != nil {
t.Fatalf("ed25519Verifier.Verify() error = %v", err)
}
}
func Test_ed25519Verifier_Verify_KeyMismatch(t *testing.T) {
// generate key
alg := AlgorithmEd25519
_, key := generateTestEd25519Key(t)
// generate a valid signature
content, sig := signTestData(t, alg, key)
// set up verifier with a different key / new key
vk, _ := generateTestEd25519Key(t)
verifier := &ed25519Verifier{
key: vk,
}
// verification should fail on key mismatch
if err := verifier.Verify(content, sig); err != ErrVerification {
t.Fatalf("ed25519Verifier.Verify() error = %v, wantErr %v", err, ErrVerification)
}
}
func Test_ed25519Verifier_Verify_InvalidSignature(t *testing.T) {
// generate key
alg := AlgorithmEd25519
vk, sk := generateTestEd25519Key(t)
// generate a valid signature with a tampered one
content, sig := signTestData(t, alg, sk)
tamperedSig := make([]byte, len(sig))
copy(tamperedSig, sig)
tamperedSig[0]++
// set up verifier with a different algorithm
verifier := &ed25519Verifier{
key: vk,
}
// verification should fail on invalid signature
tests := []struct {
name string
signature []byte
}{
{
name: "nil signature",
signature: nil,
},
{
name: "empty signature",
signature: []byte{},
},
{
name: "incomplete signature",
signature: sig[:len(sig)-2],
},
{
name: "tampered signature",
signature: tamperedSig,
},
{
name: "too many signature bytes",
signature: append(sig, 0),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := verifier.Verify(content, tt.signature); err != ErrVerification {
t.Errorf("ed25519Verifier.Verify() error = %v, wantErr %v", err, ErrVerification)
}
})
}
}
golang-github-veraison-go-cose-1.2.1/errors.go 0000664 0000000 0000000 00000001673 14551116371 0021276 0 ustar 00root root 0000000 0000000 package cose
import "errors"
// Common errors
var (
ErrAlgorithmMismatch = errors.New("algorithm mismatch")
ErrAlgorithmNotFound = errors.New("algorithm not found")
ErrAlgorithmNotSupported = errors.New("algorithm not supported")
ErrEmptySignature = errors.New("empty signature")
ErrInvalidAlgorithm = errors.New("invalid algorithm")
ErrMissingPayload = errors.New("missing payload")
ErrNoSignatures = errors.New("no signatures attached")
ErrUnavailableHashFunc = errors.New("hash function is not available")
ErrVerification = errors.New("verification error")
ErrInvalidPubKey = errors.New("invalid public key")
ErrInvalidPrivKey = errors.New("invalid private key")
ErrNotPrivKey = errors.New("not a private key")
ErrSignOpNotSupported = errors.New("sign key_op not supported by key")
ErrVerifyOpNotSupported = errors.New("verify key_op not supported by key")
)
golang-github-veraison-go-cose-1.2.1/example_test.go 0000664 0000000 0000000 00000011435 14551116371 0022451 0 ustar 00root root 0000000 0000000 package cose_test
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
_ "crypto/sha512"
"fmt"
"github.com/veraison/go-cose"
)
// This example demonstrates signing and verifying COSE_Sign signatures.
//
// The COSE Sign API is EXPERIMENTAL and may be changed or removed in a later
// release.
func ExampleSignMessage() {
// create a signature holder
sigHolder := cose.NewSignature()
sigHolder.Headers.Protected.SetAlgorithm(cose.AlgorithmES512)
sigHolder.Headers.Unprotected[cose.HeaderLabelKeyID] = []byte("1")
// create message to be signed
msgToSign := cose.NewSignMessage()
msgToSign.Payload = []byte("hello world")
msgToSign.Signatures = append(msgToSign.Signatures, sigHolder)
// create a signer
privateKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
panic(err)
}
signer, err := cose.NewSigner(cose.AlgorithmES512, privateKey)
if err != nil {
panic(err)
}
// sign message
err = msgToSign.Sign(rand.Reader, nil, signer)
if err != nil {
panic(err)
}
sig, err := msgToSign.MarshalCBOR()
if err != nil {
panic(err)
}
fmt.Println("message signed")
// create a verifier from a trusted public key
publicKey := privateKey.Public()
verifier, err := cose.NewVerifier(cose.AlgorithmES512, publicKey)
if err != nil {
panic(err)
}
// verify message
var msgToVerify cose.SignMessage
err = msgToVerify.UnmarshalCBOR(sig)
if err != nil {
panic(err)
}
err = msgToVerify.Verify(nil, verifier)
if err != nil {
panic(err)
}
fmt.Println("message verified")
// tamper the message and verification should fail
msgToVerify.Payload = []byte("foobar")
err = msgToVerify.Verify(nil, verifier)
if err != cose.ErrVerification {
panic(err)
}
fmt.Println("verification error as expected")
// Output:
// message signed
// message verified
// verification error as expected
}
// This example demonstrates signing and verifying COSE_Sign1 signatures.
func ExampleSign1Message() {
// create message to be signed
msgToSign := cose.NewSign1Message()
msgToSign.Payload = []byte("hello world")
msgToSign.Headers.Protected.SetAlgorithm(cose.AlgorithmES512)
msgToSign.Headers.Unprotected[cose.HeaderLabelKeyID] = []byte("1")
// create a signer
privateKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
panic(err)
}
signer, err := cose.NewSigner(cose.AlgorithmES512, privateKey)
if err != nil {
panic(err)
}
// sign message
err = msgToSign.Sign(rand.Reader, nil, signer)
if err != nil {
panic(err)
}
sig, err := msgToSign.MarshalCBOR()
if err != nil {
panic(err)
}
fmt.Println("message signed")
// create a verifier from a trusted public key
publicKey := privateKey.Public()
verifier, err := cose.NewVerifier(cose.AlgorithmES512, publicKey)
if err != nil {
panic(err)
}
// verify message
var msgToVerify cose.Sign1Message
err = msgToVerify.UnmarshalCBOR(sig)
if err != nil {
panic(err)
}
err = msgToVerify.Verify(nil, verifier)
if err != nil {
panic(err)
}
fmt.Println("message verified")
// tamper the message and verification should fail
msgToVerify.Payload = []byte("foobar")
err = msgToVerify.Verify(nil, verifier)
if err != cose.ErrVerification {
panic(err)
}
fmt.Println("verification error as expected")
// Output:
// message signed
// message verified
// verification error as expected
}
// This example demonstrates signing COSE_Sign1_Tagged signatures using Sign1().
func ExampleSign1() {
// create a signer
privateKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
panic(err)
}
signer, err := cose.NewSigner(cose.AlgorithmES512, privateKey)
if err != nil {
panic(err)
}
// sign message
headers := cose.Headers{
Protected: cose.ProtectedHeader{
cose.HeaderLabelAlgorithm: cose.AlgorithmES512,
},
Unprotected: cose.UnprotectedHeader{
cose.HeaderLabelKeyID: []byte("1"),
},
}
sig, err := cose.Sign1(rand.Reader, signer, headers, []byte("hello world"), nil)
if err != nil {
panic(err)
}
fmt.Println("message signed")
_ = sig // further process on sig
// Output:
// message signed
}
// This example demonstrates signing COSE_Sign1 signatures using Sign1Untagged().
func ExampleSign1Untagged() {
// create a signer
privateKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
panic(err)
}
signer, err := cose.NewSigner(cose.AlgorithmES512, privateKey)
if err != nil {
panic(err)
}
// sign message
headers := cose.Headers{
Protected: cose.ProtectedHeader{
cose.HeaderLabelAlgorithm: cose.AlgorithmES512,
},
Unprotected: cose.UnprotectedHeader{
cose.HeaderLabelKeyID: []byte("1"),
},
}
sig, err := cose.Sign1Untagged(rand.Reader, signer, headers, []byte("hello world"), nil)
if err != nil {
panic(err)
}
fmt.Println("message signed")
_ = sig // further process on sig
// Output:
// message signed
}
golang-github-veraison-go-cose-1.2.1/fuzz_test.go 0000664 0000000 0000000 00000011623 14551116371 0022013 0 ustar 00root root 0000000 0000000 //go:build go1.18
// +build go1.18
package cose_test
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"github.com/fxamacker/cbor/v2"
"github.com/veraison/go-cose"
)
var supportedAlgorithms = [...]cose.Algorithm{
cose.AlgorithmPS256, cose.AlgorithmPS384, cose.AlgorithmPS512,
cose.AlgorithmES256, cose.AlgorithmES384, cose.AlgorithmES512,
cose.AlgorithmEd25519,
}
func FuzzSign1Message_UnmarshalCBOR(f *testing.F) {
testdata, err := os.ReadDir("testdata")
if err != nil {
f.Fatalf("failed to read testdata directory: %s", err)
}
for _, de := range testdata {
if de.IsDir() || !strings.HasPrefix(de.Name(), "sign1-") || !strings.HasSuffix(de.Name(), ".json") {
continue
}
b, err := os.ReadFile(filepath.Join("testdata", de.Name()))
if err != nil {
f.Fatalf("failed to read testdata: %s", err)
}
type testCase struct {
Sign1 *Sign1 `json:"sign1::sign"`
Verify1 *Verify1 `json:"sign1::verify"`
}
var tc testCase
err = json.Unmarshal(b, &tc)
if err != nil {
f.Fatal(err)
}
if tc.Sign1 != nil {
f.Add(mustHexToBytes(tc.Sign1.Output.CBORHex))
} else if tc.Verify1 != nil {
f.Add(mustHexToBytes(tc.Verify1.TaggedCOSESign1.CBORHex))
}
}
enc, _ := cbor.CanonicalEncOptions().EncMode()
dec, _ := cbor.DecOptions{IntDec: cbor.IntDecConvertSigned}.DecMode()
isCanonical := func(b []byte) bool {
var tmp interface{}
err := dec.Unmarshal(b, &tmp)
if err != nil {
return false
}
b1, err := enc.Marshal(tmp)
if err != nil {
return false
}
return bytes.Equal(b, b1)
}
f.Fuzz(func(t *testing.T, b []byte) {
var msg cose.Sign1Message
if err := msg.UnmarshalCBOR(b); err != nil {
return
}
got, err := msg.MarshalCBOR()
if err != nil {
t.Fatalf("failed to marshal valid message: %s", err)
}
if !isCanonical(b) {
return
}
if len(b) > len(got) {
b = b[:len(got)]
}
if !bytes.Equal(b, got) {
t.Fatalf("roundtripped message has changed, got: %v, want: %v", got, b)
}
})
}
func FuzzSign1(f *testing.F) {
testdata, err := os.ReadDir("testdata")
if err != nil {
f.Fatalf("failed to read testdata directory: %s", err)
}
for _, de := range testdata {
if de.IsDir() || !strings.HasPrefix(de.Name(), "sign1-sign") || !strings.HasSuffix(de.Name(), ".json") {
continue
}
b, err := os.ReadFile(filepath.Join("testdata", de.Name()))
if err != nil {
f.Fatalf("failed to read testdata: %s", err)
}
type testCase struct {
Sign1 *Sign1 `json:"sign1::sign"`
}
var tc testCase
err = json.Unmarshal(b, &tc)
if err != nil {
f.Fatal(err)
}
if tc.Sign1 != nil {
hdr, _ := encMode.Marshal(mustHexToBytes(tc.Sign1.ProtectedHeaders.CBORHex))
f.Add(hdr, mustHexToBytes(tc.Sign1.Payload), mustHexToBytes(tc.Sign1.External))
}
}
// Generating new keys consumes a lot of memory,
// to the point that the host can decide to kill the fuzzing execution
// when the memory is low.
// We can avoid this by always reusing the same signer and verifier for a given algorithm.
signverif := make(map[cose.Algorithm]signVerifier, len(supportedAlgorithms))
for _, alg := range supportedAlgorithms {
signverif[alg], err = newSignerWithEphemeralKey(alg)
if err != nil {
f.Fatal(err)
}
}
f.Fuzz(func(t *testing.T, hdr_data, payload, external []byte) {
hdr := make(cose.ProtectedHeader)
err := hdr.UnmarshalCBOR(hdr_data)
if err != nil {
return
}
alg, err := hdr.Algorithm()
if err != nil {
return
}
sv, ok := signverif[alg]
if !ok {
return
}
msg := cose.Sign1Message{
Headers: cose.Headers{Protected: hdr},
Payload: payload,
}
err = msg.Sign(rand.Reader, external, sv.signer)
if err != nil {
t.Fatal(err)
}
err = msg.Verify(external, sv.verifier)
if err != nil {
t.Fatal(err)
}
err = msg.Verify(append(external, []byte{0}...), sv.verifier)
if err == nil {
t.Fatal("verification error expected")
}
})
}
type signVerifier struct {
signer cose.Signer
verifier cose.Verifier
}
func newSignerWithEphemeralKey(alg cose.Algorithm) (sv signVerifier, err error) {
var key crypto.Signer
switch alg {
case cose.AlgorithmPS256:
key, err = rsa.GenerateKey(rand.Reader, 2048)
case cose.AlgorithmPS384:
key, err = rsa.GenerateKey(rand.Reader, 3072)
case cose.AlgorithmPS512:
key, err = rsa.GenerateKey(rand.Reader, 4096)
case cose.AlgorithmES256:
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
case cose.AlgorithmES384:
key, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
case cose.AlgorithmES512:
key, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
case cose.AlgorithmEd25519:
_, key, err = ed25519.GenerateKey(rand.Reader)
default:
err = cose.ErrAlgorithmNotSupported
}
if err != nil {
return
}
sv.signer, err = cose.NewSigner(alg, key)
if err != nil {
return
}
sv.verifier, err = cose.NewVerifier(alg, key.Public())
return
}
golang-github-veraison-go-cose-1.2.1/go.mod 0000664 0000000 0000000 00000000335 14551116371 0020533 0 ustar 00root root 0000000 0000000 module github.com/veraison/go-cose
go 1.18
require github.com/fxamacker/cbor/v2 v2.4.0
require github.com/x448/float16 v0.8.4 // indirect
retract (
v1.2.1 // contains retractions only
v1.2.0 // published in error
)
golang-github-veraison-go-cose-1.2.1/go.sum 0000664 0000000 0000000 00000000524 14551116371 0020560 0 ustar 00root root 0000000 0000000 github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
golang-github-veraison-go-cose-1.2.1/headers.go 0000664 0000000 0000000 00000040562 14551116371 0021375 0 ustar 00root root 0000000 0000000 package cose
import (
"errors"
"fmt"
"math/big"
"github.com/fxamacker/cbor/v2"
)
// COSE Header labels registered in the IANA "COSE Header Parameters" registry.
//
// Reference: https://www.iana.org/assignments/cose/cose.xhtml#header-parameters
const (
HeaderLabelAlgorithm int64 = 1
HeaderLabelCritical int64 = 2
HeaderLabelContentType int64 = 3
HeaderLabelKeyID int64 = 4
HeaderLabelIV int64 = 5
HeaderLabelPartialIV int64 = 6
HeaderLabelCounterSignature int64 = 7
HeaderLabelCounterSignature0 int64 = 9
HeaderLabelX5Bag int64 = 32
HeaderLabelX5Chain int64 = 33
HeaderLabelX5T int64 = 34
HeaderLabelX5U int64 = 35
)
// ProtectedHeader contains parameters that are to be cryptographically
// protected.
type ProtectedHeader map[interface{}]interface{}
// MarshalCBOR encodes the protected header into a CBOR bstr object.
// A zero-length header is encoded as a zero-length string rather than as a
// zero-length map (encoded as h'a0').
func (h ProtectedHeader) MarshalCBOR() ([]byte, error) {
var encoded []byte
if len(h) == 0 {
encoded = []byte{}
} else {
err := validateHeaderParameters(h, true)
if err != nil {
return nil, fmt.Errorf("protected header: %w", err)
}
encoded, err = encMode.Marshal(map[interface{}]interface{}(h))
if err != nil {
return nil, err
}
}
return encMode.Marshal(encoded)
}
// UnmarshalCBOR decodes a CBOR bstr object into ProtectedHeader.
//
// ProtectedHeader is an empty_or_serialized_map where
//
// empty_or_serialized_map = bstr .cbor header_map / bstr .size 0
func (h *ProtectedHeader) UnmarshalCBOR(data []byte) error {
if h == nil {
return errors.New("cbor: UnmarshalCBOR on nil ProtectedHeader pointer")
}
var encoded byteString
if err := encoded.UnmarshalCBOR(data); err != nil {
return err
}
if encoded == nil {
return errors.New("cbor: nil protected header")
}
if len(encoded) == 0 {
*h = make(ProtectedHeader)
} else {
if encoded[0]>>5 != 5 { // major type 5: map
return errors.New("cbor: protected header: require map type")
}
if err := validateHeaderLabelCBOR(encoded); err != nil {
return err
}
var header map[interface{}]interface{}
if err := decMode.Unmarshal(encoded, &header); err != nil {
return err
}
candidate := ProtectedHeader(header)
if err := validateHeaderParameters(candidate, true); err != nil {
return fmt.Errorf("protected header: %w", err)
}
// cast to type Algorithm if `alg` presents
if alg, err := candidate.Algorithm(); err == nil {
candidate.SetAlgorithm(alg)
}
*h = candidate
}
return nil
}
// SetAlgorithm sets the algorithm value to the algorithm header.
func (h ProtectedHeader) SetAlgorithm(alg Algorithm) {
h[HeaderLabelAlgorithm] = alg
}
// Algorithm gets the algorithm value from the algorithm header.
func (h ProtectedHeader) Algorithm() (Algorithm, error) {
value, ok := h[HeaderLabelAlgorithm]
if !ok {
return 0, ErrAlgorithmNotFound
}
switch alg := value.(type) {
case Algorithm:
return alg, nil
case int:
return Algorithm(alg), nil
case int8:
return Algorithm(alg), nil
case int16:
return Algorithm(alg), nil
case int32:
return Algorithm(alg), nil
case int64:
return Algorithm(alg), nil
case string:
v := algorithmFromString(alg)
var err error
if v == AlgorithmInvalid {
err = fmt.Errorf("unknown algorithm value %q", alg)
}
return v, err
default:
return AlgorithmInvalid, ErrInvalidAlgorithm
}
}
// Critical indicates which protected header labels an application that is
// processing a message is required to understand.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-3.1
func (h ProtectedHeader) Critical() ([]interface{}, error) {
value, ok := h[HeaderLabelCritical]
if !ok {
return nil, nil
}
err := ensureCritical(value, h)
if err != nil {
return nil, err
}
return value.([]interface{}), nil
}
// ensureCritical ensures all critical headers are present in the protected bucket.
func ensureCritical(value interface{}, headers map[interface{}]interface{}) error {
labels, ok := value.([]interface{})
if !ok {
return errors.New("invalid crit header")
}
// if present, the array MUST have at least one value in it.
if len(labels) == 0 {
return errors.New("empty crit header")
}
for _, label := range labels {
if !canInt(label) && !canTstr(label) {
return fmt.Errorf("require int / tstr type, got '%T': %v", label, label)
}
if _, ok := headers[label]; !ok {
return fmt.Errorf("missing critical header: %v", label)
}
}
return nil
}
// UnprotectedHeader contains parameters that are not cryptographically
// protected.
type UnprotectedHeader map[interface{}]interface{}
// MarshalCBOR encodes the unprotected header into a CBOR map object.
// A zero-length header is encoded as a zero-length map (encoded as h'a0').
func (h UnprotectedHeader) MarshalCBOR() ([]byte, error) {
if len(h) == 0 {
return []byte{0xa0}, nil
}
if err := validateHeaderParameters(h, false); err != nil {
return nil, fmt.Errorf("unprotected header: %w", err)
}
return encMode.Marshal(map[interface{}]interface{}(h))
}
// UnmarshalCBOR decodes a CBOR map object into UnprotectedHeader.
//
// UnprotectedHeader is a header_map.
func (h *UnprotectedHeader) UnmarshalCBOR(data []byte) error {
if h == nil {
return errors.New("cbor: UnmarshalCBOR on nil UnprotectedHeader pointer")
}
if data == nil {
return errors.New("cbor: nil unprotected header")
}
if len(data) == 0 {
return errors.New("cbor: unprotected header: missing type")
}
if data[0]>>5 != 5 { // major type 5: map
return errors.New("cbor: unprotected header: require map type")
}
if err := validateHeaderLabelCBOR(data); err != nil {
return err
}
var header map[interface{}]interface{}
if err := decMode.Unmarshal(data, &header); err != nil {
return err
}
if err := validateHeaderParameters(header, false); err != nil {
return fmt.Errorf("unprotected header: %w", err)
}
*h = header
return nil
}
// Headers represents "two buckets of information that are not
// considered to be part of the payload itself, but are used for
// holding information about content, algorithms, keys, or evaluation
// hints for the processing of the layer."
//
// It is represented by CDDL fragments:
//
// Headers = (
// protected : empty_or_serialized_map,
// unprotected : header_map
// )
//
// header_map = {
// Generic_Headers,
// * label => values
// }
//
// label = int / tstr
// values = any
//
// empty_or_serialized_map = bstr .cbor header_map / bstr .size 0
//
// # See Also
//
// https://tools.ietf.org/html/rfc8152#section-3
type Headers struct {
// RawProtected contains the raw CBOR encoded data for the protected header.
// It is populated when decoding.
// Applications can use this field for customized encoding / decoding of
// the protected header in case the default decoder provided by this library
// is not preferred.
RawProtected cbor.RawMessage
// Protected contains parameters that are to be cryptographically protected.
// When encoding or signing, the protected header is encoded using the
// default CBOR encoder if RawProtected is set to nil. Otherwise,
// RawProtected will be used with Protected ignored.
Protected ProtectedHeader
// RawUnprotected contains the raw CBOR encoded data for the unprotected
// header. It is populated when decoding.
// Applications can use this field for customized encoding / decoding of
// the unprotected header in case the default decoder provided by this
// library is not preferred.
RawUnprotected cbor.RawMessage
// Unprotected contains parameters that are not cryptographically protected.
// When encoding, the unprotected header is encoded using the default CBOR
// encoder if RawUnprotected is set to nil. Otherwise, RawUnprotected will
// be used with Unprotected ignored.
Unprotected UnprotectedHeader
}
// marshal encoded both headers.
// It returns RawProtected and RawUnprotected if those are set.
func (h *Headers) marshal() (cbor.RawMessage, cbor.RawMessage, error) {
if err := h.ensureIV(); err != nil {
return nil, nil, err
}
protected, err := h.MarshalProtected()
if err != nil {
return nil, nil, err
}
unprotected, err := h.MarshalUnprotected()
if err != nil {
return nil, nil, err
}
return protected, unprotected, nil
}
// MarshalProtected encodes the protected header.
// RawProtected is returned if it is not set to nil.
func (h *Headers) MarshalProtected() ([]byte, error) {
if len(h.RawProtected) > 0 {
return h.RawProtected, nil
}
return encMode.Marshal(h.Protected)
}
// MarshalUnprotected encodes the unprotected header.
// RawUnprotected is returned if it is not set to nil.
func (h *Headers) MarshalUnprotected() ([]byte, error) {
if len(h.RawUnprotected) > 0 {
return h.RawUnprotected, nil
}
return encMode.Marshal(h.Unprotected)
}
// UnmarshalFromRaw decodes Protected from RawProtected and Unprotected from
// RawUnprotected.
func (h *Headers) UnmarshalFromRaw() error {
if err := decMode.Unmarshal(h.RawProtected, &h.Protected); err != nil {
return fmt.Errorf("cbor: invalid protected header: %w", err)
}
if err := decMode.Unmarshal(h.RawUnprotected, &h.Unprotected); err != nil {
return fmt.Errorf("cbor: invalid unprotected header: %w", err)
}
if err := h.ensureIV(); err != nil {
return err
}
return nil
}
// ensureSigningAlgorithm ensures the presence of the `alg` header if there is
// no externally supplied data for signing.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4
func (h *Headers) ensureSigningAlgorithm(alg Algorithm, external []byte) error {
candidate, err := h.Protected.Algorithm()
switch err {
case nil:
if candidate != alg {
return fmt.Errorf("%w: signer %v: header %v", ErrAlgorithmMismatch, alg, candidate)
}
return nil
case ErrAlgorithmNotFound:
if len(external) > 0 {
return nil
}
if h.RawProtected != nil {
return ErrAlgorithmNotFound
}
if h.Protected == nil {
h.Protected = make(ProtectedHeader)
}
h.Protected.SetAlgorithm(alg)
return nil
}
return err
}
// ensureVerificationAlgorithm ensures the presence of the `alg` header if there
// is no externally supplied data for verification.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4
func (h *Headers) ensureVerificationAlgorithm(alg Algorithm, external []byte) error {
candidate, err := h.Protected.Algorithm()
switch err {
case nil:
if candidate != alg {
return fmt.Errorf("%w: verifier %v: header %v", ErrAlgorithmMismatch, alg, candidate)
}
return nil
case ErrAlgorithmNotFound:
if len(external) > 0 {
return nil
}
}
return err
}
// ensureIV ensures IV and Partial IV are not both present
// in the protected and unprotected headers.
// It does not check if they are both present within one header,
// as it will be checked later on.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-3.1
func (h *Headers) ensureIV() error {
if hasLabel(h.Protected, HeaderLabelIV) && hasLabel(h.Unprotected, HeaderLabelPartialIV) {
return errors.New("IV (protected) and PartialIV (unprotected) parameters must not both be present")
}
if hasLabel(h.Protected, HeaderLabelPartialIV) && hasLabel(h.Unprotected, HeaderLabelIV) {
return errors.New("IV (unprotected) and PartialIV (protected) parameters must not both be present")
}
return nil
}
// hasLabel returns true if h contains label.
func hasLabel(h map[interface{}]interface{}, label interface{}) bool {
_, ok := h[label]
return ok
}
// validateHeaderParameters validates all headers conform to the spec.
func validateHeaderParameters(h map[interface{}]interface{}, protected bool) error {
existing := make(map[interface{}]struct{}, len(h))
for label, value := range h {
// Validate that all header labels are integers or strings.
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-1.4
label, ok := normalizeLabel(label)
if !ok {
return errors.New("header label: require int / tstr type")
}
// Validate that there are no duplicated labels.
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-3
if _, ok := existing[label]; ok {
return fmt.Errorf("header label: duplicated label: %v", label)
} else {
existing[label] = struct{}{}
}
// Validate the generic parameters.
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-3.1
switch label {
case HeaderLabelAlgorithm:
_, is_alg := value.(Algorithm)
if !is_alg && !canInt(value) && !canTstr(value) {
return errors.New("header parameter: alg: require int / tstr type")
}
case HeaderLabelCritical:
if !protected {
return errors.New("header parameter: crit: not allowed")
}
if err := ensureCritical(value, h); err != nil {
return fmt.Errorf("header parameter: crit: %w", err)
}
case HeaderLabelContentType:
if !canTstr(value) && !canUint(value) {
return errors.New("header parameter: content type: require tstr / uint type")
}
case HeaderLabelKeyID:
if !canBstr(value) {
return errors.New("header parameter: kid: require bstr type")
}
case HeaderLabelIV:
if !canBstr(value) {
return errors.New("header parameter: IV: require bstr type")
}
if hasLabel(h, HeaderLabelPartialIV) {
return errors.New("header parameter: IV and PartialIV: parameters must not both be present")
}
case HeaderLabelPartialIV:
if !canBstr(value) {
return errors.New("header parameter: Partial IV: require bstr type")
}
if hasLabel(h, HeaderLabelIV) {
return errors.New("header parameter: IV and PartialIV: parameters must not both be present")
}
}
}
return nil
}
// canUint reports whether v can be used as a CBOR uint type.
func canUint(v interface{}) bool {
switch v := v.(type) {
case uint, uint8, uint16, uint32, uint64:
return true
case int:
return v >= 0
case int8:
return v >= 0
case int16:
return v >= 0
case int32:
return v >= 0
case int64:
return v >= 0
}
return false
}
// canInt reports whether v can be used as a CBOR int type.
func canInt(v interface{}) bool {
switch v.(type) {
case int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64:
return true
}
return false
}
// canTstr reports whether v can be used as a CBOR tstr type.
func canTstr(v interface{}) bool {
_, ok := v.(string)
return ok
}
// canBstr reports whether v can be used as a CBOR bstr type.
func canBstr(v interface{}) bool {
_, ok := v.([]byte)
return ok
}
// normalizeLabel tries to cast label into a int64 or a string.
// Returns (nil, false) if the label type is not valid.
func normalizeLabel(label interface{}) (interface{}, bool) {
switch v := label.(type) {
case int:
label = int64(v)
case int8:
label = int64(v)
case int16:
label = int64(v)
case int32:
label = int64(v)
case int64:
label = int64(v)
case uint:
label = int64(v)
case uint8:
label = int64(v)
case uint16:
label = int64(v)
case uint32:
label = int64(v)
case uint64:
label = int64(v)
case string:
// no conversion
default:
return nil, false
}
return label, true
}
// headerLabelValidator is used to validate the header label of a COSE header.
type headerLabelValidator struct {
value interface{}
}
// String prints the value without brackets `{}`. Useful in error printing.
func (hlv headerLabelValidator) String() string {
return fmt.Sprint(hlv.value)
}
// UnmarshalCBOR decodes the label value of a COSE header, and returns error if
// label is not a int (major type 0, 1) or string (major type 3).
func (hlv *headerLabelValidator) UnmarshalCBOR(data []byte) error {
if len(data) == 0 {
return errors.New("cbor: header label: missing type")
}
switch data[0] >> 5 {
case 0, 1, 3:
err := decMode.Unmarshal(data, &hlv.value)
if err != nil {
return err
}
if _, ok := hlv.value.(big.Int); ok {
return errors.New("cbor: header label: int key must not be higher than 1<<63 - 1")
}
return nil
}
return errors.New("cbor: header label: require int / tstr type")
}
// discardedCBORMessage is used to read CBOR message and discard it.
type discardedCBORMessage struct{}
// UnmarshalCBOR discards the read CBOR object.
func (discardedCBORMessage) UnmarshalCBOR(data []byte) error {
return nil
}
// validateHeaderLabelCBOR validates if all header labels are integers or
// strings of a CBOR map object.
//
// label = int / tstr
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-1.4
func validateHeaderLabelCBOR(data []byte) error {
var header map[headerLabelValidator]discardedCBORMessage
return decMode.Unmarshal(data, &header)
}
golang-github-veraison-go-cose-1.2.1/headers_test.go 0000664 0000000 0000000 00000055655 14551116371 0022445 0 ustar 00root root 0000000 0000000 package cose
import (
"errors"
"reflect"
"testing"
)
func TestProtectedHeader_MarshalCBOR(t *testing.T) {
tests := []struct {
name string
h ProtectedHeader
want []byte
wantErr string
}{
{
name: "valid header",
h: ProtectedHeader{
HeaderLabelAlgorithm: AlgorithmES256,
HeaderLabelCritical: []interface{}{
HeaderLabelContentType,
"foo",
},
HeaderLabelContentType: "text/plain",
"foo": "bar",
},
want: []byte{
0x58, 0x1e, // bstr
0xa4, // map
0x01, 0x26, // alg
0x02, 0x82, 0x03, 0x63, 0x66, 0x6f, 0x6f, // crit
0x03, 0x6a, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, // cty
0x63, 0x66, 0x6f, 0x6f, 0x63, 0x62, 0x61, 0x72, // foo: bar
},
},
{
name: "nil header",
h: nil,
want: []byte{0x40},
},
{
name: "empty header",
h: ProtectedHeader{},
want: []byte{0x40},
},
{
name: "various types of integer label",
h: ProtectedHeader{
uint(10): 0,
uint8(11): 0,
uint16(12): 0,
uint32(13): 0,
uint64(14): 0,
int(-1): 0,
int8(-2): 0,
int16(-3): 0,
int32(-4): 0,
int64(-5): 0,
},
want: []byte{
0x55, // bstr
0xaa, // map
0x0a, 0x00,
0x0b, 0x00,
0x0c, 0x00,
0x0d, 0x00,
0x0e, 0x00,
0x20, 0x00,
0x21, 0x00,
0x22, 0x00,
0x23, 0x00,
0x24, 0x00,
},
},
{
name: "invalid header label: struct type",
h: ProtectedHeader{
struct {
value int
}{}: 42,
},
wantErr: "protected header: header label: require int / tstr type",
},
{
name: "empty critical",
h: ProtectedHeader{
HeaderLabelCritical: []interface{}{},
},
wantErr: "protected header: header parameter: crit: empty crit header",
},
{
name: "invalid critical",
h: ProtectedHeader{
HeaderLabelCritical: 42,
},
wantErr: "protected header: header parameter: crit: invalid crit header",
},
{
name: "missing header marked as critical",
h: ProtectedHeader{
HeaderLabelCritical: []interface{}{
HeaderLabelContentType,
},
},
wantErr: "protected header: header parameter: crit: missing critical header: 3",
},
{
name: "critical header contains non-label element",
h: ProtectedHeader{
HeaderLabelCritical: []interface{}{[]uint8{}},
},
wantErr: "protected header: header parameter: crit: require int / tstr type, got '[]uint8': []",
},
{
name: "duplicated key",
h: ProtectedHeader{
int8(42): "foo",
int64(42): "bar",
},
wantErr: "protected header: header label: duplicated label: 42",
},
{
name: "un-marshalable content",
h: ProtectedHeader{
"foo": make(chan bool),
},
wantErr: "cbor: unsupported type: chan bool",
},
{
name: "iv and partial iv present",
h: ProtectedHeader{
HeaderLabelIV: []byte("foo"),
HeaderLabelPartialIV: []byte("bar"),
},
wantErr: "protected header: header parameter: IV and PartialIV: parameters must not both be present",
},
{
name: "content type is string",
h: ProtectedHeader{
HeaderLabelContentType: []byte("foo"),
},
wantErr: "protected header: header parameter: content type: require tstr / uint type",
},
{
name: "content type is negative int8",
h: ProtectedHeader{
HeaderLabelContentType: int8(-1),
},
wantErr: "protected header: header parameter: content type: require tstr / uint type",
},
{
name: "content type is negative int16",
h: ProtectedHeader{
HeaderLabelContentType: int16(-1),
},
wantErr: "protected header: header parameter: content type: require tstr / uint type",
},
{
name: "content type is negative int32",
h: ProtectedHeader{
HeaderLabelContentType: int32(-1),
},
wantErr: "protected header: header parameter: content type: require tstr / uint type",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.h.MarshalCBOR()
if err != nil && (err.Error() != tt.wantErr) {
t.Errorf("ProtectedHeader.MarshalCBOR() error = %v, wantErr %v", err, tt.wantErr)
return
} else if err == nil && tt.wantErr != "" {
t.Errorf("ProtectedHeader.MarshalCBOR() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ProtectedHeader.MarshalCBOR() = %v, want %v", got, tt.want)
}
})
}
}
func TestProtectedHeader_UnmarshalCBOR(t *testing.T) {
tests := []struct {
name string
data []byte
want ProtectedHeader
wantErr string
}{
{
name: "valid header",
data: []byte{
0x58, 0x1e, // bstr
0xa4, // map
0x01, 0x26, // alg
0x02, 0x82, 0x03, 0x63, 0x66, 0x6f, 0x6f, // crit
0x03, 0x6a, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, // cty
0x63, 0x66, 0x6f, 0x6f, 0x63, 0x62, 0x61, 0x72, // foo: bar
},
want: ProtectedHeader{
HeaderLabelAlgorithm: AlgorithmES256,
HeaderLabelCritical: []interface{}{
HeaderLabelContentType,
"foo",
},
HeaderLabelContentType: "text/plain",
"foo": "bar",
},
},
{
name: "empty header",
data: []byte{0x40},
want: ProtectedHeader{},
},
{
name: "empty map",
data: []byte{0x41, 0xa0},
want: ProtectedHeader{},
},
{
name: "nil CBOR data",
data: nil,
wantErr: "EOF",
},
{
name: "empty CBOR data",
data: []byte{},
wantErr: "EOF",
},
{
name: "bad CBOR data",
data: []byte{0x00, 0x01, 0x02, 0x04},
wantErr: "cbor: require bstr type",
},
{
name: "nil bstr",
data: []byte{0xf6},
wantErr: "cbor: nil protected header",
},
{
name: "non-map header",
data: []byte{0x41, 0x00},
wantErr: "cbor: protected header: require map type",
},
{
name: "invalid header label type: bstr type",
data: []byte{
0x43, 0xa1, 0x40, 0x00,
},
wantErr: "cbor: header label: require int / tstr type",
},
{
name: "invalid header label type: major type 7: simple value", // issue #38
data: []byte{
0x43, 0xa1, 0xf3, 0x00,
},
wantErr: "cbor: header label: require int / tstr type",
},
{
name: "empty critical",
data: []byte{
0x43, 0xa1, 0x02, 0x80,
},
wantErr: "protected header: header parameter: crit: empty crit header",
},
{
name: "invalid critical",
data: []byte{
0x43, 0xa1, 0x02, 0x00,
},
wantErr: "protected header: header parameter: crit: invalid crit header",
},
{
name: "missing header marked as critical",
data: []byte{
0x44, 0xa1, 0x02, 0x81, 0x03,
},
wantErr: "protected header: header parameter: crit: missing critical header: 3",
},
{
name: "critical header contains non-label element",
data: []byte{
0x44, 0xa1, 0x2, 0x81, 0x40,
},
wantErr: "protected header: header parameter: crit: require int / tstr type, got '[]uint8': []",
},
{
name: "duplicated key",
data: []byte{
0x45, 0xa2, 0x01, 0x00, 0x01, 0x00,
},
wantErr: "cbor: found duplicate map key \"1\" at map element index 1",
},
{
name: "incomplete CBOR data",
data: []byte{
0x45,
},
wantErr: "unexpected EOF",
},
{
name: "invalid map value",
data: []byte{
0x46, 0xa1, 0x00, 0xa1, 0x00, 0x4f, 0x01,
},
wantErr: "unexpected EOF",
},
{
name: "int map key too large",
data: []byte{
0x4b, 0xa1, 0x3b, 0x83, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
},
wantErr: "cbor: header label: int key must not be higher than 1<<63 - 1",
},
{
name: "header as a byte array",
data: []byte{
0x80,
},
wantErr: "cbor: require bstr type",
},
{
name: "iv must be bstr",
data: []byte{
0x46, 0xa1, 0x5, 0x63, 0x66, 0x6f, 0x6f,
},
wantErr: "protected header: header parameter: IV: require bstr type",
},
{
name: "partial iv must be bstr",
data: []byte{
0x46, 0xa1, 0x6, 0x63, 0x62, 0x61, 0x72,
},
wantErr: "protected header: header parameter: Partial IV: require bstr type",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var got ProtectedHeader
err := got.UnmarshalCBOR(tt.data)
if err != nil && (err.Error() != tt.wantErr) {
t.Errorf("ProtectedHeader.UnmarshalCBOR() error = %v, wantErr %v", err, tt.wantErr)
return
} else if err == nil && tt.wantErr != "" {
t.Errorf("ProtectedHeader.UnmarshalCBOR() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ProtectedHeader.UnmarshalCBOR() = %v, want %v", got, tt.want)
}
})
}
}
func TestProtectedHeader_Algorithm(t *testing.T) {
tests := []struct {
name string
h ProtectedHeader
want Algorithm
wantErr error
}{
{
name: "algorithm",
h: ProtectedHeader{
HeaderLabelAlgorithm: AlgorithmES256,
},
want: AlgorithmES256,
},
{
name: "int",
h: ProtectedHeader{
HeaderLabelAlgorithm: int(AlgorithmES256),
},
want: AlgorithmES256,
},
{
name: "int8",
h: ProtectedHeader{
HeaderLabelAlgorithm: int8(AlgorithmES256),
},
want: AlgorithmES256,
},
{
name: "int16",
h: ProtectedHeader{
HeaderLabelAlgorithm: int16(AlgorithmES256),
},
want: AlgorithmES256,
},
{
name: "int32",
h: ProtectedHeader{
HeaderLabelAlgorithm: int32(AlgorithmES256),
},
want: AlgorithmES256,
},
{
name: "int64",
h: ProtectedHeader{
HeaderLabelAlgorithm: int64(AlgorithmES256),
},
want: AlgorithmES256,
},
{
name: "nil header",
h: nil,
wantErr: ErrAlgorithmNotFound,
},
{
name: "empty header",
h: ProtectedHeader{},
wantErr: ErrAlgorithmNotFound,
},
{
name: "missing algorithm header",
h: ProtectedHeader{
"foo": "bar",
},
wantErr: ErrAlgorithmNotFound,
},
{
name: "unknown algorithm",
h: ProtectedHeader{
HeaderLabelAlgorithm: "foo",
},
wantErr: errors.New("unknown algorithm value \"foo\""),
},
{
name: "invalid algorithm",
h: ProtectedHeader{
HeaderLabelAlgorithm: 2.5,
},
wantErr: ErrInvalidAlgorithm,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.h.Algorithm()
if tt.wantErr != nil && err.Error() != tt.wantErr.Error() {
t.Errorf("ProtectedHeader.Algorithm() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ProtectedHeader.Algorithm() = %v, want %v", got, tt.want)
}
})
}
}
func TestProtectedHeader_Critical(t *testing.T) {
tests := []struct {
name string
h ProtectedHeader
want []interface{}
wantErr string
}{
{
name: "valid header",
h: ProtectedHeader{
HeaderLabelAlgorithm: AlgorithmES256,
HeaderLabelCritical: []interface{}{
HeaderLabelContentType,
"foo",
},
HeaderLabelContentType: "text/plain",
"foo": "bar",
},
want: []interface{}{
HeaderLabelContentType,
"foo",
},
},
{
name: "nil header",
h: nil,
want: nil,
},
{
name: "empty header",
h: ProtectedHeader{},
want: nil,
},
{
name: "nothing critical",
h: ProtectedHeader{
HeaderLabelAlgorithm: AlgorithmES256,
},
want: nil,
},
{
name: "empty critical",
h: ProtectedHeader{
HeaderLabelCritical: []interface{}{},
},
wantErr: "empty crit header",
},
{
name: "invalid critical",
h: ProtectedHeader{
HeaderLabelCritical: 42,
},
wantErr: "invalid crit header",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.h.Critical()
if err != nil && (err.Error() != tt.wantErr) {
t.Errorf("ProtectedHeader.Critical() error = %v, wantErr %v", err, tt.wantErr)
return
} else if err == nil && tt.wantErr != "" {
t.Errorf("ProtectedHeader.Critical() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ProtectedHeader.Critical() = %v, want %v", got, tt.want)
}
})
}
}
func TestUnprotectedHeader_MarshalCBOR(t *testing.T) {
tests := []struct {
name string
h UnprotectedHeader
want []byte
wantErr string
}{
{
name: "valid header",
h: UnprotectedHeader{
HeaderLabelAlgorithm: "foobar",
},
want: []byte{
0xa1, // map
0x01, // alg
0x66, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, // foobar
},
},
{
name: "nil header",
h: nil,
want: []byte{0xa0},
},
{
name: "empty header",
h: UnprotectedHeader{},
want: []byte{0xa0},
},
{
name: "various types of integer label",
h: UnprotectedHeader{
uint(10): 0,
uint8(11): 0,
uint16(12): 0,
uint32(13): 0,
uint64(14): 0,
int(-1): 0,
int8(-2): 0,
int16(-3): 0,
int32(-4): 0,
int64(-5): 0,
},
want: []byte{
0xaa, // map
0x0a, 0x00,
0x0b, 0x00,
0x0c, 0x00,
0x0d, 0x00,
0x0e, 0x00,
0x20, 0x00,
0x21, 0x00,
0x22, 0x00,
0x23, 0x00,
0x24, 0x00,
},
},
{
name: "invalid header label: struct type",
h: UnprotectedHeader{
struct {
value int
}{}: 42,
},
wantErr: "unprotected header: header label: require int / tstr type",
},
{
name: "duplicated key",
h: UnprotectedHeader{
int8(42): "foo",
int64(42): "bar",
},
wantErr: "unprotected header: header label: duplicated label: 42",
},
{
name: "un-marshalable content",
h: UnprotectedHeader{
"foo": make(chan bool),
},
wantErr: "cbor: unsupported type: chan bool",
},
{
name: "iv and partial iv present",
h: UnprotectedHeader{
HeaderLabelIV: []byte("foo"),
HeaderLabelPartialIV: []byte("bar"),
},
wantErr: "unprotected header: header parameter: IV and PartialIV: parameters must not both be present",
},
{
name: "critical present",
h: UnprotectedHeader{
HeaderLabelCritical: []string{"foo"},
},
wantErr: "unprotected header: header parameter: crit: not allowed",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.h.MarshalCBOR()
if err != nil && (err.Error() != tt.wantErr) {
t.Errorf("UnprotectedHeader.MarshalCBOR() error = %v, wantErr %v", err, tt.wantErr)
return
} else if err == nil && tt.wantErr != "" {
t.Errorf("UnprotectedHeader.MarshalCBOR() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("UnprotectedHeader.MarshalCBOR() = %v, want %v", got, tt.want)
}
})
}
}
func TestUnprotectedHeader_UnmarshalCBOR(t *testing.T) {
tests := []struct {
name string
data []byte
want UnprotectedHeader
wantErr string
}{
{
name: "valid header",
data: []byte{
0xa1, // map
0x01, // alg
0x66, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, // foobar
},
want: UnprotectedHeader{
HeaderLabelAlgorithm: "foobar",
},
},
{
name: "empty map",
data: []byte{0xa0},
want: UnprotectedHeader{},
},
{
name: "nil CBOR data",
data: nil,
wantErr: "cbor: nil unprotected header",
},
{
name: "empty CBOR data",
data: []byte{},
wantErr: "cbor: unprotected header: missing type",
},
{
name: "bad CBOR data",
data: []byte{0x00, 0x01, 0x02, 0x04},
wantErr: "cbor: unprotected header: require map type",
},
{
name: "non-map header",
data: []byte{0x00},
wantErr: "cbor: unprotected header: require map type",
},
{
name: "invalid header label type: bstr type",
data: []byte{
0xa1, 0x40, 0x00,
},
wantErr: "cbor: header label: require int / tstr type",
},
{
name: "invalid header label type: major type 7: simple value", // issue #38
data: []byte{
0xa1, 0xf3, 0x00,
},
wantErr: "cbor: header label: require int / tstr type",
},
{
name: "duplicated key",
data: []byte{
0xa2, 0x01, 0x00, 0x01, 0x00,
},
wantErr: "cbor: found duplicate map key \"1\" at map element index 1",
},
{
name: "incomplete CBOR data",
data: []byte{
0xa5,
},
wantErr: "unexpected EOF",
},
{
name: "invalid map value",
data: []byte{
0xa1, 0x00, 0xa1, 0x00, 0x4f, 0x01,
},
wantErr: "unexpected EOF",
},
{
name: "int map key too large",
data: []byte{
0xa1, 0x3b, 0x83, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
},
wantErr: "cbor: header label: int key must not be higher than 1<<63 - 1",
},
{
name: "iv must be bstr",
data: []byte{
0xa1, 0x5, 0x63, 0x66, 0x6f, 0x6f,
},
wantErr: "unprotected header: header parameter: IV: require bstr type",
},
{
name: "partial iv must be bstr",
data: []byte{
0xa1, 0x6, 0x63, 0x62, 0x61, 0x72,
},
wantErr: "unprotected header: header parameter: Partial IV: require bstr type",
},
{
name: "critical present",
data: []byte{
0xa1, // map
0x02, 0x82, 0x03, 0x63, 0x66, 0x6f, 0x6f, // crit
},
wantErr: "unprotected header: header parameter: crit: not allowed",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var got UnprotectedHeader
err := got.UnmarshalCBOR(tt.data)
if err != nil && (err.Error() != tt.wantErr) {
t.Errorf("UnprotectedHeader.UnmarshalCBOR() error = %v, wantErr %v", err, tt.wantErr)
return
} else if err == nil && tt.wantErr != "" {
t.Errorf("UnprotectedHeader.UnmarshalCBOR() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("UnprotectedHeader.UnmarshalCBOR() = %v, want %v", got, tt.want)
}
})
}
}
func TestHeaders_MarshalProtected(t *testing.T) {
tests := []struct {
name string
h Headers
want []byte
wantErr string
}{
{
name: "pre-marshaled protected header",
h: Headers{
RawProtected: []byte{0x43, 0xa1, 0x01, 0x26},
Unprotected: UnprotectedHeader{
HeaderLabelKeyID: 42,
},
},
want: []byte{0x43, 0xa1, 0x01, 0x26},
},
{
name: "raw over protected",
h: Headers{
RawProtected: []byte{0x43, 0xa1, 0x01, 0x26},
Protected: ProtectedHeader{
HeaderLabelAlgorithm: AlgorithmPS512,
},
Unprotected: UnprotectedHeader{
HeaderLabelKeyID: 42,
},
},
want: []byte{0x43, 0xa1, 0x01, 0x26},
},
{
name: "no pre-marshaled protected header",
h: Headers{
Protected: ProtectedHeader{
HeaderLabelAlgorithm: AlgorithmES256,
},
Unprotected: UnprotectedHeader{
HeaderLabelKeyID: 42,
},
},
want: []byte{0x43, 0xa1, 0x01, 0x26},
},
{
name: "invalid protected header",
h: Headers{
Protected: ProtectedHeader{
HeaderLabelAlgorithm: make(chan bool),
},
Unprotected: UnprotectedHeader{
HeaderLabelKeyID: 42,
},
},
wantErr: "protected header: header parameter: alg: require int / tstr type",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.h.MarshalProtected()
if err != nil && (err.Error() != tt.wantErr) {
t.Errorf("Headers.MarshalProtected() error = %v, wantErr %v", err, tt.wantErr)
return
} else if err == nil && tt.wantErr != "" {
t.Errorf("Headers.MarshalProtected() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Headers.MarshalProtected() = %v, want %v", got, tt.want)
}
})
}
}
func TestHeaders_MarshalUnprotected(t *testing.T) {
tests := []struct {
name string
h Headers
want []byte
wantErr string
}{
{
name: "pre-marshaled protected header",
h: Headers{
Protected: ProtectedHeader{
HeaderLabelAlgorithm: AlgorithmES256,
},
RawUnprotected: []byte{0xa1, 0x04, 0x18, 0x2a},
Unprotected: UnprotectedHeader{
HeaderLabelKeyID: 42,
},
},
want: []byte{0xa1, 0x04, 0x18, 0x2a},
},
{
name: "raw over protected",
h: Headers{
Protected: ProtectedHeader{
HeaderLabelAlgorithm: AlgorithmES256,
},
RawUnprotected: []byte{0xa1, 0x04, 0x18, 0x2a},
Unprotected: UnprotectedHeader{
HeaderLabelKeyID: 43,
},
},
want: []byte{0xa1, 0x04, 0x18, 0x2a},
},
{
name: "no pre-marshaled protected header",
h: Headers{
Protected: ProtectedHeader{
HeaderLabelAlgorithm: AlgorithmES256,
},
Unprotected: UnprotectedHeader{
HeaderLabelContentType: uint8(42),
},
},
want: []byte{0xa1, 0x03, 0x18, 0x2a},
},
{
name: "invalid protected header",
h: Headers{
Protected: ProtectedHeader{
HeaderLabelAlgorithm: AlgorithmES256,
},
Unprotected: UnprotectedHeader{
HeaderLabelKeyID: make(chan bool),
},
},
wantErr: "unprotected header: header parameter: kid: require bstr type",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.h.MarshalUnprotected()
if err != nil && (err.Error() != tt.wantErr) {
t.Errorf("Headers.MarshalUnprotected() error = %v, wantErr %v", err, tt.wantErr)
return
} else if err == nil && tt.wantErr != "" {
t.Errorf("Headers.MarshalUnprotected() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Headers.MarshalUnprotected() = %v, want %v", got, tt.want)
}
})
}
}
func TestHeaders_UnmarshalFromRaw(t *testing.T) {
tests := []struct {
name string
h Headers
want Headers
wantErr string
}{
{
name: "nil raw protected header",
h: Headers{
RawUnprotected: []byte{0xa1, 0x04, 0x18, 0x2a},
},
wantErr: "cbor: invalid protected header: EOF",
},
{
name: "nil raw unprotected header",
h: Headers{
RawProtected: []byte{0x43, 0xa1, 0x01, 0x26},
},
wantErr: "cbor: invalid unprotected header: EOF",
},
{
name: "valid raw header",
h: Headers{
RawProtected: []byte{0x43, 0xa1, 0x01, 0x26},
RawUnprotected: []byte{0xa1, 0x03, 0x18, 0x2a},
},
want: Headers{
RawProtected: []byte{0x43, 0xa1, 0x01, 0x26},
Protected: ProtectedHeader{
HeaderLabelAlgorithm: AlgorithmES256,
},
RawUnprotected: []byte{0xa1, 0x04, 0x18, 0x2a},
Unprotected: UnprotectedHeader{
HeaderLabelContentType: int8(42),
},
},
},
{
name: "replaced with raw header",
h: Headers{
RawProtected: []byte{0x43, 0xa1, 0x01, 0x26},
Protected: ProtectedHeader{
HeaderLabelAlgorithm: AlgorithmES512,
},
RawUnprotected: []byte{0xa1, 0x03, 0x18, 0x2a},
Unprotected: UnprotectedHeader{
HeaderLabelContentType: int16(43),
},
},
want: Headers{
RawProtected: []byte{0x43, 0xa1, 0x01, 0x26},
Protected: ProtectedHeader{
HeaderLabelAlgorithm: AlgorithmES256,
},
RawUnprotected: []byte{0xa1, 0x04, 0x18, 0x2a},
Unprotected: UnprotectedHeader{
HeaderLabelKeyID: 42,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.h
err := got.UnmarshalFromRaw()
if err != nil && (err.Error() != tt.wantErr) {
t.Errorf("Headers.UnmarshalFromRaw() error = %v, wantErr %v", err, tt.wantErr)
return
} else if err == nil && tt.wantErr != "" {
t.Errorf("Headers.UnmarshalFromRaw() error = %v, wantErr %v", err, tt.wantErr)
return
}
})
}
}
golang-github-veraison-go-cose-1.2.1/key.go 0000664 0000000 0000000 00000045617 14551116371 0020560 0 ustar 00root root 0000000 0000000 package cose
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"errors"
"fmt"
"math/big"
"strconv"
cbor "github.com/fxamacker/cbor/v2"
)
const (
// An inviald key_op value
KeyOpInvalid KeyOp = 0
// The key is used to create signatures. Requires private key fields.
KeyOpSign KeyOp = 1
// The key is used for verification of signatures.
KeyOpVerify KeyOp = 2
// The key is used for key transport encryption.
KeyOpEncrypt KeyOp = 3
// The key is used for key transport decryption. Requires private key fields.
KeyOpDecrypt KeyOp = 4
// The key is used for key wrap encryption.
KeyOpWrapKey KeyOp = 5
// The key is used for key wrap decryption.
KeyOpUnwrapKey KeyOp = 6
// The key is used for deriving keys. Requires private key fields.
KeyOpDeriveKey KeyOp = 7
// The key is used for deriving bits not to be used as a key. Requires
// private key fields.
KeyOpDeriveBits KeyOp = 8
// The key is used for creating MACs.
KeyOpMACCreate KeyOp = 9
// The key is used for validating MACs.
KeyOpMACVerify KeyOp = 10
)
// KeyOp represents a key_ops value used to restrict purposes for which a Key
// may be used.
type KeyOp int64
// KeyOpFromString returns the KeyOp corresponding to the specified name.
// The values are taken from https://www.rfc-editor.org/rfc/rfc7517#section-4.3
func KeyOpFromString(val string) (KeyOp, error) {
switch val {
case "sign":
return KeyOpSign, nil
case "verify":
return KeyOpVerify, nil
case "encrypt":
return KeyOpEncrypt, nil
case "decrypt":
return KeyOpDecrypt, nil
case "wrapKey":
return KeyOpWrapKey, nil
case "unwrapKey":
return KeyOpUnwrapKey, nil
case "deriveKey":
return KeyOpDeriveKey, nil
case "deriveBits":
return KeyOpDeriveBits, nil
default:
return KeyOpInvalid, fmt.Errorf("unknown key_ops value %q", val)
}
}
// String returns a string representation of the KeyType. Note does not
// represent a valid value of the corresponding serialized entry, and must not
// be used as such. (The values returned _mostly_ correspond to those accepted
// by KeyOpFromString, except for MAC create/verify, which are not defined by
// RFC7517).
func (ko KeyOp) String() string {
switch ko {
case KeyOpSign:
return "sign"
case KeyOpVerify:
return "verify"
case KeyOpEncrypt:
return "encrypt"
case KeyOpDecrypt:
return "decrypt"
case KeyOpWrapKey:
return "wrapKey"
case KeyOpUnwrapKey:
return "unwrapKey"
case KeyOpDeriveKey:
return "deriveKey"
case KeyOpDeriveBits:
return "deriveBits"
case KeyOpMACCreate:
return "MAC create"
case KeyOpMACVerify:
return "MAC verify"
default:
return "unknown key_op value " + strconv.Itoa(int(ko))
}
}
// IsSupported returnns true if the specified value is represents one of the
// key_ops defined in
// https://www.rfc-editor.org/rfc/rfc9052.html#name-cose-key-common-parameters
func (ko KeyOp) IsSupported() bool {
return ko >= 1 && ko <= 10
}
// MarshalCBOR marshals the KeyOp as a CBOR int.
func (ko KeyOp) MarshalCBOR() ([]byte, error) {
return encMode.Marshal(int64(ko))
}
// UnmarshalCBOR populates the KeyOp from the provided CBOR value (must be int
// or tstr).
func (ko *KeyOp) UnmarshalCBOR(data []byte) error {
var raw intOrStr
if err := raw.UnmarshalCBOR(data); err != nil {
return fmt.Errorf("invalid key_ops value %w", err)
}
if raw.IsString() {
v, err := KeyOpFromString(raw.String())
if err != nil {
return err
}
*ko = v
} else {
v := raw.Int()
*ko = KeyOp(v)
if !ko.IsSupported() {
return fmt.Errorf("unknown key_ops value %d", v)
}
}
return nil
}
// KeyType identifies the family of keys represented by the associated Key.
// This determines which files within the Key must be set in order for it to be
// valid.
type KeyType int64
const (
// Invlaid key type
KeyTypeInvalid KeyType = 0
// Octet Key Pair
KeyTypeOKP KeyType = 1
// Elliptic Curve Keys w/ x- and y-coordinate pair
KeyTypeEC2 KeyType = 2
// Symmetric Keys
KeyTypeSymmetric KeyType = 4
)
// String returns a string representation of the KeyType. Note does not
// represent a valid value of the corresponding serialized entry, and must
// not be used as such.
func (kt KeyType) String() string {
switch kt {
case KeyTypeOKP:
return "OKP"
case KeyTypeEC2:
return "EC2"
case KeyTypeSymmetric:
return "Symmetric"
default:
return "unknown key type value " + strconv.Itoa(int(kt))
}
}
// MarshalCBOR marshals the KeyType as a CBOR int.
func (kt KeyType) MarshalCBOR() ([]byte, error) {
return encMode.Marshal(int(kt))
}
// UnmarshalCBOR populates the KeyType from the provided CBOR value (must be
// int or tstr).
func (kt *KeyType) UnmarshalCBOR(data []byte) error {
var raw intOrStr
if err := raw.UnmarshalCBOR(data); err != nil {
return fmt.Errorf("invalid key type value: %w", err)
}
if raw.IsString() {
v, err := keyTypeFromString(raw.String())
if err != nil {
return err
}
*kt = v
} else {
v := raw.Int()
if v == 0 {
// 0 is reserved, and so can never be valid
return fmt.Errorf("invalid key type value 0")
}
if v > 4 || v < 0 || v == 3 {
return fmt.Errorf("unknown key type value %d", v)
}
*kt = KeyType(v)
}
return nil
}
// NOTE: there are currently no registered string key type values.
func keyTypeFromString(v string) (KeyType, error) {
return KeyTypeInvalid, fmt.Errorf("unknown key type value %q", v)
}
const (
// Invalid/unrecognised curve
CurveInvalid Curve = 0
// NIST P-256 also known as secp256r1
CurveP256 Curve = 1
// NIST P-384 also known as secp384r1
CurveP384 Curve = 2
// NIST P-521 also known as secp521r1
CurveP521 Curve = 3
// X25519 for use w/ ECDH only
CurveX25519 Curve = 4
// X448 for use w/ ECDH only
CurveX448 Curve = 5
// Ed25519 for use /w EdDSA only
CurveEd25519 Curve = 6
// Ed448 for use /w EdDSA only
CurveEd448 Curve = 7
)
// Curve represents the EC2/OKP key's curve. See:
// https://datatracker.ietf.org/doc/html/rfc8152#section-13.1
type Curve int64
// String returns a string representation of the Curve. Note does not
// represent a valid value of the corresponding serialized entry, and must
// not be used as such.
func (c Curve) String() string {
switch c {
case CurveP256:
return "P-256"
case CurveP384:
return "P-384"
case CurveP521:
return "P-521"
case CurveX25519:
return "X25519"
case CurveX448:
return "X448"
case CurveEd25519:
return "Ed25519"
case CurveEd448:
return "Ed448"
default:
return "unknown curve value " + strconv.Itoa(int(c))
}
}
// MarshalCBOR marshals the KeyType as a CBOR int.
func (c Curve) MarshalCBOR() ([]byte, error) {
return encMode.Marshal(int(c))
}
// UnmarshalCBOR populates the KeyType from the provided CBOR value (must be
// int or tstr).
func (c *Curve) UnmarshalCBOR(data []byte) error {
var raw intOrStr
if err := raw.UnmarshalCBOR(data); err != nil {
return fmt.Errorf("invalid curve value: %w", err)
}
if raw.IsString() {
v, err := curveFromString(raw.String())
if err != nil {
return err
}
*c = v
} else {
v := raw.Int()
if v < 1 || v > 7 {
return fmt.Errorf("unknown curve value %d", v)
}
*c = Curve(v)
}
return nil
}
// NOTE: there are currently no registered string values for curves.
func curveFromString(v string) (Curve, error) {
return CurveInvalid, fmt.Errorf("unknown curve value %q", v)
}
// Key represents a COSE_Key structure, as defined by RFC8152.
// Note: currently, this does NOT support RFC8230 (RSA algorithms).
type Key struct {
// Common parameters. These are independent of the key type. Only
// KeyType common parameter MUST be set.
// KeyType identifies the family of keys for this structure, and thus,
// which of the key-type-specific parameters need to be set.
KeyType KeyType `cbor:"1,keyasint"`
// KeyID is the identification value matched to the kid in the message.
KeyID []byte `cbor:"2,keyasint,omitempty"`
// KeyOps can be set to restrict the set of operations that the Key is used for.
KeyOps []KeyOp `cbor:"4,keyasint,omitempty"`
// BaseIV is the Base IV to be xor-ed with Partial IVs.
BaseIV []byte `cbor:"5,keyasint,omitempty"`
// Algorithm is used to restrict the algorithm that is used with the
// key. If it is set, the application MUST verify that it matches the
// algorithm for which the Key is being used.
Algorithm Algorithm `cbor:"-"`
// Curve is EC identifier -- taken form "COSE Elliptic Curves" IANA registry.
// Populated from keyStruct.RawKeyParam when key type is EC2 or OKP.
Curve Curve `cbor:"-"`
// K is the key value. Populated from keyStruct.RawKeyParam when key
// type is Symmetric.
K []byte `cbor:"-"`
// EC2/OKP params
// X is the x-coordinate
X []byte `cbor:"-2,keyasint,omitempty"`
// Y is the y-coordinate (sign bits are not supported)
Y []byte `cbor:"-3,keyasint,omitempty"`
// D is the private key
D []byte `cbor:"-4,keyasint,omitempty"`
}
// NewOKPKey returns a Key created using the provided Octet Key Pair data.
func NewOKPKey(alg Algorithm, x, d []byte) (*Key, error) {
if alg != AlgorithmEd25519 {
return nil, fmt.Errorf("unsupported algorithm %q", alg)
}
key := &Key{
KeyType: KeyTypeOKP,
Algorithm: alg,
Curve: CurveEd25519,
X: x,
D: d,
}
return key, key.Validate()
}
// NewEC2Key returns a Key created using the provided elliptic curve key
// data.
func NewEC2Key(alg Algorithm, x, y, d []byte) (*Key, error) {
var curve Curve
switch alg {
case AlgorithmES256:
curve = CurveP256
case AlgorithmES384:
curve = CurveP384
case AlgorithmES512:
curve = CurveP521
default:
return nil, fmt.Errorf("unsupported algorithm %q", alg)
}
key := &Key{
KeyType: KeyTypeEC2,
Algorithm: alg,
Curve: curve,
X: x,
Y: y,
D: d,
}
return key, key.Validate()
}
// NewSymmetricKey returns a Key created using the provided Symmetric key
// bytes.
func NewSymmetricKey(k []byte) (*Key, error) {
key := &Key{
KeyType: KeyTypeSymmetric,
K: k,
}
return key, key.Validate()
}
// NewKeyFromPublic returns a Key created using the provided crypto.PublicKey
// and Algorithm.
func NewKeyFromPublic(alg Algorithm, pub crypto.PublicKey) (*Key, error) {
switch alg {
case AlgorithmES256, AlgorithmES384, AlgorithmES512:
vk, ok := pub.(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("%v: %w", alg, ErrInvalidPubKey)
}
return NewEC2Key(alg, vk.X.Bytes(), vk.Y.Bytes(), nil)
case AlgorithmEd25519:
vk, ok := pub.(ed25519.PublicKey)
if !ok {
return nil, fmt.Errorf("%v: %w", alg, ErrInvalidPubKey)
}
return NewOKPKey(alg, []byte(vk), nil)
default:
return nil, ErrAlgorithmNotSupported
}
}
// NewKeyFromPrivate returns a Key created using provided crypto.PrivateKey
// and Algorithm.
func NewKeyFromPrivate(alg Algorithm, priv crypto.PrivateKey) (*Key, error) {
switch alg {
case AlgorithmES256, AlgorithmES384, AlgorithmES512:
sk, ok := priv.(*ecdsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("%v: %w", alg, ErrInvalidPrivKey)
}
return NewEC2Key(alg, sk.X.Bytes(), sk.Y.Bytes(), sk.D.Bytes())
case AlgorithmEd25519:
sk, ok := priv.(ed25519.PrivateKey)
if !ok {
return nil, fmt.Errorf("%v: %w", alg, ErrInvalidPrivKey)
}
return NewOKPKey(alg, []byte(sk[32:]), []byte(sk[:32]))
default:
return nil, ErrAlgorithmNotSupported
}
}
// Validate ensures that the parameters set inside the Key are internally
// consistent (e.g., that the key type is appropriate to the curve.)
func (k Key) Validate() error {
switch k.KeyType {
case KeyTypeEC2:
switch k.Curve {
case CurveP256, CurveP384, CurveP521:
// ok
default:
return fmt.Errorf(
"EC2 curve must be P-256, P-384, or P-521; found %q",
k.Curve.String(),
)
}
case KeyTypeOKP:
switch k.Curve {
case CurveX25519, CurveX448, CurveEd25519, CurveEd448:
// ok
default:
return fmt.Errorf(
"OKP curve must be X25519, X448, Ed25519, or Ed448; found %q",
k.Curve.String(),
)
}
case KeyTypeSymmetric:
default:
return errors.New(k.KeyType.String())
}
// If Algorithm is set, it must match the specified key parameters.
if k.Algorithm != AlgorithmInvalid {
expectedAlg, err := k.deriveAlgorithm()
if err != nil {
return err
}
if k.Algorithm != expectedAlg {
return fmt.Errorf(
"found algorithm %q (expected %q)",
k.Algorithm.String(),
expectedAlg.String(),
)
}
}
return nil
}
type keyalias Key
type marshaledKey struct {
keyalias
// RawAlgorithm contains the raw Algorithm value, this is necessary
// because cbor library ignores omitempty on types that implement the
// cbor.Marshaler interface.
RawAlgorithm cbor.RawMessage `cbor:"3,keyasint,omitempty"`
// RawKeyParam contains the raw CBOR encoded data for the label -1.
// Depending on the KeyType this is used to populate either Curve or K
// below.
RawKeyParam cbor.RawMessage `cbor:"-1,keyasint,omitempty"`
}
// MarshalCBOR encodes Key into a COSE_Key object.
func (k *Key) MarshalCBOR() ([]byte, error) {
tmp := marshaledKey{
keyalias: keyalias(*k),
}
var err error
switch k.KeyType {
case KeyTypeSymmetric:
if tmp.RawKeyParam, err = encMode.Marshal(k.K); err != nil {
return nil, err
}
case KeyTypeEC2, KeyTypeOKP:
if tmp.RawKeyParam, err = encMode.Marshal(k.Curve); err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("invalid key type: %q", k.KeyType.String())
}
if k.Algorithm != AlgorithmInvalid {
if tmp.RawAlgorithm, err = encMode.Marshal(k.Algorithm); err != nil {
return nil, err
}
}
return encMode.Marshal(tmp)
}
// UnmarshalCBOR decodes a COSE_Key object into Key.
func (k *Key) UnmarshalCBOR(data []byte) error {
var tmp marshaledKey
if err := decMode.Unmarshal(data, &tmp); err != nil {
return err
}
*k = Key(tmp.keyalias)
if tmp.RawAlgorithm != nil {
if err := decMode.Unmarshal(tmp.RawAlgorithm, &k.Algorithm); err != nil {
return err
}
}
switch k.KeyType {
case KeyTypeEC2:
if tmp.RawKeyParam == nil {
return errors.New("missing Curve parameter (required for EC2 key type)")
}
if err := decMode.Unmarshal(tmp.RawKeyParam, &k.Curve); err != nil {
return err
}
case KeyTypeOKP:
if tmp.RawKeyParam == nil {
return errors.New("missing Curve parameter (required for OKP key type)")
}
if err := decMode.Unmarshal(tmp.RawKeyParam, &k.Curve); err != nil {
return err
}
case KeyTypeSymmetric:
if tmp.RawKeyParam == nil {
return errors.New("missing K parameter (required for Symmetric key type)")
}
if err := decMode.Unmarshal(tmp.RawKeyParam, &k.K); err != nil {
return err
}
default:
// this should not be reachable as KeyType.UnmarshalCBOR would
// result in an error during decMode.Unmarshal() above, if the
// value in the data doesn't correspond to one of the above
// types.
return fmt.Errorf("unexpected key type %q", k.KeyType.String())
}
return k.Validate()
}
// PublicKey returns a crypto.PublicKey generated using Key's parameters.
func (k *Key) PublicKey() (crypto.PublicKey, error) {
alg, err := k.deriveAlgorithm()
if err != nil {
return nil, err
}
switch alg {
case AlgorithmES256, AlgorithmES384, AlgorithmES512:
var curve elliptic.Curve
switch alg {
case AlgorithmES256:
curve = elliptic.P256()
case AlgorithmES384:
curve = elliptic.P384()
case AlgorithmES512:
curve = elliptic.P521()
}
pub := &ecdsa.PublicKey{Curve: curve, X: new(big.Int), Y: new(big.Int)}
pub.X.SetBytes(k.X)
pub.Y.SetBytes(k.Y)
return pub, nil
case AlgorithmEd25519:
return ed25519.PublicKey(k.X), nil
default:
return nil, ErrAlgorithmNotSupported
}
}
// PrivateKey returns a crypto.PrivateKey generated using Key's parameters.
func (k *Key) PrivateKey() (crypto.PrivateKey, error) {
alg, err := k.deriveAlgorithm()
if err != nil {
return nil, err
}
if len(k.D) == 0 {
return nil, ErrNotPrivKey
}
switch alg {
case AlgorithmES256, AlgorithmES384, AlgorithmES512:
var curve elliptic.Curve
switch alg {
case AlgorithmES256:
curve = elliptic.P256()
case AlgorithmES384:
curve = elliptic.P384()
case AlgorithmES512:
curve = elliptic.P521()
}
priv := &ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{Curve: curve, X: new(big.Int), Y: new(big.Int)},
D: new(big.Int),
}
priv.X.SetBytes(k.X)
priv.Y.SetBytes(k.Y)
priv.D.SetBytes(k.D)
return priv, nil
case AlgorithmEd25519:
buf := make([]byte, ed25519.PrivateKeySize)
copy(buf, k.D)
copy(buf[32:], k.X)
return ed25519.PrivateKey(buf), nil
default:
return nil, ErrAlgorithmNotSupported
}
}
// AlgorithmOrDefault returns the Algorithm associated with Key. If Key.Algorithm is
// set, that is what is returned. Otherwise, the algorithm is inferred using
// Key.Curve. This method does NOT validate that Key.Algorithm, if set, aligns
// with Key.Curve.
func (k *Key) AlgorithmOrDefault() (Algorithm, error) {
if k.Algorithm != AlgorithmInvalid {
return k.Algorithm, nil
}
return k.deriveAlgorithm()
}
// Signer returns a Signer created using Key.
func (k *Key) Signer() (Signer, error) {
if err := k.Validate(); err != nil {
return nil, err
}
if k.KeyOps != nil {
signFound := false
for _, kop := range k.KeyOps {
if kop == KeyOpSign {
signFound = true
break
}
}
if !signFound {
return nil, ErrSignOpNotSupported
}
}
priv, err := k.PrivateKey()
if err != nil {
return nil, err
}
alg, err := k.AlgorithmOrDefault()
if err != nil {
return nil, err
}
var signer crypto.Signer
var ok bool
switch alg {
case AlgorithmES256, AlgorithmES384, AlgorithmES512:
signer, ok = priv.(*ecdsa.PrivateKey)
if !ok {
return nil, ErrInvalidPrivKey
}
case AlgorithmEd25519:
signer, ok = priv.(ed25519.PrivateKey)
if !ok {
return nil, ErrInvalidPrivKey
}
default:
return nil, ErrAlgorithmNotSupported
}
return NewSigner(alg, signer)
}
// Verifier returns a Verifier created using Key.
func (k *Key) Verifier() (Verifier, error) {
if err := k.Validate(); err != nil {
return nil, err
}
if k.KeyOps != nil {
verifyFound := false
for _, kop := range k.KeyOps {
if kop == KeyOpVerify {
verifyFound = true
break
}
}
if !verifyFound {
return nil, ErrVerifyOpNotSupported
}
}
pub, err := k.PublicKey()
if err != nil {
return nil, err
}
alg, err := k.AlgorithmOrDefault()
if err != nil {
return nil, err
}
return NewVerifier(alg, pub)
}
// deriveAlgorithm derives the intended algorithm for the key from its curve.
// The deriviation is based on the recommendation in RFC8152 that SHA-256 is
// only used with P-256, etc. For other combinations, the Algorithm in the Key
// must be explicitly set,so that this derivation is not used.
func (k *Key) deriveAlgorithm() (Algorithm, error) {
switch k.KeyType {
case KeyTypeEC2, KeyTypeOKP:
switch k.Curve {
case CurveP256:
return AlgorithmES256, nil
case CurveP384:
return AlgorithmES384, nil
case CurveP521:
return AlgorithmES512, nil
case CurveEd25519:
return AlgorithmEd25519, nil
default:
return AlgorithmInvalid, fmt.Errorf("unsupported curve %q", k.Curve.String())
}
default:
// Symmetric algorithms are not supported in the current inmplementation.
return AlgorithmInvalid, fmt.Errorf("unexpected key type %q", k.KeyType.String())
}
}
golang-github-veraison-go-cose-1.2.1/key_test.go 0000664 0000000 0000000 00000040631 14551116371 0021606 0 ustar 00root root 0000000 0000000 package cose
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"testing"
"github.com/fxamacker/cbor/v2"
)
func Test_KeyOp(t *testing.T) {
tvs := []struct {
Name string
Value KeyOp
}{
{"sign", KeyOpSign},
{"verify", KeyOpVerify},
{"encrypt", KeyOpEncrypt},
{"decrypt", KeyOpDecrypt},
{"wrapKey", KeyOpWrapKey},
{"unwrapKey", KeyOpUnwrapKey},
{"deriveKey", KeyOpDeriveKey},
{"deriveBits", KeyOpDeriveBits},
}
for _, tv := range tvs {
if tv.Name != tv.Value.String() {
t.Errorf(
"String value mismatch: expected %q, got %q",
tv.Name,
tv.Value.String(),
)
}
data, err := cbor.Marshal(tv.Name)
if err != nil {
t.Errorf("Unexpected error: %s", err)
return
}
var ko KeyOp
err = cbor.Unmarshal(data, &ko)
if err != nil {
t.Errorf("Unexpected error: %s", err)
return
}
if tv.Value != ko {
t.Errorf(
"Value mismatch: want %v, got %v",
tv.Value,
ko,
)
}
data, err = cbor.Marshal(int(tv.Value))
if err != nil {
t.Errorf("Unexpected error: %q", err)
return
}
err = cbor.Unmarshal(data, &ko)
if err != nil {
t.Errorf("Unexpected error: %q", err)
return
}
if tv.Value != ko {
t.Errorf(
"Value mismatch: want %v, got %v",
tv.Value,
ko,
)
}
}
var ko KeyOp
data := []byte{0x20}
err := ko.UnmarshalCBOR(data)
assertEqualError(t, err, "unknown key_ops value -1")
data = []byte{0x18, 0xff}
err = ko.UnmarshalCBOR(data)
assertEqualError(t, err, "unknown key_ops value 255")
data = []byte{0x63, 0x66, 0x6f, 0x6f}
err = ko.UnmarshalCBOR(data)
assertEqualError(t, err, "unknown key_ops value \"foo\"")
data = []byte{0x40}
err = ko.UnmarshalCBOR(data)
assertEqualError(t, err, "invalid key_ops value must be int or string, found []uint8")
if "MAC create" != KeyOpMACCreate.String() {
t.Errorf("Unexpected value: %q", KeyOpMACCreate.String())
}
if "MAC verify" != KeyOpMACVerify.String() {
t.Errorf("Unexpected value: %q", KeyOpMACVerify.String())
}
if "unknown key_op value 42" != KeyOp(42).String() {
t.Errorf("Unexpected value: %q", KeyOp(42).String())
}
}
func Test_KeyType(t *testing.T) {
var ko KeyType
data := []byte{0x20}
err := ko.UnmarshalCBOR(data)
assertEqualError(t, err, "unknown key type value -1")
data = []byte{0x00}
err = ko.UnmarshalCBOR(data)
assertEqualError(t, err, "invalid key type value 0")
data = []byte{0x03}
err = ko.UnmarshalCBOR(data)
assertEqualError(t, err, "unknown key type value 3")
data = []byte{0x63, 0x66, 0x6f, 0x6f}
err = ko.UnmarshalCBOR(data)
assertEqualError(t, err, "unknown key type value \"foo\"")
data = []byte{0x40}
err = ko.UnmarshalCBOR(data)
assertEqualError(t, err, "invalid key type value: must be int or string, found []uint8")
}
func Test_Curve(t *testing.T) {
var c Curve
data := []byte{0x20}
err := c.UnmarshalCBOR(data)
assertEqualError(t, err, "unknown curve value -1")
data = []byte{0x00}
err = c.UnmarshalCBOR(data)
assertEqualError(t, err, "unknown curve value 0")
data = []byte{0x63, 0x66, 0x6f, 0x6f}
err = c.UnmarshalCBOR(data)
assertEqualError(t, err, "unknown curve value \"foo\"")
data = []byte{0x40}
err = c.UnmarshalCBOR(data)
assertEqualError(t, err, "invalid curve value: must be int or string, found []uint8")
if "unknown curve value 42" != Curve(42).String() {
t.Errorf("Unexpected string value %q", Curve(42).String())
}
}
func Test_Key_UnmarshalCBOR(t *testing.T) {
tvs := []struct {
Name string
Value []byte
WantErr string
Validate func(k *Key)
}{
{
Name: "ok OKP",
Value: []byte{
0xa5, // map (5)
0x01, 0x01, // kty: OKP
0x03, 0x27, // alg: EdDSA w/ Ed25519
0x04, // key ops
0x81, // array (1)
0x02, // verify
0x20, 0x06, // curve: Ed25519
0x21, 0x58, 0x20, // x-coordinate: bytes(32)
0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, // 32-byte value
0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9,
0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9,
0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46,
},
WantErr: "",
Validate: func(k *Key) {
assertEqual(t, KeyTypeOKP, k.KeyType)
assertEqual(t, AlgorithmEd25519, k.Algorithm)
assertEqual(t, CurveEd25519, k.Curve)
assertEqual(t, []KeyOp{KeyOpVerify}, k.KeyOps)
assertEqual(t, []byte{
0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3,
0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9,
0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9,
0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46,
},
k.X,
)
assertEqual(t, []byte(nil), k.K)
},
},
{
Name: "invalid key type",
Value: []byte{
0xa1, // map (2)
0x01, 0x00, // kty: invalid
},
WantErr: "invalid key type value 0",
Validate: nil,
},
{
Name: "missing curve OKP",
Value: []byte{
0xa1, // map (2)
0x01, 0x01, // kty: OKP
},
WantErr: "missing Curve parameter (required for OKP key type)",
Validate: nil,
},
{
Name: "missing curve EC2",
Value: []byte{
0xa1, // map (2)
0x01, 0x02, // kty: EC2
},
WantErr: "missing Curve parameter (required for EC2 key type)",
Validate: nil,
},
{
Name: "invalid curve OKP",
Value: []byte{
0xa2, // map (2)
0x01, 0x01, // kty: OKP
0x20, 0x01, // curve: CurveP256
},
WantErr: "OKP curve must be X25519, X448, Ed25519, or Ed448; found \"P-256\"",
Validate: nil,
},
{
Name: "invalid curve EC2",
Value: []byte{
0xa2, // map (2)
0x01, 0x02, // kty: EC2
0x20, 0x06, // curve: CurveEd25519
},
WantErr: "EC2 curve must be P-256, P-384, or P-521; found \"Ed25519\"",
Validate: nil,
},
{
Name: "ok Symmetric",
Value: []byte{
0xa4, // map (4)
0x01, 0x04, // kty: Symmetric
0x03, 0x38, 0x24, // alg: PS256
0x04, // key ops
0x81, // array (1)
0x02, // verify
0x20, 0x58, 0x20, // k: bytes(32)
0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, // 32-byte value
0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9,
0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9,
0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46,
},
WantErr: "",
Validate: func(k *Key) {
assertEqual(t, KeyTypeSymmetric, k.KeyType)
assertEqual(t, AlgorithmPS256, k.Algorithm)
assertEqual(t, int64(0), int64(k.Curve))
assertEqual(t, []KeyOp{KeyOpVerify}, k.KeyOps)
assertEqual(t, []byte{
0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3,
0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9,
0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9,
0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46,
},
k.K,
)
},
},
{
Name: "missing K",
Value: []byte{
0xa1, // map (1)
0x01, 0x04, // kty: Symmetric
},
WantErr: "missing K parameter (required for Symmetric key type)",
Validate: nil,
},
{
Name: "wrong algorithm",
Value: []byte{
0xa4, // map (3)
0x01, 0x01, // kty: OKP
0x03, 0x26, // alg: ECDSA w/ SHA-256
0x20, 0x06, // curve: Ed25519
0x21, 0x58, 0x20, // x-coordinate: bytes(32)
0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, // 32-byte value
0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9,
0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9,
0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46,
},
WantErr: "found algorithm \"ES256\" (expected \"EdDSA\")",
Validate: nil,
},
}
for _, tv := range tvs {
t.Run(tv.Name, func(t *testing.T) {
var k Key
err := k.UnmarshalCBOR(tv.Value)
if tv.WantErr != "" {
if err == nil || err.Error() != tv.WantErr {
t.Errorf("Unexpected error: want %q, got %q", tv.WantErr, err)
}
} else {
tv.Validate(&k)
}
})
}
}
func Test_Key_MarshalCBOR(t *testing.T) {
k := Key{
KeyType: KeyTypeOKP,
KeyOps: []KeyOp{KeyOpVerify, KeyOpEncrypt},
X: []byte{
0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3,
0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9,
0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9,
0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46,
},
Algorithm: AlgorithmEd25519,
Curve: CurveEd25519,
}
data, err := k.MarshalCBOR()
if err != nil {
t.Errorf("Unexpected error: %s", err)
return
}
expected := []byte{
0xa5, // map (5)
0x01, 0x01, // kty: OKP
0x03, 0x27, // alg: EdDSA w/ Ed25519
0x04, // key ops
0x82, // array (2)
0x02, 0x03, // verify, encrypt
0x20, 0x06, // curve: Ed25519
0x21, 0x58, 0x20, // x-coordinate: bytes(32)
0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, // 32-byte value
0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9,
0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9,
0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46,
}
if !bytes.Equal(expected, data) {
t.Errorf("Bad marshal: %v", data)
}
k = Key{
KeyType: KeyTypeSymmetric,
K: []byte{
0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3,
0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9,
0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9,
0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46,
},
}
data, err = k.MarshalCBOR()
if err != nil {
t.Errorf("Unexpected error: %s", err)
return
}
expected = []byte{
0xa2, // map (2)
0x01, 0x04, // kty: Symmetric
0x20, 0x58, 0x20, // K: bytes(32)
0x15, 0x52, 0x2e, 0xf1, 0x57, 0x29, 0xcc, 0xf3, // 32-byte value
0x95, 0x09, 0xea, 0x5c, 0x15, 0xa2, 0x6b, 0xe9,
0x49, 0xe3, 0x88, 0x07, 0xa5, 0xc2, 0x6e, 0xf9,
0x28, 0x14, 0x87, 0xef, 0x4a, 0xe6, 0x7b, 0x46,
}
if !bytes.Equal(expected, data) {
t.Errorf("Bad marshal: %v", data)
}
k.KeyType = KeyType(42)
_, err = k.MarshalCBOR()
wantErr := "invalid key type: \"unknown key type value 42\""
if err == nil || err.Error() != wantErr {
t.Errorf("Unexpected error: want %q, got %q", wantErr, err)
}
}
func Test_Key_Create_and_Validate(t *testing.T) {
x := []byte{
0x30, 0xa0, 0x42, 0x4c, 0xd2, 0x1c, 0x29, 0x44,
0x83, 0x8a, 0x2d, 0x75, 0xc9, 0x2b, 0x37, 0xe7,
0x6e, 0xa2, 0x0d, 0x9f, 0x00, 0x89, 0x3a, 0x3b,
0x4e, 0xee, 0x8a, 0x3c, 0x0a, 0xaf, 0xec, 0x3e,
}
y := []byte{
0xe0, 0x4b, 0x65, 0xe9, 0x24, 0x56, 0xd9, 0x88,
0x8b, 0x52, 0xb3, 0x79, 0xbd, 0xfb, 0xd5, 0x1e,
0xe8, 0x69, 0xef, 0x1f, 0x0f, 0xc6, 0x5b, 0x66,
0x59, 0x69, 0x5b, 0x6c, 0xce, 0x08, 0x17, 0x23,
}
key, err := NewOKPKey(AlgorithmEd25519, x, nil)
requireNoError(t, err)
assertEqual(t, KeyTypeOKP, key.KeyType)
assertEqual(t, x, key.X)
_, err = NewOKPKey(AlgorithmES256, x, nil)
assertEqualError(t, err, "unsupported algorithm \"ES256\"")
_, err = NewEC2Key(AlgorithmEd25519, x, y, nil)
assertEqualError(t, err, "unsupported algorithm \"EdDSA\"")
key, err = NewEC2Key(AlgorithmES256, x, y, nil)
requireNoError(t, err)
assertEqual(t, KeyTypeEC2, key.KeyType)
assertEqual(t, x, key.X)
assertEqual(t, y, key.Y)
key, err = NewSymmetricKey(x)
requireNoError(t, err)
assertEqual(t, x, key.K)
key.KeyType = KeyType(7)
err = key.Validate()
assertEqualError(t, err, "unknown key type value 7")
_, err = NewKeyFromPublic(AlgorithmES256,
crypto.PublicKey([]byte{0xde, 0xad, 0xbe, 0xef}))
assertEqualError(t, err, "ES256: invalid public key")
_, err = NewKeyFromPublic(AlgorithmEd25519,
crypto.PublicKey([]byte{0xde, 0xad, 0xbe, 0xef}))
assertEqualError(t, err, "EdDSA: invalid public key")
_, err = NewKeyFromPublic(AlgorithmInvalid,
crypto.PublicKey([]byte{0xde, 0xad, 0xbe, 0xef}))
assertEqualError(t, err, "algorithm not supported")
_, err = NewKeyFromPrivate(AlgorithmES256,
crypto.PublicKey([]byte{0xde, 0xad, 0xbe, 0xef}))
assertEqualError(t, err, "ES256: invalid private key")
_, err = NewKeyFromPrivate(AlgorithmEd25519,
crypto.PublicKey([]byte{0xde, 0xad, 0xbe, 0xef}))
assertEqualError(t, err, "EdDSA: invalid private key")
_, err = NewKeyFromPrivate(AlgorithmInvalid,
crypto.PublicKey([]byte{0xde, 0xad, 0xbe, 0xef}))
assertEqualError(t, err, "algorithm not supported")
}
func Test_Key_ed25519_signature_round_trip(t *testing.T) {
pub, priv, err := ed25519.GenerateKey(rand.Reader)
requireNoError(t, err)
key, err := NewKeyFromPrivate(AlgorithmEd25519, priv)
requireNoError(t, err)
assertEqual(t, AlgorithmEd25519, key.Algorithm)
assertEqual(t, CurveEd25519, key.Curve)
assertEqual(t, pub, key.X)
assertEqual(t, priv[:32], key.D)
signer, err := key.Signer()
requireNoError(t, err)
message := []byte("foo bar")
sig, err := signer.Sign(rand.Reader, message)
requireNoError(t, err)
key, err = NewKeyFromPublic(AlgorithmEd25519, pub)
requireNoError(t, err)
assertEqual(t, AlgorithmEd25519, key.Algorithm)
assertEqual(t, CurveEd25519, key.Curve)
assertEqual(t, pub, key.X)
verifier, err := key.Verifier()
requireNoError(t, err)
err = verifier.Verify(message, sig)
requireNoError(t, err)
}
func Test_Key_ecdsa_signature_round_trip(t *testing.T) {
for _, tv := range []struct {
EC elliptic.Curve
Curve Curve
Algorithm Algorithm
}{
{elliptic.P256(), CurveP256, AlgorithmES256},
{elliptic.P384(), CurveP384, AlgorithmES384},
{elliptic.P521(), CurveP521, AlgorithmES512},
} {
t.Run(tv.Curve.String(), func(t *testing.T) {
priv, err := ecdsa.GenerateKey(tv.EC, rand.Reader)
requireNoError(t, err)
key, err := NewKeyFromPrivate(tv.Algorithm, priv)
requireNoError(t, err)
assertEqual(t, tv.Algorithm, key.Algorithm)
assertEqual(t, tv.Curve, key.Curve)
assertEqual(t, priv.X.Bytes(), key.X)
assertEqual(t, priv.Y.Bytes(), key.Y)
assertEqual(t, priv.D.Bytes(), key.D)
signer, err := key.Signer()
requireNoError(t, err)
message := []byte("foo bar")
sig, err := signer.Sign(rand.Reader, message)
requireNoError(t, err)
pub := priv.Public()
key, err = NewKeyFromPublic(tv.Algorithm, pub)
requireNoError(t, err)
assertEqual(t, tv.Algorithm, key.Algorithm)
assertEqual(t, tv.Curve, key.Curve)
assertEqual(t, priv.X.Bytes(), key.X)
assertEqual(t, priv.Y.Bytes(), key.Y)
verifier, err := key.Verifier()
requireNoError(t, err)
err = verifier.Verify(message, sig)
requireNoError(t, err)
})
}
}
func Test_Key_derive_algorithm(t *testing.T) {
k := Key{
KeyType: KeyTypeOKP,
Curve: CurveX448,
}
_, err := k.AlgorithmOrDefault()
assertEqualError(t, err, "unsupported curve \"X448\"")
k = Key{
KeyType: KeyTypeOKP,
Curve: CurveEd25519,
}
alg, err := k.AlgorithmOrDefault()
requireNoError(t, err)
assertEqual(t, AlgorithmEd25519, alg)
}
func Test_Key_signer_validation(t *testing.T) {
pub, priv, err := ed25519.GenerateKey(rand.Reader)
requireNoError(t, err)
key, err := NewKeyFromPublic(AlgorithmEd25519, pub)
requireNoError(t, err)
_, err = key.Signer()
assertEqualError(t, err, ErrNotPrivKey.Error())
key, err = NewKeyFromPrivate(AlgorithmEd25519, priv)
requireNoError(t, err)
key.KeyType = KeyTypeEC2
_, err = key.Signer()
assertEqualError(t, err, "EC2 curve must be P-256, P-384, or P-521; found \"Ed25519\"")
key.Curve = CurveP256
_, err = key.Signer()
assertEqualError(t, err, "found algorithm \"EdDSA\" (expected \"ES256\")")
key.KeyType = KeyTypeOKP
key.Algorithm = AlgorithmEd25519
key.Curve = CurveEd25519
key.KeyOps = []KeyOp{}
_, err = key.Signer()
assertEqualError(t, err, ErrSignOpNotSupported.Error())
key.KeyOps = []KeyOp{KeyOpSign}
_, err = key.Signer()
requireNoError(t, err)
key.Algorithm = AlgorithmES256
_, err = key.Signer()
assertEqualError(t, err, "found algorithm \"ES256\" (expected \"EdDSA\")")
key.Curve = CurveX448
_, err = key.Signer()
assertEqualError(t, err, "unsupported curve \"X448\"")
}
func Test_Key_verifier_validation(t *testing.T) {
pub, _, err := ed25519.GenerateKey(rand.Reader)
requireNoError(t, err)
key, err := NewKeyFromPublic(AlgorithmEd25519, pub)
requireNoError(t, err)
_, err = key.Verifier()
requireNoError(t, err)
key.KeyType = KeyTypeEC2
_, err = key.Verifier()
assertEqualError(t, err, "EC2 curve must be P-256, P-384, or P-521; found \"Ed25519\"")
key.KeyType = KeyTypeOKP
key.KeyOps = []KeyOp{}
_, err = key.Verifier()
assertEqualError(t, err, ErrVerifyOpNotSupported.Error())
key.KeyOps = []KeyOp{KeyOpVerify}
_, err = key.Verifier()
requireNoError(t, err)
}
func Test_Key_crypto_keys(t *testing.T) {
k := Key{
KeyType: KeyType(7),
}
_, err := k.PublicKey()
assertEqualError(t, err, "unexpected key type \"unknown key type value 7\"")
_, err = k.PrivateKey()
assertEqualError(t, err, "unexpected key type \"unknown key type value 7\"")
k = Key{
KeyType: KeyTypeOKP,
Curve: CurveX448,
}
_, err = k.PublicKey()
assertEqualError(t, err, "unsupported curve \"X448\"")
_, err = k.PrivateKey()
assertEqualError(t, err, "unsupported curve \"X448\"")
}
golang-github-veraison-go-cose-1.2.1/reports/ 0000775 0000000 0000000 00000000000 14551116371 0021122 5 ustar 00root root 0000000 0000000 golang-github-veraison-go-cose-1.2.1/reports/NCC_Microsoft-go-cose-Report_2022-05-26_v1.0.pdf 0000664 0000000 0000000 00001114401 14551116371 0030732 0 ustar 00root root 0000000 0000000 %PDF-1.7
%π€
1 0 obj
<<
/Type /Pages
/Kids [ 6 0 R 8 0 R 13 0 R 17 0 R 19 0 R 25 0 R 29 0 R 35 0 R 37 0 R 39 0 R 45 0 R 54 0 R 62 0 R 74 0 R 79 0 R 83 0 R 89 0 R 93 0 R 95 0 R ]
/Count 19
>>
endobj
2 0 obj
<<
/Title (go-cose Security Assessment)
/Author
/Keywords (NCC Group report, E002762)
/Creator (Limb 84ef8528)
/Producer (WeasyPrint 55.0b1)
>>
endobj
3 0 obj
<<
/Type /Catalog
/Pages 1 0 R
/Outlines 125 0 R
/Names <<
/Dests <<
/Names [ (finding:6XB) [ 25 0 R /XYZ 85.037795 736.061024 0 ] (section:2JA) [ 35 0 R /XYZ 85.037795 770.561024 0 ] (section:BJM) [ 6 0 R /XYZ 0 792 0 ] (section:BNX) [ 8 0 R /XYZ 85.037795 770.561024 0 ] (section:JQV) [ 79 0 R /XYZ 85.037795 770.561024 0 ] (section:KPF) [ 17 0 R /XYZ 85.037795 770.561024 0 ] (section:NGF) [ 95 0 R /XYZ 85.037795 770.561024 0 ] (section:QCH) [ 39 0 R /XYZ 85.037795 770.561024 0 ] (section:QLJ) [ 19 0 R /XYZ 85.037795 770.561024 0 ] (section:T6X) [ 25 0 R /XYZ 85.037795 770.561024 0 ] ]
>>
>>
>>
endobj
4 0 obj
<<
/ExtGState <<
/a1.0 <<
/ca 1
>>
/a0.027450980392156862 <<
/ca 0.027451
>>
>>
/XObject <<
>>
/Pattern <<
/p0 167 0 R
/p1 171 0 R
/p2 175 0 R
/p3 179 0 R
/p4 183 0 R
/p5 187 0 R
/p6 191 0 R
/p7 195 0 R
/p8 199 0 R
/p9 203 0 R
/p10 207 0 R
/p11 211 0 R
/p12 215 0 R
/p13 219 0 R
/p14 223 0 R
/p15 227 0 R
/p16 231 0 R
/p17 235 0 R
/p18 239 0 R
/p19 243 0 R
/p20 247 0 R
/p21 251 0 R
/p22 255 0 R
/p23 259 0 R
/p24 263 0 R
/p25 267 0 R
/p26 271 0 R
/p27 275 0 R
/p28 279 0 R
/p29 283 0 R
/p30 287 0 R
/p31 291 0 R
/p32 295 0 R
/p33 299 0 R
/p34 303 0 R
/p35 307 0 R
/p36 311 0 R
/p37 315 0 R
/p38 319 0 R
/p39 323 0 R
/p40 327 0 R
/p41 331 0 R
/p42 335 0 R
/p43 339 0 R
/p44 343 0 R
/p45 347 0 R
/p46 351 0 R
/p47 355 0 R
/p48 359 0 R
/p49 363 0 R
/p50 367 0 R
/p51 371 0 R
/p52 375 0 R
/p53 379 0 R
/p54 383 0 R
/p55 387 0 R
/p56 391 0 R
/p57 395 0 R
/p58 399 0 R
/p59 403 0 R
/p60 407 0 R
/p61 411 0 R
/p62 415 0 R
/p63 419 0 R
/p64 423 0 R
/p65 427 0 R
/p66 431 0 R
/p67 435 0 R
/p68 439 0 R
/p69 443 0 R
/p70 447 0 R
/p71 451 0 R
/p72 455 0 R
/p73 459 0 R
/p74 463 0 R
/p75 467 0 R
/p76 471 0 R
/p77 475 0 R
/p78 479 0 R
/p79 483 0 R
/p80 487 0 R
/p81 491 0 R
/p82 495 0 R
/p83 499 0 R
/p84 503 0 R
/p85 507 0 R
/p86 511 0 R
/p87 515 0 R
/p88 519 0 R
/p89 523 0 R
/p90 527 0 R
/p91 531 0 R
/p92 535 0 R
/p93 539 0 R
/p94 543 0 R
/p95 547 0 R
/p96 551 0 R
/p97 555 0 R
/p98 559 0 R
/p99 563 0 R
/p100 567 0 R
/p101 571 0 R
/p102 575 0 R
/p103 579 0 R
>>
/Shading <<
>>
/Font 166 0 R
>>
endobj
5 0 obj
<<
/Filter /FlateDecode
/Length 5105
>>
stream
xν]ΫΉ}――¨gJσ~
xΓ}X[1lψ‘έf±ΜμΓώώ Lfu%+Y₯Φά
UΑL&'HV΅<όΎψΟGu~ωzϊω$&o~RΣβMjώόϋ9_&ΌώόΎzύΒ»0ιΌ
g©¦¨£ΊtS:2N*mΥY[=cη―\i;y¬uη/'δ2¬Κ~Ύ~όΝι+³~rτγΟΏ|Zιϊ*,―4VΔιJο'ςφόΛO§ί>ΛIϊΣηί€©sr²:(:9Kg&γd4:ϋάt"΅mΗ(Xγ>N2jνμY‘kM]Λ³RΞ³uRΣέωy2^―ήLωs½DNj§L7π!ΨψΎδGιͺv¨ςόΏΎΗΏ>σΏ§‘ίϋΊoΥφΘI§Νω ςkT)@mήδfΌψzRHG-Jψ)*E/!qΡ\Θ΅0 κι,cΌI7ji¦t;ΩάέΚNΞΨ,ΚΤ"QΙϊL]Χ)ί²άMR¦ώVi"Z£K{xΘ²TM>Θ,ΥSp~ΧιF₯ε€dL/£Κ/
ό/χ¬<^:w!}9)γ'αΓεΥΚΞ/cQORΛ=Kα>dTΙ΄ε1€a³¦ΌΌRΨ6κG#WΉ;MΣBcZ<X&ό€,
¦AΔ.btβSNuR,}ΝBtΑGαb
όΩPP‘Ϊ±b>|ΚX-³A 3G *]K@F 3Α$τΕ <<'u ΩιaΉVdtrZ³F€+Ξ'Mb[ΒBΓ₯Ιϊ;νmψ!€UΕ,$· |¦/
*΄Οέβ₯΅μ³Τ
Γ°g5ς ΕΎ8u±y=b_vΑΜΧ Ξ;xγm Jr-h
ZΞΝ@σNΨK=AΩ/,ΕΎͺΩWg=΄V|ΦuluΌ±νΏΠ
YGyy5¦’'KRΐ?\"₯»RG%°ς\H€7F(`τEw±ΊΘΩ―Iξ³άΥ‘ϊΖ<³ΩεΜ¨δ2AχjI6PXd*BΛWΘθΕυ
Z*Ω φW#_!
ϊφΉrigSϊΉZi\|F.ΐΔdς%
riέeEΆΘ₯Ι·rΖΛΒLX’Qγ¦-r§JΆj
γΝ-D―X΅E.Δ`+=χL6S|ΚFgrρ>W
ΌHζΡF\n³ΣyΙ°J¦ωrͺ_vFzΑMΟϊ HjhΕδ¦π|ς0ΎςποΈ'ΐ¦)±~) Ι₯sΈπΚ<ωS
f°³`DjδIs}Σo+>@b·\.]UζλyΘΟl§(ΙYsb_Sο5ν4ςF΅dίy³φ―Zj/°dSΗaβ*amE΄Yυ¨½28;Κ·βBB)ΚΛ©#B-κ80ύ!Ο/i:θιΛ)ͺ\ͺ<Ϊx©gVqpRG£v""Ρ°2Ν―)γετ_§θgjj+§ύΊ{ Nθ\ΑAυe|Νm‘±*l°= @<²#’ΩA]H‘λτΔWWJ\(έF
ΙΔ8ψτ!«Ιθ(³‘Α3Dδ4ΣJ*0>ΉΖtχF}i
Dΰ~pQ
$κ¬RδYdΖΧ "ΧZkBτ΄Έ³8κ΅0JI?T>jP²uΰ`IΑΜ8oMkύpέ1φ‘WΜήΑS²ύ,ΆvV&Μ’m)΅’p0δF>6quh\ΡΠκJlJ_N)ΩδΫ« 2qFΒ9¬Z)ΖaMι£cΤ>δ«Ρ3ΧSσ―*Π?Wx 4ZΚO₯,,ΘͺτΩΓ&BΧ
©*ω΄σrΠ~Irλ1χζ°¨€Π΅¬§zζIλIUΥζ¨$±‘ΚΜΗloΒfx"'FnA_ΌΞΆͺ,ΫΪ7¬ buεΐTθqνf1$Wf¨uZ)Βl}4r°―΄p«1$ξNͺ₯δgΩ@ΞJΘ
SΜ~¦ω,·r2΅V
Π'}F\\©ͺ.aΩΥ9ΚΩm WO+9@+'`'lMb^0Δκ’d±-E°S#§§ΗUΎ©Pez²u
ε/d;TΥή/ΕΦW
Pp£dO@Zξ §RόPί9ρΞbTsF©ζΥΖ&0ωZDΗn TIPq)έM!;EΓB
nsfρΨf₯H'
%/{δ=¦r^WρΘΛ\ΔR6R+Ξ+I#9#1Ή.λσK+wT½ΡRι^ΣSο^π3:ϋ!t_r°a―kp―d]d
γ9ΞdCH’Z_6|Ι
©,Ό½75 J4g)Χ&1`Ϋ&ώJicvR"――VTΊ½ξ.o_
z‘XpWΖ7’ΛeΓbΊ1Φε5ω’£FN―μSΥ( ₯Ί8cE«YΡKΚ―n,ΥτάHQu4λxΧ
»·8±Γ€³ζ[¦AΥΟrIK
2s
Έ%-€Ξ+ V±ψ)ΖK1UNiένΥυZRyΛ½WΖ°·aΡ0sΤ»S½ͺb
p=)9=/}]6 jΧl6 ;0Ξ%FHTραΠ°,ΟvNωRQ^αλη,qΊ2ϊEG΄²T½ΈΟ
|ύφ%1tYFΣδ})3ΡΧΔ/₯W₯I_ΫWβ6υ·
ΊμΩ}a=1σβ,ΔbφJE°nX.kU¬ΝdSδ>η\ίΆ%Π―*@ΝΥΦ"σH0―s$«ΐ¦Ξ]Hα^]Ω1T¬+Χi7Δ?3'3\i0΅‘lXf m³
ΘΛϊΤdxΕI©¬W"¬f1}ΩX4«ΆyΊAΪyΆZΚΑ₯Ρ΄.Y{Χ\μmλFh<κ0iRΜ`!ΙIΡ’V
9±'«ΦΡΐί\m(Ψal5wΑlC$yέΊq΄°Ι
Ν€ΘA£BUρkKν7Έ
ζyΛ$5
±²+©ΙψΝhBΟ λΊ(y1ε?7plΈΐ3Ή
Ό.&έ κhDY
FϊΩRΎΐ³4J’€-Νδ0`~"ΰ7%χYlλϊ-Ήρ,φ―©ΘΦ8φK ηKiRqΟb]τ±’βύrΥ―+ΌΤ .ξLm`zΡ«κ ¨.Όςu%_
¨^,hΡK
Bh·¦R¨`KώS «,ΤHM«=