Skip to content

Commit 9da84e7

Browse files
authored
Merge pull request #1 from adityasaky/add-dsse
Add DSSE
2 parents d68b968 + a7f373c commit 9da84e7

File tree

6 files changed

+788
-0
lines changed

6 files changed

+788
-0
lines changed

dsse/sign.go

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*
2+
Package dsse implements the Dead Simple Signing Envelope (DSSE)
3+
https://github.com/secure-systems-lab/dsse
4+
*/
5+
package dsse
6+
7+
import (
8+
"encoding/base64"
9+
"errors"
10+
"fmt"
11+
)
12+
13+
// ErrUnknownKey indicates that the implementation does not recognize the
14+
// key.
15+
var ErrUnknownKey = errors.New("unknown key")
16+
17+
// ErrNoSignature indicates that an envelope did not contain any signatures.
18+
var ErrNoSignature = errors.New("no signature found")
19+
20+
// ErrNoSigners indicates that no signer was provided.
21+
var ErrNoSigners = errors.New("no signers provided")
22+
23+
/*
24+
Envelope captures an envelope as described by the Secure Systems Lab
25+
Signing Specification. See here:
26+
https://github.com/secure-systems-lab/signing-spec/blob/master/envelope.md
27+
*/
28+
type Envelope struct {
29+
PayloadType string `json:"payloadType"`
30+
Payload string `json:"payload"`
31+
Signatures []Signature `json:"signatures"`
32+
}
33+
34+
/*
35+
Signature represents a generic in-toto signature that contains the identifier
36+
of the key which was used to create the signature.
37+
The used signature scheme has to be agreed upon by the signer and verifer
38+
out of band.
39+
The signature is a base64 encoding of the raw bytes from the signature
40+
algorithm.
41+
*/
42+
type Signature struct {
43+
KeyID string `json:"keyid"`
44+
Sig string `json:"sig"`
45+
}
46+
47+
/*
48+
PAE implementes the DSSE Pre-Authentic Encoding
49+
https://github.com/secure-systems-lab/dsse/blob/master/protocol.md#signature-definition
50+
*/
51+
func PAE(payloadType, payload string) []byte {
52+
return []byte(fmt.Sprintf("DSSEv1 %d %s %d %s",
53+
len(payloadType), payloadType,
54+
len(payload), payload))
55+
}
56+
57+
/*
58+
Signer defines the interface for an abstract signing algorithm.
59+
The Signer interface is used to inject signature algorithm implementations
60+
into the EnevelopeSigner. This decoupling allows for any signing algorithm
61+
and key management system can be used.
62+
The full message is provided as the parameter. If the signature algorithm
63+
depends on hashing of the message prior to signature calculation, the
64+
implementor of this interface must perform such hashing.
65+
The function must return raw bytes representing the calculated signature
66+
using the current algorithm, and the key used (if applicable).
67+
For an example see EcdsaSigner in sign_test.go.
68+
*/
69+
type Signer interface {
70+
Sign(data []byte) ([]byte, string, error)
71+
}
72+
73+
// SignVerifer provides both the signing and verification interface.
74+
type SignVerifier interface {
75+
Signer
76+
Verifier
77+
}
78+
79+
// EnvelopeSigner creates signed Envelopes.
80+
type EnvelopeSigner struct {
81+
providers []SignVerifier
82+
ev EnvelopeVerifier
83+
}
84+
85+
/*
86+
NewEnvelopeSigner creates an EnvelopeSigner that uses 1+ Signer
87+
algorithms to sign the data.
88+
*/
89+
func NewEnvelopeSigner(p ...SignVerifier) (*EnvelopeSigner, error) {
90+
var providers []SignVerifier
91+
92+
for _, sv := range p {
93+
if sv != nil {
94+
providers = append(providers, sv)
95+
}
96+
}
97+
98+
if len(providers) == 0 {
99+
return nil, ErrNoSigners
100+
}
101+
102+
evps := []Verifier{}
103+
for _, p := range providers {
104+
evps = append(evps, p.(Verifier))
105+
}
106+
107+
return &EnvelopeSigner{
108+
providers: providers,
109+
ev: EnvelopeVerifier{
110+
providers: evps,
111+
},
112+
}, nil
113+
}
114+
115+
/*
116+
SignPayload signs a payload and payload type according to DSSE.
117+
Returned is an envelope as defined here:
118+
https://github.com/secure-systems-lab/dsse/blob/master/envelope.md
119+
One signature will be added for each Signer in the EnvelopeSigner.
120+
*/
121+
func (es *EnvelopeSigner) SignPayload(payloadType string, body []byte) (*Envelope, error) {
122+
var e = Envelope{
123+
Payload: base64.StdEncoding.EncodeToString(body),
124+
PayloadType: payloadType,
125+
}
126+
127+
paeEnc := PAE(payloadType, string(body))
128+
129+
for _, signer := range es.providers {
130+
sig, keyID, err := signer.Sign(paeEnc)
131+
if err != nil {
132+
return nil, err
133+
}
134+
135+
e.Signatures = append(e.Signatures, Signature{
136+
KeyID: keyID,
137+
Sig: base64.StdEncoding.EncodeToString(sig),
138+
})
139+
}
140+
141+
return &e, nil
142+
}
143+
144+
/*
145+
Verify decodes the payload and verifies the signature.
146+
Any domain specific validation such as parsing the decoded body and
147+
validating the payload type is left out to the caller.
148+
*/
149+
func (es *EnvelopeSigner) Verify(e *Envelope) error {
150+
return es.ev.Verify(e)
151+
}
152+
153+
/*
154+
Both standard and url encoding are allowed:
155+
https://github.com/secure-systems-lab/dsse/blob/master/envelope.md
156+
*/
157+
func b64Decode(s string) ([]byte, error) {
158+
b, err := base64.StdEncoding.DecodeString(s)
159+
if err != nil {
160+
b, err = base64.URLEncoding.DecodeString(s)
161+
if err != nil {
162+
return nil, err
163+
}
164+
}
165+
166+
return b, nil
167+
}

0 commit comments

Comments
 (0)