-
Notifications
You must be signed in to change notification settings - Fork 0
/
auth.go
168 lines (142 loc) · 4.46 KB
/
auth.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
// SPDX-FileCopyrightText: 2023-2024 Steffen Vogel <[email protected]>
// SPDX-License-Identifier: Apache-2.0
package openpgp
import (
"errors"
"fmt"
iso "cunicu.li/go-iso7816"
"cunicu.li/go-iso7816/encoding/tlv"
)
// VerifyPassword attempts to unlock a given password.
//
// Access condition: Always
// See: OpenPGP Smart Card Application - Section 7.2.2 VERIFY
func (c *Card) VerifyPassword(pwType byte, pw string) (err error) {
var pwBuf []byte
if c.kdf == nil {
pwBuf = []byte(pw)
} else {
if pwBuf, err = c.kdf.DerivePassword(pwType, pw); err != nil {
return fmt.Errorf("failed to derive password: %w", err)
}
}
_, err = send(c.tx, iso.InsVerify, 0x00, pwType, pwBuf)
return err
}
// ClearPasswordState clears the passwort unlock state from the card.
//
// Access condition: Always
// Note: Appears to be broken on YubiKey 5
// See: OpenPGP Smart Card Application - Section 7.2.2 VERIFY
func (c *Card) ClearPasswordState(pwType byte) error {
_, err := send(c.tx, iso.InsVerify, 0xff, pwType, nil)
return err
}
// PasswordState returns true if the given password is unlocked.
//
// Access condition: Always
// Note: Appears to be broken on YubiKey 5
// See: OpenPGP Smart Card Application - Section 7.2.2 VERIFY
func (c *Card) PasswordState(pwType byte) (bool, error) {
_, err := send(c.tx, iso.InsVerify, 0x00, pwType, nil)
var aErr *AuthError
if errors.Is(err, iso.ErrSuccess) {
return true, nil
} else if errors.As(err, &aErr) {
return false, nil
}
return false, err
}
// ChangePassword changes the user or admin password.
//
// Access condition: Always
// Access level: None (current password must be provided)
// See: OpenPGP Smart Card Application - Section 7.2.3 CHANGE REFERENCE DATA
func (c *Card) ChangePassword(pwType byte, pwCurrent, pwNew string) error {
switch pwType {
case PW1:
if len(pwNew) < 6 || len(pwNew) > int(c.PasswordStatus.LengthPW1) {
return ErrInvalidLength
}
case PW3:
if len(pwNew) < 8 || len(pwNew) > int(c.PasswordStatus.LengthPW3) {
return ErrInvalidLength
}
default:
return ErrUnsupported
}
_, err := send(c.tx, iso.InsChangeReferenceData, 0x00, pwType, []byte(pwCurrent+pwNew))
return err
}
// ChangeResettingCode sets the resetting code of the cards.
//
// Access condition: Admin/PW3
// See: OpenPGP Smart Card Application - Section 4.3.4 Resetting Code
func (c *Card) ChangeResettingCode(rc string) error {
if len(rc) < 8 || len(rc) > int(c.PasswordStatus.LengthRC) {
return ErrInvalidLength
}
return c.putData(tagResettingCode, []byte(rc))
}
func (c *Card) ClearResettingCode() error {
return c.putData(tagResettingCode, nil)
}
// ResetRetryCounter reset the PIN retry counter and a new password.
//
// Access condition: Admin/PW3
// See: OpenPGP Smart Card Application - Section 7.2.4 RESET RETRY COUNTER
func (c *Card) ResetRetryCounter(newPw string) error {
if len(newPw) < 6 {
return ErrInvalidLength
}
_, err := send(c.tx, iso.InsResetRetryCounter, 0x02, PW1, []byte(newPw))
return err
}
// ResetRetryCounterWithResettingCode resets the PIN retry counter using a reset code.
//
// Access condition: None (reset code is required)
// See: OpenPGP Smart Card Application - Section 7.2.4 RESET RETRY COUNTER
func (c *Card) ResetRetryCounterWithResettingCode(rc, newPw string) error {
if len(newPw) < 6 {
return ErrInvalidLength
}
_, err := send(c.tx, iso.InsResetRetryCounter, 0x00, PW1, []byte(rc+newPw))
return err
}
// SetRetryCounters sets the number of PIN attempts to allow before blocking.
//
// Access condition: Admin/PW3
// Note: This is a YubiKey extensions
// Warning: On YubiKey NEO this will reset the PINs to their default values.
func (c *Card) SetRetryCounters(pw1, rc, pw3 byte) error {
_, err := send(c.tx, insSetPINRetries, 0, 0, []byte{pw1, rc, pw3})
return err
}
func (c *Card) SetUserInteractionMode(op SecurityOperation, mode UserInteractionMode, feat GeneralFeatures) error {
uif := UIF{mode, feat}
return c.putData(tagUIFSign+tlv.Tag(op), uif.Encode())
}
type PasswordMode struct {
RequirePW1ForEachSignature bool
UsePINBlockFormat2ForPW1 bool
}
func (c *Card) SetPasswordMode(mode PasswordMode) error {
sts, err := c.getData(tagPasswordStatus)
if err != nil {
return err
}
if mode.RequirePW1ForEachSignature {
sts[0] = 0
} else {
sts[0] = 1
}
if mode.UsePINBlockFormat2ForPW1 {
if c.Capabilities.Pin2BlockFormat == 0 {
return fmt.Errorf("PIN block 2 format is %w", ErrUnsupported)
}
sts[1] |= 0b1
} else {
sts[1] &= 0b1
}
return c.putData(tagPasswordStatus, sts)
}