vendor: initial vendor

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2019-03-22 13:31:59 -07:00
parent 4f2cc0e220
commit fd8fbf21e6
2584 changed files with 828696 additions and 0 deletions

View File

@ -0,0 +1,111 @@
package signed
import (
"crypto/rand"
"errors"
"github.com/theupdateframework/notary/trustmanager"
"github.com/theupdateframework/notary/tuf/data"
"github.com/theupdateframework/notary/tuf/utils"
)
type edCryptoKey struct {
role data.RoleName
privKey data.PrivateKey
}
// Ed25519 implements a simple in memory cryptosystem for ED25519 keys
type Ed25519 struct {
keys map[string]edCryptoKey
}
// NewEd25519 initializes a new empty Ed25519 CryptoService that operates
// entirely in memory
func NewEd25519() *Ed25519 {
return &Ed25519{
make(map[string]edCryptoKey),
}
}
// AddKey allows you to add a private key
func (e *Ed25519) AddKey(role data.RoleName, gun data.GUN, k data.PrivateKey) error {
e.addKey(role, k)
return nil
}
// addKey allows you to add a private key
func (e *Ed25519) addKey(role data.RoleName, k data.PrivateKey) {
e.keys[k.ID()] = edCryptoKey{
role: role,
privKey: k,
}
}
// RemoveKey deletes a key from the signer
func (e *Ed25519) RemoveKey(keyID string) error {
delete(e.keys, keyID)
return nil
}
// ListKeys returns the list of keys IDs for the role
func (e *Ed25519) ListKeys(role data.RoleName) []string {
keyIDs := make([]string, 0, len(e.keys))
for id, edCryptoKey := range e.keys {
if edCryptoKey.role == role {
keyIDs = append(keyIDs, id)
}
}
return keyIDs
}
// ListAllKeys returns the map of keys IDs to role
func (e *Ed25519) ListAllKeys() map[string]data.RoleName {
keys := make(map[string]data.RoleName)
for id, edKey := range e.keys {
keys[id] = edKey.role
}
return keys
}
// Create generates a new key and returns the public part
func (e *Ed25519) Create(role data.RoleName, gun data.GUN, algorithm string) (data.PublicKey, error) {
if algorithm != data.ED25519Key {
return nil, errors.New("only ED25519 supported by this cryptoservice")
}
private, err := utils.GenerateED25519Key(rand.Reader)
if err != nil {
return nil, err
}
e.addKey(role, private)
return data.PublicKeyFromPrivate(private), nil
}
// PublicKeys returns a map of public keys for the ids provided, when those IDs are found
// in the store.
func (e *Ed25519) PublicKeys(keyIDs ...string) (map[string]data.PublicKey, error) {
k := make(map[string]data.PublicKey)
for _, keyID := range keyIDs {
if edKey, ok := e.keys[keyID]; ok {
k[keyID] = data.PublicKeyFromPrivate(edKey.privKey)
}
}
return k, nil
}
// GetKey returns a single public key based on the ID
func (e *Ed25519) GetKey(keyID string) data.PublicKey {
if privKey, _, err := e.GetPrivateKey(keyID); err == nil {
return data.PublicKeyFromPrivate(privKey)
}
return nil
}
// GetPrivateKey returns a single private key and role if present, based on the ID
func (e *Ed25519) GetPrivateKey(keyID string) (data.PrivateKey, data.RoleName, error) {
if k, ok := e.keys[keyID]; ok {
return k.privKey, k.role, nil
}
return nil, "", trustmanager.ErrKeyNotFound{KeyID: keyID}
}

View File

