cose

package module
v0.0.0-...-5c1fcf5 Latest Latest
Warning

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

Go to latest
Published: Apr 20, 2018 License: MPL-2.0 Imports: 12 Imported by: 0

README

go-cose

Build Status Coverage Status

A COSE library for go.

It currently supports signing and verifying the SignMessage type with the ES{256,384,512} and PS256 algorithms.

API docs

Usage

Install
go get -u go.mozilla.org/cose
Signing a message

From example/sign.go:

...
	// create a signer
	signer, err := cose.NewSigner(&ecdsaPrivateKey)
	if err != nil {
		panic(fmt.Sprintf(fmt.Sprintf("Error creating signer %s", err)))
	}

	// create a signature
	sig := cose.NewSignature()
	sig.Headers.Unprotected["kid"] = 1
	sig.Headers.Protected["alg"] = "ES256"

	// create a message
	payload := []byte("payload to sign")
	external := []byte("") // optional external data see https://tools.ietf.org/html/rfc8152#section-4.3

	msg := cose.NewSignMessage(payload) // can update via .Payload later too
	msg.AddSignature(sig)

	randReader := rand.New(rand.NewSource(time.Now().UnixNano()))
	err = msg.Sign(randReader, external, cose.SignOpts{
		HashFunc: crypto.SHA256,
		GetSigner: func(index int, signature cose.Signature) (cose.Signer, error) {
			if signature.Headers.Unprotected["kid"] == 1 || signature.Headers.Unprotected[cose.GetCommonHeaderTagOrPanic("kid")] == 1 {
				return *signer, nil
			} else {
				return *signer, cose.ErrNoSignerFound
			}
		},
	})
	if err == nil {
		fmt.Println(fmt.Sprintf("Message signature (ES256): %x", msg.Signatures[0].SignatureBytes))
	} else {
		fmt.Println(fmt.Sprintf("Error signing the message %+v", err))
	}
...

To run the full example (your signature will vary):

$ go run example/sign.go
Message signature (ES256): 043685f99421f9e80c7c3c50d0fc8266161d3d614aaa3b63d2cdf581713fca62bb5d2e34d2352dbe41424b31d0b4a11d6b2d4764c18e2af04f4520fbe494d51c
Verifying a message

Continuing from the signer example in example/verify.go:

...
	// derive a verifier from out signer's public key
	verifier := signer.Verifier(cose.GetAlgByNameOrPanic("ES256"))

	// Verify
	err = msg.Verify(external, &cose.VerifyOpts{
		GetVerifier: func(index int, signature cose.Signature) (cose.Verifier, error) {
			// or return cose.ErrNoVerifierFound
			return *verifier, nil
		},
	})
	if err == nil {
		fmt.Println("Message signature verified")
	} else {
		fmt.Println(fmt.Sprintf("Error verifying the message %+v", err))
	}
...

To run the full example (your signature will vary):

$ go run example/verify.go
Message signature (ES256): 9411dc5200c1cb67ccd76424ade09ce89c4a8d8d2b66f2bbf70edf63beb2dc3cbde83250773e659b635d3715442a1efaa6b0c030ee8a2523c3e37a22ddb055fa
Message signature verified

Development

Running tests:

make godep golint  # skip if you already have them
make install
go test # note that the rust tests will fail

The cose-rust tests run in CI. To run them locally:

  1. Install rust and cargo

  2. On OSX, you might need to:

  3. brew install nss nss

  4. Add NSS_LIB_DIR to the cmd in sign_verify_cose_rust_cli_test.go e.g. cmd.Env = append(os.Environ(), "NSS_LIB_DIR=/usr/local/opt/nss/lib", "RUSTFLAGS=-A dead_code -A unused_imports")

  5. It can also be helpful to add the following to print output from the cmd too:

    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    

Documentation

Index

Constants

View Source
const (

	// Encrypt0MessageCBORTag is the CBOR tag for an Encrypt0Message
	Encrypt0MessageCBORTag = 16

	// MAC0MessageCBORTag is the CBOR tag for a MAC0Message
	MAC0MessageCBORTag = 17

	// Sign1MessageCBORTag is the CBOR tag for a Sign1Message
	Sign1MessageCBORTag = 18

	// EncryptMessageCBORTag is the CBOR tag for an EncryptMessage
	EncryptMessageCBORTag = 96

	// MACMessageCBORTag is the CBOR tag for a MACMessage
	MACMessageCBORTag = 97

	// SignMessageCBORTag is the CBOR tag for a SignMessage
	SignMessageCBORTag = 98
)
View Source
const (

	// ContextSignature for signatures using the COSE_Signature structure
	ContextSignature = "Signature"

	// ContextSignature1 for signatures using the COSE_Sign1 structure
	ContextSignature1 = "Signature1"

	// ContextCounterSignature for signatures used as counter signature attributes
	ContextCounterSignature = "CounterSignature"
)

