Initial copy of notary-signer

Signed-off-by: Diogo Monica <diogo@docker.com>
This commit is contained in:
Diogo Monica 2015-07-13 20:51:25 -07:00
parent ead0224526
commit 9a4c2dc744
22 changed files with 1963 additions and 12 deletions

View File

@ -23,6 +23,10 @@ ${PREFIX}/bin/notary: version/version.go $(shell find . -type f -name '*.go')
@echo "+ $@"
@godep go build -o $@ ${GO_LDFLAGS} ./cmd/notary
${PREFIX}/bin/notary-signer: version/version.go $(shell find . -type f -name '*.go')
@echo "+ $@"
@godep go build -o $@ ${GO_LDFLAGS} ./cmd/notary-signer
vet:
@echo "+ $@"
@test -z "$$(go tool vet -printf=false . 2>&1 | grep -v Godeps/_workspace/src/ | tee /dev/stderr)"
@ -53,9 +57,9 @@ protos:
clean-protos:
@rm proto/*.pb.go
binaries: ${PREFIX}/bin/notary-server ${PREFIX}/bin/notary
binaries: ${PREFIX}/bin/notary-server ${PREFIX}/bin/notary ${PREFIX}/bin/notary-signer
@echo "+ $@"
clean:
@echo "+ $@"
@rm -rf "${PREFIX}/bin/notary-server" "${PREFIX}/bin/notary"
@rm -rf "${PREFIX}/bin/notary-server" "${PREFIX}/bin/notary" "${PREFIX}/bin/notary-signer"

178
cmd/notary-signer/main.go Normal file
View File

@ -0,0 +1,178 @@
package main
import (
"crypto/rand"
"crypto/tls"
_ "expvar"
"flag"
"log"
"net"
"net/http"
"os"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
_ "github.com/docker/distribution/health"
"github.com/docker/notary/signer/api"
"github.com/docker/notary/signer/keys"
"github.com/miekg/pkcs11"
pb "github.com/docker/notary/proto"
)
const (
_Addr = ":4444"
_RpcAddr = ":7899"
_DebugAddr = "localhost:8080"
)
var debug, yubikey bool
var certFile, keyFile, pkcs11Lib, pin string
func init() {
flag.StringVar(&certFile, "cert", "", "Intermediate certificates")
flag.StringVar(&keyFile, "key", "", "Private key file")
flag.StringVar(&pkcs11Lib, "pkcs11", "", "enables HSM mode and uses the provided pkcs11 library path")
flag.StringVar(&pin, "pin", "", "the PIN to use for the HSM")
flag.BoolVar(&yubikey, "yubikey", false, "enables yubikey support mode. should be used with --pkcs11")
flag.BoolVar(&debug, "debug", false, "show the version and exit")
}
func main() {
flag.Usage = usage
flag.Parse()
if _DebugAddr != "" {
go debugServer(_DebugAddr)
}
if certFile == "" || keyFile == "" {
usage()
log.Fatalf("Certificate and key are mandatory")
}
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS12,
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_CBC_SHA},
}
tlsConfig.Rand = rand.Reader
sigServices := make(signer.SigningServiceIndex)
if pkcs11Lib != "" {
if pin == "" {
log.Fatalf("Using PIN is mandatory with pkcs11")
}
ctx, session := SetupHSMEnv(pkcs11Lib)
defer cleanup(ctx, session)
sigServices[api.RSAAlgorithm] = api.NewRSASigningService(ctx, session)
}
sigServices[api.ED25519] = api.EdDSASigningService{KeyDB: keys.NewKeyDB()}
//RPC server setup
kms := &api.KeyManagementServer{SigServices: sigServices}
ss := &api.SignerServer{SigServices: sigServices}
grpcServer := grpc.NewServer()
pb.RegisterKeyManagementServer(grpcServer, kms)
pb.RegisterSignerServer(grpcServer, ss)
lis, err := net.Listen("tcp", _RpcAddr)
if err != nil {
log.Fatalf("failed to listen %v", err)
}
creds, err := credentials.NewServerTLSFromFile(certFile, keyFile)
if err != nil {
log.Fatalf("failed to generate credentials %v", err)
}
go grpcServer.Serve(creds.NewListener(lis))
//HTTP server setup
server := http.Server{
Addr: _Addr,
Handler: api.Handlers(sigServices),
TLSConfig: tlsConfig,
}
if debug {
log.Println("[Notary-signer RPC Server] : Listening on", _RpcAddr)
log.Println("[Notary-signer Server] : Listening on", _Addr)
}
err = server.ListenAndServeTLS(certFile, keyFile)
if err != nil {
log.Fatalf("[Notary-signer Server] : Failed to start %s", err)
}
}
func usage() {
log.Println("usage:", os.Args[0], "<config>")
flag.PrintDefaults()
}
// debugServer starts the debug server with pprof, expvar among other
// endpoints. The addr should not be exposed externally. For most of these to
// work, tls cannot be enabled on the endpoint, so it is generally separate.
func debugServer(addr string) {
log.Println("[Notary-signer Debug Server] server listening on", addr)
if err := http.ListenAndServe(addr, nil); err != nil {
log.Fatalf("[Notary-signer Debug Server] error listening on debug interface: %v", err)
}
}
// SetupHSMEnv is a method that depends on the existences
func SetupHSMEnv(libraryPath string) (*pkcs11.Ctx, pkcs11.SessionHandle) {
p := pkcs11.New(libraryPath)
if p == nil {
log.Fatalf("Failed to init library")
}
if err := p.Initialize(); err != nil {
log.Fatalf("Initialize error %s\n", err.Error())
}
slots, err := p.GetSlotList(true)
if err != nil {
log.Fatalf("Failed to list HSM slots %s", err)
}
// Check to see if we got any slots from the HSM.
if len(slots) < 1 {
log.Fatalln("No HSM Slots found")
}
// CKF_SERIAL_SESSION: TRUE if cryptographic functions are performed in serial with the application; FALSE if the functions may be performed in parallel with the application.
// CKF_RW_SESSION: TRUE if the session is read/write; FALSE if the session is read-only
session, err := p.OpenSession(slots[0], pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
if err != nil {
log.Fatalf("Failed to Start Session with HSM %s", err)
}
// (diogo): Configure PIN from config file
if err = p.Login(session, pkcs11.CKU_USER, pin); err != nil {
log.Fatalf("User PIN %s\n", err.Error())
}
return p, session
}
func cleanup(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) {
ctx.Destroy()
ctx.Finalize()
ctx.CloseSession(session)
ctx.Logout(session)
}

View File

@ -0,0 +1 @@
package main

27
fixtures/rufus.key Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAtjV1orRtFazywT7ueDSVB3KsgFaAOUfhtyk2upTmvF8vg+6C
47zCzgkPLiELBdMi93jP/86Osnud3zGZxLcb+rdP1SWfGzd5QeLHwlM17NTYLhFc
6nFzQozKWSZlZGQeCPxpynSSuGHS9qIH+bgqmrWQSc7nz2qsqqDxO2yX9XFJ669O
OIK3zIroCzi0ElOluerWnaW0430IWk4ykPWUyGYtwsdW+vHIqjXMgx0dPkQParpH
kDsCSbUYmnsK0PUmuuToIaTX0yR99FJZdnPOprwHWTqOe239Xbnmb6QX8qidXjVT
J+FFJwWBMHWd2Z5wIs8+7oTKC8r3tWea66IhaQIDAQABAoIBACNLkcD1wFe9i7yh
SyJf1Sp/LSkjGG2AHhoT0rUr2NJOge7FifdBfl7Y5GbkIbV8I77aWThNM9khFwDA
I/A2JCZZ0g4Q3pQwF74KhvV+luiMhl/OHziOx2vxx9SjGmrq9eJ5uqhsEmvDD1GV
K4RVB3O2jf+uI7tLB615xaDkNT9m4d/cDLW1drP3RKXO9LJt290NfMEoyP1018Gi
rEAnkyG9JHDeXGOCKCDDw0+tgNrhjiky8hmakpd+/TmCP8QEeTYC0n7W5vDn1jFb
1nLW9x9dQxWoRVW9kfQDrOeGlOvmbvVIgoU9aFmtOF0ys2l23iIS/fkFOVtSmxO1
/d7TAY0CgYEA3PYdIau1sfLOzCRUNiW0O/EubZPWDWCcX8bua+YDtZHU04ZWaj1x
AqJPFCCuZS5PXrEvIjgoTRB47KJqw/sb5AXtZXYB3jBrhdWaxKMEmOh/abwm6zvS
jhVyp/hYs7trROZ9ZF178CndbJMMG29w4NA6hRTe9FEbF/dko2J+zE8CgYEA0xox
Y4FxucckktsvNvl/SKd0ueN8aJHxy3jtuQTtKuhmrcf9ZFLlyhsZ1osHe0F+dapL
3FqzgMWdb2mLha+CZ0wgS3wJyGps18AkA8MsOJ7ANMZy8IrTewbo1MZkxU10a5Hs
kYfUy2YGgRiumqFN+UsEMxB0FEV3V56IMCn1sMcCgYEAmHRvUSYKbUccjBmcyOdm
3dzgrdZM4FoCSssVAfloIcUG1RNhWFggOzMF4NtTuJglRy51WOTqWZG9/XdqtuHx
MnksfM7ZJxa9eVASQw4/Q8lW7/tT1wYllscRnvdpLElqD9YAOlmOA/y+vAURW3Se
q17AocXutx+m0/hrxZdV6V0CgYA+45q7DiEXO2CF745NTl7BAkIf7sltTa0+9LO9
Xt5Y3gtY0i+G57tVTmWQKLL94TEPVMBs0QiQ4E5alpLeLL8ojuFAG5++eRYb4D3a
cRaHd6PDFSvAxVrjV9edK81xifWY5kwXvuOCBM8DPpslrdBQ6CxEGi66q0c2byAb
WSS8mQKBgDtkAAKNTFlSAXxoqqPxESFJTFVHKRZ6QVTwf2s3oNWmCAlxx5oh4W6t
Yy4dZP7dkCYp6KSwZ0lF8bUGc54hoUZFR+SewLiKgbRq+mgWmnHbMvMoUxGhaHGV
P4cieXBWZrCa4eq2UPyYv8RvXlEvKht5zvIc2wc2aupOu/0dMG52
-----END RSA PRIVATE KEY-----

30
fixtures/rufus.pem Normal file
View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQsFADBZMRMwEQYDVQQDEwpFeGFt
cGxlIENBMQswCQYDVQQGEwJVUzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEQMA4G
A1UEChMHRXhhbXBsZTELMAkGA1UECBMCQ0EwHhcNMTUwNDI4MDEyNTU0WhcNMTYw
NDI3MDEyNTU0WjBUMQ4wDAYDVQQDEwVydWZ1czELMAkGA1UEBhMCVVMxFjAUBgNV
BAcTDVNhbiBGcmFuY2lzY28xEDAOBgNVBAoTB0V4YW1wbGUxCzAJBgNVBAgTAkNB
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtjV1orRtFazywT7ueDSV
B3KsgFaAOUfhtyk2upTmvF8vg+6C47zCzgkPLiELBdMi93jP/86Osnud3zGZxLcb
+rdP1SWfGzd5QeLHwlM17NTYLhFc6nFzQozKWSZlZGQeCPxpynSSuGHS9qIH+bgq
mrWQSc7nz2qsqqDxO2yX9XFJ669OOIK3zIroCzi0ElOluerWnaW0430IWk4ykPWU
yGYtwsdW+vHIqjXMgx0dPkQParpHkDsCSbUYmnsK0PUmuuToIaTX0yR99FJZdnPO
prwHWTqOe239Xbnmb6QX8qidXjVTJ+FFJwWBMHWd2Z5wIs8+7oTKC8r3tWea66Ih
aQIDAQABo4H1MIHyMIGBBgNVHSMEejB4gBR233eeV1v6NtTVfho+G5C9LAyys6Fd
pFswWTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJh
bmNpc2NvMRAwDgYDVQQKEwdFeGFtcGxlMRMwEQYDVQQDEwpFeGFtcGxlIENBggEB
MAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA4G
A1UdDwEB/wQEAwIFoDAQBgNVHREECTAHggVydWZ1czAdBgNVHQ4EFgQUj0GbM9xU
MHPV4NhlA3a8EoYKtWIwDQYJKoZIhvcNAQELBQADggIBACs49/6/X1piMMuIYGhT
2TL1bjs1ew1kSoqzT36cVKv8hTpuy5O/4uYZnIXzyEZ8Fa3jtcs0AHdTxdmbwg/V
Txzhma8Zl4Asumd36fWMgFRWmM5kBRRhkkT86Zgsi9/DOaZ8QTuxDdhJ7W9VX1gp
Gbl/vvhKu5QEldT/G+ZmWITBG6vspioPQr/GmEew9bLEQ3X90Ogo+3XFxxBSvlMu
ueaUKUQOlotwZ/feSnd3N8m9CqsMahGDehPBdq9gPuFNdiwmAq0zvLA+KupPa63x
bEHjgEJSRwVUvjrST08rSoUtfEL8SzbV95VCffASOuVZM5GN1e8czwxckhKyk6dr
9knCQS5EsgtluR4jbW2o76t4zEHYxsLQ3KtQ/uAxQ9h5Q6T4z+PA2/TScMOjlm3W
fWpGaQcWmfyz/T4T2P7GWu0mNzzkVkUls8sz70a7b6xQdf1+l/TRUEO5JUvvYhcF
f4DB6hnLeaocPeVvReuGX51nSZM2/0eAwCoF8RMfsk6L+dnb8XDv4td0fY6FIVqM
0zQ/tdVPl2iEH1cRZLFzHJ+AQRxaR665tOS6mR/VFb0mNumvFWHyLYgz/GeGRcEO
8tQL96ZlYR/Iz98zJBuaJM3hfraYR5PQ6K+F8LJZn4SBdBUy3rZOY80BM2PwZsQw
yUmpY7ItUcmo5C05DCmUDgnW
-----END CERTIFICATE-----

294
proto/rufus.pb.go Normal file
View File

@ -0,0 +1,294 @@
// Code generated by protoc-gen-go.
// source: proto/rufus.proto
// DO NOT EDIT!
/*
Package proto is a generated protocol buffer package.
It is generated from these files:
proto/rufus.proto
It has these top-level messages:
KeyInfo
Algorithm
PublicKey
Signature
SignatureRequest
Void
*/
package proto
import proto1 "github.com/golang/protobuf/proto"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto1.Marshal
// KeyInfo holds an ID that is used to reference the key and it's algorithm
type KeyInfo struct {
ID string `protobuf:"bytes,1,opt" json:"ID,omitempty"`
Algorithm *Algorithm `protobuf:"bytes,2,opt,name=algorithm" json:"algorithm,omitempty"`
}
func (m *KeyInfo) Reset() { *m = KeyInfo{} }
func (m *KeyInfo) String() string { return proto1.CompactTextString(m) }
func (*KeyInfo) ProtoMessage() {}
func (m *KeyInfo) GetAlgorithm() *Algorithm {
if m != nil {
return m.Algorithm
}
return nil
}
// Type holds the type of crypto algorithm used
type Algorithm struct {
Algorithm string `protobuf:"bytes,1,opt,name=algorithm" json:"algorithm,omitempty"`
}
func (m *Algorithm) Reset() { *m = Algorithm{} }
func (m *Algorithm) String() string { return proto1.CompactTextString(m) }
func (*Algorithm) ProtoMessage() {}
// PublicKey has a KeyInfo that is used to reference the key, and opaque bytes of a publicKey
type PublicKey struct {
KeyInfo *KeyInfo `protobuf:"bytes,1,opt,name=keyInfo" json:"keyInfo,omitempty"`
PublicKey []byte `protobuf:"bytes,2,opt,name=publicKey,proto3" json:"publicKey,omitempty"`
}
func (m *PublicKey) Reset() { *m = PublicKey{} }
func (m *PublicKey) String() string { return proto1.CompactTextString(m) }
func (*PublicKey) ProtoMessage() {}
func (m *PublicKey) GetKeyInfo() *KeyInfo {
if m != nil {
return m.KeyInfo
}
return nil
}
// Signature specifies a KeyInfo that was used for signing and signed content
type Signature struct {
KeyInfo *KeyInfo `protobuf:"bytes,1,opt,name=keyInfo" json:"keyInfo,omitempty"`
Content []byte `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"`
}
func (m *Signature) Reset() { *m = Signature{} }
func (m *Signature) String() string { return proto1.CompactTextString(m) }
func (*Signature) ProtoMessage() {}
func (m *Signature) GetKeyInfo() *KeyInfo {
if m != nil {
return m.KeyInfo
}
return nil
}
// SignatureRequests specifies a KeyInfo, and content to be signed
type SignatureRequest struct {
KeyInfo *KeyInfo `protobuf:"bytes,1,opt,name=keyInfo" json:"keyInfo,omitempty"`
Content []byte `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"`
}
func (m *SignatureRequest) Reset() { *m = SignatureRequest{} }
func (m *SignatureRequest) String() string { return proto1.CompactTextString(m) }
func (*SignatureRequest) ProtoMessage() {}
func (m *SignatureRequest) GetKeyInfo() *KeyInfo {
if m != nil {
return m.KeyInfo
}
return nil
}
// Void represents an empty message type
type Void struct {
}
func (m *Void) Reset() { *m = Void{} }
func (m *Void) String() string { return proto1.CompactTextString(m) }
func (*Void) ProtoMessage() {}
// Client API for KeyManagement service
type KeyManagementClient interface {
// CreateKey creates as asymmetric key pair and returns the PublicKey
CreateKey(ctx context.Context, in *Algorithm, opts ...grpc.CallOption) (*PublicKey, error)
// DeleteKey deletes the key associated with a KeyInfo
DeleteKey(ctx context.Context, in *KeyInfo, opts ...grpc.CallOption) (*Void, error)
// GetKeyInfo returns the PublicKey associated with a KeyInfo
GetKeyInfo(ctx context.Context, in *KeyInfo, opts ...grpc.CallOption) (*PublicKey, error)
}
type keyManagementClient struct {
cc *grpc.ClientConn
}
func NewKeyManagementClient(cc *grpc.ClientConn) KeyManagementClient {
return &keyManagementClient{cc}
}
func (c *keyManagementClient) CreateKey(ctx context.Context, in *Algorithm, opts ...grpc.CallOption) (*PublicKey, error) {
out := new(PublicKey)
err := grpc.Invoke(ctx, "/proto.KeyManagement/CreateKey", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *keyManagementClient) DeleteKey(ctx context.Context, in *KeyInfo, opts ...grpc.CallOption) (*Void, error) {
out := new(Void)
err := grpc.Invoke(ctx, "/proto.KeyManagement/DeleteKey", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *keyManagementClient) GetKeyInfo(ctx context.Context, in *KeyInfo, opts ...grpc.CallOption) (*PublicKey, error) {
out := new(PublicKey)
err := grpc.Invoke(ctx, "/proto.KeyManagement/GetKeyInfo", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for KeyManagement service
type KeyManagementServer interface {
// CreateKey creates as asymmetric key pair and returns the PublicKey
CreateKey(context.Context, *Algorithm) (*PublicKey, error)
// DeleteKey deletes the key associated with a KeyInfo
DeleteKey(context.Context, *KeyInfo) (*Void, error)
// GetKeyInfo returns the PublicKey associated with a KeyInfo
GetKeyInfo(context.Context, *KeyInfo) (*PublicKey, error)
}
func RegisterKeyManagementServer(s *grpc.Server, srv KeyManagementServer) {
s.RegisterService(&_KeyManagement_serviceDesc, srv)
}
func _KeyManagement_CreateKey_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
in := new(Algorithm)
if err := codec.Unmarshal(buf, in); err != nil {
return nil, err
}
out, err := srv.(KeyManagementServer).CreateKey(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
func _KeyManagement_DeleteKey_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
in := new(KeyInfo)
if err := codec.Unmarshal(buf, in); err != nil {
return nil, err
}
out, err := srv.(KeyManagementServer).DeleteKey(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
func _KeyManagement_GetKeyInfo_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
in := new(KeyInfo)
if err := codec.Unmarshal(buf, in); err != nil {
return nil, err
}
out, err := srv.(KeyManagementServer).GetKeyInfo(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
var _KeyManagement_serviceDesc = grpc.ServiceDesc{
ServiceName: "proto.KeyManagement",
HandlerType: (*KeyManagementServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "CreateKey",
Handler: _KeyManagement_CreateKey_Handler,
},
{
MethodName: "DeleteKey",
Handler: _KeyManagement_DeleteKey_Handler,
},
{
MethodName: "GetKeyInfo",
Handler: _KeyManagement_GetKeyInfo_Handler,
},
},
Streams: []grpc.StreamDesc{},
}
// Client API for Signer service
type SignerClient interface {
// Sign calculates a cryptographic signature using the Key associated with a KeyInfo and returns the signature
Sign(ctx context.Context, in *SignatureRequest, opts ...grpc.CallOption) (*Signature, error)
}
type signerClient struct {
cc *grpc.ClientConn
}
func NewSignerClient(cc *grpc.ClientConn) SignerClient {
return &signerClient{cc}
}
func (c *signerClient) Sign(ctx context.Context, in *SignatureRequest, opts ...grpc.CallOption) (*Signature, error) {
out := new(Signature)
err := grpc.Invoke(ctx, "/proto.Signer/Sign", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Signer service
type SignerServer interface {
// Sign calculates a cryptographic signature using the Key associated with a KeyInfo and returns the signature
Sign(context.Context, *SignatureRequest) (*Signature, error)
}
func RegisterSignerServer(s *grpc.Server, srv SignerServer) {
s.RegisterService(&_Signer_serviceDesc, srv)
}
func _Signer_Sign_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
in := new(SignatureRequest)
if err := codec.Unmarshal(buf, in); err != nil {
return nil, err
}
out, err := srv.(SignerServer).Sign(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
var _Signer_serviceDesc = grpc.ServiceDesc{
ServiceName: "proto.Signer",
HandlerType: (*SignerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Sign",
Handler: _Signer_Sign_Handler,
},
},
Streams: []grpc.StreamDesc{},
}

55
proto/rufus.proto Normal file
View File

@ -0,0 +1,55 @@
syntax = "proto3";
package proto;
// KeyManagement Interface
service KeyManagement {
// CreateKey creates as asymmetric key pair and returns the PublicKey
rpc CreateKey(Algorithm) returns (PublicKey) {}
// DeleteKey deletes the key associated with a KeyInfo
rpc DeleteKey(KeyInfo) returns (Void) {}
// GetKeyInfo returns the PublicKey associated with a KeyInfo
rpc GetKeyInfo(KeyInfo) returns (PublicKey) {}
}
// Signer Interface
service Signer {
// Sign calculates a cryptographic signature using the Key associated with a KeyInfo and returns the signature
rpc Sign(SignatureRequest) returns (Signature) {}
}
// KeyInfo holds an ID that is used to reference the key and it's algorithm
message KeyInfo {
string ID = 1;
Algorithm algorithm = 2;
}
// Type holds the type of crypto algorithm used
message Algorithm {
string algorithm = 1;
}
// PublicKey has a KeyInfo that is used to reference the key, and opaque bytes of a publicKey
message PublicKey {
KeyInfo keyInfo = 1;
bytes publicKey = 2;
}
// Signature specifies a KeyInfo that was used for signing and signed content
message Signature {
KeyInfo keyInfo = 1;
bytes content = 2;
}
// SignatureRequests specifies a KeyInfo, and content to be signed
message SignatureRequest {
KeyInfo keyInfo = 1;
bytes content = 2;
}
// Void represents an empty message type
message Void {
}

27
signer/Dockerfile Normal file
View File

@ -0,0 +1,27 @@
FROM diogomonica/golang-softhsm2
MAINTAINER Diogo Monica "diogo@docker.com"
# CHANGE-ME: Default values for SoftHSM2 PIN and SOPIN, used to initialize the first token
ENV PIN="1234"
ENV SOPIN="1234"
ENV LIBDIR="/usr/local/lib/softhsm/"
# Install openSC and dependencies
RUN apt-get update
RUN apt-get install -y build-essential autoconf automake libtool gtk-doc-tools gengetopt help2man libpcsclite-dev libzip-dev opensc libssl-dev usbutils vim
# Initialize the SoftHSM2 token on slod 0, using PIN and SOPIN varaibles
RUN softhsm2-util --init-token --slot 0 --label "test_token" --pin $PIN --so-pin $SOPIN
# Copy the local repo to the expected go path
COPY . /go/src/github.com/docker/rufus
# Install rufus
RUN go get github.com/docker/rufus/cmd/rufus
EXPOSE 4443
#ENTRYPOINT rufus -cert /go/src/github.com/docker/rufus/fixtures/rufus.pem -key /go/src/github.com/docker/rufus/fixtures/rufus.key -debug -pkcs11 /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so -pin 123456 -yubikey
ENTRYPOINT rufus -cert /go/src/github.com/docker/rufus/fixtures/rufus.pem -key /go/src/github.com/docker/rufus/fixtures/rufus.key -debug -pkcs11 $LIBDIR/libsofthsm2.so -pin 1234

176
signer/api/api.go Normal file
View File

@ -0,0 +1,176 @@
package api
import (
"encoding/json"
"net/http"
"github.com/docker/rufus"
"github.com/docker/rufus/keys"
"github.com/gorilla/mux"
pb "github.com/docker/rufus/proto"
)
// Handlers sets up all the handers for the routes, injecting a specific SigningService object for them to use
func Handlers(sigServices rufus.SigningServiceIndex) *mux.Router {
r := mux.NewRouter()
r.Methods("GET").Path("/{Algorithm}/{ID}").Handler(KeyInfo(sigServices))
r.Methods("POST").Path("/new/{Algorithm}").Handler(CreateKey(sigServices))
r.Methods("POST").Path("/delete").Handler(DeleteKey(sigServices))
r.Methods("POST").Path("/sign").Handler(Sign(sigServices))
return r
}
// getSigningService handles looking up the correct signing service, given the
// algorithm specified in the HTTP request. If the algorithm isn't specified
// or isn't supported, an error is returned to the client and this function
// returns a nil SigningService
func getSigningService(w http.ResponseWriter, algorithm string, sigServices rufus.SigningServiceIndex) rufus.SigningService {
if algorithm == "" {
http.Error(w, "algorithm not specified", http.StatusBadRequest)
return nil
}
service := sigServices[algorithm]
if service == nil {
http.Error(w, "algorithm "+algorithm+" not supported", http.StatusBadRequest)
return nil
}
return service
}
// KeyInfo returns a Handler that given a specific Key ID param, returns the public key bits of that key
func KeyInfo(sigServices rufus.SigningServiceIndex) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
algorithm := vars["Algorithm"]
sigService := getSigningService(w, algorithm, sigServices)
if sigService == nil {
// Error handled inside getSigningService
return
}
keyInfo := &pb.KeyInfo{ID: vars["ID"], Algorithm: &pb.Algorithm{Algorithm: algorithm}}
key, err := sigService.KeyInfo(keyInfo)
if err != nil {
switch err {
// If we received an ErrInvalidKeyID, the key doesn't exist, return 404
case keys.ErrInvalidKeyID:
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(err.Error()))
return
// If we received anything else, it is unexpected, and we return a 500
default:
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
}
json.NewEncoder(w).Encode(key)
return
})
}
// CreateKey returns a handler that generates a new
func CreateKey(sigServices rufus.SigningServiceIndex) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
sigService := getSigningService(w, vars["Algorithm"], sigServices)
if sigService == nil {
// Error handled inside getSigningService
return
}
key, err := sigService.CreateKey()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
json.NewEncoder(w).Encode(key)
return
})
}
// DeleteKey returns a handler that delete a specific KeyID
func DeleteKey(sigServices rufus.SigningServiceIndex) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var keyInfo *pb.KeyInfo
err := json.NewDecoder(r.Body).Decode(&keyInfo)
defer r.Body.Close()
if err != nil || keyInfo.ID == "" || keyInfo.Algorithm == nil || keyInfo.Algorithm.Algorithm == "" {
w.WriteHeader(http.StatusBadRequest)
jsonErr, _ := json.Marshal("Malformed request")
w.Write([]byte(jsonErr))
return
}
sigService := getSigningService(w, keyInfo.Algorithm.Algorithm, sigServices)
if sigService == nil {
// Error handled inside getSigningService
return
}
_, err = sigService.DeleteKey(keyInfo)
if err != nil {
switch err {
// If we received an ErrInvalidKeyID, the key doesn't exist, return 404
case keys.ErrInvalidKeyID:
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(err.Error()))
return
// If we received anything else, it is unexpected, and we return a 500
default:
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
}
// In case we successfully delete this key, return 200
return
})
}
// Sign returns a handler that is able to perform signatures on a given blob
func Sign(sigServices rufus.SigningServiceIndex) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var sigRequest *pb.SignatureRequest
err := json.NewDecoder(r.Body).Decode(&sigRequest)
defer r.Body.Close()
if err != nil || sigRequest.Content == nil ||
sigRequest.KeyInfo == nil || sigRequest.KeyInfo.Algorithm == nil {
w.WriteHeader(http.StatusBadRequest)
jsonErr, _ := json.Marshal("Malformed request")
w.Write([]byte(jsonErr))
return
}
sigService := getSigningService(w, sigRequest.KeyInfo.Algorithm.Algorithm, sigServices)
if sigService == nil {
// Error handled inside getSigningService
return
}
signer, err := sigService.Signer(sigRequest.KeyInfo)
if err == keys.ErrInvalidKeyID {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(err.Error()))
return
}
signature, err := signer.Sign(sigRequest)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
json.NewEncoder(w).Encode(signature)
return
})
}

