cose

package module
v1.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2024 License: MPL-2.0 Imports: 15 Imported by: 101

README

go-cose

go.dev tests codecov

A golang library for the COSE specification

Project Status

Current Release: go-cose v1.1.0

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.

Community

The veraison/go-cose project is an open source community effort.

You can reach the go-cose community via::

Participation in the go-cose community is governed by the Veraison CODE_OF_CONDUCT.md and GOVERNANCE.md

Code of Conduct

This project has adopted the Contributor Covenant Code of Conduct.

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 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 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 for examples.

Features

Signing and Verifying Objects

go-cose supports two different signature structures:

⚠ 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.

Countersignatures

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.

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.

API docs

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 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.

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

Documentation

Index

Examples

Constants

View Source
const (
	CBORTagSignMessage  = 98
	CBORTagSign1Message = 18
)

CBOR Tags for COSE signatures registered in the IANA "CBOR Tags" registry.

Reference: https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml#tags

View Source
const (
	CWTClaimIssuer         int64 = 1
	CWTClaimSubject        int64 = 2
	CWTClaimAudience       int64 = 3
	CWTClaimExpirationTime int64 = 4
	CWTClaimNotBefore      int64 = 5
	CWTClaimIssuedAt       int64 = 6
	CWTClaimCWTID          int64 = 7
	CWTClaimConfirmation   int64 = 8
	CWTClaimScope          int64 = 9
)

https://www.iana.org/assignments/cwt/cwt.xhtml#claims-registry

View Source
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
	HeaderLabelCounterSignatureV2  int64 = 11
	HeaderLabelCounterSignature0V2 int64 = 12
	HeaderLabelCWTClaims           int64 = 15
	HeaderLabelType                int64 = 16
	HeaderLabelX5Bag               int64 = 32
	HeaderLabelX5Chain             int64 = 33
	HeaderLabelX5T                 int64 = 34
	HeaderLabelX5U                 int64 = 35
)

COSE Header labels registered in the IANA "COSE Header Parameters" registry.

Reference: https://www.iana.org/assignments/cose/cose.xhtml#header-parameters

View Source
const (
	KeyLabelOKPCurve int64 = -1
	KeyLabelOKPX     int64 = -2
	KeyLabelOKPD     int64 = -4

	KeyLabelEC2Curve int64 = -1
	KeyLabelEC2X     int64 = -2
	KeyLabelEC2Y     int64 = -3
	KeyLabelEC2D     int64 = -4

	KeyLabelSymmetricK int64 = -1
)

Variables

View Source
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")
	ErrInvalidKey            = errors.New("invalid key")
	ErrInvalidPubKey         = errors.New("invalid public key")
	ErrInvalidPrivKey        = errors.New("invalid private key")
	ErrNotPrivKey            = errors.New("not a private key")
	ErrOpNotSupported        = errors.New("key_op not supported by key")
	ErrEC2NoPub              = errors.New("cannot create PrivateKey from EC2 key: missing x or y")
	ErrOKPNoPub              = errors.New("cannot create PrivateKey from OKP key: missing x")
)

Common errors

Functions

func Countersign0 added in v1.3.0

func Countersign0(rand io.Reader, signer Signer, parent any, external []byte) ([]byte, error)

Countersign0 performs an abbreviated signature over a parent message using the provided Signer.

The parent message must be completely fulfilled prior signing.

Reference: https://datatracker.ietf.org/doc/html/rfc9338#section-3.2

Experimental

Notice: The COSE Countersignature API is EXPERIMENTAL and may be changed or removed in a later release.

func I2OSP

func I2OSP(x *big.Int, buf []byte) error

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 OS2IP

func OS2IP(x []byte) *big.Int

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 Sign1

func Sign1(rand io.Reader, signer Signer, headers Headers, payload []byte, external []byte) ([]byte, error)

Sign1 signs a Sign1Message using the provided Signer.

This method is a wrapper of `Sign1Message.Sign()`.

Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4

Example

This example demonstrates signing COSE_Sign1_Tagged signatures using Sign1().

// 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

func Sign1Untagged added in v1.1.0

func Sign1Untagged(rand io.Reader, signer Signer, headers Headers, payload []byte, external []byte) ([]byte, error)

Sign1Untagged signs an UntaggedSign1Message using the provided Signer.

