-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathsign.go
More file actions
130 lines (113 loc) · 4.07 KB
/
sign.go
File metadata and controls
130 lines (113 loc) · 4.07 KB
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
129
130
package replidentity
import (
"crypto/ed25519"
"encoding/base64"
"errors"
"fmt"
"github.com/o1egl/paseto"
"github.com/replit/go-replidentity/paserk"
"github.com/replit/go-replidentity/protos/external/goval/api"
"google.golang.org/protobuf/proto"
)
// SigningAuthority can generate tokens that prove the identity of one repl
// (your own) against another repl (the audience). Use this to prevent the
// target repl from spoofing your own identity by forwarding the token.
type SigningAuthority struct {
privateKey ed25519.PrivateKey
signingAuthority *api.GovalSigningAuthority
identity *api.GovalReplIdentity
}
// NewSigningAuthority returns a new SigningAuthority given the marshaled
// private key (obtained from the `REPL_IDENTITY_KEY` environment variable or /tmp/replidentity.key),
// the identity token (obtained from the `REPL_IDENTITY` environment variable or /tmp/replidentity),
// the current Repl ID (obtained from the `REPL_ID` environment varaible), and
// the source of public keys (typically [ReadPublicKeyFromEnv]).
func NewSigningAuthority(
marshaledPrivateKey,
marshaledIdentity string,
replid string,
getPubKey PubKeySource,
) (*SigningAuthority, error) {
v, bytes, _, err := verifyChain(marshaledIdentity, getPubKey)
if err != nil {
return nil, fmt.Errorf("failed verify message: %w", err)
}
signingAuthority, err := getSigningAuthority(marshaledIdentity)
if err != nil {
return nil, fmt.Errorf("failed to read body type: %w", err)
}
privateKey, err := paserk.PASERKSecretToPrivateKey(paserk.PASERKSecret(marshaledPrivateKey))
if err != nil {
return nil, fmt.Errorf("failed to read private key: %w", err)
}
var identity api.GovalReplIdentity
switch signingAuthority.GetVersion() {
case api.TokenVersion_BARE_REPL_TOKEN:
return nil, errors.New("wrong type of token provided")
case api.TokenVersion_TYPE_AWARE_TOKEN:
err = proto.Unmarshal(bytes, &identity)
if err != nil {
return nil, fmt.Errorf("failed to decode body: %w", err)
}
}
err = v.checkClaimsAgainstToken(&identity)
if err != nil {
return nil, fmt.Errorf("claim mismatch: %w", err)
}
if replid != identity.Replid {
return nil, fmt.Errorf("message replid mismatch. expected %q, got %q", replid, identity.Replid)
}
if replid != identity.Aud {
return nil, fmt.Errorf("message audience mismatch. expected %q, got %q", replid, identity.Aud)
}
return &SigningAuthority{
privateKey: privateKey,
signingAuthority: signingAuthority,
identity: &identity,
}, nil
}
func (s *SigningAuthority) String() string {
return fmt.Sprintf("SigningAuthority{signingAuthority: %s, identity: %s}", s.signingAuthority, s.identity)
}
// Sign generates a new token that can be given to the provided audience, and
// is resistant against forwarding, so that the recipient cannot forward this
// token to another repl and claim it came directly from you.
func (a *SigningAuthority) Sign(audience string) (string, error) {
replIdentity := api.GovalReplIdentity{
Replid: a.identity.Replid,
User: a.identity.User,
Slug: a.identity.Slug,
Aud: audience,
OriginReplid: a.identity.OriginReplid,
UserId: a.identity.UserId,
Org: a.identity.Org,
BuildInfo: a.identity.BuildInfo,
IsTeam: a.identity.IsTeam,
Roles: a.identity.Roles,
Runtime: a.identity.Runtime,
}
token, err := signIdentity(a.privateKey, a.signingAuthority, &replIdentity)
if err != nil {
return "", fmt.Errorf("sign identity: %w", err)
}
return token, nil
}
func signIdentity(
parentPrivateKey ed25519.PrivateKey,
parentAuthority *api.GovalSigningAuthority,
identity *api.GovalReplIdentity,
) (string, error) {
encodedIdentity, err := proto.Marshal(identity)
if err != nil {
return "", fmt.Errorf("failed to serialize the identity: %w", err)
}
serializedCert, err := proto.Marshal(parentAuthority)
if err != nil {
return "", fmt.Errorf("failed to serialize the cert: %w", err)
}
return paseto.NewV2().Sign(
parentPrivateKey,
base64.StdEncoding.EncodeToString(encodedIdentity),
base64.StdEncoding.EncodeToString(serializedCert),
)
}