Variables

View Source
var (
	ErrInvalidAlg             = errors.New("Invalid algorithm")
	ErrAlgNotFound            = errors.New("Error fetching alg")
	ErrECDSAVerification      = errors.New("verification failed ecdsa.Verify")
	ErrRSAPSSVerification     = errors.New("verification failed rsa.VerifyPSS err crypto/rsa: verification error")
	ErrMissingCOSETagForLabel = errors.New("No common COSE tag for label")
	ErrMissingCOSETagForTag   = errors.New("No common COSE label for tag")
	ErrNilSigHeader           = errors.New("Signature.headers is nil")
	ErrNilSigProtectedHeaders = errors.New("Signature.headers.protected is nil")
	ErrNilSignatures          = errors.New("SignMessage.signatures is nil. Use AddSignature to add one")
	ErrNoSignatures           = errors.New("No signatures to sign the message. Use AddSignature to add them")
	ErrNoSignerFound          = errors.New("No signer found")
	ErrNoVerifierFound        = errors.New("No verifier found")
	ErrUnavailableHashFunc    = errors.New("hash function is not available")
	ErrUnknownPrivateKeyType  = errors.New("Unrecognized private key type")
	ErrUnknownPublicKeyType   = errors.New("Unrecognized public key type")
)
View Source
var Algorithms = []Algorithm{
	Algorithm{
		Name:  "RSAES-OAEP w/ SHA-512",
		Value: -42,
	},
	Algorithm{
		Name:  "RSAES-OAEP w/ SHA-256",
		Value: -41,
	},
	Algorithm{
		Name:  "RSAES-OAEP w/ RFC 8017 default parameters",
		Value: -40,
	},
	Algorithm{
		Name:  "PS512",
		Value: -39,
	},
	Algorithm{
		Name:  "PS384",
		Value: -38,
	},
	Algorithm{
		Name:     "PS256",
		Value:    -37,
		HashFunc: crypto.SHA256,
	},
	Algorithm{
		Name:     "ES512",
		Value:    -36,
		HashFunc: crypto.SHA512,
		// contains filtered or unexported fields
	},
	Algorithm{
		Name:     "ES384",
		Value:    -35,
		HashFunc: crypto.SHA384,
		// contains filtered or unexported fields
	},
	Algorithm{
		Name:  "ECDH-SS + A256KW",
		Value: -34,
	},
	Algorithm{
		Name:  "ECDH-SS + A192KW",
		Value: -33,
	},
	Algorithm{
		Name:  "ECDH-SS + A128KW",
		Value: -32,
	},
	Algorithm{
		Name:  "ECDH-ES + A256KW",
		Value: -31,
	},
	Algorithm{
		Name:  "ECDH-ES + A192KW",
		Value: -30,
	},
	Algorithm{
		Name:  "ECDH-ES + A128KW",
		Value: -29,
	},
	Algorithm{
		Name:  "ECDH-SS + HKDF-512",
		Value: -28,
	},
	Algorithm{
		Name:  "ECDH-SS + HKDF-256",
		Value: -27,
	},
	Algorithm{
		Name:  "ECDH-ES + HKDF-512",
		Value: -26,
	},
	Algorithm{
		Name:  "ECDH-ES + HKDF-256",
		Value: -25,
	},
	Algorithm{
		Name:  "direct+HKDF-AES-256",
		Value: -13,
	},
	Algorithm{
		Name:  "direct+HKDF-AES-128",
		Value: -12,
	},
	Algorithm{
		Name:  "direct+HKDF-SHA-512",
		Value: -11,
	},
	Algorithm{
		Name:  "direct+HKDF-SHA-256",
		Value: -10,
	},
	Algorithm{
		Name:  "EdDSA",
		Value: -8,
	},
	Algorithm{
		Name:     "ES256",
		Value:    -7,
		HashFunc: crypto.SHA256,
		// contains filtered or unexported fields
	},
	Algorithm{
		Name:  "direct",
		Value: -6,
	},
	Algorithm{
		Name:  "A256KW",
		Value: -5,
	},
	Algorithm{
		Name:  "A192KW",
		Value: -4,
	},
	Algorithm{
		Name:  "A128KW",
		Value: -3,
	},
	Algorithm{
		Name:  "A128GCM",
		Value: 1,
	},
	Algorithm{
		Name:  "A192GCM",
		Value: 2,
	},
	Algorithm{
		Name:  "A256GCM",
		Value: 3,
	},
	Algorithm{
		Name:  "HMAC 256/64",
		Value: 4,
	},
	Algorithm{
		Name:  "HMAC 256/256",
		Value: 5,
	},
	Algorithm{
		Name:  "HMAC 384/384",
		Value: 6,
	},
	Algorithm{
		Name:  "HMAC 512/512",
		Value: 7,
	},
	Algorithm{
		Name:  "AES-CCM-16-64-128",
		Value: 10,
	},
	Algorithm{
		Name:  "AES-CCM-16-64-256",
		Value: 11,
	},
	Algorithm{
		Name:  "AES-CCM-64-64-128",
		Value: 12,
	},
	Algorithm{
		Name:  "AES-CCM-64-64-256",
		Value: 13,
	},
	Algorithm{
		Name:  "AES-MAC 128/64",
		Value: 14,
	},
	Algorithm{
		Name:  "AES-MAC 256/64",
		Value: 15,
	},
	Algorithm{
		Name:  "ChaCha20/Poly1305",
		Value: 24,
	},
	Algorithm{
		Name:  "AES-MAC 128/128",
		Value: 25,
	},
	Algorithm{
		Name:  "AES-MAC 256/128",
		Value: 26,
	},
	Algorithm{
		Name:  "AES-CCM-16-128-128",
		Value: 30,
	},
	Algorithm{
		Name:  "AES-CCM-16-128-256",
		Value: 31,
	},
	Algorithm{
		Name:  "AES-CCM-64-128-128",
		Value: 32,
	},
	Algorithm{
		Name:  "AES-CCM-64-128-256",
		Value: 33,
	},
}

