feat: init commit
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
external-signer-tpm
|
||||
# ---> Go
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
# external-signer-tpm
|
||||
|
||||
> Reference: https://github.com/google/go-tpm/tree/main/examples/tpm-sign
|
||||
|
||||
|
||||
7
common.go
Normal file
7
common.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package main
|
||||
|
||||
const (
|
||||
srkAuthEnvVar = "TPM_SRK_AUTH"
|
||||
usageAuthEnvVar = "TPM_USAGE_AUTH"
|
||||
migrationAuthEnvVar = "TPM_MIGRATION_AUTH"
|
||||
)
|
||||
136
generate.go
Normal file
136
generate.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-tpm/tpm"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func buildGenerateKeyCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "generate_key",
|
||||
Usage: "Generate key",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "parameter",
|
||||
Usage: "The path to the TPM device to use",
|
||||
DefaultText: "/dev/tpm0",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "keyblob",
|
||||
Usage: "Output path of the generated keyblob",
|
||||
DefaultText: "keyblob",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "public-key",
|
||||
Usage: "Output path of the generated keyblob's public key",
|
||||
DefaultText: "publickey",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "pcrs",
|
||||
Usage: "A comma-separated list of PCR numbers against which the generated key will be bound. If blank, it will not be bound to any PCR values.",
|
||||
},
|
||||
},
|
||||
Action: func(ctx *cli.Context) error {
|
||||
tpmname := ctx.String("tpmname")
|
||||
keyblob := ctx.String("keyblob")
|
||||
publicKey := ctx.String("public-key")
|
||||
pcrs := ctx.String("pcrs")
|
||||
|
||||
generateAction(tpmname, keyblob, publicKey, pcrs)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func generateAction(tpmname, keyblobPath, pubKeyPath, pcrsStr string) {
|
||||
|
||||
var pcrs []int
|
||||
if pcrsStr != "" {
|
||||
for _, pcr := range strings.Split(pcrsStr, ",") {
|
||||
pcrNum, err := strconv.Atoi(pcr)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Bad value in pcrs argument: %s\n", pcr)
|
||||
return
|
||||
}
|
||||
pcrs = append(pcrs, pcrNum)
|
||||
}
|
||||
}
|
||||
|
||||
rwc, err := tpm.OpenTPM(tpmname)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Couldn't open the TPM file %s: %s\n", tpmname, err)
|
||||
return
|
||||
}
|
||||
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 := tpm.CreateWrapKey(rwc, srkAuth[:], usageAuth, migrationAuth, pcrs)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Couldn't make a new signing key: %s\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("Writing keyblob to %s\n", keyblobPath)
|
||||
if err = os.WriteFile(keyblobPath, keyblob, 0644); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error writing keyblob file: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
pubKey, err := tpm.UnmarshalRSAPublicKey(keyblob)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not get public key: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
pubKeyBytes, err := x509.MarshalPKIXPublicKey(pubKey)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not marshal public key: %s\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("Writing public key to %s\n", pubKeyPath)
|
||||
if err = os.WriteFile(pubKeyPath, pubKeyBytes, 0644); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error writing public key file: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
tpmKey := &TpmKey{
|
||||
Tpm: tpmname,
|
||||
Keyblob: base64.StdEncoding.EncodeToString(keyblob),
|
||||
}
|
||||
|
||||
tpmKeyJson, err := json.Marshal(&tpmKey)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error marshal tmp key: %s\n", err)
|
||||
return
|
||||
}
|
||||
println(base64.StdEncoding.EncodeToString(tpmKeyJson))
|
||||
}
|
||||
15
go.mod
Normal file
15
go.mod
Normal file
@@ -0,0 +1,15 @@
|
||||
module git.hatter.ink/external-signer-tpm
|
||||
|
||||
go 1.24.1
|
||||
|
||||
require (
|
||||
github.com/google/go-tpm v0.9.5
|
||||
github.com/urfave/cli/v2 v2.27.6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
)
|
||||
12
go.sum
Normal file
12
go.sum
Normal file
@@ -0,0 +1,12 @@
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/google/go-tpm v0.9.5 h1:ocUmnDebX54dnW+MQWGQRbdaAcJELsa6PqZhJ48KwVU=
|
||||
github.com/google/go-tpm v0.9.5/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=
|
||||
github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
267
main.go
Normal file
267
main.go
Normal file
@@ -0,0 +1,267 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user