This method is a wrapper of `UntaggedSign1Message.Sign()`.

Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4

Example

This example demonstrates signing COSE_Sign1 signatures using Sign1Untagged().

// 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

func VerifyCountersign0 added in v1.3.0

func VerifyCountersign0(verifier Verifier, parent any, external, signature []byte) error

VerifyCountersign0 verifies an abbreviated signature over a parent message using the provided Verifier.

Reference: https://datatracker.ietf.org/doc/html/rfc9338#section-3.2

Experimental

Notice: The COSE Countersignature API is EXPERIMENTAL and may be changed or removed in a later release.

Types

type Algorithm

type Algorithm int64

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

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.
	//
	// Deprecated: use AlgorithmEdDSA instead, which has
	// the same value but with a more accurate name.
	AlgorithmEd25519 Algorithm = -8

	// PureEdDSA by RFC 8152.
	AlgorithmEdDSA Algorithm = -8

	// Reserved value.
	AlgorithmReserved Algorithm = 0
)

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-PKCS1-v1_5 using SHA-256 by RFC 8812.
	AlgorithmRS256 Algorithm = -257

	// RSASSA-PKCS1-v1_5 using SHA-384 by RFC 8812.
	AlgorithmRS384 Algorithm = -258

	// RSASSA-PKCS1-v1_5 using SHA-512 by RFC 8812.
	AlgorithmRS512 Algorithm = -259
)

Algorithms known, but not supported by this library.

Signers and Verifiers requiring the algorithms below are not directly supported by this library. They need to be provided as an external cose.Signer or cose.Verifier implementation.

An example use case where RS256 is allowed and used is in WebAuthn: https://www.w3.org/TR/webauthn-2/#sctn-sample-registration.

func (Algorithm) String

func (a Algorithm) String() string

String returns the name of the algorithm

type CWTClaims added in v1.3.0

type CWTClaims map[any]any

CWTClaims contains parameters that are to be cryptographically protected.

type Countersignature added in v1.3.0

type Countersignature Signature

Countersignature represents a decoded COSE_Countersignature.

Reference: https://tools.ietf.org/html/rfc9338#section-3.1

Experimental

Notice: The COSE Countersignature API is EXPERIMENTAL and may be changed or removed in a later release.

Example

This example demonstrates signing and verifying countersignatures.

The COSE Countersignature API is EXPERIMENTAL and may be changed or removed in a later release.

// 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, _ := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
signer, _ := cose.NewSigner(cose.AlgorithmES512, privateKey)

// sign message
msgToSign.Sign(rand.Reader, nil, signer)

// create a countersignature holder for the message
msgCountersig := cose.NewCountersignature()
msgCountersig.Headers.Protected.SetAlgorithm(cose.AlgorithmES512)
msgCountersig.Headers.Unprotected[cose.HeaderLabelKeyID] = []byte("11")

// create a countersigner
counterPrivateKey, _ := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
countersigner, _ := cose.NewSigner(cose.AlgorithmES512, counterPrivateKey)

// countersign message
err := msgCountersig.Sign(rand.Reader, countersigner, msgToSign, nil)
if err != nil {
	panic(err)
}

// add countersignature as message unprotected header; notice the
// countersignature should be assigned as reference
msgToSign.Headers.Unprotected[cose.HeaderLabelCounterSignatureV2] = msgCountersig

// create a countersignature holder for the signature
sigCountersig := cose.NewCountersignature()
sigCountersig.Headers.Protected.SetAlgorithm(cose.AlgorithmES512)
sigCountersig.Headers.Unprotected[cose.HeaderLabelKeyID] = []byte("11")

// countersign signature
err = sigCountersig.Sign(rand.Reader, countersigner, sigHolder, nil)
if err != nil {
	panic(err)
}

// add countersignature as signature unprotected header; notice the
// countersignature should be assigned as reference
sigHolder.Headers.Unprotected[cose.HeaderLabelCounterSignatureV2] = sigCountersig

sig, err := msgToSign.MarshalCBOR()
if err != nil {
	panic(err)
}
fmt.Println("message signed and countersigned")

// create a verifier from a trusted public key
publicKey := counterPrivateKey.Public()
verifier, err := cose.NewVerifier(cose.AlgorithmES512, publicKey)
if err != nil {
	panic(err)
}

