Skip to content

Commit 12c06a0

Browse files
marainoericchiang
authored andcommitted
Drop support for versions of Go lower than 1.20
This commit removes the code to support Go 1.16 to 1.19, requiring now Go 1.20. With this requirement we can remove the build tags. It also renames the X25519 SharedKey to ECDH.
1 parent 2c985a1 commit 12c06a0

File tree

7 files changed

+163
-328
lines changed

7 files changed

+163
-328
lines changed

v2/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module github.com/go-piv/piv-go/v2
22

3-
go 1.16
3+
go 1.20

v2/piv/key.go

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,9 +1006,13 @@ func (yk *YubiKey) PrivateKey(slot Slot, public crypto.PublicKey, auth KeyAuth)
10061006
return &keyEd25519{yk, slot, pub, auth, pp}, nil
10071007
case *rsa.PublicKey:
10081008
return &keyRSA{yk, slot, pub, auth, pp}, nil
1009+
case *ecdh.PublicKey:
1010+
if crv := pub.Curve(); crv != ecdh.X25519() {
1011+
return nil, fmt.Errorf("unsupported ecdh curve: %v", crv)
1012+
}
1013+
return &X25519PrivateKey{yk, slot, pub, auth, pp}, nil
10091014
default:
1010-
// Add support for X25519 keys using build tags
1011-
return yk.tryX25519PrivateKey(slot, public, auth, pp)
1015+
return nil, fmt.Errorf("unsupported public key type: %T", public)
10121016
}
10131017
}
10141018

@@ -1087,13 +1091,17 @@ func (yk *YubiKey) SetPrivateKeyInsecure(key []byte, slot Slot, private crypto.P
10871091
privateKey := make([]byte, elemLen)
10881092
copy(privateKey, priv[:32])
10891093
params = append(params, privateKey)
1090-
default:
1091-
// Add support for X25519 keys using build tags
1092-
var err error
1093-
params, paramTag, elemLen, err = yk.tryX22519PrivateKeyInsecure(private)
1094-
if err != nil {
1095-
return err
1094+
case *ecdh.PrivateKey:
1095+
if crv := priv.Curve(); crv != ecdh.X25519() {
1096+
return fmt.Errorf("unsupported ecdh curve: %v", crv)
10961097
}
1098+
paramTag = 0x08
1099+
elemLen = 32
1100+
1101+
// seed
1102+
params = append(params, priv.Bytes())
1103+
default:
1104+
return fmt.Errorf("unsupported private key type: %T", private)
10971105
}
10981106

10991107
elemLenASN1 := marshalASN1Length(uint64(elemLen))
@@ -1250,6 +1258,33 @@ func (k *ECDSAPrivateKey) ECDH(peer *ecdh.PublicKey) ([]byte, error) {
12501258
})
12511259
}
12521260

1261+
// X25519PrivateKey is a crypto.PrivateKey implementation for X25519 keys. It
1262+
// implements the method ECDH to perform Diffie-Hellman key agreements.
1263+
//
1264+
// Keys returned by YubiKey.PrivateKey() may be type asserted to
1265+
// *X25519PrivateKey, if the slot contains an X25519 key.
1266+
type X25519PrivateKey struct {
1267+
yk *YubiKey
1268+
slot Slot
1269+
pub *ecdh.PublicKey
1270+
auth KeyAuth
1271+
pp PINPolicy
1272+
}
1273+
1274+
func (k *X25519PrivateKey) Public() crypto.PublicKey {
1275+
return k.pub
1276+
}
1277+
1278+
// ECDH performs an ECDH exchange and returns the shared secret.
1279+
//
1280+
// Peer's public key must use the same algorithm as the key in this slot, or an
1281+
// error will be returned.
1282+
func (k *X25519PrivateKey) ECDH(peer *ecdh.PublicKey) ([]byte, error) {
1283+
return k.auth.do(k.yk, k.pp, func(tx *scTx) ([]byte, error) {
1284+
return ykECDHX25519(tx, k.slot, k.pub, peer)
1285+
})
1286+
}
1287+
12531288
type keyEd25519 struct {
12541289
yk *YubiKey
12551290
slot Slot
@@ -1335,6 +1370,38 @@ func ykSignECDSA(tx *scTx, slot Slot, pub *ecdsa.PublicKey, digest []byte) ([]by
13351370
return rs, nil
13361371
}
13371372

1373+
func ykECDHX25519(tx *scTx, slot Slot, pub *ecdh.PublicKey, peer *ecdh.PublicKey) ([]byte, error) {
1374+
if crv := pub.Curve(); crv != ecdh.X25519() {
1375+
return nil, fmt.Errorf("unsupported ecdh curve: %v", crv)
1376+
}
1377+
if pub.Curve() != peer.Curve() {
1378+
return nil, errMismatchingAlgorithms
1379+
}
1380+
cmd := apdu{
1381+
instruction: insAuthenticate,
1382+
param1: algX25519,
1383+
param2: byte(slot.Key),
1384+
data: marshalASN1(0x7c,
1385+
append([]byte{0x82, 0x00},
1386+
marshalASN1(0x85, peer.Bytes())...)),
1387+
}
1388+
resp, err := tx.Transmit(cmd)
1389+
if err != nil {
1390+
return nil, fmt.Errorf("command failed: %w", err)
1391+
}
1392+
1393+
sig, _, err := unmarshalASN1(resp, 1, 0x1c) // 0x7c
1394+
if err != nil {
1395+
return nil, fmt.Errorf("unmarshal response: %v", err)
1396+
}
1397+
sharedSecret, _, err := unmarshalASN1(sig, 2, 0x02) // 0x82
1398+
if err != nil {
1399+
return nil, fmt.Errorf("unmarshal response signature: %v", err)
1400+
}
1401+
1402+
return sharedSecret, nil
1403+
}
1404+
13381405
// This function only works on SoloKeys prototypes and other PIV devices that choose
13391406
// to implement Ed25519 signatures under alg 0x22.
13401407
func skSignEd25519(tx *scTx, slot Slot, pub ed25519.PublicKey, digest []byte) ([]byte, error) {
@@ -1432,6 +1499,16 @@ func decodeRSAPublic(b []byte) (*rsa.PublicKey, error) {
14321499
return &rsa.PublicKey{N: &n, E: int(e.Int64())}, nil
14331500
}
14341501

1502+
func decodeX25519Public(b []byte) (*ecdh.PublicKey, error) {
1503+
// Adaptation of
1504+
// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-73-4.pdf#page=95
1505+
p, _, err := unmarshalASN1(b, 2, 0x06)
1506+
if err != nil {
1507+
return nil, fmt.Errorf("unmarshal points: %v", err)
1508+
}
1509+
return ecdh.X25519().NewPublicKey(p)
1510+
}
1511+
14351512
func rsaAlg(pub *rsa.PublicKey) (byte, error) {
14361513
size := pub.N.BitLen()
14371514
switch size {

v2/piv/key_go120.go

Lines changed: 0 additions & 115 deletions
This file was deleted.

v2/piv/key_go120_test.go

Lines changed: 0 additions & 120 deletions
This file was deleted.

0 commit comments

Comments
 (0)