Skip to content

Commit

Permalink
Validate argon2 params on read (#246)
Browse files Browse the repository at this point in the history
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 <d.huigens@protonmail.com>
  • Loading branch information
lubux and twiss authored Nov 11, 2024
1 parent c0ca2b8 commit d7733dc
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 2 deletions.
26 changes: 24 additions & 2 deletions openpgp/s2k/s2k.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,8 @@ func Generate(rand io.Reader, c *Config) (*Params, error) {
}

params = &Params{
mode: SaltedS2K,
hashId: hashId,
mode: SaltedS2K,
hashId: hashId,
}
} else { // Enforce IteratedSaltedS2K method otherwise
hashId, ok := algorithm.HashToHashId(c.hash())
Expand Down Expand Up @@ -283,6 +283,9 @@ func ParseIntoParams(r io.Reader) (params *Params, err error) {
params.passes = buf[Argon2SaltSize]
params.parallelism = buf[Argon2SaltSize+1]
params.memoryExp = buf[Argon2SaltSize+2]
if err := validateArgon2Params(params); err != nil {
return nil, err
}
return params, nil
case GnuS2K:
// This is a GNU extension. See
Expand Down Expand Up @@ -412,3 +415,22 @@ func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte, c *Co
f(key, passphrase)
return nil
}

// validateArgon2Params checks that the argon2 parameters are valid according to RFC9580.
func validateArgon2Params(params *Params) error {
// The number of passes t and the degree of parallelism p MUST be non-zero.
if params.parallelism == 0 {
return errors.StructuralError("invalid argon2 params: parallelism is 0")
}
if params.passes == 0 {
return errors.StructuralError("invalid argon2 params: iterations is 0")
}

// The encoded memory size MUST be a value from 3+ceil(log2(p)) to 31,
// such that the decoded memory size m is a value from 8*p to 2^31.
if params.memoryExp > 31 || decodeMemory(params.memoryExp) < 8*uint32(params.parallelism) {
return errors.StructuralError("invalid argon2 params: memory is out of bounds")
}

return nil
}
46 changes: 46 additions & 0 deletions openpgp/s2k/s2k_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,49 @@ func testSerializeConfigOK(t *testing.T, c *Config) *Params {

return params
}

func TestValidateArgon2Params(t *testing.T) {
tests := []struct {
params Params
wantErr bool
}{
{
params: Params{parallelism: 4, passes: 3, memoryExp: 6},
wantErr: false,
},
{
params: Params{parallelism: 0, passes: 3, memoryExp: 6},
wantErr: true,
},
{
params: Params{parallelism: 4, passes: 0, memoryExp: 6},
wantErr: true,
},
{
params: Params{parallelism: 4, passes: 3, memoryExp: 4},
wantErr: true,
},
{
params: Params{parallelism: 4, passes: 3, memoryExp: 32},
wantErr: true,
},
{
params: Params{parallelism: 4, passes: 3, memoryExp: 5},
wantErr: false,
},
{
params: Params{parallelism: 4, passes: 3, memoryExp: 31},
wantErr: false,
},
}

for _, tt := range tests {
err := validateArgon2Params(&tt.params)
if tt.wantErr && err == nil {
t.Errorf("validateArgon2Params: expected an error")
}
if !tt.wantErr && err != nil {
t.Error("validateArgon2Params: expected no error")
}
}
}

0 comments on commit d7733dc

Please sign in to comment.