veraison/go-cose
GitHub: veraison/go-cose
Stars: 62 | Forks: 30
# 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
The verasion/go-cose project is actively maintained.
See [current releases](https://github.com/veraison/go-cose/releases).
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.
## 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:
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:
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:
go get github.com/veraison/go-cose@main
## Usage
### Signing and Verification
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:
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:
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`.
#### Signing and Verification of payload digest
When `cose.NewSigner` is used with PS{256,384,512} or ES{256,384,512}, the returned signer
can be casted to the `cose.DigestSigner` interface, whose `SignDigest` method signs an
already digested message.
When `cose.NewVerifier` is used with PS{256,384,512} or ES{256,384,512}, the returned verifier
can be casted to the `cose.DigestVerifier` interface, whose `VerifyDigest` method verifies an
already digested message.
Please refer to [example_test.go](./example_test.go) for the API usage.
### 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:
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.AlgorithmEdDSA: none
### Countersigning
It is possible to countersign `cose.Sign1Message`, `cose.SignMessage`, `cose.Signature` and
`cose.Countersignature` objects and add them as unprotected headers. In order to do so, first create
a countersignature holder with `cose.NewCountersignature()` and call its `Sign` function passing
the parent object which is going to be countersigned. Then assign the countersignature as an
unprotected header `cose.HeaderLabelCounterSignatureV2` or, if preferred, maintain it as a
detached countersignature.
When verifying countersignatures, it is necessary to pass the parent object in the `Verify` function
of the countersignature holder.
See [example_test.go](./example_test.go) for examples.
## 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).
### Countersignatures
go-cose supports [COSE_Countersignature](https://tools.ietf.org/html/rfc9338#section-3.1), check [cose.Countersignature](https://pkg.go.dev/github.com/veraison/go-cose#Countersignature).
### 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
It is possible to use custom algorithms with this library, for example:
package cose_test
import (
"errors"
"io"
"testing"
"github.com/cloudflare/circl/sign"
"github.com/cloudflare/circl/sign/schemes"
"github.com/veraison/go-cose"
)
type customKeySigner struct {
alg cose.Algorithm
key sign.PrivateKey
}
func (ks *customKeySigner) Algorithm() cose.Algorithm {
return ks.alg
}
func (ks *customKeySigner) Sign(rand io.Reader, content []byte) ([]byte, error) {
suite := schemes.ByName("ML-DSA-44")
return suite.Sign(ks.key, content, nil), nil
}
type customKeyVerifier struct {
alg cose.Algorithm
key sign.PublicKey
}
func (ks *customKeyVerifier) Algorithm() cose.Algorithm {
return ks.alg
}
func (ks *customKeyVerifier) Verify(content []byte, signature []byte) error {
suite := schemes.ByName("ML-DSA-44")
valid := suite.Verify(ks.key, content, signature, nil)
if !valid {
return errors.New("Signature not from public key")
}
return nil
}
func TestCustomSigner(t *testing.T) {
const (
COSE_ALG_ML_DSA_44 = -48
)
suite := schemes.ByName("ML-DSA-44")
var seed [32]byte // zero seed
pub, priv := suite.DeriveKey(seed[:])
var ks cose.Signer = &customKeySigner{
alg: COSE_ALG_ML_DSA_44,
key: priv,
}
var kv = customKeyVerifier{
alg: COSE_ALG_ML_DSA_44,
key: pub,
}
headers := cose.Headers{
Protected: cose.ProtectedHeader{
cose.HeaderLabelAlgorithm: COSE_ALG_ML_DSA_44,
cose.HeaderLabelKeyID: []byte("key-42"),
},
}
var payload = []byte("hello post quantum signatures")
signature, _ := cose.Sign1(nil, ks, headers, payload, nil)
var sign1 cose.Sign1Message
_ = sign1.UnmarshalCBOR(signature)
var verifier cose.Verifier = &kv
verifyError := sign1.Verify(nil, verifier)
if verifyError != nil {
t.Fatalf("Verification failed")
} else {
// fmt.Println(cbor.Diagnose(signature))
// 18([
// <<{
// / alg / 1: -48,
// / kid / 4: h'6B65792D3432'}
// >>,
// {},
// h'4974...722e',
// h'cb5a...293b'
// ])
}
}
### 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:
go test -fuzz=FuzzSign1
### Security Reviews
`go-cose` undergoes periodic security review. The security review reports are located [here](./reports)
标签:EVTX分析