veyron/runtimes/google/security: replace private key with a generic Signer.
The goal here is to abstract the signing operation so that it can be performed
by agents that don't necessarily want to expose their private keys (e.g.,
ssh-agent, android KeyStore).
Change-Id: Ic118aa006f3bf121f92998392c3a6a21babad599
diff --git a/lib/testutil/security/util.go b/lib/testutil/security/util.go
index f2a4214..ddd65e5 100644
--- a/lib/testutil/security/util.go
+++ b/lib/testutil/security/util.go
@@ -18,7 +18,7 @@
// under the provided name. This function is meant to be used for testing purposes only,
// it panics if there is an error.
func NewBlessedIdentity(blesser security.PrivateID, name string) security.PrivateID {
- id, err := isecurity.NewPrivateID("test")
+ id, err := isecurity.NewPrivateID("test", nil)
if err != nil {
panic(err)
}
diff --git a/runtimes/google/ipc/stream/manager/manager_test.go b/runtimes/google/ipc/stream/manager/manager_test.go
index 2e02ffb..84a78ee 100644
--- a/runtimes/google/ipc/stream/manager/manager_test.go
+++ b/runtimes/google/ipc/stream/manager/manager_test.go
@@ -24,7 +24,7 @@
)
func newID(name string) security.PrivateID {
- id, err := isecurity.NewPrivateID(name)
+ id, err := isecurity.NewPrivateID(name, nil)
if err != nil {
panic(err)
}
diff --git a/runtimes/google/ipc/stream/proxy/proxy_test.go b/runtimes/google/ipc/stream/proxy/proxy_test.go
index 7b3e1dd..c5ccf58 100644
--- a/runtimes/google/ipc/stream/proxy/proxy_test.go
+++ b/runtimes/google/ipc/stream/proxy/proxy_test.go
@@ -20,7 +20,7 @@
)
func newID(name string) security.PrivateID {
- id, err := isecurity.NewPrivateID(name)
+ id, err := isecurity.NewPrivateID(name, nil)
if err != nil {
panic(err)
}
diff --git a/runtimes/google/ipc/stream/vc/init.go b/runtimes/google/ipc/stream/vc/init.go
index a801353..261df0d 100644
--- a/runtimes/google/ipc/stream/vc/init.go
+++ b/runtimes/google/ipc/stream/vc/init.go
@@ -11,7 +11,7 @@
func init() {
var err error
- if anonymousID, err = isecurity.NewPrivateID("anonymous"); err != nil {
+ if anonymousID, err = isecurity.NewPrivateID("anonymous", nil); err != nil {
vlog.Fatalf("could create anonymousID for IPCs: %s", err)
}
}
diff --git a/runtimes/google/ipc/stream/vc/listener_test.go b/runtimes/google/ipc/stream/vc/listener_test.go
index a621b97..80c52b4 100644
--- a/runtimes/google/ipc/stream/vc/listener_test.go
+++ b/runtimes/google/ipc/stream/vc/listener_test.go
@@ -16,7 +16,7 @@
type noopFlow struct{}
func newID(name string) security.PrivateID {
- id, err := isecurity.NewPrivateID(name)
+ id, err := isecurity.NewPrivateID(name, nil)
if err != nil {
panic(err)
}
diff --git a/runtimes/google/ipc/stream/vc/vc_test.go b/runtimes/google/ipc/stream/vc/vc_test.go
index 3acd6fb..2cd770e 100644
--- a/runtimes/google/ipc/stream/vc/vc_test.go
+++ b/runtimes/google/ipc/stream/vc/vc_test.go
@@ -43,7 +43,7 @@
)
func newID(name string) security.PrivateID {
- id, err := isecurity.NewPrivateID(name)
+ id, err := isecurity.NewPrivateID(name, nil)
if err != nil {
panic(err)
}
diff --git a/runtimes/google/ipc/stream/vif/vif_test.go b/runtimes/google/ipc/stream/vif/vif_test.go
index a743542..769a90d 100644
--- a/runtimes/google/ipc/stream/vif/vif_test.go
+++ b/runtimes/google/ipc/stream/vif/vif_test.go
@@ -26,7 +26,7 @@
)
func newLocalID(name string) vc.LocalID {
- id, err := isecurity.NewPrivateID(name)
+ id, err := isecurity.NewPrivateID(name, nil)
if err != nil {
panic(err)
}
diff --git a/runtimes/google/ipc/testutil_test.go b/runtimes/google/ipc/testutil_test.go
index f990ca7..2768b0c 100644
--- a/runtimes/google/ipc/testutil_test.go
+++ b/runtimes/google/ipc/testutil_test.go
@@ -42,7 +42,7 @@
}
func newID(name string) security.PrivateID {
- id, err := isecurity.NewPrivateID(name)
+ id, err := isecurity.NewPrivateID(name, nil)
if err != nil {
panic(err)
}
diff --git a/runtimes/google/rt/ipc_test.go b/runtimes/google/rt/ipc_test.go
index 1822541..85c49fd 100644
--- a/runtimes/google/rt/ipc_test.go
+++ b/runtimes/google/rt/ipc_test.go
@@ -26,7 +26,7 @@
type S []string
func newID(name string) security.PrivateID {
- id, err := isecurity.NewPrivateID(name)
+ id, err := isecurity.NewPrivateID(name, nil)
if err != nil {
panic(err)
}
diff --git a/runtimes/google/rt/security.go b/runtimes/google/rt/security.go
index 466cb8a..5326b2c 100644
--- a/runtimes/google/rt/security.go
+++ b/runtimes/google/rt/security.go
@@ -12,7 +12,7 @@
)
func (rt *vrt) NewIdentity(name string) (security.PrivateID, error) {
- return isecurity.NewPrivateID(name)
+ return isecurity.NewPrivateID(name, nil)
}
func (rt *vrt) Identity() security.PrivateID {
diff --git a/runtimes/google/security/identity.go b/runtimes/google/security/identity.go
index 857408c..a4765ee 100644
--- a/runtimes/google/security/identity.go
+++ b/runtimes/google/security/identity.go
@@ -2,9 +2,13 @@
import "veyron2/security"
-// NewPrivateID returns a new PrivateID containing a freshly generated
-// private key, and a single self-signed certificate specifying the provided
-// name and the public key corresponding to the generated private key.
-func NewPrivateID(name string) (security.PrivateID, error) {
- return newChainPrivateID(name)
+// NewPrivateID returns a new PrivateID that uses the provided Signer to generate
+// signatures. The returned PrivateID additionaly contains a single self-signed
+// certificate with the given name.
+//
+// If a nil signer is provided, this method will generate a new public/private key pair
+// and use a system-default signer, which stores the private key in the clear in the memory
+// of the running process.
+func NewPrivateID(name string, signer security.Signer) (security.PrivateID, error) {
+ return newChainPrivateID(name, signer)
}
diff --git a/runtimes/google/security/identity_chain.go b/runtimes/google/security/identity_chain.go
index 2c3c4d8..87f55ac 100644
--- a/runtimes/google/security/identity_chain.go
+++ b/runtimes/google/security/identity_chain.go
@@ -116,24 +116,20 @@
// chainPrivateID implements security.PrivateID
type chainPrivateID struct {
+ security.Signer
publicID *chainPublicID
- privateKey *ecdsa.PrivateKey
+ privateKey *ecdsa.PrivateKey // can be nil
}
func (id *chainPrivateID) PublicID() security.PublicID { return id.publicID }
-func (id *chainPrivateID) Sign(message []byte) (sig security.Signature, err error) {
- r, s, err := ecdsa.Sign(rand.Reader, id.privateKey, message)
- if err != nil {
- return
- }
- sig.R, sig.S = r.Bytes(), s.Bytes()
- return
-}
-
func (id *chainPrivateID) String() string { return fmt.Sprintf("PrivateID:%v", id.publicID) }
func (id *chainPrivateID) VomEncode() (*wire.ChainPrivateID, error) {
+ if id.privateKey == nil {
+ // TODO(ataly): Figure out a clean way to serialize Signers.
+ return nil, fmt.Errorf("cannot vom-encode a chainPrivateID that doesn't have access to a private key")
+ }
pub, err := id.publicID.VomEncode()
if err != nil {
return nil, err
@@ -150,6 +146,7 @@
PublicKey: *id.publicID.publicKey,
D: new(big.Int).SetBytes(w.Secret),
}
+ id.Signer = NewClearSigner(id.privateKey)
return nil
}
@@ -172,11 +169,11 @@
if cert.Caveats, err = wire.EncodeCaveats(caveats); err != nil {
return nil, err
}
- vomID, err := id.VomEncode()
+ vomPubID, err := id.publicID.VomEncode()
if err != nil {
return nil, err
}
- if err := cert.Sign(vomID); err != nil {
+ if err := cert.Sign(id, vomPubID); err != nil {
return nil, err
}
w := &wire.ChainPublicID{
@@ -197,6 +194,7 @@
switch p := pub.(type) {
case *chainPublicID:
return &chainPrivateID{
+ Signer: id.Signer,
publicID: p,
privateKey: id.privateKey,
}, nil
@@ -218,35 +216,47 @@
return caveat.NewPublicKeyDischarge(id, cav, ctx, duration, dischargeCaveats)
}
-// newChainPrivateID returns a new PrivateID containing a freshly generated
-// private key, and a single self-signed certificate specifying the provided
-// name and the public key corresponding to the generated private key.
-func newChainPrivateID(name string) (security.PrivateID, error) {
+// newChainPrivateID returns a new PrivateID that uses the provided Signer to generate
+// signatures. The returned PrivateID additionaly contains a single self-signed
+// certificate with the given name.
+//
+// If a nil signer is provided, this method will generate a new public/private key pair
+// and use a system-default signer, which stores the private key in the clear in the memory
+// of the running process.
+func newChainPrivateID(name string, signer security.Signer) (security.PrivateID, error) {
if err := wire.ValidateBlessingName(name); err != nil {
return nil, err
}
- key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
- if err != nil {
- return nil, err
+ var privKey *ecdsa.PrivateKey
+ if signer == nil {
+ var err error
+ privKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ return nil, err
+ }
+ signer = NewClearSigner(privKey)
}
+
id := &chainPrivateID{
+ Signer: signer,
publicID: &chainPublicID{
certificates: []wire.Certificate{{Name: name}},
name: name,
- publicKey: &key.PublicKey,
- rootKey: &key.PublicKey,
+ publicKey: signer.PublicKey(),
+ rootKey: signer.PublicKey(),
},
- privateKey: key,
+ privateKey: privKey,
}
+ // Self-sign the (single) certificate.
cert := &id.publicID.certificates[0]
- if err := cert.PublicKey.Encode(&key.PublicKey); err != nil {
+ if err := cert.PublicKey.Encode(signer.PublicKey()); err != nil {
return nil, err
}
- vomID, err := id.VomEncode()
+ vomPubID, err := id.publicID.VomEncode()
if err != nil {
return nil, err
}
- if err := cert.Sign(vomID); err != nil {
+ if err := cert.Sign(id, vomPubID); err != nil {
return nil, err
}
return id, nil
diff --git a/runtimes/google/security/identity_set.go b/runtimes/google/security/identity_set.go
index 0b2bd89..1c00e00 100644
--- a/runtimes/google/security/identity_set.go
+++ b/runtimes/google/security/identity_set.go
@@ -167,6 +167,8 @@
func (s setPrivateID) Sign(message []byte) (security.Signature, error) { return s[0].Sign(message) }
+func (s setPrivateID) PublicKey() *ecdsa.PublicKey { return s[0].PublicKey() }
+
func (s setPrivateID) Bless(blessee security.PublicID, blessingName string, duration time.Duration, caveats []security.ServiceCaveat) (security.PublicID, error) {
pubs := make([]security.PublicID, len(s))
for ix, id := range s {
diff --git a/runtimes/google/security/identity_test.go b/runtimes/google/security/identity_test.go
index 18b43bf..890325c 100644
--- a/runtimes/google/security/identity_test.go
+++ b/runtimes/google/security/identity_test.go
@@ -32,7 +32,7 @@
{"google/alice/bob", "invalid blessing name"},
}
for _, d := range testdata {
- if _, err := NewPrivateID(d.name); !matchesErrorPattern(err, d.err) {
+ if _, err := NewPrivateID(d.name, nil); !matchesErrorPattern(err, d.err) {
t.Errorf("NewPrivateID(%q): got: %s, want to match: %s", d.name, err, d.err)
}
}
diff --git a/runtimes/google/security/performance_test.go b/runtimes/google/security/performance_test.go
index 06cbe5e..4f9fd76 100644
--- a/runtimes/google/security/performance_test.go
+++ b/runtimes/google/security/performance_test.go
@@ -58,7 +58,7 @@
func BenchmarkNewChain(b *testing.B) {
for i := 0; i < b.N; i++ {
- if _, err := newChainPrivateID("X"); err != nil {
+ if _, err := newChainPrivateID("X", nil); err != nil {
b.Fatalf("Failed newChainPrivateID #%d: %v", i, err)
}
diff --git a/runtimes/google/security/signer.go b/runtimes/google/security/signer.go
new file mode 100644
index 0000000..9d96754
--- /dev/null
+++ b/runtimes/google/security/signer.go
@@ -0,0 +1,31 @@
+package security
+
+import (
+ "crypto/ecdsa"
+ "crypto/rand"
+
+ "veyron2/security"
+)
+
+// NewClearSigner creates a Signer that uses the provided private key to sign messages.
+// This private key is kept in the clear in the memory of the running process.
+func NewClearSigner(key *ecdsa.PrivateKey) security.Signer {
+ return &clearSigner{key}
+}
+
+type clearSigner struct {
+ key *ecdsa.PrivateKey
+}
+
+func (c *clearSigner) Sign(message []byte) (sig security.Signature, err error) {
+ r, s, err := ecdsa.Sign(rand.Reader, c.key, message)
+ if err != nil {
+ return
+ }
+ sig.R, sig.S = r.Bytes(), s.Bytes()
+ return
+}
+
+func (c *clearSigner) PublicKey() *ecdsa.PublicKey {
+ return &c.key.PublicKey
+}
diff --git a/runtimes/google/security/signer_test.go b/runtimes/google/security/signer_test.go
new file mode 100644
index 0000000..cd81097
--- /dev/null
+++ b/runtimes/google/security/signer_test.go
@@ -0,0 +1,36 @@
+package security
+
+import (
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/rand"
+ "crypto/sha256"
+ "math/big"
+ "testing"
+)
+
+func TestSigner(t *testing.T) {
+ key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ t.Fatalf("Couldn't generate ECDSA private key: %v", err)
+ }
+ signer := NewClearSigner(key)
+ testdata := [][]byte{
+ nil,
+ []byte{},
+ []byte(""),
+ []byte("withorwithoutyou"),
+ []byte("wherethestreetshavenoname"),
+ }
+ for _, d := range testdata {
+ hash := sha256.Sum256(d)
+ sig, err := signer.Sign(hash[:])
+ if err != nil {
+ t.Errorf("Sign(%q) returned err: %q, expected success.", d, err)
+ continue
+ }
+ if !ecdsa.Verify(signer.PublicKey(), hash[:], new(big.Int).SetBytes(sig.R), new(big.Int).SetBytes(sig.S)) {
+ t.Errorf("Sign(%q) signature couldn't be verified.", d)
+ }
+ }
+}
diff --git a/runtimes/google/security/util_test.go b/runtimes/google/security/util_test.go
index f1f76fc..4c495c9 100644
--- a/runtimes/google/security/util_test.go
+++ b/runtimes/google/security/util_test.go
@@ -50,7 +50,7 @@
}
func newChain(name string) security.PrivateID {
- id, err := newChainPrivateID(name)
+ id, err := newChainPrivateID(name, nil)
if err != nil {
panic(err)
}