307
signer/api/api_test.go Normal file
View File

@ -0,0 +1,307 @@
package api_test
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"github.com/docker/rufus"
"github.com/docker/rufus/api"
"github.com/docker/rufus/keys"
"github.com/miekg/pkcs11"
"github.com/stretchr/testify/assert"
pb "github.com/docker/rufus/proto"
)
var (
server *httptest.Server
reader io.Reader
hsmSigService *api.RSASigningService
softwareSigService *api.EdDSASigningService
deleteKeyBaseURL string
createKeyBaseURL string
keyInfoBaseURL string
signBaseURL string
)
func SetupHSMEnv(t *testing.T) (*pkcs11.Ctx, pkcs11.SessionHandle) {
var libPath = "/usr/local/lib/softhsm/libsofthsm2.so"
if _, err := os.Stat(libPath); err != nil {
t.Skipf("Skipping test. Library path: %s does not exist", libPath)
}
p := pkcs11.New(libPath)
if p == nil {
t.Fatalf("Failed to init library")
}
if err := p.Initialize(); err != nil {
t.Fatalf("Initialize error %s\n", err.Error())
}
slots, err := p.GetSlotList(true)
if err != nil {
t.Fatalf("Failed to list HSM slots %s", err)
}
session, err := p.OpenSession(slots[0], pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
if err != nil {
t.Fatalf("Failed to Start Session with HSM %s", err)
}
if err = p.Login(session, pkcs11.CKU_USER, "1234"); err != nil {
t.Fatalf("User PIN %s\n", err.Error())
}
return p, session
}
func setup(sigServices rufus.SigningServiceIndex) {
server = httptest.NewServer(api.Handlers(sigServices))
deleteKeyBaseURL = fmt.Sprintf("%s/delete", server.URL)
createKeyBaseURL = fmt.Sprintf("%s/new", server.URL)
keyInfoBaseURL = fmt.Sprintf("%s", server.URL)
signBaseURL = fmt.Sprintf("%s/sign", server.URL)
}
func TestDeleteKeyHandlerReturns404WithNonexistentKey(t *testing.T) {
setup(rufus.SigningServiceIndex{api.ED25519: api.NewEdDSASigningService(keys.NewKeyDB())})
fakeID := "c62e6d68851cef1f7e55a9d56e3b0c05f3359f16838cad43600f0554e7d3b54d"
keyInfo := &pb.KeyInfo{ID: fakeID, Algorithm: &pb.Algorithm{Algorithm: api.ED25519}}
requestJson, _ := json.Marshal(keyInfo)
reader = strings.NewReader(string(requestJson))
request, err := http.NewRequest("POST", deleteKeyBaseURL, reader)
assert.Nil(t, err)
res, err := http.DefaultClient.Do(request)
assert.Nil(t, err)
assert.Equal(t, 404, res.StatusCode)
}
func TestDeleteKeyHandler(t *testing.T) {
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
setup(rufus.SigningServiceIndex{api.ED25519: sigService})
key, _ := sigService.CreateKey()
requestJson, _ := json.Marshal(key.KeyInfo)
reader = strings.NewReader(string(requestJson))
request, err := http.NewRequest("POST", deleteKeyBaseURL, reader)
assert.Nil(t, err)
res, err := http.DefaultClient.Do(request)
assert.Nil(t, err)
assert.Equal(t, 200, res.StatusCode)
}
func TestKeyInfoHandler(t *testing.T) {
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
setup(rufus.SigningServiceIndex{api.ED25519: sigService})
key, _ := sigService.CreateKey()
keyInfoURL := fmt.Sprintf("%s/%s/%s", keyInfoBaseURL, api.ED25519, key.KeyInfo.ID)
request, err := http.NewRequest("GET", keyInfoURL, nil)
assert.Nil(t, err)
res, err := http.DefaultClient.Do(request)
assert.Nil(t, err)
jsonBlob, err := ioutil.ReadAll(res.Body)
assert.Nil(t, err)
var keyInfo *pb.PublicKey
err = json.Unmarshal(jsonBlob, &keyInfo)
assert.Nil(t, err)
assert.Equal(t, key.KeyInfo.ID, keyInfo.KeyInfo.ID)
assert.Equal(t, 200, res.StatusCode)
}
func TestKeyInfoHandlerReturns404WithNonexistentKey(t *testing.T) {
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
setup(rufus.SigningServiceIndex{api.ED25519: sigService})
fakeID := "c62e6d68851cef1f7e55a9d56e3b0c05f3359f16838cad43600f0554e7d3b54d"
keyInfoURL := fmt.Sprintf("%s/%s/%s", keyInfoBaseURL, api.ED25519, fakeID)
request, err := http.NewRequest("GET", keyInfoURL, nil)
assert.Nil(t, err)
res, err := http.DefaultClient.Do(request)
assert.Nil(t, err)
assert.Equal(t, 404, res.StatusCode)
}
func TestHSMCreateKeyHandler(t *testing.T) {
ctx, session := SetupHSMEnv(t)
defer ctx.Destroy()
defer ctx.Finalize()
defer ctx.CloseSession(session)
defer ctx.Logout(session)
setup(rufus.SigningServiceIndex{api.RSAAlgorithm: api.NewRSASigningService(ctx, session)})
createKeyURL := fmt.Sprintf("%s/%s", createKeyBaseURL, api.RSAAlgorithm)
request, err := http.NewRequest("POST", createKeyURL, nil)
assert.Nil(t, err)
res, err := http.DefaultClient.Do(request)
assert.Nil(t, err)
jsonBlob, err := ioutil.ReadAll(res.Body)
assert.Nil(t, err)
var keyInfo *pb.PublicKey
err = json.Unmarshal(jsonBlob, &keyInfo)
assert.Nil(t, err)
assert.Equal(t, 200, res.StatusCode)
}
func TestSoftwareCreateKeyHandler(t *testing.T) {
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
setup(rufus.SigningServiceIndex{api.ED25519: sigService})
createKeyURL := fmt.Sprintf("%s/%s", createKeyBaseURL, api.ED25519)
request, err := http.NewRequest("POST", createKeyURL, nil)
assert.Nil(t, err)
res, err := http.DefaultClient.Do(request)
assert.Nil(t, err)
assert.Equal(t, 200, res.StatusCode)
jsonBlob, err := ioutil.ReadAll(res.Body)
assert.Nil(t, err)
var keyInfo *pb.PublicKey
err = json.Unmarshal(jsonBlob, &keyInfo)
assert.Nil(t, err)
}
func TestHSMSignHandler(t *testing.T) {
ctx, session := SetupHSMEnv(t)
defer ctx.Destroy()
defer ctx.Finalize()
defer ctx.CloseSession(session)
defer ctx.Logout(session)
sigService := api.NewRSASigningService(ctx, session)
setup(rufus.SigningServiceIndex{api.RSAAlgorithm: sigService})
key, _ := sigService.CreateKey()
sigRequest := &pb.SignatureRequest{KeyInfo: &pb.KeyInfo{ID: key.KeyInfo.ID, Algorithm: &pb.Algorithm{Algorithm: "RSA"}}, Content: make([]byte, 10)}
requestJson, _ := json.Marshal(sigRequest)
reader = strings.NewReader(string(requestJson))
request, err := http.NewRequest("POST", signBaseURL, reader)
assert.Nil(t, err)
res, err := http.DefaultClient.Do(request)
assert.Nil(t, err)
jsonBlob, err := ioutil.ReadAll(res.Body)
assert.Nil(t, err)
var sig *pb.Signature
err = json.Unmarshal(jsonBlob, &sig)
assert.Nil(t, err)
assert.Equal(t, key.KeyInfo.ID, sig.KeyInfo.ID)
assert.Equal(t, 200, res.StatusCode)
}
func TestSoftwareSignHandler(t *testing.T) {
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
setup(rufus.SigningServiceIndex{api.ED25519: sigService})
key, _ := sigService.CreateKey()
sigRequest := &pb.SignatureRequest{KeyInfo: &pb.KeyInfo{ID: key.KeyInfo.ID, Algorithm: &pb.Algorithm{Algorithm: api.ED25519}}, Content: make([]byte, 10)}
requestJson, _ := json.Marshal(sigRequest)
reader = strings.NewReader(string(requestJson))
request, err := http.NewRequest("POST", signBaseURL, reader)
assert.Nil(t, err)
res, err := http.DefaultClient.Do(request)
assert.Nil(t, err)
assert.Equal(t, 200, res.StatusCode)
jsonBlob, err := ioutil.ReadAll(res.Body)
assert.Nil(t, err)
var sig *pb.Signature
err = json.Unmarshal(jsonBlob, &sig)
assert.Nil(t, err)
assert.Equal(t, key.KeyInfo.ID, sig.KeyInfo.ID)
}
func TestSoftwareSignWithInvalidRequestHandler(t *testing.T) {
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
setup(rufus.SigningServiceIndex{api.ED25519: sigService})
requestJson := "{\"blob\":\"7d16f1d0b95310a7bc557747fc4f20fcd41c1c5095ae42f189df0717e7d7f4a0a2b55debce630f43c4ac099769c612965e3fda3cd4c0078ee6a460f14fa19307\"}"
reader = strings.NewReader(requestJson)
request, err := http.NewRequest("POST", signBaseURL, reader)
assert.Nil(t, err)
res, err := http.DefaultClient.Do(request)
assert.Nil(t, err)
jsonBlob, err := ioutil.ReadAll(res.Body)
assert.Nil(t, err)
var sig *pb.Signature
err = json.Unmarshal(jsonBlob, &sig)
assert.Equal(t, 400, res.StatusCode)
}
func TestSignHandlerReturns404WithNonexistentKey(t *testing.T) {
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
setup(rufus.SigningServiceIndex{api.ED25519: sigService})
fakeID := "c62e6d68851cef1f7e55a9d56e3b0c05f3359f16838cad43600f0554e7d3b54d"
sigService.CreateKey()
sigRequest := &pb.SignatureRequest{KeyInfo: &pb.KeyInfo{ID: fakeID, Algorithm: &pb.Algorithm{Algorithm: api.ED25519}}, Content: make([]byte, 10)}
requestJson, _ := json.Marshal(sigRequest)
reader = strings.NewReader(string(requestJson))
request, err := http.NewRequest("POST", signBaseURL, reader)
assert.Nil(t, err)
res, err := http.DefaultClient.Do(request)
assert.Nil(t, err)
assert.Equal(t, 404, res.StatusCode)
}

View File

@ -0,0 +1,30 @@
package api
import (
"github.com/agl/ed25519"
"github.com/docker/rufus/keys"
pb "github.com/docker/rufus/proto"
)
// ED25519 represents an ed25519 algorithm
const ED25519 string = "ed25519"
// Ed25519Signer implements the Signer interface for Ed25519 keys
type Ed25519Signer struct {
privateKey *keys.Key
}
// Sign returns a signature for a given blob
func (s *Ed25519Signer) Sign(request *pb.SignatureRequest) (*pb.Signature, error) {
priv := [ed25519.PrivateKeySize]byte{}
copy(priv[:], s.privateKey.Private[:])
sig := ed25519.Sign(&priv, request.Content)
return &pb.Signature{KeyInfo: &pb.KeyInfo{ID: s.privateKey.ID, Algorithm: &pb.Algorithm{Algorithm: ED25519}}, Content: sig[:]}, nil
}
// NewEd25519Signer returns a Ed25519Signer, given a private key
func NewEd25519Signer(key *keys.Key) *Ed25519Signer {
return &Ed25519Signer{privateKey: key}
}

View File

@ -0,0 +1,61 @@
package api_test
import (
"encoding/hex"
"fmt"
"testing"
"github.com/agl/ed25519"
"github.com/docker/rufus/api"
"github.com/docker/rufus/keys"
"github.com/stretchr/testify/assert"
pb "github.com/docker/rufus/proto"
)
type zeroReader struct{}
func (zeroReader) Read(buf []byte) (int, error) {
for i := range buf {
buf[i] = 0
}
return len(buf), nil
}
func TestSign(t *testing.T) {
var zero zeroReader
public, private, _ := ed25519.GenerateKey(zero)
blob := []byte("test message")
directSig := ed25519.Sign(private, blob)
directSigHex := hex.EncodeToString(directSig[:])
signer := api.NewEd25519Signer(&keys.Key{Private: private, Public: *public, ID: "fake_id"})
sigRequest := &pb.SignatureRequest{KeyInfo: &pb.KeyInfo{ID: "fake_id", Algorithm: &pb.Algorithm{Algorithm: "ed25519"}}, Content: blob}
sig, err := signer.Sign(sigRequest)
assert.Nil(t, err)
signatureHex := fmt.Sprintf("%x", sig.Content)
assert.Equal(t, directSigHex, signatureHex)
assert.Equal(t, sig.KeyInfo.ID, "fake_id")
}
func BenchmarkSign(b *testing.B) {
blob := []byte("7d16f1d0b95310a7bc557747fc4f20fcd41c1c5095ae42f189df")
keyDB := keys.NewKeyDB()
var sigService = api.NewEdDSASigningService(keyDB)
key, _ := sigService.CreateKey()
privkey, _ := keyDB.GetKey(key.KeyInfo)
signer := api.NewEd25519Signer(privkey)
sigRequest := &pb.SignatureRequest{KeyInfo: &pb.KeyInfo{ID: key.KeyInfo.ID, Algorithm: &pb.Algorithm{Algorithm: "ed25519"}}, Content: blob}
for n := 0; n < b.N; n++ {
_, _ = signer.Sign(sigRequest)
}
}

View File

@ -0,0 +1,68 @@
package api
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"github.com/agl/ed25519"
"github.com/docker/rufus"
"github.com/docker/rufus/keys"
pb "github.com/docker/rufus/proto"
)
// EdDSASigningService is an implementation of SigningService
type EdDSASigningService struct {
KeyDB rufus.KeyDatabase
}
// CreateKey creates a key and returns its public components
func (s EdDSASigningService) CreateKey() (*pb.PublicKey, error) {
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
k := &keys.Key{
Algorithm: ED25519,
Public: *pub,
Private: priv,
}
digest := sha256.Sum256(k.Public[:])
k.ID = hex.EncodeToString(digest[:])
err = s.KeyDB.AddKey(k)
if err != nil {
return nil, err
}
pubKey := &pb.PublicKey{KeyInfo: &pb.KeyInfo{ID: k.ID, Algorithm: &pb.Algorithm{Algorithm: k.Algorithm}}, PublicKey: k.Public[:]}
return pubKey, nil
}
// DeleteKey removes a key from the key database
func (s EdDSASigningService) DeleteKey(keyInfo *pb.KeyInfo) (*pb.Void, error) {
return s.KeyDB.DeleteKey(keyInfo)
}
// KeyInfo returns the public components of a particular key
func (s EdDSASigningService) KeyInfo(keyInfo *pb.KeyInfo) (*pb.PublicKey, error) {
return s.KeyDB.KeyInfo(keyInfo)
}
// Signer returns a Signer for a specific KeyID
func (s EdDSASigningService) Signer(keyInfo *pb.KeyInfo) (rufus.Signer, error) {
key, err := s.KeyDB.GetKey(keyInfo)
if err != nil {
return nil, keys.ErrInvalidKeyID
}
return &Ed25519Signer{privateKey: key}, nil
}
// NewEdDSASigningService returns an instance of KeyDB
func NewEdDSASigningService(keyDB rufus.KeyDatabase) *EdDSASigningService {
return &EdDSASigningService{
KeyDB: keyDB,
}
}

