@@ -847,14 +847,47 @@ func ykSetProtectedMetadata(tx *scTx, key [24]byte, m *Metadata) error {
847
847
return nil
848
848
}
849
849
850
- // Card Holder Unique Identifier
851
- type CardId []byte
852
-
853
- func (yk * YubiKey ) CardId () (CardId , error ) {
854
- return ykGetCardId (yk .tx )
855
-
856
- }
857
- func ykGetCardId (tx * scTx ) (CardId , error ) {
850
+ // CardID is the Card Holder Unique Identifier with settable GUID
851
+ // Raw contains the whole object
852
+ // GUID contains the Card Universally Unique Identifier.
853
+ type CardID struct {
854
+ Raw []byte
855
+ GUID [16 ]byte
856
+ }
857
+
858
+ // CardID returns the card CHUID with the GUID extracted
859
+ func (yk * YubiKey ) CardID () (CardID , error ) {
860
+ return ykGetCardID (yk .tx )
861
+
862
+ }
863
+
864
+ /*
865
+ * From https://github.com/Yubico/yubico-piv-tool/blob/ebee7f63b85fe4373efc4d8d44cbe5fe321c158c/lib/util.c#L44
866
+ * Format defined in SP-800-73-4, Appendix A, Table 9
867
+ *
868
+ * FASC-N containing S9999F9999F999999F0F1F0000000000300001E encoded in
869
+ * 4-bit BCD with 1 bit parity. run through the tools/fasc.pl script to get
870
+ * bytes. This CHUID has an expiry of 2030-01-01.
871
+ *
872
+ * Defined fields:
873
+ * - 0x30: FASC-N (hard-coded)
874
+ * - 0x34: Card UUID / GUID (settable)
875
+ * - 0x35: Exp. Date (hard-coded)
876
+ * - 0x3e: Signature (hard-coded, empty)
877
+ * - 0xfe: Error Detection Code (hard-coded)
878
+ */
879
+
880
+ var chuidTemplate = []byte {
881
+ 0x30 , 0x19 , 0xd4 , 0xe7 , 0x39 , 0xda , 0x73 , 0x9c , 0xed , 0x39 , 0xce , 0x73 , 0x9d ,
882
+ 0x83 , 0x68 , 0x58 , 0x21 , 0x08 , 0x42 , 0x10 , 0x84 , 0x21 , 0xc8 , 0x42 , 0x10 , 0xc3 ,
883
+ 0xeb , 0x34 , 0x10 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
884
+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x35 , 0x08 , 0x32 , 0x30 , 0x33 , 0x30 , 0x30 ,
885
+ 0x31 , 0x30 , 0x31 , 0x3e , 0x00 , 0xfe , 0x00 ,
886
+ }
887
+
888
+ const uuidOffset = 29
889
+
890
+ func ykGetCardID (tx * scTx ) (id CardID , err error ) {
858
891
// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-73-4.pdf#page=17
859
892
// OID for CardId is 5FC102
860
893
@@ -872,12 +905,62 @@ func ykGetCardId(tx *scTx) (CardId, error) {
872
905
}
873
906
resp , err := tx .Transmit (cmd )
874
907
if err != nil {
875
- return nil , fmt .Errorf ("command failed: %w" , err )
908
+ return id , fmt .Errorf ("command failed: %w" , err )
876
909
}
877
910
// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-73-4.pdf#page=85
878
911
obj , _ , err := unmarshalASN1 (resp , 1 , 0x13 ) // tag 0x53
879
912
if err != nil {
880
- return nil , fmt .Errorf ("unmarshaling response: %v" , err )
913
+ return id , fmt .Errorf ("unmarshaling response: %v" , err )
881
914
}
882
- return obj , nil
915
+ id .Raw = obj
916
+ if obj [27 ] == 0x34 {
917
+ endPos := uuidOffset + int (obj [28 ])
918
+ copy (id .GUID [:], obj [uuidOffset :endPos ])
919
+ }
920
+ return
921
+ }
922
+
923
+ // SetCardID initialize the CHUID card object using a predefined template defined as
924
+ // * Defined fields:
925
+ // - 0x30: FASC-N (hard-coded)
926
+ // - 0x34: Card UUID / GUID (settable)
927
+ // - 0x35: Exp. Date (hard-coded)
928
+ // - 0x3e: Signature (hard-coded, empty)
929
+ // - 0xfe: Error Detection Code (hard-coded)
930
+ func (yk * YubiKey ) SetCardID (GUID [16 ]byte , key [24 ]byte ) (CardID , error ) {
931
+ return ykSetCardID (yk .tx , key , GUID )
932
+
933
+ }
934
+
935
+ func ykSetCardID (tx * scTx , key [24 ]byte , guid [16 ]byte ) (id CardID , err error ) {
936
+
937
+ id .Raw = make ([]byte , len (chuidTemplate ))
938
+ copy (id .Raw , chuidTemplate )
939
+ copy (id .Raw [uuidOffset :], guid [:])
940
+
941
+ data := append ([]byte {
942
+ 0x5c , // Tag list
943
+ 0x03 ,
944
+ 0x5f ,
945
+ 0xc1 ,
946
+ 0x02 ,
947
+ }, marshalASN1 (0x53 , id .Raw )... )
948
+
949
+ cmd := apdu {
950
+ instruction : insPutData ,
951
+ param1 : 0x3f ,
952
+ param2 : 0xff ,
953
+ data : data ,
954
+ }
955
+
956
+ if err := ykAuthenticate (tx , key , rand .Reader ); err != nil {
957
+ return id , fmt .Errorf ("authenticating with key: %w" , err )
958
+ }
959
+
960
+ _ , err = tx .Transmit (cmd )
961
+ if err != nil {
962
+ return id , fmt .Errorf ("command failed: %w" , err )
963
+ }
964
+
965
+ return
883
966
}
0 commit comments