diff --git a/bootscope/export_test.go b/bootscope/export_test.go index 3f8b0224..07bdd93f 100644 --- a/bootscope/export_test.go +++ b/bootscope/export_test.go @@ -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 diff --git a/bootscope/keydata.go b/bootscope/keydata.go index d2882b47..f5448a2e 100644 --- a/bootscope/keydata.go +++ b/bootscope/keydata.go @@ -41,7 +41,7 @@ import ( ) const ( - nilHash hashAlg = 0 + nilHash secboot.HashAlg = 0 ) var ( @@ -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) @@ -213,8 +127,8 @@ 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 { @@ -222,9 +136,9 @@ type additionalData struct { 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 } @@ -232,9 +146,9 @@ 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 }) } @@ -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), }, }, }, @@ -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), diff --git a/bootscope/keydata_test.go b/bootscope/keydata_test.go index 453b6784..17b61e54 100644 --- a/bootscope/keydata_test.go +++ b/bootscope/keydata_test.go @@ -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) @@ -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))) @@ -660,7 +660,7 @@ 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) @@ -668,7 +668,7 @@ func (s *keyDataPlatformSuite) TestHashAlgUnmarshalJSON(c *C) { } func (s *keyDataPlatformSuite) TestHashAlgUnmarshalJSONInvalid(c *C) { - hashAlg := NewHashAlg(crypto.SHA256) + hashAlg := HashAlg(crypto.SHA256) err := hashAlg.UnmarshalJSON([]byte("}")) e, ok := err.(*json.SyntaxError) diff --git a/export_test.go b/export_test.go index c2b4762f..e24f3cc8 100644 --- a/export_test.go +++ b/export_test.go @@ -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() { diff --git a/keydata.go b/keydata.go index 0bb9c373..19b0a992 100644 --- a/keydata.go +++ b/keydata.go @@ -39,7 +39,7 @@ import ( const ( kdfType = "argon2i" - nilHash hashAlg = 0 + nilHash HashAlg = 0 passphraseKeyLen = 32 passphraseEncryptionKeyLen = 32 passphraseEncryption = "aes-cfb" @@ -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) { @@ -225,7 +229,7 @@ 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 @@ -233,15 +237,15 @@ func (a *hashAlg) UnmarshalJSON(b []byte) error { 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. @@ -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 @@ -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"` @@ -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)) }) @@ -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, }, } diff --git a/keydata_legacy.go b/keydata_legacy.go index 40b1ce3c..0feebd9c 100644 --- a/keydata_legacy.go +++ b/keydata_legacy.go @@ -85,15 +85,15 @@ 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"` } @@ -101,8 +101,8 @@ type keyDigest struct { // 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 diff --git a/keydata_legacy_test.go b/keydata_legacy_test.go index bfa867b7..6e88a816 100644 --- a/keydata_legacy_test.go +++ b/keydata_legacy_test.go @@ -25,26 +25,28 @@ func marshalV1Keys(key DiskUnlockKey, auxKey PrimaryKey) []byte { return w.Bytes() } -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) @@ -54,7 +56,7 @@ 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 := marshalV1Keys(key, auxKey) k := make([]byte, 48) @@ -84,12 +86,6 @@ func (s *keyDataLegacyTestBase) mockProtectKeys(c *C, key DiskUnlockKey, auxKey return } -type keyDataLegacySuite struct { - keyDataLegacyTestBase -} - -var _ = Suite(&keyDataLegacySuite{}) - type testLegacyKeyPayloadData struct { key DiskUnlockKey auxKey PrimaryKey diff --git a/keydata_test.go b/keydata_test.go index ba585f41..1b8874b4 100644 --- a/keydata_test.go +++ b/keydata_test.go @@ -45,7 +45,6 @@ import ( "golang.org/x/crypto/cryptobyte" cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1" "golang.org/x/crypto/hkdf" - "golang.org/x/xerrors" . "gopkg.in/check.v1" ) @@ -84,7 +83,7 @@ func (h *mockPlatformKeyDataHandler) checkState() error { func (h *mockPlatformKeyDataHandler) unmarshalHandle(data *PlatformKeyData) (*mockPlatformKeyDataHandle, error) { var handle mockPlatformKeyDataHandle if err := json.Unmarshal(data.EncodedHandle, &handle); err != nil { - return nil, &PlatformHandlerError{Type: PlatformHandlerErrorInvalidData, Err: xerrors.Errorf("JSON decode error: %w", err)} + return nil, &PlatformHandlerError{Type: PlatformHandlerErrorInvalidData, Err: fmt.Errorf("JSON decode error: %w", err)} } if data.Generation != handle.ExpectedGeneration { @@ -117,7 +116,7 @@ func (h *mockPlatformKeyDataHandler) checkKey(handle *mockPlatformKeyDataHandle, func (h *mockPlatformKeyDataHandler) recoverKeys(handle *mockPlatformKeyDataHandle, payload []byte) ([]byte, error) { b, err := aes.NewCipher(handle.Key) if err != nil { - return nil, xerrors.Errorf("cannot create cipher: %w", err) + return nil, fmt.Errorf("cannot create cipher: %w", err) } s := cipher.NewCFBDecrypter(b, handle.IV) @@ -489,7 +488,7 @@ func (s *keyDataTestBase) checkKeyDataJSONDecodedAuthModePassphrase(c *C, j map[ c.Check(err, IsNil) // TODO properly unmarshal from field - // and expose hashAlg helpers + // and expose HashAlg helpers kdfAlg := crypto.SHA256 sha256Oid := asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1} @@ -1626,12 +1625,9 @@ func (s *keyDataSuite) TestKeyDataDerivePassphraseKeysExpectedInfoFields(c *C) { `"digest":"8sVvLZOkRD6RWjLFSp/pOPrKoibsr+VWyGhv4M2aph8="},` + `"hmacs":null}} `) - expectedKey, err := base64.StdEncoding.DecodeString("C058QWvAAc5sp6Ef2NeQwk0mJk8OS4wrcceYEruHXno=") - c.Check(err, IsNil) - expectedIV, err := base64.StdEncoding.DecodeString("x78OL7OTqRQfONsOb8yaPQ==") - c.Check(err, IsNil) - expectedAuth, err := base64.StdEncoding.DecodeString("+AdPOck2Ek8CyCVfSOV3eYClrQMiNqAri0Ra4Ldbohc=") - c.Check(err, IsNil) + expectedKey := testutil.DecodeHexString(c, "0b4e7c416bc001ce6ca7a11fd8d790c24d26264f0e4b8c2b71c79812bb875e7a") + expectedIV := testutil.DecodeHexString(c, "c7bf0e2fb393a9141f38db0e6fcc9a3d") + expectedAuth := testutil.DecodeHexString(c, "f8074f39c936124f02c8255f48e5777980a5ad032236a02b8b445ae0b75ba217") kd, err := ReadKeyData(&mockKeyDataReader{"foo", bytes.NewReader(j)}) c.Assert(err, IsNil)