Algorithms is an array/slice of IANA algorithms

Functions

func CompressHeaders

func CompressHeaders(headers map[interface{}]interface{}) (compressed map[interface{}]interface{})

CompressHeaders replaces string tags with their int values and alg tags with their IANA int values. Is the inverse of DecompressHeaders.

func DecompressHeaders

func DecompressHeaders(headers map[interface{}]interface{}) (decompressed map[interface{}]interface{})

DecompressHeaders replaces int values with string tags and alg int values with their IANA labels. Is the inverse of CompressHeaders.

func FromBase64Int

func FromBase64Int(data string) *big.Int

FromBase64Int decodes a base64-encoded string into a big.Int or panics

from https://github.com/square/go-jose/blob/789a4c4bd4c118f7564954f441b29c153ccd6a96/utils_test.go#L45 Apache License 2.0

func GetCOSEHandle

func GetCOSEHandle() (h *codec.CborHandle)

GetCOSEHandle returns a codec.CborHandle with an extension registered for COSE SignMessage as CBOR tag 98

func GetCommonHeaderLabel

func GetCommonHeaderLabel(tag int) (label string, err error)

GetCommonHeaderLabel returns the CBOR label for the map tag. Is the inverse of GetCommonHeaderTag.

func GetCommonHeaderTag

func GetCommonHeaderTag(label string) (tag int, err error)

GetCommonHeaderTag returns the CBOR tag for the map label

using Common COSE Headers Parameters Table 2 https://tools.ietf.org/html/rfc8152#section-3.1

func GetCommonHeaderTagOrPanic

func GetCommonHeaderTagOrPanic(label string) (tag int)

GetCommonHeaderTagOrPanic returns the CBOR label for a string. Is the inverse of GetCommonHeaderLabel.

func I2OSP

func I2OSP(b *big.Int, n int) []byte

I2OSP converts a nonnegative integer to an octet string of a specified length https://tools.ietf.org/html/rfc8017#section-4.1

implementation from https://github.com/r2ishiguro/vrf/blob/69d5bfb37b72b7b932ffe34213778bdb319f0438/go/vrf_ed25519/vrf_ed25519.go#L206 (Apache License 2.0)

