generated from cunicu/skeleton
-
Notifications
You must be signed in to change notification settings - Fork 1
/
device_info.go
204 lines (166 loc) · 4.78 KB
/
device_info.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// SPDX-FileCopyrightText: 2023 Steffen Vogel <post@steffenvogel.de>
// SPDX-License-Identifier: Apache-2.0
package yubikey
import (
"encoding/binary"
"fmt"
"time"
iso "cunicu.li/go-iso7816"
"cunicu.li/go-iso7816/encoding/tlv"
)
const (
TagCapsSupportedUSB tlv.Tag = 0x01
TagSerialNumber tlv.Tag = 0x02
TagCapsEnabledUSB tlv.Tag = 0x03
TagFormFactor tlv.Tag = 0x04
TagFirmwareVersion tlv.Tag = 0x05
TagAutoEjectTimeout tlv.Tag = 0x06
TagChalRespTimeout tlv.Tag = 0x07
TagDeviceFlags tlv.Tag = 0x08
TagAppVersions tlv.Tag = 0x09
TagConfigLock tlv.Tag = 0x0a
TagUnlock tlv.Tag = 0x0b
TagReboot tlv.Tag = 0x0c
TagCapsSupportedNFC tlv.Tag = 0x0d
TagCapsEnabledNFC tlv.Tag = 0x0e
)
type DeviceFlag byte
const (
DeviceFlagRemoteWakeup DeviceFlag = 0x40
DeviceFlagEject DeviceFlag = 0x80
)
type Capability int
const (
CapOTP Capability = 0x01
CapU2F Capability = 0x02
CapFIDO2 Capability = 0x200
CapOATH Capability = 0x20
CapPIV Capability = 0x10
CapOpenPGP Capability = 0x08
CapHSMAUTH Capability = 0x100
)
type FormFactor byte
const (
FormFactorUnknown FormFactor = 0x00
FormFactorUSBAKeychain FormFactor = 0x01
FormFactorUSBANano FormFactor = 0x02
FormFactorUSBCKeychain FormFactor = 0x03
FormFactorUSBCNano FormFactor = 0x04
FormFactorUSBCLightning FormFactor = 0x05
FormFactorUSBABio FormFactor = 0x06
FormFactorUSBCBio FormFactor = 0x07
)
type DeviceInfo struct {
Flags DeviceFlag
CapsSupportedUSB Capability
CapsEnabledUSB Capability
CapsSupportedNFC Capability
CapsEnabledNFC Capability
SerialNumber uint32
FirmwareVersion iso.Version
FormFactor FormFactor
AutoEjectTimeout time.Duration
ChalRespTimeout time.Duration
IsLocked bool
IsSky bool
IsFIPS bool
}
// nolint: gocognit
func (di *DeviceInfo) Unmarshal(b []byte) error {
tvs, err := tlv.DecodeSimple(b[1:])
if err != nil {
return fmt.Errorf("failed to decode response: %w", err)
}
readCap := func(tv tlv.TagValue) (Capability, error) {
switch len(tv.Value) {
case 1: // For Yubikey 4.x
return Capability(tv.Value[0]), nil
case 2:
return Capability(binary.BigEndian.Uint16(tv.Value)), nil
default:
return 0, ErrInvalidResponseLength
}
}
for _, tv := range tvs {
switch tv.Tag {
case TagCapsSupportedUSB:
if di.CapsSupportedNFC, err = readCap(tv); err != nil {
return fmt.Errorf("%w: CapsSupportedUSB", err)
}
case TagCapsEnabledUSB:
if di.CapsEnabledUSB, err = readCap(tv); err != nil {
return fmt.Errorf("%w: CapsEnabledUSB", err)
}
case TagCapsSupportedNFC:
if len(tv.Value) != 2 {
return fmt.Errorf("%w: CapsSupportedNFC", ErrInvalidResponseLength)
}
di.CapsSupportedNFC = Capability(binary.BigEndian.Uint16(tv.Value))
case TagCapsEnabledNFC:
if len(tv.Value) != 2 {
return fmt.Errorf("%w: CapsEnabledNFC", ErrInvalidResponseLength)
}
di.CapsEnabledNFC = Capability(binary.BigEndian.Uint16(tv.Value))
case TagSerialNumber:
if len(tv.Value) != 4 {
return fmt.Errorf("%w: SerialNumber", ErrInvalidResponseLength)
}
di.SerialNumber = binary.BigEndian.Uint32(tv.Value)
case TagFormFactor:
if len(tv.Value) != 1 {
return fmt.Errorf("%w: FormFactor", ErrInvalidResponseLength)
}
di.FormFactor = FormFactor(tv.Value[0] & 0xf)
di.IsFIPS = tv.Value[0]&0x80 != 0
di.IsSky = tv.Value[0]&0x40 != 0
case TagFirmwareVersion:
if len(tv.Value) != 3 {
return fmt.Errorf("%w: FirmwareVersion", ErrInvalidResponseLength)
}
di.FirmwareVersion = iso.Version{
Major: int(tv.Value[0]),
Minor: int(tv.Value[1]),
Patch: int(tv.Value[2]),
}
case TagAutoEjectTimeout:
if len(tv.Value) != 2 {
return fmt.Errorf("%w: AutoEjectTimeout", ErrInvalidResponseLength)
}
di.AutoEjectTimeout = time.Second * time.Duration(binary.BigEndian.Uint16(tv.Value))
case TagChalRespTimeout:
if len(tv.Value) != 1 {
return fmt.Errorf("%w: ChalRespTimeout", ErrInvalidResponseLength)
}
di.ChalRespTimeout = time.Second * time.Duration(tv.Value[0])
case TagDeviceFlags:
if len(tv.Value) != 1 {
return fmt.Errorf("%w: DeviceFlags", ErrInvalidResponseLength)
}
di.Flags = DeviceFlag(tv.Value[0])
case TagAppVersions:
// TODO
case TagConfigLock:
if len(tv.Value) != 1 {
return fmt.Errorf("%w: ConfigLock", ErrInvalidResponseLength)
}
di.IsLocked = tv.Value[0] != 0
}
}
return nil
}
// GetDeviceInfo returns device information about the YubiKey token.
func GetDeviceInfo(c *iso.Card) (*DeviceInfo, error) {
resp, err := c.Send(&iso.CAPDU{
Ins: 0x1D,
P1: 0x00,
P2: 0x00,
})
if err != nil {
return nil, err
}
di := &DeviceInfo{}
if err := di.Unmarshal(resp); err != nil {
return nil, err
}
return di, nil
}