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
|
package plugin
import (
"fmt"
"filippo.io/hpke/crypto/ecdh"
"filippo.io/nistec"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpm2/transport"
)
type TPMKeyExchange struct {
tpm transport.TPMCloser
i *Identity
pin []byte
}
var _ ecdh.KeyExchanger = &TPMKeyExchange{}
func NewTPMKeyExchange(tpm transport.TPMCloser, pin []byte, i *Identity) *TPMKeyExchange {
return &TPMKeyExchange{
tpm, i, pin,
}
}
func (t *TPMKeyExchange) PublicKey() *ecdh.PublicKey {
return t.i.Publickey()
}
func (t *TPMKeyExchange) Curve() ecdh.Curve {
// TODO: We can derive this from the TPM key. But this is never going to change.
return ecdh.P256()
}
func (t *TPMKeyExchange) ECDH(remoteKey *ecdh.PublicKey) ([]byte, error) {
// We'll be using the SRK for the session encryption, and we need it as the
// parent for our application key. Make sure it's created and available.
srkHandle, srkPublic, err := AcquireIdentitySRK(t.tpm, t.i)
if err != nil {
return nil, err
}
defer FlushHandle(t.tpm, srkHandle)
// We load the identity into the TPM, using the SRK parent.
handle, err := LoadIdentityWithParent(t.tpm, *srkHandle, t.i)
if err != nil {
return nil, err
}
defer FlushHandle(t.tpm, handle.Handle)
// Add the AuthSession for the handle
handle.Auth = tpm2.PasswordAuth(t.pin)
p, err := nistec.NewP256Point().SetBytes(remoteKey.Bytes())
if err != nil {
return nil, err
}
// Get X/Y points from sessionkey
x, y := xyECC(p.Bytes())
// ECDHZGen command for the TPM, turns the sesion key into something we understand.
ecdh := tpm2.ECDHZGen{
KeyHandle: *handle,
InPoint: tpm2.New2B(
tpm2.TPMSECCPoint{
X: tpm2.TPM2BECCParameter{Buffer: x},
Y: tpm2.TPM2BECCParameter{Buffer: y},
},
),
}
// Execute the ECDHZGen command, we also add session encryption.
// In this case the session encryption only encrypts the private part going out of the TPM, which is the shared
// session key we are using in our kdf.
ecdhRsp, err := ecdh.Execute(t.tpm,
tpm2.HMAC(tpm2.TPMAlgSHA256, 16,
tpm2.AESEncryption(128, tpm2.EncryptOut),
tpm2.Salted(srkHandle.Handle, *srkPublic)))
if err != nil {
return nil, fmt.Errorf("failed ecdhzgen: %v", err)
}
shared, err := ecdhRsp.OutPoint.Contents()
if err != nil {
return nil, fmt.Errorf("failed getting ecdh point: %v", err)
}
return shared.X.Buffer, nil
}
|