mirror of
https://github.com/google/go-attestation.git
synced 2025-03-06 04:41:45 +00:00
Add a basic example of attestation over BLE
Add a trivial remote attestation client and server based on Bluetooth low energy. This is a PoC rather than production-ready code and has multiple rough edges, but demonstrates that remote attestation can be performed over a local network rather than requiring an internet connection and remote server.
This commit is contained in:
parent
e9e2656545
commit
bbc1a84749
25
README.md
25
README.md
@ -125,3 +125,28 @@ if err != nil {
|
||||
|
||||
At this point, the server records the AK and EK association and allows the client
|
||||
to use its AK as a credential (e.g. by issuing it a client certificate).
|
||||
|
||||
## Bluetooth
|
||||
|
||||
Currently only supported under Linux. Build attest/bluetooth-server
|
||||
and attest/bluetooth-client. Create a 16 byte shared secret:
|
||||
|
||||
```
|
||||
dd if=/dev/random of=secret bs=1 count=16
|
||||
```
|
||||
|
||||
and place it on client and server. On the machine you wish to attest, run
|
||||
|
||||
```
|
||||
bluetooth-server name secret
|
||||
```
|
||||
|
||||
where name is some name that defines the system and secret is the path
|
||||
to the shared secret. On the machine that should verify the attestation, run
|
||||
|
||||
```
|
||||
bluetooth-client name secret
|
||||
```
|
||||
|
||||
where name is the same name used on the attesting system and secret is
|
||||
the path to the shared secret.
|
||||
|
297
attest/bluetooth-client/bluetooth-client.go
Normal file
297
attest/bluetooth-client/bluetooth-client.go
Normal file
@ -0,0 +1,297 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-ble/ble"
|
||||
"github.com/go-ble/ble/examples/lib/dev"
|
||||
"github.com/google/go-attestation/attest"
|
||||
)
|
||||
|
||||
var AuthCharUUID = ble.MustParse("ebee1790-50b3-4943-8396-16c0b7231cad")
|
||||
var EkCharUUID = ble.MustParse("ebee1791-50b3-4943-8396-16c0b7231cad")
|
||||
var AkCharUUID = ble.MustParse("ebee1792-50b3-4943-8396-16c0b7231cad")
|
||||
var ActivateCharUUID = ble.MustParse("ebee1793-50b3-4943-8396-16c0b7231cad")
|
||||
var AttestationCharUUID = ble.MustParse("ebee1794-50b3-4943-8396-16c0b7231cad")
|
||||
|
||||
func readCharacteristic(cln ble.Client, profile ble.Profile, uuid ble.UUID) ([]byte, error) {
|
||||
readlen := 0
|
||||
var buf []byte
|
||||
char := profile.Find(ble.NewCharacteristic(uuid))
|
||||
if char == nil {
|
||||
return nil, fmt.Errorf("unable to find uuid %s", uuid)
|
||||
}
|
||||
|
||||
for {
|
||||
data, err := cln.ReadCharacteristic(char.(*ble.Characteristic))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil, fmt.Errorf("0 byte read")
|
||||
}
|
||||
if readlen == 0 {
|
||||
if len(data) < 4 {
|
||||
return nil, fmt.Errorf("Read too little data: %v", data)
|
||||
}
|
||||
readlen = int(binary.LittleEndian.Uint32(data[0:4]))
|
||||
buf = append(buf, data[4:]...)
|
||||
} else {
|
||||
buf = append(buf, data...)
|
||||
}
|
||||
if len(buf) == readlen {
|
||||
return buf, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeCharacteristic(cln ble.Client, profile ble.Profile, uuid ble.UUID, buf []byte) error {
|
||||
written := 0
|
||||
char := profile.Find(ble.NewCharacteristic(uuid))
|
||||
if char == nil {
|
||||
return fmt.Errorf("unable to find uuid %s", uuid)
|
||||
}
|
||||
|
||||
for {
|
||||
var writebuf [20]byte
|
||||
writelen := 20
|
||||
if written == 0 {
|
||||
binary.LittleEndian.PutUint32(writebuf[:], uint32(len(buf)))
|
||||
end := len(buf)
|
||||
if end > 16 {
|
||||
end = 16
|
||||
}
|
||||
copy(writebuf[4:20], buf[0:end])
|
||||
written = 16
|
||||
} else if written < len(buf) {
|
||||
end := written + 20
|
||||
if end > len(buf) {
|
||||
end = len(buf)
|
||||
writelen = end - written
|
||||
}
|
||||
copy(writebuf[0:20], buf[written:end])
|
||||
written = end
|
||||
} else {
|
||||
break
|
||||
}
|
||||
err := cln.WriteCharacteristic(char.(*ble.Characteristic), writebuf[:writelen], true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func filter(arg string) ble.AdvFilter {
|
||||
target := strings.ToLower(arg)
|
||||
return func(device ble.Advertisement) bool {
|
||||
if strings.ToLower(device.LocalName()) == target ||
|
||||
strings.ToLower(device.Addr().String()) == target {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var ap attest.AttestationParameters
|
||||
var ek rsa.PublicKey
|
||||
var att attest.PlatformParameters
|
||||
|
||||
if len(os.Args) != 3 {
|
||||
log.Fatalf("Usage: %s target keydata")
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(os.Args[2])
|
||||
if len(data) != 16 {
|
||||
log.Fatalf("Authentication data is %d bytes long, should be 16", len(data))
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(data)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create cipher: %v", err)
|
||||
}
|
||||
|
||||
ctx, _ := context.WithCancel(context.Background())
|
||||
|
||||
d, err := dev.NewDevice("default")
|
||||
if err != nil {
|
||||
log.Fatalf("can't new device : %v", err)
|
||||
}
|
||||
ble.SetDefaultDevice(d)
|
||||
|
||||
cln, err := ble.Connect(ctx, filter(os.Args[1]))
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to connect: %v", err)
|
||||
}
|
||||
|
||||
profile, err := cln.DiscoverProfile(true)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to obtain device profile: %v", err)
|
||||
}
|
||||
|
||||
// Read the challenge from the server. We will encrypt it and pass it
|
||||
// back to the server in order to prove that we have access to the
|
||||
// authentication secret.
|
||||
auth, err := readCharacteristic(cln, *profile, AuthCharUUID)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to read auth challenge: %v", err)
|
||||
}
|
||||
|
||||
if len(auth) != 32 {
|
||||
log.Fatalf("Challenge is %d bytes, should be 32", len(auth))
|
||||
}
|
||||
|
||||
// Encrypt the challenge
|
||||
aescbc := cipher.NewCBCEncrypter(block, auth[0:16])
|
||||
ciphertext := make([]byte, 16)
|
||||
aescbc.CryptBlocks(ciphertext, auth[16:32])
|
||||
|
||||
// Pass the encrypted challenge back to the server
|
||||
err = writeCharacteristic(cln, *profile, AuthCharUUID, ciphertext)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to write auth challenge: %v", err)
|
||||
}
|
||||
|
||||
// Read the server's Endorsement Key
|
||||
encodedEk, err := readCharacteristic(cln, *profile, EkCharUUID)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to read ek: %v", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(encodedEk, &ek)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to unmarshal ek: %v", err)
|
||||
}
|
||||
|
||||
// At this point we should verify that the EK certificate chains
|
||||
// back to a known TPM manufacturer, but that's not needed for this
|
||||
// PoC. We should also save the EK and use it in future in order to
|
||||
// verify that we're talking to the same TPM.
|
||||
|
||||
// Ask the remote TPM to generate an AK and send it to us. Instead,
|
||||
// we could persist the AK locally and send it back to the TPM. TODO.
|
||||
ak, err := readCharacteristic(cln, *profile, AkCharUUID)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to read ak: %v", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(ak, &ap)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to unmarshal ak: %v", err)
|
||||
}
|
||||
|
||||
// We now have an AK and an EK. We need to verify that the AK
|
||||
// matches the EK in order to prove that we're talking to the same
|
||||
// TPM. This is done with credential activation (see
|
||||
// docs/credential-activation.md)
|
||||
activation := attest.ActivationParameters{
|
||||
TPMVersion: attest.TPMVersion12,
|
||||
EK: &ek,
|
||||
AK: ap,
|
||||
}
|
||||
|
||||
secret, challenge, err := activation.Generate()
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to generate activation challenge: %v", err)
|
||||
}
|
||||
|
||||
encodedChallenge, err := json.Marshal(challenge)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to marshal challenge: %v", err)
|
||||
}
|
||||
|
||||
err = writeCharacteristic(cln, *profile, ActivateCharUUID, encodedChallenge)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to write credential: %v", err)
|
||||
}
|
||||
|
||||
// The remote will TPM now attempt to decrypt the secret with the EK
|
||||
// (proving it owns it) and check that the AK matches the appropriate
|
||||
// characteristics. If so, it'll return the decrypted secret to us.
|
||||
|
||||
decrypted, err := readCharacteristic(cln, *profile, ActivateCharUUID)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to read decrypted secret: %v", err)
|
||||
}
|
||||
|
||||
if subtle.ConstantTimeCompare(secret, decrypted) == 0 {
|
||||
log.Fatalf("Remote failed to generate correct secret - %v != %v", secret, decrypted)
|
||||
}
|
||||
|
||||
// At this point, we know that the AK corresponds to the EK. We now
|
||||
// want a quote (a signed copy of the PCR values) and a copy of the
|
||||
// event log.
|
||||
nonce := make([]byte, 32)
|
||||
_, err = rand.Read(nonce)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to generate a nonce: %v", err)
|
||||
}
|
||||
|
||||
err = writeCharacteristic(cln, *profile, AttestationCharUUID, nonce)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to write nonce: %v", err)
|
||||
}
|
||||
|
||||
rawquote, err := readCharacteristic(cln, *profile, AttestationCharUUID)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read quote: %v", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(rawquote, &att)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to marshal platform attestation: %v", err)
|
||||
}
|
||||
|
||||
// Pull out the AK pub and verify that the quotes are signed with it.
|
||||
// Since we've already verified that the AK was generated from the EK,
|
||||
// we know that the quote is coming from the TPM.
|
||||
pub, err := attest.ParseAKPublic(attest.TPMVersion12, ap.Public)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse AK public: %v", err)
|
||||
}
|
||||
|
||||
for i, q := range att.Quotes {
|
||||
if err := pub.Verify(q, att.PCRs, nonce); err != nil {
|
||||
log.Fatalf("quote[%d] verification failed: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the event log is well formed.
|
||||
el, err := attest.ParseEventLog(att.EventLog)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse event log: %v", err)
|
||||
}
|
||||
|
||||
// Verify that the event log matches the PCR values we got. We do this
|
||||
// by replaying the values in the event log in the same way that the
|
||||
// TPM responded to them originally. If the values in the log match
|
||||
// the values that were sent to the TPM, that means that the values
|
||||
// in the event log match what actually happened during boot.
|
||||
events, err := el.Verify(att.PCRs)
|
||||
if err != nil {
|
||||
log.Fatalf("Log failed to replay: %v", err)
|
||||
}
|
||||
|
||||
// Finally, examine the event log to determine whether the system
|
||||
// had UEFI secure boot enabled. There is a specific event recorded
|
||||
// to PCR 7 that tells us this.
|
||||
sbState, err := attest.ParseSecurebootState(events)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse secure boot state: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("Validation succeeded - secure boot state is %v", sbState.Enabled)
|
||||
}
|
377
attest/bluetooth-server/bluetooth-server.go
Normal file
377
attest/bluetooth-server/bluetooth-server.go
Normal file
@ -0,0 +1,377 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/go-ble/ble"
|
||||
"github.com/go-ble/ble/examples/lib/dev"
|
||||
"github.com/go-ble/ble/linux/hci/evt"
|
||||
"github.com/google/go-attestation/attest"
|
||||
)
|
||||
|
||||
var TpmSvcUUID = ble.MustParse("99a84b8f-8d7c-47b0-b24e-800e00fa5d57")
|
||||
|
||||
var AuthCharUUID = ble.MustParse("ebee1790-50b3-4943-8396-16c0b7231cad")
|
||||
var EkCharUUID = ble.MustParse("ebee1791-50b3-4943-8396-16c0b7231cad")
|
||||
var AkCharUUID = ble.MustParse("ebee1792-50b3-4943-8396-16c0b7231cad")
|
||||
var ActivateCharUUID = ble.MustParse("ebee1793-50b3-4943-8396-16c0b7231cad")
|
||||
var AttestationCharUUID = ble.MustParse("ebee1794-50b3-4943-8396-16c0b7231cad")
|
||||
|
||||
var decrypted []byte
|
||||
var encodedek []byte
|
||||
var encodedak []byte
|
||||
var encodedec []byte
|
||||
var encodedattestation []byte
|
||||
var nonce []byte
|
||||
var tpm *attest.TPM
|
||||
var ak *attest.AK
|
||||
var marshaledak []byte
|
||||
var authkey [16]byte
|
||||
var authenticated bool
|
||||
|
||||
func writeBuf(written *int, buf []byte, rsp ble.ResponseWriter, force bool) bool {
|
||||
var writebuf [20]byte
|
||||
|
||||
if authenticated == false && force == false {
|
||||
return false
|
||||
}
|
||||
|
||||
writelen := 20
|
||||
if *written == 0 {
|
||||
binary.LittleEndian.PutUint32(writebuf[:], uint32(len(buf)))
|
||||
end := len(buf)
|
||||
if end > 16 {
|
||||
end = 16
|
||||
}
|
||||
copy(writebuf[4:end+4], buf[0:end])
|
||||
*written = end
|
||||
writelen = end + 4
|
||||
} else if *written < len(buf) {
|
||||
end := *written + 20
|
||||
if end > len(buf) {
|
||||
end = len(buf)
|
||||
writelen = end - *written
|
||||
}
|
||||
copy(writebuf[0:20], buf[*written:end])
|
||||
*written = end
|
||||
} else {
|
||||
writelen = 0
|
||||
}
|
||||
rsp.Write(writebuf[:writelen])
|
||||
if *written == len(buf) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func readBuf(readlen *int, buf *[]byte, req ble.Request) bool {
|
||||
if authenticated == false {
|
||||
return false
|
||||
}
|
||||
|
||||
if *readlen == 0 {
|
||||
*readlen = int(binary.LittleEndian.Uint32(req.Data()[0:4]))
|
||||
*buf = append(*buf, req.Data()[4:]...)
|
||||
} else {
|
||||
*buf = append(*buf, req.Data()...)
|
||||
}
|
||||
if len(*buf) == *readlen {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AuthChar() *ble.Characteristic {
|
||||
var valid bool
|
||||
var data []byte
|
||||
written := 0
|
||||
|
||||
block, err := aes.NewCipher(authkey[:])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
authnonce := make([]byte, 16)
|
||||
challenge := make([]byte, 16)
|
||||
|
||||
_, err = rand.Read(nonce)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
_, err = rand.Read(challenge)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
data = append(data, authnonce...)
|
||||
data = append(data, challenge...)
|
||||
|
||||
valid = true
|
||||
|
||||
c := ble.NewCharacteristic(AuthCharUUID)
|
||||
c.HandleRead(ble.ReadHandlerFunc(func(req ble.Request, rsp ble.ResponseWriter) {
|
||||
if valid == false {
|
||||
_, err := rand.Read(authnonce)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = rand.Read(challenge)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data = data[:0]
|
||||
data = append(data, authnonce...)
|
||||
data = append(data, challenge...)
|
||||
|
||||
valid = true
|
||||
}
|
||||
|
||||
complete := writeBuf(&written, data, rsp, true)
|
||||
if complete == true {
|
||||
written = 0
|
||||
}
|
||||
|
||||
}))
|
||||
c.HandleWrite(ble.WriteHandlerFunc(func(req ble.Request, rsp ble.ResponseWriter) {
|
||||
written = 0
|
||||
if valid == false {
|
||||
return
|
||||
}
|
||||
|
||||
valid = false
|
||||
if len(req.Data()) != 20 {
|
||||
return
|
||||
}
|
||||
aescbc := cipher.NewCBCDecrypter(block, authnonce)
|
||||
|
||||
ciphertext := req.Data()[4:20]
|
||||
|
||||
aescbc.CryptBlocks(ciphertext, ciphertext)
|
||||
|
||||
if subtle.ConstantTimeCompare(challenge, ciphertext) != 0 {
|
||||
authenticated = true
|
||||
} else {
|
||||
log.Printf("Auth expected %v, got %v", challenge, ciphertext)
|
||||
}
|
||||
}))
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func EkChar() *ble.Characteristic {
|
||||
written := 0
|
||||
|
||||
// Read the Endorsement Key from the TPM and pass it back to the
|
||||
// client
|
||||
c := ble.NewCharacteristic(EkCharUUID)
|
||||
c.HandleRead(ble.ReadHandlerFunc(func(req ble.Request, rsp ble.ResponseWriter) {
|
||||
complete := writeBuf(&written, encodedek, rsp, false)
|
||||
if complete == true {
|
||||
written = 0
|
||||
}
|
||||
}))
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func AkChar() *ble.Characteristic {
|
||||
var err error
|
||||
written := 0
|
||||
readlen := 0
|
||||
|
||||
c := ble.NewCharacteristic(AkCharUUID)
|
||||
|
||||
// Load an Attestation Key that was given to us by the client
|
||||
c.HandleWrite(ble.WriteHandlerFunc(func(req ble.Request, rsp ble.ResponseWriter) {
|
||||
written = 0
|
||||
complete := readBuf(&readlen, &marshaledak, req)
|
||||
if complete == true {
|
||||
ak, err = tpm.LoadAK(marshaledak)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load AK: %v", err)
|
||||
}
|
||||
readlen = 0
|
||||
}
|
||||
}))
|
||||
|
||||
// Generate a new Attestation Key and provide it to the client on
|
||||
// request
|
||||
c.HandleRead(ble.ReadHandlerFunc(func(req ble.Request, rsp ble.ResponseWriter) {
|
||||
if len(encodedak) == 0 {
|
||||
if ak == nil {
|
||||
ak, err = tpm.NewAK(nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create AK: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
encodedak, err = json.Marshal(ak.AttestationParameters())
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to marshal AK: %v", err)
|
||||
}
|
||||
}
|
||||
complete := writeBuf(&written, encodedak, rsp, false)
|
||||
if complete == true {
|
||||
written = 0
|
||||
}
|
||||
}))
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func ActivateChar() *ble.Characteristic {
|
||||
written := 0
|
||||
readlen := 0
|
||||
|
||||
c := ble.NewCharacteristic(ActivateCharUUID)
|
||||
|
||||
// The client has given us a Credential Activation
|
||||
// challenge. Decrypt it using the Endorsement Key in order to prove
|
||||
// that we are the legitimate TPM and that the Activation Key
|
||||
// matches the Endorsement Key.
|
||||
c.HandleWrite(ble.WriteHandlerFunc(func(req ble.Request, rsp ble.ResponseWriter) {
|
||||
written = 0
|
||||
complete := readBuf(&readlen, &encodedec, req)
|
||||
if complete == true {
|
||||
var ec attest.EncryptedCredential
|
||||
err := json.Unmarshal(encodedec, &ec)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to unmarshal encrypted credential: %v", err)
|
||||
}
|
||||
decrypted, err = ak.ActivateCredential(tpm, ec)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to activate credentials: %v", err)
|
||||
}
|
||||
readlen = 0
|
||||
}
|
||||
}))
|
||||
|
||||
// Give the decrypted secret from the Credential Activation
|
||||
// challenge back to the client. The client will then compare it to
|
||||
// the secret it generated - if they match then we must have access
|
||||
// to the Endorsement Key (because otherwise we couldn't have
|
||||
// decrypted it)
|
||||
c.HandleRead(ble.ReadHandlerFunc(func(req ble.Request, rsp ble.ResponseWriter) {
|
||||
complete := writeBuf(&written, decrypted, rsp, false)
|
||||
if complete == true {
|
||||
written = 0
|
||||
}
|
||||
}))
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func AttestationChar() *ble.Characteristic {
|
||||
written := 0
|
||||
readlen := 0
|
||||
|
||||
c := ble.NewCharacteristic(AttestationCharUUID)
|
||||
|
||||
// The client sends us a random nonce. The TPM will take this nonce
|
||||
// and add it to the list of PCR values, and then sign all of this
|
||||
// with the Attestation Key. The client will check that the
|
||||
// signature is valid and that the nonce matches the nonce it sent -
|
||||
// this avoids a replay attack where an attacker simply sends back
|
||||
// an old quote. The client is then able to look at the PCR values
|
||||
// and check whether they're valid.
|
||||
c.HandleWrite(ble.WriteHandlerFunc(func(req ble.Request, rsp ble.ResponseWriter) {
|
||||
written = 0
|
||||
complete := readBuf(&readlen, &nonce, req)
|
||||
if complete == true {
|
||||
att, err := tpm.AttestPlatform(ak, nonce, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to perform attestation: %v", err)
|
||||
}
|
||||
|
||||
encodedattestation, err = json.Marshal(att)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to marshal attestation data: %v", err)
|
||||
}
|
||||
readlen = 0
|
||||
}
|
||||
}))
|
||||
|
||||
// Send the signed PCR quote back to the client
|
||||
c.HandleRead(ble.ReadHandlerFunc(func(req ble.Request, rsp ble.ResponseWriter) {
|
||||
complete := writeBuf(&written, encodedattestation, rsp, false)
|
||||
if complete == true {
|
||||
written = 0
|
||||
}
|
||||
}))
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func connected(evt.LEConnectionComplete) {
|
||||
decrypted = decrypted[:0]
|
||||
encodedak = encodedak[:0]
|
||||
encodedec = encodedec[:0]
|
||||
encodedattestation = encodedattestation[:0]
|
||||
nonce = nonce[:0]
|
||||
}
|
||||
|
||||
func disconnected(evt.DisconnectionComplete) {
|
||||
authenticated = false
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
|
||||
if len(os.Args) != 3 {
|
||||
log.Fatalf("Usage: %s name keydata")
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(os.Args[2])
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read authentication key data: %v", err)
|
||||
}
|
||||
if len(data) != 16 {
|
||||
log.Fatalf("Authentication data is %d bytes long, should be 16", len(data))
|
||||
}
|
||||
copy(authkey[:], data)
|
||||
|
||||
tpm, err = attest.OpenTPM(nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open TPM: %v", err)
|
||||
}
|
||||
|
||||
eks, err := tpm.EKs()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read EKs: %v", err)
|
||||
}
|
||||
|
||||
encodedek, err = json.Marshal(eks[0].Public)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to marshal EK: %v", err)
|
||||
}
|
||||
|
||||
d, err := dev.NewDevice("default", ble.OptConnectHandler(connected), ble.OptDisconnectHandler(disconnected))
|
||||
if err != nil {
|
||||
log.Fatalf("can't new device : %v", err)
|
||||
}
|
||||
ble.SetDefaultDevice(d)
|
||||
tpmSvc := ble.NewService(TpmSvcUUID)
|
||||
|
||||
tpmSvc.AddCharacteristic(AuthChar())
|
||||
tpmSvc.AddCharacteristic(EkChar())
|
||||
tpmSvc.AddCharacteristic(AkChar())
|
||||
tpmSvc.AddCharacteristic(ActivateChar())
|
||||
tpmSvc.AddCharacteristic(AttestationChar())
|
||||
|
||||
if err := ble.AddService(tpmSvc); err != nil {
|
||||
log.Fatalf("can't add service: %s", err)
|
||||
}
|
||||
ctx := ble.WithSigHandler(context.WithCancel(context.Background()))
|
||||
err = ble.AdvertiseNameAndServices(ctx, os.Args[1], tpmSvc.UUID)
|
||||
log.Fatalf("Exiting with %v", err)
|
||||
}
|
3
go.mod
3
go.mod
@ -3,11 +3,12 @@ module github.com/google/go-attestation
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/go-ble/ble v0.0.0-20200407180624-067514cd6e24
|
||||
github.com/google/certificate-transparency-go v1.0.22-0.20190605205155-41fc2ef3a2a8
|
||||
github.com/google/go-cmp v0.3.1
|
||||
github.com/google/go-tpm v0.2.1-0.20191015210219-431489f43254
|
||||
github.com/google/go-tpm-tools v0.1.1
|
||||
github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 // indirect
|
||||
golang.org/x/sys v0.0.0-20190919044723-0c1ff786ef13
|
||||
golang.org/x/sys v0.0.0-20191126131656-8a8471f7e56d
|
||||
)
|
||||
|
25
go.sum
25
go.sum
@ -4,8 +4,12 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-ble/ble v0.0.0-20200407180624-067514cd6e24 h1:6St0uI/mfzuJX/y596wl2dJmA1VfdBSqopaUfS29z24=
|
||||
github.com/go-ble/ble v0.0.0-20200407180624-067514cd6e24/go.mod h1:nwmyxHsP2cqjashMTTAl3A5t6V3vzev1rLgMb/pZ7jc=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
|
||||
@ -27,19 +31,36 @@ github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad/go.mod h1:xfMGI3G
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mgutz/logxi v0.0.0-20161027140823-aebf8a7d67ab h1:n8cgpHzJ5+EDyDri2s/GC7a9+qK3/YEGnBsd0uS/8PY=
|
||||
github.com/mgutz/logxi v0.0.0-20161027140823-aebf8a7d67ab/go.mod h1:y1pL58r5z2VvAjeG1VLGc8zOQgSOzbKN7kMHPvFXJ+8=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/raff/goble v0.0.0-20190909174656-72afc67d6a99/go.mod h1:CxaUhijgLFX0AROtH5mluSY71VqpjQBw9JXE2UKZmc4=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
@ -50,9 +71,13 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190919044723-0c1ff786ef13 h1:/zi0zzlPHWXYXrO1LjNRByFu8sdGgCkj2JLDdBIB84k=
|
||||
golang.org/x/sys v0.0.0-20190919044723-0c1ff786ef13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191126131656-8a8471f7e56d h1:kCXqdOO2GMlu0vCsEMBXwj/b0E9wyFpNPBpuv/go/F8=
|
||||
golang.org/x/sys v0.0.0-20191126131656-8a8471f7e56d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
Loading…
x
Reference in New Issue
Block a user