Compare commits

...

93 Commits

Author SHA1 Message Date
Brandon Weeks
c5d6b1e758
Fix CodeSQL permissions take 2 (#413)
Some checks failed
CodeQL / Analyze (go) (push) Has been cancelled
Test / test-linux (1.24.x) (push) Has been cancelled
Test / test-linux-tpm12 (1.24.x) (push) Has been cancelled
Test / test-macos (1.24.x) (push) Has been cancelled
Test / test-windows (1.24.x) (push) Has been cancelled
2025-03-05 20:25:46 +00:00
Brandon Weeks
7d4525c388
Fix CodeSQL permissions (#412) 2025-03-05 12:06:09 -08:00
dependabot[bot]
dce70c6163
Bump the go-modules group with 3 updates (#410)
Bumps the go-modules group with 3 updates: [github.com/google/go-cmp](https://github.com/google/go-cmp), [github.com/google/go-tpm-tools](https://github.com/google/go-tpm-tools) and [golang.org/x/sys](https://github.com/golang/sys).

Updates `github.com/google/go-cmp` from 0.6.0 to 0.7.0
- [Release notes](https://github.com/google/go-cmp/releases)
- [Commits](https://github.com/google/go-cmp/compare/v0.6.0...v0.7.0)

Updates `github.com/google/go-tpm-tools` from 0.4.4 to 0.4.5
- [Release notes](https://github.com/google/go-tpm-tools/releases)
- [Changelog](https://github.com/google/go-tpm-tools/blob/main/.goreleaser.yaml)
- [Commits](https://github.com/google/go-tpm-tools/compare/v0.4.4...v0.4.5)

Updates `golang.org/x/sys` from 0.30.0 to 0.31.0
- [Commits](https://github.com/golang/sys/compare/v0.30.0...v0.31.0)

---
updated-dependencies:
- dependency-name: github.com/google/go-cmp
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-modules
- dependency-name: github.com/google/go-tpm-tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-modules
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-modules
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-05 11:52:46 -08:00
Brandon Weeks
51a20034c0
Bump Go version to 1.24 (#411) 2025-03-05 11:47:52 -08:00
dependabot[bot]
a94a8af69d
Bump the github-actions group with 4 updates (#409)
Bumps the github-actions group with 4 updates: [actions/checkout](https://github.com/actions/checkout), [github/codeql-action](https://github.com/github/codeql-action), [actions/setup-go](https://github.com/actions/setup-go) and [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action).


Updates `actions/checkout` from 2 to 4
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v4)

Updates `github/codeql-action` from 1 to 3
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v1...v3)

Updates `actions/setup-go` from 2 to 5
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2...v5)

Updates `golangci/golangci-lint-action` from 3 to 6
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-05 19:29:43 +00:00
Brandon Weeks
f37925d5d0
Add github-actions to Dependabot config (#408) 2025-03-05 11:26:41 -08:00
zhsh
f7a27487f1
TPM.PCRBanks() should ignore empty PCR banks. (#406)
Some checks failed
CodeQL / Analyze (go) (push) Has been cancelled
Test / test-linux (1.22.x) (push) Has been cancelled
Test / test-linux-tpm12 (1.22.x) (push) Has been cancelled
Test / test-macos (1.22.x) (push) Has been cancelled
Test / test-windows (1.22.x) (push) Has been cancelled
2025-02-24 08:53:28 -08:00
zhsh
d9d8fdc48e
attest: Implement discovery of supported PCR banks, rather than always blithely assuming we have exactly SHA1 and SHA256. (#404)
Some checks failed
CodeQL / Analyze (go) (push) Has been cancelled
Test / test-linux (1.22.x) (push) Has been cancelled
Test / test-linux-tpm12 (1.22.x) (push) Has been cancelled
Test / test-macos (1.22.x) (push) Has been cancelled
Test / test-windows (1.22.x) (push) Has been cancelled
To do this, add a function to attest.TPM called PCRBanks() which enumerates the available PCR banks on a TPM. This requires plumbing through tpmBase and its implementations; the TPM1.2 implementations statically return []HashAlg{HashSHA1}, as one might expect.

To accomplish all of this, the implementation of HashAlg needed to be rethought. Now, instead of a reimplementation of tpm2.Algorithm, it's a lightweight wrapper around it. Dependent methods -- like Hash() and String() -- no longer have case HashSHA1/case HashSHA256 blocks; instead, they simply delegate to go-tpm2 for their implementations. As a result, we should never need to do something like this again.

Also add convenience constants HashSHA384 and HashSHA512.
2025-02-13 23:38:32 -08:00
dependabot[bot]
f44f5ffe7e
Bump golang.org/x/sys from 0.29.0 to 0.30.0 in the go-modules group (#403)
Some checks failed
CodeQL / Analyze (go) (push) Has been cancelled
Test / test-linux (1.22.x) (push) Has been cancelled
Test / test-linux-tpm12 (1.22.x) (push) Has been cancelled
Test / test-macos (1.22.x) (push) Has been cancelled
Test / test-windows (1.22.x) (push) Has been cancelled
Bumps the go-modules group with 1 update: [golang.org/x/sys](https://github.com/golang/sys).


Updates `golang.org/x/sys` from 0.29.0 to 0.30.0
- [Commits](https://github.com/golang/sys/compare/v0.29.0...v0.30.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-modules
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-10 09:54:13 -08:00
zhsh
9cdb0fcd55
attest: tpm.NewKeyCertifiedBy() method that does not need an entire attest.AK object. The new method only needs the AK handle and its algorithm. (#402)
Some checks failed
CodeQL / Analyze (go) (push) Has been cancelled
Test / test-linux (1.22.x) (push) Has been cancelled
Test / test-linux-tpm12 (1.22.x) (push) Has been cancelled
Test / test-macos (1.22.x) (push) Has been cancelled
Test / test-windows (1.22.x) (push) Has been cancelled
2025-02-03 14:24:55 +11:00
zhsh
dfabc9c919
attest: Support "qualifyingData" when creating a new key. (#401) 2025-02-03 13:43:19 +11:00
Evgeny Shatokhin
c7aee80c5d attest: Support certification by ECC AKs.
Some checks failed
CodeQL / Analyze (go) (push) Has been cancelled
Test / test-linux (1.22.x) (push) Has been cancelled
Test / test-linux-tpm12 (1.22.x) (push) Has been cancelled
Test / test-macos (1.22.x) (push) Has been cancelled
Test / test-windows (1.22.x) (push) Has been cancelled
2025-01-22 08:27:10 -08:00
Evgeny Shatokhin
1b202b12e8 attest: Support creation of ECC AK. 2025-01-22 08:27:10 -08:00
dependabot[bot]
183ad1d5ad
Bump the go-modules group across 1 directory with 2 updates (#394)
Some checks failed
CodeQL / Analyze (go) (push) Has been cancelled
Test / test-linux (1.22.x) (push) Has been cancelled
Test / test-linux-tpm12 (1.22.x) (push) Has been cancelled
Test / test-macos (1.22.x) (push) Has been cancelled
Test / test-windows (1.22.x) (push) Has been cancelled
Bumps the go-modules group with 2 updates in the / directory: [github.com/google/go-tpm](https://github.com/google/go-tpm) and [golang.org/x/sys](https://github.com/golang/sys).


Updates `github.com/google/go-tpm` from 0.9.1 to 0.9.3
- [Release notes](https://github.com/google/go-tpm/releases)
- [Commits](https://github.com/google/go-tpm/compare/v0.9.1...v0.9.3)

Updates `golang.org/x/sys` from 0.28.0 to 0.29.0
- [Commits](https://github.com/golang/sys/compare/v0.28.0...v0.29.0)

---
updated-dependencies:
- dependency-name: github.com/google/go-tpm
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-modules
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-modules
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-06 17:03:05 -08:00
dependabot[bot]
9cc576ead1
Bump golang.org/x/crypto from 0.17.0 to 0.31.0 in the go_modules group (#390)
Some checks failed
CodeQL / Analyze (go) (push) Has been cancelled
Test / test-linux (1.22.x) (push) Has been cancelled
Test / test-linux-tpm12 (1.22.x) (push) Has been cancelled
Test / test-macos (1.22.x) (push) Has been cancelled
Test / test-windows (1.22.x) (push) Has been cancelled
Bumps the go_modules group with 1 update: [golang.org/x/crypto](https://github.com/golang/crypto).


Updates `golang.org/x/crypto` from 0.17.0 to 0.31.0
- [Commits](https://github.com/golang/crypto/compare/v0.17.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
  dependency-group: go_modules
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-12 15:24:52 +01:00
dependabot[bot]
62f7ad0785
Bump golang.org/x/sys in the go-modules group across 1 directory (#385) 2024-11-11 07:32:39 -08:00
dependabot[bot]
f203ad3090
Bump golang.org/x/sys from 0.24.0 to 0.25.0 in the go-modules group (#383) 2024-09-09 16:25:25 +02:00
dependabot[bot]
72657612f0
Bump golang.org/x/sys from 0.23.0 to 0.24.0 in the go-modules group (#382)
Bumps the go-modules group with 1 update: [golang.org/x/sys](https://github.com/golang/sys).


Updates `golang.org/x/sys` from 0.23.0 to 0.24.0
- [Commits](https://github.com/golang/sys/compare/v0.23.0...v0.24.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-modules
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-12 09:17:08 -07:00
dependabot[bot]
ec740ef912
Bump golang.org/x/sys from 0.22.0 to 0.23.0 in the go-modules group (#381)
Bumps the go-modules group with 1 update: [golang.org/x/sys](https://github.com/golang/sys).


Updates `golang.org/x/sys` from 0.22.0 to 0.23.0
- [Commits](https://github.com/golang/sys/compare/v0.22.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-modules
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 10:41:44 -04:00
dependabot[bot]
51d1c6c3c5
Bump golang.org/x/sys from 0.21.0 to 0.22.0 in the go-modules group (#378) 2024-07-08 07:56:01 -07:00
Brandon Weeks
0c084813e6
Configured Dependabot grouped updates (#376) 2024-06-10 18:00:21 +00:00
dependabot[bot]
5d68dfee1b
Bump github.com/google/go-tpm from 0.9.0 to 0.9.1 (#375)
Bumps [github.com/google/go-tpm](https://github.com/google/go-tpm) from 0.9.0 to 0.9.1.
- [Release notes](https://github.com/google/go-tpm/releases)
- [Commits](https://github.com/google/go-tpm/compare/v0.9.0...v0.9.1)

---
updated-dependencies:
- dependency-name: github.com/google/go-tpm
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 09:58:30 -07:00
dependabot[bot]
b7a5927d66
Bump golang.org/x/sys from 0.20.0 to 0.21.0 (#374)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.20.0 to 0.21.0.
- [Commits](https://github.com/golang/sys/compare/v0.20.0...v0.21.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 08:58:12 -07:00
dependabot[bot]
b36ec6af0a
Bump golang.org/x/sys from 0.19.0 to 0.20.0 (#373)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.19.0 to 0.20.0.
- [Commits](https://github.com/golang/sys/compare/v0.19.0...v0.20.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-06 08:34:28 -07:00
whongda
0722a4900b
Support sha384 RSA signature (#372)
Add signature algorithm details for sha384 RSA signature so certificates
using it can has its signature algorithm properly parsed
2024-04-15 15:05:48 -07:00
dependabot[bot]
545501297e
Bump golang.org/x/sys from 0.18.0 to 0.19.0 (#371)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.18.0 to 0.19.0.
- [Commits](https://github.com/golang/sys/compare/v0.18.0...v0.19.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-08 15:29:20 -04:00
dependabot[bot]
5148956a0c
Bump github.com/google/go-tpm-tools from 0.4.3 to 0.4.4 (#369)
Bumps [github.com/google/go-tpm-tools](https://github.com/google/go-tpm-tools) from 0.4.3 to 0.4.4.
- [Release notes](https://github.com/google/go-tpm-tools/releases)
- [Changelog](https://github.com/google/go-tpm-tools/blob/main/.goreleaser.yaml)
- [Commits](https://github.com/google/go-tpm-tools/compare/v0.4.3...v0.4.4)

---
updated-dependencies:
- dependency-name: github.com/google/go-tpm-tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 08:15:39 -07:00
zhsh
a9866d34bb
A note for Linux clients of tpm.EKs(). (#368)
Fix the doc for attest.ActivateCredentialWithEK() as well.
2024-03-27 09:09:08 -07:00
Liam Murphy
5b3763098f
Add critical bool arg to MarshalSubjectAltName to allow SANs to be critical (#367) 2024-03-26 23:34:46 +00:00
dependabot[bot]
e6ab626979
Bump github.com/google/go-tpm-tools from 0.4.2 to 0.4.3 (#364)
Bumps [github.com/google/go-tpm-tools](https://github.com/google/go-tpm-tools) from 0.4.2 to 0.4.3.
- [Release notes](https://github.com/google/go-tpm-tools/releases)
- [Changelog](https://github.com/google/go-tpm-tools/blob/main/.goreleaser.yaml)
- [Commits](https://github.com/google/go-tpm-tools/compare/v0.4.2...v0.4.3)

---
updated-dependencies:
- dependency-name: github.com/google/go-tpm-tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-26 16:55:42 +00:00
dependabot[bot]
52542411c5
Bump golang.org/x/sys from 0.16.0 to 0.18.0 (#365)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.16.0 to 0.18.0.
- [Commits](https://github.com/golang/sys/compare/v0.16.0...v0.18.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-26 09:53:17 -07:00
dependabot[bot]
02cf9e2ddd
Bump golang.org/x/sys from 0.15.0 to 0.16.0 (#360)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.15.0 to 0.16.0.
- [Commits](https://github.com/golang/sys/compare/v0.15.0...v0.16.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-08 09:25:02 -08:00
dependabot[bot]
8b301f2d45
Bump golang.org/x/crypto from 0.13.0 to 0.17.0 (#359)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.13.0 to 0.17.0.
- [Commits](https://github.com/golang/crypto/compare/v0.13.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-18 15:34:50 -08:00
dependabot[bot]
3d017c0234
Bump golang.org/x/sys from 0.14.0 to 0.15.0 (#358)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.14.0 to 0.15.0.
- [Commits](https://github.com/golang/sys/compare/v0.14.0...v0.15.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-04 10:58:25 -08:00
Marcin Wielgoszewski
a3545dfc94
don't drop leading zeroes when performing generic ecdsa signing (#357)
call ret.FillBytes() instead of ret.Bytes() to preserve leading zeroes that may have been dropped when converting the digest to an integer
2023-11-09 08:39:58 -08:00
dependabot[bot]
93c5899459
Bump golang.org/x/sys from 0.13.0 to 0.14.0 (#356)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.13.0 to 0.14.0.
- [Commits](https://github.com/golang/sys/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-06 07:47:43 -08:00
dependabot[bot]
74a49366bd
Bump github.com/google/go-tpm-tools from 0.4.1 to 0.4.2 (#355)
Bumps [github.com/google/go-tpm-tools](https://github.com/google/go-tpm-tools) from 0.4.1 to 0.4.2.
- [Release notes](https://github.com/google/go-tpm-tools/releases)
- [Changelog](https://github.com/google/go-tpm-tools/blob/main/.goreleaser.yaml)
- [Commits](https://github.com/google/go-tpm-tools/compare/v0.4.1...v0.4.2)

---
updated-dependencies:
- dependency-name: github.com/google/go-tpm-tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-30 09:14:26 -07:00
dependabot[bot]
776dc3ac22
Bump golang.org/x/sys from 0.12.0 to 0.13.0 (#353)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.12.0 to 0.13.0.
- [Commits](https://github.com/golang/sys/compare/v0.12.0...v0.13.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-16 16:58:28 +00:00
dependabot[bot]
136789e2e1
Bump github.com/google/go-cmp from 0.5.9 to 0.6.0 (#354)
Bumps [github.com/google/go-cmp](https://github.com/google/go-cmp) from 0.5.9 to 0.6.0.
- [Release notes](https://github.com/google/go-cmp/releases)
- [Commits](https://github.com/google/go-cmp/compare/v0.5.9...v0.6.0)

---
updated-dependencies:
- dependency-name: github.com/google/go-cmp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-16 09:55:31 -07:00
dependabot[bot]
82eb5d47a2
Bump github.com/google/go-tpm-tools from 0.4.0 to 0.4.1 (#352)
Bumps [github.com/google/go-tpm-tools](https://github.com/google/go-tpm-tools) from 0.4.0 to 0.4.1.
- [Release notes](https://github.com/google/go-tpm-tools/releases)
- [Changelog](https://github.com/google/go-tpm-tools/blob/master/.goreleaser.yaml)
- [Commits](https://github.com/google/go-tpm-tools/compare/v0.4.0...v0.4.1)

---
updated-dependencies:
- dependency-name: github.com/google/go-tpm-tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-18 08:32:24 -07:00
dependabot[bot]
f4ab877258
Bump github.com/google/go-tpm-tools (#351)
Bumps [github.com/google/go-tpm-tools](https://github.com/google/go-tpm-tools) from 0.3.13-0.20230620182252-4639ecce2aba to 0.4.0.
- [Release notes](https://github.com/google/go-tpm-tools/releases)
- [Changelog](https://github.com/google/go-tpm-tools/blob/master/.goreleaser.yaml)
- [Commits](https://github.com/google/go-tpm-tools/commits/v0.4.0)

---
updated-dependencies:
- dependency-name: github.com/google/go-tpm-tools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-11 08:03:38 -07:00
Herman Slatman
3d71f101b1
Fix Intel EK certificate URLs on Linux (#347) 2023-09-08 18:23:49 +00:00
Damien Miller
42c11fc152
Fix typo (#349) 2023-09-08 17:46:44 +00:00
dependabot[bot]
3c84bff65e
Bump golang.org/x/sys from 0.9.0 to 0.12.0 (#348)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.9.0 to 0.12.0.
- [Commits](https://github.com/golang/sys/compare/v0.9.0...v0.12.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-08 17:42:41 +00:00
Brandon Weeks
ab5dee2ae5
ci: don't install OpenSSL 1.1 on macOS (#350)
GitHub actions runner macos-13 version 20230801.2 appears to include
this by default, causing a link failure.

da18545f2f
2023-09-08 10:33:41 -07:00
zhsh
046550658b
attest: Create keys under non-default SRKs (#342) 2023-07-11 10:14:13 -04:00
zhsh
310e2caafe
attest: Remove the EK field from AK struct (#341) 2023-06-29 07:53:53 -07:00
zhsh
60adf13bc0
attest: ActivateCredentialWithEK() method that can be used with non-default EKs. (#340) 2023-06-28 20:38:36 -07:00
zhsh
a56e8c4896
Activate AK with ECC EKs. (#339) 2023-06-27 20:02:47 -07:00
smo4201
8af5f4e7de
attest: Make PCRs included in quote configurable (#311)
Change the low-level Quote() functions so that the PCRs to be
included in the quote is selectable. Does not change the
high-level attestPlatform functions, which still retrieve
all PCRs.
2023-06-26 23:04:59 +00:00
zhsh
b92d1c69bf
Add TPM.EKCertificates() method, it returns all certificates from TPM's NVRAM (#333) 2023-06-23 15:10:34 -07:00
zhsh
d29df30553
Add EK as a field to AK struct. (#332)
The change is a no-op for existing clients, and it will simplify
adding the support for ECC EKs. The activation code no longer makes
assumptions about EK's type and handle (i.e. RSA and 0x81010001),
and instead relies on TPM.EKs() to provide the EK's details.
2023-06-22 13:17:47 -07:00
Brandon Weeks
63dd90f699
Bump github.com/google/go-tpm from 0.3.4 to 0.9.0 (#337) 2023-06-21 16:18:54 +02:00
dependabot[bot]
ac9aa2497f
Bump golang.org/x/sys from 0.8.0 to 0.9.0 (#335)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.8.0 to 0.9.0.
- [Commits](https://github.com/golang/sys/compare/v0.8.0...v0.9.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-20 16:40:32 +00:00
Ludovic Fernandez
2788b541c7
Fix golangci-lint workflow (#336) 2023-06-20 09:35:01 -07:00
Chris Fenner
a9b6eb1eb8
use legacy tpm2 at its new path (#331) 2023-06-13 07:43:38 -07:00
zhsh
50c1e1e03b
Renamed some variables and methods to highlight that only RSA EKs are (#330)
currently supported.

This is the first step towards supporting ECC EKs.
2023-06-12 18:36:51 -07:00
juanvallejo
258084d04e Add support for generating TPM2.0 challenges using AttestedCertifyInfo
Fixes: issues/320.

Adds support for generating an activation challenge using
CertificationParameters.
Achieves symmetry with challenge-generation in
AttestationParameters, in order to provide a challenge to a
TPM to activate a TPM-certified key.

`attest.Activation` currently supports verifying and
generating a challenge given attestationData, an EK, an AK,
and a signature. In the attestationData, the CreationInfo
field is used to further validate and create the resulting
challenge.
In this change, `attest.Certification` will now support
generating a challenge given attestationData, an EK, a
TPM-certified public key, and a signature, in addition to
an AK used to verify the certification of the provided
public key we are generating an activation challenge for.
2023-06-06 10:46:12 -07:00
Herman Slatman
89884d0a74
Fix Intel EK certificate URL (#310)
* Fix Intel EK certificate URL

To download the certificate for an Intel TPM, the base64 padding
in the URL needs to be replaced with `%3D`. If it's not replaced,
requesting the URL will result in HTTP 403 Forbidden.

* Use `url.QueryEscape` to escape base64 padding
2023-06-02 09:17:59 -07:00
zhsh
b474b712d4
wrappedTPM20.ekTemplate() never returns an error. (#327) 2023-05-29 10:16:09 -07:00
dependabot[bot]
a4b579bcf0
Bump github.com/google/go-tpm-tools from 0.3.9 to 0.3.12 (#324)
Bumps [github.com/google/go-tpm-tools](https://github.com/google/go-tpm-tools) from 0.3.9 to 0.3.12.
- [Release notes](https://github.com/google/go-tpm-tools/releases)
- [Commits](https://github.com/google/go-tpm-tools/compare/v0.3.9...v0.3.12)

---
updated-dependencies:
- dependency-name: github.com/google/go-tpm-tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-22 11:14:50 -07:00
dependabot[bot]
62a036b369
Bump golang.org/x/sys from 0.0.0-20220209214540-3681064d5158 to 0.8.0 (#316)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.0.0-20220209214540-3681064d5158 to 0.8.0.
- [Commits](https://github.com/golang/sys/commits/v0.8.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-22 11:03:00 -07:00
Brandon Weeks
10dd5f7a05
Bump Go version to 1.19 (#325) 2023-05-22 10:52:09 -07:00
Noah Stride
3ef3949b46 Fix comments referring to .Serialize() instead of .Marshal() 2023-05-15 10:56:35 -07:00
José Martínez
1f9c436d57 Parse TCG_PCR_EVENT2 structures with an eventSize of 0 2023-05-15 09:19:59 -07:00
dependabot[bot]
270ecbab1f
Bump github.com/google/go-tspi (#307)
Bumps [github.com/google/go-tspi](https://github.com/google/go-tspi) from 0.2.1-0.20190423175329-115dea689aad to 0.3.0.
- [Release notes](https://github.com/google/go-tspi/releases)
- [Commits](https://github.com/google/go-tspi/commits/v0.3.0)

---
updated-dependencies:
- dependency-name: github.com/google/go-tspi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-10 10:28:28 -08:00
Brandon Weeks
0ccbb50494
Handle multiple ELAM events (#309) 2023-03-08 13:32:50 -08:00
Mike Gerow
68deb4ce55 Use NV cert index as auth hierarchy for EK cert
This is the same approach tpm2_getekcertificate uses, with its
`TPM2_HANDLE_FLAGS_NV` flag.

The main impetus here is is ChromeOS's vtpm implementation[1], which
doesn't have a concept of an "owner" or "platform" password and expects
the NV index itself as the auth hierarchy. In either case, as this is
the same approach tpm2_getekcertificate uses this should provide a more
standard/common approach as opposed to relying on the owner password to
be empty.

Tested with both CrOS's vTPM and a real TPM on Debian.

b/258300352

[1]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/vtpm/commands/nv_read_command.cc;l=64-68;drc=1efd0c8f36050d56b8550354a4c7af925e44118a
2023-01-05 12:25:14 -08:00
Marcin Wielgoszewski
5238453493 Truncate digests to the left most bits to match the bit-length of the order of the curve 2022-11-15 15:26:00 -08:00
Mike Gerow
b93151db1f
Preserve error logic in getPrimaryKeyHandle (#296)
In `wrappedTPM20.getPrimaryKeyHandle()`, preserve any error from the
short-circuit `tpm2.ReadPublic()` logic, so that we can return it
alongside any failure in `tpm2.CreatePrimary()`

Co-authored-by: Justin King-Lacroix <justinkl@google.com>
2022-11-04 14:57:37 -07:00
Brandon Weeks
0dc056af7d Fix golangci-lint findings 2022-11-01 13:38:49 -07:00
Brandon Weeks
19d3c4de97 Run golangci-lint as part of CI
https://golangci-lint.run/usage/install/#ci-installation
2022-11-01 13:38:49 -07:00
Brandon Weeks
438907edb0
Fix lints; run gofmt (#293)
$ gofmt -s -w .
2022-11-01 12:19:57 -07:00
hansinator
17f9c05652
fix returning wrong error in ParseWinEvents (#291)
Co-authored-by: Hans-Gert Dahmen <hans-gert.dahmen@immu.ne>
2022-10-11 09:22:10 -07:00
hansinator
d98599d257
Fix decoding of uints in windows events (#290)
Co-authored-by: Hans-Gert Dahmen <hans-gert.dahmen@immu.ne>
2022-10-07 13:01:04 -07:00
dependabot[bot]
053c50e8ad
Bump github.com/google/go-cmp from 0.5.8 to 0.5.9 (#286)
Bumps [github.com/google/go-cmp](https://github.com/google/go-cmp) from 0.5.8 to 0.5.9.
- [Release notes](https://github.com/google/go-cmp/releases)
- [Commits](https://github.com/google/go-cmp/compare/v0.5.8...v0.5.9)

---
updated-dependencies:
- dependency-name: github.com/google/go-cmp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-12 11:14:05 -04:00
Malte Poll
e99c3e104e
Ignore MokListTrusted events in ParseUEFIVariableAuthority (#284) 2022-09-09 15:58:48 -07:00
dependabot[bot]
dff2daeaf0
Bump github.com/google/go-tpm-tools from 0.3.8 to 0.3.9 (#285) 2022-08-22 19:31:56 +00:00
Brandon Weeks
f5d560164e
Set NoDa flag on the AK template (#280)
Resolves an issue where a TPM in DA lockout mode cannot generate an AK.
2022-06-03 12:51:56 -07:00
Brandon Weeks
cb976082a3
x509ext: initial version of package (#279) 2022-06-02 15:05:51 -07:00
Brandon Weeks
50e72a4743
attest: fix OSS-Fuzz build (#278) 2022-05-31 21:50:58 -07:00
Brandon Weeks
f1ff544e51
attest: restore change from a35bd36 mistakingly removed in be496f1 (#277) 2022-05-31 13:12:21 -07:00
dependabot[bot]
e0bd974e4e
Bump github.com/google/go-cmp from 0.5.7 to 0.5.8 (#275)
Bumps [github.com/google/go-cmp](https://github.com/google/go-cmp) from 0.5.7 to 0.5.8.
- [Release notes](https://github.com/google/go-cmp/releases)
- [Commits](https://github.com/google/go-cmp/compare/v0.5.7...v0.5.8)

---
updated-dependencies:
- dependency-name: github.com/google/go-cmp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-16 12:44:25 -07:00
dependabot[bot]
ad58dc770e
Bump github.com/google/go-tpm-tools from 0.3.7 to 0.3.8 (#276)
Bumps [github.com/google/go-tpm-tools](https://github.com/google/go-tpm-tools) from 0.3.7 to 0.3.8.
- [Release notes](https://github.com/google/go-tpm-tools/releases)
- [Commits](https://github.com/google/go-tpm-tools/compare/v0.3.7...v0.3.8)

---
updated-dependencies:
- dependency-name: github.com/google/go-tpm-tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-16 12:44:06 -07:00
dependabot[bot]
8235370483
Bump github.com/google/go-tpm-tools from 0.3.1 to 0.3.7 (#273)
Bumps [github.com/google/go-tpm-tools](https://github.com/google/go-tpm-tools) from 0.3.1 to 0.3.7.
- [Release notes](https://github.com/google/go-tpm-tools/releases)
- [Commits](https://github.com/google/go-tpm-tools/compare/v0.3.1...v0.3.7)

---
updated-dependencies:
- dependency-name: github.com/google/go-tpm-tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-18 08:39:55 -07:00
Joe Richey
8820d49b18 CI: Allow SHA1 on Go 1.18
Signed-off-by: Joe Richey <joerichey@google.com>
2022-04-04 13:48:39 -07:00
Joe Richey
0961a88d7c parseEfiSignature: Don't rely on type of error code
The specific error type is not part of x509.ParseCertificate documented
API. So we shouldn't rely on it for this workaround.

Signed-off-by: Joe Richey <joerichey@google.com>
2022-04-04 13:48:39 -07:00
Joe Richey
df6b91cbdb test: Use Fatalf instead of Errorf to prevent segfault
Signed-off-by: Joe Richey <joerichey@google.com>
2022-04-04 13:48:39 -07:00
Joe Richey
03018e6828 Remove certificate-transparency-go dependancy
Signed-off-by: Joe Richey <joerichey@google.com>
2022-04-04 13:48:39 -07:00
Joe Richey
0a9ecdcf7c Run CI for Go 1.18
Signed-off-by: Joe Richey <joerichey@google.com>
2022-03-25 13:55:33 -07:00
Joe Richey
4b44082d2c ci: ONly run on pushes to master
This prevents running the CI twice when opening a PR with a non-master
branch.

Signed-off-by: Joe Richey <joerichey@google.com>
2022-03-25 13:55:33 -07:00
dependabot[bot]
2a5dfec7cf
Bump github.com/google/go-cmp from 0.5.5 to 0.5.7 (#261)
Bumps [github.com/google/go-cmp](https://github.com/google/go-cmp) from 0.5.5 to 0.5.7.
- [Release notes](https://github.com/google/go-cmp/releases)
- [Commits](https://github.com/google/go-cmp/compare/v0.5.5...v0.5.7)

---
updated-dependencies:
- dependency-name: github.com/google/go-cmp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-23 21:08:15 -07:00
Jiankun Lü
83d71b1c53
Bump go-tpm version (#264)
Certify now returns raw TPMT_SIGNATURE, so no need to pack it.
2022-02-14 16:31:48 -08:00
43 changed files with 2225 additions and 520 deletions

View File

@ -4,3 +4,16 @@ updates:
directory: "/" directory: "/"
schedule: schedule:
interval: "weekly" interval: "weekly"
groups:
"Go modules":
patterns:
- "*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
groups:
github-actions:
patterns:
- "*"

View File

@ -1,5 +1,9 @@
name: "CodeQL" name: "CodeQL"
permissions:
contents: read
security-events: write
on: on:
push: push:
branches: [master] branches: [master]
@ -20,7 +24,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v4
with: with:
fetch-depth: 2 fetch-depth: 2
@ -28,12 +32,12 @@ jobs:
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v1 uses: github/codeql-action/init@v3
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@v1 uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis - 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
View 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

View File

@ -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 name: Test
jobs: jobs:
test-linux: test-linux:
strategy: strategy:
matrix: matrix:
go-version: [1.16.x, 1.17.x] go-version: [1.24.x]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Install Go - name: Install Go
uses: actions/setup-go@v2 uses: actions/setup-go@v5
with: with:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Test - name: Test
run: go test ./... run: go test ./...
test-linux-tpm12: test-linux-tpm12:
strategy: strategy:
matrix: matrix:
go-version: [1.16.x, 1.17.x] go-version: [1.24.x]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Install Go - name: Install Go
uses: actions/setup-go@v2 uses: actions/setup-go@v5
with: with:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Install libtspi - name: Install libtspi
run: sudo apt-get install -y libtspi-dev run: sudo apt-get install -y libtspi-dev
- name: Test - name: Test
@ -34,33 +43,29 @@ jobs:
test-macos: test-macos:
strategy: strategy:
matrix: matrix:
go-version: [1.16.x, 1.17.x] go-version: [1.24.x]
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- name: Install Go - name: Install Go
uses: actions/setup-go@v2 uses: actions/setup-go@v5
with: with:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v4
# See https://github.com/google/go-tpm-tools#macos-dev # 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 - name: Test
run: C_INCLUDE_PATH="$(brew --prefix openssl@1.1)/include" LIBRARY_PATH="$(brew --prefix openssl@1.1)/lib" go test ./... run: C_INCLUDE_PATH="$(brew --prefix openssl@1.1)/include" LIBRARY_PATH="$(brew --prefix openssl@1.1)/lib" go test ./...
test-windows: test-windows:
strategy: strategy:
matrix: matrix:
go-version: [1.16.x, 1.17.x] go-version: [1.24.x]
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Install Go - name: Install Go
uses: actions/setup-go@v2 uses: actions/setup-go@v5
with: with:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Test - name: Test
run: go build ./... run: go build ./...

5
.golangci.yaml Normal file
View File

@ -0,0 +1,5 @@
linters:
enable:
- gofmt
disable:
- errcheck

View File

@ -81,7 +81,7 @@ if err != nil {
// handle error // 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 // handle error
} }
@ -115,7 +115,7 @@ returning the same secret to the server.
```go ```go
// Client decrypts the credential // Client decrypts the credential
akBytes, err := ioutil.ReadFile("encrypted_aik.json") akBytes, err := os.ReadFile("encrypted_aik.json")
if err != nil { if err != nil {
// handle error // handle error
} }

View File

@ -9,11 +9,11 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/google/go-tpm/legacy/tpm2"
tpm1 "github.com/google/go-tpm/tpm" tpm1 "github.com/google/go-tpm/tpm"
"github.com/google/go-tpm/tpm2"
// TODO(jsonp): Move activation generation code to internal package. // 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" "github.com/google/go-tspi/verification"
) )

View File

@ -72,6 +72,14 @@ type KeyConfig struct {
// Size is used to specify the bit size of the key or elliptic curve. For // Size is used to specify the bit size of the key or elliptic curve. For
// example, '256' is used to specify curve P-256. // example, '256' is used to specify curve P-256.
Size int 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. // defaultConfig is used when no other configuration is specified.

View File

@ -12,10 +12,10 @@
// License for the specific language governing permissions and limitations under // License for the specific language governing permissions and limitations under
// the License. // the License.
//go:build (!localtest || !tpm12) && linux && cgo //go:build (!localtest || !tpm12) && cgo && !gofuzz
// +build !localtest !tpm12 // +build !localtest !tpm12
// +build linux
// +build cgo // +build cgo
// +build !gofuzz
// NOTE: simulator requires cgo, hence the build tag. // NOTE: simulator requires cgo, hence the build tag.
@ -28,11 +28,10 @@ import (
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/x509"
"encoding/asn1" "encoding/asn1"
"math/big" "math/big"
"testing" "testing"
"github.com/google/certificate-transparency-go/x509"
) )
func TestSimTPM20KeyCreateAndLoad(t *testing.T) { func TestSimTPM20KeyCreateAndLoad(t *testing.T) {
@ -101,6 +100,22 @@ func testKeyCreateAndLoad(t *testing.T, tpm *TPM) {
Size: 2048, 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) { t.Run(test.name, func(t *testing.T) {
sk, err := tpm.NewKey(ak, test.opts) sk, err := tpm.NewKey(ak, test.opts)
@ -497,8 +512,7 @@ func testKeyOpts(t *testing.T, tpm *TPM) {
expected = defaultConfig expected = defaultConfig
} }
pub := sk.Public() switch pub := sk.Public().(type) {
switch pub.(type) {
case *ecdsa.PublicKey: case *ecdsa.PublicKey:
if expected.Algorithm != ECDSA { if expected.Algorithm != ECDSA {
t.Errorf("incorrect key type generated, expected %q, got EC", expected.Algorithm) 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 { if !ok {
t.Fatalf("cannot match curve to key size %d", expected.Size) t.Fatalf("cannot match curve to key size %d", expected.Size)
} }
curve := pub.(*ecdsa.PublicKey).Curve if expectedCurve != pub.Curve {
if expectedCurve != curve { t.Errorf("incorrect curve, expected %v, got %v", expectedCurve, pub.Curve)
t.Errorf("incorrect curve, expected %v, got %v", expectedCurve, curve)
} }
case *rsa.PublicKey: case *rsa.PublicKey:
if expected.Algorithm != RSA { if expected.Algorithm != RSA {
t.Errorf("incorrect key type, expected %q, got RSA", expected.Algorithm) t.Errorf("incorrect key type, expected %q, got RSA", expected.Algorithm)
} }
if pub.(*rsa.PublicKey).Size()*8 != expected.Size { if pub.Size()*8 != expected.Size {
t.Errorf("incorrect key size, expected %d, got %d", expected.Size, pub.(*rsa.PublicKey).Size()*8) t.Errorf("incorrect key size, expected %d, got %d", expected.Size, pub.Size()*8)
} }
default: default:
t.Errorf("unsupported key type: %T", pub) t.Errorf("unsupported key type: %T", pub)

View File

@ -6,16 +6,15 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/x509"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"encoding/pem" "encoding/pem"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"github.com/google/certificate-transparency-go/x509"
"github.com/google/go-attestation/attest" "github.com/google/go-attestation/attest"
"github.com/google/go-attestation/attest/attest-tool/internal" "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("Version: %d\n", info.Version)
fmt.Printf("Interface: %d\n", info.Interface) fmt.Printf("Interface: %d\n", info.Interface)
fmt.Printf("VendorInfo: %x\n", info.VendorInfo) 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": case "make-ak", "make-aik":
k, err := tpm.NewAK(nil) k, err := tpm.NewAK(nil)
@ -153,10 +152,10 @@ func runCommand(tpm *attest.TPM) error {
if err != nil { if err != nil {
return err return err
} }
return ioutil.WriteFile(*keyPath, b, 0644) return os.WriteFile(*keyPath, b, 0644)
case "quote": case "quote":
b, err := ioutil.ReadFile(*keyPath) b, err := os.ReadFile(*keyPath)
if err != nil { if err != nil {
return err return err
} }

View File

@ -16,7 +16,7 @@ package eventlog
import ( import (
"encoding/json" "encoding/json"
"io/ioutil" "os"
"testing" "testing"
"github.com/google/go-attestation/attest" "github.com/google/go-attestation/attest"
@ -24,7 +24,7 @@ import (
) )
func parseEvents(t *testing.T, testdata string) []attest.Event { func parseEvents(t *testing.T, testdata string) []attest.Event {
data, err := ioutil.ReadFile(testdata) data, err := os.ReadFile(testdata)
if err != nil { if err != nil {
t.Fatalf("reading test data: %v", err) t.Fatalf("reading test data: %v", err)
} }

View File

@ -3,7 +3,7 @@ package internal
import ( import (
"github.com/google/go-attestation/attest" "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. // Dump describes the layout of serialized information from the dump command.

View File

@ -17,14 +17,15 @@ package attest
import ( import (
"crypto" "crypto"
"crypto/x509"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"strings" "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/tpm"
"github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpmutil"
) )
// TPMVersion is used to configure a preference in // TPMVersion is used to configure a preference in
@ -98,13 +99,25 @@ const (
keyEncodingParameterized 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 { type ak interface {
close(tpmBase) error close(tpmBase) error
marshal() ([]byte, error) marshal() ([]byte, error)
activateCredential(tpm tpmBase, in EncryptedCredential) ([]byte, error) activateCredential(tpm tpmBase, in EncryptedCredential, ek *EK) ([]byte, error)
quote(t tpmBase, nonce []byte, alg HashAlg) (*Quote, error) quote(t tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error)
attestationParameters() AttestationParameters 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. // 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 // 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. // This operation is synonymous with TPM2_ActivateCredential.
func (k *AK) ActivateCredential(tpm *TPM, in EncryptedCredential) (secret []byte, err error) { 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. // 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 // This is a low-level API. Consumers seeking to attest the state of the
// platform should use tpm.AttestPlatform() instead. // platform should use tpm.AttestPlatform() instead.
func (k *AK) Quote(tpm *TPM, nonce []byte, alg HashAlg) (*Quote, error) { 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 // 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 // key. Depending on the actual instantiation it can accept different handle
// types (e.g., tpmutil.Handle on Linux or uintptr on Windows). // types (e.g., tpmutil.Handle on Linux or uintptr on Windows).
func (k *AK) Certify(tpm *TPM, handle interface{}) (*CertificationParameters, error) { 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 // AKConfig encapsulates parameters for minting keys.
// now (despite being empty) for future interface compatibility.
type AKConfig struct { 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 // 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 // Public key. Clients or servers can perform an HTTP GET to this URL, and
// use ParseEKCertificate on the response body. // use ParseEKCertificate on the response body.
CertificateURL string CertificateURL string
// The EK persistent handle.
handle tpmutil.Handle
} }
// AttestationParameters describes information about a key which is necessary // AttestationParameters describes information about a key which is necessary
@ -357,41 +399,30 @@ func (a *AKPublic) VerifyAll(quotes []Quote, pcrs []PCR, nonce []byte) error {
// HashAlg identifies a hashing Algorithm. // HashAlg identifies a hashing Algorithm.
type HashAlg uint8 type HashAlg uint8
// Valid hash algorithms. // Known valid hash algorithms.
var ( var (
HashSHA1 = HashAlg(tpm2.AlgSHA1) HashSHA1 = HashAlg(tpm2.AlgSHA1)
HashSHA256 = HashAlg(tpm2.AlgSHA256) HashSHA256 = HashAlg(tpm2.AlgSHA256)
HashSHA384 = HashAlg(tpm2.AlgSHA384)
HashSHA512 = HashAlg(tpm2.AlgSHA512)
) )
func (a HashAlg) cryptoHash() crypto.Hash { func (a HashAlg) cryptoHash() crypto.Hash {
switch a { g := a.goTPMAlg()
case HashSHA1: h, err := g.Hash()
return crypto.SHA1 if err != nil {
case HashSHA256: panic(fmt.Sprintf("HashAlg %v (corresponding to TPM2.Algorithm %v) has no corresponding crypto.Hash", a, g))
return crypto.SHA256
} }
return 0 return h
} }
func (a HashAlg) goTPMAlg() tpm2.Algorithm { func (a HashAlg) goTPMAlg() tpm2.Algorithm {
switch a { return tpm2.Algorithm(a)
case HashSHA1:
return tpm2.AlgSHA1
case HashSHA256:
return tpm2.AlgSHA256
}
return 0
} }
// String returns a human-friendly representation of the hash algorithm. // String returns a human-friendly representation of the hash algorithm.
func (a HashAlg) String() string { func (a HashAlg) String() string {
switch a { return a.goTPMAlg().String()
case HashSHA1:
return "SHA1"
case HashSHA256:
return "SHA256"
}
return fmt.Sprintf("HashAlg<%d>", int(a))
} }
// PlatformParameters encapsulates the set of information necessary to attest // PlatformParameters encapsulates the set of information necessary to attest

View File

@ -12,10 +12,10 @@
// License for the specific language governing permissions and limitations under // License for the specific language governing permissions and limitations under
// the License. // the License.
//go:build (!localtest || !tpm12) && linux && cgo //go:build (!localtest || !tpm12) && cgo && !gofuzz
// +build !localtest !tpm12 // +build !localtest !tpm12
// +build linux
// +build cgo // +build cgo
// +build !gofuzz
// NOTE: simulator requires cgo, hence the build tag. // NOTE: simulator requires cgo, hence the build tag.
@ -35,7 +35,7 @@ func setupSimulatedTPM(t *testing.T) (*simulator.Simulator, *TPM) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
attestTPM, err := OpenTPM(&OpenConfig{CommandChannel: &linuxCmdChannel{tpm}}) attestTPM, err := OpenTPM(&OpenConfig{CommandChannel: &fakeCmdChannel{tpm}})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -67,8 +67,29 @@ func TestSimTPM20Info(t *testing.T) {
func TestSimTPM20AKCreateAndLoad(t *testing.T) { func TestSimTPM20AKCreateAndLoad(t *testing.T) {
sim, tpm := setupSimulatedTPM(t) sim, tpm := setupSimulatedTPM(t)
defer sim.Close() defer sim.Close()
for _, test := range []struct {
ak, err := tpm.NewAK(nil) 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 { if err != nil {
t.Fatalf("NewAK() failed: %v", err) t.Fatalf("NewAK() failed: %v", err)
} }
@ -84,7 +105,7 @@ func TestSimTPM20AKCreateAndLoad(t *testing.T) {
loaded, err := tpm.LoadAK(enc) loaded, err := tpm.LoadAK(enc)
if err != nil { if err != nil {
t.Fatalf("LoadKey() failed: %v", err) t.Fatalf("LoadAK() failed: %v", err)
} }
defer loaded.Close(tpm) defer loaded.Close(tpm)
@ -95,35 +116,50 @@ func TestSimTPM20AKCreateAndLoad(t *testing.T) {
t.Logf("Original = %v", k1.public) t.Logf("Original = %v", k1.public)
t.Logf("Loaded = %v", k2.public) t.Logf("Loaded = %v", k2.public)
} }
})
}
} }
func TestSimTPM20ActivateCredential(t *testing.T) { 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) sim, tpm := setupSimulatedTPM(t)
defer sim.Close() 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() EKs, err := tpm.EKs()
if err != nil { if err != nil {
t.Fatalf("EKs() failed: %v", err) t.Fatalf("EKs() failed: %v", err)
} }
ek := chooseEK(t, EKs) ek := chooseEK(t, EKs)
ak, err := tpm.NewAK(nil)
if err != nil {
t.Fatalf("NewAK() failed: %v", err)
}
defer ak.Close(tpm)
ap := ActivationParameters{ ap := ActivationParameters{
TPMVersion: TPMVersion20, TPMVersion: TPMVersion20,
AK: ak.AttestationParameters(), AK: ak.AttestationParameters(),
EK: ek, EK: ek.Public,
} }
secret, challenge, err := ap.Generate() secret, challenge, err := ap.Generate()
if err != nil { if err != nil {
t.Fatalf("Generate() failed: %v", err) 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 { if err != nil {
t.Errorf("ak.ActivateCredential() failed: %v", err) 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) sim, tpm := setupSimulatedTPM(t)
defer sim.Close() defer sim.Close()
ekHnd, _, err := tpm.tpm.(*wrappedTPM20).getPrimaryKeyHandle(commonEkEquivalentHandle) srkHnd, _, err := tpm.tpm.(*wrappedTPM20).getStorageRootKeyHandle(parentConfig)
if err != nil { if err != nil {
t.Fatalf("getPrimaryKeyHandle() failed: %v", err) t.Fatalf("getStorageRootKeyHandle() failed: %v", err)
} }
if ekHnd != commonEkEquivalentHandle { if srkHnd != parentConfig.Handle {
t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, commonEkEquivalentHandle) 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 { if err != nil {
t.Fatalf("second getPrimaryKeyHandle() failed: %v", err) t.Fatalf("second getStorageRootKeyHandle() failed: %v", err)
} }
if ekHnd != commonEkEquivalentHandle { if srkHnd != parentConfig.Handle {
t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, commonEkEquivalentHandle) 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 { if p {
t.Fatalf("generated a new key the second time; that shouldn't happen") t.Fatalf("generated a new key the second time; that shouldn't happen")

View File

@ -16,7 +16,6 @@ package attest
import ( import (
"bytes" "bytes"
"crypto"
"flag" "flag"
"fmt" "fmt"
"reflect" "reflect"
@ -84,13 +83,35 @@ func TestAKCreateAndLoad(t *testing.T) {
if !*testLocal { if !*testLocal {
t.SkipNow() t.SkipNow()
} }
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) tpm, err := OpenTPM(nil)
if err != nil { if err != nil {
t.Fatalf("OpenTPM() failed: %v", err) t.Fatalf("OpenTPM() failed: %v", err)
} }
defer tpm.Close() defer tpm.Close()
ak, err := tpm.NewAK(nil) ak, err := tpm.NewAK(test.opts)
if err != nil { if err != nil {
t.Fatalf("NewAK() failed: %v", err) t.Fatalf("NewAK() failed: %v", err)
} }
@ -106,7 +127,7 @@ func TestAKCreateAndLoad(t *testing.T) {
loaded, err := tpm.LoadAK(enc) loaded, err := tpm.LoadAK(enc)
if err != nil { if err != nil {
t.Fatalf("LoadKey() failed: %v", err) t.Fatalf("LoadAK() failed: %v", err)
} }
defer loaded.Close(tpm) defer loaded.Close(tpm)
@ -117,18 +138,20 @@ func TestAKCreateAndLoad(t *testing.T) {
t.Logf("Original = %v", k1.public) t.Logf("Original = %v", k1.public)
t.Logf("Loaded = %v", k2.public) t.Logf("Loaded = %v", k2.public)
} }
})
}
} }
// chooseEK selects the EK public which will be activated against. // chooseEK selects the EK which will be activated against.
func chooseEK(t *testing.T, eks []EK) crypto.PublicKey { func chooseEK(t *testing.T, eks []EK) EK {
t.Helper() t.Helper()
for _, ek := range eks { for _, ek := range eks {
return ek.Public return ek
} }
t.Fatalf("No suitable EK found") t.Fatalf("No suitable EK found")
return nil return EK{}
} }
func TestPCRs(t *testing.T) { func TestPCRs(t *testing.T) {

View File

@ -151,7 +151,7 @@ func TestTPMActivateCredential(t *testing.T) {
ap := ActivationParameters{ ap := ActivationParameters{
TPMVersion: TPMVersion12, TPMVersion: TPMVersion12,
AK: ak.AttestationParameters(), AK: ak.AttestationParameters(),
EK: ek, EK: ek.Public,
} }
secret, challenge, err := ap.Generate() secret, challenge, err := ap.Generate()
if err != nil { if err != nil {

View File

@ -17,12 +17,15 @@ package attest
import ( import (
"bytes" "bytes"
"crypto" "crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa" "crypto/rsa"
"errors" "errors"
"fmt" "fmt"
"io" "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" "github.com/google/go-tpm/tpmutil"
) )
@ -62,6 +65,44 @@ type VerifyOpts struct {
Hash crypto.Hash 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: // Verify verifies the TPM2-produced certification parameters checking whether:
// - the key length is secure // - the key length is secure
// - the attestation parameters matched the attested key // - 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. // 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() { if !opts.Hash.Available() {
return fmt.Errorf("hash function is unavailable") 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) return fmt.Errorf("DecodeSignature() failed: %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 { if err := rsa.VerifyPKCS1v15(pk, opts.Hash, hsh.Sum(nil), sig.RSA.Signature); err != nil {
return fmt.Errorf("could not verify attestation: %v", err) 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 return nil
} }
// certify uses AK's handle and the passed signature scheme to certify the key // Generate returns a credential activation challenge, which can be provided
// with the `hnd` handle. // to the TPM to verify the AK parameters given are authentic & the AK
func certify(tpm io.ReadWriteCloser, hnd, akHnd tpmutil.Handle, scheme tpm2.SigScheme) (*CertificationParameters, error) { // 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) pub, _, _, err := tpm2.ReadPublic(tpm, hnd)
if err != nil { if err != nil {
return nil, fmt.Errorf("tpm2.ReadPublic() failed: %v", err) 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 { if err != nil {
return nil, fmt.Errorf("could not encode public key: %v", err) 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 { if err != nil {
return nil, fmt.Errorf("tpm2.Certify() failed: %v", err) 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{ return &CertificationParameters{
Public: public, Public: public,
CreateAttestation: att, CreateAttestation: att,
CreateSignature: signature, CreateSignature: sig,
}, nil }, nil
} }

View File

@ -12,31 +12,39 @@
// License for the specific language governing permissions and limitations under // License for the specific language governing permissions and limitations under
// the License. // the License.
//go:build (!localtest || !tpm12) && linux && cgo //go:build (!localtest || !tpm12) && cgo && !gofuzz
// +build !localtest !tpm12 // +build !localtest !tpm12
// +build linux
// +build cgo // +build cgo
// +build !gofuzz
package attest package attest
import ( import (
"bytes"
"crypto" "crypto"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"slices"
"testing" "testing"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts" "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) sim, tpm := setupSimulatedTPM(t)
defer sim.Close() 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 { if !*testLocal {
t.SkipNow() t.SkipNow()
} }
@ -45,11 +53,23 @@ func TestTPM20CertificationParameters(t *testing.T) {
t.Fatalf("OpenTPM() failed: %v", err) t.Fatalf("OpenTPM() failed: %v", err)
} }
defer tpm.Close() defer tpm.Close()
testCertificationParameters(t, tpm) testCertificationParameters(t, tpm, RSA)
} }
func testCertificationParameters(t *testing.T, tpm *TPM) { func TestTPM20CertificationParametersECC(t *testing.T) {
ak, err := tpm.NewAK(nil) 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -58,12 +78,12 @@ func testCertificationParameters(t *testing.T, tpm *TPM) {
if err != nil { if err != nil {
t.Fatal(err) 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()} pk, err := pub.Key()
hash, err := pub.RSAParameters.Sign.Hash.Hash() if err != nil {
t.Fatal(err)
}
hash, err := pub.NameAlg.Hash()
if err != nil { if err != nil {
t.Fatal(err) 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) sim, tpm := setupSimulatedTPM(t)
defer sim.Close() 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 { if !*testLocal {
t.SkipNow() t.SkipNow()
} }
@ -184,11 +210,32 @@ func TestTPM20KeyCertification(t *testing.T) {
t.Fatalf("OpenTPM() failed: %v", err) t.Fatalf("OpenTPM() failed: %v", err)
} }
defer tpm.Close() defer tpm.Close()
testKeyCertification(t, tpm) testKeyCertification(t, tpm, RSA)
} }
func testKeyCertification(t *testing.T, tpm *TPM) { func TestTPM20KeyCertificationECC(t *testing.T) {
ak, err := tpm.NewAK(nil) 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 { if err != nil {
t.Fatalf("NewAK() failed: %v", err) t.Fatalf("NewAK() failed: %v", err)
} }
@ -197,8 +244,11 @@ func testKeyCertification(t *testing.T, tpm *TPM) {
if err != nil { if err != nil {
t.Fatalf("DecodePublic() failed: %v", err) t.Fatalf("DecodePublic() failed: %v", err)
} }
pk := &rsa.PublicKey{E: int(pub.RSAParameters.Exponent()), N: pub.RSAParameters.Modulus()} pk, err := pub.Key()
hash, err := pub.RSAParameters.Sign.Hash.Hash() if err != nil {
t.Fatalf("pub.Key() failed: %v", err)
}
hash, err := pub.NameAlg.Hash()
if err != nil { if err != nil {
t.Fatalf("cannot access AK's hash function: %v", err) t.Fatalf("cannot access AK's hash function: %v", err)
} }
@ -209,6 +259,7 @@ func testKeyCertification(t *testing.T, tpm *TPM) {
for _, test := range []struct { for _, test := range []struct {
name string name string
opts *KeyConfig opts *KeyConfig
wantExtraData []byte
err error err error
}{ }{
{ {
@ -256,6 +307,26 @@ func testKeyCertification(t *testing.T, tpm *TPM) {
}, },
err: nil, 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) { t.Run(test.name, func(t *testing.T) {
sk, err := tpm.NewKey(ak, test.opts) sk, err := tpm.NewKey(ak, test.opts)
@ -264,6 +335,9 @@ func testKeyCertification(t *testing.T, tpm *TPM) {
} }
defer sk.Close() defer sk.Close()
p := sk.CertificationParameters() 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) err = p.Verify(verifyOpts)
if test.err == nil && err == nil { if test.err == nil && err == nil {
return 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)
}
})
}
}

View File

@ -4,9 +4,8 @@ import (
"bytes" "bytes"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/x509"
"testing" "testing"
"github.com/google/certificate-transparency-go/x509"
) )
func TestMakeActivationBlob(t *testing.T) { func TestMakeActivationBlob(t *testing.T) {

View File

@ -30,7 +30,7 @@ import (
// Ensure hashes are available. // Ensure hashes are available.
_ "crypto/sha256" _ "crypto/sha256"
"github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/legacy/tpm2"
"github.com/google/go-tpm/tpmutil" "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) 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. // 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 // 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) sigHash.Write(digest)
} }
for index, _ := range pcrByIndex { for index := range pcrByIndex {
if _, exists := quotePCRs[index]; !exists { if _, exists := quotePCRs[index]; !exists {
return fmt.Errorf("provided PCR %d was not included in quote", index) 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) return nil, fmt.Errorf("failed to parse spec ID event: %v", err)
} }
for _, alg := range specID.algs { for _, alg := range specID.algs {
switch tpm2.Algorithm(alg.ID) { el.Algs = append(el.Algs, HashAlg(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")
} }
// Switch to parsing crypto agile events. Don't include this in the // Switch to parsing crypto agile events. Don't include this in the
// replayed events since it intentionally doesn't extend the PCRs. // 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 { if err = binary.Read(r, binary.LittleEndian, &eventSize); err != nil {
return event, err return event, err
} }
if eventSize == 0 {
return event, errors.New("event data size is 0")
}
if eventSize > uint32(r.Len()) { if eventSize > uint32(r.Len()) {
return event, &eventSizeErr{eventSize, r.Len()} return event, &eventSizeErr{eventSize, r.Len()}
} }

View File

@ -18,10 +18,10 @@ import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"io/ioutil" "os"
"testing" "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. // 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) { func testParseEventLog(t *testing.T, testdata string) {
data, err := ioutil.ReadFile(testdata) data, err := os.ReadFile(testdata)
if err != nil { if err != nil {
t.Fatalf("reading test data: %v", err) t.Fatalf("reading test data: %v", err)
} }
@ -70,7 +70,7 @@ func testParseEventLog(t *testing.T, testdata string) {
} }
func TestParseCryptoAgileEventLog(t *testing.T) { func TestParseCryptoAgileEventLog(t *testing.T) {
data, err := ioutil.ReadFile("testdata/crypto_agile_eventlog") data, err := os.ReadFile("testdata/crypto_agile_eventlog")
if err != nil { if err != nil {
t.Fatalf("reading test data: %v", err) t.Fatalf("reading test data: %v", err)
} }
@ -88,7 +88,7 @@ func TestEventLog(t *testing.T) {
} }
func testEventLog(t *testing.T, testdata string) { func testEventLog(t *testing.T, testdata string) {
data, err := ioutil.ReadFile(testdata) data, err := os.ReadFile(testdata)
if err != nil { if err != nil {
t.Fatalf("reading test data: %v", err) 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) { func TestParseShortNoAction(t *testing.T) {
// https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf#page=110 // 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 // 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 // Currently we just assume that such events will have Data shorter than
// "EFI Specification ID" field. // "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 { if err != nil {
t.Fatalf("reading test data: %v", err) 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -340,7 +377,7 @@ func TestEBSVerifyWorkaround(t *testing.T) {
} }
func TestAppendEvents(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 { if err != nil {
t.Fatalf("reading test data: %v", err) t.Fatalf("reading test data: %v", err)
} }

View File

@ -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) { func TestExampleAK(t *testing.T) {
if !*testExamples { if !*testExamples {
t.SkipNow() t.SkipNow()
} }
ExampleAK() ExampleAK()
ExampleAK_credentialActivation() ExampleAK_credentialActivation()
ExampleAK_credentialActivationWithEK()
} }
func TestExampleTPM(t *testing.T) { func TestExampleTPM(t *testing.T) {

View File

@ -2,14 +2,12 @@ package internal
import ( import (
"bytes" "bytes"
"crypto/x509"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"unicode/utf16" "unicode/utf16"
"github.com/google/certificate-transparency-go/asn1"
"github.com/google/certificate-transparency-go/x509"
) )
const ( const (
@ -43,6 +41,8 @@ var (
shimLockGUID = efiGUID{0x605dab50, 0xe046, 0x4300, [8]byte{0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23}} shimLockGUID = efiGUID{0x605dab50, 0xe046, 0x4300, [8]byte{0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23}}
// "SbatLevel" encoded as UCS-2. // "SbatLevel" encoded as UCS-2.
shimSbatVarName = []uint16{0x53, 0x62, 0x61, 0x74, 0x4c, 0x65, 0x76, 0x65, 0x6c} 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. // EventType describes the type of event signalled in the event log.
@ -188,7 +188,7 @@ func (e EventType) String() string {
func UntrustedParseEventType(et uint32) (EventType, error) { func UntrustedParseEventType(et uint32) (EventType, error) {
// "The value associated with a UEFI specific platform event type MUST be in // "The value associated with a UEFI specific platform event type MUST be in
// the range between 0x80000000 and 0x800000FF, inclusive." // 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) return EventType(0), fmt.Errorf("event type not between [0x0, 0x12] or [0x80000000, 0x800000FF]: got %#x", et)
} }
if _, ok := eventTypeNames[EventType(et)]; !ok { 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 // https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf#page=1789
func ParseUEFIVariableAuthority(v UEFIVariableData) (UEFIVariableAuthority, error) { func ParseUEFIVariableAuthority(v UEFIVariableData) (UEFIVariableAuthority, error) {
if v.Header.VariableName == shimLockGUID && (
// Skip parsing new SBAT section logged by shim. // Skip parsing new SBAT section logged by shim.
// See https://github.com/rhboot/shim/blob/main/SBAT.md for more. // See https://github.com/rhboot/shim/blob/main/SBAT.md for more.
if v.Header.VariableName == shimLockGUID && unicodeNameEquals(v, shimSbatVarName) { unicodeNameEquals(v, shimSbatVarName) || //https://github.com/rhboot/shim/blob/20e4d9486fcae54ee44d2323ae342ffe68c920e6/include/sbat.h#L9-L12
//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 return UEFIVariableAuthority{}, nil
} }
certs, err := parseEfiSignature(v.VariableData) certs, err := parseEfiSignature(v.VariableData)
@ -444,7 +447,6 @@ func parseEfiSignature(b []byte) ([]x509.Certificate, error) {
} else { } else {
// A bug in shim may cause an event to be missing the SignatureOwner GUID. // 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. // We handle this, but signal back to the caller using ErrSigMissingGUID.
if _, isStructuralErr := err.(asn1.StructuralError); isStructuralErr {
var err2 error var err2 error
cert, err2 = x509.ParseCertificate(b) cert, err2 = x509.ParseCertificate(b)
if err2 == nil { if err2 == nil {
@ -452,7 +454,6 @@ func parseEfiSignature(b []byte) ([]x509.Certificate, error) {
err = ErrSigMissingGUID err = ErrSigMissingGUID
} }
} }
}
return certificates, err return certificates, err
} }

View File

@ -52,7 +52,7 @@ func (k *trousersKey12) close(tpm tpmBase) error {
return nil // No state for tpm 1.2. 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) t, ok := tb.(*trousersTPM)
if !ok { if !ok {
return nil, fmt.Errorf("expected *linuxTPM, got %T", tb) return nil, fmt.Errorf("expected *linuxTPM, got %T", tb)
@ -65,7 +65,7 @@ func (k *trousersKey12) activateCredential(tb tpmBase, in EncryptedCredential) (
return cred, nil 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) t, ok := tb.(*trousersTPM)
if !ok { if !ok {
return nil, fmt.Errorf("expected *linuxTPM, got %T", tb) 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 { if alg != HashSHA1 {
return nil, fmt.Errorf("only SHA1 algorithms supported on TPM 1.2, not %v", alg) 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) quote, rawSig, err := attestation.GetQuote(t.ctx, k.blob, nonce)
if err != nil { 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") return nil, fmt.Errorf("not implemented")
} }

View File

@ -20,8 +20,8 @@ package attest
import ( import (
"fmt" "fmt"
"github.com/google/go-tpm/legacy/tpm2"
tpm1 "github.com/google/go-tpm/tpm" tpm1 "github.com/google/go-tpm/tpm"
"github.com/google/go-tpm/tpm2"
) )
// windowsKey12 represents a Windows-managed key on a TPM1.2 TPM. // windowsKey12 represents a Windows-managed key on a TPM1.2 TPM.
@ -49,7 +49,7 @@ func (k *windowsKey12) marshal() ([]byte, error) {
return out.Serialize() 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) tpm, ok := t.(*windowsTPM)
if !ok { if !ok {
return nil, fmt.Errorf("expected *windowsTPM, got %T", t) 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) 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 { if alg != HashSHA1 {
return nil, fmt.Errorf("only SHA1 algorithms supported on TPM 1.2, not %v", alg) 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) 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[:]) sig, pcrc, err := tpm1.Quote(tpm, tpmKeyHnd, nonce, selectedPCRs[:], wellKnownAuth[:])
if err != nil { if err != nil {
return nil, fmt.Errorf("Quote() failed: %v", err) return nil, fmt.Errorf("Quote() failed: %v", err)
@ -112,7 +107,7 @@ func (k *windowsKey12) attestationParameters() AttestationParameters {
Public: k.public, 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") return nil, fmt.Errorf("not implemented")
} }
@ -152,7 +147,7 @@ func (k *windowsKey20) marshal() ([]byte, error) {
return out.Serialize() 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) tpm, ok := t.(*windowsTPM)
if !ok { if !ok {
return nil, fmt.Errorf("expected *windowsTPM, got %T", t) 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...)) 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) t, ok := tb.(*windowsTPM)
if !ok { if !ok {
return nil, fmt.Errorf("expected *windowsTPM, got %T", tb) 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 { if err != nil {
return nil, fmt.Errorf("TPMCommandInterface() failed: %v", err) 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 { 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) t, ok := tb.(*windowsTPM)
if !ok { if !ok {
return nil, fmt.Errorf("expected *windowsTPM, got %T", tb) 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, Alg: tpm2.AlgRSASSA,
Hash: tpm2.AlgSHA1, // PCP-created AK uses SHA1 Hash: tpm2.AlgSHA1, // PCP-created AK uses SHA1
} }
return certify(tpm, hnd, akHnd, scheme) return certify(tpm, hnd, akHnd, nil, scheme)
} }

View File

@ -19,14 +19,13 @@ package attest
import ( import (
"bytes" "bytes"
"crypto/x509"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
"syscall" "syscall"
"unsafe" "unsafe"
"github.com/google/certificate-transparency-go/x509"
"github.com/google/go-tpm/tpmutil" "github.com/google/go-tpm/tpmutil"
tpmtbs "github.com/google/go-tpm/tpmutil/tbs" tpmtbs "github.com/google/go-tpm/tpmutil/tbs"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
@ -370,8 +369,8 @@ func (h *winPCP) Close() error {
return closeNCryptObject(h.hProv) return closeNCryptObject(h.hProv)
} }
// DeleteKey permanently removes the key with the given handle // DeleteKey permanently removes the key with the given handle from the system,
// from the system, and frees its handle. // and frees its handle.
func (h *winPCP) DeleteKey(kh uintptr) error { func (h *winPCP) DeleteKey(kh uintptr) error {
r, _, msg := nCryptDeleteKey.Call(kh, 0) r, _, msg := nCryptDeleteKey.Call(kh, 0)
if r != 0 { if r != 0 {

View File

@ -16,10 +16,10 @@ package attest
import ( import (
"bytes" "bytes"
"crypto/x509"
"errors" "errors"
"fmt" "fmt"
"github.com/google/certificate-transparency-go/x509"
"github.com/google/go-attestation/attest/internal" "github.com/google/go-attestation/attest/internal"
) )

View File

@ -17,12 +17,12 @@ package attest
import ( import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"io/ioutil" "os"
"testing" "testing"
) )
func TestSecureBoot(t *testing.T) { 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 { if err != nil {
t.Fatalf("reading test data: %v", err) 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 // See: https://github.com/google/go-attestation/issues/157
func TestSecureBootBug157(t *testing.T) { func TestSecureBootBug157(t *testing.T) {
raw, err := ioutil.ReadFile("testdata/sb_cert_eventlog") raw, err := os.ReadFile("testdata/sb_cert_eventlog")
if err != nil { if err != nil {
t.Fatalf("reading test data: %v", err) t.Fatalf("reading test data: %v", err)
} }
@ -114,12 +114,12 @@ func TestSecureBootBug157(t *testing.T) {
events, err := elr.Verify(pcrs) events, err := elr.Verify(pcrs)
if err != nil { if err != nil {
t.Errorf("failed to verify log: %v", err) t.Fatalf("failed to verify log: %v", err)
} }
sbs, err := ParseSecurebootState(events) sbs, err := ParseSecurebootState(events)
if err != nil { 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 { if got, want := len(sbs.PostSeparatorAuthority), 3; got != want {
t.Errorf("len(sbs.PostSeparatorAuthority) = %d, want %d", 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) { func TestSecureBootOptionRom(t *testing.T) {
raw, err := ioutil.ReadFile("testdata/option_rom_eventlog") raw, err := os.ReadFile("testdata/option_rom_eventlog")
if err != nil { if err != nil {
t.Fatalf("reading test data: %v", err) t.Fatalf("reading test data: %v", err)
} }
@ -177,7 +177,26 @@ func TestSecureBootOptionRom(t *testing.T) {
} }
func TestSecureBootEventLogUbuntu(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 { if err != nil {
t.Fatalf("reading test data: %v", err) t.Fatalf("reading test data: %v", err)
} }

Binary file not shown.

View File

@ -18,17 +18,18 @@ import (
"bytes" "bytes"
"crypto/rsa" "crypto/rsa"
"crypto/sha256" "crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/base64" "encoding/base64"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
"net/url"
"strings" "strings"
"github.com/google/certificate-transparency-go/asn1" "github.com/google/go-tpm/legacy/tpm2"
"github.com/google/certificate-transparency-go/x509"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpmutil" "github.com/google/go-tpm/tpmutil"
"go.uber.org/multierr"
) )
const ( const (
@ -37,19 +38,21 @@ const (
tpmPtFwVersion1 = 0x00000100 + 11 // PT_FIXED + offset of 11 tpmPtFwVersion1 = 0x00000100 + 11 // PT_FIXED + offset of 11
// Defined in "Registry of reserved TPM 2.0 handles and localities". // Defined in "Registry of reserved TPM 2.0 handles and localities".
nvramCertIndex = 0x1c00002 nvramRSACertIndex = 0x1c00002
nvramEkNonceIndex = 0x1c00003 nvramRSAEkNonceIndex = 0x1c00003
nvramECCCertIndex = 0x1c0000a
nvramECCEkNonceIndex = 0x1c0000b
// Defined in "Registry of reserved TPM 2.0 handles and localities", and checked on a glinux machine. // Defined in "Registry of reserved TPM 2.0 handles and localities", and checked on a glinux machine.
commonSrkEquivalentHandle = 0x81000001 commonRSAEkEquivalentHandle = 0x81010001
commonEkEquivalentHandle = 0x81010001 commonECCEkEquivalentHandle = 0x81010002
) )
var ( var (
akTemplate = tpm2.Public{ akTemplateRSA = tpm2.Public{
Type: tpm2.AlgRSA, Type: tpm2.AlgRSA,
NameAlg: tpm2.AlgSHA256, NameAlg: tpm2.AlgSHA256,
Attributes: tpm2.FlagSignerDefault, Attributes: tpm2.FlagSignerDefault | tpm2.FlagNoDA,
RSAParameters: &tpm2.RSAParams{ RSAParameters: &tpm2.RSAParams{
Sign: &tpm2.SigScheme{ Sign: &tpm2.SigScheme{
Alg: tpm2.AlgRSASSA, Alg: tpm2.AlgRSASSA,
@ -58,7 +61,23 @@ var (
KeyBits: 2048, 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, Type: tpm2.AlgRSA,
NameAlg: tpm2.AlgSHA256, NameAlg: tpm2.AlgSHA256,
Attributes: tpm2.FlagStorageDefault | tpm2.FlagNoDA, Attributes: tpm2.FlagStorageDefault | tpm2.FlagNoDA,
@ -72,9 +91,26 @@ var (
KeyBits: 2048, 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 // https://trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf
defaultEKTemplate = tpm2.Public{ defaultRSAEKTemplate = tpm2.Public{
Type: tpm2.AlgRSA, Type: tpm2.AlgRSA,
NameAlg: tpm2.AlgSHA256, NameAlg: tpm2.AlgSHA256,
Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin |
@ -97,6 +133,32 @@ var (
ModulusRaw: make([]byte, 256), 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 // Basic template for an ECDSA key signing outside-TPM objects. Other
// fields are populated depending on the key creation options. // fields are populated depending on the key creation options.
ecdsaKeyTemplate = tpm2.Public{ ecdsaKeyTemplate = tpm2.Public{
@ -199,12 +261,12 @@ func ParseEKCertificate(ekCert []byte) (*x509.Certificate, error) {
var cert struct { var cert struct {
Raw asn1.RawContent 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) return nil, fmt.Errorf("asn1.Unmarshal() failed: %v, wasWrapped=%v", err, wasWrapped)
} }
c, err := x509.ParseCertificate(cert.Raw) 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 nil, fmt.Errorf("x509.ParseCertificate() failed: %v", err)
} }
return c, nil return c, nil
@ -220,23 +282,23 @@ func intelEKURL(ekPub *rsa.PublicKey) string {
pubHash.Write(ekPub.N.Bytes()) pubHash.Write(ekPub.N.Bytes())
pubHash.Write([]byte{0x1, 0x00, 0x01}) 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) { func readEKCertFromNVRAM20(tpm io.ReadWriter, nvramCertIndex tpmutil.Handle) (*x509.Certificate, error) {
ekCert, err := tpm2.NVReadEx(tpm, nvramCertIndex, tpm2.HandleOwner, "", 0) // 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 { if err != nil {
return nil, fmt.Errorf("reading EK cert: %v", err) return nil, fmt.Errorf("reading EK cert: %v", err)
} }
return ParseEKCertificate(ekCert) return ParseEKCertificate(ekCert)
} }
func quote20(tpm io.ReadWriter, akHandle tpmutil.Handle, hashAlg tpm2.Algorithm, nonce []byte) (*Quote, error) { func quote20(tpm io.ReadWriter, akHandle tpmutil.Handle, hashAlg tpm2.Algorithm, nonce []byte, selectedPCRs []int) (*Quote, error) {
sel := tpm2.PCRSelection{Hash: hashAlg} sel := tpm2.PCRSelection{Hash: hashAlg,
numPCRs := 24 PCRs: selectedPCRs}
for pcr := 0; pcr < numPCRs; pcr++ {
sel.PCRs = append(sel.PCRs, pcr)
}
quote, sig, err := tpm2.Quote(tpm, akHandle, "", "", nonce, sel, tpm2.AlgNull) quote, sig, err := tpm2.Quote(tpm, akHandle, "", "", nonce, sel, tpm2.AlgNull)
if err != nil { if err != nil {
@ -251,6 +313,31 @@ func quote20(tpm io.ReadWriter, akHandle tpmutil.Handle, hashAlg tpm2.Algorithm,
}, err }, 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) { func readAllPCRs20(tpm io.ReadWriter, alg tpm2.Algorithm) (map[uint32][]byte, error) {
numPCRs := 24 numPCRs := 24
out := map[uint32][]byte{} out := map[uint32][]byte{}
@ -294,17 +381,22 @@ type tpmBase interface {
close() error close() error
tpmVersion() TPMVersion tpmVersion() TPMVersion
eks() ([]EK, error) eks() ([]EK, error)
ekCertificates() ([]EK, error)
info() (*TPMInfo, error) info() (*TPMInfo, error)
pcrbanks() ([]HashAlg, error)
loadAK(opaqueBlob []byte) (*AK, error) loadAK(opaqueBlob []byte) (*AK, error)
loadAKWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*AK, error)
newAK(opts *AKConfig) (*AK, error) newAK(opts *AKConfig) (*AK, error)
loadKey(opaqueBlob []byte) (*Key, error) loadKey(opaqueBlob []byte) (*Key, error)
loadKeyWithParent(opaqueBlob []byte, parent ParentKeyConfig) (*Key, error)
newKey(ak *AK, opts *KeyConfig) (*Key, error) newKey(ak *AK, opts *KeyConfig) (*Key, error)
newKeyCertifiedByKey(ck certifyingKey, opts *KeyConfig) (*Key, error)
pcrs(alg HashAlg) ([]PCR, error) pcrs(alg HashAlg) ([]PCR, error)
measurementLog() ([]byte, 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 { type TPM struct {
// tpm refers to a concrete implementation of TPM logic, based on the current // tpm refers to a concrete implementation of TPM logic, based on the current
// platform and TPM version. // platform and TPM version.
@ -317,10 +409,20 @@ func (t *TPM) Close() error {
} }
// EKs returns the endorsement keys burned-in to the platform. // 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) { func (t *TPM) EKs() ([]EK, error) {
return t.tpm.eks() 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. // Info returns information about the TPM.
func (t *TPM) Info() (*TPMInfo, error) { func (t *TPM) Info() (*TPMInfo, error) {
return t.tpm.info() 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. // LoadAK loads a previously-created ak into the TPM for use.
// A key loaded via this function needs to be closed with .Close(). // 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. // to this function.
func (t *TPM) LoadAK(opaqueBlob []byte) (*AK, error) { func (t *TPM) LoadAK(opaqueBlob []byte) (*AK, error) {
return t.tpm.loadAK(opaqueBlob) 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. // MeasurementLog returns the present value of the System Measurement Log.
// //
// This is a low-level API. Consumers seeking to attest the state of the // 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) 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. // LoadKey loads a previously-created application key into the TPM for use.
// A key loaded via this function needs to be closed with .Close(). // 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. // to this function.
func (t *TPM) LoadKey(opaqueBlob []byte) (*Key, error) { func (t *TPM) LoadKey(opaqueBlob []byte) (*Key, error) {
return t.tpm.loadKey(opaqueBlob) return t.tpm.loadKey(opaqueBlob)
@ -386,11 +510,20 @@ func (t *TPM) PCRs(alg HashAlg) ([]PCR, error) {
return t.tpm.pcrs(alg) 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) { func (t *TPM) attestPCRs(ak *AK, nonce []byte, alg HashAlg) (*Quote, []PCR, error) {
pcrs, err := t.PCRs(alg) pcrs, err := t.PCRs(alg)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to read %v PCRs: %v", alg, err) return nil, nil, fmt.Errorf("failed to read %v PCRs: %v", alg, err)
} }
quote, err := ak.Quote(t, nonce, alg) quote, err := ak.Quote(t, nonce, alg)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to quote using %v: %v", alg, err) 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, EventLog: eventLog,
} }
algs := []HashAlg{HashSHA1} algs, err := t.PCRBanks()
if t.Version() == TPMVersion20 { if err != nil {
algs = []HashAlg{HashSHA1, HashSHA256} return nil, fmt.Errorf("failed to get PCR banks: %w", err)
} }
var lastErr error var lastErr error

View File

@ -19,11 +19,11 @@ package attest
import ( import (
"crypto" "crypto"
"crypto/x509"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io/ioutil" "os"
"github.com/google/certificate-transparency-go/x509"
"github.com/google/go-tspi/attestation" "github.com/google/go-tspi/attestation"
"github.com/google/go-tspi/tspi" "github.com/google/go-tspi/tspi"
"github.com/google/go-tspi/tspiconst" "github.com/google/go-tspi/tspiconst"
@ -94,7 +94,7 @@ func readEKCertFromNVRAM12(ctx *tspi.Context) (*x509.Certificate, error) {
return ParseEKCertificate(ekCert) return ParseEKCertificate(ekCert)
} }
func (t *trousersTPM) eks() ([]EK, error) { func (t *trousersTPM) ekCertificates() ([]EK, error) {
cert, err := readEKCertFromNVRAM12(t.ctx) cert, err := readEKCertFromNVRAM12(t.ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("readEKCertFromNVRAM failed: %v", err) return nil, fmt.Errorf("readEKCertFromNVRAM failed: %v", err)
@ -104,14 +104,26 @@ func (t *trousersTPM) eks() ([]EK, error) {
}, nil }, nil
} }
func (t *trousersTPM) eks() ([]EK, error) {
return t.ekCertificates()
}
func (t *trousersTPM) newKey(*AK, *KeyConfig) (*Key, error) { func (t *trousersTPM) newKey(*AK, *KeyConfig) (*Key, error) {
return nil, fmt.Errorf("not implemented") 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) { func (t *trousersTPM) loadKey(opaqueBlob []byte) (*Key, error) {
return nil, fmt.Errorf("not implemented") 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) { func (t *trousersTPM) newAK(opts *AKConfig) (*AK, error) {
pub, blob, err := attestation.CreateAIK(t.ctx) pub, blob, err := attestation.CreateAIK(t.ctx)
if err != nil { if err != nil {
@ -132,6 +144,10 @@ func (t *trousersTPM) loadAK(opaqueBlob []byte) (*AK, error) {
return &AK{ak: newTrousersKey12(sKey.Blob, sKey.Public)}, nil 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 // allPCRs12 returns a map of all the PCR values on the TPM
func allPCRs12(ctx *tspi.Context) (map[uint32][]byte, error) { func allPCRs12(ctx *tspi.Context) (map[uint32][]byte, error) {
tpm := ctx.GetTPM() tpm := ctx.GetTPM()
@ -147,6 +163,10 @@ func allPCRs12(ctx *tspi.Context) (map[uint32][]byte, error) {
return PCRs, nil return PCRs, nil
} }
func (t *trousersTPM) pcrbanks() ([]HashAlg, error) {
return []HashAlg{HashSHA1}, nil
}
func (t *trousersTPM) pcrs(alg HashAlg) ([]PCR, error) { func (t *trousersTPM) pcrs(alg HashAlg) ([]PCR, error) {
if alg != HashSHA1 { if alg != HashSHA1 {
return nil, fmt.Errorf("non-SHA1 algorithm %v is not supported on TPM 1.2", alg) 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) { 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")
} }

View File

@ -21,12 +21,11 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path" "path"
"strings" "strings"
"github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/legacy/tpm2"
) )
const ( const (
@ -49,7 +48,7 @@ func InjectSimulatedTPMForTest(rwc io.ReadWriteCloser) *TPM {
func probeSystemTPMs() ([]probedTPM, error) { func probeSystemTPMs() ([]probedTPM, error) {
var tpms []probedTPM var tpms []probedTPM
tpmDevs, err := ioutil.ReadDir(tpmRoot) tpmDevs, err := os.ReadDir(tpmRoot)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return nil, err return nil, err
} }
@ -82,7 +81,7 @@ type linuxCmdChannel struct {
// MeasurementLog implements CommandChannelTPM20. // MeasurementLog implements CommandChannelTPM20.
func (cc *linuxCmdChannel) MeasurementLog() ([]byte, error) { 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) { 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 // If the TPM has a kernel-provided resource manager, we should
// use that instead of communicating directly. // use that instead of communicating directly.
devPath := path.Join("/dev", path.Base(tpm.Path)) 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 err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
return nil, err return nil, err

View File

@ -2,25 +2,28 @@ package attest
import ( import (
"crypto/rsa" "crypto/rsa"
"crypto/x509"
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"testing" "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----- var testRSAKey = mustParseRSAKey(`-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq8zyTXCjVALZzjS8wgNH MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwyDi8kSoYBqs8+AdJsZl
nAVdt4ZGM3N450xOnLplx/RbCVwXyu83SWh0B3Ka+92aocqcHzo+j6e6Urppre/I JJk1Vi3h2hl+nn8HbEaWE8+2U+mOwsOG/B0TPyyMbMM4tzLwsgi9g4qHej5bvD4d
+7VVKTdUAr8t5gxgSLGvo+ev+zv70GF4DmJthb8JNheHCmk3RnoSFs5TnDuSdvGb QIToNcfIkGocBbTS0w/b68HbrZUPprFlvUtqhkYDFGFkwMT1nUiQEe8fko3upukA
KcSzas0186LQyxvwfFjTxLweGrZKh/CTewD0/f5ozXmbTtJpl+qYrMi9GJamGlg6 YfPTdeVkYnMVHvYiJSCYvhpKsB3AoSInxgn9rOsRWvQI1Gk6b0mRl3RpWwwSvBih
N6EsWKh1xos8J/cEmS2vbyCGGADyBwRV8Zkto5EU1HJaEli10HVZf0D06vuKzzxM /3EgpzN7L7XxlR2Lt/CU1bVUwRyVI7MHKf5keH0KE7nmMEiNq039hmNKUnDscvzF
+6W7LzGqzAPeaWvHi07ezShqdr5q5y1KKhFJcy8HOpwN8iFfIw70y3FtMlrMprrU pE3GeajzKTjdgZfina6Dn1tMoPXeJ8lSLCPFThws5XhZUlEYvURwsYGA7veK5CZ7
twIDAQAB zQIDAQAB
-----END PUBLIC KEY-----`) -----END PUBLIC KEY-----`)
func mustParseRSAKey(data string) *rsa.PublicKey { func mustParseRSAKey(data string) *rsa.PublicKey {
@ -47,7 +50,7 @@ func parseRSAKey(data string) (*rsa.PublicKey, error) {
} }
func TestIntelEKURL(t *testing.T) { func TestIntelEKURL(t *testing.T) {
want := "https://ekop.intel.com/ekcertservice/7YtWV2nT3LpvSCfJt7ENIznN1R1jYkj_3S6mez3yyzg=" want := "https://ekop.intel.com/ekcertservice/WVEG2rRwkQ7m3RpXlUphgo6Y2HLxl18h6ZZkkOAdnBE%3D"
got := intelEKURL(testRSAKey) got := intelEKURL(testRSAKey)
if got != want { if got != want {
t.Fatalf("intelEKURL(), got=%q, want=%q", got, want) t.Fatalf("intelEKURL(), got=%q, want=%q", got, want)

View File

@ -152,6 +152,18 @@ func (t *windowsTPM) info() (*TPMInfo, error) {
return &tInfo, nil 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) { func (t *windowsTPM) eks() ([]EK, error) {
ekCerts, err := t.pcp.EKCerts() ekCerts, err := t.pcp.EKCerts()
if err != nil { 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) { func (t *windowsTPM) newKey(*AK, *KeyConfig) (*Key, error) {
return nil, fmt.Errorf("not implemented") 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) { func (t *windowsTPM) loadKey(opaqueBlob []byte) (*Key, error) {
return nil, fmt.Errorf("not implemented") 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) { func allPCRs12(tpm io.ReadWriter) (map[uint32][]byte, error) {
numPCRs := 24 numPCRs := 24
out := map[uint32][]byte{} out := map[uint32][]byte{}
@ -352,6 +376,23 @@ func allPCRs12(tpm io.ReadWriter) (map[uint32][]byte, error) {
return out, nil 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) { func (t *windowsTPM) pcrs(alg HashAlg) ([]PCR, error) {
var PCRs map[uint32][]byte var PCRs map[uint32][]byte

View File

@ -164,7 +164,7 @@ type WinEvents struct {
// BootCount contains the value of the monotonic boot counter. This // BootCount contains the value of the monotonic boot counter. This
// value is not set for TPM 1.2 devices and some TPMs with buggy // value is not set for TPM 1.2 devices and some TPMs with buggy
// implementations of monotonic counters. // implementations of monotonic counters.
BootCount int BootCount uint64
// LoadedModules contains authenticode hashes for binaries which // LoadedModules contains authenticode hashes for binaries which
// were loaded during boot. // were loaded during boot.
LoadedModules map[string]WinModuleLoad 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) return nil, fmt.Errorf("invalid tagged event structure at event %d: %w", e.sequence, err)
} }
if digestVerify != nil { 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 { if err := out.readWinEventBlock(s, e.Index); err != nil {
return nil, fmt.Errorf("invalid SIPA events in event %d: %w", e.sequence, err) 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) return nil, fmt.Errorf("invalid tagged event structure at event %d: %w", e.sequence, err)
} }
if digestVerify != nil { 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 { if err := out.readWinEventBlock(s, e.Index); err != nil {
return nil, fmt.Errorf("invalid SIPA events in event %d: %w", e.sequence, err) 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 return nil
} }
func (w *WinEvents) readUint(header microsoftEventHeader, r io.Reader) (uint64, error) { func (w *WinEvents) readUint32(header microsoftEventHeader, r io.Reader) (uint32, error) {
if header.Size > 8 { if header.Size != 4 {
return 0, fmt.Errorf("integer too large (%d bytes)", header.Size) return 0, fmt.Errorf("integer size not uint32 (%d bytes)", header.Size)
} }
data := make([]uint8, header.Size) data := make([]uint8, header.Size)
if err := binary.Read(r, binary.LittleEndian, &data); err != nil { 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) i := binary.LittleEndian.Uint32(data)
if n <= 0 {
return 0, fmt.Errorf("reading u%d: invalid varint", header.Size<<8) 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 return i, nil
} }
func (w *WinEvents) readBootCounter(header microsoftEventHeader, r *bytes.Reader) error { 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 { if err != nil {
return fmt.Errorf("boot counter: %v", err) 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) return fmt.Errorf("conflicting values for boot counter: %d != %d", i, w.BootCount)
} }
w.BootCount = int(i) w.BootCount = i
return nil return nil
} }
func (w *WinEvents) readTransferControl(header microsoftEventHeader, r *bytes.Reader) error { 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 { if err != nil {
return fmt.Errorf("transfer control: %v", err) 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) { 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 { if err != nil {
return 0, fmt.Errorf("hash algorithm ID: %v", err) 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 { if imgSize != 0 {
return errors.New("duplicate image size in LMA event") 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 return err
} }
case hashAlgorithmID: case hashAlgorithmID:
@ -589,7 +600,7 @@ func (w *WinEvents) readLoadedModuleAggregation(rdr *bytes.Reader, header micros
return err return err
} }
case imageValidated: case imageValidated:
if imgValidated == true { if imgValidated {
return errors.New("duplicate image validated field in LMA event") return errors.New("duplicate image validated field in LMA event")
} }
if imgValidated, err = w.parseImageValidated(h, r); err != nil { 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 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 ( var (
r = &io.LimitedReader{R: rdr, N: int64(header.Size)} r = &io.LimitedReader{R: rdr, N: int64(header.Size)}
driverName string driverName string
@ -687,6 +698,11 @@ func (w *WinEvents) readELAMAggregation(rdr *bytes.Reader, header microsoftEvent
var err error var err error
switch h.Type { switch h.Type {
case elamAggregation:
w.readELAMAggregation(r, h)
if r.N == 0 {
return nil
}
case elamKeyname: case elamKeyname:
if driverName != "" { if driverName != "" {
return errors.New("duplicate driver name in ELAM aggregation event") return errors.New("duplicate driver name in ELAM aggregation event")

View File

@ -16,7 +16,7 @@ package attest
import ( import (
"encoding/json" "encoding/json"
"io/ioutil" "os"
"testing" "testing"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
@ -31,9 +31,10 @@ func TestParseWinEvents(t *testing.T) {
CodeIntegrityEnabled: TernaryTrue, CodeIntegrityEnabled: TernaryTrue,
BitlockerUnlocks: []BitlockerStatus{0, 0}, BitlockerUnlocks: []BitlockerStatus{0, 0},
LoadedModules: map[string]WinModuleLoad{ LoadedModules: map[string]WinModuleLoad{
"0fdce7d71936f79445e7d2c84cbeb97c948d3730e0b839166b0a4e625c2d4547": WinModuleLoad{ "0fdce7d71936f79445e7d2c84cbeb97c948d3730e0b839166b0a4e625c2d4547": {
FilePath: `\Windows\System32\drivers\vioscsi.sys`, FilePath: `\Windows\System32\drivers\vioscsi.sys`,
ImageBase: []uint64{81416192}, ImageBase: []uint64{81416192},
ImageSize: uint64(86016),
HashAlgorithm: WinAlgSHA256, HashAlgorithm: WinAlgSHA256,
ImageValidated: true, ImageValidated: true,
AuthorityIssuer: "Microsoft Windows Third Party Component CA 2014", 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}, 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`, FilePath: `\Windows\System32\Drivers\ksecpkg.sys`,
ImageBase: []uint64{82952192}, ImageBase: []uint64{82952192},
ImageSize: uint64(204800),
HashAlgorithm: WinAlgSHA256, HashAlgorithm: WinAlgSHA256,
ImageValidated: true, ImageValidated: true,
AuthorityIssuer: "Microsoft Windows Production PCA 2011", 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}, 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`, FilePath: `\Windows\System32\drivers\volmgrx.sys`,
ImageBase: []uint64{80875520}, ImageBase: []uint64{80875520},
ImageSize: uint64(405504),
HashAlgorithm: WinAlgSHA256, HashAlgorithm: WinAlgSHA256,
ImageValidated: true, ImageValidated: true,
AuthorityIssuer: "Microsoft Windows Production PCA 2011", AuthorityIssuer: "Microsoft Windows Production PCA 2011",
@ -84,11 +87,11 @@ func TestParseWinEvents(t *testing.T) {
}, },
}, },
ELAM: map[string]WinELAM{ 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 { if err != nil {
t.Fatalf("reading test data: %v", err) t.Fatalf("reading test data: %v", err)
} }
@ -118,7 +121,7 @@ func TestParseWinEvents(t *testing.T) {
"055a36a9921b98cc04042ca95249c7eca655536868dafcec7508947ebe5e71f4": true, "055a36a9921b98cc04042ca95249c7eca655536868dafcec7508947ebe5e71f4": true,
"2bedd1589410b6fa13c82f35db735025b6a160595922750248771f5abd0fee58": true, "2bedd1589410b6fa13c82f35db735025b6a160595922750248771f5abd0fee58": true,
} }
for k, _ := range winState.LoadedModules { for k := range winState.LoadedModules {
if _, keep := keep[k]; !keep { if _, keep := keep[k]; !keep {
delete(winState.LoadedModules, k) delete(winState.LoadedModules, k)
} }

View File

@ -18,14 +18,15 @@ import (
"bytes" "bytes"
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa" "crypto/rsa"
"encoding/asn1"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"math/big" "math/big"
"github.com/google/certificate-transparency-go/asn1" "github.com/google/go-tpm/legacy/tpm2"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpmutil" "github.com/google/go-tpm/tpmutil"
) )
@ -33,27 +34,49 @@ import (
type wrappedTPM20 struct { type wrappedTPM20 struct {
interf TPMInterface interf TPMInterface
rwc CommandChannelTPM20 rwc CommandChannelTPM20
tpmEkTemplate *tpm2.Public tpmRSAEkTemplate *tpm2.Public
tpmECCEkTemplate *tpm2.Public
} }
func (t *wrappedTPM20) ekTemplate() (tpm2.Public, error) { // certifyingKey contains details of a TPM key that could certify other keys.
if t.tpmEkTemplate != nil { type certifyingKey struct {
return *t.tpmEkTemplate, nil 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 { if err != nil {
t.tpmEkTemplate = &defaultEKTemplate // No nonce, use the default template t.tpmRSAEkTemplate = &defaultRSAEKTemplate // No nonce, use the default template
} else { } else {
template := defaultEKTemplate template := defaultRSAEKTemplate
copy(template.RSAParameters.ModulusRaw, nonce) 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 { func (t *wrappedTPM20) tpmVersion() TPMVersion {
return TPMVersion20 return TPMVersion20
@ -84,52 +107,105 @@ func (t *wrappedTPM20) info() (*TPMInfo, error) {
return &tInfo, nil return &tInfo, nil
} }
// Return value: handle, whether we generated a new one, error // Return value: handle, whether we generated a new one, error.
func (t *wrappedTPM20) getPrimaryKeyHandle(pHnd tpmutil.Handle) (tpmutil.Handle, bool, error) { func (t *wrappedTPM20) getEndorsementKeyHandle(ek *EK) (tpmutil.Handle, bool, error) {
_, _, _, err := tpm2.ReadPublic(t.rwc, pHnd) var ekHandle tpmutil.Handle
if err == nil { var ekTemplate tpm2.Public
// Found the persistent handle, assume it's the key we want.
return pHnd, false, nil 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 _, _, _, err := tpm2.ReadPublic(t.rwc, ekHandle)
switch pHnd { if err == nil {
case commonSrkEquivalentHandle: // Found the persistent handle, assume it's the key we want.
keyHnd, _, err = tpm2.CreatePrimary(t.rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", defaultSRKTemplate) return ekHandle, false, nil
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)
} }
rerr := err // Preserve this failure for later logging, if needed
keyHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", ekTemplate)
if err != nil { 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) 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 { if err != nil {
return 0, false, fmt.Errorf("EvictControl failed: %v", err) 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) { 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{ return []EK{
{Public: crypto.PublicKey(cert.PublicKey), Certificate: cert}, {Public: crypto.PublicKey(cert.PublicKey), Certificate: cert, handle: commonRSAEkEquivalentHandle},
}, nil }, nil
} }
// Attempt to create an EK. // Attempt to create an EK.
tmpl, err := t.ekTemplate() ekHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", t.rsaEkTemplate())
if err != nil {
return nil, fmt.Errorf("ek template: %v", err)
}
ekHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", tmpl)
if err != nil { if err != nil {
return nil, fmt.Errorf("EK CreatePrimary failed: %v", err) return nil, fmt.Errorf("EK CreatePrimary failed: %v", err)
} }
@ -142,23 +218,50 @@ func (t *wrappedTPM20) eks() ([]EK, error) {
if pub.RSAParameters == nil { if pub.RSAParameters == nil {
return nil, errors.New("ECC EK not yet supported") return nil, errors.New("ECC EK not yet supported")
} }
return []EK{
{ i, err := t.info()
Public: &rsa.PublicKey{ if err != nil {
return nil, fmt.Errorf("Retrieving TPM info failed: %v", err)
}
ekPub := &rsa.PublicKey{
E: int(pub.RSAParameters.Exponent()), E: int(pub.RSAParameters.Exponent()),
N: pub.RSAParameters.Modulus(), N: pub.RSAParameters.Modulus(),
}, }
var certificateURL string
if i.Manufacturer.String() == manufacturerIntel {
certificateURL = intelEKURL(ekPub)
}
return []EK{
{
Public: ekPub,
CertificateURL: certificateURL,
handle: commonRSAEkEquivalentHandle,
}, },
}, nil }, nil
} }
func (t *wrappedTPM20) newAK(opts *AKConfig) (*AK, error) { func (t *wrappedTPM20) newAK(opts *AKConfig) (*AK, error) {
// TODO(jsonp): Abstract choice of hierarchy & parent. var parent ParentKeyConfig
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle) if opts != nil && opts.Parent != nil {
parent = *opts.Parent
} else {
parent = defaultParentConfig
}
srk, _, err := t.getStorageRootKeyHandle(parent)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get SRK handle: %v", err) 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) blob, pub, creationData, creationHash, tix, err := tpm2.CreateKey(t.rwc, srk, tpm2.PCRSelection{}, "", "", akTemplate)
if err != nil { if err != nil {
return nil, fmt.Errorf("CreateKeyEx() failed: %v", err) 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. // 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 { if err != nil {
return nil, fmt.Errorf("CertifyCreation failed: %v", err) return nil, fmt.Errorf("CertifyCreation failed: %v", err)
} }
// Pack the raw structure into a TPMU_SIGNATURE. return &AK{ak: newWrappedAK20(keyHandle, blob, pub, creationData, attestation, sig)}, nil
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
} }
func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) { 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) 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) parent, blob, pub, creationData, err := createKey(t, opts)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot create key: %v", err) 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 // 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 { 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) { if !bytes.Equal(pub, cp.Public) {
return nil, fmt.Errorf("certified incorrect key, expected: %v, certified: %v", 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) { 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 { if err != nil {
return 0, nil, nil, nil, fmt.Errorf("failed to get SRK handle: %v", err) 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 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) sKey, err := deserializeKey(opaqueBlob, TPMVersion20)
if err != nil { if err != nil {
return 0, nil, fmt.Errorf("deserializeKey() failed: %v", err) 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) return 0, nil, fmt.Errorf("unsupported key encoding: %x", sKey.Encoding)
} }
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle) srk, _, err := t.getStorageRootKeyHandle(parent)
if err != nil { if err != nil {
return 0, nil, fmt.Errorf("failed to get SRK handle: %v", err) 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) { 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 { if err != nil {
return nil, fmt.Errorf("cannot load attestation key: %v", err) 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) { 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 { if err != nil {
return nil, fmt.Errorf("cannot load signing key: %v", err) 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 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) { func (t *wrappedTPM20) pcrs(alg HashAlg) ([]PCR, error) {
PCRs, err := readAllPCRs20(t.rwc, alg.goTPMAlg()) PCRs, err := readAllPCRs20(t.rwc, alg.goTPMAlg())
if err != nil { if err != nil {
@ -416,7 +542,7 @@ func (k *wrappedKey20) close(t tpmBase) error {
return tpm2.FlushContext(tpm.rwc, k.hnd) 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) t, ok := tb.(*wrappedTPM20)
if !ok { if !ok {
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb) 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:] secret := in.Secret[2:]
ekHnd, _, err := t.getPrimaryKeyHandle(commonEkEquivalentHandle) ekHnd, _, err := t.getEndorsementKeyHandle(ek)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -450,7 +576,7 @@ func (k *wrappedKey20) activateCredential(tb tpmBase, in EncryptedCredential) ([
} }
defer tpm2.FlushContext(t.rwc, sessHandle) 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) 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) }, 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) t, ok := tb.(*wrappedTPM20)
if !ok { if !ok {
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb) return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb)
@ -469,19 +624,19 @@ func (k *wrappedKey20) certify(tb tpmBase, handle interface{}) (*CertificationPa
if !ok { if !ok {
return nil, fmt.Errorf("expected tpmutil.Handle, got %T", handle) return nil, fmt.Errorf("expected tpmutil.Handle, got %T", handle)
} }
scheme := tpm2.SigScheme{ scheme, err := sigSchemeFromAlgorithm(ck.alg)
Alg: tpm2.AlgRSASSA, if err != nil {
Hash: tpm2.AlgSHA256, 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) t, ok := tb.(*wrappedTPM20)
if !ok { if !ok {
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb) 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 { func (k *wrappedKey20) attestationParameters() AttestationParameters {
@ -506,16 +661,31 @@ func (k *wrappedKey20) sign(tb tpmBase, digest []byte, pub crypto.PublicKey, opt
if !ok { if !ok {
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb) return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb)
} }
switch pub.(type) { switch p := pub.(type) {
case *ecdsa.PublicKey: case *ecdsa.PublicKey:
return signECDSA(t.rwc, k.hnd, digest) return signECDSA(t.rwc, k.hnd, digest, p.Curve)
case *rsa.PublicKey: case *rsa.PublicKey:
return signRSA(t.rwc, k.hnd, digest, opts) return signRSA(t.rwc, k.hnd, digest, opts)
} }
return nil, fmt.Errorf("unsupported signing key type: %T", pub) 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) sig, err := tpm2.Sign(rw, key, "", digest, nil, nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot sign: %v", err) 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) { func (k *wrappedKey20) blobs() ([]byte, []byte, error) {
return k.public, k.blob, nil 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)
}
}

View File

@ -9,15 +9,15 @@ package attributecert
import ( import (
"bytes" "bytes"
"crypto" "crypto"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"time" "time"
"github.com/google/go-attestation/oid" "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 ( var (
@ -37,6 +37,7 @@ var (
oidSignatureRSASha1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} oidSignatureRSASha1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10} oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
oidSignatureRSASha256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} 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} oidSignatureEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112}
oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1} 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.SHA1WithRSA, "SHA1-RSA", oidSignatureRSASha1, x509.RSA, crypto.SHA1},
{x509.SHA256WithRSA, "SHA256-RSA", oidSignatureRSASha256, x509.RSA, crypto.SHA256}, {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.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256},
{x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384}, {x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384},
{x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512}, {x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512},
@ -129,50 +131,50 @@ func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgo
return x509.UnknownSignatureAlgorithm return x509.UnknownSignatureAlgorithm
} }
//RFC 5280 4.2.2.1 // RFC 5280 4.2.2.1
type authorityInfoAccess struct { type authorityInfoAccess struct {
Method asn1.ObjectIdentifier Method asn1.ObjectIdentifier
Location asn1.RawValue Location asn1.RawValue
} }
//RFC 5280 4.2.1.1 // RFC 5280 4.2.1.1
type authKeyID struct { type authKeyID struct {
ID []byte `asn1:"optional,tag:0"` ID []byte `asn1:"optional,tag:0"`
IssuerName asn1.RawValue `asn1:"set,optional,tag:1"` IssuerName asn1.RawValue `asn1:"set,optional,tag:1"`
SerialNumber *big.Int `asn1:"optional,tag:2"` SerialNumber *big.Int `asn1:"optional,tag:2"`
} }
//RFC 5280 4.2.1.4 // RFC 5280 4.2.1.4
type cpsPolicy struct { type cpsPolicy struct {
ID asn1.ObjectIdentifier ID asn1.ObjectIdentifier
Value string Value string
} }
//RFC 5280 4.2.1.4 // RFC 5280 4.2.1.4
type policyInformation struct { type policyInformation struct {
Raw asn1.RawContent Raw asn1.RawContent
ID asn1.ObjectIdentifier ID asn1.ObjectIdentifier
Policy asn1.RawValue Policy asn1.RawValue
} }
//RFC 5280 4.1.2.5 // RFC 5280 4.1.2.5
type validity struct { type validity struct {
NotBefore, NotAfter time.Time NotBefore, NotAfter time.Time
} }
//RFC 5280 4.2.1.4 // RFC 5280 4.2.1.4
type NoticeReference struct { type noticeReference struct {
Organization string Organization string
NoticeNumbers []int NoticeNumbers []int
} }
//RFC 5280 4.2.1.4 // RFC 5280 4.2.1.4
type userNotice struct { type userNotice struct {
NoticeRef NoticeReference `asn1:"optional"` NoticeRef noticeReference `asn1:"optional"`
ExplicitText string `asn1:"optional"` ExplicitText string `asn1:"optional"`
} }
//RFC 5755 4.1 // RFC 5755 4.1
type objectDigestInfo struct { type objectDigestInfo struct {
DigestedObjectType asn1.Enumerated DigestedObjectType asn1.Enumerated
OtherObjectTypeID asn1.ObjectIdentifier OtherObjectTypeID asn1.ObjectIdentifier
@ -180,14 +182,14 @@ type objectDigestInfo struct {
ObjectDigest asn1.BitString ObjectDigest asn1.BitString
} }
//RFC 5755 4.1 // RFC 5755 4.1
type attCertIssuer struct { type attCertIssuer struct {
IssuerName asn1.RawValue `asn1:"set,optional"` IssuerName asn1.RawValue `asn1:"set,optional"`
BaseCertificateID issuerSerial `asn1:"optional,tag:0"` BaseCertificateID issuerSerial `asn1:"optional,tag:0"`
ObjectDigestInfo objectDigestInfo `asn1:"optional,tag:1"` ObjectDigestInfo objectDigestInfo `asn1:"optional,tag:1"`
} }
//RFC 5755 4.1 // RFC 5755 4.1
type issuerSerial struct { type issuerSerial struct {
Raw asn1.RawContent Raw asn1.RawContent
Issuer asn1.RawValue Issuer asn1.RawValue
@ -195,7 +197,7 @@ type issuerSerial struct {
IssuerUID asn1.BitString `asn1:"optional"` IssuerUID asn1.BitString `asn1:"optional"`
} }
//RFC 5755 4.1 // RFC 5755 4.1
type holder struct { type holder struct {
Raw asn1.RawContent Raw asn1.RawContent
BaseCertificateID issuerSerial `asn1:"optional,tag:0"` BaseCertificateID issuerSerial `asn1:"optional,tag:0"`
@ -203,13 +205,13 @@ type holder struct {
ObjectDigestInfo objectDigestInfo `asn1:"optional,tag:2"` ObjectDigestInfo objectDigestInfo `asn1:"optional,tag:2"`
} }
//RFC 5755 4.1 // RFC 5755 4.1
type attribute struct { type attribute struct {
ID asn1.ObjectIdentifier ID asn1.ObjectIdentifier
RawValues []asn1.RawValue `asn1:"set"` RawValues []asn1.RawValue `asn1:"set"`
} }
//RFC 5755 4.1 // RFC 5755 4.1
type tbsAttributeCertificate struct { type tbsAttributeCertificate struct {
Raw asn1.RawContent Raw asn1.RawContent
Version int Version int

View File

@ -15,13 +15,12 @@
package attributecert package attributecert
import ( import (
"crypto/x509"
"encoding/json" "encoding/json"
"io/ioutil" "os"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
"github.com/google/certificate-transparency-go/x509"
) )
func TestVerifyAttributeCert(t *testing.T) { func TestVerifyAttributeCert(t *testing.T) {
@ -30,7 +29,7 @@ func TestVerifyAttributeCert(t *testing.T) {
"testdata/Intel_pc2.cer", "testdata/Intel_pc2.cer",
"testdata/Intel_pc3.cer", "testdata/Intel_pc3.cer",
} }
data, err := ioutil.ReadFile("testdata/IntelSigningKey_20April2017.cer") data, err := os.ReadFile("testdata/IntelSigningKey_20April2017.cer")
if err != nil { if err != nil {
t.Fatalf("failed to read Intel intermediate certificate: %v", err) t.Fatalf("failed to read Intel intermediate certificate: %v", err)
} }
@ -40,7 +39,7 @@ func TestVerifyAttributeCert(t *testing.T) {
} }
for _, filename := range testfiles { for _, filename := range testfiles {
data, err = ioutil.ReadFile(filename) data, err = os.ReadFile(filename)
if err != nil { if err != nil {
t.Fatalf("failed to read %s: %v", filename, err) t.Fatalf("failed to read %s: %v", filename, err)
} }
@ -58,7 +57,7 @@ func TestVerifyAttributeCert(t *testing.T) {
} }
func TestParseAttributeCerts(t *testing.T) { func TestParseAttributeCerts(t *testing.T) {
files, err := ioutil.ReadDir("testdata") files, err := os.ReadDir("testdata")
if err != nil { if err != nil {
t.Fatalf("failed to read test dir: %v", err) t.Fatalf("failed to read test dir: %v", err)
} }
@ -71,7 +70,7 @@ func TestParseAttributeCerts(t *testing.T) {
} }
filename := "testdata/" + file.Name() filename := "testdata/" + file.Name()
jsonfile := filename + ".json" jsonfile := filename + ".json"
data, err := ioutil.ReadFile(filename) data, err := os.ReadFile(filename)
if err != nil { if err != nil {
t.Fatalf("failed to read test data %s: %v", filename, err) t.Fatalf("failed to read test data %s: %v", filename, err)
} }
@ -79,7 +78,7 @@ func TestParseAttributeCerts(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to parse test data %s: %v", filename, err) t.Fatalf("failed to parse test data %s: %v", filename, err)
} }
jsondata, err := ioutil.ReadFile(jsonfile) jsondata, err := os.ReadFile(jsonfile)
if err != nil { if err != nil {
t.Fatalf("failed to read json test data %s: %v", jsonfile, err) t.Fatalf("failed to read json test data %s: %v", jsonfile, err)
} }

View File

@ -16,8 +16,6 @@ import (
"time" "time"
) )
var simulatorStatePath = flag.String("state_path", "/tmp/sim/NVRAM/00.permall", "Path to ibmswtpm state file")
func ekPub() *rsa.PublicKey { func ekPub() *rsa.PublicKey {
out, err := exec.Command("tpm_getpubek", "-z").Output() out, err := exec.Command("tpm_getpubek", "-z").Output()
if err != nil { if err != nil {

21
go.mod
View File

@ -1,12 +1,19 @@
module github.com/google/go-attestation module github.com/google/go-attestation
go 1.16 go 1.24
toolchain go1.24.1
require ( require (
github.com/google/certificate-transparency-go v1.1.1 github.com/google/go-cmp v0.7.0
github.com/google/go-cmp v0.5.5 github.com/google/go-tpm v0.9.3
github.com/google/go-tpm v0.3.2 github.com/google/go-tpm-tools v0.4.5
github.com/google/go-tpm-tools v0.3.1 github.com/google/go-tspi v0.3.0
github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad go.uber.org/multierr v1.11.0
golang.org/x/sys v0.0.0-20210316092937-0b90fd5c4c48 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
) )

678
go.sum

File diff suppressed because it is too large Load Diff

179
x509/x509ext.go Normal file
View 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
}