@ -0,0 +1,98 @@
package signed
import (
"fmt"
"strings"
"github.com/theupdateframework/notary/tuf/data"
)
// ErrInsufficientSignatures - can not create enough signatures on a piece of
// metadata
type ErrInsufficientSignatures struct {
FoundKeys int
NeededKeys int
MissingKeyIDs []string
}
func (e ErrInsufficientSignatures) Error() string {
candidates := ""
if len(e.MissingKeyIDs) > 0 {
candidates = fmt.Sprintf(" (%s)", strings.Join(e.MissingKeyIDs, ", "))
}
if e.FoundKeys == 0 {
return fmt.Sprintf("signing keys not available: need %d keys from %d possible keys%s",
e.NeededKeys, len(e.MissingKeyIDs), candidates)
}
return fmt.Sprintf("not enough signing keys: found %d of %d needed keys - %d other possible keys%s",
e.FoundKeys, e.NeededKeys, len(e.MissingKeyIDs), candidates)
}
// ErrExpired indicates a piece of metadata has expired
type ErrExpired struct {
Role data.RoleName
Expired string
}
func (e ErrExpired) Error() string {
return fmt.Sprintf("%s expired at %v", e.Role.String(), e.Expired)
}
// ErrLowVersion indicates the piece of metadata has a version number lower than
// a version number we're already seen for this role
type ErrLowVersion struct {
Actual int
Current int
}
func (e ErrLowVersion) Error() string {
return fmt.Sprintf("version %d is lower than current version %d", e.Actual, e.Current)
}
// ErrRoleThreshold indicates we did not validate enough signatures to meet the threshold
type ErrRoleThreshold struct {
Msg string
}
func (e ErrRoleThreshold) Error() string {
if e.Msg == "" {
return "valid signatures did not meet threshold"
}
return e.Msg
}
// ErrInvalidKeyType indicates the types for the key and signature it's associated with are
// mismatched. Probably a sign of malicious behaviour
type ErrInvalidKeyType struct{}
func (e ErrInvalidKeyType) Error() string {
return "key type is not valid for signature"
}
// ErrInvalidKeyID indicates the specified key ID was incorrect for its associated data
type ErrInvalidKeyID struct{}
func (e ErrInvalidKeyID) Error() string {
return "key ID is not valid for key content"
}
// ErrInvalidKeyLength indicates that while we may support the cipher, the provided
// key length is not specifically supported, i.e. we support RSA, but not 1024 bit keys
type ErrInvalidKeyLength struct {
msg string
}
func (e ErrInvalidKeyLength) Error() string {
return fmt.Sprintf("key length is not supported: %s", e.msg)
}
// ErrNoKeys indicates no signing keys were found when trying to sign
type ErrNoKeys struct {
KeyIDs []string
}
func (e ErrNoKeys) Error() string {
return fmt.Sprintf("could not find necessary signing keys, at least one of these keys must be available: %s",
strings.Join(e.KeyIDs, ", "))
}

View File

@ -0,0 +1,47 @@
package signed
import "github.com/theupdateframework/notary/tuf/data"
// KeyService provides management of keys locally. It will never
// accept or provide private keys. Communication between the KeyService
// and a SigningService happen behind the Create function.
type KeyService interface {
// Create issues a new key pair and is responsible for loading
// the private key into the appropriate signing service.
Create(role data.RoleName, gun data.GUN, algorithm string) (data.PublicKey, error)
// AddKey adds a private key to the specified role and gun
AddKey(role data.RoleName, gun data.GUN, key data.PrivateKey) error
// GetKey retrieves the public key if present, otherwise it returns nil
GetKey(keyID string) data.PublicKey
// GetPrivateKey retrieves the private key and role if present and retrievable,
// otherwise it returns nil and an error
GetPrivateKey(keyID string) (data.PrivateKey, data.RoleName, error)
// RemoveKey deletes the specified key, and returns an error only if the key
// removal fails. If the key doesn't exist, no error should be returned.
RemoveKey(keyID string) error
// ListKeys returns a list of key IDs for the role, or an empty list or
// nil if there are no keys.
ListKeys(role data.RoleName) []string
// ListAllKeys returns a map of all available signing key IDs to role, or
// an empty map or nil if there are no keys.
ListAllKeys() map[string]data.RoleName
}
// CryptoService is deprecated and all instances of its use should be
// replaced with KeyService
type CryptoService interface {
KeyService
}
// Verifier defines an interface for verfying signatures. An implementer
// of this interface should verify signatures for one and only one
// signing scheme.
type Verifier interface {
Verify(key data.PublicKey, sig []byte, msg []byte) error
}

View File

