Skip to content

Commit

Permalink
Merge pull request #284 from sespiros/keydata-test-cleanup
Browse files Browse the repository at this point in the history
Keydata testsuite minor refactors and improvements and hashAlg.

This addresses:

- keyDataLegacyTestBase not used from #265 (comment)
- use DecodeHexString from #265 (comment)

Also makes hashAlg public as it will be used in a follow-up PR which will slightly rework the mockPlatformKeyDataHandler to use GCM and associated data to check expected values.
  • Loading branch information
chrisccoulson authored Apr 4, 2024
2 parents f8dedd0 + f9ce5f9 commit f53e2d4
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 154 deletions.
4 changes: 0 additions & 4 deletions bootscope/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,6 @@ func (d *KeyDataScope) DeriveSigner(key secboot.PrimaryKey, role string) (crypto
return d.deriveSigner(key, role)
}

func NewHashAlg(alg crypto.Hash) hashAlg {
return hashAlg(alg)
}

func NewEcdsaPublicKey(rand []byte) (ecdsaPublicKey, error) {
var pk ecdsaPublicKey

Expand Down
114 changes: 14 additions & 100 deletions bootscope/keydata.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import (
)

const (
nilHash hashAlg = 0
nilHash secboot.HashAlg = 0
)

var (
Expand All @@ -52,101 +52,15 @@ var (
sha512Oid = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
)

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

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

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

func (a hashAlg) HashFunc() crypto.Hash {
return crypto.Hash(a)
}

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

switch crypto.Hash(a) {
case crypto.SHA1:
s = "sha1"
case crypto.SHA224:
s = "sha224"
case crypto.SHA256:
s = "sha256"
case crypto.SHA384:
s = "sha384"
case crypto.SHA512:
s = "sha512"
default:
return nil, fmt.Errorf("unknown hash algorithm: %v", crypto.Hash(a))
}

return json.Marshal(s)
}

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)
case "sha224":
*a = hashAlg(crypto.SHA224)
case "sha256":
*a = hashAlg(crypto.SHA256)
case "sha384":
*a = hashAlg(crypto.SHA384)
case "sha512":
*a = hashAlg(crypto.SHA512)
default:
// be permissive here and allow everything to be
// unmarshalled.
*a = nilHash
}

return nil
}

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

switch crypto.Hash(a) {
case crypto.SHA1:
oid = sha1Oid
case crypto.SHA224:
oid = sha224Oid
case crypto.SHA256:
oid = sha256Oid
case crypto.SHA384:
oid = sha384Oid
case crypto.SHA512:
oid = sha512Oid
default:
b.SetError(fmt.Errorf("unknown hash algorithm: %v", crypto.Hash(a)))
return
}
b.AddASN1ObjectIdentifier(oid) // algorithm OBJECT IDENTIFIER
b.AddASN1NULL() // parameters ANY DEFINED BY algorithm OPTIONAL
})
}

// digestList corresponds to a list of digests.
type digestList struct {
Alg hashAlg `json:"alg"` // The digest algorithm
Digests [][]byte `json:"digests"` // The list of digests
Alg secboot.HashAlg `json:"alg"` // The digest algorithm
Digests [][]byte `json:"digests"` // The list of digests
}

