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

Add ECDSAPrivateKey.ECDH which takes a *ecdh.PublicKey #158

Merged
merged 1 commit into from
Oct 2, 2024
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
26 changes: 24 additions & 2 deletions v2/piv/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package piv
import (
"bytes"
"crypto"
"crypto/ecdh"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
Expand Down Expand Up @@ -1151,10 +1152,31 @@ func (k *ECDSAPrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.Signer
// used for the operation. Callers should use a cryptographic key
// derivation function to extract the amount of bytes they need.
func (k *ECDSAPrivateKey) SharedKey(peer *ecdsa.PublicKey) ([]byte, error) {
if peer.Curve.Params().BitSize != k.pub.Curve.Params().BitSize {
peerECDH, err := peer.ECDH()
if err != nil {
return nil, unsupportedCurveError{curve: peer.Params().BitSize}
}
return k.ECDH(peerECDH)
}

// ECDH performs a Diffie-Hellman key agreement with the peer
// to produce a shared secret key.
//
// Peer's public key must use the same algorithm as the key in
// this slot, or an error will be returned.
//
// Length of the result depends on the types and sizes of the keys
// used for the operation. Callers should use a cryptographic key
// derivation function to extract the amount of bytes they need.
func (k *ECDSAPrivateKey) ECDH(peer *ecdh.PublicKey) ([]byte, error) {
ourECDH, err := k.pub.ECDH()
if err != nil {
return nil, unsupportedCurveError{curve: k.pub.Params().BitSize}
}
if peer.Curve() != ourECDH.Curve() {
return nil, errMismatchingAlgorithms
}
msg := elliptic.Marshal(peer.Curve, peer.X, peer.Y)
msg := peer.Bytes()
return k.auth.do(k.yk, k.pp, func(tx *scTx) ([]byte, error) {
var alg byte
size := k.pub.Params().BitSize
Expand Down
66 changes: 66 additions & 0 deletions v2/piv/key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package piv
import (
"bytes"
"crypto"
"crypto/ecdh"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
Expand Down Expand Up @@ -84,6 +85,71 @@ func TestYubiKeySignECDSA(t *testing.T) {
}
}

func TestYubiKeyECDSAECDH(t *testing.T) {
yk, close := newTestYubiKey(t)
defer close()

slot := SlotAuthentication

key := Key{
Algorithm: AlgorithmEC256,
TouchPolicy: TouchPolicyNever,
PINPolicy: PINPolicyNever,
}
pubKey, err := yk.GenerateKey(DefaultManagementKey, slot, key)
if err != nil {
t.Fatalf("generating key: %v", err)
}
pub, ok := pubKey.(*ecdsa.PublicKey)
if !ok {
t.Fatalf("public key is not an ecdsa key")
}
pubECDH, err := pub.ECDH()
if err != nil {
t.Fatalf("converting pubkey to ECDH key: %v", err)
}
priv, err := yk.PrivateKey(slot, pub, KeyAuth{})
if err != nil {
t.Fatalf("getting private key: %v", err)
}
privECDSA, ok := priv.(*ECDSAPrivateKey)
if !ok {
t.Fatalf("expected private key to be ECDSA private key")
}

t.Run("good", func(t *testing.T) {
privECDH, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("cannot generate key: %v", err)
}
secret1, err := privECDH.ECDH(pubECDH)
if err != nil {
t.Fatalf("key agreement 1 failed: %v", err)
}

secret2, err := privECDSA.ECDH(privECDH.PublicKey())
if err != nil {
t.Fatalf("key agreement 2 failed: %v", err)
}
if !bytes.Equal(secret1, secret2) {
t.Errorf("key agreement didn't match")
}
})

t.Run("bad", func(t *testing.T) {
t.Run("size", func(t *testing.T) {
privECDH, err := ecdh.P384().GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("cannot generate key: %v", err)
}
_, err = privECDSA.ECDH(privECDH.PublicKey())
if !errors.Is(err, errMismatchingAlgorithms) {
t.Fatalf("unexpected error value: wanted errMismatchingAlgorithms: %v", err)
}
})
})
}

func TestYubiKeyECDSASharedKey(t *testing.T) {
yk, close := newTestYubiKey(t)
defer close()
Expand Down
Loading