Skip to content

Commit 1c3e073

Browse files
committed
Multiple changes
* Address review comments from @pxp928 * Add docstrings * Add credits for code from in-toto-golang * Check if KeyVal is missing when creating signerverifiers * Make KeyID public and calculate key ID while loading only if it's missing Signed-off-by: Aditya Sirish <[email protected]>
1 parent 7d8d7bc commit 1c3e073

File tree

11 files changed

+91
-42
lines changed

11 files changed

+91
-42
lines changed

signerverifier/ecdsa.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,35 @@ import (
77
"crypto/rand"
88
"crypto/sha256"
99
"crypto/sha512"
10+
"fmt"
1011
"os"
1112
)
1213

1314
const ECDSAKeyType = "ecdsa"
1415

16+
// ECDSASignerVerifier is a dsse.SignerVerifier compliant interface to sign and
17+
// verify signatures using ECDSA keys.
1518
type ECDSASignerVerifier struct {
1619
keyID string
1720
curveSize int
1821
private *ecdsa.PrivateKey
1922
public *ecdsa.PublicKey
2023
}
2124

25+
// NewECDSASignerVerifierFromSSLibKey creates an ECDSASignerVerifier from an
26+
// SSLibKey.
2227
func NewECDSASignerVerifierFromSSLibKey(key *SSLibKey) (*ECDSASignerVerifier, error) {
28+
if len(key.KeyVal.Public) == 0 {
29+
return nil, ErrInvalidKey
30+
}
31+
2332
_, publicParsedKey, err := decodeAndParsePEM([]byte(key.KeyVal.Public))
2433
if err != nil {
25-
return nil, err
34+
return nil, fmt.Errorf("unable to create ECDSA signerverifier: %w", err)
2635
}
2736

2837
sv := &ECDSASignerVerifier{
29-
keyID: key.KeyID(),
38+
keyID: key.KeyID,
3039
curveSize: publicParsedKey.(*ecdsa.PublicKey).Params().BitSize,
3140
public: publicParsedKey.(*ecdsa.PublicKey),
3241
private: nil,
@@ -35,7 +44,7 @@ func NewECDSASignerVerifierFromSSLibKey(key *SSLibKey) (*ECDSASignerVerifier, er
3544
if len(key.KeyVal.Private) > 0 {
3645
_, privateParsedKey, err := decodeAndParsePEM([]byte(key.KeyVal.Private))
3746
if err != nil {
38-
return nil, err
47+
return nil, fmt.Errorf("unable to create ECDSA signerverifier: %w", err)
3948
}
4049

4150
sv.private = privateParsedKey.(*ecdsa.PrivateKey)
@@ -44,6 +53,7 @@ func NewECDSASignerVerifierFromSSLibKey(key *SSLibKey) (*ECDSASignerVerifier, er
4453
return sv, nil
4554
}
4655

56+
// Sign creates a signature for `data`.
4757
func (sv *ECDSASignerVerifier) Sign(ctx context.Context, data []byte) ([]byte, error) {
4858
if sv.private == nil {
4959
return nil, ErrNotPrivateKey
@@ -54,6 +64,7 @@ func (sv *ECDSASignerVerifier) Sign(ctx context.Context, data []byte) ([]byte, e
5464
return ecdsa.SignASN1(rand.Reader, sv.private, hashedData)
5565
}
5666

67+
// Verify verifies the `sig` value passed in against `data`.
5768
func (sv *ECDSASignerVerifier) Verify(ctx context.Context, data []byte, sig []byte) error {
5869
hashedData := getECDSAHashedData(data, sv.curveSize)
5970

@@ -64,18 +75,24 @@ func (sv *ECDSASignerVerifier) Verify(ctx context.Context, data []byte, sig []by
6475
return nil
6576
}
6677

78+
// KeyID returns the identifier of the key used to create the
79+
// ECDSASignerVerifier instance.
6780
func (sv *ECDSASignerVerifier) KeyID() (string, error) {
6881
return sv.keyID, nil
6982
}
7083

84+
// Public returns the public portion of the key used to create the
85+
// ECDSASignerVerifier instance.
7186
func (sv *ECDSASignerVerifier) Public() crypto.PublicKey {
7287
return sv.public
7388
}
7489

90+
// LoadECDSAKeyFromFile returns an SSLibKey instance for an ECDSA key stored in
91+
// a file in the custom securesystemslib format.
7592
func LoadECDSAKeyFromFile(path string) (*SSLibKey, error) {
7693
contents, err := os.ReadFile(path)
7794
if err != nil {
78-
return nil, err
95+
return nil, fmt.Errorf("unable to load ECDSA key from file: %w", err)
7996
}
8097

8198
return loadKeyFromSSLibBytes(contents)

signerverifier/ed25519.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ import (
55
"crypto"
66
"crypto/ed25519"
77
"encoding/hex"
8+
"fmt"
89
"os"
910
)
1011

1112
const ED25519KeyType = "ed25519"
1213

14+
// ED25519SignerVerifier is a dsse.SignerVerifier compliant interface to sign
15+
// and verify signatures using ED25519 keys.
1316
type ED25519SignerVerifier struct {
1417
keyID string
1518
private ed25519.PrivateKey
@@ -19,16 +22,20 @@ type ED25519SignerVerifier struct {
1922
// NewED25519SignerVerifierFromSSLibKey creates an Ed25519SignerVerifier from an
2023
// SSLibKey.
2124
func NewED25519SignerVerifierFromSSLibKey(key *SSLibKey) (*ED25519SignerVerifier, error) {
25+
if len(key.KeyVal.Public) == 0 {
26+
return nil, ErrInvalidKey
27+
}
28+
2229
public, err := hex.DecodeString(key.KeyVal.Public)
2330
if err != nil {
24-
return nil, err
31+
return nil, fmt.Errorf("unable to create ED25519 signerverifier: %w", err)
2532
}
2633

2734
var private []byte
2835
if len(key.KeyVal.Private) > 0 {
2936
private, err = hex.DecodeString(key.KeyVal.Private)
3037
if err != nil {
31-
return nil, err
38+
return nil, fmt.Errorf("unable to create ED25519 signerverifier: %w", err)
3239
}
3340

3441
// python-securesystemslib provides an interface to generate ed25519
@@ -43,7 +50,7 @@ func NewED25519SignerVerifierFromSSLibKey(key *SSLibKey) (*ED25519SignerVerifier
4350
}
4451

4552
return &ED25519SignerVerifier{
46-
keyID: key.KeyID(),
53+
keyID: key.KeyID,
4754
public: ed25519.PublicKey(public),
4855
private: ed25519.PrivateKey(private),
4956
}, nil
@@ -79,10 +86,12 @@ func (sv *ED25519SignerVerifier) Public() crypto.PublicKey {
7986
return sv.public
8087
}
8188

89+
// LoadED25519KeyFromFile returns an SSLibKey instance for an ED25519 key stored
90+
// in a file in the custom securesystemslib format.
8291
func LoadED25519KeyFromFile(path string) (*SSLibKey, error) {
8392
contents, err := os.ReadFile(path)
8493
if err != nil {
85-
return nil, err
94+
return nil, fmt.Errorf("unable to load ED25519 key from file: %w", err)
8695
}
8796

8897
return loadKeyFromSSLibBytes(contents)

signerverifier/rsa.go

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,54 +7,57 @@ import (
77
"crypto/rsa"
88
"crypto/sha256"
99
"crypto/x509"
10-
"errors"
10+
"fmt"
1111
"os"
1212
)
1313

14-
// ErrNoPEMBlock gets triggered when there is no PEM block in the provided file
15-
var ErrNoPEMBlock = errors.New("failed to decode the data as PEM block (are you sure this is a pem file?)")
16-
17-
// ErrFailedPEMParsing gets returned when PKCS1, PKCS8 or PKIX key parsing fails
18-
var ErrFailedPEMParsing = errors.New("failed parsing the PEM block: unsupported PEM type")
19-
2014
const (
2115
RSAKeyType = "rsa"
2216
RSAKeyScheme = "rsassa-pss-sha256"
2317
RSAPrivateKeyPEM = "RSA PRIVATE KEY"
2418
)
2519

20+
// RSAPSSSignerVerifier is a dsse.SignerVerifier compliant interface to sign and
21+
// verify signatures using RSA keys following the RSA-PSS scheme.
2622
type RSAPSSSignerVerifier struct {
2723
keyID string
2824
private *rsa.PrivateKey
2925
public *rsa.PublicKey
3026
}
3127

28+
// NewRSAPSSSignerVerifierFromSSLibKey creates an RSAPSSSignerVerifier from an
29+
// SSLibKey.
3230
func NewRSAPSSSignerVerifierFromSSLibKey(key *SSLibKey) (*RSAPSSSignerVerifier, error) {
31+
if len(key.KeyVal.Public) == 0 {
32+
return nil, ErrInvalidKey
33+
}
34+
3335
_, publicParsedKey, err := decodeAndParsePEM([]byte(key.KeyVal.Public))
3436
if err != nil {
35-
return nil, err
37+
return nil, fmt.Errorf("unable to create RSA-PSS signerverifier: %w", err)
3638
}
3739

3840
if len(key.KeyVal.Private) > 0 {
3941
_, privateParsedKey, err := decodeAndParsePEM([]byte(key.KeyVal.Private))
4042
if err != nil {
41-
return nil, err
43+
return nil, fmt.Errorf("unable to create RSA-PSS signerverifier: %w", err)
4244
}
4345

4446
return &RSAPSSSignerVerifier{
45-
keyID: key.KeyID(),
47+
keyID: key.KeyID,
4648
public: publicParsedKey.(*rsa.PublicKey),
4749
private: privateParsedKey.(*rsa.PrivateKey),
4850
}, nil
4951
}
5052

5153
return &RSAPSSSignerVerifier{
52-
keyID: key.KeyID(),
54+
keyID: key.KeyID,
5355
public: publicParsedKey.(*rsa.PublicKey),
5456
private: nil,
5557
}, nil
5658
}
5759

60+
// Sign creates a signature for `data`.
5861
func (sv *RSAPSSSignerVerifier) Sign(ctx context.Context, data []byte) ([]byte, error) {
5962
if sv.private == nil {
6063
return nil, ErrNotPrivateKey
@@ -65,6 +68,7 @@ func (sv *RSAPSSSignerVerifier) Sign(ctx context.Context, data []byte) ([]byte,
6568
return rsa.SignPSS(rand.Reader, sv.private, crypto.SHA256, hashedData, &rsa.PSSOptions{SaltLength: sha256.Size, Hash: crypto.SHA256})
6669
}
6770

71+
// Verify verifies the `sig` value passed in against `data`.
6872
func (sv *RSAPSSSignerVerifier) Verify(ctx context.Context, data []byte, sig []byte) error {
6973
hashedData := hashBeforeSigning(data, sha256.New())
7074

@@ -75,23 +79,29 @@ func (sv *RSAPSSSignerVerifier) Verify(ctx context.Context, data []byte, sig []b
7579
return nil
7680
}
7781

82+
// KeyID returns the identifier of the key used to create the
83+
// RSAPSSSignerVerifier instance.
7884
func (sv *RSAPSSSignerVerifier) KeyID() (string, error) {
7985
return sv.keyID, nil
8086
}
8187

88+
// Public returns the public portion of the key used to create the
89+
// RSAPSSSignerVerifier instance.
8290
func (sv *RSAPSSSignerVerifier) Public() crypto.PublicKey {
8391
return sv.public
8492
}
8593

94+
// LoadRSAPSSKeyFromFile returns an SSLibKey instance for an RSA key stored in a
95+
// file.
8696
func LoadRSAPSSKeyFromFile(path string) (*SSLibKey, error) {
8797
contents, err := os.ReadFile(path)
8898
if err != nil {
89-
return nil, err
99+
return nil, fmt.Errorf("unable to load RSA PSS key from file: %w", err)
90100
}
91101

92102
pemData, keyObj, err := decodeAndParsePEM(contents)
93103
if err != nil {
94-
return nil, err
104+
return nil, fmt.Errorf("unable to load RSA PSS key from file: %w", err)
95105
}
96106

97107
key := &SSLibKey{
@@ -105,24 +115,26 @@ func LoadRSAPSSKeyFromFile(path string) (*SSLibKey, error) {
105115
case *rsa.PublicKey:
106116
pubKeyBytes, err := x509.MarshalPKIXPublicKey(k)
107117
if err != nil {
108-
return nil, err
118+
return nil, fmt.Errorf("unable to load RSA PSS key from file: %w", err)
109119
}
110120
key.KeyVal.Public = string(generatePEMBlock(pubKeyBytes, PublicKeyPEM))
111121

112122
case *rsa.PrivateKey:
113123
pubKeyBytes, err := x509.MarshalPKIXPublicKey(k.Public())
114124
if err != nil {
115-
return nil, err
125+
return nil, fmt.Errorf("unable to load RSA PSS key from file: %w", err)
116126
}
117127
key.KeyVal.Public = string(generatePEMBlock(pubKeyBytes, PublicKeyPEM))
118128
key.KeyVal.Private = string(generatePEMBlock(pemData.Bytes, RSAPrivateKeyPEM))
119129
}
120130

121-
keyID, err := calculateKeyID(key)
122-
if err != nil {
123-
return nil, err
131+
if len(key.KeyID) == 0 {
132+
keyID, err := calculateKeyID(key)
133+
if err != nil {
134+
return nil, fmt.Errorf("unable to load RSA PSS key from file: %w", err)
135+
}
136+
key.KeyID = keyID
124137
}
125-
key.keyID = keyID
126138

127139
return key, nil
128140
}

signerverifier/signerverifier.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ var (
1111
ErrSignatureVerificationFailed = errors.New("failed to verify signature")
1212
ErrUnknownKeyType = errors.New("unknown key type")
1313
ErrInvalidThreshold = errors.New("threshold is either less than 1 or greater than number of provided public keys")
14+
ErrInvalidKey = errors.New("key object has no value")
1415
)
1516

1617
const (
@@ -23,11 +24,7 @@ type SSLibKey struct {
2324
KeyType string `json:"keytype"`
2425
KeyVal KeyVal `json:"keyval"`
2526
Scheme string `json:"scheme"`
26-
keyID string
27-
}
28-
29-
func (k *SSLibKey) KeyID() string {
30-
return k.keyID
27+
KeyID string `json:"keyid"`
3128
}
3229

3330
type KeyVal struct {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"keytype": "ecdsa", "scheme": "ecdsa-sha2-nistp256", "keyid": "98adf38602c48c5479e9a991ee3f8cbf541ee4f985e00f7a5fc4148d9a45b704", "keyval": {"public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEu+HEqqpXLa48lXH9rkRygsfsCKq1\nXM36oXymJ9wxpM68nCqkrZCVnZ9lkEeCwD8qWYTNxD5yfWXwJjFh+K7qLQ==\n-----END PUBLIC KEY-----", "private": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIAo6DxXlgqYy+TkvocIOyWlqA3KVtp6dlSY7lS3kkeEMoAoGCCqGSM49\nAwEHoUQDQgAEu+HEqqpXLa48lXH9rkRygsfsCKq1XM36oXymJ9wxpM68nCqkrZCV\nnZ9lkEeCwD8qWYTNxD5yfWXwJjFh+K7qLQ==\n-----END EC PRIVATE KEY-----"}, "keyid_hash_algorithms": ["sha256", "sha512"]}
1+
{"keytype": "ecdsa", "scheme": "ecdsa-sha2-nistp256", "keyid": "98adf38602c48c5479e9a991ee3f8cbf541ee4f985e00f7a5fc4148d9a45b704", "keyval": {"public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEu+HEqqpXLa48lXH9rkRygsfsCKq1\nXM36oXymJ9wxpM68nCqkrZCVnZ9lkEeCwD8qWYTNxD5yfWXwJjFh+K7qLQ==\n-----END PUBLIC KEY-----", "private": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIAo6DxXlgqYy+TkvocIOyWlqA3KVtp6dlSY7lS3kkeEMoAoGCCqGSM49\nAwEHoUQDQgAEu+HEqqpXLa48lXH9rkRygsfsCKq1XM36oXymJ9wxpM68nCqkrZCV\nnZ9lkEeCwD8qWYTNxD5yfWXwJjFh+K7qLQ==\n-----END EC PRIVATE KEY-----"}, "keyid_hash_algorithms": ["sha256", "sha512"]}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"keytype": "ecdsa", "scheme": "ecdsa-sha2-nistp256", "keyid_hash_algorithms": ["sha256", "sha512"], "keyval": {"public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEu+HEqqpXLa48lXH9rkRygsfsCKq1\nXM36oXymJ9wxpM68nCqkrZCVnZ9lkEeCwD8qWYTNxD5yfWXwJjFh+K7qLQ==\n-----END PUBLIC KEY-----"}}
1+
{"keytype": "ecdsa", "scheme": "ecdsa-sha2-nistp256", "keyid_hash_algorithms": ["sha256", "sha512"], "keyval": {"public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEu+HEqqpXLa48lXH9rkRygsfsCKq1\nXM36oXymJ9wxpM68nCqkrZCVnZ9lkEeCwD8qWYTNxD5yfWXwJjFh+K7qLQ==\n-----END PUBLIC KEY-----"}}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"keytype": "ed25519", "scheme": "ed25519", "keyid": "52e3b8e73279d6ebdd62a5016e2725ff284f569665eb92ccb145d83817a02997", "keyid_hash_algorithms": ["sha256", "sha512"], "keyval": {"public": "3f586ce67329419fb0081bd995914e866a7205da463d593b3b490eab2b27fd3f", "private": "66f6ebad4aeb949b91c84c9cfd6ee351fc4fd544744bab6e30fb400ba13c6e9a"}}
1+
{"keytype": "ed25519", "scheme": "ed25519", "keyid": "52e3b8e73279d6ebdd62a5016e2725ff284f569665eb92ccb145d83817a02997", "keyid_hash_algorithms": ["sha256", "sha512"], "keyval": {"public": "3f586ce67329419fb0081bd995914e866a7205da463d593b3b490eab2b27fd3f", "private": "66f6ebad4aeb949b91c84c9cfd6ee351fc4fd544744bab6e30fb400ba13c6e9a"}}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"keytype": "ed25519", "scheme": "ed25519", "keyid_hash_algorithms": ["sha256", "sha512"], "keyval": {"public": "3f586ce67329419fb0081bd995914e866a7205da463d593b3b490eab2b27fd3f"}}
1+
{"keytype": "ed25519", "scheme": "ed25519", "keyid_hash_algorithms": ["sha256", "sha512"], "keyval": {"public": "3f586ce67329419fb0081bd995914e866a7205da463d593b3b490eab2b27fd3f"}}

signerverifier/test-data/rsa-test-key

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@ AhhasnrL3Pzxf0WKzKmj/y2yEP0Vctm0muqxFnFwPwyOAd6HODJOSiFPD5VN4jvC
3636
+Yw96Qn29kHGXTKgL1J9cSL8z6Qzlc+UYCdSwmaZK5r36+NBTJgvKY9KrpkXCkSa
3737
c5YgIYtXMitmq9NmNvcSJWmuuiept3HFlwkU3pfmwzKNEeqi2jmuIOqI2zCOqX67
3838
I+YQsJgrHE0TmYxxRkgeYUy7s5DoHE25rfvdy5Lx+xAOH8ZgD1SGOw==
39-
-----END RSA PRIVATE KEY-----
39+
-----END RSA PRIVATE KEY-----

signerverifier/test-data/rsa-test-key.pub

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ vZdaZUf3brD4ZZrxEtXw/tefhn1aHsSUajVW2wwjSpKhqj7Z0XS3bDS3T95/3xsN
88
VQSgMzSxC43/2fINb2fyt8SbUHJ3Ct+mzRzd/1AQikWhBdstJLxInewzjYE/sb+c
99
2CmCxMPQG2BwmAWXaaumeJcXVPBlMgAcjMatM8bPByTbXpKDnQslOE7g/gswDIwn
1010
Em53T13mZzYUvbLJ0q3aljZVLIC3IZn3ZwA2yCWchBkVAgMBAAE=
11-
-----END PUBLIC KEY-----
11+
-----END PUBLIC KEY-----

signerverifier/utils.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,23 @@ import (
66
"encoding/hex"
77
"encoding/json"
88
"encoding/pem"
9+
"errors"
910
"hash"
1011

1112
"github.com/secure-systems-lab/go-securesystemslib/cjson"
1213
)
1314

15+
/*
16+
Credits: Parts of this file were originally authored for in-toto-golang.
17+
*/
18+
19+
var (
20+
// ErrNoPEMBlock gets triggered when there is no PEM block in the provided file
21+
ErrNoPEMBlock = errors.New("failed to decode the data as PEM block (are you sure this is a pem file?)")
22+
// ErrFailedPEMParsing gets returned when PKCS1, PKCS8 or PKIX key parsing fails
23+
ErrFailedPEMParsing = errors.New("failed parsing the PEM block: unsupported PEM type")
24+
)
25+
1426
// loadKeyFromSSLibBytes returns a pointer to a Key instance created from the
1527
// contents of the bytes. The key contents are expected to be in the custom
1628
// securesystemslib format.
@@ -20,11 +32,13 @@ func loadKeyFromSSLibBytes(contents []byte) (*SSLibKey, error) {
2032
return nil, err
2133
}
2234

23-
keyID, err := calculateKeyID(key)
24-
if err != nil {
25-
return nil, err
35+
if len(key.KeyID) == 0 {
36+
keyID, err := calculateKeyID(key)
37+
if err != nil {
38+
return nil, err
39+
}
40+
key.KeyID = keyID
2641
}
27-
key.keyID = keyID
2842

2943
return key, nil
3044
}

0 commit comments

Comments
 (0)