// decode COSE_Sign message containing countersignatures
var msgToVerify cose.SignMessage
err = msgToVerify.UnmarshalCBOR(sig)
if err != nil {
	panic(err)
}

// unwrap the message countersignature; the example assumes the header is a
// single countersignature, but real code would consider checking if it
// consists in a slice of countersignatures too.
msgCountersigHdr := msgToVerify.Headers.Unprotected[cose.HeaderLabelCounterSignatureV2]
msgCountersigToVerify := msgCountersigHdr.(*cose.Countersignature)

// verify message countersignature
err = msgCountersigToVerify.Verify(verifier, msgToVerify, nil)
if err != nil {
	panic(err)
}
fmt.Println("message countersignature verified")

// unwrap the signature countersignature; the example assumes the header is a
// single countersignature, but real code would consider checking if it
// consists in a slice of countersignatures too.
sig0 := msgToVerify.Signatures[0]
sigCountersigHdr := sig0.Headers.Unprotected[cose.HeaderLabelCounterSignatureV2]
sigCountersigToVerify := sigCountersigHdr.(*cose.Countersignature)

// verify signature countersignature
err = sigCountersigToVerify.Verify(verifier, sig0, nil)
if err != nil {
	panic(err)
}
fmt.Println("signature countersignature verified")

// tamper the message and verification should fail
msgToVerify.Payload = []byte("foobar")
err = msgCountersigToVerify.Verify(verifier, msgToVerify, nil)
if err != cose.ErrVerification {
	panic(err)
}
fmt.Println("verification error as expected")
Output:

message signed and countersigned
message countersignature verified
signature countersignature verified
verification error as expected

func NewCountersignature added in v1.3.0

func NewCountersignature() *Countersignature

NewCountersignature returns a Countersignature with header initialized.

Experimental

Notice: The COSE Countersignature API is EXPERIMENTAL and may be changed or removed in a later release.

func (*Countersignature) MarshalCBOR added in v1.3.0

func (s *Countersignature) MarshalCBOR() ([]byte, error)

MarshalCBOR encodes Countersignature into a COSE_Countersignature object.

Experimental

Notice: The COSE Countersignature API is EXPERIMENTAL and may be changed or removed in a later release.

func (*Countersignature) Sign added in v1.3.0

func (s *Countersignature) Sign(rand io.Reader, signer Signer, parent any, external []byte) error

Sign signs a Countersignature using the provided Signer. Signing a COSE_Countersignature requires the parent message to be completely fulfilled.

Reference: https://datatracker.ietf.org/doc/html/rfc9338#section-3.3

Experimental

Notice: The COSE Countersignature API is EXPERIMENTAL and may be changed or removed in a later release.

func (*Countersignature) UnmarshalCBOR added in v1.3.0

func (s *Countersignature) UnmarshalCBOR(data []byte) error

UnmarshalCBOR decodes a COSE_Countersignature object into Countersignature.

Experimental

Notice: The COSE Countersignature API is EXPERIMENTAL and may be changed or removed in a later release.

func (*Countersignature) Verify added in v1.3.0

func (s *Countersignature) Verify(verifier Verifier, parent any, external []byte) error

Verify verifies the countersignature, returning nil on success or a suitable error if verification fails. Verifying a COSE_Countersignature requires the parent message.

Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4

Experimental

Notice: The COSE Sign API is EXPERIMENTAL and may be changed or removed in a later release.

type Curve added in v1.2.0

type Curve int64

Curve represents the EC2/OKP key's curve.

https://datatracker.ietf.org/doc/html/rfc8152#section-13.1

const (
	// Reserved value
	CurveReserved 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
)

func (Curve) String added in v1.2.0

func (c Curve) String() string

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.

type DigestSigner added in v1.3.0

type DigestSigner interface {
	// Algorithm returns the signing algorithm associated with the private key.
	Algorithm() Algorithm

	// SignDigest signs message digest with the private key, possibly using
	// entropy from rand.
	// The resulting signature should follow RFC 8152 section 8.
	SignDigest(rand io.Reader, digest []byte) ([]byte, error)
}

DigestSigner is an interface for private keys to sign digested COSE signatures.

Example
// create a signer
privateKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
	panic(err)
}
signer, err := cose.NewSigner(cose.AlgorithmES256, privateKey)
if err != nil {
	panic(err)
}
digestSigner, ok := signer.(cose.DigestSigner)
if !ok {
	panic("signer does not support digest signing")
}

