-
Notifications
You must be signed in to change notification settings - Fork 2
/
rsa.go
109 lines (93 loc) · 2.89 KB
/
rsa.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
package awssigner
import (
"context"
"crypto"
"crypto/x509"
"fmt"
"io"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/kms"
"github.com/aws/aws-sdk-go-v2/service/kms/types"
)
type RSA struct {
alg types.SigningAlgorithmSpec
client *kms.Client
ctx context.Context
kid string
}
// NewRSA creates a new RSA object. This object isnot complete by itself -- it
// needs to be setup with the algorithm name to use (see
// https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/kms/types#SigningAlgorithmSpec),
// a key ID, and a context.Context object to use while the AWS SDK makes network
// requests.
func NewRSA(client *kms.Client) *RSA {
return &RSA{
client: client,
}
}
func (sv *RSA) getContext() context.Context {
ctx := sv.ctx
if ctx == nil {
ctx = context.Background()
}
return ctx
}
// Sign generates a signature from the given digest.
func (sv *RSA) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
if sv.alg == "" {
return nil, fmt.Errorf(`aws.RSA.Sign() requires the types.SigningAlgorithmSpec`)
}
if sv.kid == "" {
return nil, fmt.Errorf(`aws.RSA.Sign() requires the key ID`)
}
// sv.ctx is NOT required, but we will use context.Background here
// which means there will not be a (clean) way to interrupt this
// operation
ctx := sv.getContext()
input := kms.SignInput{
KeyId: aws.String(sv.kid),
Message: digest,
MessageType: types.MessageTypeDigest,
SigningAlgorithm: sv.alg,
}
signed, err := sv.client.Sign(ctx, &input)
if err != nil {
return nil, fmt.Errorf(`failed to sign via KMS: %w`, err)
}
return signed.Signature, nil
}
// Public returns the corresponding public key.
//
// Because the crypto.Signer API does not allow for an error to be returned,
// the return value from this function cannot describe what kind of error
// occurred.
func (sv *RSA) Public() crypto.PublicKey {
pubkey, _ := sv.GetPublicKey()
return pubkey
}
// This method is an escape hatch for those cases where the user needs
// to debug what went wrong during the GetPublicKey operation.
func (sv *RSA) GetPublicKey() (crypto.PublicKey, error) {
if sv.kid == "" {
return nil, fmt.Errorf(`aws.RSA.Sign() requires the key ID`)
}
// sv.ctx is NOT required, but we will use context.Background here
// which means there will not be a (clean) way to interrupt this
// operation
ctx := sv.getContext()
input := kms.GetPublicKeyInput{
KeyId: aws.String(sv.kid),
}
output, err := sv.client.GetPublicKey(ctx, &input)
if err != nil {
return nil, fmt.Errorf(`failed to get public key from KMS: %w`, err)
}
if output.KeyUsage != types.KeyUsageTypeSignVerify {
return nil, fmt.Errorf(`invalid key usage. expected SIGN_VERIFY, got %q`, output.KeyUsage)
}
key, err := x509.ParsePKIXPublicKey(output.PublicKey)
if err != nil {
return nil, fmt.Errorf(`failed to parse key: %w`, err)
}
return key, nil
}