View File

@ -0,0 +1,98 @@
package api_test
import (
"testing"
"github.com/docker/rufus/api"
"github.com/docker/rufus/keys"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
pb "github.com/docker/rufus/proto"
)
type FakeKeyDB struct {
mock.Mock
}
func (m *FakeKeyDB) CreateKey() (*pb.PublicKey, error) {
args := m.Mock.Called()
return args.Get(0).(*pb.PublicKey), args.Error(1)
}
func (m *FakeKeyDB) AddKey(key *keys.Key) error {
args := m.Mock.Called()
return args.Error(0)
}
func (m *FakeKeyDB) DeleteKey(keyInfo *pb.KeyInfo) (*pb.Void, error) {
args := m.Mock.Called(keyInfo.ID)
return nil, args.Error(0)
}
func (m *FakeKeyDB) KeyInfo(keyInfo *pb.KeyInfo) (*pb.PublicKey, error) {
args := m.Mock.Called(keyInfo.ID)
return args.Get(0).(*pb.PublicKey), args.Error(1)
}
func (m *FakeKeyDB) GetKey(keyInfo *pb.KeyInfo) (*keys.Key, error) {
args := m.Mock.Called(keyInfo.ID)
return args.Get(0).(*keys.Key), args.Error(1)
}
func TestDeleteKey(t *testing.T) {
fakeKeyID := "830158bb5a4af00a3f689a8f29120f0fa7f8ae57cf00ce1fede8ae8652b5181a"
m := FakeKeyDB{}
sigService := api.NewEdDSASigningService(&m)
m.On("DeleteKey", fakeKeyID).Return(nil).Once()
_, err := sigService.DeleteKey(&pb.KeyInfo{ID: fakeKeyID})
m.Mock.AssertExpectations(t)
assert.Nil(t, err)
}
func TestKeyInfo(t *testing.T) {
fakeKeyID := "830158bb5a4af00a3f689a8f29120f0fa7f8ae57cf00ce1fede8ae8652b5181a"
m := FakeKeyDB{}
sigService := api.NewEdDSASigningService(&m)
m.On("KeyInfo", fakeKeyID).Return(&pb.PublicKey{}, nil).Once()
_, err := sigService.KeyInfo(&pb.KeyInfo{ID: fakeKeyID})
m.Mock.AssertExpectations(t)
assert.Nil(t, err)
}
func TestCreateKey(t *testing.T) {
m := FakeKeyDB{}
sigService := api.NewEdDSASigningService(&m)
m.On("AddKey").Return(nil).Once()
_, err := sigService.CreateKey()
m.Mock.AssertExpectations(t)
assert.Nil(t, err)
}
func TestSigner(t *testing.T) {
fakeKeyID := "830158bb5a4af00a3f689a8f29120f0fa7f8ae57cf00ce1fede8ae8652b5181a"
m := FakeKeyDB{}
sigService := api.NewEdDSASigningService(&m)
m.On("GetKey", fakeKeyID).Return(&keys.Key{}, nil).Once()
_, err := sigService.Signer(&pb.KeyInfo{ID: fakeKeyID})
m.Mock.AssertExpectations(t)
assert.Nil(t, err)
}
func BenchmarkCreateKey(b *testing.B) {
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
for n := 0; n < b.N; n++ {
_, _ = sigService.CreateKey()
}
}

