Skip to content

Commit

Permalink
Feature: add HSM support via pkcs11-tool
Browse files Browse the repository at this point in the history
This allows to decouple the PKCS11 vendor library loading from the Fioctl process.
As a result, we can safely continue building static binaries.

This fully replaces all features of our Bash based PKI implementation with the Golang native approach.

Signed-off-by: Volodymyr Khoroz <[email protected]>
  • Loading branch information
vkhoroz committed Sep 28, 2023
1 parent 1885c26 commit 04a0476
Showing 1 changed file with 90 additions and 3 deletions.
93 changes: 90 additions & 3 deletions x509/storage_pkcs11_tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,101 @@ package x509

import (
"crypto"
"crypto/x509"
"errors"
"fmt"
"io"
"os"
"os/exec"

"github.com/foundriesio/fioctl/subcommands"
)

// TODO: The implementation is added in the next commit
const hsmObjectId = "1"

type hsmSigner struct {
hsm HsmInfo
id string
label string
}

func (s *hsmSigner) keyArgs() []string {
return []string{
"--module",
s.hsm.Module,
"--token-label",
s.hsm.TokenLabel,
"--pin",
s.hsm.Pin,
"--id",
s.id,
"--label",
s.label,
}
}

func (s *hsmSigner) Public() crypto.PublicKey {
args := append(s.keyArgs(), "--read-object", "--type=pubkey")
cmd := exec.Command("pkcs11-tool", args...)
out, err := cmd.Output()
var ex *exec.ExitError
if errors.As(err, &ex) {
fmt.Println(string(ex.Stderr))
if string(ex.Stderr) == "error: object not found\nAborting." {
return nil
}
}
subcommands.DieNotNil(err)
key, err := x509.ParsePKIXPublicKey(out)
subcommands.DieNotNil(err)
return key
}

func (s *hsmSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
// By default pkcs11-tool returns raw signature, X509 needs it wrapped into the ASN.1 sequence
args := append(s.keyArgs(), "--sign", "--mechanism=ECDSA", "--signature-format=sequence")
cmd := exec.Command("pkcs11-tool", args...)
cmd.Stderr = os.Stderr
in, err := cmd.StdinPipe()
subcommands.DieNotNil(err)

go func() {
defer in.Close()
_, err := in.Write(digest)
subcommands.DieNotNil(err)
}()

return cmd.Output()
}

func genAndSaveKeyToHsm(hsm HsmInfo, id, label string) crypto.Signer {
// The pkcs11-tool allows creating many objects with the same ID and label, potentially corrupting the storage.
// For now, allow to create only one object.
// In the future we will use the ID field to allow key rotation.
key := &hsmSigner{hsm, id, label}
if key.Public() != nil {
subcommands.DieNotNil(fmt.Errorf("Key %s already exists on the HSM device", label))
}

args := append(key.keyArgs(), "--keypairgen", "--key-type=EC:prime256v1")
cmd := exec.Command("pkcs11-tool", args...)
cmd.Stderr = os.Stderr
subcommands.DieNotNil(cmd.Run())
return key
}

func loadKeyFromHsm(hsm HsmInfo, id, label string) crypto.Signer {
key := &hsmSigner{hsm, id, label}
if key.Public() == nil {
subcommands.DieNotNil(fmt.Errorf("Key %s not found on the HSM device", label))
}
return key
}

func (s *hsmStorage) genAndSaveKey() crypto.Signer {
panic("Not implemented")
return genAndSaveKeyToHsm(s.HsmInfo, hsmObjectId, s.Label)
}

func (s *hsmStorage) loadKey() crypto.Signer {
panic("Not implemented")
return loadKeyFromHsm(s.HsmInfo, hsmObjectId, s.Label)
}

0 comments on commit 04a0476

Please sign in to comment.