@ -0,0 +1,113 @@
package signed
// The Sign function is a choke point for all code paths that do signing.
// We use this fact to do key ID translation. There are 2 types of key ID:
// - Scoped: the key ID based purely on the data that appears in the TUF
// files. This may be wrapped by a certificate that scopes the
// key to be used in a specific context.
// - Canonical: the key ID based purely on the public key bytes. This is
// used by keystores to easily identify keys that may be reused
// in many scoped locations.
// Currently these types only differ in the context of Root Keys in Notary
// for which the root key is wrapped using an x509 certificate.
import (
"crypto/rand"
"github.com/sirupsen/logrus"
"github.com/theupdateframework/notary/trustmanager"
"github.com/theupdateframework/notary/tuf/data"
"github.com/theupdateframework/notary/tuf/utils"
)
// Sign takes a data.Signed and a cryptoservice containing private keys,
// calculates and adds at least minSignature signatures using signingKeys the
// data.Signed. It will also clean up any signatures that are not in produced
// by either a signingKey or an otherWhitelistedKey.
// Note that in most cases, otherWhitelistedKeys should probably be null. They
// are for keys you don't want to sign with, but you also don't want to remove
// existing signatures by those keys. For instance, if you want to call Sign
// multiple times with different sets of signing keys without undoing removing
// signatures produced by the previous call to Sign.
func Sign(service CryptoService, s *data.Signed, signingKeys []data.PublicKey,
minSignatures int, otherWhitelistedKeys []data.PublicKey) error {
logrus.Debugf("sign called with %d/%d required keys", minSignatures, len(signingKeys))
signatures := make([]data.Signature, 0, len(s.Signatures)+1)
signingKeyIDs := make(map[string]struct{})
tufIDs := make(map[string]data.PublicKey)
privKeys := make(map[string]data.PrivateKey)
// Get all the private key objects related to the public keys
missingKeyIDs := []string{}
for _, key := range signingKeys {
canonicalID, err := utils.CanonicalKeyID(key)
tufIDs[key.ID()] = key
if err != nil {
return err
}
k, _, err := service.GetPrivateKey(canonicalID)
if err != nil {
if _, ok := err.(trustmanager.ErrKeyNotFound); ok {
missingKeyIDs = append(missingKeyIDs, canonicalID)
continue
}
return err
}
privKeys[key.ID()] = k
}
// include the list of otherWhitelistedKeys
for _, key := range otherWhitelistedKeys {
if _, ok := tufIDs[key.ID()]; !ok {
tufIDs[key.ID()] = key
}
}
// Check to ensure we have enough signing keys
if len(privKeys) < minSignatures {
return ErrInsufficientSignatures{FoundKeys: len(privKeys),
NeededKeys: minSignatures, MissingKeyIDs: missingKeyIDs}
}
emptyStruct := struct{}{}
// Do signing and generate list of signatures
for keyID, pk := range privKeys {
sig, err := pk.Sign(rand.Reader, *s.Signed, nil)
if err != nil {
logrus.Debugf("Failed to sign with key: %s. Reason: %v", keyID, err)
return err
}
signingKeyIDs[keyID] = emptyStruct
signatures = append(signatures, data.Signature{
KeyID: keyID,
Method: pk.SignatureAlgorithm(),
Signature: sig[:],
})
}
for _, sig := range s.Signatures {
if _, ok := signingKeyIDs[sig.KeyID]; ok {
// key is in the set of key IDs for which a signature has been created
continue
}
var (
k data.PublicKey
ok bool
)
if k, ok = tufIDs[sig.KeyID]; !ok {
// key is no longer a valid signing key
continue
}
if err := VerifySignature(*s.Signed, &sig, k); err != nil {
// signature is no longer valid
continue
}
// keep any signatures that still represent valid keys and are
// themselves valid
signatures = append(signatures, sig)
}
s.Signatures = signatures
return nil
}

View File

