mirror of
https://github.com/google/go-attestation.git
synced 2025-03-14 00:06:31 +00:00
Compare commits
93 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 | ||
|
63dd90f699 | ||
|
ac9aa2497f | ||
|
2788b541c7 | ||
|
a9b6eb1eb8 | ||
|
50c1e1e03b | ||
|
258084d04e | ||
|
89884d0a74 | ||
|
b474b712d4 | ||
|
a4b579bcf0 | ||
|
62a036b369 | ||
|
10dd5f7a05 | ||
|
3ef3949b46 | ||
|
1f9c436d57 | ||
|
270ecbab1f | ||
|
0ccbb50494 | ||
|
68deb4ce55 | ||
|
5238453493 | ||
|
b93151db1f | ||
|
0dc056af7d | ||
|
19d3c4de97 | ||
|
438907edb0 | ||
|
17f9c05652 | ||
|
d98599d257 | ||
|
053c50e8ad | ||
|
e99c3e104e | ||
|
dff2daeaf0 | ||
|
f5d560164e | ||
|
cb976082a3 | ||
|
50e72a4743 | ||
|
f1ff544e51 | ||
|
e0bd974e4e | ||
|
ad58dc770e | ||
|
8235370483 | ||
|
8820d49b18 | ||
|
0961a88d7c | ||
|
df6b91cbdb | ||
|
03018e6828 | ||
|
0a9ecdcf7c | ||
|
4b44082d2c | ||
|
2a5dfec7cf | ||
|
83d71b1c53 |
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
|
||||
|
18
.github/workflows/golangci-lint.yml
vendored
Normal file
18
.github/workflows/golangci-lint.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
name: golangci-lint
|
||||
on:
|
||||
pull_request:
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
- uses: actions/checkout@v4
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: v1.64.6
|
39
.github/workflows/test.yml
vendored
39
.github/workflows/test.yml
vendored
@ -1,32 +1,41 @@
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
|
||||
# Workaround for SHA1 on Go 1.18. There are some kinks to be worked out. See
|
||||
# the tracking issue for more info: https://github.com/golang/go/issues/41682
|
||||
env:
|
||||
GODEBUG: x509sha1=1
|
||||
|
||||
name: Test
|
||||
jobs:
|
||||
test-linux:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.16.x, 1.17.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.16.x, 1.17.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
|
||||
@ -34,33 +43,29 @@ jobs:
|
||||
test-macos:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.16.x, 1.17.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.16.x, 1.17.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 ./...
|
||||
|
5
.golangci.yaml
Normal file
5
.golangci.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
linters:
|
||||
enable:
|
||||
- gofmt
|
||||
disable:
|
||||
- errcheck
|
@ -81,7 +81,7 @@ if err != nil {
|
||||
// handle error
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile("encrypted_aik.json", akBytes, 0600); err != nil {
|
||||
if err := os.WriteFile("encrypted_aik.json", akBytes, 0600); err != nil {
|
||||
// handle error
|
||||
}
|
||||
|
||||
@ -115,7 +115,7 @@ returning the same secret to the server.
|
||||
```go
|
||||
// Client decrypts the credential
|
||||
|
||||
akBytes, err := ioutil.ReadFile("encrypted_aik.json")
|
||||
akBytes, err := os.ReadFile("encrypted_aik.json")
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
|
@ -9,11 +9,11 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/google/go-tpm/legacy/tpm2"
|
||||
tpm1 "github.com/google/go-tpm/tpm"
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
|
||||
// TODO(jsonp): Move activation generation code to internal package.
|
||||
"github.com/google/go-tpm/tpm2/credactivation"
|
||||
"github.com/google/go-tpm/legacy/tpm2/credactivation"
|
||||
"github.com/google/go-tspi/verification"
|
||||
)
|
||||
|
||||
|
@ -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.
|
||||
|
@ -12,10 +12,10 @@
|
||||
// License for the specific language governing permissions and limitations under
|
||||
// the License.
|
||||
|
||||
//go:build (!localtest || !tpm12) && linux && cgo
|
||||
//go:build (!localtest || !tpm12) && cgo && !gofuzz
|
||||
// +build !localtest !tpm12
|
||||
// +build linux
|
||||
// +build cgo
|
||||
// +build !gofuzz
|
||||
|
||||
// NOTE: simulator requires cgo, hence the build tag.
|
||||
|
||||
@ -28,11 +28,10 @@ import (
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
)
|
||||
|
||||
func TestSimTPM20KeyCreateAndLoad(t *testing.T) {
|
||||
@ -101,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)
|
||||
@ -497,8 +512,7 @@ func testKeyOpts(t *testing.T, tpm *TPM) {
|
||||
expected = defaultConfig
|
||||
}
|
||||
|
||||
pub := sk.Public()
|
||||
switch pub.(type) {
|
||||
switch pub := sk.Public().(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
if expected.Algorithm != ECDSA {
|
||||
t.Errorf("incorrect key type generated, expected %q, got EC", expected.Algorithm)
|
||||
@ -512,16 +526,15 @@ func testKeyOpts(t *testing.T, tpm *TPM) {
|
||||
if !ok {
|
||||
t.Fatalf("cannot match curve to key size %d", expected.Size)
|
||||
}
|
||||
curve := pub.(*ecdsa.PublicKey).Curve
|
||||
if expectedCurve != curve {
|
||||
t.Errorf("incorrect curve, expected %v, got %v", expectedCurve, curve)
|
||||
if expectedCurve != pub.Curve {
|
||||
t.Errorf("incorrect curve, expected %v, got %v", expectedCurve, pub.Curve)
|
||||
}
|
||||
case *rsa.PublicKey:
|
||||
if expected.Algorithm != RSA {
|
||||
t.Errorf("incorrect key type, expected %q, got RSA", expected.Algorithm)
|
||||
}
|
||||
if pub.(*rsa.PublicKey).Size()*8 != expected.Size {
|
||||
t.Errorf("incorrect key size, expected %d, got %d", expected.Size, pub.(*rsa.PublicKey).Size()*8)
|
||||
if pub.Size()*8 != expected.Size {
|
||||
t.Errorf("incorrect key size, expected %d, got %d", expected.Size, pub.Size()*8)
|
||||
}
|
||||
default:
|
||||
t.Errorf("unsupported key type: %T", pub)
|
||||
|
@ -6,16 +6,15 @@ import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
"github.com/google/go-attestation/attest"
|
||||
"github.com/google/go-attestation/attest/attest-tool/internal"
|
||||
)
|
||||
@ -141,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)
|
||||
@ -153,10 +152,10 @@ func runCommand(tpm *attest.TPM) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(*keyPath, b, 0644)
|
||||
return os.WriteFile(*keyPath, b, 0644)
|
||||
|
||||
case "quote":
|
||||
b, err := ioutil.ReadFile(*keyPath)
|
||||
b, err := os.ReadFile(*keyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ package eventlog
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-attestation/attest"
|
||||
@ -24,7 +24,7 @@ import (
|
||||
)
|
||||
|
||||
func parseEvents(t *testing.T, testdata string) []attest.Event {
|
||||
data, err := ioutil.ReadFile(testdata)
|
||||
data, err := os.ReadFile(testdata)
|
||||
if err != nil {
|
||||
t.Fatalf("reading test data: %v", err)
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package internal
|
||||
|
||||
import (
|
||||
"github.com/google/go-attestation/attest"
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
"github.com/google/go-tpm/legacy/tpm2"
|
||||
)
|
||||
|
||||
// Dump describes the layout of serialized information from the dump command.
|
||||
|
103
attest/attest.go
103
attest/attest.go
@ -17,14 +17,15 @@ package attest
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
"github.com/google/go-tpm/legacy/tpm2"
|
||||
"github.com/google/go-tpm/tpm"
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
"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,51 +399,40 @@ 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
|
||||
// the booted state of the machine the TPM is attached to.
|
||||
//
|
||||
// The digests contained in the event log can be considered authentic if:
|
||||
// - The AK public corresponds to the known AK for that platform.
|
||||
// - All quotes are verified with AKPublic.Verify(), and return no errors.
|
||||
// - The event log parsed successfully using ParseEventLog(), and a call
|
||||
// to EventLog.Verify() with the full set of PCRs returned no error.
|
||||
// - The AK public corresponds to the known AK for that platform.
|
||||
// - All quotes are verified with AKPublic.Verify(), and return no errors.
|
||||
// - The event log parsed successfully using ParseEventLog(), and a call
|
||||
// to EventLog.Verify() with the full set of PCRs returned no error.
|
||||
type PlatformParameters struct {
|
||||
// The version of the TPM which generated this attestation.
|
||||
TPMVersion TPMVersion
|
||||
|
@ -12,10 +12,10 @@
|
||||
// License for the specific language governing permissions and limitations under
|
||||
// the License.
|
||||
|
||||
//go:build (!localtest || !tpm12) && linux && cgo
|
||||
//go:build (!localtest || !tpm12) && cgo && !gofuzz
|
||||
// +build !localtest !tpm12
|
||||
// +build linux
|
||||
// +build cgo
|
||||
// +build !gofuzz
|
||||
|
||||
// NOTE: simulator requires cgo, hence the build tag.
|
||||
|
||||
@ -35,7 +35,7 @@ func setupSimulatedTPM(t *testing.T) (*simulator.Simulator, *TPM) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
attestTPM, err := OpenTPM(&OpenConfig{CommandChannel: &linuxCmdChannel{tpm}})
|
||||
attestTPM, err := OpenTPM(&OpenConfig{CommandChannel: &fakeCmdChannel{tpm}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -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(commonEkEquivalentHandle)
|
||||
srkHnd, _, err := tpm.tpm.(*wrappedTPM20).getStorageRootKeyHandle(parentConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("getPrimaryKeyHandle() failed: %v", err)
|
||||
t.Fatalf("getStorageRootKeyHandle() failed: %v", err)
|
||||
}
|
||||
if ekHnd != commonEkEquivalentHandle {
|
||||
t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, commonEkEquivalentHandle)
|
||||
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(commonEkEquivalentHandle)
|
||||
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 != commonEkEquivalentHandle {
|
||||
t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, commonEkEquivalentHandle)
|
||||
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,12 +17,15 @@ package attest
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
"github.com/google/go-tpm/legacy/tpm2"
|
||||
"github.com/google/go-tpm/legacy/tpm2/credactivation"
|
||||
"github.com/google/go-tpm/tpmutil"
|
||||
)
|
||||
|
||||
@ -62,6 +65,44 @@ type VerifyOpts struct {
|
||||
Hash crypto.Hash
|
||||
}
|
||||
|
||||
// ActivateOpts specifies options for the key certification's challenge generation.
|
||||
type ActivateOpts struct {
|
||||
// EK, the endorsement key, describes an asymmetric key whose
|
||||
// private key is permanently bound to the TPM.
|
||||
//
|
||||
// Activation will verify that the provided EK is held on the same
|
||||
// TPM as the key we're certifying. However, it is the caller's responsibility to
|
||||
// ensure the EK they provide corresponds to the the device which
|
||||
// they are trying to associate the certified key with.
|
||||
EK crypto.PublicKey
|
||||
// VerifierKeyNameDigest is the name digest of the public key we're using to
|
||||
// verify the certification of the tpm-generated key being activated.
|
||||
// The verifier key (usually the AK) that owns this digest should be the same
|
||||
// key used in VerifyOpts.Public.
|
||||
// Use tpm2.Public.Name() to produce the digest for a provided key.
|
||||
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) {
|
||||
pubName, err := verifierPubKey.Name()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to resolve a tpm2.Public Name struct from the given public key struct: %v", err)
|
||||
}
|
||||
|
||||
return &ActivateOpts{
|
||||
EK: ek,
|
||||
VerifierKeyNameDigest: pubName.Digest,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Verify verifies the TPM2-produced certification parameters checking whether:
|
||||
// - the key length is secure
|
||||
// - the attestation parameters matched the attested key
|
||||
@ -130,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")
|
||||
}
|
||||
@ -150,16 +186,70 @@ 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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// Generate returns a credential activation challenge, which can be provided
|
||||
// to the TPM to verify the AK parameters given are authentic & the AK
|
||||
// is present on the same TPM as the EK.
|
||||
//
|
||||
// The caller is expected to verify the secret returned from the TPM as
|
||||
// as result of calling ActivateCredential() matches the secret returned here.
|
||||
// The caller should use subtle.ConstantTimeCompare to avoid potential
|
||||
// timing attack vectors.
|
||||
func (p *CertificationParameters) Generate(rnd io.Reader, verifyOpts VerifyOpts, activateOpts ActivateOpts) (secret []byte, ec *EncryptedCredential, err error) {
|
||||
if err := p.Verify(verifyOpts); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if activateOpts.EK == nil {
|
||||
return nil, nil, errors.New("no EK provided")
|
||||
}
|
||||
|
||||
secret = make([]byte, activationSecretLen)
|
||||
if rnd == nil {
|
||||
rnd = rand.Reader
|
||||
}
|
||||
if _, err = io.ReadFull(rnd, secret); err != nil {
|
||||
return nil, nil, fmt.Errorf("error generating activation secret: %v", err)
|
||||
}
|
||||
|
||||
att, err := tpm2.DecodeAttestationData(p.CreateAttestation)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("DecodeAttestationData() failed: %v", err)
|
||||
}
|
||||
|
||||
if att.Type != tpm2.TagAttestCertify {
|
||||
return nil, nil, fmt.Errorf("attestation does not apply to certify data, got %x", att.Type)
|
||||
}
|
||||
|
||||
cred, encSecret, err := credactivation.Generate(activateOpts.VerifierKeyNameDigest, activateOpts.EK, symBlockSize, secret)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("credactivation.Generate() failed: %v", err)
|
||||
}
|
||||
|
||||
return secret, &EncryptedCredential{
|
||||
Credential: cred,
|
||||
Secret: encSecret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
@ -168,17 +258,13 @@ 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)
|
||||
}
|
||||
signature, err := tpmutil.Pack(scheme.Alg, scheme.Hash, tpmutil.U16Bytes(sig))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to pack TPMT_SIGNATURE: %v", err)
|
||||
}
|
||||
return &CertificationParameters{
|
||||
Public: public,
|
||||
CreateAttestation: att,
|
||||
CreateSignature: signature,
|
||||
CreateSignature: sig,
|
||||
}, nil
|
||||
}
|
||||
|
@ -12,31 +12,39 @@
|
||||
// License for the specific language governing permissions and limitations under
|
||||
// the License.
|
||||
|
||||
//go:build (!localtest || !tpm12) && linux && cgo
|
||||
//go:build (!localtest || !tpm12) && cgo && !gofuzz
|
||||
// +build !localtest !tpm12
|
||||
// +build linux
|
||||
// +build cgo
|
||||
// +build !gofuzz
|
||||
|
||||
package attest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
"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()
|
||||
}
|
||||
@ -45,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)
|
||||
}
|
||||
@ -58,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)
|
||||
}
|
||||
@ -169,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()
|
||||
}
|
||||
@ -184,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)
|
||||
}
|
||||
@ -197,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)
|
||||
}
|
||||
@ -207,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",
|
||||
@ -256,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)
|
||||
@ -264,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
|
||||
@ -274,3 +348,125 @@ func testKeyCertification(t *testing.T, tpm *TPM) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyActivationTPM20(t *testing.T) {
|
||||
sim, tpm := setupSimulatedTPM(t)
|
||||
defer sim.Close()
|
||||
|
||||
ak, err := tpm.NewAK(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating a new AK using simulated TPM: %v", err)
|
||||
}
|
||||
akAttestParams := ak.AttestationParameters()
|
||||
pub, err := tpm2.DecodePublic(akAttestParams.Public)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to decode public struct from AK attestation params: %v", err)
|
||||
}
|
||||
if pub.Type != tpm2.AlgRSA {
|
||||
t.Fatal("non-RSA verifying key")
|
||||
}
|
||||
|
||||
eks, err := tpm.EKs()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error retrieving EK from tpm: %v", err)
|
||||
}
|
||||
|
||||
if len(eks) == 0 {
|
||||
t.Fatal("expected at least one EK from the simulated TPM")
|
||||
}
|
||||
|
||||
pk := &rsa.PublicKey{E: int(pub.RSAParameters.Exponent()), N: pub.RSAParameters.Modulus()}
|
||||
hash, err := pub.RSAParameters.Sign.Hash.Hash()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to compute hash signature from verifying key's RSA parameters: %v", err)
|
||||
}
|
||||
verifyOpts := VerifyOpts{
|
||||
Public: pk,
|
||||
Hash: hash,
|
||||
}
|
||||
|
||||
sk, err := tpm.NewKey(ak, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create a new TPM-backed key to certify: %v", err)
|
||||
}
|
||||
|
||||
skCertParams := sk.CertificationParameters()
|
||||
activateOpts, err := NewActivateOpts(pub, eks[0].Public)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create new ActivateOpts: %v", err)
|
||||
}
|
||||
|
||||
wrongPub, err := tpm2.DecodePublic(skCertParams.Public)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to decode public struct from CertificationParameters: %v", err)
|
||||
}
|
||||
|
||||
wrongActivateOpts, err := NewActivateOpts(wrongPub, eks[0].Public)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create wrong ActivateOpts: %v", err)
|
||||
}
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
p *CertificationParameters
|
||||
verifyOpts VerifyOpts
|
||||
activateOpts ActivateOpts
|
||||
generateErr error
|
||||
activateErr error
|
||||
}{
|
||||
{
|
||||
name: "OK",
|
||||
p: &skCertParams,
|
||||
verifyOpts: verifyOpts,
|
||||
activateOpts: *activateOpts,
|
||||
generateErr: nil,
|
||||
activateErr: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid verify opts",
|
||||
p: &skCertParams,
|
||||
verifyOpts: VerifyOpts{},
|
||||
activateOpts: *activateOpts,
|
||||
generateErr: cmpopts.AnyError,
|
||||
activateErr: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid activate opts",
|
||||
p: &skCertParams,
|
||||
verifyOpts: verifyOpts,
|
||||
activateOpts: *wrongActivateOpts,
|
||||
generateErr: nil,
|
||||
activateErr: cmpopts.AnyError,
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
expectedSecret, encryptedCredentials, err := test.p.Generate(rand.Reader, test.verifyOpts, test.activateOpts)
|
||||
if test.generateErr != nil {
|
||||
if got, want := err, test.generateErr; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
|
||||
t.Errorf("p.Generate() err = %v, want = %v", got, want)
|
||||
}
|
||||
|
||||
return
|
||||
} else if err != nil {
|
||||
t.Errorf("unexpected p.Generate() error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
actualSecret, err := ak.ActivateCredential(tpm, *encryptedCredentials)
|
||||
if test.activateErr != nil {
|
||||
if got, want := err, test.activateErr; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
|
||||
t.Errorf("p.ActivateCredential() err = %v, want = %v", got, want)
|
||||
}
|
||||
|
||||
return
|
||||
} else if err != nil {
|
||||
t.Errorf("unexpected p.ActivateCredential() error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(expectedSecret, actualSecret) {
|
||||
t.Fatalf("Unexpected bytes decoded, expected %x, but got %x", expectedSecret, actualSecret)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,8 @@ import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"testing"
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
)
|
||||
|
||||
func TestMakeActivationBlob(t *testing.T) {
|
||||
|
@ -30,7 +30,7 @@ import (
|
||||
// Ensure hashes are available.
|
||||
_ "crypto/sha256"
|
||||
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
"github.com/google/go-tpm/legacy/tpm2"
|
||||
"github.com/google/go-tpm/tpmutil"
|
||||
)
|
||||
|
||||
@ -56,14 +56,6 @@ func (e ReplayError) Error() string {
|
||||
return fmt.Sprintf("event log failed to verify: the following registers failed to replay: %v", e.InvalidPCRs)
|
||||
}
|
||||
|
||||
// TPM algorithms. See the TPM 2.0 specification section 6.3.
|
||||
//
|
||||
// https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf#page=42
|
||||
const (
|
||||
algSHA1 uint16 = 0x0004
|
||||
algSHA256 uint16 = 0x000B
|
||||
)
|
||||
|
||||
// EventType indicates what kind of data an event is reporting.
|
||||
//
|
||||
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf#page=103
|
||||
@ -383,7 +375,7 @@ func (a *AKPublic) validate20Quote(quote Quote, pcrs []PCR, nonce []byte) error
|
||||
sigHash.Write(digest)
|
||||
}
|
||||
|
||||
for index, _ := range pcrByIndex {
|
||||
for index := range pcrByIndex {
|
||||
if _, exists := quotePCRs[index]; !exists {
|
||||
return fmt.Errorf("provided PCR %d was not included in quote", index)
|
||||
}
|
||||
@ -541,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.
|
||||
@ -759,9 +743,6 @@ func parseRawEvent2(r *bytes.Buffer, specID *specIDEvent) (event rawEvent, err e
|
||||
if err = binary.Read(r, binary.LittleEndian, &eventSize); err != nil {
|
||||
return event, err
|
||||
}
|
||||
if eventSize == 0 {
|
||||
return event, errors.New("event data size is 0")
|
||||
}
|
||||
if eventSize > uint32(r.Len()) {
|
||||
return event, &eventSizeErr{eventSize, r.Len()}
|
||||
}
|
||||
|
@ -18,10 +18,10 @@ import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
"github.com/google/go-tpm/legacy/tpm2"
|
||||
)
|
||||
|
||||
// Dump describes the layout of serialized information from the dump command.
|
||||
@ -56,7 +56,7 @@ func TestParseEventLogLinux(t *testing.T) {
|
||||
}
|
||||
|
||||
func testParseEventLog(t *testing.T, testdata string) {
|
||||
data, err := ioutil.ReadFile(testdata)
|
||||
data, err := os.ReadFile(testdata)
|
||||
if err != nil {
|
||||
t.Fatalf("reading test data: %v", err)
|
||||
}
|
||||
@ -70,7 +70,7 @@ func testParseEventLog(t *testing.T, testdata string) {
|
||||
}
|
||||
|
||||
func TestParseCryptoAgileEventLog(t *testing.T) {
|
||||
data, err := ioutil.ReadFile("testdata/crypto_agile_eventlog")
|
||||
data, err := os.ReadFile("testdata/crypto_agile_eventlog")
|
||||
if err != nil {
|
||||
t.Fatalf("reading test data: %v", err)
|
||||
}
|
||||
@ -88,7 +88,7 @@ func TestEventLog(t *testing.T) {
|
||||
}
|
||||
|
||||
func testEventLog(t *testing.T, testdata string) {
|
||||
data, err := ioutil.ReadFile(testdata)
|
||||
data, err := os.ReadFile(testdata)
|
||||
if err != nil {
|
||||
t.Fatalf("reading test data: %v", err)
|
||||
}
|
||||
@ -174,6 +174,43 @@ func TestParseEventLogEventSizeZero(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseEventLog2EventSizeZero(t *testing.T) {
|
||||
data := []byte{
|
||||
// PCR index
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
|
||||
// type
|
||||
0x7, 0x0, 0x0, 0x0,
|
||||
|
||||
// number of digests
|
||||
0x1, 0x0, 0x0, 0x0,
|
||||
|
||||
// algorithm
|
||||
0xb, 0x0,
|
||||
|
||||
// Digest
|
||||
0xc8, 0xe3, 0x88, 0xb4, 0x79, 0x12, 0x86, 0x0c,
|
||||
0x66, 0xa1, 0x5d, 0xad, 0xc4, 0x34, 0xf5, 0xdf,
|
||||
0x73, 0x6c, 0x3a, 0xb4, 0xbe, 0x52, 0x07, 0x08,
|
||||
0xdf, 0xac, 0x48, 0x2d, 0x71, 0xce, 0xa0, 0x73,
|
||||
|
||||
// Event size (0 B)
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
|
||||
// no "event data"
|
||||
}
|
||||
|
||||
specID := &specIDEvent{
|
||||
algs: []specAlgSize{
|
||||
{ID: uint16(tpm2.AlgSHA256), Size: 32},
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := parseRawEvent2(bytes.NewBuffer(data), specID); err != nil {
|
||||
t.Fatalf("parsing event log: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseShortNoAction(t *testing.T) {
|
||||
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf#page=110
|
||||
// says: "For EV_NO_ACTION events other than the EFI Specification ID event
|
||||
@ -183,7 +220,7 @@ func TestParseShortNoAction(t *testing.T) {
|
||||
// Currently we just assume that such events will have Data shorter than
|
||||
// "EFI Specification ID" field.
|
||||
|
||||
data, err := ioutil.ReadFile("testdata/short_no_action_eventlog")
|
||||
data, err := os.ReadFile("testdata/short_no_action_eventlog")
|
||||
if err != nil {
|
||||
t.Fatalf("reading test data: %v", err)
|
||||
}
|
||||
@ -326,7 +363,7 @@ func TestEBSVerifyWorkaround(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
elr, err := ioutil.ReadFile("testdata/ebs_event_missing_eventlog")
|
||||
elr, err := os.ReadFile("testdata/ebs_event_missing_eventlog")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -340,7 +377,7 @@ func TestEBSVerifyWorkaround(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAppendEvents(t *testing.T) {
|
||||
base, err := ioutil.ReadFile("testdata/ubuntu_2104_shielded_vm_no_secure_boot_eventlog")
|
||||
base, err := os.ReadFile("testdata/ubuntu_2104_shielded_vm_no_secure_boot_eventlog")
|
||||
if err != nil {
|
||||
t.Fatalf("reading test data: %v", err)
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -2,14 +2,12 @@ package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"unicode/utf16"
|
||||
|
||||
"github.com/google/certificate-transparency-go/asn1"
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -43,12 +41,14 @@ var (
|
||||
shimLockGUID = efiGUID{0x605dab50, 0xe046, 0x4300, [8]byte{0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23}}
|
||||
// "SbatLevel" encoded as UCS-2.
|
||||
shimSbatVarName = []uint16{0x53, 0x62, 0x61, 0x74, 0x4c, 0x65, 0x76, 0x65, 0x6c}
|
||||
// "MokListTrusted" encoded as UCS-2.
|
||||
shimMokListTrustedVarName = []uint16{0x4d, 0x6f, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64}
|
||||
)
|
||||
|
||||
// EventType describes the type of event signalled in the event log.
|
||||
type EventType uint32
|
||||
|
||||
// BIOS Events (TCG PC Client Specific Implementation Specification for Conventional BIOS 1.21)
|
||||
// BIOS Events (TCG PC Client Specific Implementation Specification for Conventional BIOS 1.21)
|
||||
const (
|
||||
PrebootCert EventType = 0x00000000
|
||||
PostCode EventType = 0x00000001
|
||||
@ -188,7 +188,7 @@ func (e EventType) String() string {
|
||||
func UntrustedParseEventType(et uint32) (EventType, error) {
|
||||
// "The value associated with a UEFI specific platform event type MUST be in
|
||||
// the range between 0x80000000 and 0x800000FF, inclusive."
|
||||
if (et < 0x80000000 && et > 0x800000FF) || (et < 0x0 && et > 0x12) {
|
||||
if (et < 0x80000000 && et > 0x800000FF) || (et <= 0x0 && et > 0x12) {
|
||||
return EventType(0), fmt.Errorf("event type not between [0x0, 0x12] or [0x80000000, 0x800000FF]: got %#x", et)
|
||||
}
|
||||
if _, ok := eventTypeNames[EventType(et)]; !ok {
|
||||
@ -276,10 +276,13 @@ type UEFIVariableAuthority struct {
|
||||
//
|
||||
// https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf#page=1789
|
||||
func ParseUEFIVariableAuthority(v UEFIVariableData) (UEFIVariableAuthority, error) {
|
||||
if v.Header.VariableName == shimLockGUID && (
|
||||
// Skip parsing new SBAT section logged by shim.
|
||||
// See https://github.com/rhboot/shim/blob/main/SBAT.md for more.
|
||||
if v.Header.VariableName == shimLockGUID && unicodeNameEquals(v, shimSbatVarName) {
|
||||
//https://github.com/rhboot/shim/blob/20e4d9486fcae54ee44d2323ae342ffe68c920e6/include/sbat.h#L9-L12
|
||||
unicodeNameEquals(v, shimSbatVarName) || //https://github.com/rhboot/shim/blob/20e4d9486fcae54ee44d2323ae342ffe68c920e6/include/sbat.h#L9-L12
|
||||
// Skip parsing new MokListTrusted section logged by shim.
|
||||
// See https://github.com/rhboot/shim/blob/main/MokVars.txt for more.
|
||||
unicodeNameEquals(v, shimMokListTrustedVarName)) { //https://github.com/rhboot/shim/blob/4e513405b4f1641710115780d19dcec130c5208f/mok.c#L169-L182
|
||||
return UEFIVariableAuthority{}, nil
|
||||
}
|
||||
certs, err := parseEfiSignature(v.VariableData)
|
||||
@ -444,13 +447,11 @@ func parseEfiSignature(b []byte) ([]x509.Certificate, error) {
|
||||
} else {
|
||||
// A bug in shim may cause an event to be missing the SignatureOwner GUID.
|
||||
// We handle this, but signal back to the caller using ErrSigMissingGUID.
|
||||
if _, isStructuralErr := err.(asn1.StructuralError); isStructuralErr {
|
||||
var err2 error
|
||||
cert, err2 = x509.ParseCertificate(b)
|
||||
if err2 == nil {
|
||||
certificates = append(certificates, *cert)
|
||||
err = ErrSigMissingGUID
|
||||
}
|
||||
var err2 error
|
||||
cert, err2 = x509.ParseCertificate(b)
|
||||
if err2 == nil {
|
||||
certificates = append(certificates, *cert)
|
||||
err = ErrSigMissingGUID
|
||||
}
|
||||
}
|
||||
return certificates, err
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -20,8 +20,8 @@ package attest
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/go-tpm/legacy/tpm2"
|
||||
tpm1 "github.com/google/go-tpm/tpm"
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
)
|
||||
|
||||
// windowsKey12 represents a Windows-managed key on a TPM1.2 TPM.
|
||||
@ -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)
|
||||
}
|
||||
|
@ -19,14 +19,13 @@ package attest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
|
||||
"github.com/google/go-tpm/tpmutil"
|
||||
tpmtbs "github.com/google/go-tpm/tpmutil/tbs"
|
||||
"golang.org/x/sys/windows"
|
||||
@ -370,8 +369,8 @@ func (h *winPCP) Close() error {
|
||||
return closeNCryptObject(h.hProv)
|
||||
}
|
||||
|
||||
// DeleteKey permanently removes the key with the given handle
|
||||
// from the system, and frees its handle.
|
||||
// DeleteKey permanently removes the key with the given handle from the system,
|
||||
// and frees its handle.
|
||||
func (h *winPCP) DeleteKey(kh uintptr) error {
|
||||
r, _, msg := nCryptDeleteKey.Call(kh, 0)
|
||||
if r != 0 {
|
||||
|
@ -16,10 +16,10 @@ package attest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
"github.com/google/go-attestation/attest/internal"
|
||||
)
|
||||
|
||||
|
@ -17,12 +17,12 @@ package attest
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSecureBoot(t *testing.T) {
|
||||
data, err := ioutil.ReadFile("testdata/windows_gcp_shielded_vm.json")
|
||||
data, err := os.ReadFile("testdata/windows_gcp_shielded_vm.json")
|
||||
if err != nil {
|
||||
t.Fatalf("reading test data: %v", err)
|
||||
}
|
||||
@ -52,7 +52,7 @@ func TestSecureBoot(t *testing.T) {
|
||||
|
||||
// See: https://github.com/google/go-attestation/issues/157
|
||||
func TestSecureBootBug157(t *testing.T) {
|
||||
raw, err := ioutil.ReadFile("testdata/sb_cert_eventlog")
|
||||
raw, err := os.ReadFile("testdata/sb_cert_eventlog")
|
||||
if err != nil {
|
||||
t.Fatalf("reading test data: %v", err)
|
||||
}
|
||||
@ -114,12 +114,12 @@ func TestSecureBootBug157(t *testing.T) {
|
||||
|
||||
events, err := elr.Verify(pcrs)
|
||||
if err != nil {
|
||||
t.Errorf("failed to verify log: %v", err)
|
||||
t.Fatalf("failed to verify log: %v", err)
|
||||
}
|
||||
|
||||
sbs, err := ParseSecurebootState(events)
|
||||
if err != nil {
|
||||
t.Errorf("failed parsing secureboot state: %v", err)
|
||||
t.Fatalf("failed parsing secureboot state: %v", err)
|
||||
}
|
||||
if got, want := len(sbs.PostSeparatorAuthority), 3; got != want {
|
||||
t.Errorf("len(sbs.PostSeparatorAuthority) = %d, want %d", got, want)
|
||||
@ -135,7 +135,7 @@ func b64MustDecode(input string) []byte {
|
||||
}
|
||||
|
||||
func TestSecureBootOptionRom(t *testing.T) {
|
||||
raw, err := ioutil.ReadFile("testdata/option_rom_eventlog")
|
||||
raw, err := os.ReadFile("testdata/option_rom_eventlog")
|
||||
if err != nil {
|
||||
t.Fatalf("reading test data: %v", err)
|
||||
}
|
||||
@ -177,7 +177,26 @@ func TestSecureBootOptionRom(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSecureBootEventLogUbuntu(t *testing.T) {
|
||||
data, err := ioutil.ReadFile("testdata/ubuntu_2104_shielded_vm_no_secure_boot_eventlog")
|
||||
data, err := os.ReadFile("testdata/ubuntu_2104_shielded_vm_no_secure_boot_eventlog")
|
||||
if err != nil {
|
||||
t.Fatalf("reading test data: %v", err)
|
||||
}
|
||||
el, err := ParseEventLog(data)
|
||||
if err != nil {
|
||||
t.Fatalf("parsing event log: %v", err)
|
||||
}
|
||||
evts := el.Events(HashSHA256)
|
||||
if err != nil {
|
||||
t.Fatalf("verifying event log: %v", err)
|
||||
}
|
||||
_, err = ParseSecurebootState(evts)
|
||||
if err != nil {
|
||||
t.Errorf("parsing sb state: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecureBootEventLogFedora36(t *testing.T) {
|
||||
data, err := os.ReadFile("testdata/coreos_36_shielded_vm_no_secure_boot_eventlog")
|
||||
if err != nil {
|
||||
t.Fatalf("reading test data: %v", err)
|
||||
}
|
||||
|
BIN
attest/testdata/coreos_36_shielded_vm_no_secure_boot_eventlog
vendored
Normal file
BIN
attest/testdata/coreos_36_shielded_vm_no_secure_boot_eventlog
vendored
Normal file
Binary file not shown.
193
attest/tpm.go
193
attest/tpm.go
@ -18,17 +18,18 @@ import (
|
||||
"bytes"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/google/certificate-transparency-go/asn1"
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
"github.com/google/go-tpm/legacy/tpm2"
|
||||
"github.com/google/go-tpm/tpmutil"
|
||||
"go.uber.org/multierr"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -37,19 +38,21 @@ const (
|
||||
tpmPtFwVersion1 = 0x00000100 + 11 // PT_FIXED + offset of 11
|
||||
|
||||
// Defined in "Registry of reserved TPM 2.0 handles and localities".
|
||||
nvramCertIndex = 0x1c00002
|
||||
nvramEkNonceIndex = 0x1c00003
|
||||
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
|
||||
commonEkEquivalentHandle = 0x81010001
|
||||
commonRSAEkEquivalentHandle = 0x81010001
|
||||
commonECCEkEquivalentHandle = 0x81010002
|
||||
)
|
||||
|
||||
var (
|
||||
akTemplate = tpm2.Public{
|
||||
akTemplateRSA = tpm2.Public{
|
||||
Type: tpm2.AlgRSA,
|
||||
NameAlg: tpm2.AlgSHA256,
|
||||
Attributes: tpm2.FlagSignerDefault,
|
||||
Attributes: tpm2.FlagSignerDefault | tpm2.FlagNoDA,
|
||||
RSAParameters: &tpm2.RSAParams{
|
||||
Sign: &tpm2.SigScheme{
|
||||
Alg: tpm2.AlgRSASSA,
|
||||
@ -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,9 +91,26 @@ var (
|
||||
KeyBits: 2048,
|
||||
},
|
||||
}
|
||||
// Default 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
|
||||
defaultEKTemplate = tpm2.Public{
|
||||
defaultRSAEKTemplate = tpm2.Public{
|
||||
Type: tpm2.AlgRSA,
|
||||
NameAlg: tpm2.AlgSHA256,
|
||||
Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin |
|
||||
@ -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{
|
||||
@ -199,12 +261,12 @@ func ParseEKCertificate(ekCert []byte) (*x509.Certificate, error) {
|
||||
var cert struct {
|
||||
Raw asn1.RawContent
|
||||
}
|
||||
if _, err := asn1.UnmarshalWithParams(ekCert, &cert, "lax"); err != nil && x509.IsFatal(err) {
|
||||
if _, err := asn1.UnmarshalWithParams(ekCert, &cert, "lax"); err != nil {
|
||||
return nil, fmt.Errorf("asn1.Unmarshal() failed: %v, wasWrapped=%v", err, wasWrapped)
|
||||
}
|
||||
|
||||
c, err := x509.ParseCertificate(cert.Raw)
|
||||
if err != nil && x509.IsFatal(err) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("x509.ParseCertificate() failed: %v", err)
|
||||
}
|
||||
return c, nil
|
||||
@ -220,23 +282,23 @@ func intelEKURL(ekPub *rsa.PublicKey) string {
|
||||
pubHash.Write(ekPub.N.Bytes())
|
||||
pubHash.Write([]byte{0x1, 0x00, 0x01})
|
||||
|
||||
return intelEKCertServiceURL + base64.URLEncoding.EncodeToString(pubHash.Sum(nil))
|
||||
return intelEKCertServiceURL + url.QueryEscape(base64.URLEncoding.EncodeToString(pubHash.Sum(nil)))
|
||||
}
|
||||
|
||||
func readEKCertFromNVRAM20(tpm io.ReadWriter) (*x509.Certificate, error) {
|
||||
ekCert, err := tpm2.NVReadEx(tpm, nvramCertIndex, tpm2.HandleOwner, "", 0)
|
||||
func readEKCertFromNVRAM20(tpm io.ReadWriter, nvramCertIndex tpmutil.Handle) (*x509.Certificate, error) {
|
||||
// By passing nvramCertIndex as our auth handle we're using the NV index
|
||||
// itself as the auth hierarchy, which is the same approach
|
||||
// tpm2_getekcertificate takes.
|
||||
ekCert, err := tpm2.NVReadEx(tpm, nvramCertIndex, nvramCertIndex, "", 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading EK cert: %v", err)
|
||||
}
|
||||
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 {
|
||||
@ -251,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{}
|
||||
@ -294,17 +381,22 @@ 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)
|
||||
}
|
||||
|
||||
//TPM interfaces with a TPM device on the system.
|
||||
// TPM interfaces with a TPM device on the system.
|
||||
type TPM struct {
|
||||
// tpm refers to a concrete implementation of TPM logic, based on the current
|
||||
// platform and TPM version.
|
||||
@ -317,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()
|
||||
@ -328,12 +430,18 @@ func (t *TPM) Info() (*TPMInfo, error) {
|
||||
|
||||
// LoadAK loads a previously-created ak into the TPM for use.
|
||||
// A key loaded via this function needs to be closed with .Close().
|
||||
// Only blobs generated by calling AK.Serialize() are valid parameters
|
||||
// Only blobs generated by calling AK.Marshal() are valid parameters
|
||||
// to this function.
|
||||
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
|
||||
@ -369,9 +477,25 @@ 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.Serialize() are valid parameters
|
||||
// Only blobs generated by calling Key.Marshal() are valid parameters
|
||||
// to this function.
|
||||
func (t *TPM) LoadKey(opaqueBlob []byte) (*Key, error) {
|
||||
return t.tpm.loadKey(opaqueBlob)
|
||||
@ -386,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)
|
||||
@ -416,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
|
||||
|
@ -19,11 +19,11 @@ package attest
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
"github.com/google/go-tspi/attestation"
|
||||
"github.com/google/go-tspi/tspi"
|
||||
"github.com/google/go-tspi/tspiconst"
|
||||
@ -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)
|
||||
@ -169,5 +189,5 @@ func (t *trousersTPM) pcrs(alg HashAlg) ([]PCR, error) {
|
||||
}
|
||||
|
||||
func (t *trousersTPM) measurementLog() ([]byte, error) {
|
||||
return ioutil.ReadFile("/sys/kernel/security/tpm0/binary_bios_measurements")
|
||||
return os.ReadFile("/sys/kernel/security/tpm0/binary_bios_measurements")
|
||||
}
|
||||
|
@ -21,12 +21,11 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
"github.com/google/go-tpm/legacy/tpm2"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -49,7 +48,7 @@ func InjectSimulatedTPMForTest(rwc io.ReadWriteCloser) *TPM {
|
||||
func probeSystemTPMs() ([]probedTPM, error) {
|
||||
var tpms []probedTPM
|
||||
|
||||
tpmDevs, err := ioutil.ReadDir(tpmRoot)
|
||||
tpmDevs, err := os.ReadDir(tpmRoot)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
@ -82,7 +81,7 @@ type linuxCmdChannel struct {
|
||||
|
||||
// MeasurementLog implements CommandChannelTPM20.
|
||||
func (cc *linuxCmdChannel) MeasurementLog() ([]byte, error) {
|
||||
return ioutil.ReadFile("/sys/kernel/security/tpm0/binary_bios_measurements")
|
||||
return os.ReadFile("/sys/kernel/security/tpm0/binary_bios_measurements")
|
||||
}
|
||||
|
||||
func openTPM(tpm probedTPM) (*TPM, error) {
|
||||
@ -98,7 +97,7 @@ func openTPM(tpm probedTPM) (*TPM, error) {
|
||||
// If the TPM has a kernel-provided resource manager, we should
|
||||
// use that instead of communicating directly.
|
||||
devPath := path.Join("/dev", path.Base(tpm.Path))
|
||||
f, err := ioutil.ReadDir(path.Join(tpm.Path, "device", "tpmrm"))
|
||||
f, err := os.ReadDir(path.Join(tpm.Path, "device", "tpmrm"))
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
|
@ -2,25 +2,28 @@ package attest
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
)
|
||||
|
||||
// Generated using the following command:
|
||||
// Created by downloading the base64-url encoded PEM data from
|
||||
// https://ekop.intel.com/ekcertservice/WVEG2rRwkQ7m3RpXlUphgo6Y2HLxl18h6ZZkkOAdnBE%3D,
|
||||
// extracting its public key, and formatting it to PEM using
|
||||
//
|
||||
// openssl genrsa 2048|openssl rsa -outform PEM -pubout
|
||||
// openssl x509 -in ekcert.pem -pubkey
|
||||
//
|
||||
// This is the public key from the EK cert that's used for testing tpm2-tools:
|
||||
// https://github.com/tpm2-software/tpm2-tools/blob/master/test/integration/tests/getekcertificate.sh
|
||||
var testRSAKey = mustParseRSAKey(`-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq8zyTXCjVALZzjS8wgNH
|
||||
nAVdt4ZGM3N450xOnLplx/RbCVwXyu83SWh0B3Ka+92aocqcHzo+j6e6Urppre/I
|
||||
+7VVKTdUAr8t5gxgSLGvo+ev+zv70GF4DmJthb8JNheHCmk3RnoSFs5TnDuSdvGb
|
||||
KcSzas0186LQyxvwfFjTxLweGrZKh/CTewD0/f5ozXmbTtJpl+qYrMi9GJamGlg6
|
||||
N6EsWKh1xos8J/cEmS2vbyCGGADyBwRV8Zkto5EU1HJaEli10HVZf0D06vuKzzxM
|
||||
+6W7LzGqzAPeaWvHi07ezShqdr5q5y1KKhFJcy8HOpwN8iFfIw70y3FtMlrMprrU
|
||||
twIDAQAB
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwyDi8kSoYBqs8+AdJsZl
|
||||
JJk1Vi3h2hl+nn8HbEaWE8+2U+mOwsOG/B0TPyyMbMM4tzLwsgi9g4qHej5bvD4d
|
||||
QIToNcfIkGocBbTS0w/b68HbrZUPprFlvUtqhkYDFGFkwMT1nUiQEe8fko3upukA
|
||||
YfPTdeVkYnMVHvYiJSCYvhpKsB3AoSInxgn9rOsRWvQI1Gk6b0mRl3RpWwwSvBih
|
||||
/3EgpzN7L7XxlR2Lt/CU1bVUwRyVI7MHKf5keH0KE7nmMEiNq039hmNKUnDscvzF
|
||||
pE3GeajzKTjdgZfina6Dn1tMoPXeJ8lSLCPFThws5XhZUlEYvURwsYGA7veK5CZ7
|
||||
zQIDAQAB
|
||||
-----END PUBLIC KEY-----`)
|
||||
|
||||
func mustParseRSAKey(data string) *rsa.PublicKey {
|
||||
@ -47,7 +50,7 @@ func parseRSAKey(data string) (*rsa.PublicKey, error) {
|
||||
}
|
||||
|
||||
func TestIntelEKURL(t *testing.T) {
|
||||
want := "https://ekop.intel.com/ekcertservice/7YtWV2nT3LpvSCfJt7ENIznN1R1jYkj_3S6mez3yyzg="
|
||||
want := "https://ekop.intel.com/ekcertservice/WVEG2rRwkQ7m3RpXlUphgo6Y2HLxl18h6ZZkkOAdnBE%3D"
|
||||
got := intelEKURL(testRSAKey)
|
||||
if got != want {
|
||||
t.Fatalf("intelEKURL(), got=%q, want=%q", got, want)
|
||||
|
@ -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
|
||||
|
||||
|
@ -164,7 +164,7 @@ type WinEvents struct {
|
||||
// BootCount contains the value of the monotonic boot counter. This
|
||||
// value is not set for TPM 1.2 devices and some TPMs with buggy
|
||||
// implementations of monotonic counters.
|
||||
BootCount int
|
||||
BootCount uint64
|
||||
// LoadedModules contains authenticode hashes for binaries which
|
||||
// were loaded during boot.
|
||||
LoadedModules map[string]WinModuleLoad
|
||||
@ -272,7 +272,7 @@ func ParseWinEvents(events []Event) (*WinEvents, error) {
|
||||
return nil, fmt.Errorf("invalid tagged event structure at event %d: %w", e.sequence, err)
|
||||
}
|
||||
if digestVerify != nil {
|
||||
return nil, fmt.Errorf("invalid digest for tagged event %d: %w", e.sequence, err)
|
||||
return nil, fmt.Errorf("invalid digest for tagged event %d: %w", e.sequence, digestVerify)
|
||||
}
|
||||
if err := out.readWinEventBlock(s, e.Index); err != nil {
|
||||
return nil, fmt.Errorf("invalid SIPA events in event %d: %w", e.sequence, err)
|
||||
@ -303,7 +303,7 @@ func ParseWinEvents(events []Event) (*WinEvents, error) {
|
||||
return nil, fmt.Errorf("invalid tagged event structure at event %d: %w", e.sequence, err)
|
||||
}
|
||||
if digestVerify != nil {
|
||||
return nil, fmt.Errorf("invalid digest for tagged event %d: %w", e.sequence, err)
|
||||
return nil, fmt.Errorf("invalid digest for tagged event %d: %w", e.sequence, digestVerify)
|
||||
}
|
||||
if err := out.readWinEventBlock(s, e.Index); err != nil {
|
||||
return nil, fmt.Errorf("invalid SIPA events in event %d: %w", e.sequence, err)
|
||||
@ -394,38 +394,49 @@ func (w *WinEvents) readBooleanByteEvent(header microsoftEventHeader, r *bytes.R
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WinEvents) readUint(header microsoftEventHeader, r io.Reader) (uint64, error) {
|
||||
if header.Size > 8 {
|
||||
return 0, fmt.Errorf("integer too large (%d bytes)", header.Size)
|
||||
func (w *WinEvents) readUint32(header microsoftEventHeader, r io.Reader) (uint32, error) {
|
||||
if header.Size != 4 {
|
||||
return 0, fmt.Errorf("integer size not uint32 (%d bytes)", header.Size)
|
||||
}
|
||||
|
||||
data := make([]uint8, header.Size)
|
||||
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
|
||||
return 0, fmt.Errorf("reading u%d: %w", header.Size<<8, err)
|
||||
return 0, fmt.Errorf("reading u32: %w", err)
|
||||
}
|
||||
i, n := binary.Uvarint(data)
|
||||
if n <= 0 {
|
||||
return 0, fmt.Errorf("reading u%d: invalid varint", header.Size<<8)
|
||||
i := binary.LittleEndian.Uint32(data)
|
||||
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (w *WinEvents) readUint64(header microsoftEventHeader, r io.Reader) (uint64, error) {
|
||||
if header.Size != 8 {
|
||||
return 0, fmt.Errorf("integer size not uint64 (%d bytes)", header.Size)
|
||||
}
|
||||
|
||||
data := make([]uint8, header.Size)
|
||||
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
|
||||
return 0, fmt.Errorf("reading u64: %w", err)
|
||||
}
|
||||
i := binary.LittleEndian.Uint64(data)
|
||||
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (w *WinEvents) readBootCounter(header microsoftEventHeader, r *bytes.Reader) error {
|
||||
i, err := w.readUint(header, r)
|
||||
i, err := w.readUint64(header, r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("boot counter: %v", err)
|
||||
}
|
||||
|
||||
if w.BootCount > 0 && w.BootCount != int(i) {
|
||||
if w.BootCount > 0 && w.BootCount != i {
|
||||
return fmt.Errorf("conflicting values for boot counter: %d != %d", i, w.BootCount)
|
||||
}
|
||||
w.BootCount = int(i)
|
||||
w.BootCount = i
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WinEvents) readTransferControl(header microsoftEventHeader, r *bytes.Reader) error {
|
||||
i, err := w.readUint(header, r)
|
||||
i, err := w.readUint32(header, r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("transfer control: %v", err)
|
||||
}
|
||||
@ -473,7 +484,7 @@ func (w *WinEvents) parseImageValidated(header microsoftEventHeader, r io.Reader
|
||||
}
|
||||
|
||||
func (w *WinEvents) parseHashAlgID(header microsoftEventHeader, r io.Reader) (WinCSPAlg, error) {
|
||||
i, err := w.readUint(header, r)
|
||||
i, err := w.readUint32(header, r)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("hash algorithm ID: %v", err)
|
||||
}
|
||||
@ -578,7 +589,7 @@ func (w *WinEvents) readLoadedModuleAggregation(rdr *bytes.Reader, header micros
|
||||
if imgSize != 0 {
|
||||
return errors.New("duplicate image size in LMA event")
|
||||
}
|
||||
if imgSize, err = w.readUint(h, r); err != nil {
|
||||
if imgSize, err = w.readUint64(h, r); err != nil {
|
||||
return err
|
||||
}
|
||||
case hashAlgorithmID:
|
||||
@ -589,7 +600,7 @@ func (w *WinEvents) readLoadedModuleAggregation(rdr *bytes.Reader, header micros
|
||||
return err
|
||||
}
|
||||
case imageValidated:
|
||||
if imgValidated == true {
|
||||
if imgValidated {
|
||||
return errors.New("duplicate image validated field in LMA event")
|
||||
}
|
||||
if imgValidated, err = w.parseImageValidated(h, r); err != nil {
|
||||
@ -667,7 +678,7 @@ func (w *WinEvents) parseUTF16(header microsoftEventHeader, r io.Reader) (string
|
||||
return strings.TrimSuffix(string(utf16.Decode(data)), "\x00"), nil
|
||||
}
|
||||
|
||||
func (w *WinEvents) readELAMAggregation(rdr *bytes.Reader, header microsoftEventHeader) error {
|
||||
func (w *WinEvents) readELAMAggregation(rdr io.Reader, header microsoftEventHeader) error {
|
||||
var (
|
||||
r = &io.LimitedReader{R: rdr, N: int64(header.Size)}
|
||||
driverName string
|
||||
@ -687,6 +698,11 @@ func (w *WinEvents) readELAMAggregation(rdr *bytes.Reader, header microsoftEvent
|
||||
|
||||
var err error
|
||||
switch h.Type {
|
||||
case elamAggregation:
|
||||
w.readELAMAggregation(r, h)
|
||||
if r.N == 0 {
|
||||
return nil
|
||||
}
|
||||
case elamKeyname:
|
||||
if driverName != "" {
|
||||
return errors.New("duplicate driver name in ELAM aggregation event")
|
||||
|
@ -16,7 +16,7 @@ package attest
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@ -31,9 +31,10 @@ func TestParseWinEvents(t *testing.T) {
|
||||
CodeIntegrityEnabled: TernaryTrue,
|
||||
BitlockerUnlocks: []BitlockerStatus{0, 0},
|
||||
LoadedModules: map[string]WinModuleLoad{
|
||||
"0fdce7d71936f79445e7d2c84cbeb97c948d3730e0b839166b0a4e625c2d4547": WinModuleLoad{
|
||||
"0fdce7d71936f79445e7d2c84cbeb97c948d3730e0b839166b0a4e625c2d4547": {
|
||||
FilePath: `\Windows\System32\drivers\vioscsi.sys`,
|
||||
ImageBase: []uint64{81416192},
|
||||
ImageSize: uint64(86016),
|
||||
HashAlgorithm: WinAlgSHA256,
|
||||
ImageValidated: true,
|
||||
AuthorityIssuer: "Microsoft Windows Third Party Component CA 2014",
|
||||
@ -48,9 +49,10 @@ func TestParseWinEvents(t *testing.T) {
|
||||
},
|
||||
AuthenticodeHash: []byte{15, 220, 231, 215, 25, 54, 247, 148, 69, 231, 210, 200, 76, 190, 185, 124, 148, 141, 55, 48, 224, 184, 57, 22, 107, 10, 78, 98, 92, 45, 69, 71},
|
||||
},
|
||||
"055a36a9921b98cc04042ca95249c7eca655536868dafcec7508947ebe5e71f4": WinModuleLoad{
|
||||
"055a36a9921b98cc04042ca95249c7eca655536868dafcec7508947ebe5e71f4": {
|
||||
FilePath: `\Windows\System32\Drivers\ksecpkg.sys`,
|
||||
ImageBase: []uint64{82952192},
|
||||
ImageSize: uint64(204800),
|
||||
HashAlgorithm: WinAlgSHA256,
|
||||
ImageValidated: true,
|
||||
AuthorityIssuer: "Microsoft Windows Production PCA 2011",
|
||||
@ -65,9 +67,10 @@ func TestParseWinEvents(t *testing.T) {
|
||||
},
|
||||
AuthenticodeHash: []byte{5, 90, 54, 169, 146, 27, 152, 204, 4, 4, 44, 169, 82, 73, 199, 236, 166, 85, 83, 104, 104, 218, 252, 236, 117, 8, 148, 126, 190, 94, 113, 244},
|
||||
},
|
||||
"2bedd1589410b6fa13c82f35db735025b6a160595922750248771f5abd0fee58": WinModuleLoad{
|
||||
"2bedd1589410b6fa13c82f35db735025b6a160595922750248771f5abd0fee58": {
|
||||
FilePath: `\Windows\System32\drivers\volmgrx.sys`,
|
||||
ImageBase: []uint64{80875520},
|
||||
ImageSize: uint64(405504),
|
||||
HashAlgorithm: WinAlgSHA256,
|
||||
ImageValidated: true,
|
||||
AuthorityIssuer: "Microsoft Windows Production PCA 2011",
|
||||
@ -84,11 +87,11 @@ func TestParseWinEvents(t *testing.T) {
|
||||
},
|
||||
},
|
||||
ELAM: map[string]WinELAM{
|
||||
"Windows Defender": WinELAM{Measured: []byte{0x06, 0x7d, 0x5b, 0x9d, 0xc5, 0x62, 0x7f, 0x97, 0xdc, 0xf3, 0xfe, 0xff, 0x60, 0x2a, 0x34, 0x2e, 0xd6, 0x98, 0xd2, 0xcc}},
|
||||
"Windows Defender": {Measured: []byte{0x06, 0x7d, 0x5b, 0x9d, 0xc5, 0x62, 0x7f, 0x97, 0xdc, 0xf3, 0xfe, 0xff, 0x60, 0x2a, 0x34, 0x2e, 0xd6, 0x98, 0xd2, 0xcc}},
|
||||
},
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile("testdata/windows_gcp_shielded_vm.json")
|
||||
data, err := os.ReadFile("testdata/windows_gcp_shielded_vm.json")
|
||||
if err != nil {
|
||||
t.Fatalf("reading test data: %v", err)
|
||||
}
|
||||
@ -118,7 +121,7 @@ func TestParseWinEvents(t *testing.T) {
|
||||
"055a36a9921b98cc04042ca95249c7eca655536868dafcec7508947ebe5e71f4": true,
|
||||
"2bedd1589410b6fa13c82f35db735025b6a160595922750248771f5abd0fee58": true,
|
||||
}
|
||||
for k, _ := range winState.LoadedModules {
|
||||
for k := range winState.LoadedModules {
|
||||
if _, keep := keep[k]; !keep {
|
||||
delete(winState.LoadedModules, k)
|
||||
}
|
||||
|
@ -18,42 +18,65 @@ import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/google/certificate-transparency-go/asn1"
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
"github.com/google/go-tpm/legacy/tpm2"
|
||||
"github.com/google/go-tpm/tpmutil"
|
||||
)
|
||||
|
||||
// wrappedTPM20 interfaces with a TPM 2.0 command channel.
|
||||
type wrappedTPM20 struct {
|
||||
interf TPMInterface
|
||||
rwc CommandChannelTPM20
|
||||
tpmEkTemplate *tpm2.Public
|
||||
interf TPMInterface
|
||||
rwc CommandChannelTPM20
|
||||
tpmRSAEkTemplate *tpm2.Public
|
||||
tpmECCEkTemplate *tpm2.Public
|
||||
}
|
||||
|
||||
func (t *wrappedTPM20) ekTemplate() (tpm2.Public, error) {
|
||||
if t.tpmEkTemplate != nil {
|
||||
return *t.tpmEkTemplate, nil
|
||||
// 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 {
|
||||
if t.tpmRSAEkTemplate != nil {
|
||||
return *t.tpmRSAEkTemplate
|
||||
}
|
||||
|
||||
nonce, err := tpm2.NVReadEx(t.rwc, nvramEkNonceIndex, tpm2.HandleOwner, "", 0)
|
||||
nonce, err := tpm2.NVReadEx(t.rwc, nvramRSAEkNonceIndex, tpm2.HandleOwner, "", 0)
|
||||
if err != nil {
|
||||
t.tpmEkTemplate = &defaultEKTemplate // No nonce, use the default template
|
||||
t.tpmRSAEkTemplate = &defaultRSAEKTemplate // No nonce, use the default template
|
||||
} else {
|
||||
template := defaultEKTemplate
|
||||
template := defaultRSAEKTemplate
|
||||
copy(template.RSAParameters.ModulusRaw, nonce)
|
||||
t.tpmEkTemplate = &template
|
||||
t.tpmRSAEkTemplate = &template
|
||||
}
|
||||
|
||||
return *t.tpmEkTemplate, nil
|
||||
return *t.tpmRSAEkTemplate
|
||||
}
|
||||
|
||||
func (*wrappedTPM20) isTPMBase() {}
|
||||
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
|
||||
@ -84,52 +107,105 @@ 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)
|
||||
if err == nil {
|
||||
// Found the persistent handle, assume it's the key we want.
|
||||
return pHnd, false, nil
|
||||
// Return value: handle, whether we generated a new one, error.
|
||||
func (t *wrappedTPM20) getEndorsementKeyHandle(ek *EK) (tpmutil.Handle, bool, error) {
|
||||
var ekHandle tpmutil.Handle
|
||||
var ekTemplate tpm2.Public
|
||||
|
||||
if ek == nil {
|
||||
// The default is RSA for backward compatibility.
|
||||
ekHandle = commonRSAEkEquivalentHandle
|
||||
ekTemplate = t.rsaEkTemplate()
|
||||
} else {
|
||||
ekHandle = ek.handle
|
||||
if ekHandle == 0 {
|
||||
// Assume RSA EK handle if it was not provided.
|
||||
ekHandle = commonRSAEkEquivalentHandle
|
||||
}
|
||||
switch pub := ek.Public.(type) {
|
||||
case *rsa.PublicKey:
|
||||
ekTemplate = t.rsaEkTemplate()
|
||||
case *ecdsa.PublicKey:
|
||||
ekTemplate = t.eccEkTemplate()
|
||||
default:
|
||||
return 0, false, fmt.Errorf("unsupported public key type %T", pub)
|
||||
}
|
||||
}
|
||||
|
||||
var keyHnd tpmutil.Handle
|
||||
switch pHnd {
|
||||
case commonSrkEquivalentHandle:
|
||||
keyHnd, _, err = tpm2.CreatePrimary(t.rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", defaultSRKTemplate)
|
||||
case commonEkEquivalentHandle:
|
||||
var tmpl tpm2.Public
|
||||
if tmpl, err = t.ekTemplate(); err != nil {
|
||||
return 0, false, fmt.Errorf("ek template: %v", err)
|
||||
}
|
||||
keyHnd, _, err = tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", tmpl)
|
||||
_, _, _, err := tpm2.ReadPublic(t.rwc, ekHandle)
|
||||
if err == nil {
|
||||
// Found the persistent handle, assume it's the key we want.
|
||||
return ekHandle, false, nil
|
||||
}
|
||||
rerr := err // Preserve this failure for later logging, if needed
|
||||
|
||||
keyHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", ekTemplate)
|
||||
if err != nil {
|
||||
return 0, false, fmt.Errorf("CreatePrimary failed: %v", err)
|
||||
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); err == nil {
|
||||
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
|
||||
}
|
||||
|
||||
// Attempt to create an EK.
|
||||
tmpl, err := t.ekTemplate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ek template: %v", err)
|
||||
}
|
||||
|
||||
ekHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", tmpl)
|
||||
ekHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", t.rsaEkTemplate())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("EK CreatePrimary failed: %v", err)
|
||||
}
|
||||
@ -142,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)
|
||||
@ -175,16 +278,11 @@ 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{tpm2.AlgRSASSA, tpm2.AlgSHA256, 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)
|
||||
}
|
||||
// Pack the raw structure into a TPMU_SIGNATURE.
|
||||
signature, err := tpmutil.Pack(tpm2.AlgRSASSA, tpm2.AlgSHA256, tpmutil.U16Bytes(sig))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to pack TPMT_SIGNATURE: %v", err)
|
||||
}
|
||||
return &AK{ak: newWrappedAK20(keyHandle, blob, pub, creationData, attestation, signature)}, nil
|
||||
return &AK{ak: newWrappedAK20(keyHandle, blob, pub, creationData, attestation, sig)}, nil
|
||||
}
|
||||
|
||||
func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) {
|
||||
@ -193,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)
|
||||
@ -210,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)
|
||||
@ -231,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)
|
||||
}
|
||||
@ -296,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)
|
||||
@ -305,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)
|
||||
}
|
||||
@ -317,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)
|
||||
}
|
||||
@ -325,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)
|
||||
}
|
||||
@ -340,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 {
|
||||
@ -416,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)
|
||||
@ -431,7 +557,7 @@ func (k *wrappedKey20) activateCredential(tb tpmBase, in EncryptedCredential) ([
|
||||
}
|
||||
secret := in.Secret[2:]
|
||||
|
||||
ekHnd, _, err := t.getPrimaryKeyHandle(commonEkEquivalentHandle)
|
||||
ekHnd, _, err := t.getEndorsementKeyHandle(ek)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -450,7 +576,7 @@ func (k *wrappedKey20) activateCredential(tb tpmBase, in EncryptedCredential) ([
|
||||
}
|
||||
defer tpm2.FlushContext(t.rwc, sessHandle)
|
||||
|
||||
if _, err := tpm2.PolicySecret(t.rwc, tpm2.HandleEndorsement, tpm2.AuthCommand{Session: tpm2.HandlePasswordSession, Attributes: tpm2.AttrContinueSession}, sessHandle, nil, nil, nil, 0); err != nil {
|
||||
if _, _, err := tpm2.PolicySecret(t.rwc, tpm2.HandleEndorsement, tpm2.AuthCommand{Session: tpm2.HandlePasswordSession, Attributes: tpm2.AttrContinueSession}, sessHandle, nil, nil, nil, 0); err != nil {
|
||||
return nil, fmt.Errorf("tpm2.PolicySecret() failed: %v", err)
|
||||
}
|
||||
|
||||
@ -460,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)
|
||||
@ -469,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 {
|
||||
@ -506,16 +661,31 @@ func (k *wrappedKey20) sign(tb tpmBase, digest []byte, pub crypto.PublicKey, opt
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb)
|
||||
}
|
||||
switch pub.(type) {
|
||||
switch p := pub.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
return signECDSA(t.rwc, k.hnd, digest)
|
||||
return signECDSA(t.rwc, k.hnd, digest, p.Curve)
|
||||
case *rsa.PublicKey:
|
||||
return signRSA(t.rwc, k.hnd, digest, opts)
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported signing key type: %T", pub)
|
||||
}
|
||||
|
||||
func signECDSA(rw io.ReadWriter, key tpmutil.Handle, digest []byte) ([]byte, error) {
|
||||
func signECDSA(rw io.ReadWriter, key tpmutil.Handle, digest []byte, curve elliptic.Curve) ([]byte, error) {
|
||||
// https://cs.opensource.google/go/go/+/refs/tags/go1.19.2:src/crypto/ecdsa/ecdsa.go;l=181
|
||||
orderBits := curve.Params().N.BitLen()
|
||||
orderBytes := (orderBits + 7) / 8
|
||||
if len(digest) > orderBytes {
|
||||
digest = digest[:orderBytes]
|
||||
}
|
||||
ret := new(big.Int).SetBytes(digest)
|
||||
excess := len(digest)*8 - orderBits
|
||||
if excess > 0 {
|
||||
ret.Rsh(ret, uint(excess))
|
||||
}
|
||||
// call ret.FillBytes() here instead of ret.Bytes() to preserve leading zeroes
|
||||
// that may have been dropped when converting the digest to an integer
|
||||
digest = ret.FillBytes(digest)
|
||||
|
||||
sig, err := tpm2.Sign(rw, key, "", digest, nil, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot sign: %v", err)
|
||||
@ -564,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)
|
||||
}
|
||||
}
|
||||
|
@ -9,15 +9,15 @@ package attributecert
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-attestation/oid"
|
||||
"github.com/google/certificate-transparency-go/asn1"
|
||||
"github.com/google/certificate-transparency-go/x509/pkix"
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -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},
|
||||
@ -129,50 +131,50 @@ func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgo
|
||||
return x509.UnknownSignatureAlgorithm
|
||||
}
|
||||
|
||||
//RFC 5280 4.2.2.1
|
||||
// RFC 5280 4.2.2.1
|
||||
type authorityInfoAccess struct {
|
||||
Method asn1.ObjectIdentifier
|
||||
Location asn1.RawValue
|
||||
}
|
||||
|
||||
//RFC 5280 4.2.1.1
|
||||
// RFC 5280 4.2.1.1
|
||||
type authKeyID struct {
|
||||
ID []byte `asn1:"optional,tag:0"`
|
||||
IssuerName asn1.RawValue `asn1:"set,optional,tag:1"`
|
||||
SerialNumber *big.Int `asn1:"optional,tag:2"`
|
||||
}
|
||||
|
||||
//RFC 5280 4.2.1.4
|
||||
// RFC 5280 4.2.1.4
|
||||
type cpsPolicy struct {
|
||||
ID asn1.ObjectIdentifier
|
||||
Value string
|
||||
}
|
||||
|
||||
//RFC 5280 4.2.1.4
|
||||
// RFC 5280 4.2.1.4
|
||||
type policyInformation struct {
|
||||
Raw asn1.RawContent
|
||||
ID asn1.ObjectIdentifier
|
||||
Policy asn1.RawValue
|
||||
}
|
||||
|
||||
//RFC 5280 4.1.2.5
|
||||
// RFC 5280 4.1.2.5
|
||||
type validity struct {
|
||||
NotBefore, NotAfter time.Time
|
||||
}
|
||||
|
||||
//RFC 5280 4.2.1.4
|
||||
type NoticeReference struct {
|
||||
// RFC 5280 4.2.1.4
|
||||
type noticeReference struct {
|
||||
Organization string
|
||||
NoticeNumbers []int
|
||||
}
|
||||
|
||||
//RFC 5280 4.2.1.4
|
||||
// RFC 5280 4.2.1.4
|
||||
type userNotice struct {
|
||||
NoticeRef NoticeReference `asn1:"optional"`
|
||||
NoticeRef noticeReference `asn1:"optional"`
|
||||
ExplicitText string `asn1:"optional"`
|
||||
}
|
||||
|
||||
//RFC 5755 4.1
|
||||
// RFC 5755 4.1
|
||||
type objectDigestInfo struct {
|
||||
DigestedObjectType asn1.Enumerated
|
||||
OtherObjectTypeID asn1.ObjectIdentifier
|
||||
@ -180,14 +182,14 @@ type objectDigestInfo struct {
|
||||
ObjectDigest asn1.BitString
|
||||
}
|
||||
|
||||
//RFC 5755 4.1
|
||||
// RFC 5755 4.1
|
||||
type attCertIssuer struct {
|
||||
IssuerName asn1.RawValue `asn1:"set,optional"`
|
||||
BaseCertificateID issuerSerial `asn1:"optional,tag:0"`
|
||||
ObjectDigestInfo objectDigestInfo `asn1:"optional,tag:1"`
|
||||
}
|
||||
|
||||
//RFC 5755 4.1
|
||||
// RFC 5755 4.1
|
||||
type issuerSerial struct {
|
||||
Raw asn1.RawContent
|
||||
Issuer asn1.RawValue
|
||||
@ -195,7 +197,7 @@ type issuerSerial struct {
|
||||
IssuerUID asn1.BitString `asn1:"optional"`
|
||||
}
|
||||
|
||||
//RFC 5755 4.1
|
||||
// RFC 5755 4.1
|
||||
type holder struct {
|
||||
Raw asn1.RawContent
|
||||
BaseCertificateID issuerSerial `asn1:"optional,tag:0"`
|
||||
@ -203,13 +205,13 @@ type holder struct {
|
||||
ObjectDigestInfo objectDigestInfo `asn1:"optional,tag:2"`
|
||||
}
|
||||
|
||||
//RFC 5755 4.1
|
||||
// RFC 5755 4.1
|
||||
type attribute struct {
|
||||
ID asn1.ObjectIdentifier
|
||||
RawValues []asn1.RawValue `asn1:"set"`
|
||||
}
|
||||
|
||||
//RFC 5755 4.1
|
||||
// RFC 5755 4.1
|
||||
type tbsAttributeCertificate struct {
|
||||
Raw asn1.RawContent
|
||||
Version int
|
||||
|
@ -15,13 +15,12 @@
|
||||
package attributecert
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
)
|
||||
|
||||
func TestVerifyAttributeCert(t *testing.T) {
|
||||
@ -30,7 +29,7 @@ func TestVerifyAttributeCert(t *testing.T) {
|
||||
"testdata/Intel_pc2.cer",
|
||||
"testdata/Intel_pc3.cer",
|
||||
}
|
||||
data, err := ioutil.ReadFile("testdata/IntelSigningKey_20April2017.cer")
|
||||
data, err := os.ReadFile("testdata/IntelSigningKey_20April2017.cer")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read Intel intermediate certificate: %v", err)
|
||||
}
|
||||
@ -40,7 +39,7 @@ func TestVerifyAttributeCert(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, filename := range testfiles {
|
||||
data, err = ioutil.ReadFile(filename)
|
||||
data, err = os.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read %s: %v", filename, err)
|
||||
}
|
||||
@ -58,7 +57,7 @@ func TestVerifyAttributeCert(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseAttributeCerts(t *testing.T) {
|
||||
files, err := ioutil.ReadDir("testdata")
|
||||
files, err := os.ReadDir("testdata")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read test dir: %v", err)
|
||||
}
|
||||
@ -71,7 +70,7 @@ func TestParseAttributeCerts(t *testing.T) {
|
||||
}
|
||||
filename := "testdata/" + file.Name()
|
||||
jsonfile := filename + ".json"
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read test data %s: %v", filename, err)
|
||||
}
|
||||
@ -79,7 +78,7 @@ func TestParseAttributeCerts(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse test data %s: %v", filename, err)
|
||||
}
|
||||
jsondata, err := ioutil.ReadFile(jsonfile)
|
||||
jsondata, err := os.ReadFile(jsonfile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read json test data %s: %v", jsonfile, err)
|
||||
}
|
||||
|
@ -16,8 +16,6 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var simulatorStatePath = flag.String("state_path", "/tmp/sim/NVRAM/00.permall", "Path to ibmswtpm state file")
|
||||
|
||||
func ekPub() *rsa.PublicKey {
|
||||
out, err := exec.Command("tpm_getpubek", "-z").Output()
|
||||
if err != nil {
|
||||
|
21
go.mod
21
go.mod
@ -1,12 +1,19 @@
|
||||
module github.com/google/go-attestation
|
||||
|
||||
go 1.16
|
||||
go 1.24
|
||||
|
||||
toolchain go1.24.1
|
||||
|
||||
require (
|
||||
github.com/google/certificate-transparency-go v1.1.1
|
||||
github.com/google/go-cmp v0.5.5
|
||||
github.com/google/go-tpm v0.3.2
|
||||
github.com/google/go-tpm-tools v0.3.1
|
||||
github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad
|
||||
golang.org/x/sys v0.0.0-20210316092937-0b90fd5c4c48
|
||||
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
|
||||
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.31.0 // indirect
|
||||
)
|
||||
|
179
x509/x509ext.go
Normal file
179
x509/x509ext.go
Normal file
@ -0,0 +1,179 @@
|
||||
// Package x509ext provides functions for (un)marshalling X.509 extensions not
|
||||
// supported by the crypto/x509 package.
|
||||
package x509ext
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/go-attestation/oid"
|
||||
)
|
||||
|
||||
// RFC 4043
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc4043
|
||||
var (
|
||||
oidPermanentIdentifier = []int{1, 3, 6, 1, 5, 5, 7, 8, 3}
|
||||
)
|
||||
|
||||
// OtherName ::= SEQUENCE {
|
||||
// type-id OBJECT IDENTIFIER,
|
||||
// value [0] EXPLICIT ANY DEFINED BY type-id }
|
||||
type otherName struct {
|
||||
TypeID asn1.ObjectIdentifier
|
||||
Value asn1.RawValue
|
||||
}
|
||||
|
||||
func marshalOtherName(typeID asn1.ObjectIdentifier, value interface{}) (asn1.RawValue, error) {
|
||||
valueBytes, err := asn1.MarshalWithParams(value, "explicit,tag:0")
|
||||
if err != nil {
|
||||
return asn1.RawValue{}, err
|
||||
}
|
||||
otherName := otherName{
|
||||
TypeID: typeID,
|
||||
Value: asn1.RawValue{FullBytes: valueBytes},
|
||||
}
|
||||
bytes, err := asn1.MarshalWithParams(otherName, "tag:0")
|
||||
if err != nil {
|
||||
return asn1.RawValue{}, err
|
||||
}
|
||||
return asn1.RawValue{FullBytes: bytes}, nil
|
||||
}
|
||||
|
||||
// PermanentIdentifier represents an ASN.1 encoded "permanent identifier" as
|
||||
// defined by RFC4043.
|
||||
//
|
||||
// PermanentIdentifier ::= SEQUENCE {
|
||||
// identifierValue UTF8String OPTIONAL,
|
||||
// assigner OBJECT IDENTIFIER OPTIONAL
|
||||
// }
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc4043
|
||||
type PermanentIdentifier struct {
|
||||
IdentifierValue string `asn1:"utf8,optional"`
|
||||
Assigner asn1.ObjectIdentifier `asn1:"optional"`
|
||||
}
|
||||
|
||||
func parsePermanentIdentifier(der []byte) (PermanentIdentifier, error) {
|
||||
var permID PermanentIdentifier
|
||||
if _, err := asn1.UnmarshalWithParams(der, &permID, "explicit,tag:0"); err != nil {
|
||||
return PermanentIdentifier{}, err
|
||||
}
|
||||
return permID, nil
|
||||
}
|
||||
|
||||
// SubjectAltName contains GeneralName variations not supported by the
|
||||
// crypto/x509 package.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc5280
|
||||
type SubjectAltName struct {
|
||||
DirectoryNames []pkix.Name
|
||||
PermanentIdentifiers []PermanentIdentifier
|
||||
}
|
||||
|
||||
// ParseSubjectAltName parses a pkix.Extension into a SubjectAltName struct.
|
||||
func ParseSubjectAltName(ext pkix.Extension) (*SubjectAltName, error) {
|
||||
var out SubjectAltName
|
||||
dirNames, otherNames, err := parseSubjectAltName(ext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parseSubjectAltName: %v", err)
|
||||
}
|
||||
out.DirectoryNames = dirNames
|
||||
|
||||
for _, otherName := range otherNames {
|
||||
if otherName.TypeID.Equal(oidPermanentIdentifier) {
|
||||
permID, err := parsePermanentIdentifier(otherName.Value.FullBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsePermanentIdentifier: %v", err)
|
||||
}
|
||||
out.PermanentIdentifiers = append(out.PermanentIdentifiers, permID)
|
||||
}
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc5280#page-35
|
||||
func parseSubjectAltName(ext pkix.Extension) (dirNames []pkix.Name, otherNames []otherName, err error) {
|
||||
err = forEachSAN(ext.Value, func(generalName asn1.RawValue) error {
|
||||
switch generalName.Tag {
|
||||
case 0: // otherName
|
||||
var otherName otherName
|
||||
if _, err := asn1.UnmarshalWithParams(generalName.FullBytes, &otherName, "tag:0"); err != nil {
|
||||
return fmt.Errorf("OtherName: asn1.UnmarshalWithParams: %v", err)
|
||||
}
|
||||
otherNames = append(otherNames, otherName)
|
||||
case 4: // directoryName
|
||||
var rdns pkix.RDNSequence
|
||||
if _, err := asn1.Unmarshal(generalName.Bytes, &rdns); err != nil {
|
||||
return fmt.Errorf("DirectoryName: asn1.Unmarshal: %v", err)
|
||||
}
|
||||
var dirName pkix.Name
|
||||
dirName.FillFromRDNSequence(&rdns)
|
||||
dirNames = append(dirNames, dirName)
|
||||
default:
|
||||
return fmt.Errorf("expected tag %d", generalName.Tag)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Borrowed from the x509 package.
|
||||
func forEachSAN(extension []byte, callback func(ext asn1.RawValue) error) error {
|
||||
var seq asn1.RawValue
|
||||
rest, err := asn1.Unmarshal(extension, &seq)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if len(rest) != 0 {
|
||||
return errors.New("x509: trailing data after X.509 extension")
|
||||
}
|
||||
if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 {
|
||||
return asn1.StructuralError{Msg: "bad SAN sequence"}
|
||||
}
|
||||
|
||||
rest = seq.Bytes
|
||||
for len(rest) > 0 {
|
||||
var v asn1.RawValue
|
||||
rest, err = asn1.Unmarshal(rest, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := callback(v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return pkix.Extension{}, err
|
||||
}
|
||||
generalNames = append(generalNames, val)
|
||||
}
|
||||
for _, dirName := range san.DirectoryNames {
|
||||
bytes, err := asn1.MarshalWithParams(dirName.ToRDNSequence(), "explicit,tag:4")
|
||||
if err != nil {
|
||||
return pkix.Extension{}, err
|
||||
}
|
||||
generalNames = append(generalNames, asn1.RawValue{FullBytes: bytes})
|
||||
}
|
||||
val, err := asn1.Marshal(generalNames)
|
||||
if err != nil {
|
||||
return pkix.Extension{}, err
|
||||
}
|
||||
return pkix.Extension{
|
||||
Id: oid.SubjectAltName,
|
||||
Critical: critical,
|
||||
Value: val,
|
||||
}, nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user