A golang library for the COSE specification
The verasion/go-cose project is actively maintained. See current releases.
The project was initially forked from the upstream mozilla-services/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 as the active project.
We thank the Mozilla maintainers and contributors for their great work that formed the base of the veraison/go-cose project.
The veraison/go-cose project is an open source community effort.
You can reach the go-cose community via::
- Mailing List
- Bi-weekly meetings: 08:00-09:00 Pacific
- Meeting Notes
- Meeting Recordings
Participation in the go-cose community is governed by the Veraison CODE_OF_CONDUCT.md and GOVERNANCE.md
This project has adopted the Contributor Covenant Code of Conduct.
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
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 for more examples.
Untagged COSE_Sign1 messages can be signed and verified as above, using
cose.UntaggedSign1Message
instead of cose.Sign1Message
.
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 for the API usage.
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
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 for examples.
go-cose supports two different signature structures:
- cose.Sign1Message implements COSE_Sign1.
- cose.SignMessage implements COSE_Sign.
⚠️ 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.
go-cose supports COSE_Countersignature, check cose.Countersignature.
⚠️ The COSE_Countersignature API is currently EXPERIMENTAL and may be changed or removed in a later release.
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.
The supported algorithms can be extended at runtime by using cose.RegisterAlgorithm.
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.
go-cose
runs the GlueCOSE test suite on every local go test
execution.
These are also executed on every CI job.
go-cose
implements several fuzz tests using Go's native fuzzing.
Fuzzing requires Go 1.18 or higher, and can be executed as follows:
go test -fuzz=FuzzSign1
go-cose
undergoes periodic security review. The security review reports are located here