Merge "Add a verify method to security.Signature. This is in preparation for adding additional fields so we can support multiple signature encodings."
diff --git a/runtimes/google/ipc/stream/vc/auth.go b/runtimes/google/ipc/stream/vc/auth.go
index 0d4b094..76e7118 100644
--- a/runtimes/google/ipc/stream/vc/auth.go
+++ b/runtimes/google/ipc/stream/vc/auth.go
@@ -11,6 +11,7 @@
"veyron/runtimes/google/ipc/stream/crypto"
"veyron/runtimes/google/lib/iobuf"
+ "veyron2/ipc/version"
"veyron2/security"
"veyron2/vom"
)
@@ -22,17 +23,17 @@
// authenticateAsServer executes the authentication protocol at the server and
// returns the identity of the client and server.
-func authenticateAsServer(conn io.ReadWriteCloser, localID LocalID, crypter crypto.Crypter) (clientID, serverID security.PublicID, err error) {
+func authenticateAsServer(conn io.ReadWriteCloser, localID LocalID, crypter crypto.Crypter, v version.IPCVersion) (clientID, serverID security.PublicID, err error) {
// The authentication protocol has the server doing the final read, so
// it is the one that closes the connection.
defer conn.Close()
if serverID, err = localID.AsServer(); err != nil {
return
}
- if err = writeIdentity(conn, serverChannelEnd, crypter, localID, serverID); err != nil {
+ if err = writeIdentity(conn, serverChannelEnd, crypter, localID, serverID, v); err != nil {
return
}
- clientID, err = readIdentity(conn, clientChannelEnd, crypter)
+ clientID, err = readIdentity(conn, clientChannelEnd, crypter, v)
return
}
@@ -41,9 +42,9 @@
//
// If serverName is non-nil, the authentication protocol will be considered
// successfull iff the server identity matches the provided regular expression.
-func authenticateAsClient(conn io.ReadWriteCloser, localID LocalID, crypter crypto.Crypter) (serverID, clientID security.PublicID, err error) {
+func authenticateAsClient(conn io.ReadWriteCloser, localID LocalID, crypter crypto.Crypter, v version.IPCVersion) (serverID, clientID security.PublicID, err error) {
defer conn.Close()
- if serverID, err = readIdentity(conn, serverChannelEnd, crypter); err != nil {
+ if serverID, err = readIdentity(conn, serverChannelEnd, crypter, v); err != nil {
return
}
// TODO(ashankar,ataly): Have the ability to avoid talking to a server we do not want to.
@@ -51,7 +52,7 @@
if clientID, err = localID.AsClient(serverID); err != nil {
return
}
- err = writeIdentity(conn, clientChannelEnd, crypter, localID, clientID)
+ err = writeIdentity(conn, clientChannelEnd, crypter, localID, clientID, v)
return
}
@@ -60,9 +61,11 @@
//
// Each field in the message is encrypted by the sender.
type identityMessage struct {
- ChannelID []byte
- ID []byte // VOM-encoded
- SignR, SignS []byte
+ ChannelID []byte
+ ID []byte // VOM-encoded
+ SignR []byte
+ SignS []byte
+ Signature []byte // VOM-encoded
}
var (
@@ -73,7 +76,7 @@
errSingleCertificateRequired = errors.New("exactly one X.509 certificate chain with exactly one certificate is required")
)
-func writeIdentity(w io.Writer, chEnd string, enc crypto.Encrypter, id LocalID, pub security.PublicID) error {
+func writeIdentity(w io.Writer, chEnd string, enc crypto.Encrypter, id LocalID, pub security.PublicID, v version.IPCVersion) error {
// Compute channel id - encrypted chEnd string
chid, err := enc.Encrypt(iobuf.NewSlice([]byte(chEnd)))
if err != nil {
@@ -97,27 +100,40 @@
if err != nil {
return err
}
- er, err := enc.Encrypt(iobuf.NewSlice(signature.R.Bytes()))
- if err != nil {
- return err
- }
- defer er.Release()
- es, err := enc.Encrypt(iobuf.NewSlice(signature.S.Bytes()))
- if err != nil {
- return err
- }
- defer es.Release()
-
- // Write the message out.
- return vom.NewEncoder(w).Encode(identityMessage{
+ msg := identityMessage{
ID: eid.Contents,
ChannelID: chid.Contents,
- SignR: er.Contents,
- SignS: es.Contents,
- })
+ }
+ if v == version.IPCVersion1 {
+ er, err := enc.Encrypt(iobuf.NewSlice(signature.R))
+ if err != nil {
+ return err
+ }
+ defer er.Release()
+ es, err := enc.Encrypt(iobuf.NewSlice(signature.S))
+ if err != nil {
+ return err
+ }
+ defer es.Release()
+ msg.SignR, msg.SignS = er.Contents, es.Contents
+ } else {
+ buf = bytes.Buffer{}
+ if err := vom.NewEncoder(&buf).Encode(signature); err != nil {
+ return err
+ }
+ esig, err := enc.Encrypt(iobuf.NewSlice(buf.Bytes()))
+ if err != nil {
+ return err
+ }
+ defer esig.Release()
+ msg.Signature = esig.Contents
+ }
+
+ // Write the message out.
+ return vom.NewEncoder(w).Encode(msg)
}
-func readIdentity(reader io.Reader, expectedChEnd string, dec crypto.Decrypter) (security.PublicID, error) {
+func readIdentity(reader io.Reader, expectedChEnd string, dec crypto.Decrypter, v version.IPCVersion) (security.PublicID, error) {
// Read the message.
var msg identityMessage
if err := vom.NewDecoder(reader).Decode(&msg); err != nil {
@@ -149,19 +165,34 @@
}
// Decrypt the signature
- r, err := dec.Decrypt(iobuf.NewSlice(msg.SignR))
- if err != nil {
- return nil, fmt.Errorf("failed to decrypt SignR: %v", err)
- }
- defer r.Release()
- s, err := dec.Decrypt(iobuf.NewSlice(msg.SignS))
- if err != nil {
- return nil, fmt.Errorf("failed to decrypt SignS: %v", err)
- }
- defer s.Release()
- // Validate the signature
- if !ecdsa.Verify(id.PublicKey(), msg.ChannelID, bigInt(r), bigInt(s)) {
- return nil, errInvalidSignatureInMessage
+ if v == version.IPCVersion1 {
+ r, err := dec.Decrypt(iobuf.NewSlice(msg.SignR))
+ if err != nil {
+ return nil, fmt.Errorf("failed to decrypt SignR: %v", err)
+ }
+ defer r.Release()
+ s, err := dec.Decrypt(iobuf.NewSlice(msg.SignS))
+ if err != nil {
+ return nil, fmt.Errorf("failed to decrypt SignS: %v", err)
+ }
+ defer s.Release()
+ if !ecdsa.Verify(id.PublicKey(), msg.ChannelID, bigInt(r), bigInt(s)) {
+ return nil, errInvalidSignatureInMessage
+ }
+ } else {
+ sigbytes, err := dec.Decrypt(iobuf.NewSlice(msg.Signature))
+ if err != nil {
+ return nil, fmt.Errorf("failed to decrypt Signature: %v", err)
+ }
+ defer sigbytes.Release()
+ var sig security.Signature
+ if err = vom.NewDecoder(bytes.NewBuffer(sigbytes.Contents)).Decode(&sig); err != nil {
+ return nil, fmt.Errorf("failed to decode security.Signature: %v", err)
+ }
+ // Validate the signature
+ if !sig.Verify(id.PublicKey(), msg.ChannelID) {
+ return nil, errInvalidSignatureInMessage
+ }
}
return id, nil
diff --git a/runtimes/google/ipc/stream/vc/vc.go b/runtimes/google/ipc/stream/vc/vc.go
index 7f70721..894928f 100644
--- a/runtimes/google/ipc/stream/vc/vc.go
+++ b/runtimes/google/ipc/stream/vc/vc.go
@@ -20,6 +20,7 @@
"veyron2"
"veyron2/ipc/stream"
+ "veyron2/ipc/version"
"veyron2/naming"
"veyron2/security"
"veyron2/vlog"
@@ -57,6 +58,7 @@
closeReason string // reason why the VC was closed
helper Helper
+ version version.IPCVersion
}
// Helper is the interface for functionality required by the stream.VC
@@ -88,6 +90,7 @@
Pool *iobuf.Pool // Byte pool used for read and write buffer allocations.
ReserveBytes uint // Number of bytes to reserve in iobuf.Slices.
Helper Helper
+ Version version.IPCVersion
}
// LocalID is the interface for providing a PrivateID and a PublicIDStore to
@@ -161,6 +164,7 @@
nextConnectFID: id.Flow(NumReservedFlows + fidOffset),
crypter: crypto.NewNullCrypter(),
helper: p.Helper,
+ version: p.Version,
}
}
@@ -417,7 +421,7 @@
if err != nil {
return vc.err(fmt.Errorf("failed to create a Flow for authentication: %v", err))
}
- rID, lID, err := authenticateAsClient(authConn, localID, crypter)
+ rID, lID, err := authenticateAsClient(authConn, localID, crypter, vc.version)
if err != nil {
return vc.err(fmt.Errorf("authentication failed: %v", err))
}
@@ -518,7 +522,7 @@
vc.mu.Lock()
vc.authFID = vc.findFlowLocked(authConn)
vc.mu.Unlock()
- rID, lID, err := authenticateAsServer(authConn, localID, crypter)
+ rID, lID, err := authenticateAsServer(authConn, localID, crypter, vc.version)
if err != nil {
sendErr(fmt.Errorf("Authentication failed: %v", err))
return
diff --git a/runtimes/google/ipc/stream/vc/vc_test.go b/runtimes/google/ipc/stream/vc/vc_test.go
index f1b1e2e..9bcb602 100644
--- a/runtimes/google/ipc/stream/vc/vc_test.go
+++ b/runtimes/google/ipc/stream/vc/vc_test.go
@@ -23,6 +23,7 @@
"veyron2"
"veyron2/ipc/stream"
+ "veyron2/ipc/version"
"veyron2/naming"
"veyron2/security"
)
@@ -100,7 +101,7 @@
}
func testHandshake(t *testing.T, security veyron2.VCSecurityLevel, localID, remoteID security.PublicID) {
- h, vc := New(security)
+ h, vc := New(security, version.IPCVersion2)
flow, err := vc.Connect()
if err != nil {
t.Fatal(err)
@@ -126,7 +127,7 @@
}
func testConnect_Small(t *testing.T, security veyron2.VCSecurityLevel) {
- h, vc := New(security)
+ h, vc := New(security, version.IPCVersion2)
defer h.Close()
flow, err := vc.Connect()
if err != nil {
@@ -138,7 +139,7 @@
func TestConnect_SmallTLS(t *testing.T) { testConnect_Small(t, SecurityTLS) }
func testConnect(t *testing.T, security veyron2.VCSecurityLevel) {
- h, vc := New(security)
+ h, vc := New(security, version.IPCVersion2)
defer h.Close()
flow, err := vc.Connect()
if err != nil {
@@ -149,13 +150,26 @@
func TestConnect(t *testing.T) { testConnect(t, SecurityNone) }
func TestConnectTLS(t *testing.T) { testConnect(t, SecurityTLS) }
+func testConnect_Version1(t *testing.T, security veyron2.VCSecurityLevel) {
+ h, vc := New(security, version.IPCVersion1)
+ defer h.Close()
+ flow, err := vc.Connect()
+ if err != nil {
+ t.Fatal(err)
+ }
+ testFlowEcho(t, flow, 10)
+}
+func TestConnect_Version1(t *testing.T) { testConnect_Version1(t, SecurityNone) }
+func TestConnect_Version1TLS(t *testing.T) { testConnect_Version1(t, SecurityTLS) }
+
+
// helper function for testing concurrent operations on multiple flows over the
// same VC. Such tests are most useful when running the race detector.
// (go test -race ...)
func testConcurrentFlows(t *testing.T, security veyron2.VCSecurityLevel, flows, gomaxprocs int) {
mp := runtime.GOMAXPROCS(gomaxprocs)
defer runtime.GOMAXPROCS(mp)
- h, vc := New(security)
+ h, vc := New(security, version.IPCVersion2)
defer h.Close()
var wg sync.WaitGroup
@@ -182,7 +196,7 @@
func testListen(t *testing.T, security veyron2.VCSecurityLevel) {
data := "the dark knight"
- h, vc := New(security)
+ h, vc := New(security, version.IPCVersion2)
defer h.Close()
if err := h.VC.AcceptFlow(id.Flow(21)); err == nil {
t.Errorf("Expected AcceptFlow on a new flow to fail as Listen was not called")
@@ -229,7 +243,7 @@
func TestListenTLS(t *testing.T) { testListen(t, SecurityTLS) }
func testNewFlowAfterClose(t *testing.T, security veyron2.VCSecurityLevel) {
- h, _ := New(security)
+ h, _ := New(security, version.IPCVersion2)
defer h.Close()
h.VC.Close("reason")
if err := h.VC.AcceptFlow(id.Flow(10)); err == nil {
@@ -240,7 +254,7 @@
func TestNewFlowAfterCloseTLS(t *testing.T) { testNewFlowAfterClose(t, SecurityTLS) }
func testConnectAfterClose(t *testing.T, security veyron2.VCSecurityLevel) {
- h, vc := New(security)
+ h, vc := New(security, version.IPCVersion2)
defer h.Close()
h.VC.Close("myerr")
if f, err := vc.Connect(); f != nil || err == nil || !strings.Contains(err.Error(), "myerr") {
@@ -262,7 +276,7 @@
// New creates both ends of a VC but returns only the "client" end (i.e., the
// one that initiated the VC). The "server" end (the one that "accepted" the VC)
// listens for flows and simply echoes data read.
-func New(security veyron2.VCSecurityLevel) (*helper, stream.VC) {
+func New(security veyron2.VCSecurityLevel, v version.IPCVersion) (*helper, stream.VC) {
clientH := &helper{bq: drrqueue.New(vc.MaxPayloadSizeBytes)}
serverH := &helper{bq: drrqueue.New(vc.MaxPayloadSizeBytes)}
clientH.otherEnd = serverH
@@ -280,6 +294,7 @@
RemoteEP: serverEP,
Pool: iobuf.NewPool(0),
Helper: clientH,
+ Version: v,
}
serverParams := vc.Params{
VCI: vci,
@@ -287,6 +302,7 @@
RemoteEP: clientEP,
Pool: iobuf.NewPool(0),
Helper: serverH,
+ Version: v,
}
clientH.VC = vc.InternalNew(clientParams)
diff --git a/runtimes/google/ipc/stream/vif/vif.go b/runtimes/google/ipc/stream/vif/vif.go
index 15ea933..2bfa7bb 100644
--- a/runtimes/google/ipc/stream/vif/vif.go
+++ b/runtimes/google/ipc/stream/vif/vif.go
@@ -590,9 +590,9 @@
}
func (vif *VIF) newVC(vci id.VC, localEP, remoteEP naming.Endpoint, dialed bool) (*vc.VC, error) {
- _, err := version.CommonVersion(localEP, remoteEP)
+ version, err := version.CommonVersion(localEP, remoteEP)
if vif.versions != nil {
- _, err = vif.versions.CommonVersion(localEP, remoteEP)
+ version, err = vif.versions.CommonVersion(localEP, remoteEP)
}
if err != nil {
return nil, err
@@ -605,6 +605,7 @@
Pool: vif.pool,
ReserveBytes: message.HeaderSizeBytes,
Helper: vcHelper{vif},
+ Version: version,
})
added, rq, wq := vif.vcMap.Insert(vc)
// Start vcWriteLoop
diff --git a/runtimes/google/ipc/version/version.go b/runtimes/google/ipc/version/version.go
index 5ad9aa8..a0fec38 100644
--- a/runtimes/google/ipc/version/version.go
+++ b/runtimes/google/ipc/version/version.go
@@ -21,7 +21,7 @@
// change that's not both forward and backward compatible.
// Min should be incremented whenever we want to remove
// support for old protocol versions.
- supportedRange = &Range{Min: version.IPCVersion1, Max: version.IPCVersion1}
+ supportedRange = &Range{Min: version.IPCVersion1, Max: version.IPCVersion2}
// Export the methods on supportedRange.
Endpoint = supportedRange.Endpoint
diff --git a/runtimes/google/security/identity_chain.go b/runtimes/google/security/identity_chain.go
index b6ad17b..2c3c4d8 100644
--- a/runtimes/google/security/identity_chain.go
+++ b/runtimes/google/security/identity_chain.go
@@ -122,8 +122,12 @@
func (id *chainPrivateID) PublicID() security.PublicID { return id.publicID }
-func (id *chainPrivateID) Sign(message []byte) (signature security.Signature, err error) {
- signature.R, signature.S, err = ecdsa.Sign(rand.Reader, id.privateKey, message)
+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
}
diff --git a/security/caveat/public_key_caveat.go b/security/caveat/public_key_caveat.go
index 0dcb615..975cbc4 100644
--- a/security/caveat/public_key_caveat.go
+++ b/security/caveat/public_key_caveat.go
@@ -2,12 +2,10 @@
import (
"bytes"
- "crypto/ecdsa"
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"fmt"
- "math/big"
"time"
"veyron2/security"
@@ -95,8 +93,7 @@
if err != nil {
return errPublicKeyCaveat(c, err)
}
- var r, s big.Int
- if !ecdsa.Verify(key, pkDischarge.contentHash(), r.SetBytes(pkDischarge.Signature.R), s.SetBytes(pkDischarge.Signature.S)) {
+ if !pkDischarge.Signature.Verify(key, pkDischarge.contentHash()) {
return errPublicKeyCaveat(c, fmt.Errorf("discharge %v has invalid signature", dis))
}
return nil
@@ -116,7 +113,7 @@
// Signature on the contents of the discharge obtained using the private key
// corresponding to the validaton key in the caveat.
- Signature wire.Signature
+ Signature security.Signature
}
// CaveatID returns a unique identity for the discharge based on the random nonce and
@@ -130,13 +127,9 @@
}
// sign uses the provided identity to sign the contents of the discharge.
-func (d *publicKeyDischarge) sign(discharger security.PrivateID) error {
- signature, err := discharger.Sign(d.contentHash())
- if err != nil {
- return err
- }
- d.Signature.Set(signature)
- return nil
+func (d *publicKeyDischarge) sign(discharger security.PrivateID) (err error) {
+ d.Signature, err = discharger.Sign(d.contentHash())
+ return
}
func (d *publicKeyDischarge) contentHash() []byte {
diff --git a/services/wsprd/security/identity.go b/services/wsprd/security/identity.go
index f72ad24..2315b32 100644
--- a/services/wsprd/security/identity.go
+++ b/services/wsprd/security/identity.go
@@ -13,7 +13,6 @@
package security
import (
- "crypto/ecdsa"
"crypto/sha256"
"io"
"sync"
@@ -116,7 +115,7 @@
return result, nil
}
- if !ecdsa.Verify(rt.Identity().PublicID().PublicKey(), signed, sig.R, sig.S) {
+ if !sig.Verify(rt.Identity().PublicID().PublicKey(), signed) {
return nil, verror.NotAuthorizedf("signature verification failed")
}