Skip to content

Commit

Permalink
[WIP] add PIN support to KeyData
Browse files Browse the repository at this point in the history
This adds PIN support to KeyData which is distinct from the existing
passphrase support.

Passphrases are used both for authentication with the hardware element
(such as the TPM) and for additional encryption on the host CPU with a
passphrase derived key, and are intended to use a memory hard key
derivation. The intention here is that this configuration provides some
additional protection in the event of a TPM compromise (eg, say a TPM
manufacturer is coerced by a government agency to provide firmware that
bypasses authentications), where sensitive data is able to be extracted
without the usual authentication, because extracting the secret from the
TPM will not be sufficient to obtain all of the key material necessary
to unlock a device.

PINs (in the literal sense) have a fairly low entropy - an 8 digit PIN
only has an entropy of 26.5bits, so this additional encryption will
provide little protection in the event of a TPM compromise - if
sensitive data is obtained from the TPM, the 26.5bits of entropy won't
provide a significant barrier to deriving the remaining key material
necessary to unlock a device. We take advantage of this by implementing
distinct PIN support that is only used for authentication. With this in
mind, the memory hard key derivation does not provide a lot of benefit,
so PINs only support PBKDF2, and it can be configured to run faster
than the key derivation for passphrases. In that sense, PIN support is
essentially just a faster and slightly weaker passphrase.

As the PIN is a PIN in the literal sense, it is encoded as a length
prefixed binary number before going through the key derivation.

This only implements the support to KeyData for now - unlocking support
will be added in another PR.
  • Loading branch information
chrisccoulson committed Mar 16, 2024
1 parent 9a2d46c commit 80c464f
Show file tree
Hide file tree
Showing 10 changed files with 371 additions and 32 deletions.
6 changes: 3 additions & 3 deletions argon2.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ type Argon2Options struct {
Parallel uint8
}