101
signer/api/rpc_api.go Normal file
View File

@ -0,0 +1,101 @@
package api
import (
"fmt"
"log"
"github.com/docker/rufus"
"github.com/docker/rufus/keys"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
pb "github.com/docker/rufus/proto"
)
//KeyManagementServer implements the KeyManagementServer grpc interface
type KeyManagementServer struct {
SigServices rufus.SigningServiceIndex
}
//SignerServer implements the SignerServer grpc interface
type SignerServer struct {
SigServices rufus.SigningServiceIndex
}
//CreateKey returns a PublicKey created using KeyManagementServer's SigningService
func (s *KeyManagementServer) CreateKey(ctx context.Context, algorithm *pb.Algorithm) (*pb.PublicKey, error) {
service := s.SigServices[algorithm.Algorithm]
if service == nil {
return nil, fmt.Errorf("algorithm %s not supported for create key", algorithm.Algorithm)
}
key, err := service.CreateKey()
if err != nil {
return nil, grpc.Errorf(codes.Internal, "Key creation failed")
}
log.Println("[Rufus CreateKey] : Created KeyID ", key.KeyInfo.ID)
return key, nil
}
//DeleteKey deletes they key associated with a KeyID
func (s *KeyManagementServer) DeleteKey(ctx context.Context, keyInfo *pb.KeyInfo) (*pb.Void, error) {
service := s.SigServices[keyInfo.Algorithm.Algorithm]
if service == nil {
return nil, fmt.Errorf("algorithm %s not supported for delete key", keyInfo.Algorithm.Algorithm)
}
_, err := service.DeleteKey(keyInfo)
log.Println("[Rufus DeleteKey] : Deleted KeyID ", keyInfo.ID)
if err != nil {
switch err {
case keys.ErrInvalidKeyID:
return nil, grpc.Errorf(codes.NotFound, "Invalid keyID: key %s not found", keyInfo.ID)
default:
return nil, grpc.Errorf(codes.Internal, "Key deletion for keyID %s failed", keyInfo.ID)
}
}
return &pb.Void{}, nil
}
//GetKeyInfo returns they PublicKey associated with a KeyID
func (s *KeyManagementServer) GetKeyInfo(ctx context.Context, keyInfo *pb.KeyInfo) (*pb.PublicKey, error) {
service := s.SigServices[keyInfo.Algorithm.Algorithm]
if service == nil {
return nil, fmt.Errorf("algorithm %s not supported for get key info", keyInfo.Algorithm.Algorithm)
}
key, err := service.KeyInfo(keyInfo)
if err != nil {
return nil, grpc.Errorf(codes.NotFound, "Invalid keyID: key %s not found", keyInfo.ID)
}
log.Println("[Rufus GetKeyInfo] : Returning PublicKey for KeyID ", keyInfo.ID)
return key, nil
}
//Sign signs a message and returns the signature using a private key associate with the KeyID from the SignatureRequest
func (s *SignerServer) Sign(ctx context.Context, sr *pb.SignatureRequest) (*pb.Signature, error) {
service := s.SigServices[sr.KeyInfo.Algorithm.Algorithm]
if service == nil {
return nil, fmt.Errorf("algorithm %s not supported for sign", sr.KeyInfo.Algorithm.Algorithm)
}
log.Println("[Rufus Sign] : Signing ", string(sr.Content), " with KeyID ", sr.KeyInfo.ID)
signer, err := service.Signer(sr.KeyInfo)
if err == keys.ErrInvalidKeyID {
return nil, grpc.Errorf(codes.NotFound, "Invalid keyID: key not found")
}
signature, err := signer.Sign(sr)
if err != nil {
return nil, grpc.Errorf(codes.Internal, "Signing failed for keyID %s on hash %s", sr.KeyInfo.ID, sr.Content)
}
return signature, nil
}