@ -0,0 +1,264 @@
package signed
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
"github.com/agl/ed25519"
"github.com/sirupsen/logrus"
"github.com/theupdateframework/notary/tuf/data"
)
const (
minRSAKeySizeBit = 2048 // 2048 bits = 256 bytes
minRSAKeySizeByte = minRSAKeySizeBit / 8
)
// Verifiers serves as a map of all verifiers available on the system and
// can be injected into a verificationService. For testing and configuration
// purposes, it will not be used by default.
var Verifiers = map[data.SigAlgorithm]Verifier{
data.RSAPSSSignature: RSAPSSVerifier{},
data.RSAPKCS1v15Signature: RSAPKCS1v15Verifier{},
data.PyCryptoSignature: RSAPyCryptoVerifier{},
data.ECDSASignature: ECDSAVerifier{},
data.EDDSASignature: Ed25519Verifier{},
}
// Ed25519Verifier used to verify Ed25519 signatures
type Ed25519Verifier struct{}
// Verify checks that an ed25519 signature is valid
func (v Ed25519Verifier) Verify(key data.PublicKey, sig []byte, msg []byte) error {
if key.Algorithm() != data.ED25519Key {
return ErrInvalidKeyType{}
}
var sigBytes [ed25519.SignatureSize]byte
if len(sig) != ed25519.SignatureSize {
logrus.Debugf("signature length is incorrect, must be %d, was %d.", ed25519.SignatureSize, len(sig))
return ErrInvalid
}
copy(sigBytes[:], sig)
var keyBytes [ed25519.PublicKeySize]byte
pub := key.Public()
if len(pub) != ed25519.PublicKeySize {
logrus.Errorf("public key is incorrect size, must be %d, was %d.", ed25519.PublicKeySize, len(pub))
return ErrInvalidKeyLength{msg: fmt.Sprintf("ed25519 public key must be %d bytes.", ed25519.PublicKeySize)}
}
n := copy(keyBytes[:], key.Public())
if n < ed25519.PublicKeySize {
logrus.Errorf("failed to copy the key, must have %d bytes, copied %d bytes.", ed25519.PublicKeySize, n)
return ErrInvalid
}
if !ed25519.Verify(&keyBytes, msg, &sigBytes) {
logrus.Debugf("failed ed25519 verification")
return ErrInvalid
}
return nil
}
func verifyPSS(key interface{}, digest, sig []byte) error {
rsaPub, ok := key.(*rsa.PublicKey)
if !ok {
logrus.Debugf("value was not an RSA public key")
return ErrInvalid
}
if rsaPub.N.BitLen() < minRSAKeySizeBit {
logrus.Debugf("RSA keys less than 2048 bits are not acceptable, provided key has length %d.", rsaPub.N.BitLen())
return ErrInvalidKeyLength{msg: fmt.Sprintf("RSA key must be at least %d bits.", minRSAKeySizeBit)}
}
if len(sig) < minRSAKeySizeByte {
logrus.Debugf("RSA keys less than 2048 bits are not acceptable, provided signature has length %d.", len(sig))
return ErrInvalid
}
opts := rsa.PSSOptions{SaltLength: sha256.Size, Hash: crypto.SHA256}
if err := rsa.VerifyPSS(rsaPub, crypto.SHA256, digest[:], sig, &opts); err != nil {
logrus.Debugf("failed RSAPSS verification: %s", err)
return ErrInvalid
}
return nil
}
func getRSAPubKey(key data.PublicKey) (crypto.PublicKey, error) {
algorithm := key.Algorithm()
var pubKey crypto.PublicKey
switch algorithm {
case data.RSAx509Key:
pemCert, _ := pem.Decode([]byte(key.Public()))
if pemCert == nil {
logrus.Debugf("failed to decode PEM-encoded x509 certificate")
return nil, ErrInvalid
}
cert, err := x509.ParseCertificate(pemCert.Bytes)
if err != nil {
logrus.Debugf("failed to parse x509 certificate: %s\n", err)
return nil, ErrInvalid
}
pubKey = cert.PublicKey
case data.RSAKey:
var err error
pubKey, err = x509.ParsePKIXPublicKey(key.Public())
if err != nil {
logrus.Debugf("failed to parse public key: %s\n", err)
return nil, ErrInvalid
}
default:
// only accept RSA keys
logrus.Debugf("invalid key type for RSAPSS verifier: %s", algorithm)
return nil, ErrInvalidKeyType{}
}
return pubKey, nil
}
// RSAPSSVerifier checks RSASSA-PSS signatures
type RSAPSSVerifier struct{}
// Verify does the actual check.
func (v RSAPSSVerifier) Verify(key data.PublicKey, sig []byte, msg []byte) error {
// will return err if keytype is not a recognized RSA type
pubKey, err := getRSAPubKey(key)
if err != nil {
return err
}
digest := sha256.Sum256(msg)
return verifyPSS(pubKey, digest[:], sig)
}
// RSAPKCS1v15Verifier checks RSA PKCS1v15 signatures
type RSAPKCS1v15Verifier struct{}
// Verify does the actual verification
func (v RSAPKCS1v15Verifier) Verify(key data.PublicKey, sig []byte, msg []byte) error {
// will return err if keytype is not a recognized RSA type
pubKey, err := getRSAPubKey(key)
if err != nil {
return err
}
digest := sha256.Sum256(msg)
rsaPub, ok := pubKey.(*rsa.PublicKey)
if !ok {
logrus.Debugf("value was not an RSA public key")
return ErrInvalid
}
if rsaPub.N.BitLen() < minRSAKeySizeBit {
logrus.Debugf("RSA keys less than 2048 bits are not acceptable, provided key has length %d.", rsaPub.N.BitLen())
return ErrInvalidKeyLength{msg: fmt.Sprintf("RSA key must be at least %d bits.", minRSAKeySizeBit)}
}
if len(sig) < minRSAKeySizeByte {
logrus.Debugf("RSA keys less than 2048 bits are not acceptable, provided signature has length %d.", len(sig))
return ErrInvalid
}
if err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, digest[:], sig); err != nil {
logrus.Errorf("Failed verification: %s", err.Error())
return ErrInvalid
}
return nil
}
// RSAPyCryptoVerifier checks RSASSA-PSS signatures
type RSAPyCryptoVerifier struct{}
// Verify does the actual check.
// N.B. We have not been able to make this work in a way that is compatible
// with PyCrypto.
func (v RSAPyCryptoVerifier) Verify(key data.PublicKey, sig []byte, msg []byte) error {
digest := sha256.Sum256(msg)
if key.Algorithm() != data.RSAKey {
return ErrInvalidKeyType{}
}
k, _ := pem.Decode([]byte(key.Public()))
if k == nil {
logrus.Debugf("failed to decode PEM-encoded x509 certificate")
return ErrInvalid
}
pub, err := x509.ParsePKIXPublicKey(k.Bytes)
if err != nil {
logrus.Debugf("failed to parse public key: %s\n", err)
return ErrInvalid
}
return verifyPSS(pub, digest[:], sig)
}
// ECDSAVerifier checks ECDSA signatures, decoding the keyType appropriately
type ECDSAVerifier struct{}
// Verify does the actual check.
func (v ECDSAVerifier) Verify(key data.PublicKey, sig []byte, msg []byte) error {
algorithm := key.Algorithm()
var pubKey crypto.PublicKey
switch algorithm {
case data.ECDSAx509Key:
pemCert, _ := pem.Decode([]byte(key.Public()))
if pemCert == nil {
logrus.Debugf("failed to decode PEM-encoded x509 certificate for keyID: %s", key.ID())
logrus.Debugf("certificate bytes: %s", string(key.Public()))
return ErrInvalid
}
cert, err := x509.ParseCertificate(pemCert.Bytes)
if err != nil {
logrus.Debugf("failed to parse x509 certificate: %s\n", err)
return ErrInvalid
}
pubKey = cert.PublicKey
case data.ECDSAKey:
var err error
pubKey, err = x509.ParsePKIXPublicKey(key.Public())
if err != nil {
logrus.Debugf("Failed to parse private key for keyID: %s, %s\n", key.ID(), err)
return ErrInvalid
}
default:
// only accept ECDSA keys.
logrus.Debugf("invalid key type for ECDSA verifier: %s", algorithm)
return ErrInvalidKeyType{}
}
ecdsaPubKey, ok := pubKey.(*ecdsa.PublicKey)
if !ok {
logrus.Debugf("value isn't an ECDSA public key")
return ErrInvalid
}
sigLength := len(sig)
expectedOctetLength := 2 * ((ecdsaPubKey.Params().BitSize + 7) >> 3)
if sigLength != expectedOctetLength {
logrus.Debugf("signature had an unexpected length")
return ErrInvalid
}
rBytes, sBytes := sig[:sigLength/2], sig[sigLength/2:]
r := new(big.Int).SetBytes(rBytes)
s := new(big.Int).SetBytes(sBytes)
digest := sha256.Sum256(msg)
if !ecdsa.Verify(ecdsaPubKey, digest[:], r, s) {
logrus.Debugf("failed ECDSA signature validation")
return ErrInvalid
}
return nil
}