// hash payload outside go-cose.
payload := []byte("hello world")
digested := sha512.Sum512(payload)
sig, err := digestSigner.SignDigest(rand.Reader, digested[:])

fmt.Println("digest signed")
_ = sig // further process on sig
Output:

digest signed

type DigestVerifier added in v1.3.0

type DigestVerifier interface {
	// Algorithm returns the signing algorithm associated with the public key.
	Algorithm() Algorithm

	// VerifyDigest verifies message digest with the public key, returning nil
	// for success.
	// Otherwise, it returns ErrVerification.
	VerifyDigest(digest, signature []byte) error
}

DigestVerifier is an interface for public keys to verify digested COSE signatures.

type Headers

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
}

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

func (*Headers) MarshalProtected

func (h *Headers) MarshalProtected() ([]byte, error)

MarshalProtected encodes the protected header. RawProtected is returned if it is not set to nil.

func (*Headers) MarshalUnprotected

func (h *Headers) MarshalUnprotected() ([]byte, error)

MarshalUnprotected encodes the unprotected header. RawUnprotected is returned if it is not set to nil.

func (*Headers) UnmarshalFromRaw

func (h *Headers) UnmarshalFromRaw() error

UnmarshalFromRaw decodes Protected from RawProtected and Unprotected from RawUnprotected.

type Key added in v1.2.0

type Key struct {
	// Type identifies the family of keys for this structure, and thus,
	// which of the key-type-specific parameters need to be set.
	Type KeyType
	// ID is the identification value matched to the kid in the message.
	ID []byte
	// 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
	// Ops can be set to restrict the set of operations that the Key is used for.
	Ops []KeyOp
	// BaseIV is the Base IV to be xor-ed with Partial IVs.
	BaseIV []byte

	// Any additional parameter (label,value) pairs.
	Params map[any]any
}

Key represents a COSE_Key structure, as defined by RFC8152. Note: currently, this does NOT support RFC8230 (RSA algorithms).

func NewKeyEC2 added in v1.3.0

func NewKeyEC2(alg Algorithm, x, y, d []byte) (*Key, error)

NewKeyEC2 returns a Key created using the provided elliptic curve key data.

func NewKeyFromPrivate added in v1.2.0

func NewKeyFromPrivate(priv crypto.PrivateKey) (*Key, error)

NewKeyFromPrivate returns a Key created using provided crypto.PrivateKey. Supported key formats are: *ecdsa.PrivateKey and ed25519.PrivateKey

func NewKeyFromPublic added in v1.2.0

func NewKeyFromPublic(pub crypto.PublicKey) (*Key, error)

NewKeyFromPublic returns a Key created using the provided crypto.PublicKey. Supported key formats are: *ecdsa.PublicKey and ed25519.PublicKey

func NewKeyOKP added in v1.3.0

func NewKeyOKP(alg Algorithm, x, d []byte) (*Key, error)

NewKeyOKP returns a Key created using the provided Octet Key Pair data.

func NewKeySymmetric added in v1.3.0

func NewKeySymmetric(k []byte) *Key

NewKeySymmetric returns a Key created using the provided Symmetric key bytes.

func (*Key) AlgorithmOrDefault added in v1.2.0

func (k *Key) AlgorithmOrDefault() (Algorithm, error)

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 (*Key) EC2 added in v1.3.0

func (k *Key) EC2() (crv Curve, x []byte, y, d []byte)

EC2 returns the Elliptic Curve parameters for the key.

func (*Key) MarshalCBOR added in v1.2.0

func (k *Key) MarshalCBOR() ([]byte, error)

MarshalCBOR encodes Key into a COSE_Key object.

func (*Key) OKP added in v1.3.0

func (k *Key) OKP() (crv Curve, x []byte, d []byte)

OKP returns the Octet Key Pair parameters for the key.

func (*Key) ParamBool added in v1.3.0

func (k *Key) ParamBool(label any) (bool, bool)

ParamBool returns the value of the parameter with the given label, if it exists and is of type bool or can be converted to bool.

func (*Key) ParamBytes added in v1.3.0

func (k *Key) ParamBytes(label any) ([]byte, bool)

ParamBytes returns the value of the parameter with the given label, if it exists and is of type []byte or can be converted to []byte.

func (*Key) ParamInt added in v1.3.0

