-
Notifications
You must be signed in to change notification settings - Fork 75
Implement Read/Write CHUID #67
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
base: master
Are you sure you want to change the base?
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -846,3 +846,121 @@ func ykSetProtectedMetadata(tx *scTx, key [24]byte, m *Metadata) error { | |
| } | ||
| return nil | ||
| } | ||
|
|
||
| // CardID is the Card Holder Unique Identifier with settable GUID | ||
| // Raw contains the whole object | ||
| // GUID contains the Card Universally Unique Identifier. | ||
| type CardID struct { | ||
| Raw []byte | ||
| GUID [16]byte | ||
| } | ||
|
|
||
| // CardID returns the card CHUID with the GUID extracted | ||
| func (yk *YubiKey) CardID() (CardID, error) { | ||
|
||
| return ykGetCardID(yk.tx) | ||
|
|
||
| } | ||
|
|
||
| /* | ||
| * From https://github.com/Yubico/yubico-piv-tool/blob/ebee7f63b85fe4373efc4d8d44cbe5fe321c158c/lib/util.c#L44 | ||
| * Format defined in SP-800-73-4, Appendix A, Table 9 | ||
| * | ||
| * FASC-N containing S9999F9999F999999F0F1F0000000000300001E encoded in | ||
| * 4-bit BCD with 1 bit parity. run through the tools/fasc.pl script to get | ||
| * bytes. This CHUID has an expiry of 2030-01-01. | ||
| * | ||
| * Defined fields: | ||
| * - 0x30: FASC-N (hard-coded) | ||
| * - 0x34: Card UUID / GUID (settable) | ||
| * - 0x35: Exp. Date (hard-coded) | ||
| * - 0x3e: Signature (hard-coded, empty) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these actually not settable or is that just the API that yubico's tools expose?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They can be set - it's hardcoded in the yubico implementation, i was thinking of eventually add these and implement the BCD encoding too in a future PR. |
||
| * - 0xfe: Error Detection Code (hard-coded) | ||
| */ | ||
|
|
||
| var chuidTemplate = []byte{ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where does this come from? Would default field values be better?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is coming straight from yubico tools. they generated that - for now, it would suggest to do the same, and later add the fields in the CardID struct and add the encoding. |
||
| 0x30, 0x19, 0xd4, 0xe7, 0x39, 0xda, 0x73, 0x9c, 0xed, 0x39, 0xce, 0x73, 0x9d, | ||
| 0x83, 0x68, 0x58, 0x21, 0x08, 0x42, 0x10, 0x84, 0x21, 0xc8, 0x42, 0x10, 0xc3, | ||
| 0xeb, 0x34, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x08, 0x32, 0x30, 0x33, 0x30, 0x30, | ||
| 0x31, 0x30, 0x31, 0x3e, 0x00, 0xfe, 0x00, | ||
| } | ||
|
|
||
| const uuidOffset = 29 | ||
|
|
||
| func ykGetCardID(tx *scTx) (id CardID, err error) { | ||
| // https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-73-4.pdf#page=17 | ||
| // OID for CardId is 5FC102 | ||
|
|
||
| cmd := apdu{ | ||
| instruction: insGetData, | ||
| param1: 0x3f, | ||
| param2: 0xff, | ||
| data: []byte{ | ||
| 0x5c, // Tag list | ||
| 0x03, | ||
| 0x5f, | ||
| 0xc1, | ||
| 0x02, | ||
| }, | ||
| } | ||
| resp, err := tx.Transmit(cmd) | ||
| if err != nil { | ||
| return id, fmt.Errorf("command failed: %w", err) | ||
| } | ||
| // https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-73-4.pdf#page=85 | ||
| obj, _, err := unmarshalASN1(resp, 1, 0x13) // tag 0x53 | ||
| if err != nil { | ||
| return id, fmt.Errorf("unmarshaling response: %v", err) | ||
| } | ||
| id.Raw = obj | ||
| if obj[27] == 0x34 { | ||
|
||
| endPos := uuidOffset + int(obj[28]) | ||
|
||
| copy(id.GUID[:], obj[uuidOffset:endPos]) | ||
| } | ||
| return | ||
| } | ||
|
|
||
| // SetCardID initialize the CHUID card object using a predefined template defined as | ||
| // * Defined fields: | ||
|
||
| // - 0x30: FASC-N (hard-coded) | ||
| // - 0x34: Card UUID / GUID (settable) | ||
| // - 0x35: Exp. Date (hard-coded) | ||
| // - 0x3e: Signature (hard-coded, empty) | ||
| // - 0xfe: Error Detection Code (hard-coded) | ||
| func (yk *YubiKey) SetCardID(GUID [16]byte, key [24]byte) (CardID, error) { | ||
|
||
| return ykSetCardID(yk.tx, key, GUID) | ||
|
|
||
| } | ||
|
|
||
| func ykSetCardID(tx *scTx, key [24]byte, guid [16]byte) (id CardID, err error) { | ||
|
|
||
| id.Raw = make([]byte, len(chuidTemplate)) | ||
|
||
| copy(id.Raw, chuidTemplate) | ||
| copy(id.Raw[uuidOffset:], guid[:]) | ||
|
|
||
| data := append([]byte{ | ||
| 0x5c, // Tag list | ||
| 0x03, | ||
| 0x5f, | ||
| 0xc1, | ||
| 0x02, | ||
| }, marshalASN1(0x53, id.Raw)...) | ||
|
|
||
| cmd := apdu{ | ||
| instruction: insPutData, | ||
| param1: 0x3f, | ||
| param2: 0xff, | ||
| data: data, | ||
| } | ||
|
|
||
| if err := ykAuthenticate(tx, key, rand.Reader); err != nil { | ||
| return id, fmt.Errorf("authenticating with key: %w", err) | ||
| } | ||
|
|
||
| _, err = tx.Transmit(cmd) | ||
| if err != nil { | ||
| return id, fmt.Errorf("command failed: %w", err) | ||
| } | ||
|
|
||
| return | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make raw unexported and in a future PR add whatever other field you want to be able to inspect. FASC-N, Exp. Date, etc.
Though if you want to use the signature in the API in a follow up PR, I think I'd prefer something like:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed