Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ jobs:
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.20.x
go-version: 1.22.x
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.53.3
version: v1.64.8
8 changes: 4 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
test-linux:
strategy:
matrix:
go-version: [1.20.x]
go-version: [1.22.x]
runs-on: ubuntu-latest
steps:
- name: Install Go
Expand All @@ -27,7 +27,7 @@ jobs:
test-linux-tpm12:
strategy:
matrix:
go-version: [1.20.x]
go-version: [1.22.x]
runs-on: ubuntu-latest
steps:
- name: Install Go
Expand All @@ -43,7 +43,7 @@ jobs:
test-macos:
strategy:
matrix:
go-version: [1.20.x]
go-version: [1.22.x]
runs-on: macos-latest
steps:
- name: Install Go
Expand All @@ -62,7 +62,7 @@ jobs:
test-windows:
strategy:
matrix:
go-version: [1.20.x]
go-version: [1.22.x]
runs-on: windows-latest
steps:
- name: Install Go
Expand Down
9 changes: 5 additions & 4 deletions attest/activation.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ const (
// activationSecretLen is the size in bytes of the generated secret
// which is generated for credential activation.
activationSecretLen = 32
// symBlockSize is the block size used for symmetric ciphers used
// when generating the credential activation challenge.
symBlockSize = 16
// defaultSymBlockSize is the block size used for symmetric ciphers
// used when generating the credential activation challenge.
defaultSymBlockSize = 16
// tpm20GeneratedMagic is a magic tag when can only be present on a
// TPM structure if the structure was generated wholly by the TPM.
tpm20GeneratedMagic = 0xff544347
Expand Down Expand Up @@ -239,7 +239,8 @@ func (p *ActivationParameters) generateChallengeTPM20(secret []byte) (*Encrypted
if att.AttestedCreationInfo.Name.Digest == nil {
return nil, fmt.Errorf("attestation creation info name has no digest")
}
cred, encSecret, err := credactivation.Generate(att.AttestedCreationInfo.Name.Digest, p.EK, symBlockSize, secret)

cred, encSecret, err := credactivation.Generate(att.AttestedCreationInfo.Name.Digest, p.EK, symBlockSizeForEK(p.EK), secret)
if err != nil {
return nil, fmt.Errorf("credactivation.Generate() failed: %v", err)
}
Expand Down
21 changes: 20 additions & 1 deletion attest/certification.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ package attest
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"errors"
Expand Down Expand Up @@ -191,6 +193,23 @@ func (p *CertificationParameters) Verify(opts VerifyOpts) error {
return nil
}

func symBlockSizeForEK(ek crypto.PublicKey) int {
// see TCG EK Credential Profile Version 2.6, December 4, 2024, B.4.4 Storage EK Template
symBlockSize := defaultSymBlockSize // default to 16 bytes; 128 bits
switch pk := ek.(type) {
case *rsa.PublicKey:
if pk.Size() >= 384 { // RSA 3072, RSA 4096
symBlockSize = 32 // 32 bytes; 256 bits
}
case *ecdsa.PublicKey:
if pk.Curve == elliptic.P384() || pk.Curve == elliptic.P521() {
symBlockSize = 32 // 32 bytes; 256 bits
}
}

return symBlockSize
}

// Generate returns a credential activation challenge, which can be provided
// to the TPM to verify the AK parameters given are authentic & the AK
// is present on the same TPM as the EK.
Expand Down Expand Up @@ -225,7 +244,7 @@ func (p *CertificationParameters) Generate(rnd io.Reader, verifyOpts VerifyOpts,
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)
cred, encSecret, err := credactivation.Generate(activateOpts.VerifierKeyNameDigest, activateOpts.EK, symBlockSizeForEK(activateOpts.EK), secret)
if err != nil {
return nil, nil, fmt.Errorf("credactivation.Generate() failed: %v", err)
}
Expand Down
196 changes: 196 additions & 0 deletions attest/certification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,17 @@ package attest
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/google/go-tpm/legacy/tpm2"
"github.com/google/go-tpm/legacy/tpm2/credactivation"
"github.com/google/go-tpm/tpmutil"
)

func TestSimTPM20CertificationParameters(t *testing.T) {
Expand Down Expand Up @@ -397,3 +401,195 @@ func TestKeyActivationTPM20(t *testing.T) {
})
}
}

func Test_credentialGeneration(t *testing.T) {
t.Parallel()

packed, err := tpmutil.Pack([]byte{1, 2, 3, 4})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
digest := &tpm2.HashValue{
Alg: tpm2.AlgSHA256,
Value: packed,
}
secret := []byte("the secret")

t.Run("rsa-2048", func(t *testing.T) {
t.Parallel()

k, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
pk := k.Public()

_, _, err = credactivation.Generate(digest, pk, symBlockSizeForEK(pk), secret)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
})

t.Run("rsa-3072", func(t *testing.T) {
t.Parallel()

k, err := rsa.GenerateKey(rand.Reader, 3072)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
pk := k.Public()

_, _, err = credactivation.Generate(digest, pk, symBlockSizeForEK(pk), secret)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
})

t.Run("rsa-4096", func(t *testing.T) {
t.Parallel()

k, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
pk := k.Public()

_, _, err = credactivation.Generate(digest, pk, symBlockSizeForEK(pk), secret)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
})

t.Run("ecdsa-P256", func(t *testing.T) {
t.Parallel()

k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
pk := k.Public()

_, _, err = credactivation.Generate(digest, pk, symBlockSizeForEK(pk), secret)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
})

t.Run("ecdsa-P384", func(t *testing.T) {
t.Parallel()

k, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
pk := k.Public()

_, _, err = credactivation.Generate(digest, pk, symBlockSizeForEK(pk), secret)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
})

t.Run("ecdsa-P521", func(t *testing.T) {
t.Parallel()

k, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
pk := k.Public()

_, _, err = credactivation.Generate(digest, pk, symBlockSizeForEK(pk), secret)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
})
}

