Skip to content

Commit

Permalink
Fix parsing of secret, policy and authPolicy
Browse files Browse the repository at this point in the history
  • Loading branch information
maraino committed Oct 30, 2023
1 parent 6f3d63c commit 6419a80
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 62 deletions.
118 changes: 71 additions & 47 deletions tpm/tss2/tss2.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import (
cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
)

var (
oidLoadableKey = asn1.ObjectIdentifier{2, 23, 133, 10, 1, 3}
oidImportableKey = asn1.ObjectIdentifier{2, 23, 133, 10, 1, 4}
oidSealedKey = asn1.ObjectIdentifier{2, 23, 133, 10, 1, 5}
)

// TPMKey is defined in https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html#section-3.1:
//
// TPMKey ::= SEQUENCE {
Expand All @@ -23,9 +29,9 @@ import (
type TPMKey struct {
Type asn1.ObjectIdentifier
EmptyAuth bool `asn1:"optional,explicit,tag:0"`
Policy []TPMPolicy `asn1:"optional,explicit,tag:0"`
Secret []byte `asn1:"optional,explicit,tag:0"`
AuthPolicy []TPMAuthPolicy `asn1:"optional,explicit,tag:0"`
Policy []TPMPolicy `asn1:"optional,explicit,tag:1"`
Secret []byte `asn1:"optional,explicit,tag:2"`
AuthPolicy []TPMAuthPolicy `asn1:"optional,explicit,tag:3"`
Parent int
Pubkey []byte
Privkey []byte
Expand Down Expand Up @@ -53,7 +59,10 @@ type TPMAuthPolicy struct {
Policy []TPMPolicy `asn1:"explicit,tag:1"`
}

func ParseTSS2PrivateKey(derBytes []byte) (*TPMKey, error) {
// ParsePrivateKey parses a single TPM key from the given ASN.1 DER data.
func ParsePrivateKey(derBytes []byte) (*TPMKey, error) {
var err error

input := cryptobyte.String(derBytes)
if !input.ReadASN1(&input, cryptobyte_asn1.SEQUENCE) {
return nil, errors.New("malformed TSS2 key")
Expand All @@ -70,57 +79,29 @@ func ParseTSS2PrivateKey(derBytes []byte) (*TPMKey, error) {
}

Check warning on line 79 in tpm/tss2/tss2.go

View check run for this annotation

Codecov / codecov/patch

tpm/tss2/tss2.go#L78-L79

Added lines #L78 - L79 were not covered by tests
}

var err error
var isPresent bool

// TODO(mariano): generate key with policy
if tag, ok := readOptionalTag(&input, 1); ok {
var policy cryptobyte.String
if !tag.ReadOptionalASN1(&policy, &isPresent, cryptobyte_asn1.SEQUENCE) {
if !tag.ReadASN1(&policy, cryptobyte_asn1.SEQUENCE) {
return nil, errors.New("malformed TSS2 policy")
}

Check warning on line 87 in tpm/tss2/tss2.go

View check run for this annotation

Codecov / codecov/patch

tpm/tss2/tss2.go#L86-L87

Added lines #L86 - L87 were not covered by tests
if isPresent {
key.Policy, err = readTPMPolicySequence(&policy)
if err != nil {
return nil, err
}
key.Policy, err = readTPMPolicySequence(&policy)
if err != nil {
return nil, err
}

Check warning on line 91 in tpm/tss2/tss2.go

View check run for this annotation

Codecov / codecov/patch

tpm/tss2/tss2.go#L90-L91

Added lines #L90 - L91 were not covered by tests
}

// TODO(mariano): generate key with secret
if tag, ok := readOptionalTag(&input, 2); ok {
if !tag.ReadOptionalASN1OctetString(&key.Secret, &isPresent, cryptobyte_asn1.OCTET_STRING) {
if key.Secret, ok = readOctetString(&tag); !ok {
return nil, errors.New("malformed TSS2 secret")
}

Check warning on line 98 in tpm/tss2/tss2.go

View check run for this annotation

Codecov / codecov/patch

tpm/tss2/tss2.go#L97-L98

Added lines #L97 - L98 were not covered by tests
}

// TODO(mariano): generate key with authPolicy
if tag, ok := readOptionalTag(&input, 3); ok {
var authPolicy cryptobyte.String
if !tag.ReadOptionalASN1(&authPolicy, &isPresent, cryptobyte_asn1.SEQUENCE) {
return nil, errors.New("malformed TSS2 authPolicy")
}
if isPresent {
var policy cryptobyte.String
for !authPolicy.Empty() {
var ap TPMAuthPolicy
var seq cryptobyte.String
if !policy.ReadASN1Element(&seq, cryptobyte_asn1.SEQUENCE) {
return nil, errors.New("malformed TSS2 authPolicy")
}
var name cryptobyte.String
if !seq.ReadOptionalASN1(&name, &isPresent, cryptobyte_asn1.UTF8String) {
return nil, errors.New("malformed TSS2 authPolicy name")
}
var policySeq cryptobyte.String
if !seq.ReadASN1Element(&policySeq, cryptobyte_asn1.SEQUENCE) {
return nil, errors.New("malformed TSS2 authPolicy policy")
}
if ap.Policy, err = readTPMPolicySequence(&policySeq); err != nil {
return nil, errors.New("malformed TSS2 authPolicy policy")
}
key.AuthPolicy = append(key.AuthPolicy, ap)
}
if key.AuthPolicy, err = readTPMAuthPolicy(&tag); err != nil {
return nil, err
}

Check warning on line 105 in tpm/tss2/tss2.go

View check run for this annotation

Codecov / codecov/patch

tpm/tss2/tss2.go#L104-L105

Added lines #L104 - L105 were not covered by tests
}

Expand All @@ -140,7 +121,8 @@ func ParseTSS2PrivateKey(derBytes []byte) (*TPMKey, error) {
return key, nil
}

func MarshalTSS2PrivateKey(key TPMKey) ([]byte, error) {
// MarshalPrivateKey converts the give key to a TSS2 ASN.1 DER form.
func MarshalPrivateKey(key TPMKey) ([]byte, error) {
return asn1.Marshal(key)
}

Expand Down Expand Up @@ -183,23 +165,65 @@ func readASN1Boolean(input *cryptobyte.String, out *bool) bool {
}

func readTPMPolicySequence(input *cryptobyte.String) ([]TPMPolicy, error) {
var (
ok bool
policies []TPMPolicy
)
var policies []TPMPolicy
for !input.Empty() {
var p TPMPolicy
var seq cryptobyte.String
if !input.ReadASN1Element(&seq, cryptobyte_asn1.SEQUENCE) {
if !input.ReadASN1(&seq, cryptobyte_asn1.SEQUENCE) {
return nil, errors.New("malformed TSS2 policy")
}

Check warning on line 174 in tpm/tss2/tss2.go

View check run for this annotation

Codecov / codecov/patch

tpm/tss2/tss2.go#L173-L174

Added lines #L173 - L174 were not covered by tests
if !seq.ReadASN1Integer(&p.CommandCode) {
tag, ok := readOptionalTag(&seq, 0)
if !ok || !tag.ReadASN1Integer(&p.CommandCode) {
return nil, errors.New("malformed TSS2 policy commandCode")
}

Check warning on line 178 in tpm/tss2/tss2.go

View check run for this annotation

Codecov / codecov/patch

tpm/tss2/tss2.go#L177-L178

Added lines #L177 - L178 were not covered by tests
if p.CommandPolicy, ok = readOctetString(&seq); ok {
tag, ok = readOptionalTag(&seq, 1)
if !ok {
return nil, errors.New("malformed TSS2 policy commandPolicy")
}

Check warning on line 182 in tpm/tss2/tss2.go

View check run for this annotation

Codecov / codecov/patch

tpm/tss2/tss2.go#L181-L182

Added lines #L181 - L182 were not covered by tests
if p.CommandPolicy, ok = readOctetString(&tag); !ok {
return nil, errors.New("malformed TSS2 policy commandPolicy")
}

Check warning on line 185 in tpm/tss2/tss2.go

View check run for this annotation

Codecov / codecov/patch

tpm/tss2/tss2.go#L184-L185

Added lines #L184 - L185 were not covered by tests
policies = append(policies, p)
}
return policies, nil
}

func readTPMAuthPolicy(input *cryptobyte.String) ([]TPMAuthPolicy, error) {
var (
err error
authPolicy cryptobyte.String
authPolicies []TPMAuthPolicy
)
if !input.ReadASN1(&authPolicy, cryptobyte_asn1.SEQUENCE) {
return nil, errors.New("malformed TSS2 authPolicy")
}

Check warning on line 199 in tpm/tss2/tss2.go

View check run for this annotation

Codecov / codecov/patch

tpm/tss2/tss2.go#L198-L199

Added lines #L198 - L199 were not covered by tests

for !authPolicy.Empty() {
var ap TPMAuthPolicy
var seq cryptobyte.String
if !authPolicy.ReadASN1(&seq, cryptobyte_asn1.SEQUENCE) {
return nil, errors.New("malformed TSS2 authPolicy")
}

Check warning on line 206 in tpm/tss2/tss2.go

View check run for this annotation

Codecov / codecov/patch

tpm/tss2/tss2.go#L205-L206

Added lines #L205 - L206 were not covered by tests

var name cryptobyte.String
if tag, ok := readOptionalTag(&seq, 0); ok {
if !tag.ReadASN1(&name, cryptobyte_asn1.UTF8String) {
return nil, errors.New("malformed TSS2 authPolicy name")
}

Check warning on line 212 in tpm/tss2/tss2.go

View check run for this annotation

Codecov / codecov/patch

tpm/tss2/tss2.go#L211-L212

Added lines #L211 - L212 were not covered by tests
ap.Name = string(name)
}

var policySeq cryptobyte.String
if tag, ok := readOptionalTag(&seq, 1); ok {
if !tag.ReadASN1(&policySeq, cryptobyte_asn1.SEQUENCE) {
return nil, errors.New("malformed TSS2 authPolicy policy")
}

Check warning on line 220 in tpm/tss2/tss2.go

View check run for this annotation

Codecov / codecov/patch

tpm/tss2/tss2.go#L219-L220

Added lines #L219 - L220 were not covered by tests
if ap.Policy, err = readTPMPolicySequence(&policySeq); err != nil {
return nil, errors.New("malformed TSS2 authPolicy policy")
}

Check warning on line 223 in tpm/tss2/tss2.go

View check run for this annotation

Codecov / codecov/patch

tpm/tss2/tss2.go#L222-L223

Added lines #L222 - L223 were not covered by tests
}
authPolicies = append(authPolicies, ap)
}

return authPolicies, nil
}
154 changes: 139 additions & 15 deletions tpm/tss2/tss2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package tss2

import (
"encoding/pem"
"reflect"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
)

// key generated using:
Expand Down Expand Up @@ -36,7 +38,21 @@ W6fVV4B/ZtnADx9/YGB9FBY8Bu07W7m+PorVTCbXFfOAFmSUg3eB0bgb2TRtFevZ
izcX
-----END TSS2 PRIVATE KEY-----`

func TestParseTSS2PrivateKey(t *testing.T) {
// key generated using:
//
// tpm2_createprimary -c primary.ctx
// tpm2_create -C primary.ctx -G ecc -u obj.pub -r obj.priv
// tpm2_encodeobject -C primary.ctx -u obj.pub -r obj.priv -o obj.pem
var p256EmptyAuthFalse = `-----BEGIN TSS2 PRIVATE KEY-----
MIHwBgZngQUKAQOgAwEBAAIEQAAAAQRYAFYAIwALAAYAcgAAABAAEAADABAAIIgq
1VllQRCT45GbLlp1Wud0jiSfojBwp1MYljWMw1T7ACAblgTFkwvSMnzpArA8GjVP
ULHy7pJubvS2W7TxmzclRQSBgAB+ACDVXoK8RpE5XxjBcfeHpip9Dz2j7AUj0oE1
RKlDg/+dYgAQd9c9mgioJc8wFL1zaU4viH1fq3fObbfZF/L8oLrLv6u3Pg8qeGzf
ePVypgEUeJGw68er7UZb4ZSVfoGId6KLX9JE7IwyBkRWLhBU3sLANdgjTqlXUhAD
mnYo
-----END TSS2 PRIVATE KEY-----`

func TestParsePrivateKey(t *testing.T) {
parsePEM := func(s string) []byte {
block, _ := pem.Decode([]byte(s))
return block.Bytes
Expand All @@ -46,13 +62,13 @@ func TestParseTSS2PrivateKey(t *testing.T) {
derBytes []byte
}
tests := []struct {
name string
args args
want *TPMKey
wantErr bool
name string
args args
want *TPMKey
assertion assert.ErrorAssertionFunc
}{
{"ok rsa", args{parsePEM(rsaTSS2PEM)}, &TPMKey{
Type: []int{2, 23, 133, 10, 1, 3},
Type: oidLoadableKey,
EmptyAuth: true,
Parent: 1073741825,
Pubkey: []byte{
Expand Down Expand Up @@ -91,9 +107,9 @@ func TestParseTSS2PrivateKey(t *testing.T) {
0x70, 0xbe, 0x9b, 0x37, 0xbc, 0xea, 0x7d, 0xf6, 0x4e, 0x43, 0xb4, 0xae, 0x14, 0x22, 0x03, 0x29,
0xfc, 0x8d, 0x2e, 0xad, 0x6d, 0x47, 0xba, 0x6f, 0x78, 0xd6, 0xc7, 0xb6, 0x1f, 0x28, 0xfe, 0xa0,
},
}, false},
}, assert.NoError},
{"ok ec", args{parsePEM(p256TSS2PEM)}, &TPMKey{
Type: []int{2, 23, 133, 10, 1, 3},
Type: oidLoadableKey,
EmptyAuth: true,
Parent: 1073741825,
Pubkey: []byte{
Expand All @@ -114,17 +130,125 @@ func TestParseTSS2PrivateKey(t *testing.T) {
0xed, 0x3b, 0x5b, 0xb9, 0xbe, 0x3e, 0x8a, 0xd5, 0x4c, 0x26, 0xd7, 0x15, 0xf3, 0x80, 0x16, 0x64,
0x94, 0x83, 0x77, 0x81, 0xd1, 0xb8, 0x1b, 0xd9, 0x34, 0x6d, 0x15, 0xeb, 0xd9, 0x8b, 0x37, 0x17,
},
}, false},
}, assert.NoError},
{"ok emptyAuth false", args{parsePEM(p256EmptyAuthFalse)}, &TPMKey{
Type: oidLoadableKey,
EmptyAuth: false,
Parent: 1073741825,
Pubkey: []byte{
0x00, 0x56, 0x00, 0x23, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x72, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10,
0x00, 0x03, 0x00, 0x10, 0x00, 0x20, 0x88, 0x2a, 0xd5, 0x59, 0x65, 0x41, 0x10, 0x93, 0xe3, 0x91,
0x9b, 0x2e, 0x5a, 0x75, 0x5a, 0xe7, 0x74, 0x8e, 0x24, 0x9f, 0xa2, 0x30, 0x70, 0xa7, 0x53, 0x18,
0x96, 0x35, 0x8c, 0xc3, 0x54, 0xfb, 0x00, 0x20, 0x1b, 0x96, 0x04, 0xc5, 0x93, 0x0b, 0xd2, 0x32,
0x7c, 0xe9, 0x02, 0xb0, 0x3c, 0x1a, 0x35, 0x4f, 0x50, 0xb1, 0xf2, 0xee, 0x92, 0x6e, 0x6e, 0xf4,
0xb6, 0x5b, 0xb4, 0xf1, 0x9b, 0x37, 0x25, 0x45,
},
Privkey: []byte{
0x00, 0x7e, 0x00, 0x20, 0xd5, 0x5e, 0x82, 0xbc, 0x46, 0x91, 0x39, 0x5f, 0x18, 0xc1, 0x71, 0xf7,
0x87, 0xa6, 0x2a, 0x7d, 0x0f, 0x3d, 0xa3, 0xec, 0x05, 0x23, 0xd2, 0x81, 0x35, 0x44, 0xa9, 0x43,
0x83, 0xff, 0x9d, 0x62, 0x00, 0x10, 0x77, 0xd7, 0x3d, 0x9a, 0x08, 0xa8, 0x25, 0xcf, 0x30, 0x14,
0xbd, 0x73, 0x69, 0x4e, 0x2f, 0x88, 0x7d, 0x5f, 0xab, 0x77, 0xce, 0x6d, 0xb7, 0xd9, 0x17, 0xf2,
0xfc, 0xa0, 0xba, 0xcb, 0xbf, 0xab, 0xb7, 0x3e, 0x0f, 0x2a, 0x78, 0x6c, 0xdf, 0x78, 0xf5, 0x72,
0xa6, 0x01, 0x14, 0x78, 0x91, 0xb0, 0xeb, 0xc7, 0xab, 0xed, 0x46, 0x5b, 0xe1, 0x94, 0x95, 0x7e,
0x81, 0x88, 0x77, 0xa2, 0x8b, 0x5f, 0xd2, 0x44, 0xec, 0x8c, 0x32, 0x06, 0x44, 0x56, 0x2e, 0x10,
0x54, 0xde, 0xc2, 0xc0, 0x35, 0xd8, 0x23, 0x4e, 0xa9, 0x57, 0x52, 0x10, 0x03, 0x9a, 0x76, 0x28,
},
}, assert.NoError},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParsePrivateKey(tt.args.derBytes)
tt.assertion(t, err)
assert.Equal(t, tt.want, got)
})
}
}

func TestMarshalParse(t *testing.T) {
modKey := func(fn func(key *TPMKey)) TPMKey {
fakeKey := TPMKey{
Type: oidLoadableKey,
EmptyAuth: true,
Parent: 1234,
Pubkey: []byte("pubkey"),
Privkey: []byte("privkey"),
}
fn(&fakeKey)
return fakeKey
}

fakePolicy1 := TPMPolicy{CommandCode: 1, CommandPolicy: []byte("fake-policy-1")}
fakePolicy2 := TPMPolicy{CommandCode: 2, CommandPolicy: []byte("fake-policy-2")}

type args struct {
key TPMKey
}
tests := []struct {
name string
args args
assertion assert.ErrorAssertionFunc
}{
{"ok", args{TPMKey{
Type: oidLoadableKey,
EmptyAuth: true,
Parent: 1234,
Pubkey: []byte("pubkey"),
Privkey: []byte("privkey"),
}}, assert.NoError},
{"ok importable key", args{modKey(func(key *TPMKey) {
key.Type = oidImportableKey
})}, assert.NoError},
{"ok sealed key", args{modKey(func(key *TPMKey) {
key.Type = oidSealedKey
})}, assert.NoError},
{"ok emptyAuth false", args{modKey(func(key *TPMKey) {
key.EmptyAuth = false
})}, assert.NoError},
{"ok policy", args{modKey(func(key *TPMKey) {
key.Policy = []TPMPolicy{fakePolicy1}
})}, assert.NoError},
{"ok policies", args{modKey(func(key *TPMKey) {
key.Policy = []TPMPolicy{fakePolicy1, fakePolicy2}
})}, assert.NoError},
{"ok secret", args{modKey(func(key *TPMKey) {
key.Secret = []byte("secret")
})}, assert.NoError},
{"ok authPolicy", args{modKey(func(key *TPMKey) {
key.AuthPolicy = []TPMAuthPolicy{
{Name: "auth", Policy: []TPMPolicy{fakePolicy1}},
}
})}, assert.NoError},
{"ok authPolicies", args{modKey(func(key *TPMKey) {
key.AuthPolicy = []TPMAuthPolicy{
{Name: "auth-1", Policy: []TPMPolicy{fakePolicy1}},
{Name: "auth-2", Policy: []TPMPolicy{fakePolicy1, fakePolicy2}},
}
})}, assert.NoError},
{"ok all", args{TPMKey{
Type: oidLoadableKey,
EmptyAuth: true,
Policy: []TPMPolicy{fakePolicy1, fakePolicy2},
Secret: []byte("secret"),
AuthPolicy: []TPMAuthPolicy{
{Name: "auth-1", Policy: []TPMPolicy{fakePolicy1, fakePolicy2}},
{Name: "auth-2", Policy: []TPMPolicy{fakePolicy1, fakePolicy2}},
},
Parent: 1234,
Pubkey: []byte("pubkey"),
Privkey: []byte("privkey"),
}}, assert.NoError},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseTSS2PrivateKey(tt.args.derBytes)
if (err != nil) != tt.wantErr {
t.Errorf("ParseTSS2PrivateKey() error = %v, wantErr %v", err, tt.wantErr)
derBytes, err := MarshalPrivateKey(tt.args.key)
if err != nil {
t.Errorf("MarshalPrivateKey() error = %v", err)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ParseTSS2PrivateKey() = %v, want %v", got, tt.want)
fmt.Printf("%x\n", derBytes)
got, err := ParsePrivateKey(derBytes)
if tt.assertion(t, err) {
assert.Equal(t, tt.args.key, *got)
}
})
}
Expand Down

0 comments on commit 6419a80

Please sign in to comment.