-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpin.go
106 lines (83 loc) · 2.24 KB
/
pin.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
// SPDX-FileCopyrightText: 2023 Steffen Vogel <post@steffenvogel.de>
// SPDX-License-Identifier: Apache-2.0
package ykoath
import (
"bytes"
"crypto/hmac"
"errors"
"fmt"
"cunicu.li/go-iso7816/encoding/tlv"
"golang.org/x/crypto/pbkdf2"
)
var errTokenResponse = errors.New("invalid token response")
func (c *Card) RemoveCode() error {
_, err := c.send(insSetCode, 0x00, 0x00, tlv.New(tagKey))
return err
}
// SetCode sets a new PIN.
// This command no authentication.
func (c *Card) SetCode(code []byte, alg Algorithm) error {
sel, err := c.Select()
if err != nil {
return err
}
key := pbkdf2.Key(code, sel.Name, 1000, 16, alg.Hash())
myChallenge := make([]byte, 8)
if _, err := c.Rand.Read(myChallenge); err != nil {
return fmt.Errorf("failed to generate challenge: %w", err)
}
mac := hmac.New(alg.Hash(), key)
mac.Write(myChallenge)
myResponse := mac.Sum(nil)
algKey := append([]byte{byte(alg)}, key...)
_, err = c.send(insSetCode, 0x00, 0x00,
tlv.New(tagKey, algKey),
tlv.New(tagChallenge, myChallenge),
tlv.New(tagResponse, myResponse),
)
return err
}
// Reset resets the application to just-installed state.
// This command requires no authentication.
func (c *Card) Validate(code []byte) error {
var myChallenge, tokenResponse, tokenResponseExpected []byte
sel, err := c.Select()
if err != nil {
return err
}
if len(sel.Algorithm) < 1 || len(sel.Name) < 1 {
return ErrNoSuchObject
}
tokenChallenge := sel.Challenge
alg := Algorithm(sel.Algorithm[0])
key := pbkdf2.Key(code, sel.Name, 1000, 16, alg.Hash())
mac := hmac.New(alg.Hash(), key)
mac.Write(tokenChallenge)
myResponse := mac.Sum(nil)
myChallenge = make([]byte, 8)
if _, err := c.Rand.Read(myChallenge); err != nil {
return fmt.Errorf("failed to generate challenge: %w", err)
}
tvs, err := c.send(insValidate, 0x00, 0x00,
tlv.New(tagResponse, myResponse),
tlv.New(tagChallenge, myChallenge),
)
if err != nil {
return err
}
for _, tv := range tvs {
if tv.Tag == tagResponse {
tokenResponse = tv.Value
}
}
if tokenResponse == nil {
return errTokenResponse
}
mac.Reset()
mac.Write(myChallenge)
tokenResponseExpected = mac.Sum(nil)
if !bytes.Equal(tokenResponse, tokenResponseExpected) {
return errTokenResponse
}
return nil
}