-
Notifications
You must be signed in to change notification settings - Fork 0
/
signer.go
124 lines (113 loc) · 4.09 KB
/
signer.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
package awsspiffe
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/sha256"
"crypto/sha512"
"crypto/x509"
"fmt"
"io"
"github.com/spiffe/go-spiffe/v2/svid/x509svid"
)
// SPIFFESigner creates signatures compatible with the AWS RolesAnywhere
// API using an X509 SVID. It implements the aws_signing_helper.Signer
// interface.
type X509SVIDSigner struct {
SVID *x509svid.SVID
}
// Public returns the public key of the keypair associated with the signer's
// X509 SVID. Implements the crypto.Signer interface.
func (s *X509SVIDSigner) Public() crypto.PublicKey {
return s.SVID.PrivateKey.Public()
}
// Sign creates a signature of the given input using the keypair associated with
// the signer's X509 SVID.
// Implements the aws_signing_helper.Signer and crypto.Signer interfaces.
func (s *X509SVIDSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
// Note(strideynet):
// As of the time of writing, it looks like the AWS signing helper will
// only ever invoke Sign with SHA256, however, their signer implementations
// do also support SHA384 and SHA512. It feels safest to support all three
// here as well.
//
// Looking at the documentation for AWS SigV4, it looks like SHA256 is also
// the only supported hash function today...
var hash []byte
switch opts.HashFunc() {
case crypto.SHA256:
sum := sha256.Sum256(digest)
hash = sum[:]
case crypto.SHA384:
sum := sha512.Sum384(digest)
hash = sum[:]
case crypto.SHA512:
sum := sha512.Sum512(digest)
hash = sum[:]
default:
return nil, fmt.Errorf("unsupported hash function: %v", opts.HashFunc())
}
// From https://docs.aws.amazon.com/rolesanywhere/latest/userguide/authentication.html
// > RSA and EC keys are supported; RSA keys are used with the RSA PKCS#
// > v1.5 signing algorithm. EC keys are used with the ECDSA.
switch key := s.SVID.PrivateKey.(type) {
case *rsa.PrivateKey:
sig, err := rsa.SignPKCS1v15(rand, key, opts.HashFunc(), hash)
if err != nil {
return nil, fmt.Errorf("signing with RSA: %w", err)
}
return sig, nil
case *ecdsa.PrivateKey:
sig, err := ecdsa.SignASN1(rand, key, hash)
if err != nil {
return nil, fmt.Errorf("signing with ECDSA: %w", err)
}
return sig, nil
default:
return nil, fmt.Errorf("unsupported key type: %T", s.SVID.PrivateKey)
}
}
// From https://docs.aws.amazon.com/rolesanywhere/latest/userguide/authentication-sign-process.html
// > Algorithm. As described above, instead of AWS4-HMAC-SHA256, the algorithm
// > field will have the values of the form AWS4-X509-RSA-SHA256 or
// > AWS4-X509-ECDSA-SHA256, depending on whether an RSA or Elliptic Curve
// > algorithm is used. This, in turn, is determined by the key bound to the
// > signing certificate.
const (
awsV4X509RSASHA256 = "AWS4-X509-RSA-SHA256"
awsV4X509ECDSASHA256 = "AWS4-X509-ECDSA-SHA256"
)
// SignatureAlgorithm returns the signature algorithm of the underlying
// private key, in the representation expected by AWS.
// See https://docs.aws.amazon.com/rolesanywhere/latest/userguide/authentication-sign-process.html
func (s *X509SVIDSigner) SignatureAlgorithm() (string, error) {
switch s.SVID.PrivateKey.(type) {
case *rsa.PrivateKey:
return awsV4X509RSASHA256, nil
case *ecdsa.PrivateKey:
return awsV4X509ECDSASHA256, nil
default:
return "", fmt.Errorf("unsupported key type: %T", s.SVID.PrivateKey)
}
}
// Certificate returns the leaf certificate e.g the one identifying the
// workload.
// Implements the aws_signing_helper.Signer interface.
func (s *X509SVIDSigner) Certificate() (*x509.Certificate, error) {
return s.SVID.Certificates[0], nil
}
// CertificateChain returns any certificates needed to chain the leaf to
// the trust anchor.
// Implements the aws_signing_helper.Signer interface.
func (s *X509SVIDSigner) CertificateChain() ([]*x509.Certificate, error) {
if len(s.SVID.Certificates) < 1 {
return s.SVID.Certificates[1:], nil
}
return nil, nil
}
// Close should be called when the signer is no longer needed. It is a no-op
// for this implementation.
// Implements the aws_signing_helper.Signer interface.
func (s *X509SVIDSigner) Close() {
// Nothing to do here...
}