Skip to content

Commit d7733dc

Browse files
lubuxtwiss
andauthored
Validate argon2 params on read (#246)
Argon2 s2k parameters were previously not validated on the OpenPGP side before invoking Argon2, which could lead to potential panics during runtime. This commit introduces a validation step for these parameters before calling Argon2, ensuring that invalid inputs are caught early and an appropriate error is returned instead of risking a panic. Co-authored-by: Daniel Huigens <[email protected]>
1 parent c0ca2b8 commit d7733dc

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

openpgp/s2k/s2k.go

+24-2
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,8 @@ func Generate(rand io.Reader, c *Config) (*Params, error) {
199199
}
200200

201201
params = &Params{
202-
mode: SaltedS2K,
203-
hashId: hashId,
202+
mode: SaltedS2K,
203+
hashId: hashId,
204204
}
205205
} else { // Enforce IteratedSaltedS2K method otherwise
206206
hashId, ok := algorithm.HashToHashId(c.hash())
@@ -283,6 +283,9 @@ func ParseIntoParams(r io.Reader) (params *Params, err error) {
283283
params.passes = buf[Argon2SaltSize]
284284
params.parallelism = buf[Argon2SaltSize+1]
285285
params.memoryExp = buf[Argon2SaltSize+2]
286+
if err := validateArgon2Params(params); err != nil {
287+
return nil, err
288+
}
286289
return params, nil
287290
case GnuS2K:
288291
// This is a GNU extension. See
@@ -412,3 +415,22 @@ func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte, c *Co
412415
f(key, passphrase)
413416
return nil
414417
}
418+
419+
// validateArgon2Params checks that the argon2 parameters are valid according to RFC9580.
420+
func validateArgon2Params(params *Params) error {
421+
// The number of passes t and the degree of parallelism p MUST be non-zero.
422+
if params.parallelism == 0 {
423+
return errors.StructuralError("invalid argon2 params: parallelism is 0")
424+
}
425+
if params.passes == 0 {
426+
return errors.StructuralError("invalid argon2 params: iterations is 0")
427+
}
428+
429+
// The encoded memory size MUST be a value from 3+ceil(log2(p)) to 31,
430+
// such that the decoded memory size m is a value from 8*p to 2^31.
431+
if params.memoryExp > 31 || decodeMemory(params.memoryExp) < 8*uint32(params.parallelism) {
432+
return errors.StructuralError("invalid argon2 params: memory is out of bounds")
433+
}
434+
435+
return nil
436+
}

openpgp/s2k/s2k_test.go

+46
Original file line numberDiff line numberDiff line change
@@ -276,3 +276,49 @@ func testSerializeConfigOK(t *testing.T, c *Config) *Params {
276276

277277
return params
278278
}
279+
280+
func TestValidateArgon2Params(t *testing.T) {
281+
tests := []struct {
282+
params Params
283+
wantErr bool
284+
}{
285+
{
286+
params: Params{parallelism: 4, passes: 3, memoryExp: 6},
287+
wantErr: false,
288+
},
289+
{
290+
params: Params{parallelism: 0, passes: 3, memoryExp: 6},
291+
wantErr: true,
292+
},
293+
{
294+
params: Params{parallelism: 4, passes: 0, memoryExp: 6},
295+
wantErr: true,
296+
},
297+
{
298+
params: Params{parallelism: 4, passes: 3, memoryExp: 4},
299+
wantErr: true,
300+
},
301+
{
302+
params: Params{parallelism: 4, passes: 3, memoryExp: 32},
303+
wantErr: true,
304+
},
305+
{
306+
params: Params{parallelism: 4, passes: 3, memoryExp: 5},
307+
wantErr: false,
308+
},
309+
{
310+
params: Params{parallelism: 4, passes: 3, memoryExp: 31},
311+
wantErr: false,
312+
},
313+
}
314+
315+
for _, tt := range tests {
316+
err := validateArgon2Params(&tt.params)
317+
if tt.wantErr && err == nil {
318+
t.Errorf("validateArgon2Params: expected an error")
319+
}
320+
if !tt.wantErr && err != nil {
321+
t.Error("validateArgon2Params: expected no error")
322+
}
323+
}
324+
}

0 commit comments

Comments
 (0)