func (o *Argon2Options) kdfParams(keyLen uint32) (*kdfParams, error) {
func (o *Argon2Options) kdfParams(defaultTargetDuration time.Duration, keyLen uint32) (*kdfParams, error) {
switch o.Mode {
case Argon2Default, Argon2i, Argon2id:
// ok
Expand Down Expand Up @@ -159,7 +159,7 @@ func (o *Argon2Options) kdfParams(keyLen uint32) (*kdfParams, error) {
default:
benchmarkParams := &argon2.BenchmarkParams{
MaxMemoryCostKiB: 1 * 1024 * 1024, // the default maximum memory cost is 1GiB.
TargetDuration: 2 * time.Second, // the default target duration is 2s.
TargetDuration: defaultTargetDuration,
}

if o.MemoryKiB != 0 {
Expand Down Expand Up @@ -187,7 +187,7 @@ func (o *Argon2Options) kdfParams(keyLen uint32) (*kdfParams, error) {
MemoryKiB: params.MemoryKiB,
ForceIterations: params.Time,
Parallel: params.Threads}
return o.kdfParams(keyLen)
return o.kdfParams(defaultTargetDuration, keyLen)
}
}

Expand Down
38 changes: 26 additions & 12 deletions argon2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ var _ = Suite(&argon2Suite{})

func (s *argon2Suite) TestKDFParamsDefault(c *C) {
var opts Argon2Options
params, err := opts.KdfParams(0)
params, err := opts.KdfParams(2*time.Second, 0)
c.Assert(err, IsNil)
c.Check(s.kdf.BenchmarkMode, Equals, Argon2id)

Expand All @@ -87,10 +87,24 @@ func (s *argon2Suite) TestKDFParamsDefault(c *C) {
})
}

func (s *argon2Suite) TestKDFParamsDefaultWithDifferentTargetDuration(c *C) {
var opts Argon2Options
params, err := opts.KdfParams(200*time.Millisecond, 32)
c.Assert(err, IsNil)
c.Check(s.kdf.BenchmarkMode, Equals, Argon2id)

c.Check(params, DeepEquals, &KdfParams{
Type: "argon2id",
Time: 4,
Memory: 102406,
CPUs: s.cpusAuto,
})
}

func (s *argon2Suite) TestKDFParamsExplicitMode(c *C) {
var opts Argon2Options
opts.Mode = Argon2i
params, err := opts.KdfParams(0)
params, err := opts.KdfParams(2*time.Second, 0)
c.Assert(err, IsNil)
c.Check(s.kdf.BenchmarkMode, Equals, Argon2i)

Expand All @@ -105,7 +119,7 @@ func (s *argon2Suite) TestKDFParamsExplicitMode(c *C) {
func (s *argon2Suite) TestKDFParamsTargetDuration(c *C) {
var opts Argon2Options
opts.TargetDuration = 1 * time.Second
params, err := opts.KdfParams(32)
params, err := opts.KdfParams(2*time.Second, 32)
c.Assert(err, IsNil)
c.Check(s.kdf.BenchmarkMode, Equals, Argon2id)

Expand All @@ -120,7 +134,7 @@ func (s *argon2Suite) TestKDFParamsTargetDuration(c *C) {
func (s *argon2Suite) TestKDFParamsMemoryLimit(c *C) {
var opts Argon2Options
opts.MemoryKiB = 32 * 1024
params, err := opts.KdfParams(0)
params, err := opts.KdfParams(2*time.Second, 0)
c.Assert(err, IsNil)
c.Check(s.kdf.BenchmarkMode, Equals, Argon2id)

Expand All @@ -135,7 +149,7 @@ func (s *argon2Suite) TestKDFParamsMemoryLimit(c *C) {
func (s *argon2Suite) TestKDFParamsForceBenchmarkedThreads(c *C) {
var opts Argon2Options
opts.Parallel = 1
params, err := opts.KdfParams(0)
params, err := opts.KdfParams(2*time.Second, 0)
c.Assert(err, IsNil)
c.Check(s.kdf.BenchmarkMode, Equals, Argon2id)

Expand All @@ -153,7 +167,7 @@ func (s *argon2Suite) TestKDFParamsForceIterations(c *C) {

var opts Argon2Options
opts.ForceIterations = 3
params, err := opts.KdfParams(0)
params, err := opts.KdfParams(2*time.Second, 0)
c.Assert(err, IsNil)
c.Check(s.kdf.BenchmarkMode, Equals, Argon2Default)

Expand All @@ -172,7 +186,7 @@ func (s *argon2Suite) TestKDFParamsForceMemory(c *C) {
var opts Argon2Options
opts.ForceIterations = 3
opts.MemoryKiB = 32 * 1024
params, err := opts.KdfParams(0)
params, err := opts.KdfParams(2*time.Second, 0)
c.Assert(err, IsNil)
c.Check(s.kdf.BenchmarkMode, Equals, Argon2Default)

Expand All @@ -190,7 +204,7 @@ func (s *argon2Suite) TestKDFParamsForceIterationsDifferentCPUNum(c *C) {

var opts Argon2Options
opts.ForceIterations = 3
params, err := opts.KdfParams(0)
params, err := opts.KdfParams(2*time.Second, 0)
c.Assert(err, IsNil)
c.Check(s.kdf.BenchmarkMode, Equals, Argon2Default)

Expand All @@ -209,7 +223,7 @@ func (s *argon2Suite) TestKDFParamsForceThreads(c *C) {
var opts Argon2Options
opts.ForceIterations = 3
opts.Parallel = 1
params, err := opts.KdfParams(9)
params, err := opts.KdfParams(2*time.Second, 0)
c.Assert(err, IsNil)
c.Check(s.kdf.BenchmarkMode, Equals, Argon2Default)

Expand All @@ -228,7 +242,7 @@ func (s *argon2Suite) TestKDFParamsForceThreadsGreatherThanCPUNum(c *C) {
var opts Argon2Options
opts.ForceIterations = 3
opts.Parallel = 8
params, err := opts.KdfParams(0)
params, err := opts.KdfParams(2*time.Second, 0)
c.Assert(err, IsNil)
c.Check(s.kdf.BenchmarkMode, Equals, Argon2Default)

Expand All @@ -243,15 +257,15 @@ func (s *argon2Suite) TestKDFParamsForceThreadsGreatherThanCPUNum(c *C) {
func (s *argon2Suite) TestKDFParamsInvalidForceIterations(c *C) {
var opts Argon2Options
opts.ForceIterations = math.MaxUint32
_, err := opts.KdfParams(0)
_, err := opts.KdfParams(2*time.Second, 0)
c.Check(err, ErrorMatches, `invalid iterations count 4294967295`)
}

func (s *argon2Suite) TestKDFParamsInvalidMemoryKiB(c *C) {
var opts Argon2Options
opts.ForceIterations = 4
opts.MemoryKiB = math.MaxUint32
_, err := opts.KdfParams(0)
_, err := opts.KdfParams(2*time.Second, 0)
c.Check(err, ErrorMatches, `invalid memory cost 4294967295KiB`)
}

Expand Down
8 changes: 4 additions & 4 deletions export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ type (
ProtectedKeys = protectedKeys
)

func (o *Argon2Options) KdfParams(keyLen uint32) (*KdfParams, error) {
return o.kdfParams(keyLen)
func (o *Argon2Options) KdfParams(defaultTargetDuration time.Duration, keyLen uint32) (*KdfParams, error) {
return o.kdfParams(defaultTargetDuration, keyLen)
}

func (o *PBKDF2Options) KdfParams(keyLen uint32) (*KdfParams, error) {
return o.kdfParams(keyLen)
func (o *PBKDF2Options) KdfParams(defaultTargetDuration time.Duration, keyLen uint32) (*KdfParams, error) {
return o.kdfParams(defaultTargetDuration, keyLen)
}

func MockLUKS2Activate(fn func(string, string, []byte, int) error) (restore func()) {
Expand Down
4 changes: 3 additions & 1 deletion kdf.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@

package secboot

import "time"

// KDFOptions is an interface for supplying options for different
// key derivation functions
type KDFOptions interface {
kdfParams(keyLen uint32) (*kdfParams, error)
kdfParams(defaultTargetDuration time.Duration, keyLen uint32) (*kdfParams, error)
}
Loading

0 comments on commit 80c464f

Please sign in to comment.