Skip to content

Commit

Permalink
feat: support detached payloads in COSESign and COSESign1
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Richards <[email protected]>
  • Loading branch information
alex-richards committed Aug 14, 2024
1 parent fec7472 commit 40060e0
Show file tree
Hide file tree
Showing 7 changed files with 557 additions and 111 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ vendor/

# Editor files
.vscode/
.idea/
180 changes: 90 additions & 90 deletions bench_test.go
Original file line number Diff line number Diff line change
@@ -1,90 +1,90 @@
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)
}
}
}
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)
}
}
}
1 change: 1 addition & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var (
ErrEmptySignature = errors.New("empty signature")
ErrInvalidAlgorithm = errors.New("invalid algorithm")
ErrMissingPayload = errors.New("missing payload")
ErrMultiplePayloads = errors.New("multiple payloads")
ErrNoSignatures = errors.New("no signatures attached")
ErrUnavailableHashFunc = errors.New("hash function is not available")
ErrVerification = errors.New("verification error")
Expand Down
87 changes: 79 additions & 8 deletions sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,12 +398,37 @@ func (m *SignMessage) UnmarshalCBOR(data []byte) error {
// Notice: The COSE Sign API is EXPERIMENTAL and may be changed or removed in a
// later release.
func (m *SignMessage) Sign(rand io.Reader, external []byte, signers ...Signer) error {
return m.sign(rand, nil, external, signers)
}

// SignDetached 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 (m *SignMessage) SignDetached(rand io.Reader, detached, external []byte, signers ...Signer) error {
if detached == nil {
return ErrMissingPayload
}
return m.sign(rand, detached, external, signers)
}

func (m *SignMessage) sign(rand io.Reader, detached, external []byte, signers []Signer) error {
if m == nil {
return errors.New("signing nil SignMessage")
}
if m.Payload == nil {
return ErrMissingPayload

payload, err := checkPayload(m.Payload, detached)
if err != nil {
return err
}

switch len(m.Signatures) {
case 0:
return ErrNoSignatures
Expand All @@ -415,14 +440,14 @@ func (m *SignMessage) Sign(rand io.Reader, external []byte, signers ...Signer) e

// populate common parameters
var protected cbor.RawMessage
protected, err := m.Headers.MarshalProtected()
protected, err = m.Headers.MarshalProtected()
if err != nil {
return err
}

// sign message accordingly
for i, signature := range m.Signatures {
if err := signature.Sign(rand, signers[i], protected, m.Payload, external); err != nil {
if err := signature.Sign(rand, signers[i], protected, payload, external); err != nil {
return err
}
}
Expand All @@ -443,12 +468,41 @@ func (m *SignMessage) Sign(rand io.Reader, external []byte, signers ...Signer) e
// Notice: The COSE Sign API is EXPERIMENTAL and may be changed or removed in a
// later release.
func (m *SignMessage) Verify(external []byte, verifiers ...Verifier) error {
return m.verify(nil, external, verifiers...)
}

// VerifyDetached verifies the signatures on the SignMessage against the corresponding
// verifier, returning nil on success or a suitable error if verification fails.
//
// Returns ErrMissingPayload if detached is nil, and ErrMultiplePayloads if the SignMessage
// also has an attached payload.
//
// 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.
func (m *SignMessage) VerifyDetached(detached, external []byte, verifiers ...Verifier) error {
if detached == nil {
return ErrMissingPayload
}
return m.verify(detached, external, verifiers...)
}

func (m *SignMessage) verify(detached, external []byte, verifiers ...Verifier) error {
if m == nil {
return errors.New("verifying nil SignMessage")
}
if m.Payload == nil {
return ErrMissingPayload

payload, err := checkPayload(m.Payload, detached)
if err != nil {
return err
}

switch len(m.Signatures) {
case 0:
return ErrNoSignatures
Expand All @@ -460,16 +514,33 @@ func (m *SignMessage) Verify(external []byte, verifiers ...Verifier) error {

// populate common parameters
var protected cbor.RawMessage
protected, err := m.Headers.MarshalProtected()
protected, err = m.Headers.MarshalProtected()
if err != nil {
return err
}

// verify message accordingly
for i, signature := range m.Signatures {
if err := signature.Verify(verifiers[i], protected, m.Payload, external); err != nil {
if err := signature.Verify(verifiers[i], protected, payload, external); err != nil {
return err
}
}
return nil
}

/**
* checkPayload checks that exactly one of payload and detached is non-nil.
*/
func checkPayload(payload, detached []byte) ([]byte, error) {
if detached == nil {
if payload == nil {
return nil, ErrMissingPayload
}
return payload, nil
} else {
if payload != nil {
return nil, ErrMultiplePayloads
}
return detached, nil
}
}
Loading

0 comments on commit 40060e0

Please sign in to comment.