Skip to content

Commit

Permalink
Merge branch 'main' into add-clusterfuzz-lite
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Bourdrez <3641580+bytemare@users.noreply.github.com>
  • Loading branch information
bytemare authored Apr 30, 2024
2 parents 51107c1 + 46e6f45 commit 3220163
Show file tree
Hide file tree
Showing 31 changed files with 1,012 additions and 393 deletions.
10 changes: 8 additions & 2 deletions .github/.golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ linters:
- contextcheck
- cyclop
- decorder
- depguard
#- depguard
- dogsled
- dupl
- dupword
- durationcheck
- errcheck
- errchkjson
Expand All @@ -25,6 +26,7 @@ linters:
- forcetypeassert
- funlen
- gci
- ginkgolinter
#- gochecknoglobals
#- gochecknoinits
- gocognit
Expand All @@ -43,6 +45,7 @@ linters:
- gomodguard
- goprintffuncname
- gosec
- gosmopolitan
- gosimple
- govet
- grouper
Expand All @@ -64,6 +67,7 @@ linters:
- nolintlint
#- nonamedreturns
- nosprintfhostport
- musttag
- paralleltest
- prealloc
- predeclared
Expand All @@ -74,9 +78,10 @@ linters:
- sqlclosecheck
- staticcheck
- stylecheck
- tagalign
- tagliatelle
- tenv
#- testableexamples
- testableexamples
- testpackage
- thelper
- tparallel
Expand All @@ -90,6 +95,7 @@ linters:
- whitespace
- wrapcheck
- wsl
- zerologlint
presets:
- bugs
- unused
Expand Down
9 changes: 5 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:

# Linting
- name: Linting
uses: golangci/golangci-lint-action@92ba55cf0d79a9feb999e9bcef95c952bbbe545a # pin@master
uses: golangci/golangci-lint-action@9cb5ef734c83c4a375008f65983ab82f8d416598 # pin@master
with:
version: latest
args: --config=./.github/.golangci.yml ./...
Expand All @@ -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@27135e314dd1818f797af1db9dae03a9f045786b # pin@master
Expand Down Expand Up @@ -69,13 +69,13 @@ jobs:

# Codecov
- name: Codecov
uses: codecov/codecov-action@29386c70ef20e286228c72b668a06fd0e8399192 # pin@master
uses: codecov/codecov-action@ef2b0df87fbbd2569a553208e78409b1bc2fb469 # pin@master
with:
file: .github/coverage.out

