-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsign.go
128 lines (112 loc) · 3.6 KB
/
sign.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package signedattestation
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/openpubkey/openpubkey/client"
"github.com/openpubkey/openpubkey/util"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
// the following types are needed until https://github.com/secure-systems-lab/dsse/pull/61 is merged
type Envelope struct {
PayloadType string `json:"payloadType"`
Payload string `json:"payload"`
Signatures []Signature `json:"signatures"`
}
type Signature struct {
KeyID string `json:"keyid"`
Sig string `json:"sig"`
Extension Extension `json:"extension"`
}
type Extension struct {
Kind string `json:"kind"`
Ext map[string]any `json:"ext"`
}
func SignInTotoStatement(ctx context.Context, stmt intoto.Statement, provider client.OpenIdProvider) (*dsse.Envelope, error) {
s, err := dsse.NewEnvelopeSigner(NewOPKSignerVerifier(provider))
if err != nil {
return nil, fmt.Errorf("error creating dsse signer: %w", err)
}
payload, err := json.Marshal(stmt)
if err != nil {
return nil, err
}
env, err := s.SignPayload(ctx, intoto.PayloadType, payload)
if err != nil {
return nil, err
}
return env, nil
}
func SignInTotoStatementExt(ctx context.Context, stmt intoto.Statement, provider client.OpenIdProvider) (*Envelope, error) {
tl := GetTL(ctx)
// encode in-toto statement
payload, err := json.Marshal(stmt)
if err != nil {
return nil, err
}
env := new(Envelope)
env.Payload = base64.StdEncoding.Strict().EncodeToString(payload)
env.PayloadType = intoto.PayloadType
encPayload := dsse.PAE(intoto.PayloadType, payload)
// statement message digest
hash := s256(encPayload)
hashHex := hex.EncodeToString(hash)
// generate ephemeral keys to sign message digest
signer, err := util.GenKeyPair(jwa.ES256)
if err != nil {
return nil, fmt.Errorf("error generating key pair: %w", err)
}
sig, err := signer.Sign(rand.Reader, hash, crypto.SHA256)
if err != nil {
return nil, err
}
ecPub, ok := signer.Public().(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("error casting signer to ecdsa public key")
}
pub, err := x509.MarshalPKIXPublicKey(ecPub)
if err != nil {
return nil, fmt.Errorf("error marshalling public key: %w", err)
}
keyID := s256(pub)
// generate pk token with message digest and ephemeral signing keys
opkClient := client.OpkClient{Op: provider}
pkToken, err := opkClient.OidcAuth(ctx, signer, jwa.ES256, map[string]any{"att": hashHex}, true)
if err != nil {
return nil, fmt.Errorf("error getting PK token: %w", err)
}
pkTokenJSON, err := json.Marshal(pkToken)
if err != nil {
return nil, fmt.Errorf("error marshalling PK token to JSON: %w", err)
}
// upload to TL
entryBytes, err := tl.UploadLogEntry(ctx, pkToken, encPayload, sig, signer)
if err != nil {
return nil, fmt.Errorf("error uploading TL entry: %w", err)
}
entryObj, err := tl.UnmarshalEntry(entryBytes)
if err != nil {
return nil, fmt.Errorf("error unmarshaling tl entry: %w", err)
}
// add signature w/ opk extension to dsse envelope
env.Signatures = append(env.Signatures, Signature{
KeyID: hex.EncodeToString(keyID), // ephemeral public key ID
Sig: base64.StdEncoding.Strict().EncodeToString(sig), // ECDSA signature using ephemeral keys
Extension: Extension{
Kind: OpkSignatureID,
Ext: map[string]any{
"pkt": pkTokenJSON, // PK token + GQ signature
"tl": entryObj, // transparency log entry metadata
},
},
})
return env, nil
}