@@ -1006,9 +1006,13 @@ func (yk *YubiKey) PrivateKey(slot Slot, public crypto.PublicKey, auth KeyAuth)
1006
1006
return & keyEd25519 {yk , slot , pub , auth , pp }, nil
1007
1007
case * rsa.PublicKey :
1008
1008
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
1009
1014
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 )
1012
1016
}
1013
1017
}
1014
1018
@@ -1087,13 +1091,17 @@ func (yk *YubiKey) SetPrivateKeyInsecure(key []byte, slot Slot, private crypto.P
1087
1091
privateKey := make ([]byte , elemLen )
1088
1092
copy (privateKey , priv [:32 ])
1089
1093
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 )
1096
1097
}
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 )
1097
1105
}
1098
1106
1099
1107
elemLenASN1 := marshalASN1Length (uint64 (elemLen ))
@@ -1250,6 +1258,33 @@ func (k *ECDSAPrivateKey) ECDH(peer *ecdh.PublicKey) ([]byte, error) {
1250
1258
})
1251
1259
}
1252
1260
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
+
1253
1288
type keyEd25519 struct {
1254
1289
yk * YubiKey
1255
1290
slot Slot
@@ -1335,6 +1370,38 @@ func ykSignECDSA(tx *scTx, slot Slot, pub *ecdsa.PublicKey, digest []byte) ([]by
1335
1370
return rs , nil
1336
1371
}
1337
1372
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
+
1338
1405
// This function only works on SoloKeys prototypes and other PIV devices that choose
1339
1406
// to implement Ed25519 signatures under alg 0x22.
1340
1407
func skSignEd25519 (tx * scTx , slot Slot , pub ed25519.PublicKey , digest []byte ) ([]byte , error ) {
@@ -1432,6 +1499,16 @@ func decodeRSAPublic(b []byte) (*rsa.PublicKey, error) {
1432
1499
return & rsa.PublicKey {N : & n , E : int (e .Int64 ())}, nil
1433
1500
}
1434
1501
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
+
1435
1512
func rsaAlg (pub * rsa.PublicKey ) (byte , error ) {
1436
1513
size := pub .N .BitLen ()
1437
1514
switch size {
0 commit comments