diff --git a/intf.go b/intf.go index f6ccd7e..bc30c24 100644 --- a/intf.go +++ b/intf.go @@ -1,11 +1,17 @@ package hsm -import "crypto" +import ( + "crypto" + "crypto/x509" +) type HSM interface { Ready() bool ListKeys() ([]Key, error) ListKeysByName(name string) ([]Key, error) + + PutCertificate(name string, cert *x509.Certificate) error + GetCertificate(name string) (*x509.Certificate, error) } type Key interface { diff --git a/software.go b/software.go index d24e7a5..4859712 100644 --- a/software.go +++ b/software.go @@ -1,6 +1,7 @@ package hsm import ( + "crypto/x509" "log" "os" "path/filepath" @@ -41,3 +42,11 @@ func (h *SoftwareHSM) ListKeysByName(name string) ([]Key, error) { // TODO return nil, nil } + +func (h *SoftwareHSM) PutCertificate(name string, cert *x509.Certificate) error { + return nil // TODO +} + +func (h *SoftwareHSM) GetCertificate(name string) (*x509.Certificate, error) { + return nil, nil // TODO +} diff --git a/yubihsm2.go b/yubihsm2.go index 0bc317c..24bcc58 100644 --- a/yubihsm2.go +++ b/yubihsm2.go @@ -5,11 +5,13 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rsa" + "crypto/x509" "errors" "fmt" "io" "log" "math/big" + "os" "sync" "syscall" @@ -85,6 +87,39 @@ func (h *YubiHSM2) ListKeysByName(name string) ([]Key, error) { return f, nil } +func (h *YubiHSM2) PutCertificate(name string, cert *x509.Certificate) error { + res, err := h.sm.ListObjects(yubihsm2.TypeOpaque, yubihsm2.Label(name)) + if err != nil { + return err + } + var id yubihsm2.ObjectID + + if len(res) > 0 { + id = res[0].ObjectID + } + + // send certificate + _, err = h.sm.PutOpaque(id, []byte(name), 1, 0, yubihsm2.OpaqueX509Cert, cert.Raw) + return err +} + +func (h *YubiHSM2) GetCertificate(name string) (*x509.Certificate, error) { + res, err := h.sm.ListObjects(yubihsm2.TypeOpaque, yubihsm2.Label(name)) + if err != nil { + return nil, err + } + if len(res) == 0 { + return nil, os.ErrNotExist + } + + // grab data + der, err := h.sm.GetOpaque(res[0].ObjectID) + if err != nil { + return nil, err + } + return x509.ParseCertificate(der) +} + func (k *YubiHSM2Key) Public() crypto.PublicKey { key, err := k.parent.sm.GetPubKey(k.kid) if err != nil { diff --git a/yubihsm2/cmd.go b/yubihsm2/cmd.go index 3a8088b..12636fc 100644 --- a/yubihsm2/cmd.go +++ b/yubihsm2/cmd.go @@ -389,3 +389,42 @@ func (call CommandHandler) PutAsymmetricKey(keyID uint16, label []byte, domains res.ReadValue(&keyID) return keyID, nil } + +func (call CommandHandler) PutOpaque(id ObjectID, label []byte, domains uint16, capabilities uint64, algorithm Algorithm, data []byte) (ObjectID, error) { + if len(label) > LabelLength { + return 0, errors.New("label is too long") + } + if len(label) < LabelLength { + label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...) + } + + command := CmdPutOpaque.New() + command.WriteValue(id) + command.Write(label) + command.WriteValue(domains) + command.WriteValue(capabilities) + command.WriteValue(algorithm) + command.Write(data) + + res, err := call(command) + if err != nil { + return 0, err + } + if res.Len() != 2 { + return 0, errors.New("invalid response payload length") + } + res.ReadValue(&id) + return id, nil +} + +func (call CommandHandler) GetOpaque(id ObjectID) ([]byte, error) { + command := CmdGetOpaque.New() + command.WriteValue(id) + + res, err := call(command) + if err != nil { + return nil, err + } + + return res.Payload, nil +}