Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func MockKeyDataGeneration(n int) (restore func()) {

func MockHashAlgAvailable() (restore func()) {
orig := hashAlgAvailable
hashAlgAvailable = func(*hashAlg) bool {
hashAlgAvailable = func(*HashAlg) bool {
return false
}
return func() {
Expand Down
54 changes: 27 additions & 27 deletions keydata.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import (

const (
kdfType = "argon2i"
nilHash hashAlg = 0
nilHash HashAlg = 0
passphraseKeyLen = 32
passphraseEncryptionKeyLen = 32
passphraseEncryption = "aes-cfb"
Expand Down Expand Up @@ -189,24 +189,24 @@ type KeyDataReader interface {
ReadableName() string
}

// hashAlg corresponds to a digest algorithm.
type hashAlg crypto.Hash
// HashAlg corresponds to a digest algorithm.
type HashAlg crypto.Hash

var hashAlgAvailable = (*hashAlg).Available
var hashAlgAvailable = (*HashAlg).Available

func (a hashAlg) Available() bool {
func (a HashAlg) Available() bool {
return crypto.Hash(a).Available()
}

func (a hashAlg) New() hash.Hash {
func (a HashAlg) New() hash.Hash {
return crypto.Hash(a).New()
}

func (a hashAlg) Size() int {
func (a HashAlg) Size() int {
return crypto.Hash(a).Size()
}

func (a hashAlg) MarshalJSON() ([]byte, error) {
func (a HashAlg) MarshalJSON() ([]byte, error) {
var s string

switch crypto.Hash(a) {
Expand All @@ -229,23 +229,23 @@ func (a hashAlg) MarshalJSON() ([]byte, error) {
return json.Marshal(s)
}

func (a *hashAlg) UnmarshalJSON(b []byte) error {
func (a *HashAlg) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}

switch s {
case "sha1":
*a = hashAlg(crypto.SHA1)
*a = HashAlg(crypto.SHA1)
case "sha224":
*a = hashAlg(crypto.SHA224)
*a = HashAlg(crypto.SHA224)
case "sha256":
*a = hashAlg(crypto.SHA256)
*a = HashAlg(crypto.SHA256)
case "sha384":
*a = hashAlg(crypto.SHA384)
*a = HashAlg(crypto.SHA384)
case "sha512":
*a = hashAlg(crypto.SHA512)
*a = HashAlg(crypto.SHA512)
default:
// be permissive here and allow everything to be
// unmarshalled.
Expand All @@ -255,7 +255,7 @@ func (a *hashAlg) UnmarshalJSON(b []byte) error {
return nil
}

func (a hashAlg) marshalASN1(b *cryptobyte.Builder) {
func (a HashAlg) MarshalASN1(b *cryptobyte.Builder) {
b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { // AlgorithmIdentifier ::= SEQUENCE {
var oid asn1.ObjectIdentifier

Expand Down Expand Up @@ -294,28 +294,28 @@ func (l snapModelHMACList) contains(h snapModelHMAC) bool {

// keyDigest contains a salted digest to verify the correctness of a key.
type keyDigest struct {
Alg hashAlg `json:"alg"`
Alg HashAlg `json:"alg"`
Salt []byte `json:"salt"`
Digest []byte `json:"digest"`
}

// hkdfData contains the parameters used to derive a key using HKDF.
type hkdfData struct {
Alg hashAlg `json:"alg"` // Digest algorithm to use for HKDF
Alg HashAlg `json:"alg"` // Digest algorithm to use for HKDF
}

type authorizedSnapModelsRaw struct {
Alg hashAlg `json:"alg"`
KDFAlg hashAlg `json:"kdf_alg,omitempty"`
Alg HashAlg `json:"alg"`
KDFAlg HashAlg `json:"kdf_alg,omitempty"`
KeyDigest json.RawMessage `json:"key_digest"`
Hmacs snapModelHMACList `json:"hmacs"`
}

// authorizedSnapModels defines the Snap models that have been
// authorized to access the data protected by a key.
type authorizedSnapModels struct {
alg hashAlg // Digest algorithm used for the authorized model HMACs
kdfAlg hashAlg // Digest algorithm used to derive the HMAC key with HKDF. Zero for legacy (DRBG) derivation.
alg HashAlg // Digest algorithm used for the authorized model HMACs
kdfAlg HashAlg // Digest algorithm used to derive the HMAC key with HKDF. Zero for legacy (DRBG) derivation.
keyDigest keyDigest // information used to validate the correctness of the HMAC key
hmacs snapModelHMACList // the list of HMACs of authorized models

Expand Down Expand Up @@ -431,7 +431,7 @@ type keyData struct {
// KDFAlg is the algorithm that is used to derive the unlock key from a primary key.
// It is also used to derive additional keys from the passphrase derived key in
// derivePassphraseKeys.
KDFAlg hashAlg `json:"kdf_alg,omitempty"`
KDFAlg HashAlg `json:"kdf_alg,omitempty"`

// EncryptedPayload is the platform protected key payload.
EncryptedPayload []byte `json:"encrypted_payload"`
Expand Down Expand Up @@ -543,7 +543,7 @@ func (d *KeyData) derivePassphraseKeys(passphrase string, kdf KDF) (key, iv, aut
builder := cryptobyte.NewBuilder(nil)
builder.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { // SEQUENCE {
b.AddASN1OctetString(params.KDF.Salt) // salt OCTET STRING
kdfAlg.marshalASN1(b) // kdfAlgorithm AlgorithmIdentifier
kdfAlg.MarshalASN1(b) // kdfAlgorithm AlgorithmIdentifier
b.AddASN1(cryptobyte_asn1.UTF8String, func(b *cryptobyte.Builder) { // encryption UTF8String
b.AddBytes([]byte(params.Encryption))
})
Expand Down Expand Up @@ -943,13 +943,13 @@ func NewKeyData(params *KeyParams) (*KeyData, error) {
Generation: keyDataGeneration,
PlatformName: params.PlatformName,
PlatformHandle: json.RawMessage(encodedHandle),
KDFAlg: hashAlg(params.KDFAlg),
KDFAlg: HashAlg(params.KDFAlg),
EncryptedPayload: params.EncryptedPayload,
AuthorizedSnapModels: authorizedSnapModels{
alg: hashAlg(params.SnapModelAuthHash),
kdfAlg: hashAlg(params.SnapModelAuthHash),
alg: HashAlg(params.SnapModelAuthHash),
kdfAlg: HashAlg(params.SnapModelAuthHash),
keyDigest: keyDigest{
Alg: hashAlg(params.SnapModelAuthHash),
Alg: HashAlg(params.SnapModelAuthHash),
Salt: salt[:]}}}}

authKey, err := kd.snapModelAuthKey(params.PrimaryKey)
Expand Down
56 changes: 33 additions & 23 deletions keydata_legacy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,32 @@ import (
"math/rand"

. "github.com/snapcore/secboot"
"golang.org/x/crypto/cryptobyte"
. "gopkg.in/check.v1"
)

type keyDataLegacyTestBase struct {
type keyDataLegacySuite struct {
keyDataTestBase
}

func (s *keyDataLegacyTestBase) SetUpSuite(c *C) {
var _ = Suite(&keyDataLegacySuite{})

func (s *keyDataLegacySuite) SetUpSuite(c *C) {
s.handler = &mockPlatformKeyDataHandler{}
s.mockPlatformName = "mock-legacy"
RegisterPlatformKeyDataHandler(s.mockPlatformName, s.handler)
}

func (s *keyDataLegacyTestBase) SetUpTest(c *C) {
func (s *keyDataLegacySuite) SetUpTest(c *C) {
s.handler.state = mockPlatformDeviceStateOK
s.handler.passphraseSupport = false
}

func (s *keyDataLegacyTestBase) TearDownSuite(c *C) {
func (s *keyDataLegacySuite) TearDownSuite(c *C) {
RegisterPlatformKeyDataHandler(s.mockPlatformName, nil)
}

func (s *keyDataLegacyTestBase) newKeyDataKeys(c *C, sz1, sz2 int) (DiskUnlockKey, PrimaryKey) {
func (s *keyDataLegacySuite) newKeyDataKeys(c *C, sz1, sz2 int) (DiskUnlockKey, PrimaryKey) {
key := make([]byte, sz1)
auxKey := make([]byte, sz2)
_, err := rand.Read(key)
Expand All @@ -41,44 +44,45 @@ func (s *keyDataLegacyTestBase) newKeyDataKeys(c *C, sz1, sz2 int) (DiskUnlockKe
return key, auxKey
}

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

k := make([]byte, 48)
k := make([]byte, 44)
_, err := rand.Read(k)
c.Assert(err, IsNil)

handle := mockPlatformKeyDataHandle{
Key: k[:32],
IV: k[32:],
Key: k[:32],
Nonce: k[32:],
}

aad := mockPlatformAdditionalData{
ExpectedGeneration: 1,
ExpectedKDFAlg: kdfAlg,
ExpectedAuthMode: AuthModeNone,
}
builder := cryptobyte.NewBuilder(nil)
aad.MarshalASN1(builder)
aadBytes, err := builder.Bytes()
c.Assert(err, IsNil)

h := hmac.New(func() hash.Hash { return kdfAlg.New() }, handle.Key)
handle.AuthKeyHMAC = h.Sum(nil)

b, err := aes.NewCipher(handle.Key)
c.Assert(err, IsNil)
stream := cipher.NewCFBEncrypter(b, handle.IV)
aead, err := cipher.NewGCM(b)
c.Assert(err, IsNil)
ciphertext := aead.Seal(nil, handle.Nonce, payload, aadBytes)

out = &KeyParams{
PlatformName: s.mockPlatformName,
Handle: &handle,
EncryptedPayload: make([]byte, len(payload)),
EncryptedPayload: ciphertext,
PrimaryKey: auxKey,
SnapModelAuthHash: modelAuthHash}
stream.XORKeyStream(out.EncryptedPayload, payload)
return
}

type keyDataLegacySuite struct {
keyDataLegacyTestBase
}

var _ = Suite(&keyDataLegacySuite{})

type testLegacyKeyPayloadData struct {
key DiskUnlockKey
auxKey PrimaryKey
Expand Down Expand Up @@ -124,6 +128,7 @@ func (s *keyDataLegacySuite) TestLegacyKeyPayloadUnmarshalInvalid1(c *C) {

key, auxKey, err := UnmarshalV1KeyPayload(payload)
c.Check(err, ErrorMatches, "EOF")

c.Check(key, IsNil)
c.Check(auxKey, IsNil)
}
Expand All @@ -134,6 +139,7 @@ func (s *keyDataLegacySuite) TestLegacyKeyPayloadUnmarshalInvalid2(c *C) {

key, auxKey, err := UnmarshalV1KeyPayload(payload)
c.Check(err, ErrorMatches, "1 excess byte\\(s\\)")

c.Check(key, IsNil)
c.Check(auxKey, IsNil)
}
Expand All @@ -144,11 +150,12 @@ func (s *keyDataLegacySuite) TestRecoverKeys(c *C) {

restore := MockKeyDataGeneration(0)
defer restore()
keyData, err := NewKeyData(protected)

keyData, err := NewKeyData(protected)
c.Assert(err, IsNil)

recoveredKey, recoveredAuxKey, err := keyData.RecoverKeys()
c.Check(err, IsNil)
c.Assert(err, IsNil)
c.Check(recoveredKey, DeepEquals, key)
c.Check(recoveredAuxKey, DeepEquals, auxKey)
}
Expand All @@ -161,9 +168,10 @@ func (s *keyDataLegacySuite) TestRecoverKeysUnrecognizedPlatform(c *C) {

restore := MockKeyDataGeneration(0)
defer restore()
keyData, err := NewKeyData(protected)

keyData, err := NewKeyData(protected)
c.Assert(err, IsNil)

recoveredKey, recoveredAuxKey, err := keyData.RecoverKeys()
c.Check(err, ErrorMatches, "no appropriate platform handler is registered")
c.Check(recoveredKey, IsNil)
Expand All @@ -178,9 +186,10 @@ func (s *keyDataLegacySuite) TestRecoverKeysInvalidData(c *C) {

restore := MockKeyDataGeneration(0)
defer restore()
keyData, err := NewKeyData(protected)

keyData, err := NewKeyData(protected)
c.Assert(err, IsNil)

recoveredKey, recoveredAuxKey, err := keyData.RecoverKeys()
c.Check(err, ErrorMatches, "invalid key data: JSON decode error: json: cannot unmarshal string into Go value of type secboot_test.mockPlatformKeyDataHandle")
c.Check(recoveredKey, IsNil)
Expand All @@ -193,6 +202,7 @@ func (s *keyDataLegacySuite) TestRecoverKeysWithPassphraseAuthModeNone(c *C) {

keyData, err := NewKeyData(protected)
c.Assert(err, IsNil)

recoveredKey, recoveredAuxKey, err := keyData.RecoverKeysWithPassphrase("", nil)
c.Check(err, ErrorMatches, "cannot recover key with passphrase")
c.Check(recoveredKey, IsNil)
Expand Down
Loading