diff --git a/.github/.golangci.yml b/.github/.golangci.yml index 8e04ac5..f3c0a5f 100644 --- a/.github/.golangci.yml +++ b/.github/.golangci.yml @@ -9,7 +9,7 @@ linters: - contextcheck - cyclop - decorder - - depguard + #- depguard - dogsled - dupl - durationcheck @@ -96,6 +96,11 @@ linters: fast: false linters-settings: + cyclop: + # The maximal code complexity to report. + # Default: 10 + max-complexity: 12 + skip-tests: true dupl: threshold: 100 errcheck: diff --git a/.github/Makefile b/.github/Makefile index ecffeda..16212c1 100644 --- a/.github/Makefile +++ b/.github/Makefile @@ -12,6 +12,7 @@ update-linters: @go install mvdan.cc/gofumpt@latest @go install github.com/daixiang0/gci@latest @go install github.com/segmentio/golines@latest + @go install golang.org/x/tools/cmd/goimports@latest @go install golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@latest @curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin diff --git a/.github/licence-header.tmpl b/.github/licence-header.tmpl index 8eca910..4d3a906 100644 --- a/.github/licence-header.tmpl +++ b/.github/licence-header.tmpl @@ -1,6 +1,6 @@ SPDX-License-Identifier: MIT -Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree or at diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8fb3ee4..cf73504 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: # Linting - name: Linting - uses: golangci/golangci-lint-action@5c56cd6c9dc07901af25baab6f2b0d9f3b7c3018 # pin@master + uses: golangci/golangci-lint-action@2e6adf08a9d304f4ac29fc9cb609dcd6a6e05c40 # pin@master with: version: latest args: --config=./.github/.golangci.yml ./... @@ -35,7 +35,7 @@ jobs: strategy: fail-fast: false matrix: - go: [ '1.20', '1.19' ] + go: [ '1.21', '1.20' ] steps: - name: Checkout repo uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # pin@master @@ -69,7 +69,7 @@ jobs: # Codecov - name: Codecov - uses: codecov/codecov-action@29386c70ef20e286228c72b668a06fd0e8399192 # pin@master + uses: codecov/codecov-action@bbeaa140357942e4e8d8e15f1cd2f4e612f64c59 # pin@master with: file: .github/coverage.out diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2d8682f..d811729 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -21,6 +21,8 @@ jobs: strategy: fail-fast: false + matrix: + go: [ '1.21', '1.20' ] steps: - name: Checkout repository @@ -28,12 +30,12 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@231aa2c8a89117b126725a0e11897209b7118144 # pin@master + uses: github/codeql-action/init@6f5223db54afb6bccb7b88162896588caa1f611b # pin@master with: languages: go - name: Autobuild - uses: github/codeql-action/autobuild@231aa2c8a89117b126725a0e11897209b7118144 # pin@master + uses: github/codeql-action/autobuild@6f5223db54afb6bccb7b88162896588caa1f611b # pin@master - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@231aa2c8a89117b126725a0e11897209b7118144 # pin@master + uses: github/codeql-action/analyze@6f5223db54afb6bccb7b88162896588caa1f611b # pin@master diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 458727d..287d3c4 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -24,7 +24,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@99c53751e09b9529366343771cc321ec74e9bd3d # pin@master + uses: ossf/scorecard-action@342acf350cd07e41a36da9be2b107614dd1661a3 # pin@master with: results_file: results.sarif results_format: sarif @@ -50,6 +50,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@807578363a7869ca324a79039e6db9c843e0e100 # pin@master + uses: github/codeql-action/upload-sarif@6f5223db54afb6bccb7b88162896588caa1f611b # pin@master with: sarif_file: results.sarif diff --git a/client.go b/client.go index 01353cb..8963d45 100644 --- a/client.go +++ b/client.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // -// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree or at diff --git a/client_state.go b/client_state.go index f0e8e0b..e28adfd 100644 --- a/client_state.go +++ b/client_state.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // -// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree or at @@ -16,13 +16,13 @@ import ( // State represents a client's state, allowing internal values to be exported and imported to resume a session. type State struct { - Identifier Identifier `json:"s"` - TweakedKey []byte `json:"t,omitempty"` - ServerPublicKey []byte `json:"p,omitempty"` - Input [][]byte `json:"i"` - Blind [][]byte `json:"r"` - Blinded [][]byte `json:"d"` - Mode Mode `json:"m"` + Identifier Ciphersuite `json:"s"` + TweakedKey []byte `json:"t,omitempty"` + ServerPublicKey []byte `json:"p,omitempty"` + Input [][]byte `json:"i"` + Blind [][]byte `json:"r"` + Blinded [][]byte `json:"d"` + Mode Mode `json:"m"` } // Export extracts the client's internal values that can be imported in another client for session resumption. diff --git a/doc.go b/doc.go index 04a1901..e0d24d0 100644 --- a/doc.go +++ b/doc.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // -// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree or at diff --git a/errors.go b/errors.go index ffd47b0..ad506ba 100644 --- a/errors.go +++ b/errors.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // -// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree or at @@ -12,7 +12,7 @@ import "errors" var ( errParamInvalidMode = errors.New("invalid OPRF mode") - errParamInvalidID = errors.New("invalid Identifier") + errParamInvalidID = errors.New("invalid Ciphersuite") errParamFinalizeLen = errors.New("invalid number of elements in evaluation") errParamInputEqualLen = errors.New("input lengths are not equal") errParamNoPubKey = errors.New("missing public key") diff --git a/evaluation.go b/evaluation.go index 23df90c..1cf8329 100644 --- a/evaluation.go +++ b/evaluation.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // -// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree or at diff --git a/examples_test.go b/examples_test.go index 904c0d9..062dde1 100644 --- a/examples_test.go +++ b/examples_test.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // -// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree or at @@ -41,7 +41,7 @@ func exchangeWithServer(blinded []byte, verifiable bool) []byte { return ev } -func ExampleClient() { +func Example_client() { input := []byte("input") // Set up a new client. Not indicating a server public key indicates we don't use the verifiable mode. @@ -102,7 +102,7 @@ func Example_verifiableClient() { // Output: } -func Example_baseServer() { +func Example_server() { // We suppose the client sends this blinded element. blinded, _ := hex.DecodeString("7eaf3d7cbe43d54637274342ce53578b2aba836f297f4f07997a6e1dced1c058") diff --git a/go.mod b/go.mod index 0819840..d2ad548 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,18 @@ module github.com/bytemare/voprf -go 1.20 +go 1.21 require ( - github.com/bytemare/crypto v0.4.4 - github.com/bytemare/hash v0.1.5 - github.com/google/go-cmp v0.5.9 + github.com/bytemare/crypto v0.5.2 + github.com/bytemare/hash v0.1.7 ) require ( - filippo.io/edwards25519 v1.0.0 // indirect - filippo.io/nistec v0.0.2 // indirect - github.com/bytemare/hash2curve v0.1.3 // indirect + filippo.io/edwards25519 v1.1.0 // indirect + filippo.io/nistec v0.0.3 // indirect + github.com/bytemare/hash2curve v0.2.3 // indirect + github.com/bytemare/secp256k1 v0.1.0 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect - golang.org/x/crypto v0.7.0 // indirect - golang.org/x/sys v0.6.0 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/sys v0.15.0 // indirect ) diff --git a/go.sum b/go.sum index 10f14fd..5075233 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,18 @@ -filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= -filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -filippo.io/nistec v0.0.2 h1:/NIXTUimcHIh0E2DsYucHlICvUisgj28/XEnKSEptUs= -filippo.io/nistec v0.0.2/go.mod h1:84fxC9mi+MhC2AERXI4LSa8cmSVOzrFikg6hZ4IfCyw= -github.com/bytemare/crypto v0.4.4 h1:BR4j35IRJIdDQa0dyqo4OS5OZ+mLlijhUxwWvgcipG4= -github.com/bytemare/crypto v0.4.4/go.mod h1:UA6K3SBPZ0C2VHQXc/9LT93rWTBwXxXNZFNL4uwapPo= -github.com/bytemare/hash v0.1.5 h1:VW+X1YQ2b3chjRFHkRUnO42uclsQjXimdBCPOgIobR4= -github.com/bytemare/hash v0.1.5/go.mod h1:+QmWXTky/2b63ngqM5IYezGydn9UTFDhpX7mLYwYxCA= -github.com/bytemare/hash2curve v0.1.3 h1:BOqV8BF5dF+BbPZgIyoeAVTwd4m7jmw4LwacD1GFBvU= -github.com/bytemare/hash2curve v0.1.3/go.mod h1:Wma3DmJdn8kqiK9j120hkWvC3tQVKS1PyA8ZzyG23BI= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +filippo.io/nistec v0.0.3 h1:h336Je2jRDZdBCLy2fLDUd9E2unG32JLwcJi0JQE9Cw= +filippo.io/nistec v0.0.3/go.mod h1:84fxC9mi+MhC2AERXI4LSa8cmSVOzrFikg6hZ4IfCyw= +github.com/bytemare/crypto v0.5.2 h1:ogvfY5mmtrPc5Uhwq4mUEUDnTVig+UEF8gwnNAPaNbU= +github.com/bytemare/crypto v0.5.2/go.mod h1:kkx4ciRQFWcjMauezZo9SHw4YmqSTolWkfOVVTOXgAY= +github.com/bytemare/hash v0.1.7 h1:5SqO0vCqefSkbY4UVfjSmUKyadDGoWk8fiXJDpnKE7Q= +github.com/bytemare/hash v0.1.7/go.mod h1:aAUXRjcoavq+IrTSZHPY9nEy8wHmWZk8y4Sbol4XkWU= +github.com/bytemare/hash2curve v0.2.3 h1:/1fbzqtmxu2sth4CWOhQHF2IueTDeJlIPu4mFMPvKsQ= +github.com/bytemare/hash2curve v0.2.3/go.mod h1:AyhJKfHs0s8+thKO+lMKKMm1aGc26GLHNdI1sD6fCSo= +github.com/bytemare/secp256k1 v0.1.0 h1:kjVJ06GAHSa+EJ7Rz1LdVgE0DQWdvUT77tmcGf7epXQ= +github.com/bytemare/secp256k1 v0.1.0/go.mod h1:hzquMsr3GXhVcqL9qFX7GGjmcT5dlQldKrArd7tcXHE= github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/oprf.go b/oprf.go index 26719ae..ce0c4b4 100644 --- a/oprf.go +++ b/oprf.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // -// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree or at @@ -29,29 +29,32 @@ const ( POPRF ) -// Identifier of the OPRF compatible cipher suite to be used. -type Identifier string +// Ciphersuite of the OPRF compatible cipher suite to be used. +type Ciphersuite string const ( // Ristretto255Sha512 is the OPRF cipher suite of the Ristretto255 group and SHA-512. - Ristretto255Sha512 Identifier = "ristretto255-SHA512" + Ristretto255Sha512 Ciphersuite = "ristretto255-SHA512" // Decaf448Sha512 is the OPRF cipher suite of the Decaf448 group and SHA-512. - // decaf448Sha512 Identifier = "decaf448-SHAKE256". + // decaf448Sha512 Ciphersuite = "decaf448-SHAKE256". // P256Sha256 is the OPRF cipher suite of the NIST P-256 group and SHA-256. - P256Sha256 Identifier = "P256-SHA256" + P256Sha256 Ciphersuite = "P256-SHA256" // P384Sha384 is the OPRF cipher suite of the NIST P-384 group and SHA-384. - P384Sha384 Identifier = "P384-SHA384" + P384Sha384 Ciphersuite = "P384-SHA384" // P521Sha512 is the OPRF cipher suite of the NIST P-512 group and SHA-512. - P521Sha512 Identifier = "P521-SHA512" + P521Sha512 Ciphersuite = "P521-SHA512" - nbIDs = 4 + // Secp256k1 is the OPRF cipher suite of the SECp256k1 group and SHA-256. + Secp256k1 Ciphersuite = "secp256k1-SHA256" - // version is a string explicitly stating the version name. - version = "OPRFV1" + nbIDs = 5 + + // Version is a string explicitly stating the Version name. + Version = "OPRFV1" // deriveKeyPairDST is the DST prefix for the DeriveKeyPair function. deriveKeyPairDST = "DeriveKeyPair" @@ -64,36 +67,36 @@ const ( ) var ( - groups = make(map[Identifier]group.Group, nbIDs) - hashes = make(map[Identifier]hash.Hashing, nbIDs) + groups = make(map[Ciphersuite]group.Group, nbIDs) + hashes = make(map[Ciphersuite]hash.Hashing, nbIDs) ) -func (i Identifier) new(mode Mode) *oprf { +func (c Ciphersuite) new(mode Mode) *oprf { return &oprf{ - hash: hashes[i].Get(), - contextString: contextString(mode, i), - id: i, + hash: hashes[c].Get(), + contextString: contextString(mode, c), + id: c, mode: mode, - group: groups[i], + group: groups[c], } } -// Available returns whether the Identifier is registered and available for usage. -func (i Identifier) Available() bool { +// Available returns whether the Ciphersuite is registered and available for usage. +func (c Ciphersuite) Available() bool { // Check for invalid identifiers - switch i { - case Ristretto255Sha512, P256Sha256, P384Sha384, P521Sha512: + switch c { + case Ristretto255Sha512, P256Sha256, P384Sha384, P521Sha512, Secp256k1: break default: return false } // Check for unregistered groups and hashes - if _, ok := groups[i]; !ok { + if _, ok := groups[c]; !ok { return false } - if _, ok := hashes[i]; !ok { + if _, ok := hashes[c]; !ok { return false } @@ -101,17 +104,17 @@ func (i Identifier) Available() bool { } // Group returns the group identifier used in the cipher suite. -func (i Identifier) Group() group.Group { - return groups[i] +func (c Ciphersuite) Group() group.Group { + return groups[c] } // Hash returns the hash function identifier used in the cipher suite. -func (i Identifier) Hash() hash.Hashing { - return hashes[i] +func (c Ciphersuite) Hash() hash.Hashing { + return hashes[c] } -// FromGroup returns a (V)OPRF Identifier given a Group Identifier. -func FromGroup(g group.Group) (Identifier, error) { +// FromGroup returns a (V)OPRF Ciphersuite given a Group Ciphersuite. +func FromGroup(g group.Group) (Ciphersuite, error) { for k, v := range groups { if v == g { return k, nil @@ -122,24 +125,44 @@ func FromGroup(g group.Group) (Identifier, error) { } // KeyGen returns a fresh KeyPair for the given cipher suite. -func (i Identifier) KeyGen() *KeyPair { - sk := i.Group().NewScalar().Random() - pk := i.Group().Base().Multiply(sk) +func (c Ciphersuite) KeyGen() *KeyPair { + sk := c.Group().NewScalar().Random() + pk := c.Group().Base().Multiply(sk) return &KeyPair{ - ID: i, + ID: c, PublicKey: pk.Encode(), SecretKey: sk.Encode(), } } +// DeriveKeyPair deterministically generates a private and public key pair from input seed. +func (c Ciphersuite) DeriveKeyPair(mode Mode, seed, info []byte) (*group.Scalar, *group.Element) { + dst := concatenate([]byte(deriveKeyPairDST), contextString(mode, c)) + deriveInput := concatenate(seed, lengthPrefixEncode(info)) + + var counter uint8 + var s *group.Scalar + + for s == nil || s.IsZero() { + if counter > 255 { + panic("impossible to generate non-zero scalar") + } + + s = c.Group().HashToScalar(concatenate(deriveInput, []byte{counter}), dst) + counter++ + } + + return s, c.Group().Base().Multiply(s) +} + // Client returns a (P|V)OPRF client. For the OPRF mode, serverPublicKey should be nil, and non-nil otherwise. -func (i Identifier) Client(mode Mode, serverPublicKey []byte) (*Client, error) { +func (c Ciphersuite) Client(mode Mode, serverPublicKey []byte) (*Client, error) { if mode != OPRF && mode != VOPRF && mode != POPRF { return nil, errParamInvalidMode } - client := i.client(mode) + client := c.client(mode) if mode == VOPRF || mode == POPRF { if serverPublicKey == nil { @@ -156,25 +179,25 @@ func (i Identifier) Client(mode Mode, serverPublicKey []byte) (*Client, error) { // Server returns a (P|V)OPRF server instantiated with the given encoded private key. // If privateKey is nil, a new private/public key pair is created. -func (i Identifier) Server(mode Mode, privateKey []byte) (*Server, error) { +func (c Ciphersuite) Server(mode Mode, privateKey []byte) (*Server, error) { if mode != OPRF && mode != VOPRF && mode != POPRF { return nil, errParamInvalidMode } - return i.server(mode, privateKey) + return c.server(mode, privateKey) } type oprf struct { hash *hash.Hash - id Identifier + id Ciphersuite contextString []byte mode Mode group group.Group } -func contextString(mode Mode, id Identifier) []byte { - ctx := make([]byte, 0, len(version)+3+len(id.String())) - ctx = append(ctx, version...) +func contextString(mode Mode, id Ciphersuite) []byte { + ctx := make([]byte, 0, len(Version)+3+len(id.String())) + ctx = append(ctx, Version...) ctx = append(ctx, "-"...) ctx = append(ctx, byte(mode)) ctx = append(ctx, "-"...) @@ -183,26 +206,6 @@ func contextString(mode Mode, id Identifier) []byte { return ctx } -// DeriveKeyPair deterministically generates a private and public key pair from input seed. -func (o *oprf) DeriveKeyPair(seed, info []byte) (*group.Scalar, *group.Element) { - dst := concatenate([]byte(deriveKeyPairDST), o.contextString) - deriveInput := concatenate(seed, lengthPrefixEncode(info)) - - var counter uint8 - var s *group.Scalar - - for s == nil || s.IsZero() { - if counter > 255 { - panic("impossible to generate non-zero scalar") - } - - s = o.group.HashToScalar(concatenate(deriveInput, []byte{counter}), dst) - counter++ - } - - return s, o.group.Base().Multiply(s) -} - // HashToGroup maps the input data to an element of the group. func (o *oprf) HashToGroup(data []byte) *group.Element { return o.group.HashToGroup(data, dst(hash2groupDSTPrefix, o.contextString)) @@ -213,11 +216,11 @@ func (o *oprf) HashToScalar(data []byte) *group.Scalar { return o.group.HashToScalar(data, dst(hash2scalarDSTPrefix, o.contextString)) } -func (i Identifier) client(mode Mode) *Client { +func (c Ciphersuite) client(mode Mode) *Client { return &Client{ tweakedKey: nil, serverPublicKey: nil, - oprf: i.new(mode), + oprf: c.new(mode), input: nil, blind: nil, blindedElement: nil, @@ -239,12 +242,11 @@ func (c *Client) setServerPublicKey(serverPublicKey []byte) error { return nil } -func (i Identifier) server(mode Mode, privateKey []byte) (*Server, error) { +func (c Ciphersuite) server(mode Mode, privateKey []byte) (*Server, error) { s := &Server{ privateKey: nil, publicKey: nil, - oprf: i.new(mode), - nonceR: nil, + oprf: c.new(mode), } if privateKey == nil { @@ -287,15 +289,15 @@ func (o *oprf) hashTranscript(input, info, unblinded []byte) []byte { return h } -// String implements the Stringer() interface for the Identifier. -func (i Identifier) String() string { - return string(i) +// String implements the Stringer() interface for the Ciphersuite. +func (c Ciphersuite) String() string { + return string(c) } -func (i Identifier) register(g group.Group, h hash.Hashing) { +func (c Ciphersuite) register(g group.Group, h hash.Hashing) { if g.Available() && h.Available() { - groups[i] = g - hashes[i] = h + groups[c] = g + hashes[c] = h } else { panic(fmt.Sprintf("OPRF dependencies not available - Group: %v, Hash: %v", g.Available(), h.Available())) } @@ -307,4 +309,5 @@ func init() { P256Sha256.register(group.P256Sha256, hash.SHA256) P384Sha384.register(group.P384Sha384, hash.SHA384) P521Sha512.register(group.P521Sha512, hash.SHA512) + Secp256k1.register(group.Secp256k1, hash.SHA256) } diff --git a/server.go b/server.go index 3b8190d..f6d1619 100644 --- a/server.go +++ b/server.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // -// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree or at @@ -19,7 +19,6 @@ type Server struct { privateKey *group.Scalar publicKey *group.Element *oprf - nonceR []byte } // KeyGen generates and sets a new private/public key pair. @@ -28,14 +27,14 @@ func (s *Server) KeyGen() { s.publicKey = s.group.Base().Multiply(s.privateKey) } -// SetProofNonce sets the nonce used in the proof generation in the VOPRF and POPRF modes. -func (s *Server) SetProofNonce(r []byte) { - s.nonceR = r -} - // Evaluate the input with the private key. func (s *Server) Evaluate(blindedElement, info []byte) (*Evaluation, error) { - return s.EvaluateBatch([][]byte{blindedElement}, info) + return s.innerEvaluateBatch([][]byte{blindedElement}, nil, info) +} + +// EvaluateWithRandom does the same as Evaluate and allows to provide a random input for proof generation. +func (s *Server) EvaluateWithRandom(blindedElement, random, info []byte) (*Evaluation, error) { + return s.innerEvaluateBatch([][]byte{blindedElement}, random, info) } func (s *Server) getPrivateKeys(info []byte) (scalar, t *group.Scalar, err error) { @@ -54,23 +53,19 @@ func (s *Server) getPrivateKeys(info []byte) (scalar, t *group.Scalar, err error return scalar, t, nil } -func (s *Server) randomScalar() *group.Scalar { - r := s.group.NewScalar() - - if s.nonceR == nil { +func setRandom(r *group.Scalar, random []byte) error { + if len(random) == 0 { r.Random() } else { - if err := r.Decode(s.nonceR); err != nil { - panic(err) + if err := r.Decode(random); err != nil { + return fmt.Errorf("decoding input random scalar: %w", err) } } - return r + return nil } -// EvaluateBatch evaluates the input batch of blindedElements and returns a pointer to the Evaluation. If the server -// was set to be un VOPRF mode, the proof will be included in the Evaluation. -func (s *Server) EvaluateBatch(blindedElements [][]byte, info []byte) (*Evaluation, error) { +func (s *Server) innerEvaluateBatch(blindedElements [][]byte, random, info []byte) (*Evaluation, error) { ev := &evaluation{ proofC: nil, proofS: nil, @@ -86,10 +81,15 @@ func (s *Server) EvaluateBatch(blindedElements [][]byte, info []byte) (*Evaluati return nil, err } - var random *group.Scalar + var r *group.Scalar + if s.mode == VOPRF || s.mode == POPRF { - random = s.randomScalar() blinded = make([]*group.Element, len(blindedElements)) + + r = s.group.NewScalar() + if err := setRandom(r, random); err != nil { + return nil, err + } } // decode and evaluate element(s) @@ -108,15 +108,26 @@ func (s *Server) EvaluateBatch(blindedElements [][]byte, info []byte) (*Evaluati // generate proof if s.mode == VOPRF { - ev.proofC, ev.proofS = s.oprf.generateProof(random, s.privateKey, s.publicKey, blinded, ev.elements) + ev.proofC, ev.proofS = s.oprf.generateProof(r, s.privateKey, s.publicKey, blinded, ev.elements) } else if s.mode == POPRF { tweakedKey := s.group.Base().Multiply(t) - ev.proofC, ev.proofS = s.oprf.generateProof(random, t, tweakedKey, ev.elements, blinded) + ev.proofC, ev.proofS = s.oprf.generateProof(r, t, tweakedKey, ev.elements, blinded) } return ev.serialize(), nil } +// EvaluateBatch evaluates the input batch of blindedElements and returns a pointer to the Evaluation. If the server +// was set to be un VOPRF mode, the proof will be included in the Evaluation. +func (s *Server) EvaluateBatch(blindedElements [][]byte, info []byte) (*Evaluation, error) { + return s.innerEvaluateBatch(blindedElements, nil, info) +} + +// EvaluateBatchWithRandom does the same as EvaluateBatch and allows to provide a random input for proof generation. +func (s *Server) EvaluateBatchWithRandom(blindedElements [][]byte, random, info []byte) (*Evaluation, error) { + return s.innerEvaluateBatch(blindedElements, random, info) +} + // FullEvaluate reproduces the full PRF but without the blinding operations, using the client's input. // This should output the same digest as the client's Finalize() function. func (s *Server) FullEvaluate(input, info []byte) ([]byte, error) { @@ -169,7 +180,7 @@ func (s *Server) PublicKey() []byte { return s.publicKey.Encode() } -// Identifier returns the cipher suite used in s' instance. -func (s *Server) Identifier() Identifier { +// Ciphersuite returns the cipher suite used in the server's instance. +func (s *Server) Ciphersuite() Ciphersuite { return s.oprf.id } diff --git a/tests/helper_test.go b/tests/helper_test.go new file mode 100644 index 0000000..449bb3d --- /dev/null +++ b/tests/helper_test.go @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree or at +// https://spdx.org/licenses/MIT.html + +package voprf_test + +import ( + "crypto/elliptic" + "crypto/rand" + "encoding/binary" + "encoding/hex" + "fmt" + "log" + "math/big" + "testing" + + group "github.com/bytemare/crypto" + + "github.com/bytemare/voprf" +) + +func init() { + log.SetFlags(log.LstdFlags | log.Lshortfile) +} + +// helper functions + +type configuration struct { + curve elliptic.Curve + ciphersuite voprf.Ciphersuite + name string +} + +var configurationTable = []configuration{ + { + name: "Ristretto255", + ciphersuite: voprf.Ristretto255Sha512, + curve: nil, + }, + { + name: "P256Sha256", + ciphersuite: voprf.P256Sha256, + curve: elliptic.P256(), + }, + { + name: "P384Sha512", + ciphersuite: voprf.P384Sha384, + curve: elliptic.P384(), + }, + { + name: "P521Sha512", + ciphersuite: voprf.P521Sha512, + curve: elliptic.P521(), + }, +} + +func testAll(t *testing.T, f func(*configuration)) { + for _, test := range configurationTable { + t.Run(test.name, func(t *testing.T) { + f(&test) + }) + } +} + +func getBadRistrettoScalar() []byte { + a := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + decoded, _ := hex.DecodeString(a) + + return decoded +} + +func getBadRistrettoElement() []byte { + a := "2a292df7e32cababbd9de088d1d1abec9fc0440f637ed2fba145094dc14bea08" + decoded, _ := hex.DecodeString(a) + + return decoded +} + +func badScalar(t *testing.T, g group.Group, curve elliptic.Curve) []byte { + order := curve.Params().P + exceeded := new(big.Int).Add(order, big.NewInt(2)).Bytes() + + err := g.NewScalar().Decode(exceeded) + if err == nil { + t.Errorf("Exceeding order did not yield an error for group %s", g) + } + + return exceeded +} + +func randomBytes(length int) []byte { + r := make([]byte, length) + if _, err := rand.Read(r); err != nil { + // We can as well not panic and try again in a loop and a counter to stop. + panic(fmt.Errorf("unexpected error in generating random bytes : %w", err)) + } + + return r +} + +func getBadNistElement(t *testing.T, id group.Group) []byte { + size := id.ElementLength() + element := randomBytes(size) + // detag compression + element[0] = 4 + + // test if invalid compression is detected + err := id.NewElement().Decode(element) + if err == nil { + t.Errorf("detagged compressed point did not yield an error for group %s", id) + } + + return element +} + +func getBadElement(t *testing.T, c *configuration) []byte { + switch c.ciphersuite { + case voprf.Ristretto255Sha512: + return getBadRistrettoElement() + default: + return getBadNistElement(t, c.ciphersuite.Group()) + } +} + +func getBadScalar(t *testing.T, c *configuration) []byte { + switch c.ciphersuite { + case voprf.Ristretto255Sha512: + return getBadRistrettoScalar() + default: + return badScalar(t, c.ciphersuite.Group(), c.curve) + } +} + +const ( + deriveKeyPairDST = "DeriveKeyPair" + hash2groupDSTPrefix = "HashToGroup-" +) + +func concatenate(input ...[]byte) []byte { + if len(input) == 1 { + if len(input[0]) == 0 { + return nil + } + + return input[0] + } + + length := 0 + for _, in := range input { + length += len(in) + } + + buf := make([]byte, 0, length) + + for _, in := range input { + buf = append(buf, in...) + } + + return buf +} + +func dst(prefix string, contextString []byte) []byte { + p := []byte(prefix) + t := make([]byte, 0, len(p)+len(contextString)) + t = append(t, p...) + t = append(t, contextString...) + + return t +} + +func i2osp2(value int) []byte { + out := make([]byte, 2) + binary.BigEndian.PutUint16(out, uint16(value)) + + return out +} + +func lengthPrefixEncode(input []byte) []byte { + return append(i2osp2(len(input)), input...) +} + +func contextString(mode voprf.Mode, id voprf.Ciphersuite) []byte { + ctx := make([]byte, 0, len(voprf.Version)+3+len(id.String())) + ctx = append(ctx, voprf.Version...) + ctx = append(ctx, "-"...) + ctx = append(ctx, byte(mode)) + ctx = append(ctx, "-"...) + ctx = append(ctx, id.String()...) + + return ctx +} + +func deriveKeyPair(seed, info []byte, mode voprf.Mode, id voprf.Ciphersuite) (*group.Scalar, *group.Element) { + dst := concatenate([]byte(deriveKeyPairDST), contextString(mode, id)) + deriveInput := concatenate(seed, lengthPrefixEncode(info)) + + var counter uint8 + var s *group.Scalar + + for s == nil || s.IsZero() { + if counter > 255 { + panic("impossible to generate non-zero scalar") + } + + s = id.Group().HashToScalar(concatenate(deriveInput, []byte{counter}), dst) + counter++ + } + + return s, id.Group().Base().Multiply(s) +} diff --git a/tests/state_test.go b/tests/state_test.go index 911f749..d312f30 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // -// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree or at @@ -9,15 +9,57 @@ package voprf_test import ( + "bytes" "encoding/json" "fmt" "testing" - "github.com/google/go-cmp/cmp" - "github.com/bytemare/voprf" ) +func TestEvaluationSerde(t *testing.T) { + suite := voprf.Ristretto255Sha512 + input := []byte("input") + mode := voprf.OPRF + + server, err := suite.Server(mode, nil) + if err != nil { + t.Fatal(err) + } + + spk := server.PublicKey() + + client, err := suite.Client(mode, spk) + if err != nil { + t.Fatal(err) + } + + blinded := client.Blind(input, nil) + evaluation, err := server.Evaluate(blinded, nil) + if err != nil { + panic(err) + } + + ser := evaluation.Serialize() + deser := &voprf.Evaluation{} + + if err := deser.Deserialize(ser); err != nil { + t.Fatal(err) + } + + if !areArraysOfArraysEqual(evaluation.Elements, deser.Elements) { + t.Fatal("evaluation serde failed") + } + + if bytes.Compare(evaluation.ProofC, evaluation.ProofC) != 0 { + t.Fatal("evaluation serde failed") + } + + if bytes.Compare(evaluation.ProofS, evaluation.ProofS) != 0 { + t.Fatal("evaluation serde failed") + } +} + func TestClient_State(t *testing.T) { suite := voprf.Ristretto255Sha512 input := []byte("input") @@ -52,9 +94,55 @@ func TestClient_State(t *testing.T) { export2 := resumed.Export() - if !cmp.Equal(export, export2) { + if !areStatesEqual(export, export2) { t.Fatal("states are not equal") } }) } } + +func areArraysOfArraysEqual(a, b [][]byte) bool { + if len(a) != len(b) { + return false + } + + for i, c := range a { + if bytes.Compare(c, b[i]) != 0 { + return false + } + } + + return true +} + +func areStatesEqual(x1, x2 *voprf.State) bool { + if x1.Mode != x2.Mode { + return false + } + + if x1.Identifier != x2.Identifier { + return false + } + + if bytes.Compare(x1.TweakedKey, x1.TweakedKey) != 0 { + return false + } + + if bytes.Compare(x1.ServerPublicKey, x1.ServerPublicKey) != 0 { + return false + } + + if !areArraysOfArraysEqual(x1.Input, x2.Input) { + return false + } + + if !areArraysOfArraysEqual(x1.Blind, x2.Blind) { + return false + } + + if !areArraysOfArraysEqual(x1.Blinded, x2.Blinded) { + return false + } + + return true +} diff --git a/tests/utils_test.go b/tests/utils_test.go index af6ef6e..f09fa0e 100644 --- a/tests/utils_test.go +++ b/tests/utils_test.go @@ -1,96 +1,9 @@ // SPDX-License-Identifier: MIT // -// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree or at // https://spdx.org/licenses/MIT.html package voprf_test - -import ( - "encoding/binary" - - group "github.com/bytemare/crypto" - - "github.com/bytemare/voprf" -) - -const ( - version = "OPRFV1" - deriveKeyPairDST = "DeriveKeyPair" - hash2groupDSTPrefix = "HashToGroup-" -) - -func concatenate(input ...[]byte) []byte { - if len(input) == 1 { - if len(input[0]) == 0 { - return nil - } - - return input[0] - } - - length := 0 - for _, in := range input { - length += len(in) - } - - buf := make([]byte, 0, length) - - for _, in := range input { - buf = append(buf, in...) - } - - return buf -} - -func dst(prefix string, contextString []byte) []byte { - p := []byte(prefix) - t := make([]byte, 0, len(p)+len(contextString)) - t = append(t, p...) - t = append(t, contextString...) - - return t -} - -func i2osp2(value int) []byte { - out := make([]byte, 2) - binary.BigEndian.PutUint16(out, uint16(value)) - - return out -} - -func lengthPrefixEncode(input []byte) []byte { - return append(i2osp2(len(input)), input...) -} - -func contextString(mode voprf.Mode, id voprf.Identifier) []byte { - ctx := make([]byte, 0, len(version)+3+len(id.String())) - ctx = append(ctx, version...) - ctx = append(ctx, "-"...) - ctx = append(ctx, byte(mode)) - ctx = append(ctx, "-"...) - ctx = append(ctx, id.String()...) - - return ctx -} - -func deriveKeyPair(seed, info []byte, mode voprf.Mode, id voprf.Identifier) (*group.Scalar, *group.Element) { - dst := concatenate([]byte(deriveKeyPairDST), contextString(mode, id)) - deriveInput := concatenate(seed, lengthPrefixEncode(info)) - - var counter uint8 - var s *group.Scalar - - for s == nil || s.IsZero() { - if counter > 255 { - panic("impossible to generate non-zero scalar") - } - - s = id.Group().HashToScalar(concatenate(deriveInput, []byte{counter}), dst) - counter++ - } - - return s, id.Group().Base().Multiply(s) -} diff --git a/tests/vectors_test.go b/tests/vectors_test.go index a9edd18..d77ee6f 100644 --- a/tests/vectors_test.go +++ b/tests/vectors_test.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // -// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree or at @@ -37,7 +37,7 @@ type test struct { } type testVector struct { - ID voprf.Identifier `json:"proof,omitempty"` + ID voprf.Ciphersuite `json:"proof,omitempty"` EvaluationProof struct { Proof string `json:"proof,omitempty"` Random string `json:"r,omitempty"` @@ -70,7 +70,7 @@ func decodeBatch(nb int, in string) ([][]byte, error) { return out, nil } -func (t *test) Verify(suite voprf.Identifier) error { +func (t *test) Verify(suite voprf.Ciphersuite) error { g := suite.Group() for i, b := range t.Blind { @@ -168,15 +168,15 @@ func (tv *testVector) Decode() (*test, error) { type vectors []vector type vector struct { - DST string `json:"groupDST"` - Hash string `json:"hash"` - KeyInfo string `json:"keyInfo"` - SksSeed string `json:"seed"` - PkSm string `json:"pkSm,omitempty"` - SkSm string `json:"skSm"` - SuiteID voprf.Identifier `json:"identifier"` - TestVectors []testVector `json:"vectors,omitempty"` - Mode voprf.Mode `json:"mode"` + DST string `json:"groupDST"` + Hash string `json:"hash"` + KeyInfo string `json:"keyInfo"` + SksSeed string `json:"seed"` + PkSm string `json:"pkSm,omitempty"` + SkSm string `json:"skSm"` + SuiteID voprf.Ciphersuite `json:"identifier"` + TestVectors []testVector `json:"vectors,omitempty"` + Mode voprf.Mode `json:"mode"` } func hashToHash(h string) hash.Identifier { @@ -226,7 +226,7 @@ func (v vector) checkParams(t *testing.T) { //} } -func testBlind(t *testing.T, id voprf.Identifier, client *voprf.Client, input, blind, expected, info []byte) { +func testBlind(t *testing.T, id voprf.Ciphersuite, client *voprf.Client, input, blind, expected, info []byte) { s := id.Group().NewScalar() if err := s.Decode(blind); err != nil { t.Fatal(fmt.Errorf("blind decoding to scalar in suite %v errored with %q", id, err)) @@ -256,7 +256,7 @@ func testBlindBatchWithBlinds(t *testing.T, client *voprf.Client, inputs, blinds func testOPRF( t *testing.T, - id voprf.Identifier, + id voprf.Ciphersuite, mode voprf.Mode, client *voprf.Client, server *voprf.Server, @@ -273,9 +273,8 @@ func testOPRF( // OPRFServer evaluating var ev *voprf.Evaluation - server.SetProofNonce(test.NonceR) if test.Batch == 1 { - ev, err = server.Evaluate(test.BlindedElement[0], test.Info) + ev, err = server.EvaluateWithRandom(test.BlindedElement[0], test.NonceR, test.Info) if err != nil { t.Fatal(err) } @@ -284,7 +283,7 @@ func testOPRF( t.Fatal("unexpected evaluation element") } } else { - ev, err = server.EvaluateBatch(test.BlindedElement, test.Info) + ev, err = server.EvaluateBatchWithRandom(test.BlindedElement, test.NonceR, test.Info) if err != nil { t.Fatal(err) } diff --git a/tests/voprf_test.go b/tests/voprf_test.go new file mode 100644 index 0000000..71b044b --- /dev/null +++ b/tests/voprf_test.go @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: MIT +// +// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree or at +// https://spdx.org/licenses/MIT.html + +package voprf_test + +import ( + "encoding/hex" + "testing" + + "github.com/bytemare/voprf" +) + +func makeClientAndServer(t *testing.T, mode voprf.Mode, ciphersuite voprf.Ciphersuite) (*voprf.Client, *voprf.Server) { + server, err := ciphersuite.Server(mode, nil) + if err != nil { + t.Fatal(err) + } + + spk := server.PublicKey() + + client, err := ciphersuite.Client(mode, spk) + if err != nil { + t.Fatal(err) + } + + return client, server +} + +func runOPRF(t *testing.T, c *configuration, mode voprf.Mode, input, info []byte) *voprf.Evaluation { + client, server := makeClientAndServer(t, mode, c.ciphersuite) + + blinded := client.Blind(input, info) + + evaluation, err := server.Evaluate(blinded, info) + if err != nil { + t.Fatal(err) + } + + if _, err = client.Finalize(evaluation, info); err != nil { + t.Fatal(err) + } + + return evaluation +} + +func TestOPRF(t *testing.T) { + mode := voprf.OPRF + input := []byte("input") + + testAll(t, func(c *configuration) { + _ = runOPRF(t, c, mode, input, nil) + }) +} + +func TestVOPRF(t *testing.T) { + mode := voprf.VOPRF + input := []byte("input") + + testAll(t, func(c *configuration) { + _ = runOPRF(t, c, mode, input, nil) + }) +} + +func TestPOPRF(t *testing.T) { + mode := voprf.POPRF + info := []byte("info") + input := []byte("input") + + testAll(t, func(c *configuration) { + _ = runOPRF(t, c, mode, input, info) + }) +} + +func TestBatching(t *testing.T) { + mode := voprf.POPRF + info := []byte("info") + inputs := [][]byte{ + []byte("input1"), + []byte("input2"), + []byte("input3"), + } + + testAll(t, func(c *configuration) { + client, server := makeClientAndServer(t, mode, c.ciphersuite) + + _, blinded, err := client.BlindBatch(inputs, info) + if err != nil { + t.Fatal(err) + } + + evaluation, err := server.EvaluateBatch(blinded, info) + if err != nil { + t.Fatal(err) + } + + if _, err = client.FinalizeBatch(evaluation, info); err != nil { + t.Fatal(err) + } + }) +} + +func TestServerKeys(t *testing.T) { + mode := voprf.OPRF + + testAll(t, func(c *configuration) { + server, err := c.ciphersuite.Server(mode, nil) + if err != nil { + t.Fatal(err) + } + + private := c.ciphersuite.Group().NewScalar() + if err = private.Decode(server.PrivateKey()); err != nil { + t.Fatal(err) + } + + public := c.ciphersuite.Group().NewElement() + if err = public.Decode(server.PublicKey()); err != nil { + t.Fatal(err) + } + + pk := c.ciphersuite.Group().Base().Multiply(private) + if pk.Equal(public) != 1 { + t.Fatal("expected equality") + } + }) +} + +func TestDeriveKeyPair(t *testing.T) { + info := []byte("some instance") + ciphersuite := voprf.Ristretto255Sha512 + + random, _ := hex.DecodeString("c332260baab120459e7ad1d47ce5a43f980abe9c19ecc0550bbd0dde58a548bf") + encodedReferenceSecretKeyR255, _ := hex.DecodeString( + "78e4560c5779791f87f6493fff0ac0476d64ebdecb9ae26a0565f673b10be906", + ) + encodedReferencePublicKeyR255, _ := hex.DecodeString( + "7c45e2a6748414358f597874d4afa951cbc39cb3300c5cfde9ac86348062560f", + ) + + refSk := ciphersuite.Group().NewScalar() + _ = refSk.Decode(encodedReferenceSecretKeyR255) + + refPk := ciphersuite.Group().NewElement() + _ = refPk.Decode(encodedReferencePublicKeyR255) + + sk, pk := ciphersuite.DeriveKeyPair(voprf.OPRF, random, info) + + if sk.Equal(refSk) != 1 || pk.Equal(refPk) != 1 { + t.Fatal("expected equality") + } +} diff --git a/utils.go b/utils.go index d0feb40..9b57cf4 100644 --- a/utils.go +++ b/utils.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // -// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree or at @@ -15,7 +15,7 @@ import ( // KeyPair assembles a VOPRF key pair. The SecretKey can be used as the evaluation key for the group identified by ID. type KeyPair struct { - ID Identifier + ID Ciphersuite PublicKey []byte SecretKey []byte } diff --git a/verifiable.go b/verifiable.go index b096a10..29094c3 100644 --- a/verifiable.go +++ b/verifiable.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // -// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree or at