136
signer/api/rpc_api_test.go Normal file
View File

@ -0,0 +1,136 @@
package api_test
import (
"fmt"
"log"
"net"
"testing"
"github.com/docker/rufus"
"github.com/docker/rufus/api"
"github.com/docker/rufus/keys"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
pb "github.com/docker/rufus/proto"
)
var (
kmClient pb.KeyManagementClient
sClient pb.SignerClient
grpcServer *grpc.Server
void *pb.Void
)
func init() {
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
sigServices := rufus.SigningServiceIndex{api.ED25519: sigService}
void = &pb.Void{}
//server setup
kms := &api.KeyManagementServer{SigServices: sigServices}
ss := &api.SignerServer{SigServices: sigServices}
grpcServer = grpc.NewServer()
pb.RegisterKeyManagementServer(grpcServer, kms)
pb.RegisterSignerServer(grpcServer, ss)
lis, err := net.Listen("tcp", "127.0.0.1:7899")
if err != nil {
log.Fatalf("failed to listen %v", err)
}
go grpcServer.Serve(lis)
//client setup
conn, err := grpc.Dial("127.0.0.1:7899")
if err != nil {
log.Fatalf("fail to dial: %v", err)
}
kmClient = pb.NewKeyManagementClient(conn)
sClient = pb.NewSignerClient(conn)
}
func TestDeleteKeyHandlerReturnsNotFoundWithNonexistentKey(t *testing.T) {
fakeID := "c62e6d68851cef1f7e55a9d56e3b0c05f3359f16838cad43600f0554e7d3b54d"
keyInfo := &pb.KeyInfo{ID: fakeID, Algorithm: &pb.Algorithm{Algorithm: api.ED25519}}
ret, err := kmClient.DeleteKey(context.Background(), keyInfo)
assert.NotNil(t, err)
assert.Equal(t, grpc.Code(err), codes.NotFound)
assert.Nil(t, ret)
}
func TestCreateKeyHandlerCreatesKey(t *testing.T) {
publicKey, err := kmClient.CreateKey(context.Background(), &pb.Algorithm{Algorithm: api.ED25519})
assert.NotNil(t, publicKey)
assert.NotEmpty(t, publicKey.PublicKey)
assert.NotEmpty(t, publicKey.KeyInfo)
assert.Nil(t, err)
assert.Equal(t, grpc.Code(err), codes.OK)
}
func TestDeleteKeyHandlerDeletesCreatedKey(t *testing.T) {
publicKey, err := kmClient.CreateKey(context.Background(), &pb.Algorithm{Algorithm: api.ED25519})
ret, err := kmClient.DeleteKey(context.Background(), publicKey.KeyInfo)
assert.Nil(t, err)
assert.Equal(t, ret, void)
}
func TestKeyInfoReturnsCreatedKeys(t *testing.T) {
publicKey, err := kmClient.CreateKey(context.Background(), &pb.Algorithm{Algorithm: api.ED25519})
fmt.Println("Pubkey ID: " + publicKey.GetKeyInfo().ID)
returnedPublicKey, err := kmClient.GetKeyInfo(context.Background(), publicKey.KeyInfo)
fmt.Println("returnedPublicKey ID: " + returnedPublicKey.GetKeyInfo().ID)
assert.Nil(t, err)
assert.Equal(t, publicKey.KeyInfo, returnedPublicKey.KeyInfo)
assert.Equal(t, publicKey.PublicKey, returnedPublicKey.PublicKey)
}
func TestCreateKeyCreatesNewKeys(t *testing.T) {
publicKey1, err := kmClient.CreateKey(context.Background(), &pb.Algorithm{Algorithm: api.ED25519})
assert.Nil(t, err)
publicKey2, err := kmClient.CreateKey(context.Background(), &pb.Algorithm{Algorithm: api.ED25519})
assert.Nil(t, err)
assert.NotEqual(t, publicKey1, publicKey2)
assert.NotEqual(t, publicKey1.KeyInfo, publicKey2.KeyInfo)
assert.NotEqual(t, publicKey1.PublicKey, publicKey2.PublicKey)
}
func TestGetKeyInfoReturnsNotFoundOnNonexistKeys(t *testing.T) {
fakeID := "c62e6d68851cef1f7e55a9d56e3b0c05f3359f16838cad43600f0554e7d3b54d"
keyInfo := &pb.KeyInfo{ID: fakeID, Algorithm: &pb.Algorithm{Algorithm: api.ED25519}}
ret, err := kmClient.GetKeyInfo(context.Background(), keyInfo)
assert.NotNil(t, err)
assert.Equal(t, grpc.Code(err), codes.NotFound)
assert.Nil(t, ret)
}
func TestCreatedKeysCanBeUsedToSign(t *testing.T) {
message := []byte{0, 0, 0, 0}
publicKey, err := kmClient.CreateKey(context.Background(), &pb.Algorithm{Algorithm: api.ED25519})
assert.Nil(t, err)
assert.NotNil(t, publicKey)
sr := &pb.SignatureRequest{Content: message, KeyInfo: publicKey.KeyInfo}
assert.NotNil(t, sr)
signature, err := sClient.Sign(context.Background(), sr)
assert.Nil(t, err)
assert.NotNil(t, signature)
assert.NotEmpty(t, signature.Content)
assert.Equal(t, publicKey.KeyInfo, signature.KeyInfo)
}
func TestSignReturnsNotFoundOnNonexistKeys(t *testing.T) {
fakeID := "c62e6d68851cef1f7e55a9d56e3b0c05f3359f16838cad43600f0554e7d3b54d"
keyInfo := &pb.KeyInfo{ID: fakeID, Algorithm: &pb.Algorithm{Algorithm: api.ED25519}}
message := []byte{0, 0, 0, 0}
sr := &pb.SignatureRequest{Content: message, KeyInfo: keyInfo}
ret, err := sClient.Sign(context.Background(), sr)
assert.NotNil(t, err)
assert.Equal(t, grpc.Code(err), codes.NotFound)
assert.Nil(t, ret)
}