func Marshal

func Marshal(o interface{}) (b []byte, err error)

Marshal returns the CBOR []byte encoding of param o

func Unmarshal

func Unmarshal(b []byte) (o interface{}, err error)

Unmarshal returns the CBOR decoding of a []byte into param o TODO: decode into object inplace to implement the more encoding interface func Unmarshal(data []byte, v interface{}) error TODO: decode with readers for better interop in autograph

Types

type Algorithm

type Algorithm struct {
	Name     string
	Value    int
	HashFunc crypto.Hash // optional hash function for SignMessages
	// contains filtered or unexported fields
}

Algorithm represents an IANA algorithm's parameters (Name, Value/ID, and optional extra data)

From the spec:

NOTE: The assignment of algorithm identifiers in this document was done so that positive numbers were used for the first layer objects (COSE_Sign, COSE_Sign1, COSE_Encrypt, COSE_Encrypt0, COSE_Mac, and COSE_Mac0). Negative numbers were used for second layer objects (COSE_Signature and COSE_recipient).

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

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

func GetAlgByName

func GetAlgByName(name string) (alg *Algorithm, err error)

GetAlgByName returns a Algorithm for an IANA name

func GetAlgByNameOrPanic

func GetAlgByNameOrPanic(name string) (alg *Algorithm)

GetAlgByNameOrPanic returns a Algorithm for an IANA name and panics otherwise

func GetAlgByValue

func GetAlgByValue(value int64) (alg *Algorithm, err error)

GetAlgByValue returns a Algorithm for an IANA value

type Ext

type Ext struct{}

Ext is a codec.cbor extension to handle custom (de)serialization of types to/from another interface{} value

https://godoc.org/github.com/ugorji/go/codec#InterfaceExt

func (Ext) ConvertExt

func (x Ext) ConvertExt(v interface{}) interface{}

ConvertExt converts a value into a simpler interface for easier encoding

func (Ext) UpdateExt

func (x Ext) UpdateExt(dest interface{}, v interface{})

UpdateExt updates a value from a simpler interface for easy decoding dest is always a point

Note: dest is always a pointer kind to the registered extension type.

Unpacks a SignMessage described by CDDL fragments:

COSE_Sign = [

Headers,
payload : bstr / nil,
signatures : [+ COSE_Signature]

]

COSE_Signature = [

Headers,
signature : bstr

]

Headers = (

protected : empty_or_serialized_map,
unprotected : header_map

)

header_map = {
    Generic_Headers,
    * label => values
}

empty_or_serialized_map = bstr .cbor header_map / bstr .size 0

Generic_Headers = (

? 1 => int / tstr,  ; algorithm identifier
? 2 => [+label],    ; criticality
? 3 => tstr / int,  ; content type
? 4 => bstr,        ; key identifier
? 5 => bstr,        ; IV
? 6 => bstr,        ; Partial IV
? 7 => COSE_Signature / [+COSE_Signature] ; Counter signature

)

Note: the decoder will convert panics to errors

type Headers

type Headers struct {
	Protected   map[interface{}]interface{}
	Unprotected map[interface{}]interface{}
}

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

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

It is represented by CDDL fragments:

Headers = (

protected : empty_or_serialized_map,
unprotected : header_map

)

header_map = {
    Generic_Headers,
    * label => values
}

empty_or_serialized_map = bstr .cbor header_map / bstr .size 0

Generic_Headers = (

? 1 => int / tstr,  ; algorithm identifier
? 2 => [+label],    ; criticality
? 3 => tstr / int,  ; content type
? 4 => bstr,        ; key identifier
? 5 => bstr,        ; IV
? 6 => bstr,        ; Partial IV
? 7 => COSE_Signature / [+COSE_Signature] ; Counter signature

)

func (*Headers) Decode

func (h *Headers) Decode(o []interface{}) (err error)

Decode loads a two element interface{} slice into Headers.protected and unprotected respectively

func (*Headers) DecodeProtected

func (h *Headers) DecodeProtected(o interface{}) (err error)

DecodeProtected Unmarshals and sets Headers.protected from an interface{}

func (*Headers) DecodeUnprotected

func (h *Headers) DecodeUnprotected(o interface{}) (err error)

DecodeUnprotected Unmarshals and sets Headers.unprotected from an interface{}

