Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change values of Identity.Raw, add fingerprints #1628

Merged
merged 2 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions pkg/pki/identity/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,24 @@
package identity

type Identity struct {
// Types include: *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey,
// *x509.Certificate, openpgp.EntityList, *minisign.PublicKey, ssh.PublicKey
// Types include:
// - *rsa.PublicKey
// - *ecdsa.PublicKey
// - ed25519.PublicKey
// - *x509.Certificate
// - openpgp.EntityList (golang.org/x/crypto/openpgp)
// - *minisign.PublicKey (github.com/jedisct1/go-minisign)
// - ssh.PublicKey (golang.org/x/crypto/ssh)
Crypto any
// Based on type of Crypto. Possible values include: PEM-encoded public key,
// PEM-encoded certificate, canonicalized PGP public key, encoded Minisign
// public key, encoded SSH public key
// Raw key or certificate extracted from Crypto. Values include:
// - PKIX ASN.1 DER-encoded public key
// - ASN.1 DER-encoded certificate
Raw []byte
// For keys, certificates, and minisign, hex-encoded SHA-256 digest
// of the public key or certificate
// For SSH and PGP, Fingerprint is the standard for each ecosystem
// For SSH, unpadded base-64 encoded SHA-256 digest of the key
// For PGP, hex-encoded SHA-1 digest of a key, which can be either
// a primary key or subkey
Fingerprint string
}
15 changes: 12 additions & 3 deletions pkg/pki/minisign/minisign.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ package minisign

