mirror of
https://github.com/google/go-attestation.git
synced 2025-03-14 00:06:31 +00:00
Compare commits
52 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c5d6b1e758 | ||
|
7d4525c388 | ||
|
dce70c6163 | ||
|
51a20034c0 | ||
|
a94a8af69d | ||
|
f37925d5d0 | ||
|
f7a27487f1 | ||
|
d9d8fdc48e | ||
|
f44f5ffe7e | ||
|
9cdb0fcd55 | ||
|
dfabc9c919 | ||
|
c7aee80c5d | ||
|
1b202b12e8 | ||
|
183ad1d5ad | ||
|
9cc576ead1 | ||
|
62f7ad0785 | ||
|
f203ad3090 | ||
|
72657612f0 | ||
|
ec740ef912 | ||
|
51d1c6c3c5 | ||
|
0c084813e6 | ||
|
5d68dfee1b | ||
|
b7a5927d66 | ||
|
b36ec6af0a | ||
|
0722a4900b | ||
|
545501297e | ||
|
5148956a0c | ||
|
a9866d34bb | ||
|
5b3763098f | ||
|
e6ab626979 | ||
|
52542411c5 | ||
|
02cf9e2ddd | ||
|
8b301f2d45 | ||
|
3d017c0234 | ||
|
a3545dfc94 | ||
|
93c5899459 | ||
|
74a49366bd | ||
|
776dc3ac22 | ||
|
136789e2e1 | ||
|
82eb5d47a2 | ||
|
f4ab877258 | ||
|
3d71f101b1 | ||
|
42c11fc152 | ||
|
3c84bff65e | ||
|
ab5dee2ae5 | ||
|
046550658b | ||
|
310e2caafe | ||
|
60adf13bc0 | ||
|
a56e8c4896 | ||
|
8af5f4e7de | ||
|
b92d1c69bf | ||
|
d29df30553 |
13
.github/dependabot.yml
vendored
13
.github/dependabot.yml
vendored
@ -4,3 +4,16 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
groups:
|
||||
"Go modules":
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
|
12
.github/workflows/codeql-analysis.yml
vendored
12
.github/workflows/codeql-analysis.yml
vendored
@ -1,5 +1,9 @@
|
||||
name: "CodeQL"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
@ -20,7 +24,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@ -28,12 +32,12 @@ jobs:
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
10
.github/workflows/golangci-lint.yml
vendored
10
.github/workflows/golangci-lint.yml
vendored
@ -8,11 +8,11 @@ jobs:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
- uses: actions/checkout@v3
|
||||
go-version: 1.24.x
|
||||
- uses: actions/checkout@v4
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: v1.53.3
|
||||
version: v1.64.6
|
||||
|
28
.github/workflows/test.yml
vendored
28
.github/workflows/test.yml
vendored
@ -13,29 +13,29 @@ jobs:
|
||||
test-linux:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.20.x]
|
||||
go-version: [1.24.x]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: Test
|
||||
run: go test ./...
|
||||
test-linux-tpm12:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.20.x]
|
||||
go-version: [1.24.x]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: Install libtspi
|
||||
run: sudo apt-get install -y libtspi-dev
|
||||
- name: Test
|
||||
@ -43,33 +43,29 @@ jobs:
|
||||
test-macos:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.20.x]
|
||||
go-version: [1.24.x]
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
# See https://github.com/google/go-tpm-tools#macos-dev
|
||||
- name: Install openssl
|
||||
run: brew install openssl@1.1
|
||||
- name: Link openssl
|
||||
run: sudo ln -s $(brew --prefix openssl@1.1)/include/openssl /usr/local/include
|
||||
- name: Test
|
||||
run: C_INCLUDE_PATH="$(brew --prefix openssl@1.1)/include" LIBRARY_PATH="$(brew --prefix openssl@1.1)/lib" go test ./...
|
||||
test-windows:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.20.x]
|
||||
go-version: [1.24.x]
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: Test
|
||||
run: go build ./...
|
||||
|
@ -72,6 +72,14 @@ type KeyConfig struct {
|
||||
// Size is used to specify the bit size of the key or elliptic curve. For
|
||||
// example, '256' is used to specify curve P-256.
|
||||
Size int
|
||||
// Parent describes the Storage Root Key that will be used as a parent.
|
||||
// If nil, the default SRK (i.e. RSA with handle 0x81000001) is assumed.
|
||||
// Supported only by TPM 2.0 on Linux.
|
||||
Parent *ParentKeyConfig
|
||||
// QualifyingData is an optional data that will be included into
|
||||
// a TPM-generated signature of the minted key.
|
||||
// It may contain any data chosen by the caller.
|
||||
QualifyingData []byte
|
||||
}
|
||||
|
||||
// defaultConfig is used when no other configuration is specified.
|
||||
|
@ -100,6 +100,22 @@ func testKeyCreateAndLoad(t *testing.T, tpm *TPM) {
|
||||
Size: 2048,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "QualifyingData-RSA",
|
||||
opts: &KeyConfig{
|
||||
Algorithm: RSA,
|
||||
Size: 2048,
|
||||
QualifyingData: []byte("qualifying data"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "QualifyingData-ECDSA",
|
||||
opts: &KeyConfig{
|
||||
Algorithm: ECDSA,
|
||||
Size: 256,
|
||||
QualifyingData: []byte("qualifying data"),
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
sk, err := tpm.NewKey(ak, test.opts)
|
||||
|
@ -140,7 +140,7 @@ func runCommand(tpm *attest.TPM) error {
|
||||
fmt.Printf("Version: %d\n", info.Version)
|
||||
fmt.Printf("Interface: %d\n", info.Interface)
|
||||
fmt.Printf("VendorInfo: %x\n", info.VendorInfo)
|
||||
fmt.Printf("Manufactorer: %v\n", info.Manufacturer)
|
||||
fmt.Printf("Manufacturer: %v\n", info.Manufacturer)
|
||||
|
||||
case "make-ak", "make-aik":
|
||||
k, err := tpm.NewAK(nil)
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
|
||||
"github.com/google/go-tpm/legacy/tpm2"
|
||||
"github.com/google/go-tpm/tpm"
|
||||
"github.com/google/go-tpm/tpmutil"
|
||||
)
|
||||
|
||||
// TPMVersion is used to configure a preference in
|
||||
@ -98,13 +99,25 @@ const (
|
||||
keyEncodingParameterized
|
||||
)
|
||||
|
||||
// ParentKeyConfig describes the Storage Root Key that is used
|
||||
// as a parent for new keys.
|
||||
type ParentKeyConfig struct {
|
||||
Algorithm Algorithm
|
||||
Handle tpmutil.Handle
|
||||
}
|
||||
|
||||
var defaultParentConfig = ParentKeyConfig{
|
||||
Algorithm: RSA,
|
||||
Handle: 0x81000001,
|
||||
}
|
||||
|
||||
type ak interface {
|
||||
close(tpmBase) error
|
||||
marshal() ([]byte, error)
|
||||
activateCredential(tpm tpmBase, in EncryptedCredential) ([]byte, error)
|
||||
quote(t tpmBase, nonce []byte, alg HashAlg) (*Quote, error)
|
||||
activateCredential(tpm tpmBase, in EncryptedCredential, ek *EK) ([]byte, error)
|
||||
quote(t tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error)
|
||||
attestationParameters() AttestationParameters
|
||||
certify(tb tpmBase, handle interface{}) (*CertificationParameters, error)
|
||||
certify(tb tpmBase, handle interface{}, opts CertifyOpts) (*CertificationParameters, error)
|
||||
}
|
||||
|
||||
// AK represents a key which can be used for attestation.
|
||||
@ -126,11 +139,22 @@ func (k *AK) Marshal() ([]byte, error) {
|
||||
}
|
||||
|
||||
// ActivateCredential decrypts the secret using the key to prove that the AK
|
||||
// was generated on the same TPM as the EK.
|
||||
// was generated on the same TPM as the EK. This method can be used with TPMs
|
||||
// that have the default EK, i.e. RSA EK with handle 0x81010001.
|
||||
//
|
||||
// This operation is synonymous with TPM2_ActivateCredential.
|
||||
func (k *AK) ActivateCredential(tpm *TPM, in EncryptedCredential) (secret []byte, err error) {
|
||||
return k.ak.activateCredential(tpm.tpm, in)
|
||||
return k.ak.activateCredential(tpm.tpm, in, nil)
|
||||
}
|
||||
|
||||
// ActivateCredentialWithEK decrypts the secret using the key to prove that the AK
|
||||
// was generated on the same TPM as the EK. This method can be used with TPMs
|
||||
// that have an ECC EK. The 'ek' argument must be one of EKs returned from
|
||||
// TPM.EKs() or TPM.EKCertificates().
|
||||
//
|
||||
// This operation is synonymous with TPM2_ActivateCredential.
|
||||
func (k *AK) ActivateCredentialWithEK(tpm *TPM, in EncryptedCredential, ek EK) (secret []byte, err error) {
|
||||
return k.ak.activateCredential(tpm.tpm, in, &ek)
|
||||
}
|
||||
|
||||
// Quote returns a quote over the platform state, signed by the AK.
|
||||
@ -138,7 +162,16 @@ func (k *AK) ActivateCredential(tpm *TPM, in EncryptedCredential) (secret []byte
|
||||
// This is a low-level API. Consumers seeking to attest the state of the
|
||||
// platform should use tpm.AttestPlatform() instead.
|
||||
func (k *AK) Quote(tpm *TPM, nonce []byte, alg HashAlg) (*Quote, error) {
|
||||
return k.ak.quote(tpm.tpm, nonce, alg)
|
||||
pcrs := make([]int, 24)
|
||||
for pcr := range pcrs {
|
||||
pcrs[pcr] = pcr
|
||||
}
|
||||
return k.ak.quote(tpm.tpm, nonce, alg, pcrs)
|
||||
}
|
||||
|
||||
// QuotePCRs is like Quote() but allows the caller to select a subset of the PCRs.
|
||||
func (k *AK) QuotePCRs(tpm *TPM, nonce []byte, alg HashAlg, pcrs []int) (*Quote, error) {
|
||||
return k.ak.quote(tpm.tpm, nonce, alg, pcrs)
|
||||
}
|
||||
|
||||
// AttestationParameters returns information about the AK, typically used to
|
||||
@ -152,12 +185,18 @@ func (k *AK) AttestationParameters() AttestationParameters {
|
||||
// key. Depending on the actual instantiation it can accept different handle
|
||||
// types (e.g., tpmutil.Handle on Linux or uintptr on Windows).
|
||||
func (k *AK) Certify(tpm *TPM, handle interface{}) (*CertificationParameters, error) {
|
||||
return k.ak.certify(tpm.tpm, handle)
|
||||
return k.ak.certify(tpm.tpm, handle, CertifyOpts{})
|
||||
}
|
||||
|
||||
// AKConfig encapsulates parameters for minting keys. This type is defined
|
||||
// now (despite being empty) for future interface compatibility.
|
||||
// AKConfig encapsulates parameters for minting keys.
|
||||
type AKConfig struct {
|
||||
// Parent describes the Storage Root Key that will be used as a parent.
|
||||
// If nil, the default SRK (i.e. RSA with handle 0x81000001) is assumed.
|
||||
// Supported only by TPM 2.0 on Linux.
|
||||
Parent *ParentKeyConfig
|
||||
|
||||
// If not specified, the default algorithm (RSA) is assumed.
|
||||
Algorithm Algorithm
|
||||
}
|
||||
|
||||
// EncryptedCredential represents encrypted parameters which must be activated
|
||||
@ -205,6 +244,9 @@ type EK struct {
|
||||
// Public key. Clients or servers can perform an HTTP GET to this URL, and
|
||||
// use ParseEKCertificate on the response body.
|
||||
CertificateURL string
|
||||
|
||||
// The EK persistent handle.
|
||||
handle tpmutil.Handle
|
||||
}
|
||||
|
||||
// AttestationParameters describes information about a key which is necessary
|
||||
@ -357,41 +399,30 @@ func (a *AKPublic) VerifyAll(quotes []Quote, pcrs []PCR, nonce []byte) error {
|
||||
// HashAlg identifies a hashing Algorithm.
|
||||
type HashAlg uint8
|
||||
|
||||
// Valid hash algorithms.
|
||||
// Known valid hash algorithms.
|
||||
var (
|
||||
HashSHA1 = HashAlg(tpm2.AlgSHA1)
|
||||
HashSHA256 = HashAlg(tpm2.AlgSHA256)
|
||||
HashSHA384 = HashAlg(tpm2.AlgSHA384)
|
||||
HashSHA512 = HashAlg(tpm2.AlgSHA512)
|
||||
)
|
||||
|
||||
func (a HashAlg) cryptoHash() crypto.Hash {
|
||||
switch a {
|
||||
case HashSHA1:
|
||||
return crypto.SHA1
|
||||
case HashSHA256:
|
||||
return crypto.SHA256
|
||||
g := a.goTPMAlg()
|
||||
h, err := g.Hash()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("HashAlg %v (corresponding to TPM2.Algorithm %v) has no corresponding crypto.Hash", a, g))
|
||||
}
|
||||
return 0
|
||||
return h
|
||||
}
|
||||
|
||||
func (a HashAlg) goTPMAlg() tpm2.Algorithm {
|
||||
switch a {
|
||||
case HashSHA1:
|
||||
return tpm2.AlgSHA1
|
||||
case HashSHA256:
|
||||
return tpm2.AlgSHA256
|
||||
}
|
||||
return 0
|
||||
return tpm2.Algorithm(a)
|
||||
}
|
||||
|
||||
// String returns a human-friendly representation of the hash algorithm.
|
||||
func (a HashAlg) String() string {
|
||||
switch a {
|
||||
case HashSHA1:
|
||||
return "SHA1"
|
||||
case HashSHA256:
|
||||
return "SHA256"
|
||||
}
|
||||
return fmt.Sprintf("HashAlg<%d>", int(a))
|
||||
return a.goTPMAlg().String()
|
||||
}
|
||||
|
||||
// PlatformParameters encapsulates the set of information necessary to attest
|
||||
|
@ -67,63 +67,99 @@ func TestSimTPM20Info(t *testing.T) {
|
||||
func TestSimTPM20AKCreateAndLoad(t *testing.T) {
|
||||
sim, tpm := setupSimulatedTPM(t)
|
||||
defer sim.Close()
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
opts *AKConfig
|
||||
}{
|
||||
{
|
||||
name: "NoConfig",
|
||||
opts: nil,
|
||||
},
|
||||
{
|
||||
name: "EmptyConfig",
|
||||
opts: &AKConfig{},
|
||||
},
|
||||
{
|
||||
name: "RSA",
|
||||
opts: &AKConfig{Algorithm: RSA},
|
||||
},
|
||||
{
|
||||
name: "ECDSA",
|
||||
opts: &AKConfig{Algorithm: ECDSA},
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ak, err := tpm.NewAK(test.opts)
|
||||
if err != nil {
|
||||
t.Fatalf("NewAK() failed: %v", err)
|
||||
}
|
||||
|
||||
ak, err := tpm.NewAK(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewAK() failed: %v", err)
|
||||
}
|
||||
enc, err := ak.Marshal()
|
||||
if err != nil {
|
||||
ak.Close(tpm)
|
||||
t.Fatalf("ak.Marshal() failed: %v", err)
|
||||
}
|
||||
if err := ak.Close(tpm); err != nil {
|
||||
t.Fatalf("ak.Close() failed: %v", err)
|
||||
}
|
||||
|
||||
enc, err := ak.Marshal()
|
||||
if err != nil {
|
||||
ak.Close(tpm)
|
||||
t.Fatalf("ak.Marshal() failed: %v", err)
|
||||
}
|
||||
if err := ak.Close(tpm); err != nil {
|
||||
t.Fatalf("ak.Close() failed: %v", err)
|
||||
}
|
||||
loaded, err := tpm.LoadAK(enc)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadAK() failed: %v", err)
|
||||
}
|
||||
defer loaded.Close(tpm)
|
||||
|
||||
loaded, err := tpm.LoadAK(enc)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadKey() failed: %v", err)
|
||||
}
|
||||
defer loaded.Close(tpm)
|
||||
k1, k2 := ak.ak.(*wrappedKey20), loaded.ak.(*wrappedKey20)
|
||||
|
||||
k1, k2 := ak.ak.(*wrappedKey20), loaded.ak.(*wrappedKey20)
|
||||
|
||||
if !bytes.Equal(k1.public, k2.public) {
|
||||
t.Error("Original & loaded AK public blobs did not match.")
|
||||
t.Logf("Original = %v", k1.public)
|
||||
t.Logf("Loaded = %v", k2.public)
|
||||
if !bytes.Equal(k1.public, k2.public) {
|
||||
t.Error("Original & loaded AK public blobs did not match.")
|
||||
t.Logf("Original = %v", k1.public)
|
||||
t.Logf("Loaded = %v", k2.public)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimTPM20ActivateCredential(t *testing.T) {
|
||||
testActivateCredential(t, false)
|
||||
}
|
||||
|
||||
func TestSimTPM20ActivateCredentialWithEK(t *testing.T) {
|
||||
testActivateCredential(t, true)
|
||||
}
|
||||
|
||||
func testActivateCredential(t *testing.T, useEK bool) {
|
||||
sim, tpm := setupSimulatedTPM(t)
|
||||
defer sim.Close()
|
||||
|
||||
ak, err := tpm.NewAK(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewAK() failed: %v", err)
|
||||
}
|
||||
defer ak.Close(tpm)
|
||||
|
||||
EKs, err := tpm.EKs()
|
||||
if err != nil {
|
||||
t.Fatalf("EKs() failed: %v", err)
|
||||
}
|
||||
ek := chooseEK(t, EKs)
|
||||
|
||||
ak, err := tpm.NewAK(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewAK() failed: %v", err)
|
||||
}
|
||||
defer ak.Close(tpm)
|
||||
|
||||
ap := ActivationParameters{
|
||||
TPMVersion: TPMVersion20,
|
||||
AK: ak.AttestationParameters(),
|
||||
EK: ek,
|
||||
EK: ek.Public,
|
||||
}
|
||||
secret, challenge, err := ap.Generate()
|
||||
if err != nil {
|
||||
t.Fatalf("Generate() failed: %v", err)
|
||||
}
|
||||
|
||||
decryptedSecret, err := ak.ActivateCredential(tpm, *challenge)
|
||||
var decryptedSecret []byte
|
||||
if useEK {
|
||||
decryptedSecret, err = ak.ActivateCredentialWithEK(tpm, *challenge, ek)
|
||||
} else {
|
||||
decryptedSecret, err = ak.ActivateCredential(tpm, *challenge)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("ak.ActivateCredential() failed: %v", err)
|
||||
}
|
||||
@ -246,24 +282,69 @@ func TestSimTPM20PCRs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimTPM20Persistence(t *testing.T) {
|
||||
func TestSimTPM20PersistenceSRK(t *testing.T) {
|
||||
testPersistenceSRK(t, defaultParentConfig)
|
||||
}
|
||||
|
||||
func TestSimTPM20PersistenceECCSRK(t *testing.T) {
|
||||
parentConfig := ParentKeyConfig{
|
||||
Algorithm: ECDSA,
|
||||
Handle: 0x81000002,
|
||||
}
|
||||
testPersistenceSRK(t, parentConfig)
|
||||
}
|
||||
|
||||
func testPersistenceSRK(t *testing.T, parentConfig ParentKeyConfig) {
|
||||
sim, tpm := setupSimulatedTPM(t)
|
||||
defer sim.Close()
|
||||
|
||||
ekHnd, _, err := tpm.tpm.(*wrappedTPM20).getPrimaryKeyHandle(commonRSAEkEquivalentHandle)
|
||||
srkHnd, _, err := tpm.tpm.(*wrappedTPM20).getStorageRootKeyHandle(parentConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("getPrimaryKeyHandle() failed: %v", err)
|
||||
t.Fatalf("getStorageRootKeyHandle() failed: %v", err)
|
||||
}
|
||||
if ekHnd != commonRSAEkEquivalentHandle {
|
||||
t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, commonRSAEkEquivalentHandle)
|
||||
if srkHnd != parentConfig.Handle {
|
||||
t.Fatalf("bad SRK-equivalent handle: got 0x%x, wanted 0x%x", srkHnd, parentConfig.Handle)
|
||||
}
|
||||
|
||||
ekHnd, p, err := tpm.tpm.(*wrappedTPM20).getPrimaryKeyHandle(commonRSAEkEquivalentHandle)
|
||||
srkHnd, p, err := tpm.tpm.(*wrappedTPM20).getStorageRootKeyHandle(parentConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("second getPrimaryKeyHandle() failed: %v", err)
|
||||
t.Fatalf("second getStorageRootKeyHandle() failed: %v", err)
|
||||
}
|
||||
if ekHnd != commonRSAEkEquivalentHandle {
|
||||
t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, commonRSAEkEquivalentHandle)
|
||||
if srkHnd != parentConfig.Handle {
|
||||
t.Fatalf("bad SRK-equivalent handle: got 0x%x, wanted 0x%x", srkHnd, parentConfig.Handle)
|
||||
}
|
||||
if p {
|
||||
t.Fatalf("generated a new key the second time; that shouldn't happen")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimTPM20PersistenceEK(t *testing.T) {
|
||||
sim, tpm := setupSimulatedTPM(t)
|
||||
defer sim.Close()
|
||||
|
||||
eks, err := tpm.EKs()
|
||||
if err != nil {
|
||||
t.Errorf("EKs() failed: %v", err)
|
||||
}
|
||||
if len(eks) == 0 || (eks[0].Public == nil) {
|
||||
t.Errorf("EKs() = %v, want at least 1 EK with populated fields", eks)
|
||||
}
|
||||
|
||||
ek := eks[0]
|
||||
ekHnd, _, err := tpm.tpm.(*wrappedTPM20).getEndorsementKeyHandle(&ek)
|
||||
if err != nil {
|
||||
t.Fatalf("getStorageRootKeyHandle() failed: %v", err)
|
||||
}
|
||||
if ekHnd != ek.handle {
|
||||
t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, ek.handle)
|
||||
}
|
||||
|
||||
ekHnd, p, err := tpm.tpm.(*wrappedTPM20).getEndorsementKeyHandle(&ek)
|
||||
if err != nil {
|
||||
t.Fatalf("second getEndorsementKeyHandle() failed: %v", err)
|
||||
}
|
||||
if ekHnd != ek.handle {
|
||||
t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, ek.handle)
|
||||
}
|
||||
if p {
|
||||
t.Fatalf("generated a new key the second time; that shouldn't happen")
|
||||
|
@ -16,7 +16,6 @@ package attest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"flag"
|
||||
"fmt"
|
||||
"reflect"
|
||||
@ -84,51 +83,75 @@ func TestAKCreateAndLoad(t *testing.T) {
|
||||
if !*testLocal {
|
||||
t.SkipNow()
|
||||
}
|
||||
tpm, err := OpenTPM(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenTPM() failed: %v", err)
|
||||
}
|
||||
defer tpm.Close()
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
opts *AKConfig
|
||||
}{
|
||||
{
|
||||
name: "NoConfig",
|
||||
opts: nil,
|
||||
},
|
||||
{
|
||||
name: "EmptyConfig",
|
||||
opts: &AKConfig{},
|
||||
},
|
||||
{
|
||||
name: "RSA",
|
||||
opts: &AKConfig{Algorithm: RSA},
|
||||
},
|
||||
{
|
||||
name: "ECDSA",
|
||||
opts: &AKConfig{Algorithm: ECDSA},
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
tpm, err := OpenTPM(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenTPM() failed: %v", err)
|
||||
}
|
||||
defer tpm.Close()
|
||||
|
||||
ak, err := tpm.NewAK(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewAK() failed: %v", err)
|
||||
}
|
||||
ak, err := tpm.NewAK(test.opts)
|
||||
if err != nil {
|
||||
t.Fatalf("NewAK() failed: %v", err)
|
||||
}
|
||||
|
||||
enc, err := ak.Marshal()
|
||||
if err != nil {
|
||||
ak.Close(tpm)
|
||||
t.Fatalf("ak.Marshal() failed: %v", err)
|
||||
}
|
||||
if err := ak.Close(tpm); err != nil {
|
||||
t.Fatalf("ak.Close() failed: %v", err)
|
||||
}
|
||||
enc, err := ak.Marshal()
|
||||
if err != nil {
|
||||
ak.Close(tpm)
|
||||
t.Fatalf("ak.Marshal() failed: %v", err)
|
||||
}
|
||||
if err := ak.Close(tpm); err != nil {
|
||||
t.Fatalf("ak.Close() failed: %v", err)
|
||||
}
|
||||
|
||||
loaded, err := tpm.LoadAK(enc)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadKey() failed: %v", err)
|
||||
}
|
||||
defer loaded.Close(tpm)
|
||||
loaded, err := tpm.LoadAK(enc)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadAK() failed: %v", err)
|
||||
}
|
||||
defer loaded.Close(tpm)
|
||||
|
||||
k1, k2 := ak.ak.(*wrappedKey20), loaded.ak.(*wrappedKey20)
|
||||
k1, k2 := ak.ak.(*wrappedKey20), loaded.ak.(*wrappedKey20)
|
||||
|
||||
if !bytes.Equal(k1.public, k2.public) {
|
||||
t.Error("Original & loaded AK public blobs did not match.")
|
||||
t.Logf("Original = %v", k1.public)
|
||||
t.Logf("Loaded = %v", k2.public)
|
||||
if !bytes.Equal(k1.public, k2.public) {
|
||||
t.Error("Original & loaded AK public blobs did not match.")
|
||||
t.Logf("Original = %v", k1.public)
|
||||
t.Logf("Loaded = %v", k2.public)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// chooseEK selects the EK public which will be activated against.
|
||||
func chooseEK(t *testing.T, eks []EK) crypto.PublicKey {
|
||||
// chooseEK selects the EK which will be activated against.
|
||||
func chooseEK(t *testing.T, eks []EK) EK {
|
||||
t.Helper()
|
||||
|
||||
for _, ek := range eks {
|
||||
return ek.Public
|
||||
return ek
|
||||
}
|
||||
|
||||
t.Fatalf("No suitable EK found")
|
||||
return nil
|
||||
return EK{}
|
||||
}
|
||||
|
||||
func TestPCRs(t *testing.T) {
|
||||
|
@ -151,7 +151,7 @@ func TestTPMActivateCredential(t *testing.T) {
|
||||
ap := ActivationParameters{
|
||||
TPMVersion: TPMVersion12,
|
||||
AK: ak.AttestationParameters(),
|
||||
EK: ek,
|
||||
EK: ek.Public,
|
||||
}
|
||||
secret, challenge, err := ap.Generate()
|
||||
if err != nil {
|
||||
|
@ -17,6 +17,7 @@ package attest
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
@ -82,6 +83,12 @@ type ActivateOpts struct {
|
||||
VerifierKeyNameDigest *tpm2.HashValue
|
||||
}
|
||||
|
||||
// CertifyOpts specifies options for the key's certification.
|
||||
type CertifyOpts struct {
|
||||
// QualifyingData is the user provided qualifying data.
|
||||
QualifyingData []byte
|
||||
}
|
||||
|
||||
// NewActivateOpts creates options for use in generating an activation challenge for a certified key.
|
||||
// The computed hash is the name digest of the public key used to verify the certification of our key.
|
||||
func NewActivateOpts(verifierPubKey tpm2.Public, ek crypto.PublicKey) (*ActivateOpts, error) {
|
||||
@ -164,11 +171,6 @@ func (p *CertificationParameters) Verify(opts VerifyOpts) error {
|
||||
}
|
||||
|
||||
// Check the signature over the attestation data verifies correctly.
|
||||
// TODO: Support ECC certifying keys
|
||||
pk, ok := opts.Public.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("only RSA verification keys are supported")
|
||||
}
|
||||
if !opts.Hash.Available() {
|
||||
return fmt.Errorf("hash function is unavailable")
|
||||
}
|
||||
@ -184,8 +186,17 @@ func (p *CertificationParameters) Verify(opts VerifyOpts) error {
|
||||
return fmt.Errorf("DecodeSignature() failed: %v", err)
|
||||
}
|
||||
|
||||
if err := rsa.VerifyPKCS1v15(pk, opts.Hash, hsh.Sum(nil), sig.RSA.Signature); err != nil {
|
||||
return fmt.Errorf("could not verify attestation: %v", err)
|
||||
switch pk := opts.Public.(type) {
|
||||
case *rsa.PublicKey:
|
||||
if err := rsa.VerifyPKCS1v15(pk, opts.Hash, hsh.Sum(nil), sig.RSA.Signature); err != nil {
|
||||
return fmt.Errorf("could not verify attestation: %v", err)
|
||||
}
|
||||
case *ecdsa.PublicKey:
|
||||
if ok := ecdsa.Verify(pk, hsh.Sum(nil), sig.ECC.R, sig.ECC.S); !ok {
|
||||
return fmt.Errorf("could not verify ECC attestation")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported public key type: %T", pub)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -236,9 +247,9 @@ func (p *CertificationParameters) Generate(rnd io.Reader, verifyOpts VerifyOpts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// certify uses AK's handle and the passed signature scheme to certify the key
|
||||
// with the `hnd` handle.
|
||||
func certify(tpm io.ReadWriteCloser, hnd, akHnd tpmutil.Handle, scheme tpm2.SigScheme) (*CertificationParameters, error) {
|
||||
// certify uses AK's handle, the passed user qualifying data, and the passed
|
||||
// signature scheme to certify the key with the `hnd` handle.
|
||||
func certify(tpm io.ReadWriteCloser, hnd, akHnd tpmutil.Handle, qualifyingData []byte, scheme tpm2.SigScheme) (*CertificationParameters, error) {
|
||||
pub, _, _, err := tpm2.ReadPublic(tpm, hnd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tpm2.ReadPublic() failed: %v", err)
|
||||
@ -247,7 +258,7 @@ func certify(tpm io.ReadWriteCloser, hnd, akHnd tpmutil.Handle, scheme tpm2.SigS
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not encode public key: %v", err)
|
||||
}
|
||||
att, sig, err := tpm2.CertifyEx(tpm, "", "", hnd, akHnd, nil, scheme)
|
||||
att, sig, err := tpm2.CertifyEx(tpm, "", "", hnd, akHnd, qualifyingData, scheme)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tpm2.Certify() failed: %v", err)
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@ -31,13 +32,19 @@ import (
|
||||
"github.com/google/go-tpm/legacy/tpm2"
|
||||
)
|
||||
|
||||
func TestSimTPM20CertificationParameters(t *testing.T) {
|
||||
func TestSimTPM20CertificationParametersRSA(t *testing.T) {
|
||||
sim, tpm := setupSimulatedTPM(t)
|
||||
defer sim.Close()
|
||||
testCertificationParameters(t, tpm)
|
||||
testCertificationParameters(t, tpm, RSA)
|
||||
}
|
||||
|
||||
func TestTPM20CertificationParameters(t *testing.T) {
|
||||
func TestSimTPM20CertificationParametersECC(t *testing.T) {
|
||||
sim, tpm := setupSimulatedTPM(t)
|
||||
defer sim.Close()
|
||||
testCertificationParameters(t, tpm, ECDSA)
|
||||
}
|
||||
|
||||
func TestTPM20CertificationParametersRSA(t *testing.T) {
|
||||
if !*testLocal {
|
||||
t.SkipNow()
|
||||
}
|
||||
@ -46,11 +53,23 @@ func TestTPM20CertificationParameters(t *testing.T) {
|
||||
t.Fatalf("OpenTPM() failed: %v", err)
|
||||
}
|
||||
defer tpm.Close()
|
||||
testCertificationParameters(t, tpm)
|
||||
testCertificationParameters(t, tpm, RSA)
|
||||
}
|
||||
|
||||
func testCertificationParameters(t *testing.T, tpm *TPM) {
|
||||
ak, err := tpm.NewAK(nil)
|
||||
func TestTPM20CertificationParametersECC(t *testing.T) {
|
||||
if !*testLocal {
|
||||
t.SkipNow()
|
||||
}
|
||||
tpm, err := OpenTPM(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenTPM() failed: %v", err)
|
||||
}
|
||||
defer tpm.Close()
|
||||
testCertificationParameters(t, tpm, ECDSA)
|
||||
}
|
||||
|
||||
func testCertificationParameters(t *testing.T, tpm *TPM, akAlg Algorithm) {
|
||||
ak, err := tpm.NewAK(&AKConfig{Algorithm: akAlg})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -59,12 +78,12 @@ func testCertificationParameters(t *testing.T, tpm *TPM) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pub.Type != tpm2.AlgRSA {
|
||||
t.Fatal("non-RSA verifying key")
|
||||
}
|
||||
|
||||
pk := &rsa.PublicKey{E: int(pub.RSAParameters.Exponent()), N: pub.RSAParameters.Modulus()}
|
||||
hash, err := pub.RSAParameters.Sign.Hash.Hash()
|
||||
pk, err := pub.Key()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hash, err := pub.NameAlg.Hash()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -170,13 +189,19 @@ func testCertificationParameters(t *testing.T, tpm *TPM) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimTPM20KeyCertification(t *testing.T) {
|
||||
func TestSimTPM20KeyCertificationRSA(t *testing.T) {
|
||||
sim, tpm := setupSimulatedTPM(t)
|
||||
defer sim.Close()
|
||||
testKeyCertification(t, tpm)
|
||||
testKeyCertification(t, tpm, RSA)
|
||||
}
|
||||
|
||||
func TestTPM20KeyCertification(t *testing.T) {
|
||||
func TestSimTPM20KeyCertificationECC(t *testing.T) {
|
||||
sim, tpm := setupSimulatedTPM(t)
|
||||
defer sim.Close()
|
||||
testKeyCertification(t, tpm, ECDSA)
|
||||
}
|
||||
|
||||
func TestTPM20KeyCertificationRSA(t *testing.T) {
|
||||
if !*testLocal {
|
||||
t.SkipNow()
|
||||
}
|
||||
@ -185,11 +210,32 @@ func TestTPM20KeyCertification(t *testing.T) {
|
||||
t.Fatalf("OpenTPM() failed: %v", err)
|
||||
}
|
||||
defer tpm.Close()
|
||||
testKeyCertification(t, tpm)
|
||||
testKeyCertification(t, tpm, RSA)
|
||||
}
|
||||
|
||||
func testKeyCertification(t *testing.T, tpm *TPM) {
|
||||
ak, err := tpm.NewAK(nil)
|
||||
func TestTPM20KeyCertificationECC(t *testing.T) {
|
||||
if !*testLocal {
|
||||
t.SkipNow()
|
||||
}
|
||||
tpm, err := OpenTPM(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenTPM() failed: %v", err)
|
||||
}
|
||||
defer tpm.Close()
|
||||
testKeyCertification(t, tpm, ECDSA)
|
||||
}
|
||||
|
||||
func extraData(t *testing.T, p CertificationParameters) []byte {
|
||||
t.Helper()
|
||||
ad, err := tpm2.DecodeAttestationData(p.CreateAttestation)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to decode attestation data: %v", err)
|
||||
}
|
||||
return ad.ExtraData
|
||||
}
|
||||
|
||||
func testKeyCertification(t *testing.T, tpm *TPM, akAlg Algorithm) {
|
||||
ak, err := tpm.NewAK(&AKConfig{Algorithm: akAlg})
|
||||
if err != nil {
|
||||
t.Fatalf("NewAK() failed: %v", err)
|
||||
}
|
||||
@ -198,8 +244,11 @@ func testKeyCertification(t *testing.T, tpm *TPM) {
|
||||
if err != nil {
|
||||
t.Fatalf("DecodePublic() failed: %v", err)
|
||||
}
|
||||
pk := &rsa.PublicKey{E: int(pub.RSAParameters.Exponent()), N: pub.RSAParameters.Modulus()}
|
||||
hash, err := pub.RSAParameters.Sign.Hash.Hash()
|
||||
pk, err := pub.Key()
|
||||
if err != nil {
|
||||
t.Fatalf("pub.Key() failed: %v", err)
|
||||
}
|
||||
hash, err := pub.NameAlg.Hash()
|
||||
if err != nil {
|
||||
t.Fatalf("cannot access AK's hash function: %v", err)
|
||||
}
|
||||
@ -208,9 +257,10 @@ func testKeyCertification(t *testing.T, tpm *TPM) {
|
||||
Hash: hash,
|
||||
}
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
opts *KeyConfig
|
||||
err error
|
||||
name string
|
||||
opts *KeyConfig
|
||||
wantExtraData []byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "default",
|
||||
@ -257,6 +307,26 @@ func testKeyCertification(t *testing.T, tpm *TPM) {
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "QualifyingData-RSA",
|
||||
opts: &KeyConfig{
|
||||
Algorithm: RSA,
|
||||
Size: 2048,
|
||||
QualifyingData: []byte("qualifying data"),
|
||||
},
|
||||
wantExtraData: []byte("qualifying data"),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "QualifyingData-ECDSA",
|
||||
opts: &KeyConfig{
|
||||
Algorithm: ECDSA,
|
||||
Size: 384,
|
||||
QualifyingData: []byte("qualifying data"),
|
||||
},
|
||||
wantExtraData: []byte("qualifying data"),
|
||||
err: nil,
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
sk, err := tpm.NewKey(ak, test.opts)
|
||||
@ -265,6 +335,9 @@ func testKeyCertification(t *testing.T, tpm *TPM) {
|
||||
}
|
||||
defer sk.Close()
|
||||
p := sk.CertificationParameters()
|
||||
if gotExtraData, wantExtraData := extraData(t, p), test.wantExtraData; !slices.Equal(gotExtraData, wantExtraData) {
|
||||
t.Errorf("ExtraData got = %v, want = %v", gotExtraData, wantExtraData)
|
||||
}
|
||||
err = p.Verify(verifyOpts)
|
||||
if test.err == nil && err == nil {
|
||||
return
|
||||
|
@ -533,15 +533,7 @@ func ParseEventLog(measurementLog []byte) (*EventLog, error) {
|
||||
return nil, fmt.Errorf("failed to parse spec ID event: %v", err)
|
||||
}
|
||||
for _, alg := range specID.algs {
|
||||
switch tpm2.Algorithm(alg.ID) {
|
||||
case tpm2.AlgSHA1:
|
||||
el.Algs = append(el.Algs, HashSHA1)
|
||||
case tpm2.AlgSHA256:
|
||||
el.Algs = append(el.Algs, HashSHA256)
|
||||
}
|
||||
}
|
||||
if len(el.Algs) == 0 {
|
||||
return nil, fmt.Errorf("measurement log didn't use sha1 or sha256 digests")
|
||||
el.Algs = append(el.Algs, HashAlg(alg.ID))
|
||||
}
|
||||
// Switch to parsing crypto agile events. Don't include this in the
|
||||
// replayed events since it intentionally doesn't extend the PCRs.
|
||||
|
@ -91,12 +91,62 @@ func ExampleAK_credentialActivation() {
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleAK_credentialActivationWithEK() {
|
||||
tpm, err := attest.OpenTPM(nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open TPM: %v", err)
|
||||
}
|
||||
defer tpm.Close()
|
||||
|
||||
// Create a new AK.
|
||||
ak, err := tpm.NewAK(nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create AK: %v", err)
|
||||
}
|
||||
defer ak.Close(tpm)
|
||||
|
||||
// Read the EK certificates.
|
||||
ekCerts, err := tpm.EKCertificates()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to enumerate EKs: %v", err)
|
||||
}
|
||||
|
||||
// Read parameters necessary to generate a challenge.
|
||||
ap := ak.AttestationParameters()
|
||||
|
||||
// Try activating with each EK certificate.
|
||||
for _, ek := range ekCerts {
|
||||
// Generate a credential activation challenge (usually done on the server).
|
||||
activation := attest.ActivationParameters{
|
||||
TPMVersion: tpm.Version(),
|
||||
EK: ek.Public,
|
||||
AK: ap,
|
||||
}
|
||||
secret, challenge, err := activation.Generate()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate activation challenge: %v", err)
|
||||
}
|
||||
|
||||
// Challenge the AK & EK properties to recieve the decrypted secret.
|
||||
decrypted, err := ak.ActivateCredentialWithEK(tpm, *challenge, ek)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to activate credential: %v", err)
|
||||
}
|
||||
|
||||
// Check that the AK completed the challenge (usually done on the server).
|
||||
if subtle.ConstantTimeCompare(secret, decrypted) == 0 {
|
||||
log.Fatal("Activation response did not match secret")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExampleAK(t *testing.T) {
|
||||
if !*testExamples {
|
||||
t.SkipNow()
|
||||
}
|
||||
ExampleAK()
|
||||
ExampleAK_credentialActivation()
|
||||
ExampleAK_credentialActivationWithEK()
|
||||
}
|
||||
|
||||
func TestExampleTPM(t *testing.T) {
|
||||
|
@ -52,7 +52,7 @@ func (k *trousersKey12) close(tpm tpmBase) error {
|
||||
return nil // No state for tpm 1.2.
|
||||
}
|
||||
|
||||
func (k *trousersKey12) activateCredential(tb tpmBase, in EncryptedCredential) ([]byte, error) {
|
||||
func (k *trousersKey12) activateCredential(tb tpmBase, in EncryptedCredential, ek *EK) ([]byte, error) {
|
||||
t, ok := tb.(*trousersTPM)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected *linuxTPM, got %T", tb)
|
||||
@ -65,7 +65,7 @@ func (k *trousersKey12) activateCredential(tb tpmBase, in EncryptedCredential) (
|
||||
return cred, nil
|
||||
}
|
||||
|
||||
func (k *trousersKey12) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
|
||||
func (k *trousersKey12) quote(tb tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error) {
|
||||
t, ok := tb.(*trousersTPM)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected *linuxTPM, got %T", tb)
|
||||
@ -73,6 +73,9 @@ func (k *trousersKey12) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, er
|
||||
if alg != HashSHA1 {
|
||||
return nil, fmt.Errorf("only SHA1 algorithms supported on TPM 1.2, not %v", alg)
|
||||
}
|
||||
if selectedPCRs != nil {
|
||||
return nil, fmt.Errorf("selecting PCRs not supported on TPM 1.2 (parameter must be nil)")
|
||||
}
|
||||
|
||||
quote, rawSig, err := attestation.GetQuote(t.ctx, k.blob, nonce)
|
||||
if err != nil {
|
||||
@ -93,6 +96,6 @@ func (k *trousersKey12) attestationParameters() AttestationParameters {
|
||||
}
|
||||
}
|
||||
|
||||
func (k *trousersKey12) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) {
|
||||
func (k *trousersKey12) certify(tb tpmBase, handle interface{}, _ CertifyOpts) (*CertificationParameters, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ func (k *windowsKey12) marshal() ([]byte, error) {
|
||||
return out.Serialize()
|
||||
}
|
||||
|
||||
func (k *windowsKey12) activateCredential(t tpmBase, in EncryptedCredential) ([]byte, error) {
|
||||
func (k *windowsKey12) activateCredential(t tpmBase, in EncryptedCredential, ek *EK) ([]byte, error) {
|
||||
tpm, ok := t.(*windowsTPM)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected *windowsTPM, got %T", t)
|
||||
@ -61,7 +61,7 @@ func (k *windowsKey12) activateCredential(t tpmBase, in EncryptedCredential) ([]
|
||||
return decryptCredential(secretKey, in.Secret)
|
||||
}
|
||||
|
||||
func (k *windowsKey12) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
|
||||
func (k *windowsKey12) quote(tb tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error) {
|
||||
if alg != HashSHA1 {
|
||||
return nil, fmt.Errorf("only SHA1 algorithms supported on TPM 1.2, not %v", alg)
|
||||
}
|
||||
@ -80,11 +80,6 @@ func (k *windowsKey12) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, err
|
||||
return nil, fmt.Errorf("TPMCommandInterface() failed: %v", err)
|
||||
}
|
||||
|
||||
selectedPCRs := make([]int, 24)
|
||||
for pcr := range selectedPCRs {
|
||||
selectedPCRs[pcr] = pcr
|
||||
}
|
||||
|
||||
sig, pcrc, err := tpm1.Quote(tpm, tpmKeyHnd, nonce, selectedPCRs[:], wellKnownAuth[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Quote() failed: %v", err)
|
||||
@ -112,7 +107,7 @@ func (k *windowsKey12) attestationParameters() AttestationParameters {
|
||||
Public: k.public,
|
||||
}
|
||||
}
|
||||
func (k *windowsKey12) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) {
|
||||
func (k *windowsKey12) certify(tb tpmBase, handle interface{}, _ CertifyOpts) (*CertificationParameters, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
@ -152,7 +147,7 @@ func (k *windowsKey20) marshal() ([]byte, error) {
|
||||
return out.Serialize()
|
||||
}
|
||||
|
||||
func (k *windowsKey20) activateCredential(t tpmBase, in EncryptedCredential) ([]byte, error) {
|
||||
func (k *windowsKey20) activateCredential(t tpmBase, in EncryptedCredential, ek *EK) ([]byte, error) {
|
||||
tpm, ok := t.(*windowsTPM)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected *windowsTPM, got %T", t)
|
||||
@ -160,7 +155,7 @@ func (k *windowsKey20) activateCredential(t tpmBase, in EncryptedCredential) ([]
|
||||
return tpm.pcp.ActivateCredential(k.hnd, append(in.Credential, in.Secret...))
|
||||
}
|
||||
|
||||
func (k *windowsKey20) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
|
||||
func (k *windowsKey20) quote(tb tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error) {
|
||||
t, ok := tb.(*windowsTPM)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected *windowsTPM, got %T", tb)
|
||||
@ -174,7 +169,7 @@ func (k *windowsKey20) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, err
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("TPMCommandInterface() failed: %v", err)
|
||||
}
|
||||
return quote20(tpm, tpmKeyHnd, alg.goTPMAlg(), nonce)
|
||||
return quote20(tpm, tpmKeyHnd, alg.goTPMAlg(), nonce, selectedPCRs)
|
||||
}
|
||||
|
||||
func (k *windowsKey20) close(tpm tpmBase) error {
|
||||
@ -190,7 +185,7 @@ func (k *windowsKey20) attestationParameters() AttestationParameters {
|
||||
}
|
||||
}
|
||||
|
||||
func (k *windowsKey20) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) {
|
||||
func (k *windowsKey20) certify(tb tpmBase, handle interface{}, _ CertifyOpts) (*CertificationParameters, error) {
|
||||
t, ok := tb.(*windowsTPM)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected *windowsTPM, got %T", tb)
|
||||
@ -215,5 +210,5 @@ func (k *windowsKey20) certify(tb tpmBase, handle interface{}) (*CertificationPa
|
||||
Alg: tpm2.AlgRSASSA,
|
||||
Hash: tpm2.AlgSHA1, // PCP-created AK uses SHA1
|
||||
}
|
||||
return certify(tpm, hnd, akHnd, scheme)
|
||||
return certify(tpm, hnd, akHnd, nil, scheme)
|
||||
}
|
||||
|
156
attest/tpm.go
156
attest/tpm.go
@ -29,6 +29,7 @@ import (
|
||||
|
||||
"github.com/google/go-tpm/legacy/tpm2"
|
||||
"github.com/google/go-tpm/tpmutil"
|
||||
"go.uber.org/multierr"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -39,14 +40,16 @@ const (
|
||||
// Defined in "Registry of reserved TPM 2.0 handles and localities".
|
||||
nvramRSACertIndex = 0x1c00002
|
||||
nvramRSAEkNonceIndex = 0x1c00003
|
||||
nvramECCCertIndex = 0x1c0000a
|
||||
nvramECCEkNonceIndex = 0x1c0000b
|
||||
|
||||
// Defined in "Registry of reserved TPM 2.0 handles and localities", and checked on a glinux machine.
|
||||
commonSrkEquivalentHandle = 0x81000001
|
||||
commonRSAEkEquivalentHandle = 0x81010001
|
||||
commonECCEkEquivalentHandle = 0x81010002
|
||||
)
|
||||
|
||||
var (
|
||||
akTemplate = tpm2.Public{
|
||||
akTemplateRSA = tpm2.Public{
|
||||
Type: tpm2.AlgRSA,
|
||||
NameAlg: tpm2.AlgSHA256,
|
||||
Attributes: tpm2.FlagSignerDefault | tpm2.FlagNoDA,
|
||||
@ -58,7 +61,23 @@ var (
|
||||
KeyBits: 2048,
|
||||
},
|
||||
}
|
||||
defaultSRKTemplate = tpm2.Public{
|
||||
akTemplateECC = tpm2.Public{
|
||||
Type: tpm2.AlgECC,
|
||||
NameAlg: tpm2.AlgSHA256,
|
||||
Attributes: tpm2.FlagSignerDefault | tpm2.FlagNoDA,
|
||||
ECCParameters: &tpm2.ECCParams{
|
||||
Sign: &tpm2.SigScheme{
|
||||
Alg: tpm2.AlgECDSA,
|
||||
Hash: tpm2.AlgSHA256,
|
||||
},
|
||||
CurveID: tpm2.CurveNISTP256,
|
||||
Point: tpm2.ECPoint{
|
||||
XRaw: make([]byte, 32),
|
||||
YRaw: make([]byte, 32),
|
||||
},
|
||||
},
|
||||
}
|
||||
defaultRSASRKTemplate = tpm2.Public{
|
||||
Type: tpm2.AlgRSA,
|
||||
NameAlg: tpm2.AlgSHA256,
|
||||
Attributes: tpm2.FlagStorageDefault | tpm2.FlagNoDA,
|
||||
@ -72,7 +91,24 @@ var (
|
||||
KeyBits: 2048,
|
||||
},
|
||||
}
|
||||
// Default RSA EK template defined in:
|
||||
defaultECCSRKTemplate = tpm2.Public{
|
||||
Type: tpm2.AlgECC,
|
||||
NameAlg: tpm2.AlgSHA256,
|
||||
Attributes: tpm2.FlagStorageDefault | tpm2.FlagNoDA,
|
||||
ECCParameters: &tpm2.ECCParams{
|
||||
Symmetric: &tpm2.SymScheme{
|
||||
Alg: tpm2.AlgAES,
|
||||
KeyBits: 128,
|
||||
Mode: tpm2.AlgCFB,
|
||||
},
|
||||
CurveID: tpm2.CurveNISTP256,
|
||||
Point: tpm2.ECPoint{
|
||||
XRaw: make([]byte, 32),
|
||||
YRaw: make([]byte, 32),
|
||||
},
|
||||
},
|
||||
}
|
||||
// Default RSA and ECC EK templates defined in:
|
||||
// https://trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf
|
||||
defaultRSAEKTemplate = tpm2.Public{
|
||||
Type: tpm2.AlgRSA,
|
||||
@ -97,6 +133,32 @@ var (
|
||||
ModulusRaw: make([]byte, 256),
|
||||
},
|
||||
}
|
||||
defaultECCEKTemplate = tpm2.Public{
|
||||
Type: tpm2.AlgECC,
|
||||
NameAlg: tpm2.AlgSHA256,
|
||||
Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin |
|
||||
tpm2.FlagAdminWithPolicy | tpm2.FlagRestricted | tpm2.FlagDecrypt,
|
||||
AuthPolicy: []byte{
|
||||
0x83, 0x71, 0x97, 0x67, 0x44, 0x84,
|
||||
0xB3, 0xF8, 0x1A, 0x90, 0xCC, 0x8D,
|
||||
0x46, 0xA5, 0xD7, 0x24, 0xFD, 0x52,
|
||||
0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64,
|
||||
0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14,
|
||||
0x69, 0xAA,
|
||||
},
|
||||
ECCParameters: &tpm2.ECCParams{
|
||||
Symmetric: &tpm2.SymScheme{
|
||||
Alg: tpm2.AlgAES,
|
||||
KeyBits: 128,
|
||||
Mode: tpm2.AlgCFB,
|
||||
},
|
||||
CurveID: tpm2.CurveNISTP256,
|
||||
Point: tpm2.ECPoint{
|
||||
XRaw: make([]byte, 32),
|
||||
YRaw: make([]byte, 32),
|
||||
},
|
||||
},
|
||||
}
|
||||
// Basic template for an ECDSA key signing outside-TPM objects. Other
|
||||
// fields are populated depending on the key creation options.
|
||||
ecdsaKeyTemplate = tpm2.Public{
|
||||
@ -234,12 +296,9 @@ func readEKCertFromNVRAM20(tpm io.ReadWriter, nvramCertIndex tpmutil.Handle) (*x
|
||||
return ParseEKCertificate(ekCert)
|
||||
}
|
||||
|
||||
func quote20(tpm io.ReadWriter, akHandle tpmutil.Handle, hashAlg tpm2.Algorithm, nonce []byte) (*Quote, error) {
|
||||
sel := tpm2.PCRSelection{Hash: hashAlg}
|
||||
numPCRs := 24
|
||||
for pcr := 0; pcr < numPCRs; pcr++ {
|
||||
sel.PCRs = append(sel.PCRs, pcr)
|
||||
}
|
||||
func quote20(tpm io.ReadWriter, akHandle tpmutil.Handle, hashAlg tpm2.Algorithm, nonce []byte, selectedPCRs []int) (*Quote, error) {
|
||||
sel := tpm2.PCRSelection{Hash: hashAlg,
|
||||
PCRs: selectedPCRs}
|
||||
|
||||
quote, sig, err := tpm2.Quote(tpm, akHandle, "", "", nonce, sel, tpm2.AlgNull)
|
||||
if err != nil {
|
||||
@ -254,6 +313,31 @@ func quote20(tpm io.ReadWriter, akHandle tpmutil.Handle, hashAlg tpm2.Algorithm,
|
||||
}, err
|
||||
}
|
||||
|
||||
func pcrbanks(tpm io.ReadWriter) ([]HashAlg, error) {
|
||||
vals, _, err := tpm2.GetCapability(tpm, tpm2.CapabilityPCRs, 1024, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get TPM available PCR banks: %w", err)
|
||||
}
|
||||
|
||||
var hAlgs []HashAlg
|
||||
var errs error
|
||||
for i, v := range vals {
|
||||
pcrb, ok := v.(tpm2.PCRSelection)
|
||||
if !ok {
|
||||
errs = multierr.Append(errs, fmt.Errorf("failed to convert value %d to tpm2.PCRSelection: %v", i, v))
|
||||
continue
|
||||
}
|
||||
|
||||
if len(pcrb.PCRs) == 0 {
|
||||
// ignore empty PCR banks.
|
||||
continue
|
||||
}
|
||||
hAlgs = append(hAlgs, HashAlg(pcrb.Hash))
|
||||
}
|
||||
|
||||
return hAlgs, errs
|
||||
}
|
||||
|
||||
func readAllPCRs20(tpm io.ReadWriter, alg tpm2.Algorithm) (map[uint32][]byte, error) {
|
||||
numPCRs := 24
|
||||
out := map[uint32][]byte{}
|
||||
@ -297,12 +381,17 @@ type tpmBase interface {
|
||||
close() error
|
||||
tpmVersion() TPMVersion
|
||||
eks() ([]EK, error)
|
||||
ekCertificates() ([]EK, error)
|
||||
info() (*TPMInfo, error)
|
||||
pcrbanks() ([]HashAlg, error)
|
||||
|
||||
loadAK(opaqueBlob []byte) (*AK, error)
|
||||
loadAKWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*AK, error)
|
||||
newAK(opts *AKConfig) (*AK, error)
|
||||
loadKey(opaqueBlob []byte) (*Key, error)
|
||||
loadKeyWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*Key, error)
|
||||
newKey(ak *AK, opts *KeyConfig) (*Key, error)
|
||||
newKeyCertifiedByKey(ck certifyingKey, opts *KeyConfig) (*Key, error)
|
||||
pcrs(alg HashAlg) ([]PCR, error)
|
||||
measurementLog() ([]byte, error)
|
||||
}
|
||||
@ -320,10 +409,20 @@ func (t *TPM) Close() error {
|
||||
}
|
||||
|
||||
// EKs returns the endorsement keys burned-in to the platform.
|
||||
// Note for Linux clients: for historical reasons, the method assumes that
|
||||
// the TPM has a single EK, and the EK's type is RSA. If the EK's type is ECC
|
||||
// and the TPM contains an ECC EK Certificate, the EKCertificates() method
|
||||
// should be used to retrieve the EKs.
|
||||
func (t *TPM) EKs() ([]EK, error) {
|
||||
return t.tpm.eks()
|
||||
}
|
||||
|
||||
// EKCertificates returns the endorsement key certificates burned-in to the platform.
|
||||
// It is guaranteed that each EK.Certificate field will be populated.
|
||||
func (t *TPM) EKCertificates() ([]EK, error) {
|
||||
return t.tpm.ekCertificates()
|
||||
}
|
||||
|
||||
// Info returns information about the TPM.
|
||||
func (t *TPM) Info() (*TPMInfo, error) {
|
||||
return t.tpm.info()
|
||||
@ -337,6 +436,12 @@ func (t *TPM) LoadAK(opaqueBlob []byte) (*AK, error) {
|
||||
return t.tpm.loadAK(opaqueBlob)
|
||||
}
|
||||
|
||||
// LoadAKWithParent loads a previously-created ak into the TPM
|
||||
// under the given parent for use.
|
||||
func (t *TPM) LoadAKWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*AK, error) {
|
||||
return t.tpm.loadAKWithParent(opaqueBlob, parent)
|
||||
}
|
||||
|
||||
// MeasurementLog returns the present value of the System Measurement Log.
|
||||
//
|
||||
// This is a low-level API. Consumers seeking to attest the state of the
|
||||
@ -372,6 +477,22 @@ func (t *TPM) NewKey(ak *AK, opts *KeyConfig) (*Key, error) {
|
||||
return t.tpm.newKey(ak, opts)
|
||||
}
|
||||
|
||||
// NewKeyCertifiedByKey creates an application key certified by
|
||||
// the attestation key. Unlike NewKey(), this method does not require
|
||||
// an attest.AK object and only requires the AK handle and its algorithm.
|
||||
// Thus it can be used in cases where the attestation key was not created
|
||||
// by go-attestation library. If opts is nil then DefaultConfig is used.
|
||||
func (t *TPM) NewKeyCertifiedByKey(akHandle tpmutil.Handle, akAlg Algorithm, opts *KeyConfig) (*Key, error) {
|
||||
if opts == nil {
|
||||
opts = defaultConfig
|
||||
}
|
||||
if opts.Algorithm == "" && opts.Size == 0 {
|
||||
opts = defaultConfig
|
||||
}
|
||||
ck := certifyingKey{handle: akHandle, alg: akAlg}
|
||||
return t.tpm.newKeyCertifiedByKey(ck, opts)
|
||||
}
|
||||
|
||||
// LoadKey loads a previously-created application key into the TPM for use.
|
||||
// A key loaded via this function needs to be closed with .Close().
|
||||
// Only blobs generated by calling Key.Marshal() are valid parameters
|
||||
@ -389,11 +510,20 @@ func (t *TPM) PCRs(alg HashAlg) ([]PCR, error) {
|
||||
return t.tpm.pcrs(alg)
|
||||
}
|
||||
|
||||
// PCRBanks returns the list of supported PCR banks on the TPM.
|
||||
//
|
||||
// This is a low-level API. Consumers seeking to attest the state of the
|
||||
// platform should use tpm.AttestPlatform() instead.
|
||||
func (t *TPM) PCRBanks() ([]HashAlg, error) {
|
||||
return t.tpm.pcrbanks()
|
||||
}
|
||||
|
||||
func (t *TPM) attestPCRs(ak *AK, nonce []byte, alg HashAlg) (*Quote, []PCR, error) {
|
||||
pcrs, err := t.PCRs(alg)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read %v PCRs: %v", alg, err)
|
||||
}
|
||||
|
||||
quote, err := ak.Quote(t, nonce, alg)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to quote using %v: %v", alg, err)
|
||||
@ -419,9 +549,9 @@ func (t *TPM) attestPlatform(ak *AK, nonce []byte, eventLog []byte) (*PlatformPa
|
||||
EventLog: eventLog,
|
||||
}
|
||||
|
||||
algs := []HashAlg{HashSHA1}
|
||||
if t.Version() == TPMVersion20 {
|
||||
algs = []HashAlg{HashSHA1, HashSHA256}
|
||||
algs, err := t.PCRBanks()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get PCR banks: %w", err)
|
||||
}
|
||||
|
||||
var lastErr error
|
||||
|
@ -94,7 +94,7 @@ func readEKCertFromNVRAM12(ctx *tspi.Context) (*x509.Certificate, error) {
|
||||
return ParseEKCertificate(ekCert)
|
||||
}
|
||||
|
||||
func (t *trousersTPM) eks() ([]EK, error) {
|
||||
func (t *trousersTPM) ekCertificates() ([]EK, error) {
|
||||
cert, err := readEKCertFromNVRAM12(t.ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("readEKCertFromNVRAM failed: %v", err)
|
||||
@ -104,14 +104,26 @@ func (t *trousersTPM) eks() ([]EK, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *trousersTPM) eks() ([]EK, error) {
|
||||
return t.ekCertificates()
|
||||
}
|
||||
|
||||
func (t *trousersTPM) newKey(*AK, *KeyConfig) (*Key, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (t *trousersTPM) newKeyCertifiedByKey(ck certifyingKey, opts *KeyConfig) (*Key, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (t *trousersTPM) loadKey(opaqueBlob []byte) (*Key, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (t *trousersTPM) loadKeyWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*Key, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (t *trousersTPM) newAK(opts *AKConfig) (*AK, error) {
|
||||
pub, blob, err := attestation.CreateAIK(t.ctx)
|
||||
if err != nil {
|
||||
@ -132,6 +144,10 @@ func (t *trousersTPM) loadAK(opaqueBlob []byte) (*AK, error) {
|
||||
return &AK{ak: newTrousersKey12(sKey.Blob, sKey.Public)}, nil
|
||||
}
|
||||
|
||||
func (t *trousersTPM) loadAKWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*AK, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
// allPCRs12 returns a map of all the PCR values on the TPM
|
||||
func allPCRs12(ctx *tspi.Context) (map[uint32][]byte, error) {
|
||||
tpm := ctx.GetTPM()
|
||||
@ -147,6 +163,10 @@ func allPCRs12(ctx *tspi.Context) (map[uint32][]byte, error) {
|
||||
return PCRs, nil
|
||||
}
|
||||
|
||||
func (t *trousersTPM) pcrbanks() ([]HashAlg, error) {
|
||||
return []HashAlg{HashSHA1}, nil
|
||||
}
|
||||
|
||||
func (t *trousersTPM) pcrs(alg HashAlg) ([]PCR, error) {
|
||||
if alg != HashSHA1 {
|
||||
return nil, fmt.Errorf("non-SHA1 algorithm %v is not supported on TPM 1.2", alg)
|
||||
|
@ -152,6 +152,18 @@ func (t *windowsTPM) info() (*TPMInfo, error) {
|
||||
return &tInfo, nil
|
||||
}
|
||||
|
||||
func (t *windowsTPM) ekCertificates() ([]EK, error) {
|
||||
ekCerts, err := t.pcp.EKCerts()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read EKCerts: %v", err)
|
||||
}
|
||||
var eks []EK
|
||||
for _, cert := range ekCerts {
|
||||
eks = append(eks, EK{Certificate: cert, Public: cert.PublicKey})
|
||||
}
|
||||
return eks, nil
|
||||
}
|
||||
|
||||
func (t *windowsTPM) eks() ([]EK, error) {
|
||||
ekCerts, err := t.pcp.EKCerts()
|
||||
if err != nil {
|
||||
@ -325,14 +337,26 @@ func (t *windowsTPM) loadAK(opaqueBlob []byte) (*AK, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *windowsTPM) loadAKWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*AK, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (t *windowsTPM) newKey(*AK, *KeyConfig) (*Key, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (t *windowsTPM) newKeyCertifiedByKey(ck certifyingKey, opts *KeyConfig) (*Key, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (t *windowsTPM) loadKey(opaqueBlob []byte) (*Key, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (t *windowsTPM) loadKeyWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*Key, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func allPCRs12(tpm io.ReadWriter) (map[uint32][]byte, error) {
|
||||
numPCRs := 24
|
||||
out := map[uint32][]byte{}
|
||||
@ -352,6 +376,23 @@ func allPCRs12(tpm io.ReadWriter) (map[uint32][]byte, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (t *windowsTPM) pcrbanks() ([]HashAlg, error) {
|
||||
switch t.version {
|
||||
case TPMVersion12:
|
||||
return []HashAlg{HashSHA1}, nil
|
||||
|
||||
case TPMVersion20:
|
||||
tpm, err := t.pcp.TPMCommandInterface()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("TPMCommandInterface() failed: %v", err)
|
||||
}
|
||||
return pcrbanks(tpm)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported TPM version: %x", t.version)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *windowsTPM) pcrs(alg HashAlg) ([]PCR, error) {
|
||||
var PCRs map[uint32][]byte
|
||||
|
||||
|
@ -35,6 +35,13 @@ type wrappedTPM20 struct {
|
||||
interf TPMInterface
|
||||
rwc CommandChannelTPM20
|
||||
tpmRSAEkTemplate *tpm2.Public
|
||||
tpmECCEkTemplate *tpm2.Public
|
||||
}
|
||||
|
||||
// certifyingKey contains details of a TPM key that could certify other keys.
|
||||
type certifyingKey struct {
|
||||
handle tpmutil.Handle
|
||||
alg Algorithm
|
||||
}
|
||||
|
||||
func (t *wrappedTPM20) rsaEkTemplate() tpm2.Public {
|
||||
@ -54,6 +61,23 @@ func (t *wrappedTPM20) rsaEkTemplate() tpm2.Public {
|
||||
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
|
||||
}
|
||||
@ -83,39 +107,100 @@ func (t *wrappedTPM20) info() (*TPMInfo, error) {
|
||||
return &tInfo, nil
|
||||
}
|
||||
|
||||
// Return value: handle, whether we generated a new one, error
|
||||
func (t *wrappedTPM20) getPrimaryKeyHandle(pHnd tpmutil.Handle) (tpmutil.Handle, bool, error) {
|
||||
_, _, _, err := tpm2.ReadPublic(t.rwc, pHnd)
|
||||
// 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 pHnd, false, nil
|
||||
return ekHandle, false, nil
|
||||
}
|
||||
rerr := err // Preserve this failure for later logging, if needed
|
||||
|
||||
var keyHnd tpmutil.Handle
|
||||
switch pHnd {
|
||||
case commonSrkEquivalentHandle:
|
||||
keyHnd, _, err = tpm2.CreatePrimary(t.rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", defaultSRKTemplate)
|
||||
case commonRSAEkEquivalentHandle:
|
||||
keyHnd, _, err = tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", t.rsaEkTemplate())
|
||||
}
|
||||
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, pHnd)
|
||||
err = tpm2.EvictControl(t.rwc, "", tpm2.HandleOwner, keyHnd, ekHandle)
|
||||
if err != nil {
|
||||
return 0, false, fmt.Errorf("EvictControl failed: %v", err)
|
||||
}
|
||||
|
||||
return pHnd, true, nil
|
||||
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},
|
||||
{Public: crypto.PublicKey(cert.PublicKey), Certificate: cert, handle: commonRSAEkEquivalentHandle},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -133,23 +218,50 @@ func (t *wrappedTPM20) eks() ([]EK, error) {
|
||||
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: &rsa.PublicKey{
|
||||
E: int(pub.RSAParameters.Exponent()),
|
||||
N: pub.RSAParameters.Modulus(),
|
||||
},
|
||||
Public: ekPub,
|
||||
CertificateURL: certificateURL,
|
||||
handle: commonRSAEkEquivalentHandle,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *wrappedTPM20) newAK(opts *AKConfig) (*AK, error) {
|
||||
// TODO(jsonp): Abstract choice of hierarchy & parent.
|
||||
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
|
||||
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)
|
||||
}
|
||||
|
||||
var akTemplate tpm2.Public
|
||||
var sigScheme *tpm2.SigScheme
|
||||
// The default is RSA.
|
||||
if opts != nil && opts.Algorithm == ECDSA {
|
||||
akTemplate = akTemplateECC
|
||||
sigScheme = akTemplateECC.ECCParameters.Sign
|
||||
} else {
|
||||
akTemplate = akTemplateRSA
|
||||
sigScheme = akTemplateRSA.RSAParameters.Sign
|
||||
}
|
||||
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)
|
||||
@ -166,7 +278,7 @@ func (t *wrappedTPM20) newAK(opts *AKConfig) (*AK, error) {
|
||||
}()
|
||||
|
||||
// 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)
|
||||
attestation, sig, err := tpm2.CertifyCreation(t.rwc, "", keyHandle, keyHandle, nil, creationHash, *sigScheme, tix)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CertifyCreation failed: %v", err)
|
||||
}
|
||||
@ -179,6 +291,15 @@ func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) {
|
||||
return nil, fmt.Errorf("expected *wrappedKey20, got: %T", k)
|
||||
}
|
||||
|
||||
kAlg, err := k.algorithm()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get algorithm: %v", err)
|
||||
}
|
||||
ck := certifyingKey{handle: k.hnd, alg: kAlg}
|
||||
return t.newKeyCertifiedByKey(ck, opts)
|
||||
}
|
||||
|
||||
func (t *wrappedTPM20) newKeyCertifiedByKey(ck certifyingKey, opts *KeyConfig) (*Key, error) {
|
||||
parent, blob, pub, creationData, err := createKey(t, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create key: %v", err)
|
||||
@ -196,9 +317,10 @@ func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) {
|
||||
}()
|
||||
|
||||
// Certify application key by AK
|
||||
cp, err := k.certify(t, keyHandle)
|
||||
certifyOpts := CertifyOpts{QualifyingData: opts.QualifyingData}
|
||||
cp, err := certifyByKey(t, keyHandle, ck, certifyOpts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ak.Certify() failed: %v", err)
|
||||
return nil, fmt.Errorf("certifyByKey() failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(pub, cp.Public) {
|
||||
return nil, fmt.Errorf("certified incorrect key, expected: %v, certified: %v", pub, cp.Public)
|
||||
@ -217,7 +339,13 @@ func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) {
|
||||
}
|
||||
|
||||
func createKey(t *wrappedTPM20, opts *KeyConfig) (tpmutil.Handle, []byte, []byte, []byte, error) {
|
||||
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
|
||||
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)
|
||||
}
|
||||
@ -282,7 +410,7 @@ func templateFromConfig(opts *KeyConfig) (tpm2.Public, error) {
|
||||
return tmpl, nil
|
||||
}
|
||||
|
||||
func (t *wrappedTPM20) deserializeAndLoad(opaqueBlob []byte) (tpmutil.Handle, *serializedKey, error) {
|
||||
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)
|
||||
@ -291,7 +419,7 @@ func (t *wrappedTPM20) deserializeAndLoad(opaqueBlob []byte) (tpmutil.Handle, *s
|
||||
return 0, nil, fmt.Errorf("unsupported key encoding: %x", sKey.Encoding)
|
||||
}
|
||||
|
||||
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
|
||||
srk, _, err := t.getStorageRootKeyHandle(parent)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("failed to get SRK handle: %v", err)
|
||||
}
|
||||
@ -303,7 +431,11 @@ func (t *wrappedTPM20) deserializeAndLoad(opaqueBlob []byte) (tpmutil.Handle, *s
|
||||
}
|
||||
|
||||
func (t *wrappedTPM20) loadAK(opaqueBlob []byte) (*AK, error) {
|
||||
hnd, sKey, err := t.deserializeAndLoad(opaqueBlob)
|
||||
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)
|
||||
}
|
||||
@ -311,7 +443,11 @@ func (t *wrappedTPM20) loadAK(opaqueBlob []byte) (*AK, error) {
|
||||
}
|
||||
|
||||
func (t *wrappedTPM20) loadKey(opaqueBlob []byte) (*Key, error) {
|
||||
hnd, sKey, err := t.deserializeAndLoad(opaqueBlob)
|
||||
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)
|
||||
}
|
||||
@ -326,6 +462,10 @@ func (t *wrappedTPM20) loadKey(opaqueBlob []byte) (*Key, error) {
|
||||
return &Key{key: newWrappedKey20(hnd, sKey.Blob, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature), pub: pub, tpm: t}, nil
|
||||
}
|
||||
|
||||
func (t *wrappedTPM20) pcrbanks() ([]HashAlg, error) {
|
||||
return pcrbanks(t.rwc)
|
||||
}
|
||||
|
||||
func (t *wrappedTPM20) pcrs(alg HashAlg) ([]PCR, error) {
|
||||
PCRs, err := readAllPCRs20(t.rwc, alg.goTPMAlg())
|
||||
if err != nil {
|
||||
@ -402,7 +542,7 @@ func (k *wrappedKey20) close(t tpmBase) error {
|
||||
return tpm2.FlushContext(tpm.rwc, k.hnd)
|
||||
}
|
||||
|
||||
func (k *wrappedKey20) activateCredential(tb tpmBase, in EncryptedCredential) ([]byte, error) {
|
||||
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)
|
||||
@ -417,7 +557,7 @@ func (k *wrappedKey20) activateCredential(tb tpmBase, in EncryptedCredential) ([
|
||||
}
|
||||
secret := in.Secret[2:]
|
||||
|
||||
ekHnd, _, err := t.getPrimaryKeyHandle(commonRSAEkEquivalentHandle)
|
||||
ekHnd, _, err := t.getEndorsementKeyHandle(ek)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -446,7 +586,36 @@ func (k *wrappedKey20) activateCredential(tb tpmBase, in EncryptedCredential) ([
|
||||
}, k.hnd, ekHnd, credential, secret)
|
||||
}
|
||||
|
||||
func (k *wrappedKey20) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) {
|
||||
func sigSchemeFromAlgorithm(alg Algorithm) (tpm2.SigScheme, error) {
|
||||
switch alg {
|
||||
case RSA:
|
||||
return tpm2.SigScheme{
|
||||
Alg: tpm2.AlgRSASSA,
|
||||
Hash: tpm2.AlgSHA256,
|
||||
}, nil
|
||||
case ECDSA:
|
||||
return tpm2.SigScheme{
|
||||
Alg: tpm2.AlgECDSA,
|
||||
Hash: tpm2.AlgSHA256,
|
||||
}, nil
|
||||
default:
|
||||
return tpm2.SigScheme{}, fmt.Errorf("algorithm %v not supported", alg)
|
||||
}
|
||||
}
|
||||
|
||||
func (k *wrappedKey20) certify(tb tpmBase, handle interface{}, opts CertifyOpts) (*CertificationParameters, error) {
|
||||
kAlg, err := k.algorithm()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unknown algorithm: %v", err)
|
||||
}
|
||||
ck := certifyingKey{
|
||||
handle: k.hnd,
|
||||
alg: kAlg,
|
||||
}
|
||||
return certifyByKey(tb, handle, ck, opts)
|
||||
}
|
||||
|
||||
func certifyByKey(tb tpmBase, handle interface{}, ck certifyingKey, opts CertifyOpts) (*CertificationParameters, error) {
|
||||
t, ok := tb.(*wrappedTPM20)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb)
|
||||
@ -455,19 +624,19 @@ func (k *wrappedKey20) certify(tb tpmBase, handle interface{}) (*CertificationPa
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected tpmutil.Handle, got %T", handle)
|
||||
}
|
||||
scheme := tpm2.SigScheme{
|
||||
Alg: tpm2.AlgRSASSA,
|
||||
Hash: tpm2.AlgSHA256,
|
||||
scheme, err := sigSchemeFromAlgorithm(ck.alg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get signature scheme: %v", err)
|
||||
}
|
||||
return certify(t.rwc, hnd, k.hnd, scheme)
|
||||
return certify(t.rwc, hnd, ck.handle, opts.QualifyingData, scheme)
|
||||
}
|
||||
|
||||
func (k *wrappedKey20) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
|
||||
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)
|
||||
return quote20(t.rwc, k.hnd, tpm2.Algorithm(alg), nonce, selectedPCRs)
|
||||
}
|
||||
|
||||
func (k *wrappedKey20) attestationParameters() AttestationParameters {
|
||||
@ -513,7 +682,9 @@ func signECDSA(rw io.ReadWriter, key tpmutil.Handle, digest []byte, curve ellipt
|
||||
if excess > 0 {
|
||||
ret.Rsh(ret, uint(excess))
|
||||
}
|
||||
digest = ret.Bytes()
|
||||
// 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 {
|
||||
@ -563,3 +734,18 @@ func (k *wrappedKey20) decrypt(tb tpmBase, ctxt []byte) ([]byte, error) {
|
||||
func (k *wrappedKey20) blobs() ([]byte, []byte, error) {
|
||||
return k.public, k.blob, nil
|
||||
}
|
||||
|
||||
func (k *wrappedKey20) algorithm() (Algorithm, error) {
|
||||
tpmPub, err := tpm2.DecodePublic(k.public)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("decode public key: %v", err)
|
||||
}
|
||||
switch tpmPub.Type {
|
||||
case tpm2.AlgRSA:
|
||||
return RSA, nil
|
||||
case tpm2.AlgECC:
|
||||
return ECDSA, nil
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported key type: %v", tpmPub.Type)
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ var (
|
||||
oidSignatureRSASha1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
|
||||
oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
|
||||
oidSignatureRSASha256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
|
||||
oidSignatureRSASha384 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
|
||||
oidSignatureEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112}
|
||||
|
||||
oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
|
||||
@ -55,6 +56,7 @@ var signatureAlgorithmDetails = []struct {
|
||||
}{
|
||||
{x509.SHA1WithRSA, "SHA1-RSA", oidSignatureRSASha1, x509.RSA, crypto.SHA1},
|
||||
{x509.SHA256WithRSA, "SHA256-RSA", oidSignatureRSASha256, x509.RSA, crypto.SHA256},
|
||||
{x509.SHA384WithRSA, "SHA384-RSA", oidSignatureRSASha384, x509.RSA, crypto.SHA384},
|
||||
{x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256},
|
||||
{x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384},
|
||||
{x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512},
|
||||
|
15
go.mod
15
go.mod
@ -1,16 +1,19 @@
|
||||
module github.com/google/go-attestation
|
||||
|
||||
go 1.20
|
||||
go 1.24
|
||||
|
||||
toolchain go1.24.1
|
||||
|
||||
require (
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/google/go-tpm v0.9.0
|
||||
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/go-tpm v0.9.3
|
||||
github.com/google/go-tpm-tools v0.4.5
|
||||
github.com/google/go-tspi v0.3.0
|
||||
golang.org/x/sys v0.9.0
|
||||
go.uber.org/multierr v1.11.0
|
||||
golang.org/x/sys v0.31.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/certificate-transparency-go v1.1.2 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
)
|
||||
|
48
go.sum
48
go.sum
@ -173,6 +173,7 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
@ -296,27 +297,29 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-configfs-tsm v0.3.3-0.20240919001351-b4b5b84fdcbc h1:SG12DWUUM5igxm+//YX5Yq4vhdoRnOG9HkCodkOn+YU=
|
||||
github.com/google/go-configfs-tsm v0.3.3-0.20240919001351-b4b5b84fdcbc/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo=
|
||||
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
|
||||
github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOmkVoJOpwnS0wfdsJCV9CoD5nJYsHoFk/0CrTK4M=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
|
||||
github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
|
||||
github.com/google/go-sev-guest v0.6.1 h1:NajHkAaLqN9/aW7bCFSUplUMtDgk2+HcN7jC2btFtk0=
|
||||
github.com/google/go-tpm v0.3.4-0.20230613064043-511507721cb1 h1:wGP91a6fiYbZhKlGcQD25K8XwXzoG4yHAEIjtpeV2QA=
|
||||
github.com/google/go-tpm v0.3.4-0.20230613064043-511507721cb1/go.mod h1:Yj9bYgsIKoza8oMlxZqvqgUIDKFaExnuLaDdOtFCwG4=
|
||||
github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk=
|
||||
github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=
|
||||
github.com/google/go-tpm-tools v0.3.12 h1:hpWglH4RaZnGVbgOK3IThI5K++jnFvjQ94EIN34xrUU=
|
||||
github.com/google/go-tpm-tools v0.3.12/go.mod h1:2OtmyPGPuaWWIOjr+IDhNQb6t5njjbSmZtzc350Q6Ro=
|
||||
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba h1:qJEJcuLzH5KDR0gKc0zcktin6KSAwL7+jWKBYceddTc=
|
||||
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba/go.mod h1:EFYHy8/1y2KfgTAsx7Luu7NGhoxtuVHnNo8jE7FikKc=
|
||||
github.com/google/go-sev-guest v0.12.1 h1:H4rFYnPIn8HtqEsNTmh56Zxcf9BI9n48ZSYCnpYLYvc=
|
||||
github.com/google/go-sev-guest v0.12.1/go.mod h1:SK9vW+uyfuzYdVN0m8BShL3OQCtXZe/JPF7ZkpD3760=
|
||||
github.com/google/go-tdx-guest v0.3.2-0.20241009005452-097ee70d0843 h1:+MoPobRN9HrDhGyn6HnF5NYo4uMBKaiFqAtf/D/OB4A=
|
||||
github.com/google/go-tdx-guest v0.3.2-0.20241009005452-097ee70d0843/go.mod h1:g/n8sKITIT9xRivBUbizo34DTsUm2nN2uU3A662h09g=
|
||||
github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc=
|
||||
github.com/google/go-tpm v0.9.3/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
|
||||
github.com/google/go-tpm-tools v0.4.5 h1:3fhthtyMDbIZFR5/0y1hvUoZ1Kf4i1eZ7C73R4Pvd+k=
|
||||
github.com/google/go-tpm-tools v0.4.5/go.mod h1:ktjTNq8yZFD6TzdBFefUfen96rF3NpYwpSb2d8bc+Y8=
|
||||
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
|
||||
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/licenseclassifier v0.0.0-20210325184830-bb04aff29e72/go.mod h1:qsqn2hxC+vURpyBRygGUuinTO42MFRLcsmQ/P8v94+M=
|
||||
github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ=
|
||||
github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
@ -347,7 +350,8 @@ github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4Mgqvf
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s=
|
||||
github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
@ -536,7 +540,6 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9
|
||||
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
@ -545,10 +548,10 @@ github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
@ -646,6 +649,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
|
||||
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
|
||||
@ -732,6 +737,8 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
@ -753,8 +760,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@ -946,8 +953,8 @@ golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -1191,7 +1198,8 @@ google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX7
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@ -1222,6 +1230,8 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -149,8 +149,9 @@ func forEachSAN(extension []byte, callback func(ext asn1.RawValue) error) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalSubjectAltName converts a SubjectAltName struct into a pkix.Extension.
|
||||
func MarshalSubjectAltName(san *SubjectAltName) (pkix.Extension, error) {
|
||||
// MarshalSubjectAltName converts a SubjectAltName struct into a pkix.Extension,
|
||||
// allowing callers to specify if the extension is critical.
|
||||
func MarshalSubjectAltName(san *SubjectAltName, critical bool) (pkix.Extension, error) {
|
||||
var generalNames []asn1.RawValue
|
||||
for _, permID := range san.PermanentIdentifiers {
|
||||
val, err := marshalOtherName(oidPermanentIdentifier, permID)
|
||||
@ -171,7 +172,8 @@ func MarshalSubjectAltName(san *SubjectAltName) (pkix.Extension, error) {
|
||||
return pkix.Extension{}, err
|
||||
}
|
||||
return pkix.Extension{
|
||||
Id: oid.SubjectAltName,
|
||||
Value: val,
|
||||
Id: oid.SubjectAltName,
|
||||
Critical: critical,
|
||||
Value: val,
|
||||
}, nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user