func (*Headers) EncodeProtected

func (h *Headers) EncodeProtected() (bstr []byte)

EncodeProtected compresses and Marshals protected headers to bytes to encode as a CBOR bstr TODO: check for dups in maps

func (*Headers) EncodeUnprotected

func (h *Headers) EncodeUnprotected() (encoded map[interface{}]interface{})

EncodeUnprotected returns compressed unprotected headers

func (*Headers) MarshalBinary

func (h *Headers) MarshalBinary() (data []byte, err error)

MarshalBinary is called by codec to serialize Headers to CBOR bytes

func (*Headers) UnmarshalBinary

func (h *Headers) UnmarshalBinary(data []byte) (err error)

UnmarshalBinary is not implemented and panics

type SignMessage

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

SignMessage represents a COSESignMessage with CDDL fragment:

COSE_Sign = [

Headers,
payload : bstr / nil,
signatures : [+ COSE_Signature]

]

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

func NewSignMessage

func NewSignMessage(payload []byte) (msg SignMessage)

NewSignMessage takes a []byte payload and returns a new SignMessage with empty headers and signatures

func (*SignMessage) AddSignature

func (m *SignMessage) AddSignature(s *Signature)

AddSignature adds a signature to the message signatures creating an empty []Signature if necessary

func (*SignMessage) SigStructure

func (m *SignMessage) SigStructure(external []byte, signature *Signature) (ToBeSigned []byte, err error)

SigStructure returns the byte slice to be signed

func (*SignMessage) Sign

func (m *SignMessage) Sign(rand io.Reader, external []byte, opts SignOpts) (err error)

Sign signs a SignMessage populating signatures[].signature inplace

func (*SignMessage) SignatureDigest

func (m *SignMessage) SignatureDigest(external []byte, signature *Signature) (digest []byte, err error)

SignatureDigest takes an extra external byte slice and a Signature and returns the SigStructure (i.e. ToBeSigned) hashed using the algorithm from the signature parameter

TODO: check that signature is in SignMessage?

func (*SignMessage) Verify

func (m *SignMessage) Verify(external []byte, opts *VerifyOpts) (err error)

Verify verifies all signatures on the SignMessage returning nil for success or an error

type SignOpts

type SignOpts struct {
	HashFunc  crypto.Hash
	GetSigner func(index int, signature Signature) (Signer, error)
}

SignOpts are options for Signer.Sign()

HashFunc is the crypto.Hash to apply to the SigStructure func GetSigner returns the cose.Signer for the signature protected key ID or an error when one isn't found

type Signature

type Signature struct {
	Headers        *Headers
	SignatureBytes []byte
}

Signature represents a COSE signature with CDDL fragment:

COSE_Signature = [

Headers,
signature : bstr

]

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

func NewSignature

func NewSignature() (s *Signature)

NewSignature returns a new COSE Signature with empty headers and nil signature bytes

func (*Signature) Decode

func (s *Signature) Decode(o interface{})

Decode updates the signature inplace from its COSE serialization

type Signer

type Signer struct {
	// contains filtered or unexported fields
}

Signer holds a private key for signing SignMessages implements crypto.Signer interface

func NewSigner

func NewSigner(privateKey crypto.PrivateKey) (signer *Signer, err error)

NewSigner checks whether the privateKey is supported and returns a new cose.Signer

func (*Signer) Public

func (s *Signer) Public() (publicKey crypto.PublicKey)

Public returns the crypto.PublicKey for the Signer's privateKey

func (*Signer) Sign

func (s *Signer) Sign(rand io.Reader, digest []byte, opts SignOpts) (signature []byte, err error)

Sign returns the COSE signature as a byte slice

func (*Signer) Verifier

func (s *Signer) Verifier(alg *Algorithm) (verifier *Verifier)

Verifier returns a Verifier using the Signer's public key and provided Algorithm

type Verifier

type Verifier struct {
	// contains filtered or unexported fields
}

Verifier holds a PublicKey and Algorithm to verify signatures

func (*Verifier) Verify

func (v *Verifier) Verify(digest []byte, signature []byte) (err error)

Verify verifies a signature returning nil for success or an error

type VerifyOpts

type VerifyOpts struct {
	GetVerifier func(index int, signature Signature) (Verifier, error)
}

VerifyOpts are options to the Verifier.Verify requires a function that returns verifier or error for a given signature and message index

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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