From f0ffbe0686d5d86026d5d25249d89d39fda13f2f Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Thu, 22 May 2025 23:08:24 +0800 Subject: [PATCH] feat: works --- go.mod | 8 +- go.sum | 8 ++ main.go | 248 +++++++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 216 insertions(+), 48 deletions(-) diff --git a/go.mod b/go.mod index 8ba444f..30ba862 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,16 @@ module git.hatter.ink/external-signer-pkcs11 go 1.24.1 -require github.com/ThalesGroup/crypto11 v1.4.1 +require ( + github.com/ThalesGroup/crypto11 v1.4.1 + github.com/urfave/cli/v2 v2.27.6 +) require ( + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/miekg/pkcs11 v1.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/thales-e-security/pool v0.0.2 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect ) diff --git a/go.sum b/go.sum index 586781e..ff48470 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/ThalesGroup/crypto11 v1.4.1 h1:6YR6aVL8LI8akReXKTEgxf+k0+b8wlV8Ra7tZnCG9y4= github.com/ThalesGroup/crypto11 v1.4.1/go.mod h1:vggvBwlVrqePDrooq/B32dMXlfEsdsFY+6YlSD7VOy0= +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/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= @@ -8,7 +10,13 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= +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= diff --git a/main.go b/main.go index baa1788..228aad0 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( "os" "github.com/ThalesGroup/crypto11" + "github.com/urfave/cli/v2" ) const ( @@ -25,61 +26,185 @@ type Pkcs11Key struct { KeyLabel string `json:"key_label"` } -func main() { - parameter := "ewogICJsaWJyYXJ5IjogIi91c3IvbG9jYWwvbGliL2xpYnlrY3MxMS5keWxpYiIsCiAgInRva2VuX2xhYmVsIjogIll1YmlLZXkgUElWICM1MDEwMjIwIiwKICAicGluIjogIiIsCiAgImtleV9sYWJlbCI6ICJQcml2YXRlIGtleSBmb3IgUElWIEF1dGhlbnRpY2F0aW9uIgp9Cg==" - message := "message" +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"` +} + +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) + } +} + +// Parameter sample: +// "ewogICJsaWJyYXJ5IjogIi91c3IvbG9jYWwvbGliL2xpYnlrY3MxMS5keWxpYiIsCiAgInRva2VuX2xhYmVsIjogIll1YmlLZXkgUElWICM1MDEwMjIwIiwKICAicGluIjogIiIsCiAgImtleV9sYWJlbCI6ICJQcml2YXRlIGtleSBmb3IgUElWIEF1dGhlbnRpY2F0aW9uIgp9Cg==" +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-pkc11", + Usage: "External signer PKCS#11", + Commands: []*cli.Command{ + buildExternalSpecCommand(), + buildExternalPublicKeyCommand(), + buildExternalSignCommand(), + }, + Action: func(ctx *cli.Context) error { + 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-pkc11/0.1.0", + Specification: "External/1.0.0-alpha", + } + 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") + return tryWithPkcs11(parameter, false, func(privateKey crypto11.Signer) error { + publicKey := privateKey.Public() + publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey) + if err != nil { + return fmt.Errorf("marshal public key failed: %v", err) + } + externalPublicKeyResponse := &ExternalPublicKeyResponse{ + Success: true, + PublicKeyBase64: base64.StdEncoding.EncodeToString(publicKeyBytes), + } + + 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") + messageBase64 := ctx.String("message-base64") + // TODO check --alg + message, err := base64.StdEncoding.DecodeString(messageBase64) + if err != nil { + return fmt.Errorf("unmarshal --message-base64 failed: %v", err) + } + return tryWithPkcs11(parameter, true, func(privateKey crypto11.Signer) error { + hashed := sha256.Sum256(message) + signature, err := privateKey.Sign(rand.Reader, hashed[:], crypto.SHA256) + if err != nil { + return fmt.Errorf("sign with private key failed: %v", err) + } + externalSignatureResponse := ExternalSignatureResponse{ + Success: true, + SignatureBase64: base64.StdEncoding.EncodeToString(signature), + } + + printResponseSlient(externalSignatureResponse) + return nil + }) + }, + } +} + +func parseParameter(parameter string) (*Pkcs11Key, error) { parameterBytes, err := base64.StdEncoding.DecodeString(parameter) if err != nil { - println("1", err.Error()) - return + return nil, fmt.Errorf("parse --parameter base64 failed: %v", err) } var pkcs11Key Pkcs11Key err = json.Unmarshal(parameterBytes, &pkcs11Key) if err != nil { - println("2", err.Error()) - return + return nil, fmt.Errorf("parse --parameter base64 failed: %v", err) } - - pin, err := getPin(pkcs11Key.Pin) - if err != nil { - println("3", err.Error()) - return - } - config := &crypto11.Config{ - Path: pkcs11Key.Library, - TokenLabel: pkcs11Key.TokenLabel, - Pin: pin, - } - keyLabel := pkcs11Key.KeyLabel - - ctx, err := crypto11.Configure(config) - if err != nil { - println("4", err.Error()) - return - } - defer ctx.Close() - - privateKey, err := ctx.FindKeyPair(nil, []byte(keyLabel)) - if err != nil { - println("5", err.Error()) - return - } - publicKey := privateKey.Public() - publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey) - if err != nil { - println("6", err.Error()) - return - } - println(base64.StdEncoding.EncodeToString(publicKeyBytes)) - - hashed := sha256.Sum256([]byte(message)) - signature, err := privateKey.Sign(rand.Reader, hashed[:], crypto.SHA256) - if err != nil { - println("7", err.Error()) - return - } - fmt.Printf("%x\n", signature) + return &pkcs11Key, nil } func getPin(pin string) (string, error) { @@ -92,3 +217,32 @@ func getPin(pin string) (string, error) { } return "", errors.New("PIN is not set, set PIN: " + EnvPkcs11Pin) } + +func tryWithPkcs11(parameter string, requirePin bool, callback func(crypto11.Signer) error) error { + pkcs11Key, err := parseParameter(parameter) + if err != nil { + return err + } + pin, err := getPin(pkcs11Key.Pin) + if requirePin && err != nil { + return err + } + config := &crypto11.Config{ + Path: pkcs11Key.Library, + TokenLabel: pkcs11Key.TokenLabel, + Pin: pin, + } + keyLabel := pkcs11Key.KeyLabel + + crypto11Ctx, err := crypto11.Configure(config) + if err != nil { + return fmt.Errorf("config crypto11 failed: %v", err) + } + defer crypto11Ctx.Close() + + privateKey, err := crypto11Ctx.FindKeyPair(nil, []byte(keyLabel)) + if err != nil { + return fmt.Errorf("find private key failed: %v", err) + } + return callback(privateKey) +}