Skip to content

Commit

Permalink
Merge pull request #42 from adityasaky/code-cleanup
Browse files Browse the repository at this point in the history
Reorganize DSSE library
  • Loading branch information
adityasaky authored May 2, 2023
2 parents 6476f36 + 7a2c08e commit 83ae237
Show file tree
Hide file tree
Showing 7 changed files with 474 additions and 468 deletions.
35 changes: 19 additions & 16 deletions cjson/canonicaljson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,29 @@ import (
"fmt"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

type KeyVal struct {
type keyVal struct {
Private string `json:"private"`
Public string `json:"public"`
Certificate string `json:"certificate,omitempty"`
}

type Key struct {
type key struct {
KeyID string `json:"keyid"`
KeyIDHashAlgorithms []string `json:"keyid_hash_algorithms"`
KeyType string `json:"keytype"`
KeyVal KeyVal `json:"keyval"`
KeyVal keyVal `json:"keyval"`
Scheme string `json:"scheme"`
}

func TestEncodeCanonical(t *testing.T) {
objects := []interface{}{
Key{},
Key{
KeyVal: KeyVal{
key{},
key{
KeyVal: keyVal{
Private: "priv",
Public: "pub",
},
Expand All @@ -42,8 +44,8 @@ func TestEncodeCanonical(t *testing.T) {
"int2": float64(42),
"string": `\"`,
},
Key{
KeyVal: KeyVal{
key{
KeyVal: keyVal{
Certificate: "cert",
Private: "priv",
Public: "pub",
Expand Down Expand Up @@ -91,21 +93,22 @@ func TestEncodeCanonicalErr(t *testing.T) {
}
}

func TestencodeCanonical(t *testing.T) {
expectedError := "Can't canonicalize"
func TestEncodeCanonicalHelper(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Error("encodeCanonical did not panic as expected")
}
}()

objects := []interface{}{
TestencodeCanonical,
[]interface{}{TestencodeCanonical},
TestEncodeCanonicalHelper,
[]interface{}{TestEncodeCanonicalHelper},
}

for i := 0; i < len(objects); i++ {
var result strings.Builder
err := encodeCanonical(objects[i], &result)
if err == nil || !strings.Contains(err.Error(), expectedError) {
t.Errorf("EncodeCanonical returned '%s', expected '%s' error",
err, expectedError)
}
assert.Nil(t, err)
}
}

Expand Down
64 changes: 64 additions & 0 deletions dsse/envelope.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package dsse

import (
"encoding/base64"
"fmt"
)

/*
Envelope captures an envelope as described by the DSSE specification. See here:
https://github.com/secure-systems-lab/dsse/blob/master/envelope.md
*/
type Envelope struct {
PayloadType string `json:"payloadType"`
Payload string `json:"payload"`
Signatures []Signature `json:"signatures"`
}

/*
DecodeB64Payload returns the serialized body, decoded from the envelope's
payload field. A flexible decoder is used, first trying standard base64, then
URL-encoded base64.
*/
func (e *Envelope) DecodeB64Payload() ([]byte, error) {
return b64Decode(e.Payload)
}

/*
Signature represents a generic in-toto signature that contains the identifier
of the key which was used to create the signature.
The used signature scheme has to be agreed upon by the signer and verifer
out of band.
The signature is a base64 encoding of the raw bytes from the signature
algorithm.
*/
type Signature struct {
KeyID string `json:"keyid"`
Sig string `json:"sig"`
}

/*
PAE implementes the DSSE Pre-Authentic Encoding
https://github.com/secure-systems-lab/dsse/blob/master/protocol.md#signature-definition
*/
func PAE(payloadType string, payload []byte) []byte {
return []byte(fmt.Sprintf("DSSEv1 %d %s %d %s",
len(payloadType), payloadType,
len(payload), payload))
}

/*
Both standard and url encoding are allowed:
https://github.com/secure-systems-lab/dsse/blob/master/envelope.md
*/
func b64Decode(s string) ([]byte, error) {
b, err := base64.StdEncoding.DecodeString(s)
if err != nil {
b, err = base64.URLEncoding.DecodeString(s)
if err != nil {
return nil, fmt.Errorf("unable to base64 decode payload (is payload in the right format?)")
}
}

return b, nil
}
132 changes: 9 additions & 123 deletions dsse/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,109 +8,32 @@ import (
"context"
"encoding/base64"
"errors"
"fmt"
)

// ErrUnknownKey indicates that the implementation does not recognize the
// key.
var ErrUnknownKey = errors.New("unknown key")

// ErrNoSignature indicates that an envelope did not contain any signatures.
var ErrNoSignature = errors.New("no signature found")

// ErrNoSigners indicates that no signer was provided.
var ErrNoSigners = errors.New("no signers provided")

/*
Envelope captures an envelope as described by the Secure Systems Lab
Signing Specification. See here:
https://github.com/secure-systems-lab/signing-spec/blob/master/envelope.md
*/
type Envelope struct {
PayloadType string `json:"payloadType"`
Payload string `json:"payload"`
Signatures []Signature `json:"signatures"`
}

/*
DecodeB64Payload returns the serialized body, decoded
from the envelope's payload field. A flexible
decoder is used, first trying standard base64, then
URL-encoded base64.
*/
func (e *Envelope) DecodeB64Payload() ([]byte, error) {
return b64Decode(e.Payload)
}

/*
Signature represents a generic in-toto signature that contains the identifier
of the key which was used to create the signature.
The used signature scheme has to be agreed upon by the signer and verifer
out of band.
The signature is a base64 encoding of the raw bytes from the signature
algorithm.
*/
type Signature struct {
KeyID string `json:"keyid"`
Sig string `json:"sig"`
}

/*
PAE implementes the DSSE Pre-Authentic Encoding
https://github.com/secure-systems-lab/dsse/blob/master/protocol.md#signature-definition
*/
func PAE(payloadType string, payload []byte) []byte {
return []byte(fmt.Sprintf("DSSEv1 %d %s %d %s",
len(payloadType), payloadType,
len(payload), payload))
}

/*
Signer defines the interface for an abstract signing algorithm.
The Signer interface is used to inject signature algorithm implementations
into the EnevelopeSigner. This decoupling allows for any signing algorithm
and key management system can be used.
The full message is provided as the parameter. If the signature algorithm
depends on hashing of the message prior to signature calculation, the
implementor of this interface must perform such hashing.
The function must return raw bytes representing the calculated signature
using the current algorithm, and the key used (if applicable).
For an example see EcdsaSigner in sign_test.go.
*/
type Signer interface {
Sign(ctx context.Context, data []byte) ([]byte, error)
KeyID() (string, error)
}

// SignVerifer provides both the signing and verification interface.
type SignVerifier interface {
Signer
Verifier
}

// EnvelopeSigner creates signed Envelopes.
type EnvelopeSigner struct {
providers []SignVerifier
ev *EnvelopeVerifier
providers []SignerVerifier
}

/*
NewEnvelopeSigner creates an EnvelopeSigner that uses 1+ Signer
algorithms to sign the data.
Creates a verifier with threshold=1, at least one of the providers must validate signitures successfully.
NewEnvelopeSigner creates an EnvelopeSigner that uses 1+ Signer algorithms to
sign the data. Creates a verifier with threshold=1, at least one of the
providers must validate signatures successfully.
*/
func NewEnvelopeSigner(p ...SignVerifier) (*EnvelopeSigner, error) {
func NewEnvelopeSigner(p ...SignerVerifier) (*EnvelopeSigner, error) {
return NewMultiEnvelopeSigner(1, p...)
}

/*
NewMultiEnvelopeSigner creates an EnvelopeSigner that uses 1+ Signer
algorithms to sign the data.
Creates a verifier with threshold.
threashold indicates the amount of providers that must validate the envelope.
algorithms to sign the data. Creates a verifier with threshold. Threshold
indicates the amount of providers that must validate the envelope.
*/
func NewMultiEnvelopeSigner(threshold int, p ...SignVerifier) (*EnvelopeSigner, error) {
var providers []SignVerifier
func NewMultiEnvelopeSigner(threshold int, p ...SignerVerifier) (*EnvelopeSigner, error) {
var providers []SignerVerifier

for _, sv := range p {
if sv != nil {
Expand All @@ -122,19 +45,8 @@ func NewMultiEnvelopeSigner(threshold int, p ...SignVerifier) (*EnvelopeSigner,
return nil, ErrNoSigners
}

evps := []Verifier{}
for _, p := range providers {
evps = append(evps, p.(Verifier))
}

ev, err := NewMultiEnvelopeVerifier(threshold, evps...)
if err != nil {
return nil, err
}

return &EnvelopeSigner{
providers: providers,
ev: ev,
}, nil
}

Expand Down Expand Up @@ -170,29 +82,3 @@ func (es *EnvelopeSigner) SignPayload(ctx context.Context, payloadType string, b

return &e, nil
}

/*
Verify decodes the payload and verifies the signature.
Any domain specific validation such as parsing the decoded body and
validating the payload type is left out to the caller.
Verify returns a list of accepted keys each including a keyid, public and signiture of the accepted provider keys.
*/
func (es *EnvelopeSigner) Verify(ctx context.Context, e *Envelope) ([]AcceptedKey, error) {
return es.ev.Verify(ctx, e)
}

/*
Both standard and url encoding are allowed:
https://github.com/secure-systems-lab/dsse/blob/master/envelope.md
*/
func b64Decode(s string) ([]byte, error) {
b, err := base64.StdEncoding.DecodeString(s)
if err != nil {
b, err = base64.URLEncoding.DecodeString(s)
if err != nil {
return nil, err
}
}

return b, nil
}
Loading

0 comments on commit 83ae237

Please sign in to comment.