import (
"bytes"
"crypto/ed25519"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
"io"
"strings"

minisign "github.com/jedisct1/go-minisign"
"github.com/sigstore/rekor/pkg/pki/identity"
"github.com/sigstore/sigstore/pkg/cryptoutils"
sigsig "github.com/sigstore/sigstore/pkg/signature"
"golang.org/x/crypto/blake2b"
)
Expand Down Expand Up @@ -186,10 +190,15 @@ func (k PublicKey) Subjects() []string {

// Identities implements the pki.PublicKey interface
func (k PublicKey) Identities() ([]identity.Identity, error) {
// returns base64-encoded key (sig alg, key ID, and public key)
key, err := k.CanonicalValue()
// PKIX encode ed25519 public key
pkixKey, err := cryptoutils.MarshalPublicKeyToDER(ed25519.PublicKey(k.key.PublicKey[:]))
if err != nil {
return nil, err
}
return []identity.Identity{{Crypto: k.key, Raw: key}}, nil
digest := sha256.Sum256(pkixKey)
return []identity.Identity{{
Crypto: k.key,
Raw: pkixKey,
Fingerprint: hex.EncodeToString(digest[:]),
}}, nil
}
44 changes: 35 additions & 9 deletions pkg/pki/minisign/minisign_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//
// Copyright 2021 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -17,6 +16,10 @@ package minisign

import (
"bytes"
"crypto/ed25519"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"errors"
"io"
"os"
Expand All @@ -25,6 +28,7 @@ import (

"github.com/google/go-cmp/cmp"
minisign "github.com/jedisct1/go-minisign"
"github.com/sigstore/sigstore/pkg/cryptoutils"
"go.uber.org/goleak"
)

Expand Down Expand Up @@ -72,12 +76,23 @@ func TestReadPublicKey(t *testing.T) {
if _, ok := ids[0].Crypto.(*minisign.PublicKey); !ok {
t.Fatalf("key is of unexpected type, expected *minisign.PublicKey, got %v", reflect.TypeOf(ids[0].Crypto))
}
val, err := rawGot.CanonicalValue()
expectedDer, err := cryptoutils.MarshalPublicKeyToDER(ed25519.PublicKey(rawBytes))
if err != nil {
t.Fatalf("error canonicalizing key: %v", err)
t.Fatalf("unexpected error generating DER: %v", err)
}
if !reflect.DeepEqual(expectedDer, ids[0].Raw) {
t.Errorf("raw keys are not equal")
}
edKey, _ := x509.ParsePKIXPublicKey(ids[0].Raw)
if err := cryptoutils.EqualKeys(edKey, ed25519.PublicKey(rawBytes)); err != nil {
t.Errorf("public keys did not match: %v", err)
}
if len(ids[0].Fingerprint) != 64 {
t.Errorf("%v: fingerprint is not expected length of 64 (hex 32-byte sha256): %d", tc.caseDesc, len(ids[0].Fingerprint))
}
if !reflect.DeepEqual(val, ids[0].Raw) {
t.Errorf("raw key and canonical value are not equal")
digest := sha256.Sum256(expectedDer)
if hex.EncodeToString(digest[:]) != ids[0].Fingerprint {
t.Fatalf("fingerprints don't match")
}
})
}
Expand Down Expand Up @@ -313,12 +328,23 @@ func TestCanonicalValuePublicKey(t *testing.T) {
if _, ok := ids[0].Crypto.(*minisign.PublicKey); !ok {
t.Fatalf("key is of unexpected type, expected *minisign.PublicKey, got %v", reflect.TypeOf(ids[0].Crypto))
}
val, err := outputKey.CanonicalValue()
expectedDer, err := cryptoutils.MarshalPublicKeyToDER(ed25519.PublicKey(rt.key.PublicKey[:]))
if err != nil {
t.Fatalf("error canonicalizing key: %v", err)
t.Fatalf("unexpected error generating DER: %v", err)
}
if !reflect.DeepEqual(expectedDer, ids[0].Raw) {
t.Errorf("raw keys are not equal")
}
edKey, _ := x509.ParsePKIXPublicKey(ids[0].Raw)
if err := cryptoutils.EqualKeys(edKey, ed25519.PublicKey(rt.key.PublicKey[:])); err != nil {
t.Errorf("public keys did not match: %v", err)
}
if len(ids[0].Fingerprint) != 64 {
t.Errorf("%v: fingerprint is not expected length of 64 (hex 32-byte sha256): %d", tc.caseDesc, len(ids[0].Fingerprint))
}
if !reflect.DeepEqual(val, ids[0].Raw) {
t.Errorf("raw key and canonical value are not equal")
digest := sha256.Sum256(expectedDer)
if hex.EncodeToString(digest[:]) != ids[0].Fingerprint {
t.Fatalf("fingerprints don't match")
}
}
}
Expand Down
37 changes: 33 additions & 4 deletions pkg/pki/pgp/pgp.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import (
"bufio"
"bytes"
"context"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"encoding/hex"
"errors"
"fmt"
"io"
Expand All @@ -32,6 +36,7 @@ import (
"golang.org/x/crypto/openpgp/packet" //nolint:staticcheck

"github.com/sigstore/rekor/pkg/pki/identity"
"github.com/sigstore/sigstore/pkg/cryptoutils"
sigsig "github.com/sigstore/sigstore/pkg/signature"
)

Expand Down Expand Up @@ -307,9 +312,33 @@ func (k PublicKey) Subjects() []string {

// Identities implements the pki.PublicKey interface
func (k PublicKey) Identities() ([]identity.Identity, error) {
key, err := k.CanonicalValue()
if err != nil {
return nil, err
var ids []identity.Identity
for _, entity := range k.key {
var keys []*packet.PublicKey
keys = append(keys, entity.PrimaryKey)
for _, subKey := range entity.Subkeys {
keys = append(keys, subKey.PublicKey)
}
for _, pk := range keys {
pubKey := pk.PublicKey
// Only process supported types. Will ignore DSA
// and ElGamal keys.
// TODO: For a V2 PGP type, enforce on upload
switch pubKey.(type) {
case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
default:
continue
}
pkixKey, err := cryptoutils.MarshalPublicKeyToDER(pubKey)
if err != nil {
return nil, err
}
ids = append(ids, identity.Identity{
Crypto: pubKey,
Raw: pkixKey,
Fingerprint: hex.EncodeToString(pk.Fingerprint[:]),
})
}
}
return []identity.Identity{{Crypto: k.key, Raw: key}}, nil
return ids, nil
}
20 changes: 10 additions & 10 deletions pkg/pki/pgp/pgp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
"sort"
"testing"

"github.com/sigstore/rekor/pkg/pki/identity"
"go.uber.org/goleak"
)

Expand Down Expand Up @@ -348,17 +347,20 @@ func TestEmailAddresses(t *testing.T) {
caseDesc string
inputFile string
subjects []string
// number of keys in key ring
// verified with gpg, ignoring DSA/ElGamal keys
keys int
}

var k PublicKey
if len(k.Subjects()) != 0 {
t.Errorf("Subjects for unitialized key should give empty slice")
}
tests := []test{
{caseDesc: "Valid armored public key", inputFile: "testdata/valid_armored_public.pgp", subjects: []string{}},
{caseDesc: "Valid armored public key with multiple subentries", inputFile: "testdata/valid_armored_complex_public.pgp", subjects: []string{"[email protected]", "[email protected]"}},
{caseDesc: "Valid binary public key", inputFile: "testdata/valid_binary_public.pgp", subjects: []string{}},
{caseDesc: "Valid binary public key with multiple subentries", inputFile: "testdata/valid_binary_complex_public.pgp", subjects: []string{"[email protected]", "[email protected]"}},
{caseDesc: "Valid armored public key", inputFile: "testdata/valid_armored_public.pgp", subjects: []string{}, keys: 2},
{caseDesc: "Valid armored public key with multiple subentries", inputFile: "testdata/valid_armored_complex_public.pgp", subjects: []string{"[email protected]", "[email protected]"}, keys: 4},
{caseDesc: "Valid binary public key", inputFile: "testdata/valid_binary_public.pgp", subjects: []string{}, keys: 2},
{caseDesc: "Valid binary public key with multiple subentries", inputFile: "testdata/valid_binary_complex_public.pgp", subjects: []string{"[email protected]", "[email protected]"}, keys: 4},
}

for _, tc := range tests {
Expand Down Expand Up @@ -388,14 +390,12 @@ func TestEmailAddresses(t *testing.T) {
t.Errorf("%v: Error getting subjects from keys length, got %v, expected %v", tc.caseDesc, len(subjects), len(tc.subjects))
}

keyVal, _ := inputKey.CanonicalValue()
expectedIDs := []identity.Identity{{Crypto: inputKey.key, Raw: keyVal}}
ids, err := inputKey.Identities()
if err != nil {
t.Fatalf("unexpected error getting identities: %v", err)
t.Fatalf("%v: unexpected error getting identities: %v", tc.caseDesc, err)
}
if !reflect.DeepEqual(ids, expectedIDs) {
t.Errorf("identities are not equal")
if len(ids) != tc.keys {
t.Fatalf("%v: expected %d keys, got %d", tc.caseDesc, tc.keys, len(ids))
}
}
}
Expand Down
22 changes: 14 additions & 8 deletions pkg/pki/pkcs7/pkcs7.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ package pkcs7
import (
"bytes"
"crypto"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
Expand Down Expand Up @@ -227,16 +229,20 @@ func (k PublicKey) Subjects() []string {
// Identities implements the pki.PublicKey interface
func (k PublicKey) Identities() ([]identity.Identity, error) {
// pkcs7 structure may contain both a key and certificate chain
pemCert, err := cryptoutils.MarshalCertificateToPEM(k.certs[0])
pkixKey, err := cryptoutils.MarshalPublicKeyToDER(k.key)
if err != nil {
return nil, err
}
pemKey, err := cryptoutils.MarshalPublicKeyToPEM(k.key)
if err != nil {
return nil, err
}
return []identity.Identity{
{Crypto: k.certs[0], Raw: pemCert},
{Crypto: k.key, Raw: pemKey},
keyDigest := sha256.Sum256(pkixKey)
certDigest := sha256.Sum256(k.certs[0].Raw)
return []identity.Identity{{
Crypto: k.certs[0],
Raw: k.certs[0].Raw,
Fingerprint: hex.EncodeToString(certDigest[:]),
}, {
Crypto: k.key,
Raw: pkixKey,
Fingerprint: hex.EncodeToString(keyDigest[:]),
},
}, nil
}
19 changes: 15 additions & 4 deletions pkg/pki/pkcs7/pkcs7_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ package pkcs7
import (
"bytes"
"crypto"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"net/url"
"reflect"
"sort"
Expand Down Expand Up @@ -496,22 +498,31 @@ A6ydFG8HXGWcnVVIVQ==

// compare certificate
cert, _ := cryptoutils.UnmarshalCertificatesFromPEM([]byte(tt.identities[0]))
expectedID := identity.Identity{Crypto: cert[0], Raw: []byte(tt.identities[0])}
digest := sha256.Sum256(cert[0].Raw)
expectedID := identity.Identity{Crypto: cert[0], Raw: cert[0].Raw, Fingerprint: hex.EncodeToString(digest[:])}
if !ids[0].Crypto.(*x509.Certificate).Equal(expectedID.Crypto.(*x509.Certificate)) {
t.Errorf("certificates did not match")
}
if !reflect.DeepEqual(ids[0].Raw, expectedID.Raw) {
t.Errorf("raw identities did not match, expected %v, got %v", ids[0].Raw, string(expectedID.Raw))
t.Errorf("raw identities did not match, expected %v, got %v", string(expectedID.Raw), ids[0].Raw)
}
if ids[0].Fingerprint != expectedID.Fingerprint {
t.Fatalf("fingerprints did not match, expected %v, got %v", expectedID.Fingerprint, ids[0].Fingerprint)
}

// compare public key
key, _ := cryptoutils.UnmarshalPEMToPublicKey([]byte(tt.identities[1]))
expectedID = identity.Identity{Crypto: key, Raw: []byte(tt.identities[1])}
pkixKey, _ := cryptoutils.MarshalPublicKeyToDER(key)
digest = sha256.Sum256(pkixKey)
expectedID = identity.Identity{Crypto: key, Raw: pkixKey, Fingerprint: hex.EncodeToString(digest[:])}
if err := cryptoutils.EqualKeys(expectedID.Crypto, ids[1].Crypto); err != nil {
t.Errorf("%v: public keys did not match: %v", tt.name, err)
}
if !reflect.DeepEqual(ids[1].Raw, expectedID.Raw) {
t.Errorf("%v: raw identities did not match", tt.name)
t.Errorf("%v: raw key identities did not match", tt.name)
}
if ids[1].Fingerprint != expectedID.Fingerprint {
t.Fatalf("key fingerprints did not match, expected %v, got %v", expectedID.Fingerprint, ids[1].Fingerprint)
}
})
}
Expand Down
16 changes: 12 additions & 4 deletions pkg/pki/ssh/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
package ssh

import (
"bytes"
"fmt"
"io"
"net/http"

"github.com/asaskevich/govalidator"
"github.com/sigstore/rekor/pkg/pki/identity"
"github.com/sigstore/sigstore/pkg/cryptoutils"
sigsig "github.com/sigstore/sigstore/pkg/signature"
"golang.org/x/crypto/ssh"
)
Expand Down Expand Up @@ -122,7 +122,15 @@ func (k PublicKey) Subjects() []string {

// Identities implements the pki.PublicKey interface
func (k PublicKey) Identities() ([]identity.Identity, error) {
// an authorized key format
authorizedKey := bytes.TrimSpace(ssh.MarshalAuthorizedKey(k.key))
return []identity.Identity{{Crypto: k.key, Raw: authorizedKey}}, nil
key := k.key.(ssh.CryptoPublicKey).CryptoPublicKey()
pkixKey, err := cryptoutils.MarshalPublicKeyToDER(key)
if err != nil {
return nil, err
}
fp := ssh.FingerprintSHA256(k.key)
return []identity.Identity{{
Crypto: k.key,
Raw: pkixKey,
Fingerprint: fp,
}}, nil
}
Loading
Loading