func (k *Key) ParamInt(label any) (int64, bool)

ParamInt returns the value of the parameter with the given label, if it exists and is of type int64 or can be converted to int64.

func (*Key) ParamString added in v1.3.0

func (k *Key) ParamString(label any) (string, bool)

ParamString returns the value of the parameter with the given label, if it exists and is of type string or can be converted to string.

func (*Key) ParamUint added in v1.3.0

func (k *Key) ParamUint(label any) (uint64, bool)

ParamUint returns the value of the parameter with the given label, if it exists and is of type uint64 or can be converted to uint64.

func (*Key) PrivateKey added in v1.2.0

func (k *Key) PrivateKey() (crypto.PrivateKey, error)

PrivateKey returns a crypto.PrivateKey generated using Key's parameters. Compressed point is not supported for EC2 keys.

func (*Key) PublicKey added in v1.2.0

func (k *Key) PublicKey() (crypto.PublicKey, error)

PublicKey returns a crypto.PublicKey generated using Key's parameters.

func (*Key) Signer added in v1.2.0

func (k *Key) Signer() (Signer, error)

Signer returns a Signer created using Key.

func (*Key) Symmetric added in v1.3.0

func (key *Key) Symmetric() (k []byte)

Symmetric returns the Symmetric parameters for the key.

func (*Key) UnmarshalCBOR added in v1.2.0

func (k *Key) UnmarshalCBOR(data []byte) error

UnmarshalCBOR decodes a COSE_Key object into Key.

func (*Key) Verifier added in v1.2.0

func (k *Key) Verifier() (Verifier, error)

Verifier returns a Verifier created using Key.

type KeyOp added in v1.2.0

type KeyOp int64

KeyOp represents a key_ops value used to restrict purposes for which a Key may be used.

https://datatracker.ietf.org/doc/html/rfc8152#section-7.1

const (
	// Reserved value.
	KeyOpReserved 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
)

func KeyOpFromString added in v1.2.0

func KeyOpFromString(val string) (KeyOp, bool)

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 (KeyOp) String added in v1.2.0

func (ko KeyOp) String() string

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).

type KeyType added in v1.2.0

type KeyType int64

KeyType identifies the family of keys represented by the associated Key.

https://datatracker.ietf.org/doc/html/rfc8152#section-13

const (
	KeyTypeReserved  KeyType = 0
	KeyTypeOKP       KeyType = 1
	KeyTypeEC2       KeyType = 2
	KeyTypeSymmetric KeyType = 4
)

func (KeyType) String added in v1.2.0

func (kt KeyType) String() string

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.

type ProtectedHeader

type ProtectedHeader map[any]any

ProtectedHeader contains parameters that are to be cryptographically protected.

func (ProtectedHeader) Algorithm

func (h ProtectedHeader) Algorithm() (Algorithm, error)

Algorithm gets the algorithm value from the algorithm header.

func (ProtectedHeader) Critical

func (h ProtectedHeader) Critical() ([]any, error)

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 (ProtectedHeader) MarshalCBOR

func (h ProtectedHeader) MarshalCBOR() ([]byte, error)

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 (ProtectedHeader) SetAlgorithm

func (h ProtectedHeader) SetAlgorithm(alg Algorithm)

SetAlgorithm sets the algorithm value of the protected header.

func (ProtectedHeader) SetCWTClaims added in v1.3.0

func (h ProtectedHeader) SetCWTClaims(claims CWTClaims) (CWTClaims, error)

SetCWTClaims sets the CWT Claims value of the protected header.

func (ProtectedHeader) SetType added in v1.3.0

func (h ProtectedHeader) SetType(typ any) (any, error)

SetType sets the type of the cose object in the protected header.

func (*ProtectedHeader) UnmarshalCBOR

func (h *ProtectedHeader) UnmarshalCBOR(data []byte) error

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

type Sign1Message

type Sign1Message struct {
	Headers   Headers
	Payload   []byte
	Signature []byte
}

Sign1Message represents a decoded COSE_Sign1 message.

Reference: https://tools.ietf.org/html/rfc8152#section-4.2

Example

This example demonstrates signing and verifying COSE_Sign1 signatures.

// 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

func NewSign1Message

func NewSign1Message() *Sign1Message

NewSign1Message returns a Sign1Message with header initialized.

func (*Sign1Message) MarshalCBOR

