forked from usbarmory/armory-drive
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkeyring.go
228 lines (179 loc) · 4.52 KB
/
keyring.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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
// Copyright (c) F-Secure Corporation
// https://foundry.f-secure.com
//
// Use of this source code is governed by the license
// that can be found in the LICENSE file.
package main
import (
"crypto/cipher"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"crypto/x509"
"errors"
"io"
"golang.org/x/crypto/hkdf"
"golang.org/x/crypto/xts"
)
// DCP key RAM indices
const (
BLOCK_KEY = iota
ESSIV_KEY
SNVS_KEY
)
// BLE key indices
const (
UA_LONGTERM_KEY = iota
UA_EPHEMERAL_KEY
MD_LONGTERM_KEY
MD_EPHEMERAL_KEY
)
type Keyring struct {
// CPU bound ESSIV cipher
cbiv cipher.Block
// CPU bound block cipher
cb cipher.Block
// CPU bound xts block cipher
cbxts *xts.Cipher
// IV encryption key for ESSIV computation
salt []byte
// persistent storage encryption key
snvs []byte
// long term BLE peer authentication keys
ArmoryLongterm *ecdsa.PrivateKey
MobileLongterm *ecdsa.PublicKey
// ephemeral BLE peer session keys
armoryEphemeral *ecdsa.PrivateKey
mobileEphemeral *ecdsa.PublicKey
// BLE shared pre-master secret
preMaster []byte
// BLE shared session key
sessionKey []byte
}
var keyring = &Keyring{}
func (keyring *Keyring) Init(overwrite bool) (err error) {
// derive persistent storage encryption key
if keyring.snvs, err = deriveKey([]byte(SNVS_DIV), SNVS_KEY, true); err != nil {
return
}
conf, err = LoadConfiguration()
if err != nil || overwrite {
var armoryLongterm []byte
if keyring.ArmoryLongterm == nil {
err = keyring.NewLongtermKey()
if err != nil {
return
}
}
armoryLongterm, err = keyring.Export(UA_LONGTERM_KEY, true)
if err != nil {
return
}
conf = &PersistentConfiguration{
ArmoryLongterm: armoryLongterm,
Settings: &Configuration{
Cipher: Cipher_AES128_CBC_PLAIN,
},
}
err = conf.save()
if err != nil {
return
}
}
err = keyring.Import(UA_LONGTERM_KEY, true, conf.ArmoryLongterm)
if err != nil {
return
}
// we might not be paired yet, so ignore errors
keyring.Import(MD_LONGTERM_KEY, false, conf.MobileLongterm)
// Derive salt, used for ESSIV computation as well as BLOCK_KEY derivation.
if keyring.salt, err = deriveKey([]byte(ESSIV_DIV), ESSIV_KEY, true); err != nil {
return
}
return
}
func (keyring *Keyring) Export(index int, private bool) ([]byte, error) {
var pubKey *ecdsa.PublicKey
var privKey *ecdsa.PrivateKey
switch index {
case UA_LONGTERM_KEY:
privKey = keyring.ArmoryLongterm
case UA_EPHEMERAL_KEY:
privKey = keyring.armoryEphemeral
case MD_LONGTERM_KEY:
pubKey = keyring.MobileLongterm
case MD_EPHEMERAL_KEY:
pubKey = keyring.mobileEphemeral
default:
return nil, errors.New("invalid key index")
}
if !private && pubKey == nil && privKey != nil {
pubKey = &privKey.PublicKey
}
if private {
if privKey == nil {
return nil, errors.New("invalid key")
}
return x509.MarshalECPrivateKey(privKey)
} else {
if pubKey == nil {
return nil, errors.New("invalid key")
}
return x509.MarshalPKIXPublicKey(pubKey)
}
}
func (keyring *Keyring) Import(index int, private bool, der []byte) (err error) {
var pubKey *ecdsa.PublicKey
var privKey *ecdsa.PrivateKey
if private {
privKey, err = x509.ParseECPrivateKey(der)
} else {
var k interface{}
k, err = x509.ParsePKIXPublicKey(der)
if err == nil {
switch key := k.(type) {
case *ecdsa.PublicKey:
pubKey = key
default:
return errors.New("incompatible key type")
}
}
}
if err != nil {
return
}
switch index {
case UA_LONGTERM_KEY:
keyring.ArmoryLongterm = privKey
case MD_LONGTERM_KEY:
keyring.MobileLongterm = pubKey
case MD_EPHEMERAL_KEY:
keyring.mobileEphemeral = pubKey
default:
return errors.New("invalid key index")
}
return
}
func (keyring *Keyring) NewLongtermKey() (err error) {
keyring.ArmoryLongterm, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
return
}
func (keyring *Keyring) NewSessionKeys(nonce []byte) (err error) {
keyring.armoryEphemeral, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return
}
peerX := keyring.mobileEphemeral.X
peerY := keyring.mobileEphemeral.Y
privX := keyring.armoryEphemeral.D.Bytes()
length := (keyring.mobileEphemeral.Params().BitSize + 7) >> 3
keyring.preMaster = make([]byte, length)
s, _ := keyring.mobileEphemeral.ScalarMult(peerX, peerY, privX)
shared := s.Bytes()
copy(keyring.preMaster[len(keyring.preMaster)-len(shared):], shared)
hkdf := hkdf.New(sha256.New, keyring.preMaster, nonce, nil)
keyring.sessionKey = make([]byte, 32)
_, err = io.ReadFull(hkdf, keyring.sessionKey)
return
}