mirror of https://github.com/kubernetes/kops.git
1113 lines
31 KiB
Go
1113 lines
31 KiB
Go
// Copyright (c) 2018, Google LLC All rights reserved.
|
|
//
|
|
// 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 tpm2
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/rsa"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"reflect"
|
|
|
|
"github.com/google/go-tpm/tpmutil"
|
|
)
|
|
|
|
// NVPublic contains the public area of an NV index.
|
|
type NVPublic struct {
|
|
NVIndex tpmutil.Handle
|
|
NameAlg Algorithm
|
|
Attributes NVAttr
|
|
AuthPolicy tpmutil.U16Bytes
|
|
DataSize uint16
|
|
}
|
|
|
|
type tpmsSensitiveCreate struct {
|
|
UserAuth tpmutil.U16Bytes
|
|
Data tpmutil.U16Bytes
|
|
}
|
|
|
|
// PCRSelection contains a slice of PCR indexes and a hash algorithm used in
|
|
// them.
|
|
type PCRSelection struct {
|
|
Hash Algorithm
|
|
PCRs []int
|
|
}
|
|
|
|
type tpmsPCRSelection struct {
|
|
Hash Algorithm
|
|
Size byte
|
|
PCRs tpmutil.RawBytes
|
|
}
|
|
|
|
// Public contains the public area of an object.
|
|
type Public struct {
|
|
Type Algorithm
|
|
NameAlg Algorithm
|
|
Attributes KeyProp
|
|
AuthPolicy tpmutil.U16Bytes
|
|
|
|
// Exactly one of the following fields should be set
|
|
// When encoding/decoding, one will be picked based on Type.
|
|
|
|
// RSAParameters contains both [rsa]parameters and [rsa]unique.
|
|
RSAParameters *RSAParams
|
|
// ECCParameters contains both [ecc]parameters and [ecc]unique.
|
|
ECCParameters *ECCParams
|
|
// SymCipherParameters contains both [sym]parameters and [sym]unique.
|
|
SymCipherParameters *SymCipherParams
|
|
// KeyedHashParameters contains both [keyedHash]parameters and [keyedHash]unique.
|
|
KeyedHashParameters *KeyedHashParams
|
|
}
|
|
|
|
// Encode serializes a Public structure in TPM wire format.
|
|
func (p Public) Encode() ([]byte, error) {
|
|
head, err := tpmutil.Pack(p.Type, p.NameAlg, p.Attributes, p.AuthPolicy)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding Type, NameAlg, Attributes, AuthPolicy: %v", err)
|
|
}
|
|
var params []byte
|
|
switch p.Type {
|
|
case AlgRSA:
|
|
params, err = p.RSAParameters.encode()
|
|
case AlgKeyedHash:
|
|
params, err = p.KeyedHashParameters.encode()
|
|
case AlgECC:
|
|
params, err = p.ECCParameters.encode()
|
|
case AlgSymCipher:
|
|
params, err = p.SymCipherParameters.encode()
|
|
default:
|
|
err = fmt.Errorf("unsupported type in TPMT_PUBLIC: 0x%x", p.Type)
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding RSAParameters, ECCParameters, SymCipherParameters or KeyedHash: %v", err)
|
|
}
|
|
return concat(head, params)
|
|
}
|
|
|
|
// Key returns the (public) key from the public area of an object.
|
|
func (p Public) Key() (crypto.PublicKey, error) {
|
|
var pubKey crypto.PublicKey
|
|
switch p.Type {
|
|
case AlgRSA:
|
|
// Endianness of big.Int.Bytes/SetBytes and modulus in the TPM is the same
|
|
// (big-endian).
|
|
pubKey = &rsa.PublicKey{N: p.RSAParameters.Modulus(), E: int(p.RSAParameters.Exponent())}
|
|
case AlgECC:
|
|
curve, ok := toGoCurve[p.ECCParameters.CurveID]
|
|
if !ok {
|
|
return nil, fmt.Errorf("can't map TPM EC curve ID 0x%x to Go elliptic.Curve value", p.ECCParameters.CurveID)
|
|
}
|
|
pubKey = &ecdsa.PublicKey{
|
|
X: p.ECCParameters.Point.X(),
|
|
Y: p.ECCParameters.Point.Y(),
|
|
Curve: curve,
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("unsupported public key type 0x%x", p.Type)
|
|
}
|
|
return pubKey, nil
|
|
}
|
|
|
|
// Name computes the Digest-based Name from the public area of an object.
|
|
func (p Public) Name() (Name, error) {
|
|
pubEncoded, err := p.Encode()
|
|
if err != nil {
|
|
return Name{}, err
|
|
}
|
|
hash, err := p.NameAlg.Hash()
|
|
if err != nil {
|
|
return Name{}, err
|
|
}
|
|
nameHash := hash.New()
|
|
nameHash.Write(pubEncoded)
|
|
return Name{
|
|
Digest: &HashValue{
|
|
Alg: p.NameAlg,
|
|
Value: nameHash.Sum(nil),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// MatchesTemplate checks if the Public area has the same algorithms and
|
|
// parameters as the provided template. Note that this does not necessarily
|
|
// mean that the key was created from this template, as the Unique field is
|
|
// both provided in the template and overridden in the key creation process.
|
|
func (p Public) MatchesTemplate(template Public) bool {
|
|
if p.Type != template.Type ||
|
|
p.NameAlg != template.NameAlg ||
|
|
p.Attributes != template.Attributes ||
|
|
!bytes.Equal(p.AuthPolicy, template.AuthPolicy) {
|
|
return false
|
|
}
|
|
switch p.Type {
|
|
case AlgRSA:
|
|
return p.RSAParameters.matchesTemplate(template.RSAParameters)
|
|
case AlgECC:
|
|
return p.ECCParameters.matchesTemplate(template.ECCParameters)
|
|
case AlgSymCipher:
|
|
return p.SymCipherParameters.matchesTemplate(template.SymCipherParameters)
|
|
case AlgKeyedHash:
|
|
return p.KeyedHashParameters.matchesTemplate(template.KeyedHashParameters)
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
|
|
// DecodePublic decodes a TPMT_PUBLIC message. No error is returned if
|
|
// the input has extra trailing data.
|
|
func DecodePublic(buf []byte) (Public, error) {
|
|
in := bytes.NewBuffer(buf)
|
|
var pub Public
|
|
var err error
|
|
if err = tpmutil.UnpackBuf(in, &pub.Type, &pub.NameAlg, &pub.Attributes, &pub.AuthPolicy); err != nil {
|
|
return pub, fmt.Errorf("decoding TPMT_PUBLIC: %v", err)
|
|
}
|
|
|
|
switch pub.Type {
|
|
case AlgRSA:
|
|
pub.RSAParameters, err = decodeRSAParams(in)
|
|
case AlgECC:
|
|
pub.ECCParameters, err = decodeECCParams(in)
|
|
case AlgSymCipher:
|
|
pub.SymCipherParameters, err = decodeSymCipherParams(in)
|
|
case AlgKeyedHash:
|
|
pub.KeyedHashParameters, err = decodeKeyedHashParams(in)
|
|
default:
|
|
err = fmt.Errorf("unsupported type in TPMT_PUBLIC: 0x%x", pub.Type)
|
|
}
|
|
return pub, err
|
|
}
|
|
|
|
// RSAParams represents parameters of an RSA key pair:
|
|
// both the TPMS_RSA_PARMS and the TPM2B_PUBLIC_KEY_RSA.
|
|
//
|
|
// Symmetric and Sign may be nil, depending on key Attributes in Public.
|
|
//
|
|
// ExponentRaw and ModulusRaw are the actual data encoded in the template, which
|
|
// is useful for templates that differ in zero-padding, for example.
|
|
type RSAParams struct {
|
|
Symmetric *SymScheme
|
|
Sign *SigScheme
|
|
KeyBits uint16
|
|
ExponentRaw uint32
|
|
ModulusRaw tpmutil.U16Bytes
|
|
}
|
|
|
|
// Exponent returns the RSA exponent value represented by ExponentRaw, handling
|
|
// the fact that an exponent of 0 represents a value of 65537 (2^16 + 1).
|
|
func (p *RSAParams) Exponent() uint32 {
|
|
if p.ExponentRaw == 0 {
|
|
return defaultRSAExponent
|
|
}
|
|
return p.ExponentRaw
|
|
}
|
|
|
|
// Modulus returns the RSA modulus value represented by ModulusRaw, handling the
|
|
// fact that the same modulus value can have multiple different representations.
|
|
func (p *RSAParams) Modulus() *big.Int {
|
|
return new(big.Int).SetBytes(p.ModulusRaw)
|
|
}
|
|
|
|
func (p *RSAParams) matchesTemplate(t *RSAParams) bool {
|
|
return reflect.DeepEqual(p.Symmetric, t.Symmetric) &&
|
|
reflect.DeepEqual(p.Sign, t.Sign) &&
|
|
p.KeyBits == t.KeyBits && p.ExponentRaw == t.ExponentRaw
|
|
}
|
|
|
|
func (p *RSAParams) encode() ([]byte, error) {
|
|
if p == nil {
|
|
return nil, nil
|
|
}
|
|
sym, err := p.Symmetric.encode()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding Symmetric: %v", err)
|
|
}
|
|
sig, err := p.Sign.encode()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding Sign: %v", err)
|
|
}
|
|
rest, err := tpmutil.Pack(p.KeyBits, p.ExponentRaw, p.ModulusRaw)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding KeyBits, Exponent, Modulus: %v", err)
|
|
}
|
|
return concat(sym, sig, rest)
|
|
}
|
|
|
|
func decodeRSAParams(in *bytes.Buffer) (*RSAParams, error) {
|
|
var params RSAParams
|
|
var err error
|
|
|
|
if params.Symmetric, err = decodeSymScheme(in); err != nil {
|
|
return nil, fmt.Errorf("decoding Symmetric: %v", err)
|
|
}
|
|
if params.Sign, err = decodeSigScheme(in); err != nil {
|
|
return nil, fmt.Errorf("decoding Sign: %v", err)
|
|
}
|
|
if err := tpmutil.UnpackBuf(in, ¶ms.KeyBits, ¶ms.ExponentRaw, ¶ms.ModulusRaw); err != nil {
|
|
return nil, fmt.Errorf("decoding KeyBits, Exponent, Modulus: %v", err)
|
|
}
|
|
return ¶ms, nil
|
|
}
|
|
|
|
// ECCParams represents parameters of an ECC key pair:
|
|
// both the TPMS_ECC_PARMS and the TPMS_ECC_POINT.
|
|
//
|
|
// Symmetric, Sign and KDF may be nil, depending on key Attributes in Public.
|
|
type ECCParams struct {
|
|
Symmetric *SymScheme
|
|
Sign *SigScheme
|
|
CurveID EllipticCurve
|
|
KDF *KDFScheme
|
|
Point ECPoint
|
|
}
|
|
|
|
// ECPoint represents a ECC coordinates for a point using byte buffers.
|
|
type ECPoint struct {
|
|
XRaw, YRaw tpmutil.U16Bytes
|
|
}
|
|
|
|
// X returns the X Point value reprsented by XRaw.
|
|
func (p ECPoint) X() *big.Int {
|
|
return new(big.Int).SetBytes(p.XRaw)
|
|
}
|
|
|
|
// Y returns the Y Point value reprsented by YRaw.
|
|
func (p ECPoint) Y() *big.Int {
|
|
return new(big.Int).SetBytes(p.YRaw)
|
|
}
|
|
|
|
func (p *ECCParams) matchesTemplate(t *ECCParams) bool {
|
|
return reflect.DeepEqual(p.Symmetric, t.Symmetric) &&
|
|
reflect.DeepEqual(p.Sign, t.Sign) &&
|
|
p.CurveID == t.CurveID && reflect.DeepEqual(p.KDF, t.KDF)
|
|
}
|
|
|
|
func (p *ECCParams) encode() ([]byte, error) {
|
|
if p == nil {
|
|
return nil, nil
|
|
}
|
|
sym, err := p.Symmetric.encode()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding Symmetric: %v", err)
|
|
}
|
|
sig, err := p.Sign.encode()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding Sign: %v", err)
|
|
}
|
|
curve, err := tpmutil.Pack(p.CurveID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding CurveID: %v", err)
|
|
}
|
|
kdf, err := p.KDF.encode()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding KDF: %v", err)
|
|
}
|
|
point, err := tpmutil.Pack(p.Point.XRaw, p.Point.YRaw)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding Point: %v", err)
|
|
}
|
|
return concat(sym, sig, curve, kdf, point)
|
|
}
|
|
|
|
func decodeECCParams(in *bytes.Buffer) (*ECCParams, error) {
|
|
var params ECCParams
|
|
var err error
|
|
|
|
if params.Symmetric, err = decodeSymScheme(in); err != nil {
|
|
return nil, fmt.Errorf("decoding Symmetric: %v", err)
|
|
}
|
|
if params.Sign, err = decodeSigScheme(in); err != nil {
|
|
return nil, fmt.Errorf("decoding Sign: %v", err)
|
|
}
|
|
if err := tpmutil.UnpackBuf(in, ¶ms.CurveID); err != nil {
|
|
return nil, fmt.Errorf("decoding CurveID: %v", err)
|
|
}
|
|
if params.KDF, err = decodeKDFScheme(in); err != nil {
|
|
return nil, fmt.Errorf("decoding KDF: %v", err)
|
|
}
|
|
if err := tpmutil.UnpackBuf(in, ¶ms.Point.XRaw, ¶ms.Point.YRaw); err != nil {
|
|
return nil, fmt.Errorf("decoding Point: %v", err)
|
|
}
|
|
return ¶ms, nil
|
|
}
|
|
|
|
// SymCipherParams represents parameters of a symmetric block cipher TPM object:
|
|
// both the TPMS_SYMCIPHER_PARMS and the TPM2B_DIGEST (hash of the key).
|
|
type SymCipherParams struct {
|
|
Symmetric *SymScheme
|
|
Unique tpmutil.U16Bytes
|
|
}
|
|
|
|
func (p *SymCipherParams) matchesTemplate(t *SymCipherParams) bool {
|
|
return reflect.DeepEqual(p.Symmetric, t.Symmetric)
|
|
}
|
|
|
|
func (p *SymCipherParams) encode() ([]byte, error) {
|
|
sym, err := p.Symmetric.encode()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding Symmetric: %v", err)
|
|
}
|
|
unique, err := tpmutil.Pack(p.Unique)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding Unique: %v", err)
|
|
}
|
|
return concat(sym, unique)
|
|
}
|
|
|
|
func decodeSymCipherParams(in *bytes.Buffer) (*SymCipherParams, error) {
|
|
var params SymCipherParams
|
|
var err error
|
|
|
|
if params.Symmetric, err = decodeSymScheme(in); err != nil {
|
|
return nil, fmt.Errorf("decoding Symmetric: %v", err)
|
|
}
|
|
if err := tpmutil.UnpackBuf(in, ¶ms.Unique); err != nil {
|
|
return nil, fmt.Errorf("decoding Unique: %v", err)
|
|
}
|
|
return ¶ms, nil
|
|
}
|
|
|
|
// KeyedHashParams represents parameters of a keyed hash TPM object:
|
|
// both the TPMS_KEYEDHASH_PARMS and the TPM2B_DIGEST (hash of the key).
|
|
type KeyedHashParams struct {
|
|
Alg Algorithm
|
|
Hash Algorithm
|
|
KDF Algorithm
|
|
Unique tpmutil.U16Bytes
|
|
}
|
|
|
|
func (p *KeyedHashParams) matchesTemplate(t *KeyedHashParams) bool {
|
|
if p.Alg != t.Alg {
|
|
return false
|
|
}
|
|
switch p.Alg {
|
|
case AlgHMAC:
|
|
return p.Hash == t.Hash
|
|
case AlgXOR:
|
|
return p.Hash == t.Hash && p.KDF == t.KDF
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
|
|
func (p *KeyedHashParams) encode() ([]byte, error) {
|
|
if p == nil {
|
|
return tpmutil.Pack(AlgNull, tpmutil.U16Bytes(nil))
|
|
}
|
|
var params []byte
|
|
var err error
|
|
switch p.Alg {
|
|
case AlgNull:
|
|
params, err = tpmutil.Pack(p.Alg)
|
|
case AlgHMAC:
|
|
params, err = tpmutil.Pack(p.Alg, p.Hash)
|
|
case AlgXOR:
|
|
params, err = tpmutil.Pack(p.Alg, p.Hash, p.KDF)
|
|
default:
|
|
err = fmt.Errorf("unsupported KeyedHash Algorithm: 0x%x", p.Alg)
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding Alg Params: %v", err)
|
|
}
|
|
unique, err := tpmutil.Pack(p.Unique)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding Unique: %v", err)
|
|
}
|
|
return concat(params, unique)
|
|
}
|
|
|
|
func decodeKeyedHashParams(in *bytes.Buffer) (*KeyedHashParams, error) {
|
|
var p KeyedHashParams
|
|
var err error
|
|
if err = tpmutil.UnpackBuf(in, &p.Alg); err != nil {
|
|
return nil, fmt.Errorf("decoding Alg: %v", err)
|
|
}
|
|
switch p.Alg {
|
|
case AlgNull:
|
|
err = nil
|
|
case AlgHMAC:
|
|
err = tpmutil.UnpackBuf(in, &p.Hash)
|
|
case AlgXOR:
|
|
err = tpmutil.UnpackBuf(in, &p.Hash, &p.KDF)
|
|
default:
|
|
err = fmt.Errorf("unsupported KeyedHash Algorithm: 0x%x", p.Alg)
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("decoding Alg Params: %v", err)
|
|
}
|
|
if err = tpmutil.UnpackBuf(in, &p.Unique); err != nil {
|
|
return nil, fmt.Errorf("decoding Unique: %v", err)
|
|
}
|
|
return &p, nil
|
|
}
|
|
|
|
// SymScheme represents a symmetric encryption scheme.
|
|
// Known in the specification by TPMT_SYM_DEF_OBJECT.
|
|
type SymScheme struct {
|
|
Alg Algorithm
|
|
KeyBits uint16
|
|
Mode Algorithm
|
|
}
|
|
|
|
func (s *SymScheme) encode() ([]byte, error) {
|
|
if s == nil || s.Alg.IsNull() {
|
|
return tpmutil.Pack(AlgNull)
|
|
}
|
|
return tpmutil.Pack(s.Alg, s.KeyBits, s.Mode)
|
|
}
|
|
|
|
func decodeSymScheme(in *bytes.Buffer) (*SymScheme, error) {
|
|
var scheme SymScheme
|
|
if err := tpmutil.UnpackBuf(in, &scheme.Alg); err != nil {
|
|
return nil, fmt.Errorf("decoding Alg: %v", err)
|
|
}
|
|
if scheme.Alg == AlgNull {
|
|
return nil, nil
|
|
}
|
|
if err := tpmutil.UnpackBuf(in, &scheme.KeyBits, &scheme.Mode); err != nil {
|
|
return nil, fmt.Errorf("decoding KeyBits, Mode: %v", err)
|
|
}
|
|
return &scheme, nil
|
|
}
|
|
|
|
// AsymScheme represents am asymmetric encryption scheme.
|
|
type AsymScheme struct {
|
|
Alg Algorithm
|
|
Hash Algorithm
|
|
}
|
|
|
|
func (s *AsymScheme) encode() ([]byte, error) {
|
|
if s == nil || s.Alg.IsNull() {
|
|
return tpmutil.Pack(AlgNull)
|
|
}
|
|
if s.Alg.UsesHash() {
|
|
return tpmutil.Pack(s.Alg, s.Hash)
|
|
}
|
|
return tpmutil.Pack(s.Alg)
|
|
}
|
|
|
|
// SigScheme represents a signing scheme.
|
|
type SigScheme struct {
|
|
Alg Algorithm
|
|
Hash Algorithm
|
|
Count uint32
|
|
}
|
|
|
|
func (s *SigScheme) encode() ([]byte, error) {
|
|
if s == nil || s.Alg.IsNull() {
|
|
return tpmutil.Pack(AlgNull)
|
|
}
|
|
if s.Alg.UsesCount() {
|
|
return tpmutil.Pack(s.Alg, s.Hash, s.Count)
|
|
}
|
|
return tpmutil.Pack(s.Alg, s.Hash)
|
|
}
|
|
|
|
func decodeSigScheme(in *bytes.Buffer) (*SigScheme, error) {
|
|
var scheme SigScheme
|
|
if err := tpmutil.UnpackBuf(in, &scheme.Alg); err != nil {
|
|
return nil, fmt.Errorf("decoding Alg: %v", err)
|
|
}
|
|
if scheme.Alg == AlgNull {
|
|
return nil, nil
|
|
}
|
|
if err := tpmutil.UnpackBuf(in, &scheme.Hash); err != nil {
|
|
return nil, fmt.Errorf("decoding Hash: %v", err)
|
|
}
|
|
if scheme.Alg.UsesCount() {
|
|
if err := tpmutil.UnpackBuf(in, &scheme.Count); err != nil {
|
|
return nil, fmt.Errorf("decoding Count: %v", err)
|
|
}
|
|
}
|
|
return &scheme, nil
|
|
}
|
|
|
|
// KDFScheme represents a KDF (Key Derivation Function) scheme.
|
|
type KDFScheme struct {
|
|
Alg Algorithm
|
|
Hash Algorithm
|
|
}
|
|
|
|
func (s *KDFScheme) encode() ([]byte, error) {
|
|
if s == nil || s.Alg.IsNull() {
|
|
return tpmutil.Pack(AlgNull)
|
|
}
|
|
return tpmutil.Pack(s.Alg, s.Hash)
|
|
}
|
|
|
|
func decodeKDFScheme(in *bytes.Buffer) (*KDFScheme, error) {
|
|
var scheme KDFScheme
|
|
if err := tpmutil.UnpackBuf(in, &scheme.Alg); err != nil {
|
|
return nil, fmt.Errorf("decoding Alg: %v", err)
|
|
}
|
|
if scheme.Alg == AlgNull {
|
|
return nil, nil
|
|
}
|
|
if err := tpmutil.UnpackBuf(in, &scheme.Hash); err != nil {
|
|
return nil, fmt.Errorf("decoding Hash: %v", err)
|
|
}
|
|
return &scheme, nil
|
|
}
|
|
|
|
// Signature combines all possible signatures from RSA and ECC keys. Only one
|
|
// of RSA or ECC will be populated.
|
|
type Signature struct {
|
|
Alg Algorithm
|
|
RSA *SignatureRSA
|
|
ECC *SignatureECC
|
|
}
|
|
|
|
// Encode serializes a Signature structure in TPM wire format.
|
|
func (s Signature) Encode() ([]byte, error) {
|
|
head, err := tpmutil.Pack(s.Alg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding Alg: %v", err)
|
|
}
|
|
var signature []byte
|
|
switch s.Alg {
|
|
case AlgRSASSA, AlgRSAPSS:
|
|
if signature, err = tpmutil.Pack(s.RSA); err != nil {
|
|
return nil, fmt.Errorf("encoding RSA: %v", err)
|
|
}
|
|
case AlgECDSA:
|
|
signature, err = tpmutil.Pack(s.ECC.HashAlg, tpmutil.U16Bytes(s.ECC.R.Bytes()), tpmutil.U16Bytes(s.ECC.S.Bytes()))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding ECC: %v", err)
|
|
}
|
|
}
|
|
return concat(head, signature)
|
|
}
|
|
|
|
// DecodeSignature decodes a serialized TPMT_SIGNATURE structure.
|
|
func DecodeSignature(in *bytes.Buffer) (*Signature, error) {
|
|
var sig Signature
|
|
if err := tpmutil.UnpackBuf(in, &sig.Alg); err != nil {
|
|
return nil, fmt.Errorf("decoding Alg: %v", err)
|
|
}
|
|
switch sig.Alg {
|
|
case AlgRSASSA, AlgRSAPSS:
|
|
sig.RSA = new(SignatureRSA)
|
|
if err := tpmutil.UnpackBuf(in, sig.RSA); err != nil {
|
|
return nil, fmt.Errorf("decoding RSA: %v", err)
|
|
}
|
|
case AlgECDSA:
|
|
sig.ECC = new(SignatureECC)
|
|
var r, s tpmutil.U16Bytes
|
|
if err := tpmutil.UnpackBuf(in, &sig.ECC.HashAlg, &r, &s); err != nil {
|
|
return nil, fmt.Errorf("decoding ECC: %v", err)
|
|
}
|
|
sig.ECC.R = big.NewInt(0).SetBytes(r)
|
|
sig.ECC.S = big.NewInt(0).SetBytes(s)
|
|
default:
|
|
return nil, fmt.Errorf("unsupported signature algorithm 0x%x", sig.Alg)
|
|
}
|
|
return &sig, nil
|
|
}
|
|
|
|
// SignatureRSA is an RSA-specific signature value.
|
|
type SignatureRSA struct {
|
|
HashAlg Algorithm
|
|
Signature tpmutil.U16Bytes
|
|
}
|
|
|
|
// SignatureECC is an ECC-specific signature value.
|
|
type SignatureECC struct {
|
|
HashAlg Algorithm
|
|
R *big.Int
|
|
S *big.Int
|
|
}
|
|
|
|
// Private contains private section of a TPM key.
|
|
type Private struct {
|
|
Type Algorithm
|
|
AuthValue tpmutil.U16Bytes
|
|
SeedValue tpmutil.U16Bytes
|
|
Sensitive tpmutil.U16Bytes
|
|
}
|
|
|
|
// Encode serializes a Private structure in TPM wire format.
|
|
func (p Private) Encode() ([]byte, error) {
|
|
if p.Type.IsNull() {
|
|
return nil, nil
|
|
}
|
|
return tpmutil.Pack(p)
|
|
}
|
|
|
|
// AttestationData contains data attested by TPM commands (like Certify).
|
|
type AttestationData struct {
|
|
Magic uint32
|
|
Type tpmutil.Tag
|
|
QualifiedSigner Name
|
|
ExtraData tpmutil.U16Bytes
|
|
ClockInfo ClockInfo
|
|
FirmwareVersion uint64
|
|
AttestedCertifyInfo *CertifyInfo
|
|
AttestedQuoteInfo *QuoteInfo
|
|
AttestedCreationInfo *CreationInfo
|
|
}
|
|
|
|
// DecodeAttestationData decode a TPMS_ATTEST message. No error is returned if
|
|
// the input has extra trailing data.
|
|
func DecodeAttestationData(in []byte) (*AttestationData, error) {
|
|
buf := bytes.NewBuffer(in)
|
|
|
|
var ad AttestationData
|
|
if err := tpmutil.UnpackBuf(buf, &ad.Magic, &ad.Type); err != nil {
|
|
return nil, fmt.Errorf("decoding Magic/Type: %v", err)
|
|
}
|
|
// All attestation structures have the magic prefix
|
|
// TPMS_GENERATED_VALUE to symbolize they were created by
|
|
// the TPM when signed with an AK.
|
|
if ad.Magic != 0xff544347 {
|
|
return nil, fmt.Errorf("incorrect magic value: %x", ad.Magic)
|
|
}
|
|
|
|
n, err := DecodeName(buf)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("decoding QualifiedSigner: %v", err)
|
|
}
|
|
ad.QualifiedSigner = *n
|
|
if err := tpmutil.UnpackBuf(buf, &ad.ExtraData, &ad.ClockInfo, &ad.FirmwareVersion); err != nil {
|
|
return nil, fmt.Errorf("decoding ExtraData/ClockInfo/FirmwareVersion: %v", err)
|
|
}
|
|
|
|
// The spec specifies several other types of attestation data. We only need
|
|
// parsing of Certify & Creation attestation data for now. If you need
|
|
// support for other attestation types, add them here.
|
|
switch ad.Type {
|
|
case TagAttestCertify:
|
|
if ad.AttestedCertifyInfo, err = decodeCertifyInfo(buf); err != nil {
|
|
return nil, fmt.Errorf("decoding AttestedCertifyInfo: %v", err)
|
|
}
|
|
case TagAttestCreation:
|
|
if ad.AttestedCreationInfo, err = decodeCreationInfo(buf); err != nil {
|
|
return nil, fmt.Errorf("decoding AttestedCreationInfo: %v", err)
|
|
}
|
|
case TagAttestQuote:
|
|
if ad.AttestedQuoteInfo, err = decodeQuoteInfo(buf); err != nil {
|
|
return nil, fmt.Errorf("decoding AttestedQuoteInfo: %v", err)
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("only Quote, Certify & Creation attestation structures are supported, got type 0x%x", ad.Type)
|
|
}
|
|
|
|
return &ad, nil
|
|
}
|
|
|
|
// Encode serializes an AttestationData structure in TPM wire format.
|
|
func (ad AttestationData) Encode() ([]byte, error) {
|
|
head, err := tpmutil.Pack(ad.Magic, ad.Type)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding Magic, Type: %v", err)
|
|
}
|
|
signer, err := ad.QualifiedSigner.Encode()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding QualifiedSigner: %v", err)
|
|
}
|
|
tail, err := tpmutil.Pack(ad.ExtraData, ad.ClockInfo, ad.FirmwareVersion)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding ExtraData, ClockInfo, FirmwareVersion: %v", err)
|
|
}
|
|
|
|
var info []byte
|
|
switch ad.Type {
|
|
case TagAttestCertify:
|
|
if info, err = ad.AttestedCertifyInfo.encode(); err != nil {
|
|
return nil, fmt.Errorf("encoding AttestedCertifyInfo: %v", err)
|
|
}
|
|
case TagAttestCreation:
|
|
if info, err = ad.AttestedCreationInfo.encode(); err != nil {
|
|
return nil, fmt.Errorf("encoding AttestedCreationInfo: %v", err)
|
|
}
|
|
case TagAttestQuote:
|
|
if info, err = ad.AttestedQuoteInfo.encode(); err != nil {
|
|
return nil, fmt.Errorf("encoding AttestedQuoteInfo: %v", err)
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("only Quote, Certify & Creation attestation structures are supported, got type 0x%x", ad.Type)
|
|
}
|
|
|
|
return concat(head, signer, tail, info)
|
|
}
|
|
|
|
// CreationInfo contains Creation-specific data for TPMS_ATTEST.
|
|
type CreationInfo struct {
|
|
Name Name
|
|
// Most TPM2B_Digest structures contain a TPMU_HA structure
|
|
// and get parsed to HashValue. This is never the case for the
|
|
// digest in TPMS_CREATION_INFO.
|
|
OpaqueDigest tpmutil.U16Bytes
|
|
}
|
|
|
|
func decodeCreationInfo(in *bytes.Buffer) (*CreationInfo, error) {
|
|
var ci CreationInfo
|
|
|
|
n, err := DecodeName(in)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("decoding Name: %v", err)
|
|
}
|
|
ci.Name = *n
|
|
|
|
if err := tpmutil.UnpackBuf(in, &ci.OpaqueDigest); err != nil {
|
|
return nil, fmt.Errorf("decoding Digest: %v", err)
|
|
}
|
|
|
|
return &ci, nil
|
|
}
|
|
|
|
func (ci CreationInfo) encode() ([]byte, error) {
|
|
n, err := ci.Name.Encode()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding Name: %v", err)
|
|
}
|
|
|
|
d, err := tpmutil.Pack(ci.OpaqueDigest)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding Digest: %v", err)
|
|
}
|
|
|
|
return concat(n, d)
|
|
}
|
|
|
|
// CertifyInfo contains Certify-specific data for TPMS_ATTEST.
|
|
type CertifyInfo struct {
|
|
Name Name
|
|
QualifiedName Name
|
|
}
|
|
|
|
func decodeCertifyInfo(in *bytes.Buffer) (*CertifyInfo, error) {
|
|
var ci CertifyInfo
|
|
|
|
n, err := DecodeName(in)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("decoding Name: %v", err)
|
|
}
|
|
ci.Name = *n
|
|
|
|
n, err = DecodeName(in)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("decoding QualifiedName: %v", err)
|
|
}
|
|
ci.QualifiedName = *n
|
|
|
|
return &ci, nil
|
|
}
|
|
|
|
func (ci CertifyInfo) encode() ([]byte, error) {
|
|
n, err := ci.Name.Encode()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding Name: %v", err)
|
|
}
|
|
qn, err := ci.QualifiedName.Encode()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding QualifiedName: %v", err)
|
|
}
|
|
return concat(n, qn)
|
|
}
|
|
|
|
// QuoteInfo represents a TPMS_QUOTE_INFO structure.
|
|
type QuoteInfo struct {
|
|
PCRSelection PCRSelection
|
|
PCRDigest tpmutil.U16Bytes
|
|
}
|
|
|
|
func decodeQuoteInfo(in *bytes.Buffer) (*QuoteInfo, error) {
|
|
var out QuoteInfo
|
|
sel, err := decodeOneTPMLPCRSelection(in)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("decoding PCRSelection: %v", err)
|
|
}
|
|
out.PCRSelection = sel
|
|
|
|
if err := tpmutil.UnpackBuf(in, &out.PCRDigest); err != nil {
|
|
return nil, fmt.Errorf("decoding PCRDigest: %v", err)
|
|
}
|
|
return &out, nil
|
|
}
|
|
|
|
func (qi QuoteInfo) encode() ([]byte, error) {
|
|
sel, err := encodeTPMLPCRSelection(qi.PCRSelection)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding PCRSelection: %v", err)
|
|
}
|
|
|
|
digest, err := tpmutil.Pack(qi.PCRDigest)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding PCRDigest: %v", err)
|
|
}
|
|
|
|
return concat(sel, digest)
|
|
}
|
|
|
|
// IDObject represents an encrypted credential bound to a TPM object.
|
|
type IDObject struct {
|
|
IntegrityHMAC tpmutil.U16Bytes
|
|
// EncIdentity is packed raw, as the bytes representing the size
|
|
// of the credential value are present within the encrypted blob.
|
|
EncIdentity tpmutil.RawBytes
|
|
}
|
|
|
|
// CreationData describes the attributes and environment for an object created
|
|
// on the TPM. This structure encodes/decodes to/from TPMS_CREATION_DATA.
|
|
type CreationData struct {
|
|
PCRSelection PCRSelection
|
|
PCRDigest tpmutil.U16Bytes
|
|
Locality byte
|
|
ParentNameAlg Algorithm
|
|
ParentName Name
|
|
ParentQualifiedName Name
|
|
OutsideInfo tpmutil.U16Bytes
|
|
}
|
|
|
|
// EncodeCreationData encodes byte array to TPMS_CREATION_DATA message.
|
|
func (cd *CreationData) EncodeCreationData() ([]byte, error) {
|
|
sel, err := encodeTPMLPCRSelection(cd.PCRSelection)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding PCRSelection: %v", err)
|
|
}
|
|
d, err := tpmutil.Pack(cd.PCRDigest, cd.Locality, cd.ParentNameAlg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding PCRDigest, Locality, ParentNameAlg: %v", err)
|
|
}
|
|
pn, err := cd.ParentName.Encode()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding ParentName: %v", err)
|
|
}
|
|
pqn, err := cd.ParentQualifiedName.Encode()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding ParentQualifiedName: %v", err)
|
|
}
|
|
o, err := tpmutil.Pack(cd.OutsideInfo)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encoding OutsideInfo: %v", err)
|
|
}
|
|
return concat(sel, d, pn, pqn, o)
|
|
}
|
|
|
|
// DecodeCreationData decodes a TPMS_CREATION_DATA message. No error is
|
|
// returned if the input has extra trailing data.
|
|
func DecodeCreationData(buf []byte) (*CreationData, error) {
|
|
in := bytes.NewBuffer(buf)
|
|
var out CreationData
|
|
|
|
sel, err := decodeOneTPMLPCRSelection(in)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("decodeOneTPMLPCRSelection returned error %v", err)
|
|
}
|
|
out.PCRSelection = sel
|
|
|
|
if err := tpmutil.UnpackBuf(in, &out.PCRDigest, &out.Locality, &out.ParentNameAlg); err != nil {
|
|
return nil, fmt.Errorf("decoding PCRDigest, Locality, ParentNameAlg: %v", err)
|
|
}
|
|
|
|
n, err := DecodeName(in)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("decoding ParentName: %v", err)
|
|
}
|
|
out.ParentName = *n
|
|
if n, err = DecodeName(in); err != nil {
|
|
return nil, fmt.Errorf("decoding ParentQualifiedName: %v", err)
|
|
}
|
|
out.ParentQualifiedName = *n
|
|
|
|
if err := tpmutil.UnpackBuf(in, &out.OutsideInfo); err != nil {
|
|
return nil, fmt.Errorf("decoding OutsideInfo: %v", err)
|
|
}
|
|
|
|
return &out, nil
|
|
}
|
|
|
|
// Name represents a TPM2B_NAME, a name for TPM entities. Only one of
|
|
// Handle or Digest should be set.
|
|
type Name struct {
|
|
Handle *tpmutil.Handle
|
|
Digest *HashValue
|
|
}
|
|
|
|
// DecodeName deserializes a Name hash from the TPM wire format.
|
|
func DecodeName(in *bytes.Buffer) (*Name, error) {
|
|
var nameBuf tpmutil.U16Bytes
|
|
if err := tpmutil.UnpackBuf(in, &nameBuf); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
name := new(Name)
|
|
switch len(nameBuf) {
|
|
case 0:
|
|
// No name is present.
|
|
case 4:
|
|
name.Handle = new(tpmutil.Handle)
|
|
if err := tpmutil.UnpackBuf(bytes.NewBuffer(nameBuf), name.Handle); err != nil {
|
|
return nil, fmt.Errorf("decoding Handle: %v", err)
|
|
}
|
|
default:
|
|
var err error
|
|
name.Digest, err = decodeHashValue(bytes.NewBuffer(nameBuf))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("decoding Digest: %v", err)
|
|
}
|
|
}
|
|
return name, nil
|
|
}
|
|
|
|
// Encode serializes a Name hash into the TPM wire format.
|
|
func (n Name) Encode() ([]byte, error) {
|
|
var buf []byte
|
|
var err error
|
|
switch {
|
|
case n.Handle != nil:
|
|
if buf, err = tpmutil.Pack(*n.Handle); err != nil {
|
|
return nil, fmt.Errorf("encoding Handle: %v", err)
|
|
}
|
|
case n.Digest != nil:
|
|
if buf, err = n.Digest.Encode(); err != nil {
|
|
return nil, fmt.Errorf("encoding Digest: %v", err)
|
|
}
|
|
default:
|
|
// Name is empty, which is valid.
|
|
}
|
|
return tpmutil.Pack(tpmutil.U16Bytes(buf))
|
|
}
|
|
|
|
// MatchesPublic compares Digest in Name against given Public structure. Note:
|
|
// this only works for regular Names, not Qualified Names.
|
|
func (n Name) MatchesPublic(p Public) (bool, error) {
|
|
if n.Digest == nil {
|
|
return false, errors.New("Name doesn't have a Digest, can't compare to Public")
|
|
}
|
|
expected, err := p.Name()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
// No secrets, so no constant-time comparison
|
|
return bytes.Equal(expected.Digest.Value, n.Digest.Value), nil
|
|
}
|
|
|
|
// HashValue is an algorithm-specific hash value.
|
|
type HashValue struct {
|
|
Alg Algorithm
|
|
Value tpmutil.U16Bytes
|
|
}
|
|
|
|
func decodeHashValue(in *bytes.Buffer) (*HashValue, error) {
|
|
var hv HashValue
|
|
if err := tpmutil.UnpackBuf(in, &hv.Alg); err != nil {
|
|
return nil, fmt.Errorf("decoding Alg: %v", err)
|
|
}
|
|
hfn, err := hv.Alg.Hash()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hv.Value = make(tpmutil.U16Bytes, hfn.Size())
|
|
if _, err := in.Read(hv.Value); err != nil {
|
|
return nil, fmt.Errorf("decoding Value: %v", err)
|
|
}
|
|
return &hv, nil
|
|
}
|
|
|
|
// Encode represents the given hash value as a TPMT_HA structure.
|
|
func (hv HashValue) Encode() ([]byte, error) {
|
|
return tpmutil.Pack(hv.Alg, tpmutil.RawBytes(hv.Value))
|
|
}
|
|
|
|
// ClockInfo contains TPM state info included in AttestationData.
|
|
type ClockInfo struct {
|
|
Clock uint64
|
|
ResetCount uint32
|
|
RestartCount uint32
|
|
Safe byte
|
|
}
|
|
|
|
// AlgorithmAttributes represents a TPMA_ALGORITHM value.
|
|
type AlgorithmAttributes uint32
|
|
|
|
// AlgorithmDescription represents a TPMS_ALGORITHM_DESCRIPTION structure.
|
|
type AlgorithmDescription struct {
|
|
ID Algorithm
|
|
Attributes AlgorithmAttributes
|
|
}
|
|
|
|
// TaggedProperty represents a TPMS_TAGGED_PROPERTY structure.
|
|
type TaggedProperty struct {
|
|
Tag TPMProp
|
|
Value uint32
|
|
}
|
|
|
|
// Ticket represents evidence the TPM previously processed
|
|
// information.
|
|
type Ticket struct {
|
|
Type tpmutil.Tag
|
|
Hierarchy tpmutil.Handle
|
|
Digest tpmutil.U16Bytes
|
|
}
|
|
|
|
// AuthCommand represents a TPMS_AUTH_COMMAND. This structure encapsulates parameters
|
|
// which authorize the use of a given handle or parameter.
|
|
type AuthCommand struct {
|
|
Session tpmutil.Handle
|
|
Nonce tpmutil.U16Bytes
|
|
Attributes SessionAttributes
|
|
Auth tpmutil.U16Bytes
|
|
}
|
|
|
|
// TPMLDigest represents the TPML_Digest structure
|
|
// It is used to convey a list of digest values.
|
|
// This type is used in TPM2_PolicyOR() and in TPM2_PCR_Read()
|
|
type TPMLDigest struct {
|
|
Digests []tpmutil.U16Bytes
|
|
}
|
|
|
|
// Encode converts the TPMLDigest structure into a byte slice
|
|
func (list *TPMLDigest) Encode() ([]byte, error) {
|
|
res, err := tpmutil.Pack(uint32(len(list.Digests)))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, item := range list.Digests {
|
|
b, err := tpmutil.Pack(item)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res = append(res, b...)
|
|
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
// DecodeTPMLDigest decodes a TPML_Digest part of a message.
|
|
func DecodeTPMLDigest(buf []byte) (*TPMLDigest, error) {
|
|
in := bytes.NewBuffer(buf)
|
|
var tpmld TPMLDigest
|
|
var count uint32
|
|
if err := binary.Read(in, binary.BigEndian, &count); err != nil {
|
|
return nil, fmt.Errorf("decoding TPML_Digest: %v", err)
|
|
}
|
|
for in.Len() > 0 {
|
|
var hash tpmutil.U16Bytes
|
|
if err := hash.TPMUnmarshal(in); err != nil {
|
|
return nil, err
|
|
}
|
|
tpmld.Digests = append(tpmld.Digests, hash)
|
|
}
|
|
if count != uint32(len(tpmld.Digests)) {
|
|
return nil, fmt.Errorf("expected size and read size does not match")
|
|
}
|
|
return &tpmld, nil
|
|
}
|