@@ -106,6 +106,8 @@ func (p PrivateKey) Equal(x crypto.PrivateKey) bool {
106106//
107107// For password-protected private keys refer to [EncryptKey].
108108func (p PrivateKey ) MarshalText () ([]byte , error ) {
109+ // A non-encrypted private key has the same format as an encrypted one.
110+ // However, the salt, and auth. tag are set to all zero.
109111 var b [privateKeySize ]byte
110112
111113 binary .LittleEndian .PutUint16 (b [:], EdDSA )
@@ -115,6 +117,8 @@ func (p PrivateKey) MarshalText() ([]byte, error) {
115117 binary .LittleEndian .PutUint64 (b [54 :], p .id )
116118 copy (b [62 :], p .bytes [:])
117119
120+ // It seems odd that the comment says: "encrypted secret key".
121+ // However, the original C implementation behaves like this.
118122 const comment = "untrusted comment: minisign encrypted secret key\n "
119123 encodedBytes := make ([]byte , len (comment )+ base64 .StdEncoding .EncodedLen (len (b )))
120124 copy (encodedBytes , []byte (comment ))
@@ -140,7 +144,7 @@ func (p *PrivateKey) UnmarshalText(text []byte) error {
140144 }
141145
142146 var (
143- empty [32 ]byte
147+ empty [32 ]byte // For checking that the salt/tag are empty
144148
145149 kType = binary .LittleEndian .Uint16 (b )
146150 kdf = binary .LittleEndian .Uint16 (b [2 :])
@@ -149,7 +153,7 @@ func (p *PrivateKey) UnmarshalText(text []byte) error {
149153 scryptOps = binary .LittleEndian .Uint64 (b [38 :])
150154 scryptMem = binary .LittleEndian .Uint64 (b [46 :])
151155 key = b [54 :126 ]
152- checksum = b [126 :privateKeySize ]
156+ tag = b [126 :privateKeySize ]
153157 )
154158 if kType != EdDSA {
155159 return fmt .Errorf ("minisign: invalid private key: invalid key type '%d'" , kType )
@@ -163,7 +167,7 @@ func (p *PrivateKey) UnmarshalText(text []byte) error {
163167 if hType != algorithmBlake2b {
164168 return fmt .Errorf ("minisign: invalid private key: invalid hash type '%d'" , hType )
165169 }
166- if ! bytes .Equal (salt [:] , empty [:]) {
170+ if ! bytes .Equal (salt , empty [:]) {
167171 return errors .New ("minisign: invalid private key: salt is not empty" )
168172 }
169173 if scryptOps != 0 {
@@ -172,11 +176,11 @@ func (p *PrivateKey) UnmarshalText(text []byte) error {
172176 if scryptMem != 0 {
173177 return errors .New ("minisign: invalid private key: scrypt mem parameter is not zero" )
174178 }
175- if ! bytes .Equal (checksum , empty [:]) {
179+ if ! bytes .Equal (tag , empty [:]) {
176180 return errors .New ("minisign: invalid private key: salt is not empty" )
177181 }
178182
179- p .id = binary .LittleEndian .Uint64 (key [: 8 ] )
183+ p .id = binary .LittleEndian .Uint64 (key )
180184 copy (p .bytes [:], key [8 :])
181185 return nil
182186}
@@ -235,10 +239,7 @@ func IsEncrypted(privateKey []byte) bool {
235239 }
236240 bytes = bytes [:n ]
237241
238- if len (bytes ) != privateKeySize {
239- return false
240- }
241- return binary .LittleEndian .Uint16 (bytes [2 :4 ]) == algorithmScrypt
242+ return len (bytes ) >= 4 && binary .LittleEndian .Uint16 (bytes [2 :]) == algorithmScrypt
242243}
243244
244245var errDecrypt = errors .New ("minisign: decryption failed" )
@@ -247,47 +248,50 @@ var errDecrypt = errors.New("minisign: decryption failed")
247248// the given password.
248249func DecryptKey (password string , privateKey []byte ) (PrivateKey , error ) {
249250 privateKey = trimUntrustedComment (privateKey )
250- bytes := make ([]byte , base64 .StdEncoding .DecodedLen (len (privateKey )))
251- n , err := base64 .StdEncoding .Decode (bytes , privateKey )
251+ b := make ([]byte , base64 .StdEncoding .DecodedLen (len (privateKey )))
252+ n , err := base64 .StdEncoding .Decode (b , privateKey )
252253 if err != nil {
253254 return PrivateKey {}, err
254255 }
255- bytes = bytes [:n ]
256+ b = b [:n ]
256257
257- if len (bytes ) != privateKeySize {
258+ if len (b ) != privateKeySize {
258259 return PrivateKey {}, errDecrypt
259260 }
260- if a := binary .LittleEndian .Uint16 (bytes [:2 ]); a != EdDSA {
261+ var (
262+ kType = binary .LittleEndian .Uint16 (b )
263+ kdf = binary .LittleEndian .Uint16 (b [2 :])
264+ hType = binary .LittleEndian .Uint16 (b [4 :])
265+ salt = b [6 :38 ]
266+ scryptOps = binary .LittleEndian .Uint64 (b [38 :])
267+ scryptMem = binary .LittleEndian .Uint64 (b [46 :])
268+ ciphertext = b [54 :]
269+ )
270+ if kType != EdDSA {
261271 return PrivateKey {}, errDecrypt
262272 }
263- if a := binary . LittleEndian . Uint16 ( bytes [ 2 : 4 ]); a != algorithmScrypt {
273+ if kdf != algorithmScrypt {
264274 return PrivateKey {}, errDecrypt
265275 }
266- if a := binary . LittleEndian . Uint16 ( bytes [ 4 : 6 ]); a != algorithmBlake2b {
276+ if hType != algorithmBlake2b {
267277 return PrivateKey {}, errDecrypt
268278 }
269-
270- var (
271- scryptOps = binary .LittleEndian .Uint64 (bytes [38 :46 ])
272- scryptMem = binary .LittleEndian .Uint64 (bytes [46 :54 ])
273- )
274279 if scryptOps > scryptOpsLimit {
275280 return PrivateKey {}, errDecrypt
276281 }
277282 if scryptMem > scryptMemLimit {
278283 return PrivateKey {}, errDecrypt
279284 }
280- var salt [32 ]byte
281- copy (salt [:], bytes [6 :38 ])
282- privateKeyBytes , err := decryptKey (password , salt [:], scryptOps , scryptMem , bytes [54 :])
285+
286+ plaintext , err := decryptKey (password , salt , scryptOps , scryptMem , ciphertext )
283287 if err != nil {
284288 return PrivateKey {}, err
285289 }
286290
287291 key := PrivateKey {
288- id : binary .LittleEndian .Uint64 (privateKeyBytes [: 8 ] ),
292+ id : binary .LittleEndian .Uint64 (plaintext ),
289293 }
290- copy (key .bytes [:], privateKeyBytes [8 :])
294+ copy (key .bytes [:], plaintext [8 :])
291295 return key , nil
292296}
293297
@@ -368,7 +372,7 @@ func decryptKey(password string, salt []byte, ops, mem uint64, ciphertext []byte
368372 binary .LittleEndian .PutUint16 (message [:2 ], EdDSA )
369373 copy (message [2 :], privateKeyBytes )
370374
371- if sum := blake2b .Sum256 (message [:]); subtle .ConstantTimeCompare (sum [:], checksum [:] ) != 1 {
375+ if sum := blake2b .Sum256 (message [:]); subtle .ConstantTimeCompare (sum [:], checksum ) != 1 {
372376 return nil , errDecrypt
373377 }
374378 return privateKeyBytes , nil
0 commit comments