View File

@ -0,0 +1,225 @@
package api
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"errors"
"log"
"math/big"
"github.com/docker/rufus"
"github.com/docker/rufus/keys"
"github.com/miekg/pkcs11"
pb "github.com/docker/rufus/proto"
)
// RSAAlgorithm represents the rsa signing algorithm
const RSAAlgorithm string = "rsa"
// RSASigningService is an implementation of SigningService
type RSASigningService struct {
keys map[string]*keys.HSMRSAKey
context *pkcs11.Ctx
session pkcs11.SessionHandle
}
// CreateKey creates a key and returns its public components
func (s RSASigningService) CreateKey() (*pb.PublicKey, error) {
// For now generate random labels for keys
// (diogo): add link between keyID and label in database so we can support multiple keys
randomLabel := make([]byte, 32)
_, err := rand.Read(randomLabel)
if err != nil {
return nil, errors.New("Could not generate a random key label.")
}
// Set the public key template
// CKA_TOKEN: Guarantees key persistence in hardware
// CKA_LABEL: Identifies this specific key inside of the HSM
publicKeyTemplate := []*pkcs11.Attribute{
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, []byte{3}),
pkcs11.NewAttribute(pkcs11.CKA_MODULUS_BITS, 2048),
pkcs11.NewAttribute(pkcs11.CKA_LABEL, string(randomLabel)),
}
privateKeyTemplate := []*pkcs11.Attribute{
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, true),
pkcs11.NewAttribute(pkcs11.CKA_SIGN, true),
pkcs11.NewAttribute(pkcs11.CKA_LABEL, string(randomLabel)),
}
// Generate a new RSA private/public keypair inside of the HSM
pub, priv, err := s.context.GenerateKeyPair(s.session,
[]*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_KEY_PAIR_GEN, nil)},
publicKeyTemplate, privateKeyTemplate)
if err != nil {
return nil, errors.New("Could not generate a new key inside of the HSM.")
}
// (diogo): This template is used for the GetAttribute
template := []*pkcs11.Attribute{
pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, nil),
pkcs11.NewAttribute(pkcs11.CKA_MODULUS_BITS, nil),
pkcs11.NewAttribute(pkcs11.CKA_MODULUS, nil),
}
// Retrieve the public-key material to be able to create a new HSMRSAKey
attr, err := s.context.GetAttributeValue(s.session, pub, template)
if err != nil {
return nil, errors.New("Failed to get Attribute value.")
}
// We're going to store the elements of the RSA Public key, exponent and Modulus inside of exp and mod
var exp int
mod := big.NewInt(0)
// Iterate through all the attributes of this key and saves CKA_PUBLIC_EXPONENT and CKA_MODULUS. Removes ordering specific issues.
for _, a := range attr {
if a.Type == pkcs11.CKA_PUBLIC_EXPONENT {
exp, _ = readInt(a.Value)
}
if a.Type == pkcs11.CKA_MODULUS {
mod.SetBytes(a.Value)
}
}
rsaPublicKey := rsa.PublicKey{N: mod, E: exp}
// Using x509 to Marshal the Public key into der encoding
pubBytes, err := x509.MarshalPKIXPublicKey(&rsaPublicKey)
if err != nil {
return nil, errors.New("Failed to Marshal public key.")
}
// (diogo): Ideally I would like to return base64 PEM encoded public keys to the client
k := &keys.HSMRSAKey{
Algorithm: RSAAlgorithm,
Public: pubBytes,
Private: priv,
}
// (diogo): Change this to be consistent with how TUF does (canonical JSON)
digest := sha256.Sum256(k.Public[:])
k.ID = hex.EncodeToString(digest[:])
s.keys[k.ID] = k
pubKey := &pb.PublicKey{KeyInfo: &pb.KeyInfo{ID: k.ID, Algorithm: &pb.Algorithm{Algorithm: k.Algorithm}}, PublicKey: k.Public[:]}
return pubKey, nil
}
// DeleteKey removes a key from the key database
func (s RSASigningService) DeleteKey(keyInfo *pb.KeyInfo) (*pb.Void, error) {
if _, ok := s.keys[keyInfo.ID]; !ok {
return nil, keys.ErrInvalidKeyID
}
delete(s.keys, keyInfo.ID)
return nil, nil
}
// KeyInfo returns the public components of a particular key
func (s RSASigningService) KeyInfo(keyInfo *pb.KeyInfo) (*pb.PublicKey, error) {
k, ok := s.keys[keyInfo.ID]
if !ok {
return nil, keys.ErrInvalidKeyID
}
pubKey := &pb.PublicKey{KeyInfo: &pb.KeyInfo{ID: k.ID, Algorithm: &pb.Algorithm{Algorithm: k.Algorithm}}, PublicKey: k.Public[:]}
return pubKey, nil
}
// Signer returns a Signer for a specific KeyID
func (s RSASigningService) Signer(keyInfo *pb.KeyInfo) (rufus.Signer, error) {
key, ok := s.keys[keyInfo.ID]
if !ok {
return nil, keys.ErrInvalidKeyID
}
// (diogo): Investigate if caching is worth it. Is this object expensive to create?
return &RSASigner{privateKey: key, context: s.context, session: s.session}, nil
}
// RSASigner implements the Signer interface for RSA keys
type RSASigner struct {
privateKey *keys.HSMRSAKey
context *pkcs11.Ctx
session pkcs11.SessionHandle
}
// Sign returns a signature for a given signature request
func (s *RSASigner) Sign(request *pb.SignatureRequest) (*pb.Signature, error) {
priv := s.privateKey.Private
var sig []byte
var err error
for i := 0; i < 3; i++ {
//TODO(mccauley): move this to RSA OAEP
s.context.SignInit(s.session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_SHA256_RSA_PKCS, nil)}, priv)
sig, err = s.context.Sign(s.session, request.Content)
if err != nil {
log.Printf("Error while signing: %s", err)
continue
}
// (diogo): XXX: Remove this before shipping
digest := sha256.Sum256(request.Content)
pub, err := x509.ParsePKIXPublicKey(s.privateKey.Public)
if err != nil {
log.Printf("Failed to parse public key: %s\n", err)
return nil, err
}
rsaPub, ok := pub.(*rsa.PublicKey)
if !ok {
log.Printf("Value returned from ParsePKIXPublicKey was not an RSA public key")
return nil, err
}
err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, digest[:], sig)
if err != nil {
log.Printf("Failed verification. Retrying: %s", err)
continue
}
break
}
// (diogo): XXX: END Area of removal
if sig == nil {
return nil, errors.New("Failed to create signature")
}
returnSig := &pb.Signature{KeyInfo: &pb.KeyInfo{ID: s.privateKey.ID, Algorithm: &pb.Algorithm{Algorithm: RSAAlgorithm}}, Content: sig[:]}
log.Printf("[Rufus Server] Signature request JSON: %s , response: %s", string(request.Content), returnSig)
return returnSig, nil
}
// NewRSASigningService returns an instance of KeyDB
func NewRSASigningService(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) *RSASigningService {
return &RSASigningService{
keys: make(map[string]*keys.HSMRSAKey),
context: ctx,
session: session,
}
}
// readInt converts a []byte into an int. It is used to convert the RSA Public key exponent into an int to create a crypto.PublicKey
func readInt(data []byte) (int, error) {
var ret int
if len(data) > 4 {
return 0, errors.New("Cannot convert byte array due to size")
}
for i, a := range data {
ret |= (int(a) << uint(i*8))
}
return ret, nil
}

