From 9e222393c642ab9d2987719dd4286862cb30647f Mon Sep 17 00:00:00 2001 From: Darnell Andries Date: Fri, 29 Nov 2024 18:47:21 -0800 Subject: [PATCH] Allow specifying OPRF client seed --- examples_test.go | 15 ++++---- server.go | 79 +++++++++++++++++++++++++++++++------------ tests/client_test.go | 24 +++++++------ tests/helper_test.go | 15 ++++++-- tests/opaque_test.go | 10 +++--- tests/server_test.go | 27 +++++++-------- tests/vectors_test.go | 13 ++++--- 7 files changed, 119 insertions(+), 64 deletions(-) diff --git a/examples_test.go b/examples_test.go index a6da1b8..bbfe26c 100644 --- a/examples_test.go +++ b/examples_test.go @@ -106,7 +106,7 @@ func Example_serverSetup() { log.Fatalln(err) } - if err := server.SetKeyMaterial(serverID, serverPrivateKey, serverPublicKey, secretOprfSeed); err != nil { + if err := server.SetKeyMaterial(serverID, serverPrivateKey, serverPublicKey, secretOprfSeed, nil); err != nil { log.Fatalln(err) } @@ -232,13 +232,16 @@ func Example_registration() { // The server creates a database entry for the client and creates a credential identifier that must absolutely // be unique among all clients. credID = opaque.RandomBytes(64) - pks, err := server.Deserialize.DecodeAkePublicKey(serverPublicKey) - if err != nil { + + if err = server.SetKeyMaterial(serverID, serverPrivateKey, serverPublicKey, secretOprfSeed, nil); err != nil { log.Fatalln(err) } // The server uses its public key and secret OPRF seed created at the setup. - response := server.RegistrationResponse(request, pks, credID, secretOprfSeed) + response, err := server.RegistrationResponse(request, credID) + if err != nil { + log.Fatalln(err) + } // The server responds with its serialized response. message2 = response.Serialize() @@ -308,7 +311,7 @@ func Example_loginKeyExchange() { log.Fatalln(err) } - if err := server.SetKeyMaterial(serverID, serverPrivateKey, serverPublicKey, secretOprfSeed); err != nil { + if err := server.SetKeyMaterial(serverID, serverPrivateKey, serverPublicKey, secretOprfSeed, nil); err != nil { log.Fatalln(err) } @@ -424,7 +427,7 @@ func Example_fakeResponse() { log.Fatalln(err) } - if err := server.SetKeyMaterial(serverID, serverPrivateKey, serverPublicKey, secretOprfSeed); err != nil { + if err := server.SetKeyMaterial(serverID, serverPrivateKey, serverPublicKey, secretOprfSeed, nil); err != nil { log.Fatalln(err) } diff --git a/server.go b/server.go index 421e36f..3ab7e58 100644 --- a/server.go +++ b/server.go @@ -58,6 +58,11 @@ type keyMaterial struct { serverSecretKey *ecc.Scalar serverPublicKey []byte oprfSeed []byte + oprfClientSeed []byte +} + +type OPRFSeedOptions struct { + OPRFSeed []byte } // NewServer returns a Server instantiation given the application Configuration. @@ -84,49 +89,67 @@ func (s *Server) GetConf() *internal.Configuration { return s.conf } -func (s *Server) oprfResponse(element *ecc.Element, oprfSeed, credentialIdentifier []byte) *ecc.Element { - seed := s.conf.KDF.Expand( - oprfSeed, - encoding.SuffixString(credentialIdentifier, tag.ExpandOPRF), - internal.SeedLength, - ) +func (s *Server) oprfResponse(element *ecc.Element, credentialIdentifier []byte) (*ecc.Element, error) { + if s.keyMaterial == nil { + return nil, fmt.Errorf("key material must be specified") + } + seed := s.keyMaterial.oprfClientSeed + if seed == nil { + if s.keyMaterial.oprfSeed == nil { + return nil, fmt.Errorf("OPRF seed must be specified") + } + seed = s.conf.KDF.Expand( + s.keyMaterial.oprfSeed, + encoding.SuffixString(credentialIdentifier, tag.ExpandOPRF), + internal.SeedLength, + ) + } ku := s.conf.OPRF.DeriveKey(seed, []byte(tag.DeriveKeyPair)) - return s.conf.OPRF.Evaluate(ku, element) + return s.conf.OPRF.Evaluate(ku, element), nil } // RegistrationResponse returns a RegistrationResponse message to the input RegistrationRequest message and given // identifiers. func (s *Server) RegistrationResponse( req *message.RegistrationRequest, - serverPublicKey *ecc.Element, - credentialIdentifier, oprfSeed []byte, -) *message.RegistrationResponse { - z := s.oprfResponse(req.BlindedMessage, oprfSeed, credentialIdentifier) + credentialIdentifier []byte, +) (*message.RegistrationResponse, error) { + z, err := s.oprfResponse(req.BlindedMessage, credentialIdentifier) + if err != nil { + return nil, err + } + + serverPublicKey := s.conf.Group.NewElement() + if err := serverPublicKey.Decode(s.keyMaterial.serverPublicKey); err != nil { + return nil, fmt.Errorf("invalid server public key: %w", err) + } return &message.RegistrationResponse{ EvaluatedMessage: z, Pks: serverPublicKey, - } + }, nil } func (s *Server) credentialResponse( req *message.CredentialRequest, - serverPublicKey []byte, record *message.RegistrationRecord, - credentialIdentifier, oprfSeed, maskingNonce []byte, -) *message.CredentialResponse { - z := s.oprfResponse(req.BlindedMessage, oprfSeed, credentialIdentifier) + credentialIdentifier, maskingNonce []byte, +) (*message.CredentialResponse, error) { + z, err := s.oprfResponse(req.BlindedMessage, credentialIdentifier) + if err != nil { + return nil, err + } maskingNonce, maskedResponse := masking.Mask( s.conf, maskingNonce, record.MaskingKey, - serverPublicKey, + s.keyMaterial.serverPublicKey, record.Envelope, ) - return message.NewCredentialResponse(z, maskingNonce, maskedResponse) + return message.NewCredentialResponse(z, maskingNonce, maskedResponse), nil } // GenerateKE2Options enable setting optional values for the session, which default to secure random values if not @@ -166,7 +189,7 @@ func getGenerateKE2Options(options []GenerateKE2Options) (*ake.Options, []byte) // - serverSecretKey is the server's secret AKE key. // - serverPublicKey is the server's public AKE key to the serverSecretKey. // - oprfSeed is the long-term OPRF input seed. -func (s *Server) SetKeyMaterial(serverIdentity, serverSecretKey, serverPublicKey, oprfSeed []byte) error { +func (s *Server) SetKeyMaterial(serverIdentity, serverSecretKey, serverPublicKey, oprfSeed []byte, oprfClientSeed []byte) error { sks := s.conf.Group.NewScalar() if err := sks.Decode(serverSecretKey); err != nil { return fmt.Errorf("invalid server AKE secret key: %w", err) @@ -176,7 +199,15 @@ func (s *Server) SetKeyMaterial(serverIdentity, serverSecretKey, serverPublicKey return ErrZeroSKS } - if len(oprfSeed) != s.conf.Hash.Size() { + if oprfSeed != nil { + if len(oprfSeed) != s.conf.Hash.Size() { + return ErrInvalidOPRFSeedLength + } + } else if oprfClientSeed != nil { + if len(oprfClientSeed) != internal.SeedLength { + return ErrInvalidOPRFSeedLength + } + } else { return ErrInvalidOPRFSeedLength } @@ -193,6 +224,7 @@ func (s *Server) SetKeyMaterial(serverIdentity, serverSecretKey, serverPublicKey serverSecretKey: sks, serverPublicKey: serverPublicKey, oprfSeed: oprfSeed, + oprfClientSeed: oprfClientSeed, } return nil @@ -217,8 +249,11 @@ func (s *Server) GenerateKE2( op, maskingNonce := getGenerateKE2Options(options) - response := s.credentialResponse(ke1.CredentialRequest, s.serverPublicKey, - record.RegistrationRecord, record.CredentialIdentifier, s.oprfSeed, maskingNonce) + response, err := s.credentialResponse(ke1.CredentialRequest, + record.RegistrationRecord, record.CredentialIdentifier, maskingNonce) + if err != nil { + return nil, err + } identities := ake.Identities{ ClientIdentity: record.ClientIdentity, diff --git a/tests/client_test.go b/tests/client_test.go index ad58b29..e6ce52e 100644 --- a/tests/client_test.go +++ b/tests/client_test.go @@ -44,7 +44,7 @@ func TestClientRegistrationFinalize_InvalidPks(t *testing.T) { t.Fatal(err) } - _, pks := conf.conf.KeyGen() + sks, pks := conf.conf.KeyGen() oprfSeed := internal.RandomBytes(conf.conf.Hash.Size()) r1 := client.RegistrationInit([]byte("yo")) @@ -52,7 +52,11 @@ func TestClientRegistrationFinalize_InvalidPks(t *testing.T) { if err = pk.Decode(pks); err != nil { panic(err) } - r2 := server.RegistrationResponse(r1, pk, credID, oprfSeed) + server.SetKeyMaterial([]byte("server"), sks, pks, oprfSeed, nil) + r2, err := server.RegistrationResponse(r1, credID) + if err != nil { + t.Fatalf("failed to generate registration response: %v", err) + } // message length badr2 := internal.RandomBytes(15) @@ -131,11 +135,11 @@ func TestClientFinish_BadMaskedResponse(t *testing.T) { sks, pks := conf.conf.KeyGen() oprfSeed := internal.RandomBytes(conf.conf.Hash.Size()) - if err = server.SetKeyMaterial(nil, sks, pks, oprfSeed); err != nil { + if err := server.SetKeyMaterial(nil, sks, pks, oprfSeed, nil); err != nil { t.Fatal(err) } - rec := buildRecord(credID, oprfSeed, []byte("yo"), pks, client, server) + rec := buildRecord(credID, oprfSeed, []byte("yo"), sks, pks, client, server, false) ke1 := client.GenerateKE1([]byte("yo")) ke2, _ := server.GenerateKE2(ke1, rec) @@ -177,11 +181,11 @@ func TestClientFinish_InvalidEnvelopeTag(t *testing.T) { sks, pks := conf.conf.KeyGen() oprfSeed := internal.RandomBytes(conf.conf.Hash.Size()) - if err = server.SetKeyMaterial(nil, sks, pks, oprfSeed); err != nil { + if err := server.SetKeyMaterial(nil, sks, pks, oprfSeed, nil); err != nil { t.Fatal(err) } - rec := buildRecord(credID, oprfSeed, []byte("yo"), pks, client, server) + rec := buildRecord(credID, oprfSeed, []byte("yo"), sks, pks, client, server, false) ke1 := client.GenerateKE1([]byte("yo")) ke2, _ := server.GenerateKE2(ke1, rec) @@ -235,11 +239,11 @@ func TestClientFinish_InvalidKE2KeyEncoding(t *testing.T) { sks, pks := conf.conf.KeyGen() oprfSeed := internal.RandomBytes(conf.conf.Hash.Size()) - if err := server.SetKeyMaterial(nil, sks, pks, oprfSeed); err != nil { + if err := server.SetKeyMaterial(nil, sks, pks, oprfSeed, nil); err != nil { t.Fatal(err) } - rec := buildRecord(credID, oprfSeed, []byte("yo"), pks, client, server) + rec := buildRecord(credID, oprfSeed, []byte("yo"), sks, pks, client, server, false) ke1 := client.GenerateKE1([]byte("yo")) ke2, _ := server.GenerateKE2(ke1, rec) @@ -318,11 +322,11 @@ func TestClientFinish_InvalidKE2Mac(t *testing.T) { sks, pks := conf.conf.KeyGen() oprfSeed := internal.RandomBytes(conf.conf.Hash.Size()) - if err := server.SetKeyMaterial(nil, sks, pks, oprfSeed); err != nil { + if err := server.SetKeyMaterial(nil, sks, pks, oprfSeed, nil); err != nil { log.Fatal(err) } - rec := buildRecord(credID, oprfSeed, []byte("yo"), pks, client, server) + rec := buildRecord(credID, oprfSeed, []byte("yo"), sks, pks, client, server, false) ke1 := client.GenerateKE1([]byte("yo")) ke2, _ := server.GenerateKE2(ke1, rec) diff --git a/tests/helper_test.go b/tests/helper_test.go index a0b9453..fbddfb0 100644 --- a/tests/helper_test.go +++ b/tests/helper_test.go @@ -167,9 +167,10 @@ func getBadScalar(t *testing.T, c *configuration) []byte { } func buildRecord( - credID, oprfSeed, password, pks []byte, + credID, oprfSeed, password, sks, pks []byte, client *opaque.Client, server *opaque.Server, + noServerID bool, ) *opaque.ClientRecord { conf := server.GetConf() r1 := client.RegistrationInit(password) @@ -178,8 +179,18 @@ func buildRecord( if err := pk.Decode(pks); err != nil { panic(err) } + var serverID []byte + if !noServerID { + serverID = []byte("server") + } - r2 := server.RegistrationResponse(r1, pk, credID, oprfSeed) + if err := server.SetKeyMaterial(serverID, sks, pks, oprfSeed, nil); err != nil { + panic(err) + } + r2, err := server.RegistrationResponse(r1, credID) + if err != nil { + panic(err) + } r3, _ := client.RegistrationFinalize(r2) return &opaque.ClientRecord{ diff --git a/tests/opaque_test.go b/tests/opaque_test.go index 24b48fb..6d77265 100644 --- a/tests/opaque_test.go +++ b/tests/opaque_test.go @@ -96,13 +96,15 @@ func testRegistration(t *testing.T, p *testParams) (*opaque.Client, *opaque.Serv } credID = internal.RandomBytes(32) - pks, err := server.Deserialize.DecodeAkePublicKey(p.serverPublicKey) + + if err := server.SetKeyMaterial(p.serverID, p.serverSecretKey, p.serverPublicKey, p.oprfSeed, nil); err != nil { + t.Fatalf(dbgErr, err) + } + respReg, err := server.RegistrationResponse(m1, credID) if err != nil { t.Fatalf(dbgErr, err) } - respReg := server.RegistrationResponse(m1, pks, credID, p.oprfSeed) - m2s = respReg.Serialize() } @@ -163,7 +165,7 @@ func testAuthentication( var state []byte server, _ := p.Server() { - if err := server.SetKeyMaterial(p.serverID, p.serverSecretKey, p.serverPublicKey, p.oprfSeed); err != nil { + if err := server.SetKeyMaterial(p.serverID, p.serverSecretKey, p.serverPublicKey, p.oprfSeed, nil); err != nil { t.Fatal(err) } diff --git a/tests/server_test.go b/tests/server_test.go index 7ad6b7b..7c4253a 100644 --- a/tests/server_test.go +++ b/tests/server_test.go @@ -66,13 +66,13 @@ func TestServerInit_InvalidPublicKey(t *testing.T) { oprfSeed := internal.RandomBytes(conf.conf.Hash.Size()) expected := "input server public key's length is invalid" - if err := server.SetKeyMaterial(nil, sk, nil, oprfSeed); err == nil || + if err := server.SetKeyMaterial(nil, sk, nil, oprfSeed, nil); err == nil || !strings.HasPrefix(err.Error(), expected) { t.Fatalf("expected error on nil pubkey - got %s", err) } expected = "invalid server public key: " - if err := server.SetKeyMaterial(nil, sk, getBadElement(t, conf), oprfSeed); err == nil || + if err := server.SetKeyMaterial(nil, sk, getBadElement(t, conf), oprfSeed, nil); err == nil || !strings.HasPrefix(err.Error(), expected) { t.Fatalf("expected error on bad secret key - got %s", err) } @@ -91,17 +91,17 @@ func TestServerInit_InvalidOPRFSeedLength(t *testing.T) { sk, pk := conf.conf.KeyGen() expected := opaque.ErrInvalidOPRFSeedLength - if err := server.SetKeyMaterial(nil, sk, pk, nil); err == nil || !errors.Is(err, expected) { + if err := server.SetKeyMaterial(nil, sk, pk, nil, nil); err == nil || !errors.Is(err, expected) { t.Fatalf("expected error on nil seed - got %s", err) } seed := internal.RandomBytes(conf.conf.Hash.Size() - 1) - if err := server.SetKeyMaterial(nil, sk, pk, seed); err == nil || !errors.Is(err, expected) { + if err := server.SetKeyMaterial(nil, sk, pk, seed, nil); err == nil || !errors.Is(err, expected) { t.Fatalf("expected error on bad seed - got %s", err) } seed = internal.RandomBytes(conf.conf.Hash.Size() + 1) - if err := server.SetKeyMaterial(nil, sk, pk, seed); err == nil || !errors.Is(err, expected) { + if err := server.SetKeyMaterial(nil, sk, pk, seed, nil); err == nil || !errors.Is(err, expected) { t.Fatalf("expected error on bad seed - got %s", err) } }) @@ -119,7 +119,7 @@ func TestServerInit_NilSecretKey(t *testing.T) { _, pk := conf.conf.KeyGen() expected := "invalid server AKE secret key: " - if err := server.SetKeyMaterial(nil, nil, pk, nil); err == nil || + if err := server.SetKeyMaterial(nil, nil, pk, nil, nil); err == nil || !strings.HasPrefix(err.Error(), expected) { t.Fatalf("expected error on nil secret key - got %s", err) } @@ -146,7 +146,7 @@ func TestServerInit_ZeroSecretKey(t *testing.T) { expected = "invalid server AKE secret key: scalar Decode: invalid scalar length" } - if err := server.SetKeyMaterial(nil, sk[:], nil, nil); err == nil || + if err := server.SetKeyMaterial(nil, sk[:], nil, nil, nil); err == nil || !strings.HasPrefix(err.Error(), expected) { t.Fatalf("expected error on nil secret key - got %s", err) } @@ -183,7 +183,7 @@ func TestServerInit_InvalidEnvelope(t *testing.T) { sk, pk := conf.conf.KeyGen() oprfSeed := internal.RandomBytes(conf.conf.Hash.Size()) - if err := server.SetKeyMaterial(nil, sk, pk, oprfSeed); err != nil { + if err := server.SetKeyMaterial(nil, sk, pk, oprfSeed, nil); err != nil { t.Fatal(err) } @@ -191,7 +191,7 @@ func TestServerInit_InvalidEnvelope(t *testing.T) { if err != nil { t.Fatal(err) } - rec := buildRecord(internal.RandomBytes(32), oprfSeed, []byte("yo"), pk, client, server) + rec := buildRecord(internal.RandomBytes(32), oprfSeed, []byte("yo"), sk, pk, client, server, false) rec.Envelope = internal.RandomBytes(15) expected := "record has invalid envelope length" @@ -259,10 +259,7 @@ func TestServerFinish_InvalidKE3Mac(t *testing.T) { client, _ := conf.Client() server, _ := conf.Server() sk, pk := conf.KeyGen() - if err := server.SetKeyMaterial(nil, sk, pk, oprfSeed); err != nil { - t.Fatal(err) - } - rec := buildRecord(credId, oprfSeed, password, pk, client, server) + rec := buildRecord(credId, oprfSeed, password, sk, pk, client, server, true) ke1 := client.GenerateKE1(password) ke2, err := server.GenerateKE2(ke1, rec) if err != nil { @@ -303,9 +300,9 @@ func TestServerSetAKEState_InvalidInput(t *testing.T) { client, _ := conf.Client() server, _ = conf.Server() sk, pk := conf.KeyGen() - rec := buildRecord(credId, seed, password, pk, client, server) + rec := buildRecord(credId, seed, password, sk, pk, client, server, false) ke1 := client.GenerateKE1(password) - _ = server.SetKeyMaterial(nil, sk, pk, seed) + _ = server.SetKeyMaterial(nil, sk, pk, seed, nil) _, _ = server.GenerateKE2(ke1, rec) state := server.SerializeState() if err := server.SetAKEState(state); err == nil || err.Error() != errStateExists.Error() { diff --git a/tests/vectors_test.go b/tests/vectors_test.go index 26f6a38..5512de3 100644 --- a/tests/vectors_test.go +++ b/tests/vectors_test.go @@ -135,13 +135,15 @@ func (v *vector) testRegistration(conf *opaque.Configuration, t *testing.T) { // Server server, _ := conf.Server() - pks, err := server.Deserialize.DecodeAkePublicKey(v.Inputs.ServerPublicKey) + + if err := server.SetKeyMaterial(v.Inputs.ServerIdentity, v.Inputs.ServerPrivateKey, v.Inputs.ServerPublicKey, v.Inputs.OprfSeed, nil); err != nil { + t.Fatal(err) + } + regResp, err := server.RegistrationResponse(regReq, v.Inputs.CredentialIdentifier) if err != nil { - panic(err) + t.Fatal(err) } - regResp := server.RegistrationResponse(regReq, pks, v.Inputs.CredentialIdentifier, v.Inputs.OprfSeed) - vRegResp, err := client.Deserialize.RegistrationResponse(v.Outputs.RegistrationResponse) if err != nil { t.Fatal(err) @@ -332,7 +334,8 @@ func (v *vector) loginResponse(t *testing.T, s *opaque.Server, record *opaque.Cl v.Inputs.ServerIdentity, v.Inputs.ServerPrivateKey, v.Inputs.ServerPublicKey, - v.Inputs.OprfSeed); err != nil { + v.Inputs.OprfSeed, + nil); err != nil { t.Fatal(err) }