2022-02-16 09:53:10 +00:00
|
|
|
package tpm
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/google/go-attestation/attest"
|
|
|
|
"github.com/gorilla/websocket"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
2022-02-17 09:10:04 +00:00
|
|
|
// Get retrieves a message from a remote ws server after
|
|
|
|
// a successfully process of the TPM challenge
|
|
|
|
func Get(url string, opts ...Option) ([]byte, error) {
|
|
|
|
c := &config{}
|
|
|
|
c.apply(opts...)
|
|
|
|
|
|
|
|
header := c.header
|
|
|
|
if c.header == nil {
|
|
|
|
header = http.Header{}
|
|
|
|
}
|
|
|
|
|
2022-02-16 09:53:10 +00:00
|
|
|
dialer := websocket.DefaultDialer
|
2022-02-17 09:10:04 +00:00
|
|
|
if len(c.cacerts) > 0 {
|
2022-02-16 09:53:10 +00:00
|
|
|
pool := x509.NewCertPool()
|
2022-05-12 09:59:24 +00:00
|
|
|
if c.systemfallback {
|
|
|
|
systemPool, err := x509.SystemCertPool()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
pool = systemPool
|
|
|
|
}
|
|
|
|
|
2022-02-17 09:10:04 +00:00
|
|
|
pool.AppendCertsFromPEM(c.cacerts)
|
2022-02-16 09:53:10 +00:00
|
|
|
dialer = &websocket.Dialer{
|
|
|
|
Proxy: http.ProxyFromEnvironment,
|
|
|
|
HandshakeTimeout: 45 * time.Second,
|
|
|
|
TLSClientConfig: &tls.Config{
|
|
|
|
RootCAs: pool,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-16 11:52:30 +00:00
|
|
|
attestationData, aikBytes, err := getAttestationData(c)
|
2022-02-16 09:53:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-02-16 11:52:30 +00:00
|
|
|
hash, err := GetPubHash(opts...)
|
2022-02-16 09:53:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
token, err := getToken(attestationData)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
header.Add("Authorization", token)
|
|
|
|
wsURL := strings.Replace(url, "http", "ws", 1)
|
|
|
|
logrus.Infof("Using TPMHash %s to dial %s", hash, wsURL)
|
|
|
|
conn, resp, err := dialer.Dial(wsURL, header)
|
|
|
|
if err != nil {
|
2022-08-10 07:40:14 +00:00
|
|
|
if resp != nil {
|
|
|
|
if resp.StatusCode == http.StatusUnauthorized {
|
|
|
|
data, err := ioutil.ReadAll(resp.Body)
|
|
|
|
if err == nil {
|
|
|
|
return nil, errors.New(string(data))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return nil, fmt.Errorf("%w (Status: %s)", err, resp.Status)
|
2022-02-16 09:53:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer conn.Close()
|
|
|
|
|
|
|
|
_, msg, err := conn.NextReader()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("reading challenge: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var challenge Challenge
|
|
|
|
if err := json.NewDecoder(msg).Decode(&challenge); err != nil {
|
|
|
|
return nil, fmt.Errorf("unmarshaling Challenge: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-02-17 11:28:58 +00:00
|
|
|
challengeResp, err := getChallengeResponse(c, challenge.EC, aikBytes)
|
2022-02-16 09:53:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
writer, err := conn.NextWriter(websocket.BinaryMessage)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer writer.Close()
|
|
|
|
|
|
|
|
if err := json.NewEncoder(writer).Encode(challengeResp); err != nil {
|
|
|
|
return nil, fmt.Errorf("encoding ChallengeResponse: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := writer.Close(); err != nil {
|
|
|
|
return nil, fmt.Errorf("closing websocket writer: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, msg, err = conn.NextReader()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("reading payload from tpm get: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ioutil.ReadAll(msg)
|
|
|
|
}
|
|
|
|
|
2022-02-17 11:28:58 +00:00
|
|
|
func getChallengeResponse(c *config, ec *attest.EncryptedCredential, aikBytes []byte) (*ChallengeResponse, error) {
|
|
|
|
tpm, err := getTPM(c)
|
2022-02-16 09:53:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("opening tpm: %w", err)
|
|
|
|
}
|
|
|
|
defer tpm.Close()
|
|
|
|
|
|
|
|
aik, err := tpm.LoadAK(aikBytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer aik.Close(tpm)
|
|
|
|
|
|
|
|
secret, err := aik.ActivateCredential(tpm, *ec)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to activate credential: %w", err)
|
|
|
|
}
|
|
|
|
return &ChallengeResponse{
|
|
|
|
Secret: secret,
|
|
|
|
}, nil
|
|
|
|
}
|