func (m *Sign1Message) MarshalCBOR() ([]byte, error)

MarshalCBOR encodes Sign1Message into a COSE_Sign1_Tagged object.

func (*Sign1Message) Sign

func (m *Sign1Message) Sign(rand io.Reader, external []byte, signer Signer) error

Sign signs a Sign1Message using the provided Signer. The signature is stored in m.Signature.

Note that m.Signature is only valid as long as m.Headers.Protected and m.Payload remain unchanged after calling this method. It is possible to modify m.Headers.Unprotected after signing, i.e., add counter signatures or timestamps.

Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4

func (*Sign1Message) UnmarshalCBOR

func (m *Sign1Message) UnmarshalCBOR(data []byte) error

UnmarshalCBOR decodes a COSE_Sign1_Tagged object into Sign1Message.

func (*Sign1Message) Verify

func (m *Sign1Message) Verify(external []byte, verifier Verifier) error

Verify verifies the signature on the Sign1Message returning nil on success or a suitable error if verification fails.

Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4

type SignMessage

type SignMessage struct {
	Headers    Headers
	Payload    []byte
	Signatures []*Signature
}

SignMessage represents a decoded COSE_Sign message.

Reference: https://tools.ietf.org/html/rfc8152#section-4.1

Experimental

Notice: The COSE Sign API is EXPERIMENTAL and may be changed or removed in a later release.

Example

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.

// 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

func NewSignMessage

func NewSignMessage() *SignMessage

NewSignMessage returns a SignMessage with header initialized.

Experimental

Notice: The COSE Sign API is EXPERIMENTAL and may be changed or removed in a later release.

func (*SignMessage) MarshalCBOR

func (m *SignMessage) MarshalCBOR() ([]byte, error)

MarshalCBOR encodes SignMessage into a COSE_Sign_Tagged object.

Experimental

Notice: The COSE Sign API is EXPERIMENTAL and may be changed or removed in a later release.

func (*SignMessage) Sign

func (m *SignMessage) Sign(rand io.Reader, external []byte, signers ...Signer) error

Sign signs a SignMessage using the provided signers corresponding to the signatures.

See `Signature.Sign()` for advanced signing scenarios.

Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4

Experimental

Notice: The COSE Sign API is EXPERIMENTAL and may be changed or removed in a later release.

func (*SignMessage) UnmarshalCBOR

func (m *SignMessage) UnmarshalCBOR(data []byte) error

UnmarshalCBOR decodes a COSE_Sign_Tagged object into SignMessage.

Experimental

Notice: The COSE Sign API is EXPERIMENTAL and may be changed or removed in a later release.

func (*SignMessage) Verify

func (m *SignMessage) Verify(external []byte, verifiers ...Verifier) error

Verify verifies the signatures on the SignMessage against the corresponding verifier, returning nil on success or a suitable error if verification fails.

See `Signature.Verify()` for advanced verification scenarios like threshold policies.

Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4

Experimental

Notice: The COSE Sign API is EXPERIMENTAL and may be changed or removed in a later release.

type Signature

type Signature struct {
	Headers   Headers
	Signature []byte
}

Signature represents a decoded COSE_Signature.

Reference: https://tools.ietf.org/html/rfc8152#section-4.1

Experimental

Notice: The COSE Sign API is EXPERIMENTAL and may be changed or removed in a later release.

func NewSignature

func NewSignature() *Signature

NewSignature returns a Signature with header initialized.

Experimental

Notice: The COSE Sign API is EXPERIMENTAL and may be changed or removed in a later release.

func (*Signature) MarshalCBOR

func (s *Signature) MarshalCBOR() ([]byte, error)

MarshalCBOR encodes Signature into a COSE_Signature object.

Experimental

Notice: The COSE Sign API is EXPERIMENTAL and may be changed or removed in a later release.

func (*Signature) Sign

func (s *Signature) Sign(rand io.Reader, signer Signer, protected cbor.RawMessage, payload, external []byte) error

Sign signs a Signature using the provided Signer. Signing a COSE_Signature requires the encoded protected header and the payload of its parent message.

Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4

Experimental

Notice: The COSE Sign API is EXPERIMENTAL and may be changed or removed in a later release.

func (*Signature) UnmarshalCBOR

func (s *Signature) UnmarshalCBOR(data []byte) error

