Skip to content

Commit

Permalink
Support PEM encoding for all key types
Browse files Browse the repository at this point in the history
This change matches python-securesystemslib by retiring the custom
serialization format. With this change, RSA, ED25519, and ECDSA keys can
be loaded from standard PEM encoding, meaning custom tooling isn't
needed to generate the keys. This commit adds deprecation warnings to
prior Load methods that expected the custom format.

Signed-off-by: Aditya Sirish <aditya@saky.in>
  • Loading branch information
adityasaky committed Jan 2, 2024
1 parent 7e48227 commit 1ca9210
Show file tree
Hide file tree
Showing 13 changed files with 439 additions and 7 deletions.
10 changes: 9 additions & 1 deletion signerverifier/ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import (
"os"
)

const ECDSAKeyType = "ecdsa"
const (
ECDSAKeyType = "ecdsa"
ECDSAKeyScheme = "ecdsa-sha2-nistp256"
)

// ECDSASignerVerifier is a dsse.SignerVerifier compliant interface to sign and
// verify signatures using ECDSA keys.
Expand Down Expand Up @@ -89,6 +92,11 @@ func (sv *ECDSASignerVerifier) Public() crypto.PublicKey {

// LoadECDSAKeyFromFile returns an SSLibKey instance for an ECDSA key stored in
// a file in the custom securesystemslib format.
//
// Deprecated: use LoadKey(). The custom serialization format has been
// deprecated. Use
// https://github.com/secure-systems-lab/securesystemslib/blob/main/docs/migrate_key.py
// to convert your key.
func LoadECDSAKeyFromFile(path string) (*SSLibKey, error) {
contents, err := os.ReadFile(path)
if err != nil {
Expand Down
91 changes: 91 additions & 0 deletions signerverifier/ecdsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,55 @@ func TestECDSASignerVerifierWithDSSEEnvelope(t *testing.T) {
assert.Equal(t, "98adf38602c48c5479e9a991ee3f8cbf541ee4f985e00f7a5fc4148d9a45b704", acceptedKeys[0].KeyID)
}

func TestECDSASignerVerifierWithDSSEEnvelopeAndPEMKey(t *testing.T) {
key, err := LoadKey(ecdsaPrivateKey)
if err != nil {
t.Fatal(err)
}

sv, err := NewECDSASignerVerifierFromSSLibKey(key)
if err != nil {
t.Fatal(err)
}

payloadType := "application/vnd.dsse+json"
payload := []byte("test message")

es, err := dsse.NewEnvelopeSigner(sv)
if err != nil {
t.Error(err)
}

env, err := es.SignPayload(context.Background(), payloadType, payload)
if err != nil {
t.Error(err)
}

assert.Equal(t, "98adf38602c48c5479e9a991ee3f8cbf541ee4f985e00f7a5fc4148d9a45b704", env.Signatures[0].KeyID)
envPayload, err := env.DecodeB64Payload()
assert.Equal(t, payload, envPayload)
assert.Nil(t, err)

key, err = LoadKey(ecdsaPublicKey)
if err != nil {
t.Fatal(err)
}

sv, err = NewECDSASignerVerifierFromSSLibKey(key)
if err != nil {
t.Fatal(err)
}

ev, err := dsse.NewEnvelopeVerifier(sv)
if err != nil {
t.Error(err)
}

acceptedKeys, err := ev.Verify(context.Background(), env)
assert.Nil(t, err)
assert.Equal(t, "98adf38602c48c5479e9a991ee3f8cbf541ee4f985e00f7a5fc4148d9a45b704", acceptedKeys[0].KeyID)
}

func TestECDSASignerVerifierWithMetablockFile(t *testing.T) {
key, err := LoadECDSAKeyFromFile(filepath.Join("test-data", "ecdsa-test-key.pub"))
if err != nil {
Expand Down Expand Up @@ -189,3 +238,45 @@ func TestECDSASignerVerifierWithMetablockFile(t *testing.T) {
err = sv.Verify(context.Background(), encodedBytes, decodedSig)
assert.Nil(t, err)
}

func TestECDSASignerVerifierWithMetablockFileAndPEMKey(t *testing.T) {
key, err := LoadKey(ecdsaPublicKey)
if err != nil {
t.Fatal(err)
}

sv, err := NewECDSASignerVerifierFromSSLibKey(key)
if err != nil {
t.Fatal(err)
}

metadataBytes, err := os.ReadFile(filepath.Join("test-data", "test-ecdsa.98adf386.link"))
if err != nil {
t.Fatal(err)
}

mb := struct {
Signatures []struct {
KeyID string `json:"keyid"`
Sig string `json:"sig"`
} `json:"signatures"`
Signed any `json:"signed"`
}{}

if err := json.Unmarshal(metadataBytes, &mb); err != nil {
t.Fatal(err)
}

assert.Equal(t, "304502201fbb03c0937504182a48c66f9218bdcb2e99a07ada273e92e5e543867f98c8d7022100dbfa7bbf74fd76d76c1d08676419cba85bbd81dfb000f3ac6a786693ddc508f5", mb.Signatures[0].Sig)
assert.Equal(t, sv.keyID, mb.Signatures[0].KeyID)

encodedBytes, err := cjson.EncodeCanonical(mb.Signed)
if err != nil {
t.Fatal(err)
}

decodedSig := hexDecode(t, mb.Signatures[0].Sig)

err = sv.Verify(context.Background(), encodedBytes, decodedSig)
assert.Nil(t, err)
}
5 changes: 5 additions & 0 deletions signerverifier/ed25519.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ func (sv *ED25519SignerVerifier) Public() crypto.PublicKey {

// LoadED25519KeyFromFile returns an SSLibKey instance for an ED25519 key stored
// in a file in the custom securesystemslib format.
//
// Deprecated: use LoadKey(). The custom serialization format has been
// deprecated. Use
// https://github.com/secure-systems-lab/securesystemslib/blob/main/docs/migrate_key.py
// to convert your key.
func LoadED25519KeyFromFile(path string) (*SSLibKey, error) {
contents, err := os.ReadFile(path)
if err != nil {
Expand Down
91 changes: 91 additions & 0 deletions signerverifier/ed25519_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,55 @@ func TestED25519SignerVerifierWithDSSEEnvelope(t *testing.T) {
assert.Equal(t, "52e3b8e73279d6ebdd62a5016e2725ff284f569665eb92ccb145d83817a02997", acceptedKeys[0].KeyID)
}

func TestED25519SignerVerifierWithDSSEEnvelopeAndPEMKey(t *testing.T) {
key, err := LoadKey(ed25519PrivateKey)
if err != nil {
t.Fatal(err)
}

sv, err := NewED25519SignerVerifierFromSSLibKey(key)
if err != nil {
t.Fatal(err)
}

payloadType := "application/vnd.dsse+json"
payload := []byte("test message")

es, err := dsse.NewEnvelopeSigner(sv)
if err != nil {
t.Error(err)
}

env, err := es.SignPayload(context.Background(), payloadType, payload)
if err != nil {
t.Error(err)
}

assert.Equal(t, "52e3b8e73279d6ebdd62a5016e2725ff284f569665eb92ccb145d83817a02997", env.Signatures[0].KeyID)
envPayload, err := env.DecodeB64Payload()
assert.Equal(t, payload, envPayload)
assert.Nil(t, err)

key, err = LoadKey(ed25519PublicKey)
if err != nil {
t.Fatal(err)
}

sv, err = NewED25519SignerVerifierFromSSLibKey(key)
if err != nil {
t.Fatal(err)
}

ev, err := dsse.NewEnvelopeVerifier(sv)
if err != nil {
t.Error(err)
}

acceptedKeys, err := ev.Verify(context.Background(), env)
assert.Nil(t, err)
assert.Equal(t, "52e3b8e73279d6ebdd62a5016e2725ff284f569665eb92ccb145d83817a02997", acceptedKeys[0].KeyID)
}

func TestED25519SignerVerifierWithMetablockFile(t *testing.T) {
key, err := LoadED25519KeyFromFile(filepath.Join("test-data", "ed25519-test-key.pub"))
if err != nil {
Expand Down Expand Up @@ -205,3 +254,45 @@ func TestED25519SignerVerifierWithMetablockFile(t *testing.T) {
err = sv.Verify(context.Background(), encodedBytes, decodedSig)
assert.Nil(t, err)
}

func TestED25519SignerVerifierWithMetablockFileAndPEMKey(t *testing.T) {
key, err := LoadKey(ed25519PublicKey)
if err != nil {
t.Fatal(err)
}

sv, err := NewED25519SignerVerifierFromSSLibKey(key)
if err != nil {
t.Fatal(err)
}

metadataBytes, err := os.ReadFile(filepath.Join("test-data", "test-ed25519.52e3b8e7.link"))
if err != nil {
t.Fatal(err)
}

mb := struct {
Signatures []struct {
KeyID string `json:"keyid"`
Sig string `json:"sig"`
} `json:"signatures"`
Signed any `json:"signed"`
}{}

if err := json.Unmarshal(metadataBytes, &mb); err != nil {
t.Fatal(err)
}

assert.Equal(t, "4c8b7605a9195d4ddba54493bbb5257a9836c1d16056a027fd77e97b95a4f3e36f8bc3c9c9960387d68187760b3072a30c44f992c5bf8f7497c303a3b0a32403", mb.Signatures[0].Sig)
assert.Equal(t, sv.keyID, mb.Signatures[0].KeyID)

encodedBytes, err := cjson.EncodeCanonical(mb.Signed)
if err != nil {
t.Fatal(err)
}

decodedSig := hexDecode(t, mb.Signatures[0].Sig)

err = sv.Verify(context.Background(), encodedBytes, decodedSig)
assert.Nil(t, err)
}
15 changes: 12 additions & 3 deletions signerverifier/rsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ func (sv *RSAPSSSignerVerifier) Public() crypto.PublicKey {

// LoadRSAPSSKeyFromFile returns an SSLibKey instance for an RSA key stored in a
// file.
//
// Deprecated: use LoadKey(). The custom serialization format has been
// deprecated. Use
// https://github.com/secure-systems-lab/securesystemslib/blob/main/docs/migrate_key.py
// to convert your key.
func LoadRSAPSSKeyFromFile(path string) (*SSLibKey, error) {
contents, err := os.ReadFile(path)
if err != nil {
Expand All @@ -103,9 +108,13 @@ func LoadRSAPSSKeyFromFile(path string) (*SSLibKey, error) {
return LoadRSAPSSKeyFromBytes(contents)
}

// LoadRSAPSSKeyFromBytes is a function that takes a byte array as input. This byte array should represent a PEM encoded RSA key, as PEM encoding is required.
// The function returns an SSLibKey instance, which is a struct that holds the key data.

// LoadRSAPSSKeyFromBytes is a function that takes a byte array as input. This
// byte array should represent a PEM encoded RSA key, as PEM encoding is
// required. The function returns an SSLibKey instance, which is a struct that
// holds the key data.
//
// Deprecated: use LoadKey() for all key types, RSA is no longer the only key
// that uses PEM serialization.
func LoadRSAPSSKeyFromBytes(contents []byte) (*SSLibKey, error) {
pemData, keyObj, err := decodeAndParsePEM(contents)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions signerverifier/rsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func TestRSAPSSSignerVerifierSignAndVerify(t *testing.T) {
}

func TestRSAPSSSignerVerifierWithDSSEEnvelope(t *testing.T) {
key, err := LoadRSAPSSKeyFromFile(filepath.Join("test-data", "rsa-test-key"))
key, err := LoadKey(rsaPrivateKey)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -130,7 +130,7 @@ func TestRSAPSSSignerVerifierWithDSSEEnvelope(t *testing.T) {
assert.Equal(t, payload, envPayload)
assert.Nil(t, err)

key, err = LoadRSAPSSKeyFromFile(filepath.Join("test-data", "rsa-test-key.pub"))
key, err = LoadKey(rsaPublicKey)
if err != nil {
t.Fatal(err)
}
Expand All @@ -151,7 +151,7 @@ func TestRSAPSSSignerVerifierWithDSSEEnvelope(t *testing.T) {
}

func TestRSAPSSSignerVerifierWithMetablockFile(t *testing.T) {
key, err := LoadRSAPSSKeyFromFile(filepath.Join("test-data", "rsa-test-key.pub"))
key, err := LoadKey(rsaPublicKey)
if err != nil {
t.Fatal(err)
}
Expand Down
Loading

0 comments on commit 1ca9210

Please sign in to comment.