Skip to content

Commit

Permalink
argon2: Add helpers for running the KDF remotely
Browse files Browse the repository at this point in the history
As Argon2 is memory intensive, it's not suitable for multiple invocations in
long-lived garbage collected processes. For this reason, Argon2 is
abstracted with an interface (Argon2KDF), of which the application sets
a global version of this which is intended to proxy KDF requests to a
short-lived remote process which uses the real InProcessArgon2KDF.

This adds some functionality to facilitate this.

First of all, InProcessArgon2KDF is no longer a variable - it's a
function. By default, it's methods return an error unless the
application code has called SetIsArgon2RemoteProcess, which unlocks the
real in-process KDF.

Then there are JSON serializable types "Argon2RemoteInput" and
"Argon2RemoteOutput". The input can be fed directly to
RunArgon2RequestInRemoteProcess on the remote side, but this is a
fairly low-level API.

There is a higher level API - NewRemoteArgon2KDF, for use in the
application process, and which returns an implementation of Argon2KDF
which proxies requests to a short-lived remote helper process. The
caller supplied a function to construct an appropriate exec.Cmd
instance for this. This function is configured so that the remote
process recieves a request on stdin and it expects a response on
stdout. The remote process passes both os.Stdin and os.Stdout to
WaitAndRunArgon2RequestInRemoteProcess, although it doesn't hardcode
these descriptors for implementations that want to construct their own
transport that doesn't rely on stdin and stdout.

Once a remote process has completed a request, it should exit cleanly.
Neither RunArgon2RequestInRemoteProcess or
WaitAndRunArgon2RequestInRemoteProcess support being called more than
once in the same process.

The code in cmd/run_argon2 provides an example remote process, although
this is mainly useful for unit testing (where it is currently used). It
is envisaged that the remote process will be a special mode of snapd and
snap-bootstrap in order to avoid adding an additional new go binary just
for this.
  • Loading branch information
chrisccoulson committed Sep 4, 2024
1 parent bc2266b commit 017287b
Show file tree
Hide file tree
Showing 7 changed files with 1,161 additions and 211 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
run_argon2
vendor/*/
38 changes: 0 additions & 38 deletions argon2.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,44 +225,6 @@ type Argon2KDF interface {
Time(mode Argon2Mode, params *Argon2CostParams) (time.Duration, error)
}

type inProcessArgon2KDFImpl struct{}

func (_ inProcessArgon2KDFImpl) Derive(passphrase string, salt []byte, mode Argon2Mode, params *Argon2CostParams, keyLen uint32) ([]byte, error) {
switch {
case mode != Argon2i && mode != Argon2id:
return nil, errors.New("invalid mode")
case params == nil:
return nil, errors.New("nil params")
case params.Time == 0:
return nil, errors.New("invalid time cost")
case params.Threads == 0:
return nil, errors.New("invalid number of threads")
}

return argon2.Key(passphrase, salt, argon2.Mode(mode), params.internalParams(), keyLen), nil
}

func (_ inProcessArgon2KDFImpl) Time(mode Argon2Mode, params *Argon2CostParams) (time.Duration, error) {
switch {
case mode != Argon2i && mode != Argon2id:
return 0, errors.New("invalid mode")
case params == nil:
return 0, errors.New("nil params")
case params.Time == 0:
return 0, errors.New("invalid time cost")
case params.Threads == 0:
return 0, errors.New("invalid number of threads")
}

return argon2.KeyDuration(argon2.Mode(mode), params.internalParams()), nil
}

// InProcessArgon2KDF is the in-process implementation of the Argon2 KDF. This
// shouldn't be used in long-lived system processes - these processes should
// instead provide their own KDF implementation which delegates to a short-lived
// utility process which will use the in-process implementation.
var InProcessArgon2KDF = inProcessArgon2KDFImpl{}

type nullArgon2KDFImpl struct{}

func (_ nullArgon2KDFImpl) Derive(passphrase string, salt []byte, mode Argon2Mode, params *Argon2CostParams, keyLen uint32) ([]byte, error) {
Expand Down
Loading

0 comments on commit 017287b

Please sign in to comment.