Skip to content

Commit

Permalink
Merge pull request #280 from sespiros/keydata-v3-auxiliary-primary-re…
Browse files Browse the repository at this point in the history
…name

Keydata v3 auxiliary primary rename and legacy keydata test.

This adds the following changes:

1. Renaming AuxiliaryKey to PrimaryKey
2. adds a basic legacy behavior test to ensure correct behavior for the upcoming
    changes related to the new keydata format.
  • Loading branch information
chrisccoulson authored Jan 25, 2024
2 parents 42c7ea9 + c62b61f commit 10c645d
Show file tree
Hide file tree
Showing 27 changed files with 234 additions and 149 deletions.
2 changes: 1 addition & 1 deletion crypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func (s *activateWithKeyDataState) errors() (out []*activateWithKeyDataError) {
return out
}

func (s *activateWithKeyDataState) tryActivateWithRecoveredKey(key DiskUnlockKey, slot int, keyData *KeyData, auxKey AuxiliaryKey) error {
func (s *activateWithKeyDataState) tryActivateWithRecoveredKey(key DiskUnlockKey, slot int, keyData *KeyData, auxKey PrimaryKey) error {
if s.model != SkipSnapModelCheck {
authorized, err := keyData.IsSnapModelAuthorized(auxKey, s.model)
switch {
Expand Down
10 changes: 5 additions & 5 deletions crypt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ func (s *cryptSuite) checkRecoveryKeyInKeyring(c *C, prefix, path string, expect
c.Check(key, DeepEquals, DiskUnlockKey(expected[:]))
}

func (s *cryptSuite) checkKeyDataKeysInKeyring(c *C, prefix, path string, expectedKey DiskUnlockKey, expectedAuxKey AuxiliaryKey) {
func (s *cryptSuite) checkKeyDataKeysInKeyring(c *C, prefix, path string, expectedKey DiskUnlockKey, expectedAuxKey PrimaryKey) {
// The following test will fail if the user keyring isn't reachable from the session keyring. If the test have succeeded
// so far, mark the current test as expected to fail.
if !s.ProcessPossessesUserKeyringKeys && !c.Failed() {
Expand All @@ -464,12 +464,12 @@ func (s *cryptSuite) checkKeyDataKeysInKeyring(c *C, prefix, path string, expect
c.Check(err, IsNil)
c.Check(key, DeepEquals, expectedKey)

auxKey, err := GetAuxiliaryKeyFromKernel(prefix, path, false)
auxKey, err := GetPrimaryKeyFromKernel(prefix, path, false)
c.Check(err, IsNil)
c.Check(auxKey, DeepEquals, expectedAuxKey)
}

func (s *cryptSuite) newMultipleNamedKeyData(c *C, names ...string) (keyData []*KeyData, keys []DiskUnlockKey, auxKeys []AuxiliaryKey) {
func (s *cryptSuite) newMultipleNamedKeyData(c *C, names ...string) (keyData []*KeyData, keys []DiskUnlockKey, auxKeys []PrimaryKey) {
for _, name := range names {
key, auxKey := s.newKeyDataKeys(c, 32, 32)
protected := s.mockProtectKeys(c, key, auxKey, crypto.SHA256)
Expand All @@ -492,7 +492,7 @@ func (s *cryptSuite) newMultipleNamedKeyData(c *C, names ...string) (keyData []*
return keyData, keys, auxKeys
}

func (s *cryptSuite) newNamedKeyData(c *C, name string) (*KeyData, DiskUnlockKey, AuxiliaryKey) {
func (s *cryptSuite) newNamedKeyData(c *C, name string) (*KeyData, DiskUnlockKey, PrimaryKey) {
keyData, keys, auxKeys := s.newMultipleNamedKeyData(c, name)
return keyData[0], keys[0], auxKeys[0]
}
Expand Down Expand Up @@ -1400,7 +1400,7 @@ type testActivateVolumeWithMultipleKeyDataData struct {
keyData []*KeyData
activateSlots []int
validKey DiskUnlockKey
validAuxKey AuxiliaryKey
validAuxKey PrimaryKey
}

func (s *cryptSuite) testActivateVolumeWithMultipleKeyData(c *C, data *testActivateVolumeWithMultipleKeyDataData) {
Expand Down
2 changes: 1 addition & 1 deletion internal/compattest/compattest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func (s *compatTestSuiteBase) testUnsealCommon(c *C) {
c.Assert(err, IsNil)
c.Check(key, DeepEquals, secboot.DiskUnlockKey(expectedKey))

var expectedAuthPrivateKey secboot.AuxiliaryKey
var expectedAuthPrivateKey secboot.PrimaryKey
authKeyPath := s.absPath("authKey")
if _, err := os.Stat(authKeyPath); err == nil {
expectedAuthPrivateKey, err = ioutil.ReadFile(authKeyPath)
Expand Down
28 changes: 14 additions & 14 deletions keydata.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,15 @@ func (e *PlatformDeviceUnavailableError) Unwrap() error {
// DiskUnlockKey is the key used to unlock a LUKS volume.
type DiskUnlockKey []byte

// AuxiliaryKey is an additional key used to modify properties of a KeyData
// PrimaryKey is an additional key used to modify properties of a KeyData
// object without having to create a new object.
type AuxiliaryKey []byte
type PrimaryKey []byte

// KeyPayload is the payload that should be encrypted by a platform's secure device.
type KeyPayload []byte

// Unmarshal obtains the keys from this payload.
func (c KeyPayload) Unmarshal() (key DiskUnlockKey, auxKey AuxiliaryKey, err error) {
func (c KeyPayload) Unmarshal() (key DiskUnlockKey, auxKey PrimaryKey, err error) {
r := bytes.NewReader(c)

var sz uint16
Expand All @@ -131,7 +131,7 @@ func (c KeyPayload) Unmarshal() (key DiskUnlockKey, auxKey AuxiliaryKey, err err
}

if sz > 0 {
auxKey = make(AuxiliaryKey, sz)
auxKey = make(PrimaryKey, sz)
if _, err := r.Read(auxKey); err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -166,9 +166,9 @@ type KeyParams struct {
EncryptedPayload []byte // The encrypted payload
PlatformName string // Name of the platform that produced this data

// AuxiliaryKey is a key used to authorize changes to the key data.
// PrimaryKey is a key used to authorize changes to the key data.
// It must match the key protected inside PlatformKeyData.EncryptedPayload.
AuxiliaryKey AuxiliaryKey
PrimaryKey PrimaryKey

// SnapModelAuthHash is the digest algorithm used for HMACs of Snap
// device models, and also the digest algorithm used to produce the
Expand Down Expand Up @@ -437,7 +437,7 @@ type KeyData struct {
data keyData
}

func (d *KeyData) snapModelAuthKeyLegacy(auxKey AuxiliaryKey) ([]byte, error) {
func (d *KeyData) snapModelAuthKeyLegacy(auxKey PrimaryKey) ([]byte, error) {
rng, err := drbg.NewCTRWithExternalEntropy(32, auxKey, nil, snapModelHMACKDFLabel, nil)
if err != nil {
return nil, xerrors.Errorf("cannot instantiate DRBG: %w", err)
Expand All @@ -456,7 +456,7 @@ func (d *KeyData) snapModelAuthKeyLegacy(auxKey AuxiliaryKey) ([]byte, error) {
return hmacKey, nil
}

func (d *KeyData) snapModelAuthKey(auxKey AuxiliaryKey) ([]byte, error) {
func (d *KeyData) snapModelAuthKey(auxKey PrimaryKey) ([]byte, error) {
kdfAlg := d.data.AuthorizedSnapModels.kdfAlg
if kdfAlg == nilHash {
return d.snapModelAuthKeyLegacy(auxKey)
Expand Down Expand Up @@ -656,7 +656,7 @@ func (d *KeyData) MarshalAndUpdatePlatformHandle(handle interface{}) error {
//
// If the keys cannot be recovered because the platform's secure device is not
// available, a *PlatformDeviceUnavailableError error will be returned.
func (d *KeyData) RecoverKeys() (DiskUnlockKey, AuxiliaryKey, error) {
func (d *KeyData) RecoverKeys() (DiskUnlockKey, PrimaryKey, error) {
if d.AuthMode() != AuthModeNone {
return nil, nil, errors.New("cannot recover key without authorization")
}
Expand All @@ -681,7 +681,7 @@ func (d *KeyData) RecoverKeys() (DiskUnlockKey, AuxiliaryKey, error) {
return key, auxKey, nil
}

func (d *KeyData) RecoverKeysWithPassphrase(passphrase string, kdf KDF) (DiskUnlockKey, AuxiliaryKey, error) {
func (d *KeyData) RecoverKeysWithPassphrase(passphrase string, kdf KDF) (DiskUnlockKey, PrimaryKey, error) {
if d.AuthMode()&AuthModePassphrase == 0 {
return nil, nil, errors.New("no passphrase is set")
}
Expand Down Expand Up @@ -716,7 +716,7 @@ func (d *KeyData) RecoverKeysWithPassphrase(passphrase string, kdf KDF) (DiskUnl
// access the data on the encrypted volume protected by this key data.
//
// The supplied auxKey is obtained using one of the RecoverKeys* functions.
func (d *KeyData) IsSnapModelAuthorized(auxKey AuxiliaryKey, model SnapModel) (bool, error) {
func (d *KeyData) IsSnapModelAuthorized(auxKey PrimaryKey, model SnapModel) (bool, error) {
hmacKey, err := d.snapModelAuthKey(auxKey)
if err != nil {
return false, xerrors.Errorf("cannot obtain auth key: %w", err)
Expand Down Expand Up @@ -744,7 +744,7 @@ func (d *KeyData) IsSnapModelAuthorized(auxKey AuxiliaryKey, model SnapModel) (b
//
// The supplied auxKey is obtained using one of the RecoverKeys* functions. If the
// supplied auxKey is incorrect, then an error will be returned.
func (d *KeyData) SetAuthorizedSnapModels(auxKey AuxiliaryKey, models ...SnapModel) error {
func (d *KeyData) SetAuthorizedSnapModels(auxKey PrimaryKey, models ...SnapModel) error {
hmacKey, err := d.snapModelAuthKey(auxKey)
if err != nil {
return xerrors.Errorf("cannot obtain auth key: %w", err)
Expand Down Expand Up @@ -919,7 +919,7 @@ func NewKeyData(params *KeyParams) (*KeyData, error) {
Alg: hashAlg(params.SnapModelAuthHash),
Salt: salt[:]}}}}

authKey, err := kd.snapModelAuthKey(params.AuxiliaryKey)
authKey, err := kd.snapModelAuthKey(params.PrimaryKey)
if err != nil {
return nil, xerrors.Errorf("cannot compute snap model auth key: %w", err)
}
Expand All @@ -934,7 +934,7 @@ func NewKeyData(params *KeyParams) (*KeyData, error) {

// MarshalKeys serializes the supplied disk unlock key and auxiliary key in
// to a format that is ready to be encrypted by a platform's secure device.
func MarshalKeys(key DiskUnlockKey, auxKey AuxiliaryKey) KeyPayload {
func MarshalKeys(key DiskUnlockKey, auxKey PrimaryKey) KeyPayload {
w := new(bytes.Buffer)
binary.Write(w, binary.BigEndian, uint16(len(key)))
w.Write(key)
Expand Down
99 changes: 92 additions & 7 deletions keydata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ func (s *keyDataTestBase) TearDownSuite(c *C) {
RegisterPlatformKeyDataHandler(mockPlatformName, nil)
}

func (s *keyDataTestBase) newKeyDataKeys(c *C, sz1, sz2 int) (DiskUnlockKey, AuxiliaryKey) {
func (s *keyDataTestBase) newKeyDataKeys(c *C, sz1, sz2 int) (DiskUnlockKey, PrimaryKey) {
key := make([]byte, sz1)
auxKey := make([]byte, sz2)
_, err := rand.Read(key)
Expand All @@ -254,7 +254,7 @@ func (s *keyDataTestBase) newKeyDataKeys(c *C, sz1, sz2 int) (DiskUnlockKey, Aux
return key, auxKey
}

func (s *keyDataTestBase) mockProtectKeys(c *C, key DiskUnlockKey, auxKey AuxiliaryKey, modelAuthHash crypto.Hash) (out *KeyParams) {
func (s *keyDataTestBase) mockProtectKeys(c *C, key DiskUnlockKey, auxKey PrimaryKey, modelAuthHash crypto.Hash) (out *KeyParams) {
payload := MarshalKeys(key, auxKey)

k := make([]byte, 48)
Expand All @@ -276,7 +276,7 @@ func (s *keyDataTestBase) mockProtectKeys(c *C, key DiskUnlockKey, auxKey Auxili
PlatformName: mockPlatformName,
Handle: &handle,
EncryptedPayload: make([]byte, len(payload)),
AuxiliaryKey: auxKey,
PrimaryKey: auxKey,
SnapModelAuthHash: modelAuthHash}
stream.XORKeyStream(out.EncryptedPayload, payload)
return
Expand Down Expand Up @@ -457,7 +457,7 @@ func (s *keyDataSuite) checkKeyDataJSONAuthModePassphrase(c *C, keyData *KeyData

type testKeyPayloadData struct {
key DiskUnlockKey
auxKey AuxiliaryKey
auxKey PrimaryKey
}

func (s *keyDataSuite) testKeyPayload(c *C, data *testKeyPayloadData) {
Expand Down Expand Up @@ -505,7 +505,7 @@ func (s *keyDataSuite) TestKeyPayloadUnmarshalInvalid1(c *C) {
}

func (s *keyDataSuite) TestKeyPayloadUnmarshalInvalid2(c *C) {
payload := MarshalKeys(make(DiskUnlockKey, 32), make(AuxiliaryKey, 32))
payload := MarshalKeys(make(DiskUnlockKey, 32), make(PrimaryKey, 32))
payload = append(payload, 0xff)

key, auxKey, err := payload.Unmarshal()
Expand Down Expand Up @@ -1044,7 +1044,7 @@ func (s *keyDataSuite) TestSetAuthorizedSnapModelsWithWrongKey(c *C) {
"grade": "secured",
}, "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij")}

c.Check(keyData.SetAuthorizedSnapModels(make(AuxiliaryKey, 32), models...), ErrorMatches, "incorrect key supplied")
c.Check(keyData.SetAuthorizedSnapModels(make(PrimaryKey, 32), models...), ErrorMatches, "incorrect key supplied")
}

type testWriteAtomicData struct {
Expand Down Expand Up @@ -1141,7 +1141,7 @@ func (s *keyDataSuite) TestWriteAtomic4(c *C) {

type testReadKeyDataData struct {
key DiskUnlockKey
auxKey AuxiliaryKey
auxKey PrimaryKey
id KeyID
r KeyDataReader
model SnapModel
Expand Down Expand Up @@ -1427,3 +1427,88 @@ func (s *keyDataSuite) TestReadAndWriteWithLegacySnapModelAuthKey(c *C) {
c.Check(err, IsNil)
c.Check(ok, testutil.IsTrue)
}

func (s *keyDataSuite) TestLegacyKeyData(c *C) {
unlockKey := testutil.DecodeHexString(c, "09a2e672131045221284e026b17de93b395581e82450a01e170150432f8cdf81")
primaryKey := testutil.DecodeHexString(c, "1850fbecbe8b3db83a894cb975756c8b69086040f097b03bd4f3b1a3e19c4b86")

j := []byte(
`{` +
`"platform_name":"mock",` +
`"platform_handle":{` +
`"key":"7AQQmeIwl5iv3V+yTszelcdF6MkJpKz+7EA0kKUJNEo=",` +
`"iv":"i88WWEI7WyJ1gXX5LGhRSg==",` +
`"auth-key-hmac":"WybrzR13ozdYwzyt4oyihIHSABZozpHyQSAn+NtQSkA="},` +
`"encrypted_payload":"eMeLrknRAi/dFBM607WPxFOCE1L9RZ4xxUs+Leodz78s/id7Eq+IHhZdOC/stXSNe+Gn/PWgPxcd0TfEPUs5TA350lo=",` +
`"authorized_snap_models":{` +
`"alg":"sha256",` +
`"kdf_alg":"sha256",` +
`"key_digest":{` +
`"alg":"sha256",` +
`"salt":"IPDKKUOoRYwvMWX8LoCCtlGgzgzokAhsh42XnbGUn0s=",` +
`"digest":"SSbv/yS8h5pqchVfV9AMHUjhS/vVateojNRRmo624qk="},` +
`"hmacs":["OCxZPr5lqnwlNTMYXObK6cXlkcWw3Dx5v+/NRMrCzhw="]}}
`)

keyData, err := ReadKeyData(&mockKeyDataReader{Reader: bytes.NewReader(j)})
c.Assert(err, IsNil)

model := testutil.MakeMockCore20ModelAssertion(c, map[string]interface{}{
"authority-id": "fake-brand",
"series": "16",
"brand-id": "fake-brand",
"model": "fake-model",
"grade": "secured",
}, "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij")

ok, err := keyData.IsSnapModelAuthorized(primaryKey, model)
c.Check(err, IsNil)
c.Check(ok, testutil.IsTrue)

recoveredUnlockKey, recoveredPrimaryKey, err := keyData.RecoverKeys()
c.Check(err, IsNil)
c.Check(recoveredUnlockKey, DeepEquals, DiskUnlockKey(unlockKey))
c.Check(recoveredPrimaryKey, DeepEquals, PrimaryKey(primaryKey))

w := makeMockKeyDataWriter()
c.Check(keyData.WriteAtomic(w), IsNil)

j2, err := ioutil.ReadAll(w.Reader())
c.Check(err, IsNil)
c.Check(j2, DeepEquals, j)

model2 := testutil.MakeMockCore20ModelAssertion(c, map[string]interface{}{
"authority-id": "fake-brand",
"series": "16",
"brand-id": "fake-brand",
"model": "other-model",
"grade": "secured",
}, "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij")
c.Check(keyData.SetAuthorizedSnapModels(primaryKey, model2), IsNil)
ok, err = keyData.IsSnapModelAuthorized(primaryKey, model)
c.Check(err, IsNil)
c.Check(ok, Not(testutil.IsTrue))
ok, err = keyData.IsSnapModelAuthorized(primaryKey, model2)
c.Check(err, IsNil)
c.Check(ok, testutil.IsTrue)

w = makeMockKeyDataWriter()
c.Check(keyData.WriteAtomic(w), IsNil)
c.Check(w.final.Bytes(), DeepEquals, []byte(
`{`+
`"platform_name":"mock",`+
`"platform_handle":{`+
`"key":"7AQQmeIwl5iv3V+yTszelcdF6MkJpKz+7EA0kKUJNEo=",`+
`"iv":"i88WWEI7WyJ1gXX5LGhRSg==",`+
`"auth-key-hmac":"WybrzR13ozdYwzyt4oyihIHSABZozpHyQSAn+NtQSkA="},`+
`"encrypted_payload":"eMeLrknRAi/dFBM607WPxFOCE1L9RZ4xxUs+Leodz78s/id7Eq+IHhZdOC/stXSNe+Gn/PWgPxcd0TfEPUs5TA350lo=",`+
`"authorized_snap_models":{`+
`"alg":"sha256",`+
`"kdf_alg":"sha256",`+
`"key_digest":{`+
`"alg":"sha256",`+
`"salt":"IPDKKUOoRYwvMWX8LoCCtlGgzgzokAhsh42XnbGUn0s=",`+
`"digest":"SSbv/yS8h5pqchVfV9AMHUjhS/vVateojNRRmo624qk="},`+
`"hmacs":["JWziaukXiAIsPU22X1RTC/2wEkPN4IdNvgDEzSnWXIc="]}}
`))
}
4 changes: 2 additions & 2 deletions keyring.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func GetDiskUnlockKeyFromKernel(prefix, devicePath string, remove bool) (DiskUnl
return key, nil
}

// GetAuxiliaryKeyFromKernel retrieves the auxiliary key associated with the
// GetPrimaryKeyFromKernel retrieves the auxiliary key associated with the
// KeyData that was used to unlock the encrypted container at the specified path.
// The value of prefix must match the prefix that was supplied via
// ActivateVolumeOptions during unlocking.
Expand All @@ -81,7 +81,7 @@ func GetDiskUnlockKeyFromKernel(prefix, devicePath string, remove bool) (DiskUnl
// to returning.
//
// If no key is found, a ErrKernelKeyNotFound error will be returned.
func GetAuxiliaryKeyFromKernel(prefix, devicePath string, remove bool) (AuxiliaryKey, error) {
func GetPrimaryKeyFromKernel(prefix, devicePath string, remove bool) (PrimaryKey, error) {
key, err := keyring.GetKeyFromUserKeyring(devicePath, keyringPurposeAuxiliary, keyringPrefixOrDefault(prefix))
if err != nil {
var e syscall.Errno
Expand Down
Loading

0 comments on commit 10c645d

Please sign in to comment.