func Test_symBlockSizeForEK(t *testing.T) {
t.Parallel()

t.Run("rsa-2048", func(t *testing.T) {
t.Parallel()

k, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

symBlockSize := symBlockSizeForEK(k.Public())
if symBlockSize != 16 {
t.Errorf("unexpected symBlockSize %d; expected 16", symBlockSize)
}
})

t.Run("rsa-3072", func(t *testing.T) {
t.Parallel()

k, err := rsa.GenerateKey(rand.Reader, 3072)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

symBlockSize := symBlockSizeForEK(k.Public())
if symBlockSize != 32 {
t.Errorf("unexpected symBlockSize %d; expected 32", symBlockSize)
}
})

t.Run("rsa-4096", func(t *testing.T) {
t.Parallel()

k, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

symBlockSize := symBlockSizeForEK(k.Public())
if symBlockSize != 32 {
t.Errorf("unexpected symBlockSize %d; expected 32", symBlockSize)
}
})

t.Run("ecdsa-P256", func(t *testing.T) {
t.Parallel()

k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

symBlockSize := symBlockSizeForEK(k.Public())
if symBlockSize != 16 {
t.Errorf("unexpected symBlockSize %d; expected 16", symBlockSize)
}
})

t.Run("ecdsa-P384", func(t *testing.T) {
t.Parallel()

k, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

symBlockSize := symBlockSizeForEK(k.Public())
if symBlockSize != 32 {
t.Errorf("unexpected symBlockSize %d; expected 32", symBlockSize)
}
})

t.Run("ecdsa-P521", func(t *testing.T) {
t.Parallel()

k, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

symBlockSize := symBlockSizeForEK(k.Public())
if symBlockSize != 32 {
t.Errorf("unexpected symBlockSize %d; expected 32", symBlockSize)
}
})
}
10 changes: 9 additions & 1 deletion attest/pcp_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"bytes"
"crypto/x509"
"encoding/binary"
"encoding/hex"
"fmt"
"io"
"syscall"
Expand Down Expand Up @@ -816,6 +817,9 @@ func (h *winPCP) ActivateCredential(hKey uintptr, activationBlob []byte) ([]byte
return nil, err
}

hexBlob := hex.EncodeToString(activationBlob)
fmt.Println("activation blob", hexBlob)

r, _, msg := nCryptSetProperty.Call(hKey, uintptr(unsafe.Pointer(&utf16ActivationStr[0])), uintptr(unsafe.Pointer(&activationBlob[0])), uintptr(len(activationBlob)), 0)
if r != 0 {
if tpmErr := maybeWinErr(r); tpmErr != nil {
Expand All @@ -828,10 +832,14 @@ func (h *winPCP) ActivateCredential(hKey uintptr, activationBlob []byte) ([]byte
var size uint32
r, _, msg = nCryptGetProperty.Call(hKey, uintptr(unsafe.Pointer(&utf16ActivationStr[0])), uintptr(unsafe.Pointer(&secretBuff[0])), uintptr(len(secretBuff)), uintptr(unsafe.Pointer(&size)), 0)
if r != 0 {
fmt.Println("secret size", size)
hexSecret := hex.EncodeToString(secretBuff)
fmt.Println("secret blob", hexSecret)

if tpmErr := maybeWinErr(r); tpmErr != nil {
msg = tpmErr
}
return nil, fmt.Errorf("NCryptGetProperty returned %X (%v) for key activation", r, msg)
return nil, fmt.Errorf("NCryptGetProperty returned %X (%v) for key activation; activation blob: %s; secret size: %d; secret blob: %s", r, msg, hexBlob, size, hexSecret)
}
return secretBuff[:size], nil
}
Expand Down
7 changes: 5 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
module github.com/smallstep/go-attestation

go 1.20
go 1.22

require (
github.com/google/go-cmp v0.5.9
github.com/google/go-tpm v0.9.0
github.com/google/go-tpm v0.9.6
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba
github.com/google/go-tspi v0.3.0
golang.org/x/sys v0.9.0
Expand All @@ -14,3 +14,6 @@ require (
github.com/google/certificate-transparency-go v1.1.2 // indirect
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
)

// required until https://github.com/google/go-tpm/pull/420 is merged
replace github.com/google/go-tpm => github.com/hslatman/go-tpm v0.0.0-20251217214657-7d0adf0a5e3b
Loading
Loading