mirror of
https://github.com/google/go-attestation.git
synced 2024-12-18 20:47:57 +00:00
a3545dfc94
call ret.FillBytes() instead of ret.Bytes() to preserve leading zeroes that may have been dropped when converting the digest to an integer
678 lines
20 KiB
Go
678 lines
20 KiB
Go
// Copyright 2020 Google Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
// use this file except in compliance with the License. You may obtain a copy of
|
|
// the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations under
|
|
// the License.
|
|
|
|
package attest
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rsa"
|
|
"encoding/asn1"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
|
|
"github.com/google/go-tpm/legacy/tpm2"
|
|
"github.com/google/go-tpm/tpmutil"
|
|
)
|
|
|
|
// wrappedTPM20 interfaces with a TPM 2.0 command channel.
|
|
type wrappedTPM20 struct {
|
|
interf TPMInterface
|
|
rwc CommandChannelTPM20
|
|
tpmRSAEkTemplate *tpm2.Public
|
|
tpmECCEkTemplate *tpm2.Public
|
|
}
|
|
|
|
func (t *wrappedTPM20) rsaEkTemplate() tpm2.Public {
|
|
if t.tpmRSAEkTemplate != nil {
|
|
return *t.tpmRSAEkTemplate
|
|
}
|
|
|
|
nonce, err := tpm2.NVReadEx(t.rwc, nvramRSAEkNonceIndex, tpm2.HandleOwner, "", 0)
|
|
if err != nil {
|
|
t.tpmRSAEkTemplate = &defaultRSAEKTemplate // No nonce, use the default template
|
|
} else {
|
|
template := defaultRSAEKTemplate
|
|
copy(template.RSAParameters.ModulusRaw, nonce)
|
|
t.tpmRSAEkTemplate = &template
|
|
}
|
|
|
|
return *t.tpmRSAEkTemplate
|
|
}
|
|
|
|
func (t *wrappedTPM20) eccEkTemplate() tpm2.Public {
|
|
if t.tpmECCEkTemplate != nil {
|
|
return *t.tpmECCEkTemplate
|
|
}
|
|
|
|
nonce, err := tpm2.NVReadEx(t.rwc, nvramECCEkNonceIndex, tpm2.HandleOwner, "", 0)
|
|
if err != nil {
|
|
t.tpmECCEkTemplate = &defaultECCEKTemplate // No nonce, use the default template
|
|
} else {
|
|
template := defaultECCEKTemplate
|
|
copy(template.ECCParameters.Point.XRaw, nonce)
|
|
t.tpmECCEkTemplate = &template
|
|
}
|
|
|
|
return *t.tpmECCEkTemplate
|
|
}
|
|
|
|
func (t *wrappedTPM20) tpmVersion() TPMVersion {
|
|
return TPMVersion20
|
|
}
|
|
|
|
func (t *wrappedTPM20) close() error {
|
|
return t.rwc.Close()
|
|
}
|
|
|
|
// Info returns information about the TPM.
|
|
func (t *wrappedTPM20) info() (*TPMInfo, error) {
|
|
var (
|
|
tInfo = TPMInfo{
|
|
Version: TPMVersion20,
|
|
Interface: t.interf,
|
|
}
|
|
t2Info tpm20Info
|
|
err error
|
|
)
|
|
|
|
if t2Info, err = readTPM2VendorAttributes(t.rwc); err != nil {
|
|
return nil, err
|
|
}
|
|
tInfo.Manufacturer = t2Info.manufacturer
|
|
tInfo.VendorInfo = t2Info.vendor
|
|
tInfo.FirmwareVersionMajor = t2Info.fwMajor
|
|
tInfo.FirmwareVersionMinor = t2Info.fwMinor
|
|
return &tInfo, nil
|
|
}
|
|
|
|
// Return value: handle, whether we generated a new one, error.
|
|
func (t *wrappedTPM20) getEndorsementKeyHandle(ek *EK) (tpmutil.Handle, bool, error) {
|
|
var ekHandle tpmutil.Handle
|
|
var ekTemplate tpm2.Public
|
|
|
|
if ek == nil {
|
|
// The default is RSA for backward compatibility.
|
|
ekHandle = commonRSAEkEquivalentHandle
|
|
ekTemplate = t.rsaEkTemplate()
|
|
} else {
|
|
ekHandle = ek.handle
|
|
if ekHandle == 0 {
|
|
// Assume RSA EK handle if it was not provided.
|
|
ekHandle = commonRSAEkEquivalentHandle
|
|
}
|
|
switch pub := ek.Public.(type) {
|
|
case *rsa.PublicKey:
|
|
ekTemplate = t.rsaEkTemplate()
|
|
case *ecdsa.PublicKey:
|
|
ekTemplate = t.eccEkTemplate()
|
|
default:
|
|
return 0, false, fmt.Errorf("unsupported public key type %T", pub)
|
|
}
|
|
}
|
|
|
|
_, _, _, err := tpm2.ReadPublic(t.rwc, ekHandle)
|
|
if err == nil {
|
|
// Found the persistent handle, assume it's the key we want.
|
|
return ekHandle, false, nil
|
|
}
|
|
rerr := err // Preserve this failure for later logging, if needed
|
|
|
|
keyHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", ekTemplate)
|
|
if err != nil {
|
|
return 0, false, fmt.Errorf("ReadPublic failed (%v), and then CreatePrimary failed: %v", rerr, err)
|
|
}
|
|
defer tpm2.FlushContext(t.rwc, keyHnd)
|
|
|
|
err = tpm2.EvictControl(t.rwc, "", tpm2.HandleOwner, keyHnd, ekHandle)
|
|
if err != nil {
|
|
return 0, false, fmt.Errorf("EvictControl failed: %v", err)
|
|
}
|
|
|
|
return ekHandle, true, nil
|
|
}
|
|
|
|
// Return value: handle, whether we generated a new one, error
|
|
func (t *wrappedTPM20) getStorageRootKeyHandle(parent ParentKeyConfig) (tpmutil.Handle, bool, error) {
|
|
srkHandle := parent.Handle
|
|
_, _, _, err := tpm2.ReadPublic(t.rwc, srkHandle)
|
|
if err == nil {
|
|
// Found the persistent handle, assume it's the key we want.
|
|
return srkHandle, false, nil
|
|
}
|
|
rerr := err // Preserve this failure for later logging, if needed
|
|
|
|
var srkTemplate tpm2.Public
|
|
switch parent.Algorithm {
|
|
case RSA:
|
|
srkTemplate = defaultRSASRKTemplate
|
|
case ECDSA:
|
|
srkTemplate = defaultECCSRKTemplate
|
|
default:
|
|
return 0, false, fmt.Errorf("unsupported SRK algorithm: %v", parent.Algorithm)
|
|
}
|
|
keyHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", srkTemplate)
|
|
if err != nil {
|
|
return 0, false, fmt.Errorf("ReadPublic failed (%v), and then CreatePrimary failed: %v", rerr, err)
|
|
}
|
|
defer tpm2.FlushContext(t.rwc, keyHnd)
|
|
|
|
err = tpm2.EvictControl(t.rwc, "", tpm2.HandleOwner, keyHnd, srkHandle)
|
|
if err != nil {
|
|
return 0, false, fmt.Errorf("EvictControl failed: %v", err)
|
|
}
|
|
|
|
return srkHandle, true, nil
|
|
}
|
|
|
|
func (t *wrappedTPM20) ekCertificates() ([]EK, error) {
|
|
var res []EK
|
|
if rsaCert, err := readEKCertFromNVRAM20(t.rwc, nvramRSACertIndex); err == nil {
|
|
res = append(res, EK{Public: crypto.PublicKey(rsaCert.PublicKey), Certificate: rsaCert, handle: commonRSAEkEquivalentHandle})
|
|
}
|
|
if eccCert, err := readEKCertFromNVRAM20(t.rwc, nvramECCCertIndex); err == nil {
|
|
res = append(res, EK{Public: crypto.PublicKey(eccCert.PublicKey), Certificate: eccCert, handle: commonECCEkEquivalentHandle})
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (t *wrappedTPM20) eks() ([]EK, error) {
|
|
if cert, err := readEKCertFromNVRAM20(t.rwc, nvramRSACertIndex); err == nil {
|
|
return []EK{
|
|
{Public: crypto.PublicKey(cert.PublicKey), Certificate: cert, handle: commonRSAEkEquivalentHandle},
|
|
}, nil
|
|
}
|
|
|
|
// Attempt to create an EK.
|
|
ekHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", t.rsaEkTemplate())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("EK CreatePrimary failed: %v", err)
|
|
}
|
|
defer tpm2.FlushContext(t.rwc, ekHnd)
|
|
|
|
pub, _, _, err := tpm2.ReadPublic(t.rwc, ekHnd)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("EK ReadPublic failed: %v", err)
|
|
}
|
|
if pub.RSAParameters == nil {
|
|
return nil, errors.New("ECC EK not yet supported")
|
|
}
|
|
|
|
i, err := t.info()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Retrieving TPM info failed: %v", err)
|
|
}
|
|
ekPub := &rsa.PublicKey{
|
|
E: int(pub.RSAParameters.Exponent()),
|
|
N: pub.RSAParameters.Modulus(),
|
|
}
|
|
var certificateURL string
|
|
if i.Manufacturer.String() == manufacturerIntel {
|
|
certificateURL = intelEKURL(ekPub)
|
|
}
|
|
return []EK{
|
|
{
|
|
Public: ekPub,
|
|
CertificateURL: certificateURL,
|
|
handle: commonRSAEkEquivalentHandle,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (t *wrappedTPM20) newAK(opts *AKConfig) (*AK, error) {
|
|
var parent ParentKeyConfig
|
|
if opts != nil && opts.Parent != nil {
|
|
parent = *opts.Parent
|
|
} else {
|
|
parent = defaultParentConfig
|
|
}
|
|
srk, _, err := t.getStorageRootKeyHandle(parent)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get SRK handle: %v", err)
|
|
}
|
|
|
|
blob, pub, creationData, creationHash, tix, err := tpm2.CreateKey(t.rwc, srk, tpm2.PCRSelection{}, "", "", akTemplate)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("CreateKeyEx() failed: %v", err)
|
|
}
|
|
keyHandle, _, err := tpm2.Load(t.rwc, srk, "", pub, blob)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Load() failed: %v", err)
|
|
}
|
|
// If any errors occur, free the AK's handle.
|
|
defer func() {
|
|
if err != nil {
|
|
tpm2.FlushContext(t.rwc, keyHandle)
|
|
}
|
|
}()
|
|
|
|
// We can only certify the creation immediately afterwards, so we cache the result.
|
|
attestation, sig, err := tpm2.CertifyCreation(t.rwc, "", keyHandle, keyHandle, nil, creationHash, tpm2.SigScheme{Alg: tpm2.AlgRSASSA, Hash: tpm2.AlgSHA256, Count: 0}, tix)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("CertifyCreation failed: %v", err)
|
|
}
|
|
return &AK{ak: newWrappedAK20(keyHandle, blob, pub, creationData, attestation, sig)}, nil
|
|
}
|
|
|
|
func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) {
|
|
k, ok := ak.ak.(*wrappedKey20)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected *wrappedKey20, got: %T", k)
|
|
}
|
|
|
|
parent, blob, pub, creationData, err := createKey(t, opts)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot create key: %v", err)
|
|
}
|
|
|
|
keyHandle, _, err := tpm2.Load(t.rwc, parent, "", pub, blob)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Load() failed: %v", err)
|
|
}
|
|
// If any errors occur, free the handle.
|
|
defer func() {
|
|
if err != nil {
|
|
tpm2.FlushContext(t.rwc, keyHandle)
|
|
}
|
|
}()
|
|
|
|
// Certify application key by AK
|
|
cp, err := k.certify(t, keyHandle)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ak.Certify() failed: %v", err)
|
|
}
|
|
if !bytes.Equal(pub, cp.Public) {
|
|
return nil, fmt.Errorf("certified incorrect key, expected: %v, certified: %v", pub, cp.Public)
|
|
}
|
|
|
|
// Pack the raw structure into a TPMU_SIGNATURE.
|
|
tpmPub, err := tpm2.DecodePublic(pub)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("decode public key: %v", err)
|
|
}
|
|
pubKey, err := tpmPub.Key()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("access public key: %v", err)
|
|
}
|
|
return &Key{key: newWrappedKey20(keyHandle, blob, pub, creationData, cp.CreateAttestation, cp.CreateSignature), pub: pubKey, tpm: t}, nil
|
|
}
|
|
|
|
func createKey(t *wrappedTPM20, opts *KeyConfig) (tpmutil.Handle, []byte, []byte, []byte, error) {
|
|
var parent ParentKeyConfig
|
|
if opts != nil && opts.Parent != nil {
|
|
parent = *opts.Parent
|
|
} else {
|
|
parent = defaultParentConfig
|
|
}
|
|
srk, _, err := t.getStorageRootKeyHandle(parent)
|
|
if err != nil {
|
|
return 0, nil, nil, nil, fmt.Errorf("failed to get SRK handle: %v", err)
|
|
}
|
|
|
|
tmpl, err := templateFromConfig(opts)
|
|
if err != nil {
|
|
return 0, nil, nil, nil, fmt.Errorf("incorrect key options: %v", err)
|
|
}
|
|
|
|
blob, pub, creationData, _, _, err := tpm2.CreateKey(t.rwc, srk, tpm2.PCRSelection{}, "", "", tmpl)
|
|
if err != nil {
|
|
return 0, nil, nil, nil, fmt.Errorf("CreateKey() failed: %v", err)
|
|
}
|
|
|
|
return srk, blob, pub, creationData, err
|
|
}
|
|
|
|
func templateFromConfig(opts *KeyConfig) (tpm2.Public, error) {
|
|
var tmpl tpm2.Public
|
|
switch opts.Algorithm {
|
|
case RSA:
|
|
tmpl = rsaKeyTemplate
|
|
if opts.Size < 0 || opts.Size > 65535 { // basic sanity check
|
|
return tmpl, fmt.Errorf("incorrect size parameter")
|
|
}
|
|
tmpl.RSAParameters.KeyBits = uint16(opts.Size)
|
|
|
|
case ECDSA:
|
|
tmpl = ecdsaKeyTemplate
|
|
switch opts.Size {
|
|
case 256:
|
|
tmpl.NameAlg = tpm2.AlgSHA256
|
|
tmpl.ECCParameters.Sign.Hash = tpm2.AlgSHA256
|
|
tmpl.ECCParameters.CurveID = tpm2.CurveNISTP256
|
|
tmpl.ECCParameters.Point = tpm2.ECPoint{
|
|
XRaw: make([]byte, 32),
|
|
YRaw: make([]byte, 32),
|
|
}
|
|
case 384:
|
|
tmpl.NameAlg = tpm2.AlgSHA384
|
|
tmpl.ECCParameters.Sign.Hash = tpm2.AlgSHA384
|
|
tmpl.ECCParameters.CurveID = tpm2.CurveNISTP384
|
|
tmpl.ECCParameters.Point = tpm2.ECPoint{
|
|
XRaw: make([]byte, 48),
|
|
YRaw: make([]byte, 48),
|
|
}
|
|
case 521:
|
|
tmpl.NameAlg = tpm2.AlgSHA512
|
|
tmpl.ECCParameters.Sign.Hash = tpm2.AlgSHA512
|
|
tmpl.ECCParameters.CurveID = tpm2.CurveNISTP521
|
|
tmpl.ECCParameters.Point = tpm2.ECPoint{
|
|
XRaw: make([]byte, 65),
|
|
YRaw: make([]byte, 65),
|
|
}
|
|
default:
|
|
return tmpl, fmt.Errorf("unsupported key size: %v", opts.Size)
|
|
}
|
|
default:
|
|
return tmpl, fmt.Errorf("unsupported algorithm type: %q", opts.Algorithm)
|
|
}
|
|
|
|
return tmpl, nil
|
|
}
|
|
|
|
func (t *wrappedTPM20) deserializeAndLoad(opaqueBlob []byte, parent ParentKeyConfig) (tpmutil.Handle, *serializedKey, error) {
|
|
sKey, err := deserializeKey(opaqueBlob, TPMVersion20)
|
|
if err != nil {
|
|
return 0, nil, fmt.Errorf("deserializeKey() failed: %v", err)
|
|
}
|
|
if sKey.Encoding != keyEncodingEncrypted {
|
|
return 0, nil, fmt.Errorf("unsupported key encoding: %x", sKey.Encoding)
|
|
}
|
|
|
|
srk, _, err := t.getStorageRootKeyHandle(parent)
|
|
if err != nil {
|
|
return 0, nil, fmt.Errorf("failed to get SRK handle: %v", err)
|
|
}
|
|
var hnd tpmutil.Handle
|
|
if hnd, _, err = tpm2.Load(t.rwc, srk, "", sKey.Public, sKey.Blob); err != nil {
|
|
return 0, nil, fmt.Errorf("Load() failed: %v", err)
|
|
}
|
|
return hnd, sKey, nil
|
|
}
|
|
|
|
func (t *wrappedTPM20) loadAK(opaqueBlob []byte) (*AK, error) {
|
|
return t.loadAKWithParent(opaqueBlob, defaultParentConfig)
|
|
}
|
|
|
|
func (t *wrappedTPM20) loadAKWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*AK, error) {
|
|
hnd, sKey, err := t.deserializeAndLoad(opaqueBlob, parent)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot load attestation key: %v", err)
|
|
}
|
|
return &AK{ak: newWrappedAK20(hnd, sKey.Blob, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)}, nil
|
|
}
|
|
|
|
func (t *wrappedTPM20) loadKey(opaqueBlob []byte) (*Key, error) {
|
|
return t.loadKeyWithParent(opaqueBlob, defaultParentConfig)
|
|
}
|
|
|
|
func (t *wrappedTPM20) loadKeyWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*Key, error) {
|
|
hnd, sKey, err := t.deserializeAndLoad(opaqueBlob, parent)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot load signing key: %v", err)
|
|
}
|
|
tpmPub, err := tpm2.DecodePublic(sKey.Public)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("decode public blob: %v", err)
|
|
}
|
|
pub, err := tpmPub.Key()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("access public key: %v", err)
|
|
}
|
|
return &Key{key: newWrappedKey20(hnd, sKey.Blob, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature), pub: pub, tpm: t}, nil
|
|
}
|
|
|
|
func (t *wrappedTPM20) pcrs(alg HashAlg) ([]PCR, error) {
|
|
PCRs, err := readAllPCRs20(t.rwc, alg.goTPMAlg())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read PCRs: %v", err)
|
|
}
|
|
|
|
out := make([]PCR, len(PCRs))
|
|
for index, digest := range PCRs {
|
|
out[int(index)] = PCR{
|
|
Index: int(index),
|
|
Digest: digest,
|
|
DigestAlg: alg.cryptoHash(),
|
|
}
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func (t *wrappedTPM20) measurementLog() ([]byte, error) {
|
|
return t.rwc.MeasurementLog()
|
|
}
|
|
|
|
// wrappedKey20 represents a key manipulated through a *wrappedTPM20.
|
|
type wrappedKey20 struct {
|
|
hnd tpmutil.Handle
|
|
|
|
blob []byte
|
|
public []byte // used by both TPM1.2 and 2.0
|
|
createData []byte
|
|
createAttestation []byte
|
|
createSignature []byte
|
|
}
|
|
|
|
func newWrappedAK20(hnd tpmutil.Handle, blob, public, createData, createAttestation, createSig []byte) ak {
|
|
return &wrappedKey20{
|
|
hnd: hnd,
|
|
blob: blob,
|
|
public: public,
|
|
createData: createData,
|
|
createAttestation: createAttestation,
|
|
createSignature: createSig,
|
|
}
|
|
}
|
|
|
|
func newWrappedKey20(hnd tpmutil.Handle, blob, public, createData, createAttestation, createSig []byte) key {
|
|
return &wrappedKey20{
|
|
hnd: hnd,
|
|
blob: blob,
|
|
public: public,
|
|
createData: createData,
|
|
createAttestation: createAttestation,
|
|
createSignature: createSig,
|
|
}
|
|
}
|
|
|
|
func (k *wrappedKey20) marshal() ([]byte, error) {
|
|
return (&serializedKey{
|
|
Encoding: keyEncodingEncrypted,
|
|
TPMVersion: TPMVersion20,
|
|
|
|
Blob: k.blob,
|
|
Public: k.public,
|
|
CreateData: k.createData,
|
|
CreateAttestation: k.createAttestation,
|
|
CreateSignature: k.createSignature,
|
|
}).Serialize()
|
|
}
|
|
|
|
func (k *wrappedKey20) close(t tpmBase) error {
|
|
tpm, ok := t.(*wrappedTPM20)
|
|
if !ok {
|
|
return fmt.Errorf("expected *wrappedTPM20, got %T", t)
|
|
}
|
|
return tpm2.FlushContext(tpm.rwc, k.hnd)
|
|
}
|
|
|
|
func (k *wrappedKey20) activateCredential(tb tpmBase, in EncryptedCredential, ek *EK) ([]byte, error) {
|
|
t, ok := tb.(*wrappedTPM20)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb)
|
|
}
|
|
|
|
if len(in.Credential) < 2 {
|
|
return nil, fmt.Errorf("malformed credential blob")
|
|
}
|
|
credential := in.Credential[2:]
|
|
if len(in.Secret) < 2 {
|
|
return nil, fmt.Errorf("malformed encrypted secret")
|
|
}
|
|
secret := in.Secret[2:]
|
|
|
|
ekHnd, _, err := t.getEndorsementKeyHandle(ek)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sessHandle, _, err := tpm2.StartAuthSession(
|
|
t.rwc,
|
|
tpm2.HandleNull, /*tpmKey*/
|
|
tpm2.HandleNull, /*bindKey*/
|
|
make([]byte, 16), /*nonceCaller*/
|
|
nil, /*secret*/
|
|
tpm2.SessionPolicy,
|
|
tpm2.AlgNull,
|
|
tpm2.AlgSHA256)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("creating session: %v", err)
|
|
}
|
|
defer tpm2.FlushContext(t.rwc, sessHandle)
|
|
|
|
if _, _, err := tpm2.PolicySecret(t.rwc, tpm2.HandleEndorsement, tpm2.AuthCommand{Session: tpm2.HandlePasswordSession, Attributes: tpm2.AttrContinueSession}, sessHandle, nil, nil, nil, 0); err != nil {
|
|
return nil, fmt.Errorf("tpm2.PolicySecret() failed: %v", err)
|
|
}
|
|
|
|
return tpm2.ActivateCredentialUsingAuth(t.rwc, []tpm2.AuthCommand{
|
|
{Session: tpm2.HandlePasswordSession, Attributes: tpm2.AttrContinueSession},
|
|
{Session: sessHandle, Attributes: tpm2.AttrContinueSession},
|
|
}, k.hnd, ekHnd, credential, secret)
|
|
}
|
|
|
|
func (k *wrappedKey20) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) {
|
|
t, ok := tb.(*wrappedTPM20)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb)
|
|
}
|
|
hnd, ok := handle.(tpmutil.Handle)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected tpmutil.Handle, got %T", handle)
|
|
}
|
|
scheme := tpm2.SigScheme{
|
|
Alg: tpm2.AlgRSASSA,
|
|
Hash: tpm2.AlgSHA256,
|
|
}
|
|
return certify(t.rwc, hnd, k.hnd, scheme)
|
|
}
|
|
|
|
func (k *wrappedKey20) quote(tb tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error) {
|
|
t, ok := tb.(*wrappedTPM20)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb)
|
|
}
|
|
return quote20(t.rwc, k.hnd, tpm2.Algorithm(alg), nonce, selectedPCRs)
|
|
}
|
|
|
|
func (k *wrappedKey20) attestationParameters() AttestationParameters {
|
|
return AttestationParameters{
|
|
Public: k.public,
|
|
CreateData: k.createData,
|
|
CreateAttestation: k.createAttestation,
|
|
CreateSignature: k.createSignature,
|
|
}
|
|
}
|
|
|
|
func (k *wrappedKey20) certificationParameters() CertificationParameters {
|
|
return CertificationParameters{
|
|
Public: k.public,
|
|
CreateAttestation: k.createAttestation,
|
|
CreateSignature: k.createSignature,
|
|
}
|
|
}
|
|
|
|
func (k *wrappedKey20) sign(tb tpmBase, digest []byte, pub crypto.PublicKey, opts crypto.SignerOpts) ([]byte, error) {
|
|
t, ok := tb.(*wrappedTPM20)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb)
|
|
}
|
|
switch p := pub.(type) {
|
|
case *ecdsa.PublicKey:
|
|
return signECDSA(t.rwc, k.hnd, digest, p.Curve)
|
|
case *rsa.PublicKey:
|
|
return signRSA(t.rwc, k.hnd, digest, opts)
|
|
}
|
|
return nil, fmt.Errorf("unsupported signing key type: %T", pub)
|
|
}
|
|
|
|
func signECDSA(rw io.ReadWriter, key tpmutil.Handle, digest []byte, curve elliptic.Curve) ([]byte, error) {
|
|
// https://cs.opensource.google/go/go/+/refs/tags/go1.19.2:src/crypto/ecdsa/ecdsa.go;l=181
|
|
orderBits := curve.Params().N.BitLen()
|
|
orderBytes := (orderBits + 7) / 8
|
|
if len(digest) > orderBytes {
|
|
digest = digest[:orderBytes]
|
|
}
|
|
ret := new(big.Int).SetBytes(digest)
|
|
excess := len(digest)*8 - orderBits
|
|
if excess > 0 {
|
|
ret.Rsh(ret, uint(excess))
|
|
}
|
|
// call ret.FillBytes() here instead of ret.Bytes() to preserve leading zeroes
|
|
// that may have been dropped when converting the digest to an integer
|
|
digest = ret.FillBytes(digest)
|
|
|
|
sig, err := tpm2.Sign(rw, key, "", digest, nil, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot sign: %v", err)
|
|
}
|
|
if sig.ECC == nil {
|
|
return nil, fmt.Errorf("expected ECDSA signature, got: %v", sig.Alg)
|
|
}
|
|
return asn1.Marshal(struct {
|
|
R *big.Int
|
|
S *big.Int
|
|
}{sig.ECC.R, sig.ECC.S})
|
|
}
|
|
|
|
func signRSA(rw io.ReadWriter, key tpmutil.Handle, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
|
|
h, err := tpm2.HashToAlgorithm(opts.HashFunc())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("incorrect hash algorithm: %v", err)
|
|
}
|
|
|
|
scheme := &tpm2.SigScheme{
|
|
Alg: tpm2.AlgRSASSA,
|
|
Hash: h,
|
|
}
|
|
|
|
if pss, ok := opts.(*rsa.PSSOptions); ok {
|
|
if pss.SaltLength != rsa.PSSSaltLengthAuto && pss.SaltLength != len(digest) {
|
|
return nil, fmt.Errorf("PSS salt length %d is incorrect, expected rsa.PSSSaltLengthAuto or %d", pss.SaltLength, len(digest))
|
|
}
|
|
scheme.Alg = tpm2.AlgRSAPSS
|
|
}
|
|
|
|
sig, err := tpm2.Sign(rw, key, "", digest, nil, scheme)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot sign: %v", err)
|
|
}
|
|
if sig.RSA == nil {
|
|
return nil, fmt.Errorf("expected RSA signature, got: %v", sig.Alg)
|
|
}
|
|
return sig.RSA.Signature, nil
|
|
}
|
|
|
|
func (k *wrappedKey20) decrypt(tb tpmBase, ctxt []byte) ([]byte, error) {
|
|
return nil, fmt.Errorf("not implemented")
|
|
}
|
|
|
|
func (k *wrappedKey20) blobs() ([]byte, []byte, error) {
|
|
return k.public, k.blob, nil
|
|
}
|