Skip to content

Commit

Permalink
Multiple changes
Browse files Browse the repository at this point in the history
* 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 <aditya@saky.in>
  • Loading branch information
adityasaky committed May 1, 2023
1 parent 7d8d7bc commit 1c3e073
Show file tree
Hide file tree
Showing 11 changed files with 91 additions and 42 deletions.
25 changes: 21 additions & 4 deletions signerverifier/ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,35 @@ import (
"crypto/rand"
"crypto/sha256"
"crypto/sha512"
"fmt"
"os"
)

const ECDSAKeyType = "ecdsa"

// ECDSASignerVerifier is a dsse.SignerVerifier compliant interface to sign and
// verify signatures using ECDSA keys.
type ECDSASignerVerifier struct {
keyID string
curveSize int
private *ecdsa.PrivateKey
public *ecdsa.PublicKey
}

// NewECDSASignerVerifierFromSSLibKey creates an ECDSASignerVerifier from an
// SSLibKey.
func NewECDSASignerVerifierFromSSLibKey(key *SSLibKey) (*ECDSASignerVerifier, error) {
if len(key.KeyVal.Public) == 0 {
return nil, ErrInvalidKey
}

_, publicParsedKey, err := decodeAndParsePEM([]byte(key.KeyVal.Public))
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to create ECDSA signerverifier: %w", err)
}

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

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

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

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

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

// KeyID returns the identifier of the key used to create the
// ECDSASignerVerifier instance.
func (sv *ECDSASignerVerifier) KeyID() (string, error) {
return sv.keyID, nil
}

// Public returns the public portion of the key used to create the
// ECDSASignerVerifier instance.
func (sv *ECDSASignerVerifier) Public() crypto.PublicKey {
return sv.public
}

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

return loadKeyFromSSLibBytes(contents)
Expand Down
17 changes: 13 additions & 4 deletions signerverifier/ed25519.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ import (
"crypto"
"crypto/ed25519"
"encoding/hex"
"fmt"
"os"
)

const ED25519KeyType = "ed25519"

// ED25519SignerVerifier is a dsse.SignerVerifier compliant interface to sign
// and verify signatures using ED25519 keys.
type ED25519SignerVerifier struct {
keyID string
private ed25519.PrivateKey
Expand All @@ -19,16 +22,20 @@ type ED25519SignerVerifier struct {
// NewED25519SignerVerifierFromSSLibKey creates an Ed25519SignerVerifier from an
// SSLibKey.
func NewED25519SignerVerifierFromSSLibKey(key *SSLibKey) (*ED25519SignerVerifier, error) {
if len(key.KeyVal.Public) == 0 {
return nil, ErrInvalidKey
}

public, err := hex.DecodeString(key.KeyVal.Public)
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to create ED25519 signerverifier: %w", err)
}

var private []byte
if len(key.KeyVal.Private) > 0 {
private, err = hex.DecodeString(key.KeyVal.Private)
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to create ED25519 signerverifier: %w", err)
}

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

return &ED25519SignerVerifier{
keyID: key.KeyID(),
keyID: key.KeyID,
public: ed25519.PublicKey(public),
private: ed25519.PrivateKey(private),
}, nil
Expand Down Expand Up @@ -79,10 +86,12 @@ func (sv *ED25519SignerVerifier) Public() crypto.PublicKey {
return sv.public
}

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

return loadKeyFromSSLibBytes(contents)
Expand Down
50 changes: 31 additions & 19 deletions signerverifier/rsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,54 +7,57 @@ import (
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"errors"
"fmt"
"os"
)

// ErrNoPEMBlock gets triggered when there is no PEM block in the provided file
var ErrNoPEMBlock = errors.New("failed to decode the data as PEM block (are you sure this is a pem file?)")

// ErrFailedPEMParsing gets returned when PKCS1, PKCS8 or PKIX key parsing fails
var ErrFailedPEMParsing = errors.New("failed parsing the PEM block: unsupported PEM type")

const (
RSAKeyType = "rsa"
RSAKeyScheme = "rsassa-pss-sha256"
RSAPrivateKeyPEM = "RSA PRIVATE KEY"
)

// RSAPSSSignerVerifier is a dsse.SignerVerifier compliant interface to sign and
// verify signatures using RSA keys following the RSA-PSS scheme.
type RSAPSSSignerVerifier struct {
keyID string
private *rsa.PrivateKey
public *rsa.PublicKey
}

// NewRSAPSSSignerVerifierFromSSLibKey creates an RSAPSSSignerVerifier from an
// SSLibKey.
func NewRSAPSSSignerVerifierFromSSLibKey(key *SSLibKey) (*RSAPSSSignerVerifier, error) {
if len(key.KeyVal.Public) == 0 {
return nil, ErrInvalidKey
}

_, publicParsedKey, err := decodeAndParsePEM([]byte(key.KeyVal.Public))
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to create RSA-PSS signerverifier: %w", err)
}

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

return &RSAPSSSignerVerifier{
keyID: key.KeyID(),
keyID: key.KeyID,
public: publicParsedKey.(*rsa.PublicKey),
private: privateParsedKey.(*rsa.PrivateKey),
}, nil
}

return &RSAPSSSignerVerifier{
keyID: key.KeyID(),
keyID: key.KeyID,
public: publicParsedKey.(*rsa.PublicKey),
private: nil,
}, nil
}

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

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

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