UnmarshalCBOR decodes a COSE_Signature object into Signature.

Experimental

Notice: The COSE Sign API is EXPERIMENTAL and may be changed or removed in a later release.

func (*Signature) Verify

func (s *Signature) Verify(verifier Verifier, protected cbor.RawMessage, payload, external []byte) error

Verify verifies the signature, returning nil on success or a suitable error if verification fails. Verifying a COSE_Signature requires the encoded protected header and the payload of its parent message.

Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4

Experimental

Notice: The COSE Sign API is EXPERIMENTAL and may be changed or removed in a later release.

type Signer

type Signer interface {
	// Algorithm returns the signing algorithm associated with the private key.
	Algorithm() Algorithm

	// Sign signs message content with the private key, possibly using entropy
	// from rand.
	// The resulting signature should follow RFC 8152 section 8.
	//
	// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-8
	Sign(rand io.Reader, content []byte) ([]byte, error)
}

Signer is an interface for private keys to sign COSE signatures.

func NewSigner

func NewSigner(alg Algorithm, key crypto.Signer) (Signer, error)

NewSigner returns a signer with a given signing key. The signing key can be a golang built-in crypto private key, a key in HSM, or a remote KMS.

Developers are encouraged to implement the `cose.Signer` interface instead of the `crypto.Signer` interface for better performance.

All signing keys implementing `crypto.Signer` with `Public()` returning a public key of type `*rsa.PublicKey`, `*ecdsa.PublicKey`, or `ed25519.PublicKey` are accepted.

The returned signer for rsa and ecdsa keys also implements `cose.DigestSigner`.

Note: `*rsa.PrivateKey`, `*ecdsa.PrivateKey`, and `ed25519.PrivateKey` implement `crypto.Signer`.

type UnprotectedHeader

type UnprotectedHeader map[any]any

UnprotectedHeader contains parameters that are not cryptographically protected.

func (UnprotectedHeader) MarshalCBOR

func (h UnprotectedHeader) MarshalCBOR() ([]byte, error)

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 (*UnprotectedHeader) UnmarshalCBOR

func (h *UnprotectedHeader) UnmarshalCBOR(data []byte) error

UnmarshalCBOR decodes a CBOR map object into UnprotectedHeader.

UnprotectedHeader is a header_map.

type UntaggedSign1Message added in v1.1.0

type UntaggedSign1Message Sign1Message

func (*UntaggedSign1Message) MarshalCBOR added in v1.1.0

func (m *UntaggedSign1Message) MarshalCBOR() ([]byte, error)

MarshalCBOR encodes UntaggedSign1Message into a COSE_Sign1 object.

func (*UntaggedSign1Message) Sign added in v1.1.0

func (m *UntaggedSign1Message) Sign(rand io.Reader, external []byte, signer Signer) error

Sign signs an UnttaggedSign1Message using the provided Signer. The signature is stored in m.Signature.

Note that m.Signature is only valid as long as m.Headers.Protected and m.Payload remain unchanged after calling this method. It is possible to modify m.Headers.Unprotected after signing, i.e., add counter signatures or timestamps.

Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4

func (*UntaggedSign1Message) UnmarshalCBOR added in v1.1.0

func (m *UntaggedSign1Message) UnmarshalCBOR(data []byte) error

UnmarshalCBOR decodes a COSE_Sign1 object into an UntaggedSign1Message.

func (*UntaggedSign1Message) Verify added in v1.1.0

func (m *UntaggedSign1Message) Verify(external []byte, verifier Verifier) error

Verify verifies the signature on the UntaggedSign1Message returning nil on success or a suitable error if verification fails.

Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4

type Verifier

type Verifier interface {
	// Algorithm returns the signing algorithm associated with the public key.
	Algorithm() Algorithm

	// 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
	Verify(content, signature []byte) error
}

Verifier is an interface for public keys to verify COSE signatures.

func NewVerifier

func NewVerifier(alg Algorithm, key crypto.PublicKey) (Verifier, error)

NewVerifier returns a verifier with a given public key. Only golang built-in crypto public keys of type `*rsa.PublicKey`, `*ecdsa.PublicKey`, and `ed25519.PublicKey` are accepted. When `*ecdsa.PublicKey` is specified, its curve must be supported by crypto/ecdh.

The returned signer for rsa and ecdsa keys also implements `cose.DigestSigner`.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL