268 lines
6.4 KiB
Go
268 lines
6.4 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/sha1"
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/google/go-tpm/tpm"
|
|
"github.com/urfave/cli/v2"
|
|
)
|
|
|
|
type TpmKey struct {
|
|
Tpm string `json:"tpm"`
|
|
Keyblob string `json:"keyblob"`
|
|
}
|
|
|
|
type ErrorResponse struct {
|
|
Success bool `json:"success"`
|
|
Error string `json:"error"`
|
|
}
|
|
|
|
type ExternalSpecResponse struct {
|
|
Success bool `json:"success"`
|
|
Agent string `json:"agent"`
|
|
Specification string `json:"specification"`
|
|
Commands []string `json:"commands"`
|
|
}
|
|
|
|
type ExternalPublicKeyResponse struct {
|
|
Success bool `json:"success"`
|
|
PublicKeyBase64 string `json:"public_key_base64"`
|
|
}
|
|
|
|
type ExternalSignatureResponse struct {
|
|
Success bool `json:"success"`
|
|
SignatureBase64 string `json:"signature_base64"`
|
|
}
|
|
|
|
func printResponse(response any) error {
|
|
errorMessageJsonBytes, jsonErr := json.Marshal(response)
|
|
if jsonErr != nil {
|
|
return jsonErr
|
|
} else {
|
|
fmt.Println(string(errorMessageJsonBytes))
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func printResponseSlient(response any) {
|
|
if err := printResponse(response); err != nil {
|
|
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
err := innerMain()
|
|
if err != nil {
|
|
errorMessage := &ErrorResponse{
|
|
Success: false,
|
|
Error: fmt.Sprintf("%v", err),
|
|
}
|
|
if printResponse(errorMessage) != nil {
|
|
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
|
|
}
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func innerMain() error {
|
|
app := cli.App{
|
|
Name: "external-signer-tpm",
|
|
Usage: "External signer TPM",
|
|
Commands: []*cli.Command{
|
|
buildExternalSpecCommand(),
|
|
buildExternalPublicKeyCommand(),
|
|
buildExternalSignCommand(),
|
|
buildGenerateKeyCommand(),
|
|
},
|
|
Action: func(ctx *cli.Context) error {
|
|
fmt.Println("External signer TPM, specification: https://openwebstandard.org/rfc1")
|
|
return nil
|
|
},
|
|
}
|
|
|
|
if err := app.Run(os.Args); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func buildExternalSpecCommand() *cli.Command {
|
|
return &cli.Command{
|
|
Name: "external_spec",
|
|
Usage: "External specification",
|
|
Flags: []cli.Flag{},
|
|
Action: func(ctx *cli.Context) error {
|
|
externalSpecResponse := &ExternalSpecResponse{
|
|
Success: true,
|
|
Agent: "external-signer-tpm/0.1.0",
|
|
Specification: "External/1.0.0-alpha",
|
|
Commands: []string{
|
|
"external_public_key",
|
|
"external_sign",
|
|
},
|
|
}
|
|
printResponseSlient(externalSpecResponse)
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
func buildExternalPublicKeyCommand() *cli.Command {
|
|
return &cli.Command{
|
|
Name: "external_public_key",
|
|
Usage: "External public key",
|
|
Flags: []cli.Flag{
|
|
&cli.StringFlag{
|
|
Name: "parameter",
|
|
Usage: "Parameter",
|
|
Required: true,
|
|
},
|
|
},
|
|
Action: func(ctx *cli.Context) error {
|
|
parameter := ctx.String("parameter")
|
|
|
|
tpmKey, err := parseParameter(parameter)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot parse parameter: %s", err)
|
|
}
|
|
|
|
keyblob, err := base64.StdEncoding.DecodeString(tpmKey.Keyblob)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot decode keyblob: %s", err)
|
|
}
|
|
pubKey, err := tpm.UnmarshalRSAPublicKey(keyblob)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot get public key: %s", err)
|
|
}
|
|
|
|
pubKeyBytes, err := x509.MarshalPKIXPublicKey(pubKey)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot marshal public key: %s", err)
|
|
}
|
|
|
|
externalPublicKeyResponse := &ExternalPublicKeyResponse{
|
|
Success: true,
|
|
PublicKeyBase64: base64.StdEncoding.EncodeToString(pubKeyBytes),
|
|
}
|
|
|
|
printResponseSlient(externalPublicKeyResponse)
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
func buildExternalSignCommand() *cli.Command {
|
|
return &cli.Command{
|
|
Name: "external_sign",
|
|
Usage: "External sign",
|
|
Flags: []cli.Flag{
|
|
&cli.StringFlag{
|
|
Name: "parameter",
|
|
Usage: "Parameter",
|
|
Required: true,
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "alg",
|
|
Usage: "Algorithm",
|
|
Required: true,
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "message-base64",
|
|
Usage: "Message base64 encoded",
|
|
Required: true,
|
|
},
|
|
},
|
|
Action: func(ctx *cli.Context) error {
|
|
parameter := ctx.String("parameter")
|
|
alg := ctx.String("alg")
|
|
messageBase64 := ctx.String("message-base64")
|
|
message, err := base64.StdEncoding.DecodeString(messageBase64)
|
|
if err != nil {
|
|
return fmt.Errorf("unmarshal --message-base64 failed: %v", err)
|
|
}
|
|
|
|
println("ALG: ", alg)
|
|
|
|
tpmKey, err := parseParameter(parameter)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot parse parameter: %s", err)
|
|
}
|
|
|
|
rwc, err := tpm.OpenTPM(tpmKey.Tpm)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot open TPM %s, error: %s", tpmKey.Tpm, err)
|
|
}
|
|
defer rwc.Close()
|
|
|
|
// Compute the auth values as needed.
|
|
var srkAuth [20]byte
|
|
srkInput := os.Getenv(srkAuthEnvVar)
|
|
if srkInput != "" {
|
|
sa := sha1.Sum([]byte(srkInput))
|
|
copy(srkAuth[:], sa[:])
|
|
}
|
|
|
|
var usageAuth [20]byte
|
|
usageInput := os.Getenv(usageAuthEnvVar)
|
|
if usageInput != "" {
|
|
ua := sha1.Sum([]byte(usageInput))
|
|
copy(usageAuth[:], ua[:])
|
|
}
|
|
|
|
var migrationAuth [20]byte
|
|
migrationInput := os.Getenv(migrationAuthEnvVar)
|
|
if migrationInput != "" {
|
|
ma := sha1.Sum([]byte(migrationInput))
|
|
copy(migrationAuth[:], ma[:])
|
|
}
|
|
|
|
keyblob, err := base64.StdEncoding.DecodeString(tpmKey.Keyblob)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot decode keyblob: %s", err)
|
|
}
|
|
keyHandle, err := tpm.LoadKey2(rwc, keyblob, srkAuth[:])
|
|
if err != nil {
|
|
return fmt.Errorf("cannot load keyblob: %s", err)
|
|
}
|
|
defer tpm.CloseKey(rwc, keyHandle)
|
|
|
|
hash := crypto.SHA256.New()
|
|
if _, err = hash.Write(message); err != nil {
|
|
return fmt.Errorf("error building hash of data: %s", err)
|
|
}
|
|
hashed := hash.Sum(nil)
|
|
signature, err := tpm.Sign(rwc, usageAuth[:], keyHandle, crypto.SHA256, hashed[:])
|
|
if err != nil {
|
|
return fmt.Errorf("cannot perform sign operation: %s", err)
|
|
}
|
|
|
|
externalSignatureResponse := ExternalSignatureResponse{
|
|
Success: true,
|
|
SignatureBase64: base64.StdEncoding.EncodeToString(signature),
|
|
}
|
|
|
|
printResponseSlient(externalSignatureResponse)
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
func parseParameter(parameter string) (*TpmKey, error) {
|
|
parameterBytes, err := base64.StdEncoding.DecodeString(parameter)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse --parameter base64 failed: %v", err)
|
|
}
|
|
var tpmKey TpmKey
|
|
err = json.Unmarshal(parameterBytes, &tpmKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse --parameter base64 failed: %v", err)
|
|
}
|
|
return &tpmKey, nil
|
|
}
|