// KeyID returns the identifier of the key used to create the
// RSAPSSSignerVerifier instance.
func (sv *RSAPSSSignerVerifier) KeyID() (string, error) {
return sv.keyID, nil
}

// Public returns the public portion of the key used to create the
// RSAPSSSignerVerifier instance.
func (sv *RSAPSSSignerVerifier) Public() crypto.PublicKey {
return sv.public
}

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

pemData, keyObj, err := decodeAndParsePEM(contents)
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to load RSA PSS key from file: %w", err)
}

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

case *rsa.PrivateKey:
pubKeyBytes, err := x509.MarshalPKIXPublicKey(k.Public())
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to load RSA PSS key from file: %w", err)
}
key.KeyVal.Public = string(generatePEMBlock(pubKeyBytes, PublicKeyPEM))
key.KeyVal.Private = string(generatePEMBlock(pemData.Bytes, RSAPrivateKeyPEM))
}

keyID, err := calculateKeyID(key)
if err != nil {
return nil, err
if len(key.KeyID) == 0 {
keyID, err := calculateKeyID(key)
if err != nil {
return nil, fmt.Errorf("unable to load RSA PSS key from file: %w", err)
}
key.KeyID = keyID
}
key.keyID = keyID

return key, nil
}
7 changes: 2 additions & 5 deletions signerverifier/signerverifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var (
ErrSignatureVerificationFailed = errors.New("failed to verify signature")
ErrUnknownKeyType = errors.New("unknown key type")
ErrInvalidThreshold = errors.New("threshold is either less than 1 or greater than number of provided public keys")
ErrInvalidKey = errors.New("key object has no value")
)

