File tpm-fido-20230621.5f8828b.obscpio of Package tpm-fido
07070100000000000041ED0000000000000000000000016493935C00000000000000000000000000000000000000000000002200000000tpm-fido-20230621.5f8828b/.github07070100000001000041ED0000000000000000000000016493935C00000000000000000000000000000000000000000000002C00000000tpm-fido-20230621.5f8828b/.github/workflows07070100000002000081A40000000000000000000000016493935C000002F4000000000000000000000000000000000000003300000000tpm-fido-20230621.5f8828b/.github/workflows/go.ymlname: Go
on: [push, pull_request]
jobs:
build:
name: Build/Test
runs-on: ${{ matrix.os }}
timeout-minutes: 3
strategy:
matrix:
go-version: [1.19.10, 1.20.5]
os: [ubuntu-latest]
steps:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Build
run: go build -v ./...
- name: Test
run: go test -v ./... --timeout 60s
- name: Cross test for i386
run: env GOOS=linux GOARCH=386 go test -v ./... --timeout 60s
- name: Cross compile for arm (RPI)
run: env GOOS=linux GOARCH=arm GOARM=5 go build -v ./...
07070100000003000081A40000000000000000000000016493935C00000438000000000000000000000000000000000000002200000000tpm-fido-20230621.5f8828b/LICENSEThe MIT License (MIT)
Copyright (c) 2021 Peter Sanford
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
07070100000004000081A40000000000000000000000016493935C000008B3000000000000000000000000000000000000002400000000tpm-fido-20230621.5f8828b/Readme.md# tpm-fido
tpm-fido is FIDO token implementation for Linux that protects the token keys by using your system's TPM. tpm-fido uses Linux's [uhid](https://github.com/psanford/uhid) facility to emulate a USB HID device so that it is properly detected by browsers.
## Implementation details
tpm-fido uses the TPM 2.0 API. The overall design is as follows:
On registration tpm-fido generates a new P256 primary key under the Owner hierarchy on the TPM. To ensure that the key is unique per site and registration, tpm-fido generates a random 20 byte seed for each registration. The primary key template is populated with unique values from a sha256 hkdf of the 20 byte random seed and the application parameter provided by the browser.
A signing child key is then generated from that primary key. The key handle returned to the caller is a concatenation of the child key's public and private key handles and the 20 byte seed.
On an authentication request, tpm-fido will attempt to load the primary key by initializing the hkdf in the same manner as above. It will then attempt to load the child key from the provided key handle. Any incorrect values or values created by a different TPM will fail to load.
## Status
tpm-fido has been tested to work with Chrome and Firefox on Linux.
## Building
```
# in the root directory of tpm-fido run:
go build
```
## Running
In order to run `tpm-fido` you will need permission to access `/dev/tpmrm0`. On Ubuntu and Arch, you can add your user to the `tss` group.
Your user also needs permission to access `/dev/uhid` so that `tpm-fido` can appear to be a USB device.
I use the following udev rule to set the appropriate `uhid` permissions:
```
KERNEL=="uhid", SUBSYSTEM=="misc", GROUP="SOME_UHID_GROUP_MY_USER_BELONGS_TO", MODE="0660"
```
To ensure the above udev rule gets triggered, I also add the `uhid` module to `/etc/modules-load.d/uhid.conf` so that it loads at boot.
To run:
```
# as a user that has permission to read and write to /dev/tpmrm0:
./tpm-fido
```
Note: do not run with `sudo` or as root, as it will not work.
## Dependencies
tpm-fido requires `pinentry` to be available on the system. If you have gpg installed you most likely already have `pinentry`.
07070100000005000041ED0000000000000000000000016493935C00000000000000000000000000000000000000000000002600000000tpm-fido-20230621.5f8828b/attestation07070100000006000081A40000000000000000000000016493935C000005E0000000000000000000000000000000000000003500000000tpm-fido-20230621.5f8828b/attestation/attestation.gopackage attestation
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
)
// this is the same cert used by github/SoftU2F
// https://github.com/github/SoftU2F/blob/master/SelfSignedCertificate/SelfSignedCertificate.m
// We resuse that cert to help reduce the ability to track
// an individual using this cert
var attestationPrivateKeyPem = `-----BEGIN PRIVATE KEY-----
MHcCAQEEIAOEKsf0zeNn3qBWxk9/OxXqfUvEg8rGl58qMZOtVzEJoAoGCCqGSM49
AwEHoUQDQgAE9pyrJBRLtO+H9w8jHFzU9XgErPjgxrKz41IYPYA5H2vSedJqTINk
dObC2iOT/6wdUDRsXCOQZVeTPsuT/27e0Q==
-----END PRIVATE KEY-----`
var attestationCertPem = `-----BEGIN CERTIFICATE-----
MIIBfjCCASSgAwIBAgIBATAKBggqhkjOPQQDAjA8MREwDwYDVQQDDAhTb2Z0IFUy
RjEUMBIGA1UECgwLR2l0SHViIEluYy4xETAPBgNVBAsMCFNlY3VyaXR5MB4XDTE3
MDcyNjIwMDkwOFoXDTI3MDcyNDIwMDkwOFowPDERMA8GA1UEAwwIU29mdCBVMkYx
FDASBgNVBAoMC0dpdEh1YiBJbmMuMREwDwYDVQQLDAhTZWN1cml0eTBZMBMGByqG
SM49AgEGCCqGSM49AwEHA0IABPacqyQUS7Tvh/cPIxxc1PV4BKz44Mays+NSGD2A
OR9r0nnSakyDZHTmwtojk/+sHVA0bFwjkGVXkz7Lk/9u3tGjFzAVMBMGCysGAQQB
guUcAgEBBAQDAgMIMAoGCCqGSM49BAMCA0gAMEUCIQD+Ih2XuOrqErufQhSFD0gX
ZbXglZNeoaPWbQ+xbzn3IgIgZNfcL1xsOCr3ZfV4ajmwsUqXRSjvfd8hAhUbiErU
QXo=
-----END CERTIFICATE-----`
var (
CertDer []byte
PrivateKey *ecdsa.PrivateKey
)
func init() {
certDer, _ := pem.Decode([]byte(attestationCertPem))
CertDer = certDer.Bytes
privKeyDer, _ := pem.Decode([]byte(attestationPrivateKeyPem))
var err error
PrivateKey, err = x509.ParseECPrivateKey(privKeyDer.Bytes)
if err != nil {
panic(err)
}
}
07070100000007000081A40000000000000000000000016493935C000009FE000000000000000000000000000000000000003A00000000tpm-fido-20230621.5f8828b/attestation/attestation_test.gopackage attestation
import (
"bytes"
"testing"
)
func TestAttestationCert(t *testing.T) {
rawCert := []byte{
0x30, 0x82, 0x01, 0x7e, 0x30, 0x82, 0x01, 0x24, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01,
0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x3c, 0x31, 0x11,
0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, 0x53, 0x6f, 0x66, 0x74, 0x20, 0x55, 0x32,
0x46, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x47, 0x69, 0x74, 0x48,
0x75, 0x62, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b,
0x0c, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x37,
0x30, 0x37, 0x32, 0x36, 0x32, 0x30, 0x30, 0x39, 0x30, 0x38, 0x5a, 0x17, 0x0d, 0x32, 0x37, 0x30,
0x37, 0x32, 0x34, 0x32, 0x30, 0x30, 0x39, 0x30, 0x38, 0x5a, 0x30, 0x3c, 0x31, 0x11, 0x30, 0x0f,
0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, 0x53, 0x6f, 0x66, 0x74, 0x20, 0x55, 0x32, 0x46, 0x31,
0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x47, 0x69, 0x74, 0x48, 0x75, 0x62,
0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08,
0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
0x42, 0x00, 0x04, 0xf6, 0x9c, 0xab, 0x24, 0x14, 0x4b, 0xb4, 0xef, 0x87, 0xf7, 0x0f, 0x23, 0x1c,
0x5c, 0xd4, 0xf5, 0x78, 0x04, 0xac, 0xf8, 0xe0, 0xc6, 0xb2, 0xb3, 0xe3, 0x52, 0x18, 0x3d, 0x80,
0x39, 0x1f, 0x6b, 0xd2, 0x79, 0xd2, 0x6a, 0x4c, 0x83, 0x64, 0x74, 0xe6, 0xc2, 0xda, 0x23, 0x93,
0xff, 0xac, 0x1d, 0x50, 0x34, 0x6c, 0x5c, 0x23, 0x90, 0x65, 0x57, 0x93, 0x3e, 0xcb, 0x93, 0xff,
0x6e, 0xde, 0xd1, 0xa3, 0x17, 0x30, 0x15, 0x30, 0x13, 0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01,
0x82, 0xe5, 0x1c, 0x02, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02, 0x03, 0x08, 0x30, 0x0a, 0x06, 0x08,
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x21, 0x00,
0xfe, 0x22, 0x1d, 0x97, 0xb8, 0xea, 0xea, 0x12, 0xbb, 0x9f, 0x42, 0x14, 0x85, 0x0f, 0x48, 0x17,
0x65, 0xb5, 0xe0, 0x95, 0x93, 0x5e, 0xa1, 0xa3, 0xd6, 0x6d, 0x0f, 0xb1, 0x6f, 0x39, 0xf7, 0x22,
0x02, 0x20, 0x64, 0xd7, 0xdc, 0x2f, 0x5c, 0x6c, 0x38, 0x2a, 0xf7, 0x65, 0xf5, 0x78, 0x6a, 0x39,
0xb0, 0xb1, 0x4a, 0x97, 0x45, 0x28, 0xef, 0x7d, 0xdf, 0x21, 0x02, 0x15, 0x1b, 0x88, 0x4a, 0xd4,
0x41, 0x7a,
}
if !bytes.Equal(CertDer, rawCert) {
t.Fatalf("cert der mismatch")
}
}
07070100000008000041ED0000000000000000000000016493935C00000000000000000000000000000000000000000000002300000000tpm-fido-20230621.5f8828b/fidoauth07070100000009000081A40000000000000000000000016493935C000009AA000000000000000000000000000000000000002F00000000tpm-fido-20230621.5f8828b/fidoauth/fidoauth.go// fidoauth implements the fido1 authentication API
package fidoauth
import (
"fmt"
)
const (
CmdRegister = 0x01
CmdAuthenticate = 0x02
CmdVersion = 0x03
CtrlCheckOnly AuthCtrl = 0x07 // Check if the provided key is valid
CtrlEnforeUserPresenceAndSign AuthCtrl = 0x03 // confirm with user then sign
CtrlDontEnforeUserPresenceAndSign AuthCtrl = 0x08 // just sign without confirming
)
type AuthenticatorRequest struct {
Command uint8
Param1 uint8
Param2 uint8
Size int
Data []byte
Register *AuthenticatorRegisterReq
Authenticate *AuthenticatorAuthReq
}
type AuthenticatorRegisterReq struct {
ChallengeParam [32]byte
ApplicationParam [32]byte
}
type AuthenticatorResponse struct {
Data []byte
Status uint16
}
type AuthCtrl uint8
type AuthenticatorAuthReq struct {
Ctrl AuthCtrl
ChallengeParam [32]byte
ApplicationParam [32]byte
KeyHandle []byte
}
func DecodeAuthenticatorRequest(raw []byte) (*AuthenticatorRequest, error) {
if len(raw) < 7 {
return nil, fmt.Errorf("authenticator request too short")
}
req := AuthenticatorRequest{
Command: raw[1],
Param1: raw[2],
Param2: raw[3],
Size: (int(raw[4]) << 16) | (int(raw[5]) << 8) | int(raw[6]),
Data: raw[7:],
}
if req.Command == CmdRegister {
var reg AuthenticatorRegisterReq
if len(req.Data) < len(reg.ChallengeParam)+len(reg.ApplicationParam) {
return nil, fmt.Errorf("register request incorrect size: %d", len(req.Data))
}
copy(reg.ChallengeParam[:], req.Data[:32])
copy(reg.ApplicationParam[:], req.Data[32:])
req.Register = ®
} else if req.Command == CmdAuthenticate {
var auth AuthenticatorAuthReq
if len(req.Data) < len(auth.ChallengeParam)+len(auth.ApplicationParam)+2 {
return nil, fmt.Errorf("authenticate request too small: %d", len(req.Data))
}
auth.Ctrl = AuthCtrl(req.Param1)
switch auth.Ctrl {
case CtrlCheckOnly, CtrlEnforeUserPresenceAndSign, CtrlDontEnforeUserPresenceAndSign:
default:
return nil, fmt.Errorf("unknown ctrl type: %02x", auth.Ctrl)
}
data := req.Data
copy(auth.ChallengeParam[:], data[:32])
data = data[32:]
copy(auth.ApplicationParam[:], data[:32])
data = data[32:]
khLen := data[0]
data = data[1:]
if len(data) < int(khLen) {
return nil, fmt.Errorf("key handle len too short %d vs %d", len(data), int(khLen))
}
auth.KeyHandle = data[:khLen]
req.Authenticate = &auth
}
return &req, nil
}
0707010000000A000041ED0000000000000000000000016493935C00000000000000000000000000000000000000000000002200000000tpm-fido-20230621.5f8828b/fidohid0707010000000B000081A40000000000000000000000016493935C000029B2000000000000000000000000000000000000002D00000000tpm-fido-20230621.5f8828b/fidohid/fidohid.gopackage fidohid
import (
"bytes"
"context"
"crypto/rand"
"encoding/binary"
"fmt"
"io"
"log"
"github.com/psanford/tpm-fido/fidoauth"
"github.com/psanford/uhid"
)
func New(ctx context.Context, name string) (*SoftToken, error) {
d, err := uhid.NewDevice(name, rdesc)
if err != nil {
return nil, err
}
d.Data.Bus = busUSB
d.Data.VendorID = vendorID
d.Data.ProductID = productID
evtChan, err := d.Open(ctx)
if err != nil {
return nil, err
}
t := SoftToken{
device: d,
evtChan: evtChan,
authEvent: make(chan AuthEvent),
}
return &t, nil
}
type SoftToken struct {
device *uhid.Device
evtChan chan uhid.Event
authEvent chan AuthEvent
authFunc func()
}
type AuthEvent struct {
chanID uint32
cmd CmdType
Req *fidoauth.AuthenticatorRequest
Error error
}
func (t *SoftToken) Events() chan AuthEvent {
return t.authEvent
}
func (t *SoftToken) Run(ctx context.Context) {
channels := make(map[uint32]bool)
allocateChan := func() (uint32, bool) {
for k := uint32(1); k < (1<<32)-1; k++ {
inUse := channels[k]
if !inUse {
channels[k] = true
return k, true
}
}
return 0, false
}
pktChan := make(chan Packet)
go parsePackets(ctx, t.evtChan, pktChan)
for {
var (
innerMsg []byte
needSize uint16
reqChanID uint32
cmd CmdType
)
for pkt := range pktChan {
if pkt.IsInitial {
if len(innerMsg) > 0 {
log.Print("new initial packet while pending packets still exist")
innerMsg = make([]byte, 0)
needSize = 0
}
needSize = pkt.TotalSize
reqChanID = pkt.ChannelID
cmd = pkt.Command
}
innerMsg = append(innerMsg, pkt.Data...)
if len(innerMsg) >= int(needSize) {
break
}
}
innerMsg = innerMsg[:int(needSize)]
switch cmd {
case CmdInit:
chanID, ok := allocateChan()
if !ok {
log.Fatalf("Channel id exhaustion")
}
var nonce [8]byte
copy(nonce[:], innerMsg)
resp := newInitResponse(chanID, nonce)
err := writeRespose(t.device, reqChanID, CmdInit, resp.Marshal(), 0)
if err != nil {
log.Printf("Write Init resp err: %s", err)
continue
}
case CmdMsg:
req, err := fidoauth.DecodeAuthenticatorRequest(innerMsg)
evt := AuthEvent{
chanID: reqChanID,
cmd: cmd,
Req: req,
Error: err,
}
select {
case t.authEvent <- evt:
case <-ctx.Done():
return
}
default:
log.Printf("unsuppoted cmd: %s %d", cmd, cmd)
writeRespose(t.device, reqChanID, cmd, nil, swInsNotSupported)
}
}
}
const (
busUSB = 0x03
vendorID = 0x15d9
productID = 0x0a37
frameTypeInit = 0x80
frameTypeCont = 0x00
CmdPing CmdType = 0x01 // Echo data through local processor only
CmdMsg CmdType = 0x03 // Send U2F message frame
CmdLock CmdType = 0x04 // Send lock channel command
CmdInit CmdType = 0x06 // Channel initialization
CmdWink CmdType = 0x08 // Send device identification wink
CmdCbor CmdType = 0x10 // Send encapsulated CTAP CBOR
CmdSync CmdType = 0x3c // Protocol resync command
CmdError CmdType = 0x3f // Error response
vendorSpecificFirstCmd = 0x40
vendorSpecificLastCmd = 0x7f
reportLen = 64
initialPacketDataLen = reportLen - 7
contPacketDataLen = reportLen - 5
u2fProtocolVersion = 2
deviceMajor = 1
deviceMinor = 0
deviceBuild = 0
winkCapability = 0x01
lockCapability = 0x02
cborCapability = 0x04
nmsgCapability = 0x08
swInsNotSupported = 0x6D00 // The Instruction of the request is not supported
)
type CmdType uint8
func (c CmdType) IsVendorSpecific() bool {
return c >= vendorSpecificFirstCmd && c <= vendorSpecificLastCmd
}
func (c CmdType) String() string {
switch c {
case CmdPing:
return "CmdPing"
case CmdMsg:
return "CmdMsg"
case CmdLock:
return "CmdLock"
case CmdInit:
return "CmdInit"
case CmdWink:
return "CmdWink"
case CmdSync:
return "CmdSync"
case CmdError:
return "CmdError"
case CmdCbor:
return "CmdCbor"
}
if c >= vendorSpecificFirstCmd && c <= vendorSpecificLastCmd {
return fmt.Sprintf("CmdVendor<%d>", c)
}
return fmt.Sprintf("CmdUnknown<%d>", c)
}
// src: http://www.usb.org/developers/hidpage/HUTRR48.pdf
var rdesc = []byte{
0x06, 0xd0, 0xf1, // USAGE_PAGE (FIDO Alliance)
0x09, 0x01, // USAGE (U2F HID Authenticator Device)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x20, // USAGE (Input Report Data)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x40, // REPORT_COUNT (64)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x21, // USAGE (Output Report Data)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x40, // REPORT_COUNT (64)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
}
func parsePackets(ctx context.Context, evtChan chan uhid.Event, pktChan chan Packet) {
for {
var (
evt uhid.Event
ok bool
)
select {
case evt, ok = <-evtChan:
if !ok {
return
}
case <-ctx.Done():
return
}
if evt.Err != nil {
log.Fatalf("got evt err: %s", evt.Err)
}
// Output means the kernel has sent us data
if evt.Type == uhid.Output {
r := newPacketReader(bytes.NewReader(evt.Data))
b1 := make([]byte, 1)
r.ReadFull(b1) // ignore first byte
var channelID uint32
r.Read(binary.BigEndian, &channelID)
_, err := r.ReadFull(b1)
if err != nil {
log.Printf("U2F protocol read error")
continue
}
typeOrSeqNo := b1[0]
if typeOrSeqNo&frameTypeInit == frameTypeInit {
typ := typeOrSeqNo
cmd := typ ^ frameTypeInit
var totalSize uint16
r.Read(binary.BigEndian, &totalSize)
data := make([]byte, initialPacketDataLen)
_, err := r.ReadFull(data)
if err != nil {
log.Printf("U2F protocol read error")
continue
}
p := Packet{
ChannelID: channelID,
IsInitial: true,
Command: CmdType(cmd),
TotalSize: totalSize,
Data: data,
}
select {
case pktChan <- p:
case <-ctx.Done():
return
}
} else {
seqNo := typeOrSeqNo
data := make([]byte, contPacketDataLen)
_, err := r.ReadFull(data)
if err != nil {
log.Printf("U2F protocol read error")
continue
}
p := Packet{
ChannelID: channelID,
SeqNo: seqNo,
Data: data,
}
select {
case pktChan <- p:
case <-ctx.Done():
return
}
}
}
}
}
type Packet struct {
ChannelID uint32
IsInitial bool
Command CmdType
SeqNo byte
TotalSize uint16
Data []byte
}
func newPacketReader(r io.Reader) *packetReader {
return &packetReader{
r: r,
}
}
type packetReader struct {
r io.Reader
err error
}
func (r *packetReader) Error() error {
return r.err
}
func (pr *packetReader) Read(order binary.ByteOrder, data interface{}) error {
if pr.err != nil {
return pr.err
}
err := binary.Read(pr.r, order, data)
if err != nil {
pr.err = err
}
return err
}
func (pr *packetReader) ReadFull(b []byte) (int, error) {
if pr.err != nil {
return 0, pr.err
}
n, err := io.ReadFull(pr.r, b)
if err != nil {
pr.err = err
return n, err
}
return n, nil
}
func mustRand(size int) []byte {
b := make([]byte, size)
if _, err := rand.Read(b); err != nil {
panic(err)
}
return b
}
type frameInit struct {
ChannelID uint32
Command uint8
Data []byte
TotalPayloadLen uint16
}
func (fi *frameInit) Marshal() []byte {
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, fi.ChannelID)
buf.WriteByte(fi.Command)
binary.Write(buf, binary.BigEndian, fi.TotalPayloadLen)
buf.Write(fi.Data)
if len(fi.Data) < initialPacketDataLen {
pad := make([]byte, initialPacketDataLen-len(fi.Data))
buf.Write(pad)
}
return buf.Bytes()
}
type frameCont struct {
ChannelID uint32
SeqNo uint8
Data []byte
}
func (fi *frameCont) Marshal() []byte {
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, fi.ChannelID)
buf.WriteByte(fi.SeqNo)
buf.Write(fi.Data)
if len(fi.Data) < contPacketDataLen {
pad := make([]byte, contPacketDataLen-len(fi.Data))
buf.Write(pad)
}
return buf.Bytes()
}
type initResponse struct {
Nonce [8]byte
Channel uint32
ProtocolVersion byte
MajorDeviceVersion byte
MinorDeviceVersion byte
BuildDeviceVersion byte
RawCapabilities byte
}
func newInitResponse(channelID uint32, nonce [8]byte) *initResponse {
return &initResponse{
Nonce: nonce,
Channel: channelID,
ProtocolVersion: u2fProtocolVersion,
MajorDeviceVersion: deviceMajor,
MinorDeviceVersion: deviceMinor,
BuildDeviceVersion: deviceBuild,
// RawCapabilities: winkCapability,
}
}
func (resp *initResponse) Marshal() []byte {
buf := new(bytes.Buffer)
buf.Write(resp.Nonce[:])
binary.Write(buf, binary.BigEndian, resp.Channel)
buf.Write([]byte{
resp.ProtocolVersion,
resp.MajorDeviceVersion,
resp.MinorDeviceVersion,
resp.BuildDeviceVersion,
resp.RawCapabilities,
})
return buf.Bytes()
}
func (t *SoftToken) WriteResponse(ctx context.Context, evt AuthEvent, data []byte, status uint16) error {
return writeRespose(t.device, evt.chanID, evt.cmd, data, status)
}
func writeRespose(d *uhid.Device, chanID uint32, cmd CmdType, data []byte, status uint16) error {
initial := true
pktSize := initialPacketDataLen
if status > 0 {
statusBytes := make([]byte, 2)
binary.BigEndian.PutUint16(statusBytes, status)
data = append(data, statusBytes...)
}
totalSize := uint16(len(data))
var seqNo uint8
for len(data) > 0 {
sliceSize := pktSize
if len(data) < sliceSize {
sliceSize = len(data)
}
pktData := data[:sliceSize]
data = data[sliceSize:]
if initial {
initial = false
pktSize = contPacketDataLen
frame := frameInit{
ChannelID: chanID,
Command: uint8(cmd) | frameTypeInit,
Data: pktData,
TotalPayloadLen: totalSize,
}
payload := frame.Marshal()
resp := uhid.Input2Request{
RequestType: uhid.Input2,
DataSize: uint16(len(payload)),
}
copy(resp.Data[:], payload)
err := d.WriteEvent(resp)
if err != nil {
return err
}
} else {
frame := frameCont{
ChannelID: chanID,
SeqNo: seqNo,
Data: pktData,
}
payload := frame.Marshal()
resp := uhid.Input2Request{
RequestType: uhid.Input2,
DataSize: uint16(len(payload)),
}
copy(resp.Data[:], payload)
err := d.WriteEvent(resp)
if err != nil {
return err
}
seqNo++
}
}
return nil
}
0707010000000C000081A40000000000000000000000016493935C000000F4000000000000000000000000000000000000002100000000tpm-fido-20230621.5f8828b/go.modmodule github.com/psanford/tpm-fido
go 1.16
require (
github.com/foxcpp/go-assuan v1.0.0
github.com/google/go-tpm v0.3.2
github.com/psanford/uhid v0.0.0-20210426002309-4864eff247db
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
)
0707010000000D000081A40000000000000000000000016493935C00004AD6000000000000000000000000000000000000002100000000tpm-fido-20230621.5f8828b/go.sumcloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
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/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/foxcpp/go-assuan v1.0.0 h1:KDSEmEGdHOzOjKmFUoaX/FGk1NxG/mHNAIdyksM8e4A=
github.com/foxcpp/go-assuan v1.0.0/go.mod h1:dQEJ9EaSSrUjd/g2PVaVEuV0TaoMDGwoh9XIDH1Q/Sc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI=
github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
github.com/google/go-tpm v0.3.2 h1:3iQQ2dlEf+1no7CLlfLPYzxhQy7j2G/emBqU5okydaw=
github.com/google/go-tpm v0.3.2/go.mod h1:j71sMBTfp3X5jPHz852ZOfQMUOf65Gb/Th8pRmp7fvg=
github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0=
github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
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/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/psanford/uhid v0.0.0-20210426002309-4864eff247db h1:V67//QISkDEEV+VrYgIzmtG+UCjDX8gQiMuf7f50i8A=
github.com/psanford/uhid v0.0.0-20210426002309-4864eff247db/go.mod h1:l7AmGL56H606RdpAQZ3qizn5ur7SoO9uQA2ZT7ZJ4RI=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
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/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
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/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d h1:MiWWjyhUzZ+jvhZvloX6ZrUsdEghn8a64Upd8EMHglE=
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
0707010000000E000041ED0000000000000000000000016493935C00000000000000000000000000000000000000000000002300000000tpm-fido-20230621.5f8828b/internal0707010000000F000041ED0000000000000000000000016493935C00000000000000000000000000000000000000000000002B00000000tpm-fido-20230621.5f8828b/internal/lencode07070100000010000081A40000000000000000000000016493935C00000D58000000000000000000000000000000000000003600000000tpm-fido-20230621.5f8828b/internal/lencode/lencode.go// This is a fork of github.com/psanford/lencode
// with the len prefix changed to a single byte
package lencode
import (
"bytes"
"errors"
"io"
)
type Encoder struct {
w io.Writer
separator []byte
err error
}
type Option struct {
encoderOpt func(e *Encoder)
decoderOpt func(e *Decoder)
}
// Specify a record separator to use as an integrity
// check when decoding. This defaults to \x6c\x65\x6e\x63 ("lenc").
// Set to nil to disable.
func SeparatorOpt(s []byte) Option {
return Option{
encoderOpt: func(e *Encoder) {
e.separator = s
},
decoderOpt: func(e *Decoder) {
e.separator = s
},
}
}
var defaultSeparator = []byte{'l', 'e', 'n', 'c'}
var separatorMismatchErr = errors.New("Separator mismatch")
func NewEncoder(w io.Writer, opts ...Option) *Encoder {
e := &Encoder{
w: w,
separator: defaultSeparator,
}
for _, opt := range opts {
opt.encoderOpt(e)
}
return e
}
// Encode a message to the underlying writer. It is not safe
// to call this method concurrently.
func (e *Encoder) Encode(msg []byte) error {
if e.err != nil {
return e.err
}
msgLen := int64(len(msg))
if msgLen > 0xff {
e.err = errors.New("Message too long to encode length in 1 byte")
return e.err
}
if e.separator != nil {
e.write(e.separator)
}
e.write([]byte{byte(msgLen)})
e.write(msg)
return e.err
}
func (e *Encoder) write(b []byte) error {
if e.err != nil {
return e.err
}
_, err := e.w.Write(b)
if err != nil {
e.err = err
}
return err
}
type Decoder struct {
r io.Reader
separator []byte
prefixBuf []byte
err error
pendingPrefix bool
pendingLen int
}
func NewDecoder(r io.Reader, opts ...Option) *Decoder {
d := &Decoder{
r: r,
separator: defaultSeparator,
}
for _, opt := range opts {
opt.decoderOpt(d)
}
d.prefixBuf = make([]byte, len(d.separator)+1)
return d
}
// Decode the next message from the io.Reader.
func (d *Decoder) Decode() ([]byte, error) {
if err := d.readPrefix(); err != nil {
return nil, err
}
buf := make([]byte, d.pendingLen)
if err := d.DecodeInto(buf); err != nil {
return nil, err
}
return buf, nil
}
// Decode the next message into a provided byte slice.
// Use NextLen() to ensure the slice is large enough
// for the message.
func (d *Decoder) DecodeInto(b []byte) error {
if err := d.readPrefix(); err != nil {
return err
}
if d.pendingLen < len(b) {
d.err = errors.New("Buffer not large enough for next message")
return d.err
}
if _, err := io.ReadFull(d.r, b[:d.pendingLen]); err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
d.err = err
return d.err
}
d.pendingPrefix = false
d.pendingLen = 0
return nil
}
// Return the length of the next message
func (d *Decoder) NextLen() (int, error) {
if d.err != nil {
return 0, d.err
}
if !d.pendingPrefix {
if err := d.readPrefix(); err != nil {
return 0, err
}
}
return d.pendingLen, nil
}
func (d *Decoder) readPrefix() error {
if d.err != nil {
return d.err
}
if d.pendingPrefix {
return nil
}
if _, err := io.ReadFull(d.r, d.prefixBuf); err != nil {
d.err = err
return err
}
if len(d.separator) > 0 {
if !bytes.Equal(d.prefixBuf[:len(d.separator)], d.separator) {
d.err = separatorMismatchErr
return d.err
}
}
l := d.prefixBuf[len(d.separator)]
d.pendingPrefix = true
d.pendingLen = int(l)
return d.err
}
07070100000011000081A40000000000000000000000016493935C00000905000000000000000000000000000000000000003B00000000tpm-fido-20230621.5f8828b/internal/lencode/lencode_test.gopackage lencode
import (
"bytes"
"io"
"testing"
)
func TestEncodeDecode(t *testing.T) {
var buf bytes.Buffer
enc := NewEncoder(&buf)
payload0 := make([]byte, 16)
for i := 0; i < len(payload0); i++ {
payload0[i] = 'a'
}
if err := enc.Encode(payload0); err != nil {
t.Fatal(err)
}
payload1 := make([]byte, 255)
for i := 0; i < len(payload1); i++ {
payload1[i] = 'b'
}
if err := enc.Encode(payload1); err != nil {
t.Fatal(err)
}
dec := NewDecoder(&buf)
n, err := dec.NextLen()
if err != nil {
t.Fatal(err)
}
if n != len(payload0) {
t.Fatalf("payload0 len mismatch %d vs %d", n, len(payload0))
}
got0, err := dec.Decode()
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(got0, payload0) {
t.Fatalf("Payload0 mismatch %v vs %v", got0, payload0)
}
got1, err := dec.Decode()
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(got1, payload1) {
t.Fatalf("Payload1 mismatch %v vs %v", got1, payload1)
}
got2, err := dec.Decode()
if err != io.EOF {
t.Fatalf("Expected EOF, got %s %v", err, got2)
}
}
func TestSeparatorMismatch(t *testing.T) {
var buf bytes.Buffer
enc := NewEncoder(&buf, SeparatorOpt(nil))
payload0 := make([]byte, 16)
for i := 0; i < len(payload0); i++ {
payload0[i] = 'a'
}
if err := enc.Encode(payload0); err != nil {
t.Fatal(err)
}
r := bytes.NewReader(buf.Bytes())
dec := NewDecoder(r)
_, err := dec.Decode()
if err != separatorMismatchErr {
t.Fatalf("Expected %s got %s", separatorMismatchErr, err)
}
r = bytes.NewReader(buf.Bytes())
dec = NewDecoder(r, SeparatorOpt(nil))
got0, err := dec.Decode()
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(got0, payload0) {
t.Fatalf("Payload0 mismatch %v vs %v", got0, payload0)
}
}
func TestDecodeUnexpectedEOF(t *testing.T) {
payload := []byte{0x01}
r := bytes.NewReader(payload)
dec := NewDecoder(r, SeparatorOpt(nil))
_, err := dec.Decode()
if err != io.ErrUnexpectedEOF {
t.Fatalf("Short read should trigger UnexpectedEOF error but was %s", err)
}
}
func TestTooLong(t *testing.T) {
var buf bytes.Buffer
enc := NewEncoder(&buf)
payload := bytes.Repeat([]byte{'a'}, 256)
err := enc.Encode(payload)
expect := "Message too long to encode length in 1 byte"
if err.Error() != expect {
t.Fatalf("Expected too long error, got: %s", err)
}
}
07070100000012000041ED0000000000000000000000016493935C00000000000000000000000000000000000000000000002100000000tpm-fido-20230621.5f8828b/memory07070100000013000081A40000000000000000000000016493935C000009D1000000000000000000000000000000000000002B00000000tpm-fido-20230621.5f8828b/memory/memory.gopackage memory
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"fmt"
"math/big"
"golang.org/x/crypto/chacha20poly1305"
)
type Mem struct {
masterPrivateKey []byte
signCounter uint32
}
func New() (*Mem, error) {
return &Mem{
masterPrivateKey: mustRand(chacha20poly1305.KeySize),
}, nil
}
func (m *Mem) Counter() uint32 {
m.signCounter++
return m.signCounter
}
func (m *Mem) RegisterKey(applicationParam []byte) ([]byte, *big.Int, *big.Int, error) {
curve := elliptic.P256()
childPrivateKey, x, y, err := elliptic.GenerateKey(curve, rand.Reader)
if err != nil {
return nil, nil, nil, fmt.Errorf("gen key err: %w", err)
}
metadata := []byte("fido_wrapping_key")
metadata = append(metadata, applicationParam...)
h := sha256.New()
h.Write(metadata)
sum := h.Sum(nil)
aead, err := chacha20poly1305.NewX(m.masterPrivateKey)
if err != nil {
return nil, nil, nil, fmt.Errorf("chacha NewX err: %w", err)
}
nonce := mustRand(chacha20poly1305.NonceSizeX)
encryptedChildPrivateKey := aead.Seal(nil, nonce, childPrivateKey, sum)
keyHandle := make([]byte, 0, len(nonce)+len(encryptedChildPrivateKey))
keyHandle = append(keyHandle, nonce...)
keyHandle = append(keyHandle, encryptedChildPrivateKey...)
if len(keyHandle) > 255 {
panic("keyHandle is too big")
}
return keyHandle, x, y, nil
}
func (m *Mem) SignASN1(keyHandle, applicationParam, digest []byte) ([]byte, error) {
aead, err := chacha20poly1305.NewX(m.masterPrivateKey)
if err != nil {
panic(err)
}
if len(keyHandle) < chacha20poly1305.NonceSizeX {
return nil, fmt.Errorf("incorrect size for key handle: %d smaller than nonce)", len(keyHandle))
}
nonce := keyHandle[:chacha20poly1305.NonceSizeX]
cipherText := keyHandle[chacha20poly1305.NonceSizeX:]
metadata := []byte("fido_wrapping_key")
metadata = append(metadata, applicationParam[:]...)
h := sha256.New()
h.Write(metadata)
sum := h.Sum(nil)
childPrivateKey, err := aead.Open(nil, nonce, cipherText, sum)
if err != nil {
return nil, fmt.Errorf("open child private key err: %w", err)
}
var ecdsaKey ecdsa.PrivateKey
ecdsaKey.D = new(big.Int).SetBytes(childPrivateKey)
ecdsaKey.PublicKey.Curve = elliptic.P256()
ecdsaKey.PublicKey.X, ecdsaKey.PublicKey.Y = ecdsaKey.PublicKey.Curve.ScalarBaseMult(ecdsaKey.D.Bytes())
return ecdsa.SignASN1(rand.Reader, &ecdsaKey, digest)
}
func mustRand(size int) []byte {
b := make([]byte, size)
if _, err := rand.Read(b); err != nil {
panic(err)
}
return b
}
07070100000014000041ED0000000000000000000000016493935C00000000000000000000000000000000000000000000002300000000tpm-fido-20230621.5f8828b/pinentry07070100000015000081A40000000000000000000000016493935C00000E55000000000000000000000000000000000000002F00000000tpm-fido-20230621.5f8828b/pinentry/pinentry.gopackage pinentry
import (
"context"
"errors"
"fmt"
"log"
"os/exec"
"sync"
"time"
assuan "github.com/foxcpp/go-assuan/client"
"github.com/foxcpp/go-assuan/pinentry"
)
func New() *Pinentry {
return &Pinentry{}
}
type Pinentry struct {
mu sync.Mutex
activeRequest *request
}
type request struct {
timeout time.Duration
pendingResult chan Result
extendTimeout chan time.Duration
challengeParam [32]byte
applicationParam [32]byte
}
type Result struct {
OK bool
Error error
}
func (pe *Pinentry) ConfirmPresence(prompt string, challengeParam, applicationParam [32]byte) (chan Result, error) {
pe.mu.Lock()
defer pe.mu.Unlock()
timeout := 2 * time.Second
if pe.activeRequest != nil {
if challengeParam != pe.activeRequest.challengeParam || applicationParam != pe.activeRequest.applicationParam {
return nil, errors.New("other request already in progress")
}
extendTimeoutChan := pe.activeRequest.extendTimeout
go func() {
select {
case extendTimeoutChan <- timeout:
case <-time.After(timeout):
}
}()
return pe.activeRequest.pendingResult, nil
}
pe.activeRequest = &request{
timeout: timeout,
challengeParam: challengeParam,
applicationParam: applicationParam,
pendingResult: make(chan Result),
extendTimeout: make(chan time.Duration),
}
go pe.prompt(pe.activeRequest, prompt)
return pe.activeRequest.pendingResult, nil
}
func (pe *Pinentry) prompt(req *request, prompt string) {
sendResult := func(r Result) {
select {
case req.pendingResult <- r:
case <-time.After(req.timeout):
// we expect requests to come in every ~750ms.
// If we've been waiting for 2 seconds the client
// is likely gone.
}
pe.mu.Lock()
pe.activeRequest = nil
pe.mu.Unlock()
}
childCtx, cancel := context.WithCancel(context.Background())
defer cancel()
p, cmd, err := launchPinEntry(childCtx)
if err != nil {
sendResult(Result{
OK: false,
Error: fmt.Errorf("failed to start pinentry: %w", err),
})
return
}
defer func() {
cancel()
cmd.Wait()
}()
defer p.Shutdown()
p.SetTitle("TPM-FIDO")
p.SetPrompt("TPM-FIDO")
p.SetDesc(prompt)
promptResult := make(chan bool)
go func() {
err := p.Confirm()
promptResult <- err == nil
}()
timer := time.NewTimer(req.timeout)
for {
select {
case ok := <-promptResult:
sendResult(Result{
OK: ok,
})
return
case <-timer.C:
sendResult(Result{
OK: false,
Error: errors.New("request timed out"),
})
return
case d := <-req.extendTimeout:
if !timer.Stop() {
<-timer.C
}
timer.Reset(d)
}
}
}
func FindPinentryGUIPath() string {
candidates := []string{
"pinentry-gnome3",
"pinentry-qt5",
"pinentry-qt4",
"pinentry-qt",
"pinentry-gtk-2",
"pinentry-x11",
"pinentry-fltk",
}
for _, candidate := range candidates {
p, _ := exec.LookPath(candidate)
if p != "" {
return p
}
}
return ""
}
func launchPinEntry(ctx context.Context) (*pinentry.Client, *exec.Cmd, error) {
pinEntryCmd := FindPinentryGUIPath()
if pinEntryCmd == "" {
log.Printf("Failed to detect gui pinentry binary. Falling back to default `pinentry`")
pinEntryCmd = "pinentry"
}
cmd := exec.CommandContext(ctx, pinEntryCmd)
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, nil, err
}
stdin, err := cmd.StdinPipe()
if err != nil {
return nil, nil, err
}
if err := cmd.Start(); err != nil {
return nil, nil, err
}
var c pinentry.Client
c.Session, err = assuan.Init(assuan.ReadWriteCloser{
ReadCloser: stdout,
WriteCloser: stdin,
})
if err != nil {
return nil, nil, err
}
return &c, cmd, nil
}
07070100000016000041ED0000000000000000000000016493935C00000000000000000000000000000000000000000000002900000000tpm-fido-20230621.5f8828b/sitesignatures07070100000017000081A40000000000000000000000016493935C00000828000000000000000000000000000000000000003B00000000tpm-fido-20230621.5f8828b/sitesignatures/sitesignatures.gopackage sitesignatures
import (
"crypto/sha256"
"fmt"
)
// from https://github.com/danstiner/rust-u2f/blob/master/u2f-core/src/known_app_ids.rs
// and https://github.com/github/SoftU2F/blob/master/SoftU2FTool/KnownFacets.swift
var reverseSignatures = map[[32]byte]string{
hashURL("https://api-9dcf9b83.duosecurity.com"): "duosecurity.com",
hashURL("https://dashboard.stripe.com"): "dashboard.stripe.com",
hashURL("https://demo.yubico.com"): "demo.yubico.com",
hashURL("https://github.com/u2f/trusted_facets"): "github.com",
hashURL("https://gitlab.com"): "gitlab.com",
hashURL("https://id.fedoraproject.org/u2f-origins.json"): "id.fedoraproject.org",
hashURL("https://keepersecurity.com"): "keepersecurity.com",
hashURL("https://lastpass.com"): "lastpass.com",
hashURL("https://mdp.github.io"): "mdp.github.io",
hashURL("https://personal.vanguard.com"): "vanguard.com",
hashURL("https://u2f.aws.amazon.com/app-id.json"): "aws.amazon.com",
hashURL("https://u2f.bin.coffee"): "u2f.bin.coffee",
hashURL("https://vault.bitwarden.com/app-id.json"): "vault.bitwarden.com",
hashURL("https://www.dropbox.com/u2f-app-id.json"): "dropbox.com",
hashURL("https://www.fastmail.com"): "www.fastmail.com",
hashURL("https://www.gstatic.com/securitykey/origins.json"): "google.com",
hashURL("bin.coffee"): "bin.coffee",
hashURL("coinbase.com"): "coinbase.com",
hashURL("demo.yubico.com"): "demo.yubico.com",
hashURL("github.com"): "github.com",
hashURL("webauthn.bin.coffee"): "webauthn.bin.coffee",
hashURL("webauthn.io"): "webauthn.io",
}
func hashURL(url string) [32]byte {
return sha256.Sum256([]byte(url))
}
func FromAppParam(sig [32]byte) string {
site := reverseSignatures[sig]
if site == "" {
site = fmt.Sprintf("<unknown %x>", sig)
}
return site
}
07070100000018000041ED0000000000000000000000016493935C00000000000000000000000000000000000000000000002500000000tpm-fido-20230621.5f8828b/statuscode07070100000019000081A40000000000000000000000016493935C000002BE000000000000000000000000000000000000003300000000tpm-fido-20230621.5f8828b/statuscode/statuscode.gopackage statuscode
// status codes from section 3.3 of
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
const (
NoError = 0x9000 // The command completed successfully without error.
ConditionsNotSatisfied = 0x6985 // The request was rejected due to test-of-user-presence being required.
WrongData = 0x6A80 // The request was rejected due to an invalid key handle.
WrongLength = 0x6700 // The length of the request was invalid
ClaNotSupported = 0x6E00 // The Class byte of the request is not supported
InsNotSupported = 0x6D00 // The Instruction of the request is not supported
)
0707010000001A000041ED0000000000000000000000016493935C00000000000000000000000000000000000000000000001E00000000tpm-fido-20230621.5f8828b/tpm0707010000001B000081A40000000000000000000000016493935C000014F0000000000000000000000000000000000000002500000000tpm-fido-20230621.5f8828b/tpm/tpm.gopackage tpm
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"fmt"
"io"
"math/big"
"sync"
"time"
"github.com/google/go-tpm/tpm2"
"github.com/psanford/tpm-fido/internal/lencode"
"golang.org/x/crypto/cryptobyte"
"golang.org/x/crypto/cryptobyte/asn1"
"golang.org/x/crypto/hkdf"
)
var (
separator = []byte("TPM")
seedSizeBytes = 20
)
type TPM struct {
devicePath string
mu sync.Mutex
}
func (t *TPM) open() (io.ReadWriteCloser, error) {
return tpm2.OpenTPM(t.devicePath)
}
func New(devicePath string) (*TPM, error) {
t := &TPM{
devicePath: devicePath,
}
tpm, err := t.open()
if err != nil {
return nil, err
}
tpm.Close()
return t, nil
}
func primaryKeyTmpl(seed, applicationParam []byte) tpm2.Public {
info := append([]byte("tpm-fido-application-key"), applicationParam...)
r := hkdf.New(sha256.New, seed, []byte{}, info)
unique := tpm2.ECPoint{
XRaw: make([]byte, 32),
YRaw: make([]byte, 32),
}
if _, err := io.ReadFull(r, unique.XRaw); err != nil {
panic(err)
}
if _, err := io.ReadFull(r, unique.YRaw); err != nil {
panic(err)
}
return tpm2.Public{
Type: tpm2.AlgECC,
NameAlg: tpm2.AlgSHA256,
Attributes: tpm2.FlagRestricted | tpm2.FlagDecrypt |
tpm2.FlagFixedTPM | tpm2.FlagFixedParent |
tpm2.FlagSensitiveDataOrigin | tpm2.FlagUserWithAuth,
ECCParameters: &tpm2.ECCParams{
Symmetric: &tpm2.SymScheme{
Alg: tpm2.AlgAES,
KeyBits: 128,
Mode: tpm2.AlgCFB,
},
CurveID: tpm2.CurveNISTP256,
Point: unique,
},
}
}
var baseTime = time.Date(2021, time.January, 1, 0, 0, 0, 0, time.UTC)
func (t *TPM) Counter() uint32 {
unix := time.Now().Unix()
return uint32(unix - baseTime.Unix())
}
// Register a new key with the TPM for the given applicationParam.
// RegisterKey returns the KeyHandle or an error.
func (t *TPM) RegisterKey(applicationParam []byte) ([]byte, *big.Int, *big.Int, error) {
t.mu.Lock()
defer t.mu.Unlock()
tpm, err := t.open()
if err != nil {
return nil, nil, nil, fmt.Errorf("open tpm err: %w", err)
}
defer tpm.Close()
randSeed := mustRand(seedSizeBytes)
primaryTmpl := primaryKeyTmpl(randSeed, applicationParam)
childTmpl := tpm2.Public{
Type: tpm2.AlgECC,
NameAlg: tpm2.AlgSHA256,
Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent |
tpm2.FlagSensitiveDataOrigin | tpm2.FlagUserWithAuth |
tpm2.FlagSign,
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),
},
},
}
parentHandle, _, err := tpm2.CreatePrimary(tpm, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", primaryTmpl)
if err != nil {
return nil, nil, nil, fmt.Errorf("CreatePrimary key err: %w", err)
}
defer tpm2.FlushContext(tpm, parentHandle)
private, public, _, _, _, err := tpm2.CreateKey(tpm, parentHandle, tpm2.PCRSelection{}, "", "", childTmpl)
if err != nil {
return nil, nil, nil, fmt.Errorf("CreateKey (child) err: %w", err)
}
var out bytes.Buffer
enc := lencode.NewEncoder(&out, lencode.SeparatorOpt(separator))
enc.Encode(private)
enc.Encode(public)
enc.Encode(randSeed)
keyHandle, _, err := tpm2.Load(tpm, parentHandle, "", public, private)
if err != nil {
return nil, nil, nil, fmt.Errorf("load child key err: %w", err)
}
defer tpm2.FlushContext(tpm, keyHandle)
pub, _, _, err := tpm2.ReadPublic(tpm, keyHandle)
if err != nil {
return nil, nil, nil, fmt.Errorf("read public key err: %w", err)
}
x := new(big.Int).SetBytes(pub.ECCParameters.Point.XRaw)
y := new(big.Int).SetBytes(pub.ECCParameters.Point.YRaw)
return out.Bytes(), x, y, nil
}
func (t *TPM) SignASN1(keyHandle, applicationParam, digest []byte) ([]byte, error) {
t.mu.Lock()
defer t.mu.Unlock()
tpm, err := t.open()
if err != nil {
return nil, fmt.Errorf("open tpm err: %w", err)
}
defer tpm.Close()
dec := lencode.NewDecoder(bytes.NewReader(keyHandle), lencode.SeparatorOpt(separator))
invalidHandleErr := fmt.Errorf("invalid key handle")
private, err := dec.Decode()
if err != nil {
return nil, invalidHandleErr
}
public, err := dec.Decode()
if err != nil {
return nil, invalidHandleErr
}
seed, err := dec.Decode()
if err != nil {
return nil, invalidHandleErr
}
_, err = dec.Decode()
if err != io.EOF {
return nil, invalidHandleErr
}
srkTemplate := primaryKeyTmpl(seed, applicationParam)
parentHandle, _, err := tpm2.CreatePrimary(tpm, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", srkTemplate)
if err != nil {
return nil, fmt.Errorf("CreatePrimary key err: %w", err)
}
defer tpm2.FlushContext(tpm, parentHandle)
key, _, err := tpm2.Load(tpm, parentHandle, "", public, private)
if err != nil {
return nil, fmt.Errorf("Load err: %w", err)
}
defer tpm2.FlushContext(tpm, key)
scheme := &tpm2.SigScheme{
Alg: tpm2.AlgECDSA,
Hash: tpm2.AlgSHA256,
}
sig, err := tpm2.Sign(tpm, key, "", digest[:], nil, scheme)
if err != nil {
return nil, fmt.Errorf("sign err: %w", err)
}
var b cryptobyte.Builder
b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
b.AddASN1BigInt(sig.ECC.R)
b.AddASN1BigInt(sig.ECC.S)
})
return b.Bytes()
}
func mustRand(size int) []byte {
b := make([]byte, size)
if _, err := rand.Read(b); err != nil {
panic(err)
}
return b
}
0707010000001C000081A40000000000000000000000016493935C00002268000000000000000000000000000000000000002500000000tpm-fido-20230621.5f8828b/tpmfido.gopackage main
import (
"bytes"
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"flag"
"log"
"math/big"
"time"
"github.com/psanford/tpm-fido/attestation"
"github.com/psanford/tpm-fido/fidoauth"
"github.com/psanford/tpm-fido/fidohid"
"github.com/psanford/tpm-fido/memory"
"github.com/psanford/tpm-fido/pinentry"
"github.com/psanford/tpm-fido/sitesignatures"
"github.com/psanford/tpm-fido/statuscode"
"github.com/psanford/tpm-fido/tpm"
)
var backend = flag.String("backend", "tpm", "tpm|memory")
var device = flag.String("device", "/dev/tpmrm0", "TPM device path")
func main() {
flag.Parse()
s := newServer()
s.run()
}
type server struct {
pe *pinentry.Pinentry
signer Signer
}
type Signer interface {
RegisterKey(applicationParam []byte) ([]byte, *big.Int, *big.Int, error)
SignASN1(keyHandle, applicationParam, digest []byte) ([]byte, error)
Counter() uint32
}
func newServer() *server {
s := server{
pe: pinentry.New(),
}
if *backend == "tpm" {
signer, err := tpm.New(*device)
if err != nil {
panic(err)
}
s.signer = signer
} else if *backend == "memory" {
signer, err := memory.New()
if err != nil {
panic(err)
}
s.signer = signer
}
return &s
}
func (s *server) run() {
ctx := context.Background()
if pinentry.FindPinentryGUIPath() == "" {
log.Printf("warning: no gui pinentry binary detected in PATH. tpm-fido may not work correctly without a gui based pinentry")
}
token, err := fidohid.New(ctx, "tpm-fido")
if err != nil {
log.Fatalf("create fido hid error: %s", err)
}
go token.Run(ctx)
for evt := range token.Events() {
if evt.Error != nil {
log.Printf("got token error: %s", err)
continue
}
req := evt.Req
if req.Command == fidoauth.CmdAuthenticate {
log.Printf("got AuthenticateCmd site=%s", sitesignatures.FromAppParam(req.Authenticate.ApplicationParam))
s.handleAuthenticate(ctx, token, evt)
} else if req.Command == fidoauth.CmdRegister {
log.Printf("got RegisterCmd site=%s", sitesignatures.FromAppParam(req.Register.ApplicationParam))
s.handleRegister(ctx, token, evt)
} else if req.Command == fidoauth.CmdVersion {
log.Print("got VersionCmd")
s.handleVersion(ctx, token, evt)
} else {
log.Printf("unsupported request type: 0x%02x\n", req.Command)
// send a not supported error for any commands that we don't understand.
// Browsers depend on this to detect what features the token supports
// (i.e. the u2f backwards compatibility)
token.WriteResponse(ctx, evt, nil, statuscode.ClaNotSupported)
}
}
}
func (s *server) handleVersion(parentCtx context.Context, token *fidohid.SoftToken, evt fidohid.AuthEvent) {
token.WriteResponse(parentCtx, evt, []byte("U2F_V2"), statuscode.NoError)
}
func (s *server) handleAuthenticate(parentCtx context.Context, token *fidohid.SoftToken, evt fidohid.AuthEvent) {
req := evt.Req
keyHandle := req.Authenticate.KeyHandle
appParam := req.Authenticate.ApplicationParam[:]
dummySig := sha256.Sum256([]byte("meticulously-Bacardi"))
_, err := s.signer.SignASN1(keyHandle, appParam, dummySig[:])
if err != nil {
log.Printf("invalid key: %s (key handle size: %d)", err, len(keyHandle))
err := token.WriteResponse(parentCtx, evt, nil, statuscode.WrongData)
if err != nil {
log.Printf("send bad key handle msg err: %s", err)
}
return
}
switch req.Authenticate.Ctrl {
case fidoauth.CtrlCheckOnly,
fidoauth.CtrlDontEnforeUserPresenceAndSign,
fidoauth.CtrlEnforeUserPresenceAndSign:
default:
log.Printf("unknown authenticate control value: %d", req.Authenticate.Ctrl)
err := token.WriteResponse(parentCtx, evt, nil, statuscode.WrongData)
if err != nil {
log.Printf("send wrong-data msg err: %s", err)
}
return
}
if req.Authenticate.Ctrl == fidoauth.CtrlCheckOnly {
// check if the provided key is known by the token
log.Printf("check-only success")
// test-of-user-presence-required: note that despite the name this signals a success condition
err := token.WriteResponse(parentCtx, evt, nil, statuscode.ConditionsNotSatisfied)
if err != nil {
log.Printf("send bad key handle msg err: %s", err)
}
return
}
var userPresent uint8
if req.Authenticate.Ctrl == fidoauth.CtrlEnforeUserPresenceAndSign {
pinResultCh, err := s.pe.ConfirmPresence("FIDO Confirm Auth", req.Authenticate.ChallengeParam, req.Authenticate.ApplicationParam)
if err != nil {
log.Printf("pinentry err: %s", err)
token.WriteResponse(parentCtx, evt, nil, statuscode.ConditionsNotSatisfied)
return
}
childCtx, cancel := context.WithTimeout(parentCtx, 750*time.Millisecond)
defer cancel()
select {
case result := <-pinResultCh:
if result.OK {
userPresent = 0x01
} else {
if result.Error != nil {
log.Printf("Got pinentry result err: %s", result.Error)
}
// Got user cancelation, we want to propagate that so the browser gives up.
// This isn't normally supported by a key so there's no status code for this.
// WrongData seems like the least incorrect status code ¯\_(ツ)_/¯
err := token.WriteResponse(parentCtx, evt, nil, statuscode.WrongData)
if err != nil {
log.Printf("Write WrongData resp err: %s", err)
}
return
}
case <-childCtx.Done():
err := token.WriteResponse(parentCtx, evt, nil, statuscode.ConditionsNotSatisfied)
if err != nil {
log.Printf("Write swConditionsNotSatisfied resp err: %s", err)
}
return
}
}
signCounter := s.signer.Counter()
var toSign bytes.Buffer
toSign.Write(req.Authenticate.ApplicationParam[:])
toSign.WriteByte(userPresent)
binary.Write(&toSign, binary.BigEndian, signCounter)
toSign.Write(req.Authenticate.ChallengeParam[:])
sigHash := sha256.New()
sigHash.Write(toSign.Bytes())
sig, err := s.signer.SignASN1(keyHandle, appParam, sigHash.Sum(nil))
if err != nil {
log.Fatalf("auth sign err: %s", err)
}
var out bytes.Buffer
out.WriteByte(userPresent)
binary.Write(&out, binary.BigEndian, signCounter)
out.Write(sig)
err = token.WriteResponse(parentCtx, evt, out.Bytes(), statuscode.NoError)
if err != nil {
log.Printf("write auth response err: %s", err)
return
}
}
func (s *server) handleRegister(parentCtx context.Context, token *fidohid.SoftToken, evt fidohid.AuthEvent) {
ctx, cancel := context.WithTimeout(parentCtx, 750*time.Millisecond)
defer cancel()
req := evt.Req
pinResultCh, err := s.pe.ConfirmPresence("FIDO Confirm Register", req.Register.ChallengeParam, req.Register.ApplicationParam)
if err != nil {
log.Printf("pinentry err: %s", err)
token.WriteResponse(ctx, evt, nil, statuscode.ConditionsNotSatisfied)
return
}
select {
case result := <-pinResultCh:
if !result.OK {
if result.Error != nil {
log.Printf("Got pinentry result err: %s", result.Error)
}
// Got user cancelation, we want to propagate that so the browser gives up.
// This isn't normally supported by a key so there's no status code for this.
// WrongData seems like the least incorrect status code ¯\_(ツ)_/¯
err := token.WriteResponse(ctx, evt, nil, statuscode.WrongData)
if err != nil {
log.Printf("Write WrongData resp err: %s", err)
return
}
return
}
s.registerSite(parentCtx, token, evt)
case <-ctx.Done():
err := token.WriteResponse(ctx, evt, nil, statuscode.ConditionsNotSatisfied)
if err != nil {
log.Printf("Write swConditionsNotSatisfied resp err: %s", err)
return
}
}
}
func (s *server) registerSite(ctx context.Context, token *fidohid.SoftToken, evt fidohid.AuthEvent) {
req := evt.Req
keyHandle, x, y, err := s.signer.RegisterKey(req.Register.ApplicationParam[:])
if err != nil {
log.Printf("RegisteKey err: %s", err)
return
}
if len(keyHandle) > 255 {
log.Printf("Error: keyHandle too large: %d, max=255", len(keyHandle))
return
}
childPubKey := elliptic.Marshal(elliptic.P256(), x, y)
var toSign bytes.Buffer
toSign.WriteByte(0)
toSign.Write(req.Register.ApplicationParam[:])
toSign.Write(req.Register.ChallengeParam[:])
toSign.Write(keyHandle)
toSign.Write(childPubKey)
sigHash := sha256.New()
sigHash.Write(toSign.Bytes())
sum := sigHash.Sum(nil)
sig, err := ecdsa.SignASN1(rand.Reader, attestation.PrivateKey, sum)
if err != nil {
log.Fatalf("attestation sign err: %s", err)
}
var out bytes.Buffer
out.WriteByte(0x05) // reserved value
out.Write(childPubKey)
out.WriteByte(byte(len(keyHandle)))
out.Write(keyHandle)
out.Write(attestation.CertDer)
out.Write(sig)
err = token.WriteResponse(ctx, evt, out.Bytes(), statuscode.NoError)
if err != nil {
log.Printf("write register response err: %s", err)
return
}
}
func mustRand(size int) []byte {
b := make([]byte, size)
if _, err := rand.Read(b); err != nil {
panic(err)
}
return b
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!145 blocks