-
Notifications
You must be signed in to change notification settings - Fork 0
/
ykoath.go
119 lines (99 loc) · 2.6 KB
/
ykoath.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
// SPDX-FileCopyrightText: 2018 Joern Barthel <[email protected]>
// SPDX-License-Identifier: Apache-2.0
package ykoath
import (
"crypto/rand"
"errors"
"fmt"
"io"
"time"
iso "cunicu.li/go-iso7816"
"cunicu.li/go-iso7816/encoding/tlv"
)
const (
DefaultTimeStep = 30 * time.Second
HMACMinimumKeySize = 14
)
// TLV tags for credential data
const (
tagName tlv.Tag = 0x71
tagNameList tlv.Tag = 0x72
tagKey tlv.Tag = 0x73
tagChallenge tlv.Tag = 0x74
tagResponse tlv.Tag = 0x75
tagTruncated tlv.Tag = 0x76
tagHOTP tlv.Tag = 0x77
tagProperty tlv.Tag = 0x78
tagVersion tlv.Tag = 0x79
tagImf tlv.Tag = 0x7A
tagAlgorithm tlv.Tag = 0x7B
tagTouch tlv.Tag = 0x7C
)
// Instruction bytes for commands
const (
insList iso.Instruction = 0xA1
insSelect iso.Instruction = 0xA4
insPut iso.Instruction = 0x01
insDelete iso.Instruction = 0x02
insSetCode iso.Instruction = 0x03
insReset iso.Instruction = 0x04
insRename iso.Instruction = 0x05
insCalculate iso.Instruction = 0xA2
insValidate iso.Instruction = 0xA3
insCalculateAll iso.Instruction = 0xA4
insSendRemaining iso.Instruction = 0xA5
)
// Card implements most parts of the TOTP portion of the YKOATH specification
// https://developers.yubico.com/Card/YKOATH_Protocol.html
type Card struct {
*iso.Card
Clock func() time.Time
Timestep time.Duration
Rand io.Reader
tx *iso.Transaction
}
var errUnknownTag = errors.New("unknown tag")
// NewCard initializes a new OATH card.
func NewCard(pcscCard iso.PCSCCard) (*Card, error) {
isoCard := iso.NewCard(pcscCard)
isoCard.InsGetRemaining = insSendRemaining
tx, err := isoCard.NewTransaction()
if err != nil {
return nil, fmt.Errorf("failed to initiate transaction: %w", err)
}
return &Card{
Card: isoCard,
Clock: time.Now,
Timestep: DefaultTimeStep,
Rand: rand.Reader,
tx: tx,
}, nil
}
// Close terminates an OATH session
func (c *Card) Close() error {
if c.tx != nil {
if err := c.tx.EndTransaction(); err != nil {
return err
}
}
return nil
}
func (c *Card) send(ins iso.Instruction, p1, p2 byte, tvsCmd ...tlv.TagValue) (tvsResp []tlv.TagValue, err error) {
data, err := tlv.EncodeSimple(tvsCmd...)
if err != nil {
return nil, fmt.Errorf("failed to encode command: %w", err)
}
res, err := c.Card.Send(&iso.CAPDU{
Ins: ins,
P1: p1,
P2: p2,
Data: data,
})
if err != nil {
return nil, wrapError(err)
}
if tvsResp, err = tlv.DecodeSimple(res); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return tvsResp, nil
}