View File

@ -0,0 +1,123 @@
package signed
import (
"errors"
"fmt"
"strings"
"time"
"github.com/docker/go/canonical/json"
"github.com/sirupsen/logrus"
"github.com/theupdateframework/notary/tuf/data"
"github.com/theupdateframework/notary/tuf/utils"
)
// Various basic signing errors
var (
ErrNoSignatures = errors.New("tuf: data has no signatures")
ErrInvalid = errors.New("tuf: signature verification failed")
ErrWrongType = errors.New("tuf: meta file has wrong type")
)
// IsExpired checks if the given time passed before the present time
func IsExpired(t time.Time) bool {
return t.Before(time.Now())
}
// VerifyExpiry returns ErrExpired if the metadata is expired
func VerifyExpiry(s *data.SignedCommon, role data.RoleName) error {
if IsExpired(s.Expires) {
logrus.Errorf("Metadata for %s expired", role)
return ErrExpired{Role: role, Expired: s.Expires.Format("Mon Jan 2 15:04:05 MST 2006")}
}
return nil
}
// VerifyVersion returns ErrLowVersion if the metadata version is lower than the min version
func VerifyVersion(s *data.SignedCommon, minVersion int) error {
if s.Version < minVersion {
return ErrLowVersion{Actual: s.Version, Current: minVersion}
}
return nil
}
// VerifySignatures checks the we have sufficient valid signatures for the given role
func VerifySignatures(s *data.Signed, roleData data.BaseRole) error {
if len(s.Signatures) == 0 {
return ErrNoSignatures
}
if roleData.Threshold < 1 {
return ErrRoleThreshold{}
}
logrus.Debugf("%s role has key IDs: %s", roleData.Name, strings.Join(roleData.ListKeyIDs(), ","))
// remarshal the signed part so we can verify the signature, since the signature has
// to be of a canonically marshalled signed object
var decoded map[string]interface{}
if err := json.Unmarshal(*s.Signed, &decoded); err != nil {
return err
}
msg, err := json.MarshalCanonical(decoded)
if err != nil {
return err
}
valid := make(map[string]struct{})
for i := range s.Signatures {
sig := &(s.Signatures[i])
logrus.Debug("verifying signature for key ID: ", sig.KeyID)
key, ok := roleData.Keys[sig.KeyID]
if !ok {
logrus.Debugf("continuing b/c keyid lookup was nil: %s\n", sig.KeyID)
continue
}
// Check that the signature key ID actually matches the content ID of the key
if key.ID() != sig.KeyID {
return ErrInvalidKeyID{}
}
if err := VerifySignature(msg, sig, key); err != nil {
logrus.Debugf("continuing b/c %s", err.Error())
continue
}
valid[sig.KeyID] = struct{}{}
}
if len(valid) < roleData.Threshold {
return ErrRoleThreshold{
Msg: fmt.Sprintf("valid signatures did not meet threshold for %s", roleData.Name),
}
}
return nil
}
// VerifySignature checks a single signature and public key against a payload
// If the signature is verified, the signature's is valid field will actually
// be mutated to be equal to the boolean true
func VerifySignature(msg []byte, sig *data.Signature, pk data.PublicKey) error {
// method lookup is consistent due to Unmarshal JSON doing lower case for us.
method := sig.Method
verifier, ok := Verifiers[method]
if !ok {
return fmt.Errorf("signing method is not supported: %s", sig.Method)
}
if err := verifier.Verify(pk, sig.Signature, msg); err != nil {
return fmt.Errorf("signature was invalid")
}
sig.IsValid = true
return nil
}
// VerifyPublicKeyMatchesPrivateKey checks if the private key and the public keys forms valid key pairs.
// Supports both x509 certificate PublicKeys and non-certificate PublicKeys
func VerifyPublicKeyMatchesPrivateKey(privKey data.PrivateKey, pubKey data.PublicKey) error {
pubKeyID, err := utils.CanonicalKeyID(pubKey)
if err != nil {
return fmt.Errorf("could not verify key pair: %v", err)
}
if privKey == nil || pubKeyID != privKey.ID() {
return fmt.Errorf("private key is nil or does not match public key")
}
return nil
}