func (l *digestList) marshalASN1WithTag(tag cryptobyte_asn1.Tag, b *cryptobyte.Builder) {
b.AddASN1(tag, func(b *cryptobyte.Builder) { // DigestList ::= SEQUENCE {
l.Alg.marshalASN1(b) // algorithm AlgorithmIdentifier
l.Alg.MarshalASN1(b) // algorithm AlgorithmIdentifier
b.AddASN1(cryptobyte_asn1.SET, func(b *cryptobyte.Builder) { // digests Digests
for _, digest := range l.Digests {
b.AddASN1OctetString(digest)
Expand Down Expand Up @@ -213,28 +127,28 @@ type keyDataScope struct {
Signature []byte `json:"signature"`
PublicKey ecdsaPublicKey `json:"pubkey"`

KDFAlg hashAlg `json:"kdf_alg"`
MDAlg hashAlg `json:"md_alg"`
KDFAlg secboot.HashAlg `json:"kdf_alg"`
MDAlg secboot.HashAlg `json:"md_alg"`
}

type additionalData struct {
// Version corresponds to the version field of the keyDataScope object
Version int
// Generation corresponds to the generation field of the keyData object
Generation int
KdfAlg hashAlg
KdfAlg secboot.HashAlg
AuthMode secboot.AuthMode
KeyIdentifierAlg hashAlg
KeyIdentifierAlg secboot.HashAlg
KeyIdentifier []byte
}

func (d *additionalData) marshalASN1(b *cryptobyte.Builder) {
b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { // SEQUENCE {
b.AddASN1Int64(int64(d.Version)) // version INTEGER
b.AddASN1Int64(int64(d.Generation)) // generation INTEGER
d.KdfAlg.marshalASN1(b) // kdfAlg AlgorithmIdentifier
d.KdfAlg.MarshalASN1(b) // kdfAlg AlgorithmIdentifier
b.AddASN1Enum(int64(d.AuthMode)) // authMode ENUMERATED
d.KeyIdentifierAlg.marshalASN1(b) // keyIdentifierAlg AlgorithmIdentifier
d.KeyIdentifierAlg.MarshalASN1(b) // keyIdentifierAlg AlgorithmIdentifier
b.AddASN1OctetString(d.KeyIdentifier) // keyIdentifier OCTET STRING
})
}
Expand Down Expand Up @@ -293,11 +207,11 @@ func NewKeyDataScope(params *KeyDataScopeParams) (*KeyDataScope, error) {
out := &KeyDataScope{
data: keyDataScope{
Version: 1,
KDFAlg: hashAlg(params.KDFAlg),
MDAlg: hashAlg(params.MDAlg),
KDFAlg: secboot.HashAlg(params.KDFAlg),
MDAlg: secboot.HashAlg(params.MDAlg),
Params: scopeParams{
ModelDigests: digestList{
Alg: hashAlg(params.ModelAlg),
Alg: secboot.HashAlg(params.ModelAlg),
},
},
},
Expand Down Expand Up @@ -514,7 +428,7 @@ func (d *KeyDataScope) MakeAEADAdditionalData(generation int, kdfAlg crypto.Hash
aad := &additionalData{
Version: d.data.Version,
Generation: generation,
KdfAlg: hashAlg(kdfAlg),
KdfAlg: secboot.HashAlg(kdfAlg),
AuthMode: authMode,
KeyIdentifierAlg: alg,
KeyIdentifier: h.Sum(nil),
Expand Down
8 changes: 4 additions & 4 deletions bootscope/keydata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,7 @@ func (s *keyDataPlatformSuite) TestHashAlgMarshalJSON(c *C) {
{crypto.SHA384, "\"sha384\""},
{crypto.SHA512, "\"sha512\""},
} {
hashAlg := NewHashAlg(t.alg)
hashAlg := HashAlg(t.alg)
hashAlgJSON, err := hashAlg.MarshalJSON()
c.Assert(err, IsNil)
c.Check(string(hashAlgJSON), Equals, t.nameAlg)
Expand All @@ -641,7 +641,7 @@ func (s *keyDataPlatformSuite) TestHashAlgMarshalJSONInvalid(c *C) {
}

for _, alg := range unsupportedAlgorithms {
hashAlg := NewHashAlg(alg)
hashAlg := HashAlg(alg)
hashAlgJSON, err := hashAlg.MarshalJSON()
c.Assert(string(hashAlgJSON), Equals, "")
c.Check(err.Error(), Equals, fmt.Sprintf("unknown hash algorithm: %v", crypto.Hash(alg)))
Expand All @@ -660,15 +660,15 @@ func (s *keyDataPlatformSuite) TestHashAlgUnmarshalJSON(c *C) {
{crypto.SHA512, "\"sha512\""},
{0, "\"foo\""},
} {
hashAlg := NewHashAlg(crypto.SHA256)
hashAlg := HashAlg(crypto.SHA256)
err := hashAlg.UnmarshalJSON([]byte(t.nameAlg))
c.Assert(err, IsNil)
c.Check(crypto.Hash(hashAlg), Equals, t.alg)
}
}

func (s *keyDataPlatformSuite) TestHashAlgUnmarshalJSONInvalid(c *C) {
hashAlg := NewHashAlg(crypto.SHA256)
hashAlg := HashAlg(crypto.SHA256)
err := hashAlg.UnmarshalJSON([]byte("}"))

e, ok := err.(*json.SyntaxError)
Expand Down
2 changes: 1 addition & 1 deletion export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,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
40 changes: 22 additions & 18 deletions keydata.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import (

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

// hashAlg corresponds to a digest algorithm.
type hashAlg crypto.Hash
// HashAlg provides an abstraction for crypto.Hash that can be serialized to JSON and DER.
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) HashFunc() crypto.Hash {
return crypto.Hash(a)
}

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 @@ -225,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 @@ -251,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 @@ -325,7 +329,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 @@ -394,7 +398,7 @@ func (d *KeyData) derivePassphraseKeys(passphrase string) (key, iv, auth []byte,
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 @@ -719,7 +723,7 @@ func NewKeyData(params *KeyParams) (*KeyData, error) {
PlatformName: params.PlatformName,
Role: params.Role,
PlatformHandle: json.RawMessage(encodedHandle),
KDFAlg: hashAlg(params.KDFAlg),
KDFAlg: HashAlg(params.KDFAlg),
EncryptedPayload: params.EncryptedPayload,
},
}
Expand Down
10 changes: 5 additions & 5 deletions keydata_legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,24 +85,24 @@ func (l snapModelHMACList) contains(h snapModelHMAC) bool {
}

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"`
}

// 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"`
}

// 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
Loading

0 comments on commit f53e2d4

Please sign in to comment.