Skip to content

Commit

Permalink
feat: Validate argon2 s2k params on read
Browse files Browse the repository at this point in the history
  • Loading branch information
lubux committed Nov 11, 2024
1 parent c0ca2b8 commit afaf038
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 12 deletions.
6 changes: 4 additions & 2 deletions openpgp/packet/private_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type PrivateKey struct {
Encrypted bool // if true then the private key is unavailable until Decrypt has been called.
encryptedData []byte
cipher CipherFunction
s2k func(out, in []byte)
s2k func(out, in []byte) error
aead AEADMode // only relevant if S2KAEAD is enabled
// An *{rsa|dsa|elgamal|ecdh|ecdsa|ed25519|ed448}.PrivateKey or
// crypto.Signer/crypto.Decrypter (Decryptor RSA only).
Expand Down Expand Up @@ -629,7 +629,9 @@ func (pk *PrivateKey) Decrypt(passphrase []byte) error {
}

key := make([]byte, pk.cipher.KeySize())
pk.s2k(key, passphrase)
if err := pk.s2k(key, passphrase); err != nil {
return err
}
if pk.s2kType == S2KAEAD {
key = pk.applyHKDF(key)
}
Expand Down
6 changes: 4 additions & 2 deletions openpgp/packet/symmetric_key_encrypted.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type SymmetricKeyEncrypted struct {
Version int
CipherFunc CipherFunction
Mode AEADMode
s2k func(out, in []byte)
s2k func(out, in []byte) error
iv []byte
encryptedKey []byte // Contains also the authentication tag for AEAD
}
Expand Down Expand Up @@ -121,7 +121,9 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
// packet.
func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunction, error) {
key := make([]byte, ske.CipherFunc.KeySize())
ske.s2k(key, passphrase)
if err := ske.s2k(key, passphrase); err != nil {
return nil, ske.CipherFunc, err
}
if len(ske.encryptedKey) == 0 {
return key, ske.CipherFunc, nil
}
Expand Down
50 changes: 42 additions & 8 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 All @@ -226,7 +226,7 @@ func Generate(rand io.Reader, c *Config) (*Params, error) {
// and returns a function which performs that transform. If the S2K is a special
// GNU extension that indicates that the private key is missing, then the error
// returned is errors.ErrDummyPrivateKey.
func Parse(r io.Reader) (f func(out, in []byte), err error) {
func Parse(r io.Reader) (f func(out, in []byte) error, err error) {
params, err := ParseIntoParams(r)
if err != nil {
return nil, err
Expand Down Expand Up @@ -319,7 +319,7 @@ func (params *Params) salt() []byte {
}
}

func (params *Params) Function() (f func(out, in []byte), err error) {
func (params *Params) Function() (f func(out, in []byte) error, err error) {
if params.Dummy() {
return nil, errors.ErrDummyPrivateKey("dummy key found")
}
Expand All @@ -337,26 +337,33 @@ func (params *Params) Function() (f func(out, in []byte), err error) {

switch params.mode {
case SimpleS2K:
f := func(out, in []byte) {
f := func(out, in []byte) error {
Simple(out, hashObj.New(), in)
return nil
}

return f, nil
case SaltedS2K:
f := func(out, in []byte) {
f := func(out, in []byte) error {
Salted(out, hashObj.New(), in, params.salt())
return nil
}

return f, nil
case IteratedSaltedS2K:
f := func(out, in []byte) {
f := func(out, in []byte) error {
Iterated(out, hashObj.New(), in, params.salt(), decodeCount(params.countByte))
return nil
}

return f, nil
case Argon2S2K:
f := func(out, in []byte) {
f := func(out, in []byte) error {
if err := validateArgon2Params(params); err != nil {
return err
}
Argon2(out, in, params.salt(), params.passes, params.parallelism, params.memoryExp)
return nil
}
return f, nil
}
Expand Down Expand Up @@ -412,3 +419,30 @@ 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.
p := params.parallelism
ceiledLogarithm := byte(0)
for p > 1 {
p /= 2
ceiledLogarithm += 1
}
if byte(1)<<ceiledLogarithm != params.parallelism {
ceiledLogarithm += 1
}
if params.memoryExp < 3+ceiledLogarithm || params.memoryExp > 31 {
return errors.StructuralError("invalid argon2 params: memory is out of bounds")
}

return nil
}

0 comments on commit afaf038

Please sign in to comment.