36
signer/keys/hex_bytes.go Normal file
View File

@ -0,0 +1,36 @@
package keys
import (
"encoding/hex"
"errors"
)
// HexBytes represents hexadecimal bytes
type HexBytes []byte
// UnmarshalJSON allows the representation in JSON of hexbytes
func (b *HexBytes) UnmarshalJSON(data []byte) error {
if len(data) < 2 || len(data)%2 != 0 || data[0] != '"' || data[len(data)-1] != '"' {
return errors.New("tuf: invalid JSON hex bytes")
}
res := make([]byte, hex.DecodedLen(len(data)-2))
_, err := hex.Decode(res, data[1:len(data)-1])
if err != nil {
return err
}
*b = res
return nil
}
// MarshalJSON allows the representation in JSON of hexbytes
func (b HexBytes) MarshalJSON() ([]byte, error) {
res := make([]byte, hex.EncodedLen(len(b))+2)
res[0] = '"'
res[len(res)-1] = '"'
hex.Encode(res[1:], b)
return res, nil
}
func (b HexBytes) String() string {
return hex.EncodeToString(b)
}

59
signer/keys/key_db.go Normal file
View File

@ -0,0 +1,59 @@
package keys
import pb "github.com/docker/rufus/proto"
// KeyDB represents an in-memory key keystore
type KeyDB struct {
keys map[string]*Key
}
// CreateKey is needed to implement KeyManager. Returns an empty key.
func (db *KeyDB) CreateKey() (*pb.PublicKey, error) {
k := &pb.PublicKey{}
return k, nil
}
// AddKey Adds a new key to the database
func (db *KeyDB) AddKey(key *Key) error {
if _, ok := db.keys[key.ID]; ok {
return ErrExists
}
db.keys[key.ID] = key
return nil
}
// GetKey returns the private bits of a key
func (db *KeyDB) GetKey(keyInfo *pb.KeyInfo) (*Key, error) {
if key, ok := db.keys[keyInfo.ID]; ok {
return key, nil
}
return nil, ErrInvalidKeyID
}
// DeleteKey deletes the keyID from the database
func (db *KeyDB) DeleteKey(keyInfo *pb.KeyInfo) (*pb.Void, error) {
_, err := db.GetKey(keyInfo)
if err != nil {
return nil, err
}
delete(db.keys, keyInfo.ID)
return nil, nil
}
// KeyInfo returns the public bits of a key, given a specific keyID
func (db *KeyDB) KeyInfo(keyInfo *pb.KeyInfo) (*pb.PublicKey, error) {
key, err := db.GetKey(keyInfo)
if err != nil {
return nil, err
}
return &pb.PublicKey{KeyInfo: &pb.KeyInfo{ID: keyInfo.ID, Algorithm: &pb.Algorithm{Algorithm: key.Algorithm}}, PublicKey: key.Public[:]}, nil
}
// NewKeyDB returns an instance of KeyDB
func NewKeyDB() *KeyDB {
return &KeyDB{
keys: make(map[string]*Key),
}
}