const (
Expand All @@ -23,11 +24,7 @@ type SSLibKey struct {
KeyType string `json:"keytype"`
KeyVal KeyVal `json:"keyval"`
Scheme string `json:"scheme"`
keyID string
}

func (k *SSLibKey) KeyID() string {
return k.keyID
KeyID string `json:"keyid"`
}

type KeyVal struct {
Expand Down
2 changes: 1 addition & 1 deletion signerverifier/test-data/ecdsa-test-key
Original file line number Diff line number Diff line change
@@ -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"]}
{"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"]}
2 changes: 1 addition & 1 deletion signerverifier/test-data/ecdsa-test-key.pub
Original file line number Diff line number Diff line change
@@ -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-----"}}
{"keytype": "ecdsa", "scheme": "ecdsa-sha2-nistp256", "keyid_hash_algorithms": ["sha256", "sha512"], "keyval": {"public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEu+HEqqpXLa48lXH9rkRygsfsCKq1\nXM36oXymJ9wxpM68nCqkrZCVnZ9lkEeCwD8qWYTNxD5yfWXwJjFh+K7qLQ==\n-----END PUBLIC KEY-----"}}
2 changes: 1 addition & 1 deletion signerverifier/test-data/ed25519-test-key
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"keytype": "ed25519", "scheme": "ed25519", "keyid": "52e3b8e73279d6ebdd62a5016e2725ff284f569665eb92ccb145d83817a02997", "keyid_hash_algorithms": ["sha256", "sha512"], "keyval": {"public": "3f586ce67329419fb0081bd995914e866a7205da463d593b3b490eab2b27fd3f", "private": "66f6ebad4aeb949b91c84c9cfd6ee351fc4fd544744bab6e30fb400ba13c6e9a"}}
{"keytype": "ed25519", "scheme": "ed25519", "keyid": "52e3b8e73279d6ebdd62a5016e2725ff284f569665eb92ccb145d83817a02997", "keyid_hash_algorithms": ["sha256", "sha512"], "keyval": {"public": "3f586ce67329419fb0081bd995914e866a7205da463d593b3b490eab2b27fd3f", "private": "66f6ebad4aeb949b91c84c9cfd6ee351fc4fd544744bab6e30fb400ba13c6e9a"}}
2 changes: 1 addition & 1 deletion signerverifier/test-data/ed25519-test-key.pub
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"keytype": "ed25519", "scheme": "ed25519", "keyid_hash_algorithms": ["sha256", "sha512"], "keyval": {"public": "3f586ce67329419fb0081bd995914e866a7205da463d593b3b490eab2b27fd3f"}}
{"keytype": "ed25519", "scheme": "ed25519", "keyid_hash_algorithms": ["sha256", "sha512"], "keyval": {"public": "3f586ce67329419fb0081bd995914e866a7205da463d593b3b490eab2b27fd3f"}}
2 changes: 1 addition & 1 deletion signerverifier/test-data/rsa-test-key
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ AhhasnrL3Pzxf0WKzKmj/y2yEP0Vctm0muqxFnFwPwyOAd6HODJOSiFPD5VN4jvC
+Yw96Qn29kHGXTKgL1J9cSL8z6Qzlc+UYCdSwmaZK5r36+NBTJgvKY9KrpkXCkSa
c5YgIYtXMitmq9NmNvcSJWmuuiept3HFlwkU3pfmwzKNEeqi2jmuIOqI2zCOqX67
I+YQsJgrHE0TmYxxRkgeYUy7s5DoHE25rfvdy5Lx+xAOH8ZgD1SGOw==
-----END RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----
2 changes: 1 addition & 1 deletion signerverifier/test-data/rsa-test-key.pub
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ vZdaZUf3brD4ZZrxEtXw/tefhn1aHsSUajVW2wwjSpKhqj7Z0XS3bDS3T95/3xsN
VQSgMzSxC43/2fINb2fyt8SbUHJ3Ct+mzRzd/1AQikWhBdstJLxInewzjYE/sb+c
2CmCxMPQG2BwmAWXaaumeJcXVPBlMgAcjMatM8bPByTbXpKDnQslOE7g/gswDIwn
Em53T13mZzYUvbLJ0q3aljZVLIC3IZn3ZwA2yCWchBkVAgMBAAE=
-----END PUBLIC KEY-----
-----END PUBLIC KEY-----
22 changes: 18 additions & 4 deletions signerverifier/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,23 @@ import (
"encoding/hex"
"encoding/json"
"encoding/pem"
"errors"
"hash"

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

/*
Credits: Parts of this file were originally authored for in-toto-golang.
*/

var (
// ErrNoPEMBlock gets triggered when there is no PEM block in the provided file
ErrNoPEMBlock = errors.New("failed to decode the data as PEM block (are you sure this is a pem file?)")
// ErrFailedPEMParsing gets returned when PKCS1, PKCS8 or PKIX key parsing fails
ErrFailedPEMParsing = errors.New("failed parsing the PEM block: unsupported PEM type")
)

// loadKeyFromSSLibBytes returns a pointer to a Key instance created from the
// contents of the bytes. The key contents are expected to be in the custom
// securesystemslib format.
Expand All @@ -20,11 +32,13 @@ func loadKeyFromSSLibBytes(contents []byte) (*SSLibKey, error) {
return nil, err
}

keyID, err := calculateKeyID(key)
if err != nil {
return nil, err
if len(key.KeyID) == 0 {
keyID, err := calculateKeyID(key)
if err != nil {
return nil, err
}
key.KeyID = keyID
}
key.keyID = keyID

return key, nil
}
Expand Down

0 comments on commit 1c3e073

Please sign in to comment.