File: tpmkeyexchange.go

package info (click to toggle)
age-plugin-tpm 1.0.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 196 kB
  • sloc: makefile: 23
file content (90 lines) | stat: -rw-r--r-- 2,364 bytes parent folder | download | duplicates (2)
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
}