1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
|
package keyfile
import (
"bytes"
"crypto"
"crypto/ecdh"
"crypto/ecdsa"
"crypto/rsa"
encasn1 "encoding/asn1"
"fmt"
"github.com/foxboron/go-tpm-keyfiles/template"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpm2/transport"
)
type TPMPolicy struct {
CommandCode int
CommandPolicy []byte
}
type TPMAuthPolicy struct {
Name string
Policy []*TPMPolicy
}
type TPMKey struct {
Keytype encasn1.ObjectIdentifier
EmptyAuth bool
Policy []*TPMPolicy
Secret tpm2.TPM2BEncryptedSecret
AuthPolicy []*TPMAuthPolicy
Description string
Parent tpm2.TPMHandle
Pubkey tpm2.TPM2BPublic
Privkey tpm2.TPM2BPrivate
userAuth []byte // Internal detail
}
func NewTPMKey(oid encasn1.ObjectIdentifier, pubkey tpm2.TPM2BPublic, privkey tpm2.TPM2BPrivate, fn ...TPMKeyOption) *TPMKey {
var key TPMKey
// Set defaults
key.AddOptions(
WithKeytype(oid),
// We always start of with assuming this key shouldn't have an auth
WithUserAuth([]byte(nil)),
// Start out with setting the Owner as parent
WithParent(tpm2.TPMRHOwner),
WithPubkey(pubkey),
WithPrivkey(privkey),
)
key.AddOptions(fn...)
return &key
}
func (t *TPMKey) AddOptions(fn ...TPMKeyOption) {
// Run over TPMKeyFn
for _, f := range fn {
f(t)
}
}
// Internal function to deserialize the TPMTPublic
func (t *TPMKey) contents() *tpm2.TPMTPublic {
pub, err := t.Pubkey.Contents()
if err != nil {
// This should just not happen. So panic if we get this
// Prevents a bunch of error in our code.
panic(fmt.Sprintf("can't serialize public key: %v", err))
}
return pub
}
func (t *TPMKey) HasSigner() bool {
return t.contents().ObjectAttributes.SignEncrypt
}
func (t *TPMKey) HasAuth() bool {
return !t.EmptyAuth
}
func (t *TPMKey) KeyAlgo() tpm2.TPMAlgID {
return t.contents().Type
}
func (t *TPMKey) KeySize() int {
pubkey, err := t.PublicKey()
if err != nil {
return 0
}
switch pk := pubkey.(type) {
case *ecdsa.PublicKey:
// TODO: IDK yo
return 0
case *rsa.PublicKey:
return pk.Size()
}
return 0
}
func (t *TPMKey) Bytes() []byte {
var b bytes.Buffer
if err := Encode(&b, t); err != nil {
return nil
}
return b.Bytes()
}
// PublicKey returns the ecdsa.Publickey or rsa.Publickey of the TPMKey
func (t *TPMKey) PublicKey() (crypto.PublicKey, error) {
return template.FromTPMPublicToPubkey(t.contents())
}
// Wraps TPMSigner with some sane defaults
// Use NewTPMSigner if you need more control of the parameters
func (t *TPMKey) Signer(tpm transport.TPMCloser, ownerAuth, auth []byte) (crypto.Signer, error) {
if !t.HasSigner() {
// TODO: Implement support for signing with Decrypt operations
return nil, fmt.Errorf("does not have sign/encrypt attribute set")
}
return NewTPMKeySigner(
t,
func() ([]byte, error) { return ownerAuth, nil },
func() transport.TPMCloser { return tpm },
func(_ *TPMKey) ([]byte, error) { return auth, nil },
), nil
}
func (t *TPMKey) Verify(alg crypto.Hash, hashed []byte, sig []byte) (bool, error) {
pubkey, err := t.PublicKey()
if err != nil {
return false, fmt.Errorf("failed getting pubkey: %v", err)
}
switch pk := pubkey.(type) {
case *ecdsa.PublicKey:
if !ecdsa.VerifyASN1(pk, hashed[:], sig) {
return false, fmt.Errorf("invalid signature")
}
case *rsa.PublicKey:
if err := rsa.VerifyPKCS1v15(pk, alg, hashed[:], sig); err != nil {
return false, fmt.Errorf("signature verification failed: %v", err)
}
}
return true, nil
}
func (t *TPMKey) Derive(tpm transport.TPMCloser, sessionkey *ecdh.PublicKey, ownerAuth, auth []byte) ([]byte, error) {
// TODO: This should only be available for ECC keys
sess := NewTPMSession(tpm)
defer sess.FlushHandle()
return DeriveECDH(sess, t, sessionkey, ownerAuth, auth)
}
|