33
signer/keys/keys.go Normal file
View File

@ -0,0 +1,33 @@
package keys
import (
"errors"
"github.com/agl/ed25519"
"github.com/miekg/pkcs11"
)
var (
// ErrExists happens when a Key already exists in a database
ErrExists = errors.New("rufus: key already in db")
// ErrInvalidKeyID error happens when a key isn't found
ErrInvalidKeyID = errors.New("rufus: invalid key id")
// ErrFailedKeyGeneration happens when there is a failure in generating a key
ErrFailedKeyGeneration = errors.New("rufus: failed to generate new key")
)
// Key represents all the information of a key, including the private and public bits
type Key struct {
ID string
Algorithm string
Public [ed25519.PublicKeySize]byte
Private *[ed25519.PrivateKeySize]byte
}
// HSMRSAKey represents the information for an HSMRSAKey with ObjectHandle for private portion
type HSMRSAKey struct {
ID string
Algorithm string
Public []byte
Private pkcs11.ObjectHandle
}

View File

@ -4,21 +4,21 @@ import (
"net"
"github.com/Sirupsen/logrus"
pb "github.com/docker/rufus/proto"
pb "github.com/docker/notary/proto"
"github.com/endophage/gotuf/data"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
// RufusSigner implements a RPC based Trust service that calls the Rufus Service
type RufusSigner struct {
// NotarySigner implements a RPC based Trust service that calls the Notary-signer Service
type NotarySigner struct {
kmClient pb.KeyManagementClient
sClient pb.SignerClient
}
// NewRufusSigner is a convinience method that returns RufusSigner
func NewRufusSigner(hostname string, port string, tlscafile string) *RufusSigner {
// NewNotarySigner is a convinience method that returns NotarySigner
func NewNotarySigner(hostname string, port string, tlscafile string) *NotarySigner {
var opts []grpc.DialOption
netAddr := net.JoinHostPort(hostname, port)
creds, err := credentials.NewClientTLSFromFile(tlscafile, hostname)
@ -33,14 +33,14 @@ func NewRufusSigner(hostname string, port string, tlscafile string) *RufusSigner
}
kmClient := pb.NewKeyManagementClient(conn)
sClient := pb.NewSignerClient(conn)
return &RufusSigner{
return &NotarySigner{
kmClient: kmClient,
sClient: sClient,
}
}
// Sign signs a byte string with a number of KeyIDs
func (trust *RufusSigner) Sign(keyIDs []string, toSign []byte) ([]data.Signature, error) {
func (trust *NotarySigner) Sign(keyIDs []string, toSign []byte) ([]data.Signature, error) {
signatures := make([]data.Signature, 0, len(keyIDs))
for _, ID := range keyIDs {
keyID := pb.KeyID{ID: ID}
@ -62,8 +62,8 @@ func (trust *RufusSigner) Sign(keyIDs []string, toSign []byte) ([]data.Signature
}
// Create creates a remote key and returns the PublicKey associated with the remote private key
// TODO(diogo): Ignoring algorithm for now until rufus supports it
func (trust *RufusSigner) Create(role string, _ data.KeyAlgorithm) (*data.PublicKey, error) {
// TODO(diogo): Ignoring algorithm for now until notary-signer supports it
func (trust *NotarySigner) Create(role string, _ data.KeyAlgorithm) (*data.PublicKey, error) {
publicKey, err := trust.kmClient.CreateKey(context.Background(), &pb.Void{})
if err != nil {
return nil, err
@ -74,7 +74,7 @@ func (trust *RufusSigner) Create(role string, _ data.KeyAlgorithm) (*data.Public
}
// PublicKeys returns the public key(s) associated with the passed in keyIDs
func (trust *RufusSigner) PublicKeys(keyIDs ...string) (map[string]*data.PublicKey, error) {
func (trust *NotarySigner) PublicKeys(keyIDs ...string) (map[string]*data.PublicKey, error) {
publicKeys := make(map[string]*data.PublicKey)
for _, ID := range keyIDs {
keyID := pb.KeyID{ID: ID}

5
signer/softhsm2.conf Normal file
View File

@ -0,0 +1,5 @@
# SoftHSM v2 configuration file
directories.tokendir = /softhsm2/tokens
objectstore.backend = db
log.level = INFO