@@ -29,6 +29,13 @@ type Argon2idParams struct {
29
29
Memory uint32
30
30
}
31
31
32
+ // Keyfields describes keyfile fields that must be preserved when a key is
33
+ // reencrypted.
34
+ type Keyfields struct {
35
+ Comment string
36
+ Fingerprint string
37
+ }
38
+
32
39
// GenerateKeys generates a random Streamlined NTRU Prime 4591^761
33
40
// public/secret key pair, writing the public key to pkw and secret key to skw.
34
41
// The secret key is encrypted with ChaCha20-Poly1305 using a symmetric key
@@ -76,35 +83,68 @@ func GenerateKeys(rand io.Reader, pkw, skw io.Writer, passphrase []byte, kdfp *A
76
83
77
84
// Write secret key
78
85
buf .Reset ()
86
+ kf := Keyfields {
87
+ Comment : comment ,
88
+ Fingerprint : fingerprint ,
89
+ }
90
+ err = writeSecretKey (buf , sk , kf , skKey , salt , time , memory , ncpu )
91
+ if err != nil {
92
+ return "" , err
93
+ }
94
+ _ , err = io .Copy (skw , buf )
95
+ if err != nil {
96
+ return "" , err
97
+ }
98
+
99
+ return fingerprint , nil
100
+ }
101
+
102
+ func writeSecretKey (buf * bytes.Buffer , sk * SecretKey , kf Keyfields , skKey []byte , salt []byte , time , memory uint32 , threads uint8 ) error {
79
103
fmt .Fprintf (buf , "ss encryption secret key\n " )
80
- fmt .Fprintf (buf , "comment: %s\n " , comment )
104
+ fmt .Fprintf (buf , "comment: %s\n " , kf . Comment )
81
105
fmt .Fprintf (buf , "cryptosystem: sntrup4591761\n " )
82
- fmt .Fprintf (buf , "fingerprint: %s\n " , fingerprint )
106
+ fmt .Fprintf (buf , "fingerprint: %s\n " , kf . Fingerprint )
83
107
fmt .Fprintf (buf , "encryption: argon2id-chacha20-poly1305\n " )
84
108
fmt .Fprintf (buf , "argon2id-salt: %s\n " , base64 .StdEncoding .EncodeToString (salt ))
85
109
fmt .Fprintf (buf , "argon2id-time: %d\n " , time )
86
110
fmt .Fprintf (buf , "argon2id-memory: %d\n " , memory )
87
- fmt .Fprintf (buf , "argon2id-threads: %d\n " , ncpu )
111
+ fmt .Fprintf (buf , "argon2id-threads: %d\n " , threads )
88
112
fmt .Fprintf (buf , "encoding: base64\n " )
89
113
// Everything above is Associated Data
90
114
data := buf .Bytes ()
91
115
fmt .Fprintf (buf , "\n " )
92
116
aead , err := chacha20poly1305 .New (skKey )
93
117
if err != nil {
94
- return "" , err
118
+ return err
95
119
}
96
120
nonce := make ([]byte , aead .NonceSize ())
97
121
skCiphertext := aead .Seal (nil , nonce , sk [:], data )
98
- enc = base64 .NewEncoder (base64 .StdEncoding , buf )
122
+ enc : = base64 .NewEncoder (base64 .StdEncoding , buf )
99
123
enc .Write (skCiphertext )
100
124
enc .Close ()
101
125
fmt .Fprintf (buf , "\n " )
102
- _ , err = io .Copy (skw , buf )
126
+ return nil
127
+ }
128
+
129
+ // EncryptSecretKey writes the secret key encrypted in keyfile format to skw.
130
+ func EncryptSecretKey (rand io.Reader , skw io.Writer , sk * SecretKey , passphrase []byte , kdfp * Argon2idParams , kf Keyfields ) error {
131
+ salt := make ([]byte , saltsize )
132
+ _ , err := rand .Read (salt )
103
133
if err != nil {
104
- return "" , err
134
+ return err
105
135
}
136
+ ncpu := uint8 (runtime .NumCPU ())
137
+ time := kdfp .Time
138
+ memory := kdfp .Memory
139
+ skKey := argon2 .IDKey (passphrase , salt , time , memory , ncpu , chacha20poly1305 .KeySize )
106
140
107
- return fingerprint , nil
141
+ buf := new (bytes.Buffer )
142
+ err = writeSecretKey (buf , sk , kf , skKey , salt , time , memory , ncpu )
143
+ if err != nil {
144
+ return err
145
+ }
146
+ _ , err = io .Copy (skw , buf )
147
+ return err
108
148
}
109
149
110
150
func readKeyFile (r io.Reader , firstLine string ) (fields map [string ]string , ad []byte , encodedKey string , err error ) {
@@ -200,53 +240,60 @@ func ReadPublicKey(r io.Reader) (*PublicKey, error) {
200
240
201
241
// OpenSecretKey reads and decrypts an encryted Streamlined NTRU Prime 4591^761
202
242
// secret key in the keyfile format from r.
203
- func OpenSecretKey (r io.Reader , passphrase []byte ) (* SecretKey , error ) {
243
+ func OpenSecretKey (r io.Reader , passphrase []byte ) (_ * SecretKey , _ Keyfields , err error ) {
244
+ e := func (err error ) (* SecretKey , Keyfields , error ) {
245
+ return nil , Keyfields {}, err
246
+ }
247
+
204
248
fields , keyAD , encodedSealedKey , err := readKeyFile (r , "ss encryption secret key" )
205
249
if err != nil {
206
- return nil , err
250
+ return
207
251
}
208
252
sealedKey , err := base64 .StdEncoding .DecodeString (encodedSealedKey )
209
253
if err != nil {
210
- return nil , err
254
+ return
211
255
}
212
256
err = requireFields (fields , map [string ]string {
213
257
"cryptosystem" : "sntrup4591761" ,
214
258
"encryption" : "argon2id-chacha20-poly1305" ,
215
259
"encoding" : "base64" ,
216
260
})
217
261
if err != nil {
218
- return nil , err
262
+ return
219
263
}
220
264
salt , err := base64 .StdEncoding .DecodeString (fields ["argon2id-salt" ])
221
265
if err != nil {
222
- return nil , err
266
+ return
223
267
}
224
268
time , err := strconv .ParseUint (fields ["argon2id-time" ], 10 , 32 )
225
269
if err != nil {
226
- return nil , fmt .Errorf ("argon2id-time: %w" , err )
270
+ return e ( fmt .Errorf ("argon2id-time: %w" , err ) )
227
271
}
228
272
memory , err := strconv .ParseUint (fields ["argon2id-memory" ], 10 , 32 )
229
273
if err != nil {
230
- return nil , fmt .Errorf ("argon2id-memory: %w" , err )
274
+ return e ( fmt .Errorf ("argon2id-memory: %w" , err ) )
231
275
}
232
276
ncpu , err := strconv .ParseUint (fields ["argon2id-threads" ], 10 , 8 )
233
277
if err != nil {
234
- return nil , fmt .Errorf ("argon2id-threads: %w" , err )
278
+ return e ( fmt .Errorf ("argon2id-threads: %w" , err ) )
235
279
}
236
280
derivedKey := argon2 .IDKey (passphrase , salt , uint32 (time ), uint32 (memory ), uint8 (ncpu ), chacha20poly1305 .KeySize )
237
281
aead , err := chacha20poly1305 .New (derivedKey )
238
282
if err != nil {
239
- return nil , err
283
+ return
240
284
}
241
285
skNonce := make ([]byte , aead .NonceSize ())
242
286
key , err := aead .Open (sealedKey [:0 ], skNonce , sealedKey , keyAD )
243
287
if err != nil {
244
- return nil , err
288
+ return
245
289
}
246
290
sk := new (SecretKey )
247
291
if len (key ) != len (sk ) {
248
- return nil , fmt .Errorf ("secret key has invalid length %d" , len (key ))
292
+ return e ( fmt .Errorf ("secret key has invalid length %d" , len (key ) ))
249
293
}
250
294
copy (sk [:], key )
251
- return sk , nil
295
+ var kf Keyfields
296
+ kf .Comment = fields ["comment" ]
297
+ kf .Fingerprint = fields ["fingerprint" ]
298
+ return sk , kf , nil
252
299
}
0 commit comments