@@ -35,6 +35,18 @@ import (
35
35
// is given keys using different algorithms.
36
36
var errMismatchingAlgorithms = errors .New ("mismatching key algorithms" )
37
37
38
+ // errUnsupportedKeySize is returned when a key has an unsupported size
39
+ var errUnsupportedKeySize = errors .New ("unsupported key size" )
40
+
41
+ // unsupportedCurveError is used when a key has an unsupported curve
42
+ type unsupportedCurveError struct {
43
+ curve int
44
+ }
45
+
46
+ func (e unsupportedCurveError ) Error () string {
47
+ return fmt .Sprintf ("unsupported curve: %d" , e .curve )
48
+ }
49
+
38
50
// Slot is a private key and certificate combination managed by the security key.
39
51
type Slot struct {
40
52
// Key is a reference for a key type.
@@ -445,19 +457,25 @@ func (yk *YubiKey) Certificate(slot Slot) (*x509.Certificate, error) {
445
457
return cert , nil
446
458
}
447
459
448
- // marshalASN1 encodes a tag, length and data.
449
- //
450
- // TODO: clean this up and maybe switch to cryptobyte?
451
- func marshalASN1 (tag byte , data []byte ) []byte {
460
+ // marshalASN1Length encodes the length.
461
+ func marshalASN1Length (n uint64 ) []byte {
452
462
var l []byte
453
- n := uint64 (len (data ))
454
463
if n < 0x80 {
455
464
l = []byte {byte (n )}
456
- } else if len ( data ) < 0x100 {
465
+ } else if n < 0x100 {
457
466
l = []byte {0x81 , byte (n )}
458
467
} else {
459
468
l = []byte {0x82 , byte (n >> 8 ), byte (n )}
460
469
}
470
+
471
+ return l
472
+ }
473
+
474
+ // marshalASN1 encodes a tag, length and data.
475
+ //
476
+ // TODO: clean this up and maybe switch to cryptobyte?
477
+ func marshalASN1 (tag byte , data []byte ) []byte {
478
+ l := marshalASN1Length (uint64 (len (data )))
461
479
d := append ([]byte {tag }, l ... )
462
480
return append (d , data ... )
463
481
}
@@ -709,6 +727,125 @@ func (yk *YubiKey) PrivateKey(slot Slot, public crypto.PublicKey, auth KeyAuth)
709
727
}
710
728
}
711
729
730
+ // SetPrivateKeyInsecure is an insecure method which imports a private key into the slot.
731
+ // Users should almost always use GeneratePrivateKey() instead.
732
+ //
733
+ // Importing a private key breaks functionality provided by this package, including
734
+ // AttestationCertificate() and Attest(). There are no stability guarantees for other
735
+ // methods for imported private keys.
736
+ //
737
+ // Keys generated outside of the YubiKey should not be considered hardware-backed,
738
+ // as there's no way to prove the key wasn't copied, exfiltrated, or replaced with malicious
739
+ // material before being imported.
740
+ func (yk * YubiKey ) SetPrivateKeyInsecure (key [24 ]byte , slot Slot , private crypto.PrivateKey , policy Key ) error {
741
+ // Reference implementation
742
+ // https://github.com/Yubico/yubico-piv-tool/blob/671a5740ef09d6c5d9d33f6e5575450750b58bde/lib/ykpiv.c#L1812
743
+
744
+ params := make ([][]byte , 0 )
745
+
746
+ var paramTag byte
747
+ var elemLen int
748
+
749
+ switch priv := private .(type ) {
750
+ case * rsa.PrivateKey :
751
+ paramTag = 0x01
752
+ switch priv .N .BitLen () {
753
+ case 1024 :
754
+ policy .Algorithm = AlgorithmRSA1024
755
+ elemLen = 64
756
+ case 2048 :
757
+ policy .Algorithm = AlgorithmRSA2048
758
+ elemLen = 128
759
+ default :
760
+ return errUnsupportedKeySize
761
+ }
762
+
763
+ priv .Precompute ()
764
+
765
+ params = append (params , priv .Primes [0 ].Bytes ()) // P
766
+ params = append (params , priv .Primes [1 ].Bytes ()) // Q
767
+ params = append (params , priv .Precomputed .Dp .Bytes ()) // dP
768
+ params = append (params , priv .Precomputed .Dq .Bytes ()) // dQ
769
+ params = append (params , priv .Precomputed .Qinv .Bytes ()) // Qinv
770
+ case * ecdsa.PrivateKey :
771
+ paramTag = 0x6
772
+ size := priv .PublicKey .Params ().BitSize
773
+ switch size {
774
+ case 256 :
775
+ policy .Algorithm = AlgorithmEC256
776
+ elemLen = 32
777
+ case 384 :
778
+ policy .Algorithm = AlgorithmEC384
779
+ elemLen = 48
780
+ default :
781
+ return unsupportedCurveError {curve : size }
782
+ }
783
+
784
+ // S value
785
+ privateKey := make ([]byte , elemLen )
786
+ valueBytes := priv .D .Bytes ()
787
+ padding := len (privateKey ) - len (valueBytes )
788
+ copy (privateKey [padding :], valueBytes )
789
+
790
+ params = append (params , privateKey )
791
+ default :
792
+ return errors .New ("unsupported private key type" )
793
+ }
794
+
795
+ elemLenASN1 := marshalASN1Length (uint64 (elemLen ))
796
+
797
+ tags := make ([]byte , 0 )
798
+ for i , param := range params {
799
+ tag := paramTag + byte (i )
800
+ tags = append (tags , tag )
801
+ tags = append (tags , elemLenASN1 ... )
802
+
803
+ padding := elemLen - len (param )
804
+ param = append (make ([]byte , padding ), param ... )
805
+ tags = append (tags , param ... )
806
+ }
807
+
808
+ if err := ykAuthenticate (yk .tx , key , yk .rand ); err != nil {
809
+ return fmt .Errorf ("authenticating with management key: %w" , err )
810
+ }
811
+
812
+ return ykImportKey (yk .tx , tags , slot , policy )
813
+ }
814
+
815
+ func ykImportKey (tx * scTx , tags []byte , slot Slot , o Key ) error {
816
+ alg , ok := algorithmsMap [o .Algorithm ]
817
+ if ! ok {
818
+ return fmt .Errorf ("unsupported algorithm" )
819
+
820
+ }
821
+ tp , ok := touchPolicyMap [o .TouchPolicy ]
822
+ if ! ok {
823
+ return fmt .Errorf ("unsupported touch policy" )
824
+ }
825
+ pp , ok := pinPolicyMap [o .PINPolicy ]
826
+ if ! ok {
827
+ return fmt .Errorf ("unsupported pin policy" )
828
+ }
829
+
830
+ // This command is a Yubico PIV extension.
831
+ // https://developers.yubico.com/PIV/Introduction/Yubico_extensions.html
832
+ cmd := apdu {
833
+ instruction : insImportKey ,
834
+ param1 : alg ,
835
+ param2 : byte (slot .Key ),
836
+ data : append (tags , []byte {
837
+ tagPINPolicy , 0x01 , pp ,
838
+ tagTouchPolicy , 0x01 , tp ,
839
+ }... ),
840
+ }
841
+
842
+ if _ , err := tx .Transmit (cmd ); err != nil {
843
+ return fmt .Errorf ("command failed: %w" , err )
844
+ }
845
+
846
+ return nil
847
+ }
848
+
712
849
// ECDSAPrivateKey is a crypto.PrivateKey implementation for ECDSA
713
850
// keys. It implements crypto.Signer and the method SharedKey performs
714
851
// Diffie-Hellman key agreements.
@@ -760,7 +897,7 @@ func (k *ECDSAPrivateKey) SharedKey(peer *ecdsa.PublicKey) ([]byte, error) {
760
897
case 384 :
761
898
alg = algECCP384
762
899
default :
763
- return nil , fmt . Errorf ( "unsupported curve: %d" , size )
900
+ return nil , unsupportedCurveError { curve : size }
764
901
}
765
902
766
903
// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-73-4.pdf#page=118
@@ -840,7 +977,7 @@ func ykSignECDSA(tx *scTx, slot Slot, pub *ecdsa.PublicKey, digest []byte) ([]by
840
977
case 384 :
841
978
alg = algECCP384
842
979
default :
843
- return nil , fmt . Errorf ( "unsupported curve: %d" , size )
980
+ return nil , unsupportedCurveError { curve : size }
844
981
}
845
982
846
983
// Same as the standard library
0 commit comments