# Sonar
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@db501078e936e4b4c8773d1bb949ba9ddb7b6b6a # pin@master
uses: SonarSource/sonarcloud-github-action@5ee47de3c96f0c1c51b09d2ff1fec0cfeefcf67c # pin@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
Expand All @@ -85,6 +85,7 @@ jobs:
-Dsonar.projectKey=bytemare_opaque
-Dsonar.go.coverage.reportPaths=.github/coverage.out
-Dsonar.sources=.
-Dsonar.exclusions=examples_test.go
-Dsonar.test.exclusions=examples_test.go,tests/**
-Dsonar.coverage.exclusions=examples_test.go,tests/**
-Dsonar.tests=tests/
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/scorecards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
persist-credentials: false

- name: "Run analysis"
uses: ossf/scorecard-action@c1aec4ac820532bab364f02a81873c555a0ba3a1 # pin@master
uses: ossf/scorecard-action@54b14e138ebbcc54a5d81dab8f75463a3cd21e12 # pin@master
with:
results_file: results.sarif
results_format: sarif
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/snyk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
steps:
- uses: actions/checkout@61b9e3751b92087fd0b06925ba6dd6314e06f089 # pin@master
- name: Run Snyk to check for vulnerabilities
uses: snyk/actions/golang@806182742461562b67788a64410098c9d9b96adb # pin@master
uses: snyk/actions/golang@39091e69b560da335383b404e50d65b408f4f812 # pin@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ correspond to the [official draft versions](https://datatracker.ietf.org/doc/dra

#### What is OPAQUE?

> OPAQUE is a PKI-free secure aPAKE that is secure against pre-computation attacks. OPAQUE provides forward secrecy with
> OPAQUE is an aPAKE that is secure against pre-computation attacks. OPAQUE provides forward secrecy with
> respect to password leakage while also hiding the password from the server, even during password registration. OPAQUE
> allows applications to increase the difficulty of offline dictionary attacks via iterated hashing or other key
> stretching schemes. OPAQUE is also extensible, allowing clients to safely store and retrieve arbitrary application data
Expand Down
62 changes: 31 additions & 31 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var (
// errInvalidMaskedLength happens when unmasking a masked response.
errInvalidMaskedLength = errors.New("invalid masked response length")

// errKe1Missing happens when LoginFinish is called and the client has no Ke1 in state.
// errKe1Missing happens when GenerateKE3 is called and the client has no Ke1 in state.
errKe1Missing = errors.New("missing KE1 in client state")
)

Expand Down Expand Up @@ -130,9 +130,9 @@ func (c *Client) RegistrationFinalize(
options ...ClientRegistrationFinalizeOptions,
) (record *message.RegistrationRecord, exportKey []byte) {
credentials := initClientRegistrationFinalizeOptions(options)
randomizedPwd := c.buildPRK(resp.EvaluatedMessage)
maskingKey := c.conf.KDF.Expand(randomizedPwd, []byte(tag.MaskingKey), c.conf.KDF.Size())
envelope, clientPublicKey, exportKey := keyrecovery.Store(c.conf, randomizedPwd, resp.Pks, credentials)
randomizedPassword := c.buildPRK(resp.EvaluatedMessage)
maskingKey := c.conf.KDF.Expand(randomizedPassword, []byte(tag.MaskingKey), c.conf.KDF.Size())
envelope, clientPublicKey, exportKey := keyrecovery.Store(c.conf, randomizedPassword, resp.Pks, credentials)

return &message.RegistrationRecord{
PublicKey: clientPublicKey,
Expand All @@ -141,59 +141,59 @@ func (c *Client) RegistrationFinalize(
}, exportKey
}

// ClientLoginInitOptions enables setting optional values for the session, which default to secure random values if not
// GenerateKE1Options enables setting optional values for the session, which default to secure random values if not
// set.
type ClientLoginInitOptions struct {
type GenerateKE1Options struct {
// Blind: optional
Blind *group.Scalar
// EphemeralSecretKey: optional
EphemeralSecretKey *group.Scalar
// KeyShareSeed: optional
KeyShareSeed []byte
// Nonce: optional
Nonce []byte
// NonceLength: optional
NonceLength uint
}

func (c ClientLoginInitOptions) get() (*group.Scalar, ake.Options) {
func (c GenerateKE1Options) get() (*group.Scalar, ake.Options) {
return c.Blind, ake.Options{
EphemeralSecretKey: c.EphemeralSecretKey,
Nonce: c.Nonce,
NonceLength: c.NonceLength,
KeyShareSeed: c.KeyShareSeed,
Nonce: c.Nonce,
NonceLength: c.NonceLength,
}
}

func getClientLoginInitOptions(options []ClientLoginInitOptions) (*group.Scalar, ake.Options) {
func getGenerateKE1Options(options []GenerateKE1Options) (*group.Scalar, ake.Options) {
if len(options) != 0 {
return options[0].get()
}

return nil, ake.Options{
EphemeralSecretKey: nil,
Nonce: nil,
NonceLength: internal.NonceLength,
KeyShareSeed: nil,
Nonce: nil,
NonceLength: internal.NonceLength,
}
}

// LoginInit initiates the authentication process, returning a KE1 message blinding the given password.
func (c *Client) LoginInit(password []byte, options ...ClientLoginInitOptions) *message.KE1 {
blind, akeOptions := getClientLoginInitOptions(options)
// GenerateKE1 initiates the authentication process, returning a KE1 message blinding the given password.
func (c *Client) GenerateKE1(password []byte, options ...GenerateKE1Options) *message.KE1 {
blind, akeOptions := getGenerateKE1Options(options)
m := c.OPRF.Blind(password, blind)
ke1 := c.Ake.Start(c.conf.Group, akeOptions)
ke1.CredentialRequest = message.NewCredentialRequest(c.conf.OPRF, m)
ke1.CredentialRequest = message.NewCredentialRequest(m)
c.Ake.Ke1 = ke1.Serialize()

return ke1
}

// ClientLoginFinishOptions enables setting optional client values for the client registration.
type ClientLoginFinishOptions struct {
// GenerateKE3Options enables setting optional client values for the client registration.
type GenerateKE3Options struct {
// ClientIdentity: optional
ClientIdentity []byte
// ServerIdentity: optional
ServerIdentity []byte
}

func initClientLoginFinishOptions(options []ClientLoginFinishOptions) *ake.Identities {
func initGenerateKE3Options(options []GenerateKE3Options) *ake.Identities {
if len(options) == 0 {
return &ake.Identities{
ClientIdentity: nil,
Expand All @@ -207,10 +207,10 @@ func initClientLoginFinishOptions(options []ClientLoginFinishOptions) *ake.Ident
}
}

// LoginFinish returns a KE3 message given the server's KE2 response message and the identities. If the idc
// GenerateKE3 returns a KE3 message given the server's KE2 response message and the identities. If the idc
// or ids parameters are nil, the client and server's public keys are taken as identities for both.
func (c *Client) LoginFinish(
ke2 *message.KE2, options ...ClientLoginFinishOptions,
func (c *Client) GenerateKE3(
ke2 *message.KE2, options ...GenerateKE3Options,
) (ke3 *message.KE3, exportKey []byte, err error) {
if len(c.Ake.Ke1) == 0 {
return nil, nil, errKe1Missing
Expand All @@ -221,14 +221,14 @@ func (c *Client) LoginFinish(
return nil, nil, errInvalidMaskedLength
}

identities := initClientLoginFinishOptions(options)
identities := initGenerateKE3Options(options)

// Finalize the OPRF.
randomizedPwd := c.buildPRK(ke2.EvaluatedMessage)
randomizedPassword := c.buildPRK(ke2.EvaluatedMessage)

// Decrypt the masked response.
serverPublicKey, serverPublicKeyBytes,
envelope, err := masking.Unmask(c.conf, randomizedPwd, ke2.MaskingNonce, ke2.MaskedResponse)
envelope, err := masking.Unmask(c.conf, randomizedPassword, ke2.MaskingNonce, ke2.MaskedResponse)
if err != nil {
return nil, nil, fmt.Errorf("unmasking: %w", err)
}
Expand All @@ -237,7 +237,7 @@ func (c *Client) LoginFinish(
clientSecretKey, clientPublicKey,
exportKey, err := keyrecovery.Recover(
c.conf,
randomizedPwd,
randomizedPassword,
serverPublicKeyBytes,
identities.ClientIdentity,
identities.ServerIdentity,
Expand All @@ -257,7 +257,7 @@ func (c *Client) LoginFinish(
return ke3, exportKey, nil
}

// SessionKey returns the session key if the previous call to LoginFinish() was successful.
// SessionKey returns the session key if the previous call to GenerateKE3() was successful.
func (c *Client) SessionKey() []byte {
return c.Ake.SessionKey()
}
18 changes: 9 additions & 9 deletions deserializer.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func (d *Deserializer) deserializeCredentialRequest(input []byte) (*message.Cred
return nil, errInvalidBlindedData
}

return message.NewCredentialRequest(d.conf.OPRF, blindedMessage), nil
return message.NewCredentialRequest(blindedMessage), nil
}

func (d *Deserializer) deserializeCredentialResponse(
Expand Down Expand Up @@ -148,9 +148,9 @@ func (d *Deserializer) KE1(ke1 []byte) (*message.KE1, error) {
}

return &message.KE1{
CredentialRequest: request,
NonceU: nonceU,
EpkU: epku,
CredentialRequest: request,
ClientNonce: nonceU,
ClientPublicKeyshare: epku,
}, nil
}

Expand Down Expand Up @@ -189,10 +189,10 @@ func (d *Deserializer) KE2(ke2 []byte) (*message.KE2, error) {
}

return &message.KE2{
CredentialResponse: cresp,
NonceS: nonceS,
EpkS: epks,
Mac: mac,
CredentialResponse: cresp,
ServerNonce: nonceS,
ServerPublicKeyshare: epks,
ServerMac: mac,
}, nil
}

Expand All @@ -202,7 +202,7 @@ func (d *Deserializer) KE3(ke3 []byte) (*message.KE3, error) {
return nil, errInvalidMessageLength
}

return &message.KE3{Mac: ke3}, nil
return &message.KE3{ClientMac: ke3}, nil
}

// DecodeAkePrivateKey takes a serialized private key (a scalar) and attempts to return it's decoded form.
Expand Down
10 changes: 5 additions & 5 deletions examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ func Example_loginKeyExchange() {

// The client initiates the ball and sends the serialized ke1 to the server.
{
ke1 := client.LoginInit(password)
ke1 := client.GenerateKE1(password)
message1 = ke1.Serialize()
}

Expand All @@ -330,7 +330,7 @@ func Example_loginKeyExchange() {
log.Fatalln(err)
}

ke2, err := server.LoginInit(ke1, exampleClientRecord)
ke2, err := server.GenerateKE2(ke1, exampleClientRecord)
if err != nil {
log.Fatalln(err)
}
Expand All @@ -347,7 +347,7 @@ func Example_loginKeyExchange() {
}

// In this example, we don't use the secret export key. The client sends the serialized ke3 to the server.
ke3, _, err := client.LoginFinish(ke2, opaque.ClientLoginFinishOptions{
ke3, _, err := client.GenerateKE3(ke2, opaque.GenerateKE3Options{
ClientIdentity: clientID,
ServerIdentity: serverID,
})
Expand Down Expand Up @@ -391,7 +391,7 @@ func Example_loginKeyExchange() {

// Example_FakeResponse shows how to counter some client enumeration attacks by faking an existing client entry.
// Precompute the fake client record, and return it when no valid record was found.
// Use this with the server's LoginInit function whenever a client wants to retrieve an envelope but a client
// Use this with the server's GenerateKE1 function whenever a client wants to retrieve an envelope but a client
// entry does not exist. Failing to do so results in an attacker being able to enumerate users.
func Example_fakeResponse() {
// The server must have been set up with its long term values once. So we're calling this, here, for the demo.
Expand Down Expand Up @@ -433,7 +433,7 @@ func Example_fakeResponse() {
log.Fatalln(err)
}

ke2, err := server.LoginInit(ke1, fakeRecord)
ke2, err := server.GenerateKE2(ke1, fakeRecord)
if err != nil {
log.Fatalln(err)
}
Expand Down
13 changes: 7 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
module github.com/bytemare/opaque

go 1.20
go 1.21

require (
github.com/bytemare/crypto v0.4.3
github.com/bytemare/crypto v0.5.2
github.com/bytemare/hash v0.1.5
github.com/bytemare/ksf v0.1.0
)

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/nistec v0.0.3 // indirect
github.com/bytemare/hash2curve v0.2.2 // 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.14.0 // indirect
golang.org/x/sys v0.13.0 // indirect
)
Loading

0 comments on commit 3220163

Please sign in to comment.