diff --git a/go.mod b/go.mod index 18f312d704c9b102549cae8c0170f91432e5de57..17661bbab92822b5ebb61e6cea8d77c0b11831b0 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 github.com/spf13/viper v1.7.1 gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228 - gitlab.com/elixxir/comms v0.0.4-0.20210921011907-2be8c9faa4d8 + gitlab.com/elixxir/comms v0.0.4-0.20210922201638-6f29a4b4f1e3 gitlab.com/elixxir/crypto v0.0.7-0.20210920180151-6c9b84bae372 gitlab.com/elixxir/ekv v0.1.5 gitlab.com/elixxir/primitives v0.0.3-0.20210920180121-b85bca5212f4 diff --git a/go.sum b/go.sum index 238ea4e16aaee9b1d3a428a4641f8f71aef19154..259d63fe7295960e4791ecc1277d3a615a81a106 100644 --- a/go.sum +++ b/go.sum @@ -255,6 +255,8 @@ gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228 h1:Gi6rj4mAlK0 gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k= gitlab.com/elixxir/comms v0.0.4-0.20210921011907-2be8c9faa4d8 h1:MqVGu0ADBHMw7rfiSJTG4PRlGk8c8N6GLY1CwJwC3Dk= gitlab.com/elixxir/comms v0.0.4-0.20210921011907-2be8c9faa4d8/go.mod h1:h41+FHc9zlQGveEao3aw8VSfzyOPecEhhUIadUsW1C8= +gitlab.com/elixxir/comms v0.0.4-0.20210922201638-6f29a4b4f1e3 h1:xm9szmYscDwLUtbDyIcDlToKb1c7MDCCxQ4HFZJVPCk= +gitlab.com/elixxir/comms v0.0.4-0.20210922201638-6f29a4b4f1e3/go.mod h1:h41+FHc9zlQGveEao3aw8VSfzyOPecEhhUIadUsW1C8= gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c= gitlab.com/elixxir/crypto v0.0.3/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA= gitlab.com/elixxir/crypto v0.0.7-0.20210920180151-6c9b84bae372 h1:W5Ax+cwqOOcsVegaMLvsFJ/Cs24a4Wyhp5UHFwvMQxo= diff --git a/registration/register.go b/registration/register.go index 5a21fd97267ab0ae4880ceb9a996ddbdfb09dbd8..9ec57b4de8f766a50e5c3ed24e58a387441d6f9a 100644 --- a/registration/register.go +++ b/registration/register.go @@ -8,42 +8,91 @@ package registration import ( + "github.com/golang/protobuf/proto" "github.com/pkg/errors" pb "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/elixxir/crypto/registration" "gitlab.com/xx_network/comms/connect" "gitlab.com/xx_network/crypto/signature/rsa" ) func (perm *Registration) Register(transmissionPublicKey, receptionPublicKey *rsa.PublicKey, - registrationCode string) ([]byte, []byte, int64, error) { + registrationCode string) (transmissionSig []byte, receptionSig []byte, regTimestamp int64, err error) { return register(perm.comms, perm.host, transmissionPublicKey, receptionPublicKey, registrationCode) } // client.Comms should implement this interface type registrationMessageSender interface { - SendRegistrationMessage(host *connect.Host, message *pb.UserRegistration) (*pb.UserRegistrationConfirmation, error) + SendRegistrationMessage(host *connect.Host, message *pb.ClientRegistration) (*pb.SignedClientRegistrationConfirmations, error) } //register registers the user with optional registration code // Returns an error if registration fails. func register(comms registrationMessageSender, host *connect.Host, - transmissionPublicKey, receptionPublicKey *rsa.PublicKey, registrationCode string) ([]byte, []byte, int64, error) { + transmissionPublicKey, receptionPublicKey *rsa.PublicKey, + registrationCode string) ( + transmissionSig []byte, receptionSig []byte, regTimestamp int64, err error) { + // Send the message + transmissionPem := string(rsa.CreatePublicKeyPem(transmissionPublicKey)) + receptionPem := string(rsa.CreatePublicKeyPem(receptionPublicKey)) response, err := comms. SendRegistrationMessage(host, - &pb.UserRegistration{ + &pb.ClientRegistration{ RegistrationCode: registrationCode, - ClientRSAPubKey: string(rsa.CreatePublicKeyPem(transmissionPublicKey)), - ClientReceptionRSAPubKey: string(rsa.CreatePublicKeyPem(receptionPublicKey)), + ClientTransmissionRSAPubKey: receptionPem, + ClientReceptionRSAPubKey: transmissionPem, }) if err != nil { - err = errors.Wrap(err, "sendRegistrationMessage: Unable to contact Identity Server!") + err = errors.Wrap(err, "sendRegistrationMessage: Unable to " + + "contact Identity Server!") return nil, nil, 0, err } if response.Error != "" { - return nil, nil, 0, errors.Errorf("sendRegistrationMessage: error handling message: %s", response.Error) + return nil, nil, 0, errors.Errorf("sendRegistrationMessage: " + + "error handling message: %s", response.Error) } - return response.ClientSignedByServer.Signature, - response.ClientReceptionSignedByServer.Signature, response.Timestamp, nil + + + // Unmarshal reception confirmation + receptionConfirmation := &pb.ClientRegistrationConfirmation{} + err = proto.Unmarshal(response.GetClientReceptionConfirmation(). + ClientRegistrationConfirmation, receptionConfirmation) + if err != nil { + return nil, nil, 0, errors.WithMessage(err, "Failed to unmarshal " + + "reception confirmation message") + } + + transmissionConfirmation := &pb.ClientRegistrationConfirmation{} + err = proto.Unmarshal(response.GetClientReceptionConfirmation(). + ClientRegistrationConfirmation, transmissionConfirmation) + if err != nil { + return nil, nil, 0, errors.WithMessage(err, "Failed to unmarshal " + + "transmission confirmation message") + } + + // Verify reception signature + receptionSignature := response.GetClientReceptionConfirmation(). + GetRegistrarSignature().Signature + err = registration.VerifyWithTimestamp(host.GetPubKey(), + receptionConfirmation.Timestamp, receptionPem, + receptionSignature) + if err != nil { + return nil, nil, 0, errors.WithMessage(err, "Failed to verify reception signature") + } + + // Verify transmission signature + transmissionSignature := response.GetClientTransmissionConfirmation(). + GetRegistrarSignature().Signature + err = registration.VerifyWithTimestamp(host.GetPubKey(), + transmissionConfirmation.Timestamp, transmissionPem, + transmissionSignature) + if err != nil { + return nil, nil, 0, errors.WithMessage(err, "Failed to verify transmission signature") + } + + return transmissionSignature, + receptionSignature, + receptionConfirmation.Timestamp, nil } diff --git a/registration/register_test.go b/registration/register_test.go index 9840187ce77010144c0c0ccf8674f62a2c766354..5c942ceb0797896976aae488bf5d944d3493389d 100644 --- a/registration/register_test.go +++ b/registration/register_test.go @@ -8,90 +8,163 @@ package registration import ( + "fmt" + "github.com/golang/protobuf/proto" "github.com/pkg/errors" pb "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/elixxir/comms/testkeys" + "gitlab.com/elixxir/crypto/registration" "gitlab.com/xx_network/comms/connect" "gitlab.com/xx_network/comms/messages" "gitlab.com/xx_network/crypto/csprng" "gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/primitives/id" - "reflect" + "gitlab.com/xx_network/primitives/utils" "testing" + "time" ) +func NewMockRegSender(key, cert []byte) (*MockRegistrationSender, error) { + privKey, err := rsa.LoadPrivateKeyFromPem(key) + if err != nil { + return nil, err + } + + // Generate a pre-canned time for consistent testing + testTime, err := time.Parse(time.RFC3339, + "2012-12-21T22:08:41+00:00") + if err != nil { + return nil, errors.Errorf("SignVerify error: "+ + "Could not parse precanned time: %v", err.Error()) + } + + h, err := connect.NewHost(&id.ClientRegistration, "address", cert, + connect.GetDefaultHostParams()) + if err != nil { + return nil, err + } + + return &MockRegistrationSender{ + privKey: privKey, + getHost: h, + prng: &CountingReader{count: 0}, + mockTS: testTime, + }, nil +} + type MockRegistrationSender struct { - reg *pb.UserRegistration + reg *pb.ClientRegistration // param passed to SendRegistrationMessage host *connect.Host + privKey *rsa.PrivateKey + prng *CountingReader + mockTS time.Time // original host returned from GetHost getHost *connect.Host - succeedGetHost bool errSendRegistration error errInReply string } -func (s *MockRegistrationSender) SendRegistrationMessage(host *connect.Host, message *pb.UserRegistration) (*pb.UserRegistrationConfirmation, error) { - s.reg = message - s.host = host - return &pb.UserRegistrationConfirmation{ - ClientSignedByServer: &messages.RSASignature{ - Nonce: []byte("nonce"), - Signature: []byte("sig"), +func (s *MockRegistrationSender) SendRegistrationMessage(host *connect.Host, message *pb.ClientRegistration) (*pb.SignedClientRegistrationConfirmations, error) { + fmt.Printf("mockTs: %v\n", s.mockTS.UnixNano()) + fmt.Printf("transKet: %v\n", message.ClientTransmissionRSAPubKey) + transSig, err := registration.SignWithTimestamp(s.prng, s.privKey, + s.mockTS.UnixNano(), message.ClientTransmissionRSAPubKey) + if err != nil { + return nil, errors.Errorf("Failed to sign transmission: %v", err) + } + + receptionSig, err := registration.SignWithTimestamp(s.prng, s.privKey, + s.mockTS.UnixNano(), message.ClientReceptionRSAPubKey) + if err != nil { + return nil, errors.Errorf("Failed to sign reception: %v", err) + } + + transConfirmation := &pb.ClientRegistrationConfirmation{ + Timestamp: s.mockTS.UnixNano(), + RSAPubKey: message.ClientTransmissionRSAPubKey, + } + + + receptionConfirmation := &pb.ClientRegistrationConfirmation{ + Timestamp: s.mockTS.UnixNano(), + RSAPubKey: message.ClientReceptionRSAPubKey, + } + + transConfirmationData, err := proto.Marshal(transConfirmation) + if err != nil { + return nil, err + } + + receptionConfirmationData, err := proto.Marshal(receptionConfirmation) + if err != nil { + return nil, err + } + + return &pb.SignedClientRegistrationConfirmations{ + ClientTransmissionConfirmation: &pb.SignedRegistrationConfirmation{ + RegistrarSignature: &messages.RSASignature{ + Signature: transSig, + }, + ClientRegistrationConfirmation: transConfirmationData, }, - ClientReceptionSignedByServer: &messages.RSASignature{ - Nonce: []byte("receptionnonce"), - Signature: []byte("receptionsig"), + ClientReceptionConfirmation: &pb.SignedRegistrationConfirmation{ + RegistrarSignature: &messages.RSASignature{ + Signature: receptionSig, + }, + ClientRegistrationConfirmation: receptionConfirmationData, }, Error: s.errInReply, }, s.errSendRegistration } func (s *MockRegistrationSender) GetHost(*id.ID) (*connect.Host, bool) { - return s.getHost, s.succeedGetHost + return s.getHost, true } // Shows that we get expected result from happy path // Shows that registration gets RPCs with the correct parameters func TestRegisterWithPermissioning(t *testing.T) { - rng := csprng.NewSystemRNG() - key, err := rsa.GenerateKey(rng, 256) + + + certData, err := utils.ReadFile(testkeys.GetNodeCertPath()) if err != nil { - t.Fatal(err) + t.Fatalf("Could not load certificate: %v", err) } - var sender MockRegistrationSender - sender.succeedGetHost = true - sender.getHost, err = connect.NewHost(&id.Permissioning, "address", nil, - connect.GetDefaultHostParams()) + keyData, err := utils.ReadFile(testkeys.GetNodeKeyPath()) if err != nil { - t.Fatal(err) + t.Fatalf("Could not load private key: %v", err) } - regCode := "flooble doodle" - sig1, sig2, _, err := register(&sender, sender.getHost, key.GetPublic(), key.GetPublic(), regCode) + key, err := rsa.LoadPrivateKeyFromPem(keyData) if err != nil { - t.Error(err) + t.Fatalf("Could not load public key") } - if string(sig1) != "sig" { - t.Error("expected signature to be 'sig'") - } - if string(sig2) != "receptionsig" { - t.Error("expected signature to be 'receptionsig'") - } - if sender.host.String() != sender.getHost.String() { - t.Errorf("hosts differed. expected %v, got %v", sender.host, sender.getHost) + + sender, err := NewMockRegSender(keyData, certData) + if err != nil { + t.Fatalf("Failed to create mock sender: %v", err) } - passedPub, err := rsa.LoadPublicKeyFromPem([]byte(sender.reg.ClientRSAPubKey)) + + + regCode := "flooble doodle" + sig1, sig2, regTimestamp, err := register(sender, sender.getHost, key.GetPublic(), key.GetPublic(), regCode) if err != nil { - t.Error("failed to decode passed public key") t.Error(err) } - if !reflect.DeepEqual(passedPub, key.GetPublic()) { - t.Error("public keys different from expected") - } - if sender.reg.RegistrationCode != regCode { - t.Error("passed regcode different from expected") + + if regTimestamp != sender.mockTS.UnixNano() { + t.Fatalf("Unexpected timestamp returned from register: " + + "\n\tExpected: %v" + + "\n\tReceived: %v", sender.mockTS.UnixNano(), regTimestamp) } + + // todo compare sigs + t.Logf("sig1: %v", sig1) + t.Logf("sig2: %v", sig2) + + } // Shows that returning an error from the registration server results in an @@ -103,7 +176,6 @@ func TestRegisterWithPermissioning_ResponseErr(t *testing.T) { t.Fatal(err) } var sender MockRegistrationSender - sender.succeedGetHost = true sender.errInReply = "failure occurred on registration" _, _, _, err = register(&sender, nil, key.GetPublic(), key.GetPublic(), "") if err == nil { @@ -120,10 +192,22 @@ func TestRegisterWithPermissioning_ConnectionErr(t *testing.T) { t.Fatal(err) } var sender MockRegistrationSender - sender.succeedGetHost = true sender.errSendRegistration = errors.New("connection problem") _, _, _, err = register(&sender, nil, key.GetPublic(), key.GetPublic(), "") if err == nil { t.Error("no error if e.g. context deadline exceeded") } } + +type CountingReader struct { + count uint8 +} + +// Read just counts until 254 then starts over again +func (c *CountingReader) Read(b []byte) (int, error) { + for i := 0; i < len(b); i++ { + c.count = (c.count + 1) % 255 + b[i] = c.count + } + return len(b), nil +}