├── nike
├── csidh
│ ├── csidh_not_supported.go
│ ├── csidh_test.go
│ └── csidh.go
├── hybrid
│ ├── no_nobs_csidh.go
│ ├── nobs_csidh.go
│ └── ctidh.go
├── diffiehellman
│ ├── dh_test.go
│ └── LICENSE
├── ctidh
│ ├── ctidh1024
│ │ ├── ctidh_test.go
│ │ └── ctidh.go
│ ├── ctidh2048
│ │ ├── ctidh_test.go
│ │ └── ctidh.go
│ ├── ctidh511
│ │ ├── ctidh_test.go
│ │ └── ctidh.go
│ └── ctidh512
│ │ ├── ctidh_test.go
│ │ └── ctidh.go
├── schemes
│ ├── schemes.go
│ └── schemes_test.go
├── nike.go
├── x25519
│ └── ecdh_test.go
├── pem
│ └── pem.go
└── x448
│ └── x448.go
├── sign
├── sphincsplus
│ ├── sphincs_not_supported.go
│ ├── sphincs_test.go
│ └── sphincs.go
├── hybrid
│ ├── sphincsplus_not_supported.go
│ ├── sphincsplus.go
│ └── hybrid.go
├── schemes
│ ├── schemes.go
│ └── schemes_test.go
├── pem
│ └── pem.go
├── ed25519
│ └── eddsa_test.go
└── interfaces.go
├── hash
└── hash.go
├── .github
└── workflows
│ ├── go.yml
│ ├── macos-golang-test.yml
│ ├── ubuntu-golang-cross-compile-test.yml
│ └── windows-msys-64bit-gcc-ucrt-msvcrt-golang-test.yml
├── kem
├── adapter
│ └── kem_test.go
├── schemes
│ ├── kem_benchmark_test.go
│ ├── kem_test.go
│ └── schemes.go
├── mkem
│ ├── ciphertext.go
│ ├── mkem_test.go
│ └── mkem.go
├── sntrup
│ └── sntrup_test.go
├── hybrid
│ ├── LICENSE
│ └── hybrid.go
├── util
│ └── split_prf.go
├── mlkem768
│ └── mlkem768.go
├── pem
│ └── pem.go
├── xwing
│ └── xwing.go
└── interfaces.go
├── rand
├── rand_fallback.go
├── deterministic_rand_test.go
├── rand_test.go
├── deterministic_rand_reader.go
├── rand_reader.go
├── rand_linux.go
└── math.go
├── util
├── ctIsZero.go
├── explicitBzero.go
└── pem
│ └── pem.go
├── go.mod
├── examples
├── nike_and_cipher
│ └── main.go
└── kem_and_cipher
│ └── main.go
├── misc
└── install-debian-go-deps-by-arch.sh
└── go.sum
/nike/csidh/csidh_not_supported.go:
--------------------------------------------------------------------------------
1 | //go:build !amd64 && !arm64
2 |
3 | package csidh
4 |
5 | import "github.com/katzenpost/hpqc/nike"
6 |
7 | var NOBS_CSIDH512Scheme nike.Scheme = nil
8 |
--------------------------------------------------------------------------------
/sign/sphincsplus/sphincs_not_supported.go:
--------------------------------------------------------------------------------
1 | //go:build windows
2 |
3 | package sphincsplus
4 |
5 | import "github.com/katzenpost/hpqc/sign"
6 |
7 | func Scheme() sign.Scheme {
8 | return nil
9 | }
10 |
--------------------------------------------------------------------------------
/sign/hybrid/sphincsplus_not_supported.go:
--------------------------------------------------------------------------------
1 | //go:build windows
2 |
3 | // SPDX-FileCopyrightText: (c) 2024 David Stainton
4 | // SPDX-License-Identifier: AGPL-3.0-only
5 |
6 | package hybrid
7 |
8 | import "github.com/katzenpost/hpqc/sign"
9 |
10 | var Ed25519Sphincs sign.Scheme = nil
11 | var Ed448Sphincs sign.Scheme = nil
12 |
--------------------------------------------------------------------------------
/nike/hybrid/no_nobs_csidh.go:
--------------------------------------------------------------------------------
1 | //go:build ppc64le
2 |
3 | package hybrid
4 |
5 | import (
6 | "github.com/katzenpost/hpqc/nike"
7 | ecdh "github.com/katzenpost/hpqc/nike/x25519"
8 | "github.com/katzenpost/hpqc/rand"
9 | )
10 |
11 | var NOBS_CSIDH512X25519 nike.Scheme = &Scheme{
12 | name: "NOBS_CSIDH-X25519",
13 | first: ecdh.Scheme(rand.Reader),
14 | second: nil,
15 | }
16 |
--------------------------------------------------------------------------------
/nike/hybrid/nobs_csidh.go:
--------------------------------------------------------------------------------
1 | //go:build !ppc64le
2 |
3 | package hybrid
4 |
5 | import (
6 | "github.com/katzenpost/hpqc/nike"
7 | "github.com/katzenpost/hpqc/nike/csidh"
8 | ecdh "github.com/katzenpost/hpqc/nike/x25519"
9 | "github.com/katzenpost/hpqc/rand"
10 | )
11 |
12 | var NOBS_CSIDH512X25519 nike.Scheme = &Scheme{
13 | name: "NOBS_CSIDH-X25519",
14 | first: ecdh.Scheme(rand.Reader),
15 | second: csidh.NOBS_CSIDH512Scheme,
16 | }
17 |
--------------------------------------------------------------------------------
/hash/hash.go:
--------------------------------------------------------------------------------
1 | package hash
2 |
3 | import (
4 | "encoding"
5 |
6 | "golang.org/x/crypto/blake2b"
7 | )
8 |
9 | const HashSize = blake2b.Size256
10 |
11 | func Sum256(data []byte) [blake2b.Size256]byte {
12 | return blake2b.Sum256(data)
13 | }
14 |
15 | func Sum256From(key encoding.BinaryMarshaler) [blake2b.Size256]byte {
16 | blob, err := key.MarshalBinary()
17 | if err != nil {
18 | panic(err)
19 | }
20 | return blake2b.Sum256(blob)
21 | }
22 |
--------------------------------------------------------------------------------
/nike/diffiehellman/dh_test.go:
--------------------------------------------------------------------------------
1 | package diffiehellman
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/require"
7 | )
8 |
9 | func NoTestDHKeyLengths(t *testing.T) {
10 | s := Scheme()
11 |
12 | for i := 0; i < 1000; i++ {
13 | pubkey1, privkey1, err := s.GenerateKeyPair()
14 | require.NoError(t, err)
15 |
16 | require.Equal(t, s.PublicKeySize(), len(pubkey1.Bytes()))
17 | require.Equal(t, s.PrivateKeySize(), len(privkey1.Bytes()))
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/sign/hybrid/sphincsplus.go:
--------------------------------------------------------------------------------
1 | //go:build !windows
2 | // +build !windows
3 |
4 | // SPDX-FileCopyrightText: (c) 2024 David Stainton
5 | // SPDX-License-Identifier: AGPL-3.0-only
6 |
7 | package hybrid
8 |
9 | import (
10 | "github.com/katzenpost/circl/sign/ed448"
11 |
12 | "github.com/katzenpost/hpqc/sign/ed25519"
13 | "github.com/katzenpost/hpqc/sign/sphincsplus"
14 | )
15 |
16 | var Ed25519Sphincs = New("Ed25519 Sphincs+", ed25519.Scheme(), sphincsplus.Scheme())
17 | var Ed448Sphincs = New("Ed448-Sphincs+", ed448.Scheme(), sphincsplus.Scheme())
18 |
--------------------------------------------------------------------------------
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
1 | name: Go tests
2 | on: [push, pull_request]
3 | permissions:
4 | contents: read
5 | jobs:
6 | test:
7 | strategy:
8 | fail-fast: false
9 | matrix:
10 | os: [ubuntu-latest]
11 | arch: [amd64]
12 | go-version: ["1.23.0"]
13 | runs-on: ${{ matrix.os }}
14 | steps:
15 | - name: Checkout repository
16 | uses: actions/checkout@v4
17 | with:
18 | fetch-depth: 0
19 | - name: Install Go (from go.mod)
20 | uses: actions/setup-go@v5
21 | with:
22 | go-version-file: go.mod
23 | check-latest: true
24 | - name: Run tests
25 | run: go test -v ./...
26 |
--------------------------------------------------------------------------------
/nike/csidh/csidh_test.go:
--------------------------------------------------------------------------------
1 | //go:build amd64 || arm64
2 |
3 | package csidh
4 |
5 | import (
6 | "testing"
7 |
8 | "github.com/stretchr/testify/require"
9 | )
10 |
11 | func TestCtidhNike(t *testing.T) {
12 | scheme := NOBS_CSIDH512Scheme
13 |
14 | alicePublicKey, alicePrivateKey, err := scheme.GenerateKeyPair()
15 | require.NoError(t, err)
16 |
17 | tmp := scheme.DerivePublicKey(alicePrivateKey.(*PrivateKey))
18 | require.Equal(t, alicePublicKey.Bytes(), tmp.Bytes())
19 |
20 | bobPubKey, bobPrivKey, err := scheme.GenerateKeyPair()
21 | require.NoError(t, err)
22 |
23 | aliceS := scheme.DeriveSecret(alicePrivateKey, bobPubKey)
24 |
25 | bobS := scheme.DeriveSecret(bobPrivKey, alicePublicKey.(*PublicKey))
26 | require.Equal(t, bobS, aliceS)
27 | }
28 |
--------------------------------------------------------------------------------
/kem/adapter/kem_test.go:
--------------------------------------------------------------------------------
1 | package adapter
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/require"
7 |
8 | ecdh "github.com/katzenpost/hpqc/nike/x25519"
9 | "github.com/katzenpost/hpqc/rand"
10 | )
11 |
12 | // NOTE that there is a deterministic which covers the behavior this
13 | // NIKE to KEM adapter, located in core/crypto/kem/schemes/kem_test.go
14 |
15 | func TestNikeToKemAdapter(t *testing.T) {
16 | ecdhNike := ecdh.Scheme(rand.Reader)
17 | s := FromNIKE(ecdhNike)
18 |
19 | t.Logf("hello my name is %s", s.Name())
20 |
21 | pubkey1, privkey1, err := s.GenerateKeyPair()
22 | require.NoError(t, err)
23 |
24 | ct, ssA, err := s.Encapsulate(pubkey1)
25 | require.NoError(t, err)
26 |
27 | ssB, err := s.Decapsulate(privkey1, ct)
28 | require.NoError(t, err)
29 |
30 | require.Equal(t, ssA, ssB)
31 |
32 | t.Logf("our shared key is %x", ssA)
33 | }
34 |
--------------------------------------------------------------------------------
/nike/ctidh/ctidh1024/ctidh_test.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (C) 2022-2024 David Stainton.
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package ctidh1024
5 |
6 | import (
7 | "testing"
8 |
9 | "github.com/stretchr/testify/require"
10 |
11 | ctidh "codeberg.org/vula/highctidh/src/ctidh1024"
12 | )
13 |
14 | func TestCTIDH1024_NIKE(t *testing.T) {
15 | ctidhNike := Scheme()
16 |
17 | alicePublicKey, alicePrivateKey, err := ctidhNike.GenerateKeyPair()
18 | require.NoError(t, err)
19 |
20 | tmp := ctidh.DerivePublicKey(alicePrivateKey.(*PrivateKey).privateKey)
21 | require.Equal(t, alicePublicKey.Bytes(), tmp.Bytes())
22 |
23 | bobPubKey, bobPrivKey, err := ctidhNike.GenerateKeyPair()
24 | require.NoError(t, err)
25 |
26 | aliceS := ctidhNike.DeriveSecret(alicePrivateKey, bobPubKey)
27 |
28 | bobS := ctidh.DeriveSecret(bobPrivKey.(*PrivateKey).privateKey, alicePublicKey.(*PublicKey).publicKey)
29 | require.Equal(t, bobS, aliceS)
30 | }
31 |
--------------------------------------------------------------------------------
/nike/ctidh/ctidh2048/ctidh_test.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (C) 2022-2024 David Stainton.
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package ctidh2048
5 |
6 | import (
7 | "testing"
8 |
9 | "github.com/stretchr/testify/require"
10 |
11 | ctidh "codeberg.org/vula/highctidh/src/ctidh2048"
12 | )
13 |
14 | func TestCTIDH2048_NIKE(t *testing.T) {
15 | ctidhNike := Scheme()
16 |
17 | alicePublicKey, alicePrivateKey, err := ctidhNike.GenerateKeyPair()
18 | require.NoError(t, err)
19 |
20 | tmp := ctidh.DerivePublicKey(alicePrivateKey.(*PrivateKey).privateKey)
21 | require.Equal(t, alicePublicKey.Bytes(), tmp.Bytes())
22 |
23 | bobPubKey, bobPrivKey, err := ctidhNike.GenerateKeyPair()
24 | require.NoError(t, err)
25 |
26 | aliceS := ctidhNike.DeriveSecret(alicePrivateKey, bobPubKey)
27 |
28 | bobS := ctidh.DeriveSecret(bobPrivKey.(*PrivateKey).privateKey, alicePublicKey.(*PublicKey).publicKey)
29 | require.Equal(t, bobS, aliceS)
30 | }
31 |
--------------------------------------------------------------------------------
/nike/ctidh/ctidh511/ctidh_test.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (C) 2022-2024 David Stainton.
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package ctidh511
5 |
6 | import (
7 | "testing"
8 |
9 | "github.com/stretchr/testify/require"
10 |
11 | ctidh "codeberg.org/vula/highctidh/src/ctidh511"
12 | )
13 |
14 | func TestCTIDH511_NIKE(t *testing.T) {
15 | ctidhNike := Scheme()
16 |
17 | alicePublicKey, alicePrivateKey, err := ctidhNike.GenerateKeyPair()
18 | require.NoError(t, err)
19 |
20 | tmp := ctidh.DerivePublicKey(alicePrivateKey.(*PrivateKey).privateKey)
21 | require.Equal(t, alicePublicKey.Bytes(), tmp.Bytes())
22 |
23 | bobPubKey, bobPrivKey, err := ctidhNike.GenerateKeyPair()
24 | require.NoError(t, err)
25 |
26 | aliceS := ctidhNike.DeriveSecret(alicePrivateKey, bobPubKey)
27 |
28 | bobS := ctidh.DeriveSecret(bobPrivKey.(*PrivateKey).privateKey, alicePublicKey.(*PublicKey).publicKey)
29 | require.Equal(t, bobS, aliceS)
30 | }
31 |
--------------------------------------------------------------------------------
/nike/ctidh/ctidh512/ctidh_test.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (C) 2022-2024 David Stainton.
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package ctidh512
5 |
6 | import (
7 | "testing"
8 |
9 | "github.com/stretchr/testify/require"
10 |
11 | ctidh "codeberg.org/vula/highctidh/src/ctidh512"
12 | )
13 |
14 | func TestCTIDH512_NIKE(t *testing.T) {
15 | ctidhNike := Scheme()
16 |
17 | alicePublicKey, alicePrivateKey, err := ctidhNike.GenerateKeyPair()
18 | require.NoError(t, err)
19 |
20 | tmp := ctidh.DerivePublicKey(alicePrivateKey.(*PrivateKey).privateKey)
21 | require.Equal(t, alicePublicKey.Bytes(), tmp.Bytes())
22 |
23 | bobPubKey, bobPrivKey, err := ctidhNike.GenerateKeyPair()
24 | require.NoError(t, err)
25 |
26 | aliceS := ctidhNike.DeriveSecret(alicePrivateKey, bobPubKey)
27 |
28 | bobS := ctidh.DeriveSecret(bobPrivKey.(*PrivateKey).privateKey, alicePublicKey.(*PublicKey).publicKey)
29 | require.Equal(t, bobS, aliceS)
30 | }
31 |
--------------------------------------------------------------------------------
/rand/rand_fallback.go:
--------------------------------------------------------------------------------
1 | // rand_fallback.go - Portable fallback for non-Linux systems.
2 | // Copyright (C) 2016 Yawning Angel.
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Affero General Public License as
6 | // published by the Free Software Foundation, either version 3 of the
7 | // License, or (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Affero General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Affero General Public License
15 | // along with this program. If not, see .
16 |
17 | // +build !linux
18 |
19 | package rand
20 |
21 | import "crypto/rand"
22 |
23 | func init() {
24 | Reader = rand.Reader
25 | initWhitening()
26 | }
27 |
--------------------------------------------------------------------------------
/util/ctIsZero.go:
--------------------------------------------------------------------------------
1 | // ctIsZero.go - Constant time buffer is zero.
2 | // Copyright (C) 2017 Yawning Angel.
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Affero General Public License as
6 | // published by the Free Software Foundation, either version 3 of the
7 | // License, or (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Affero General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Affero General Public License
15 | // along with this program. If not, see .
16 |
17 | package util
18 |
19 | // CtIsZero returns true iff the buffer b is all 0x00, doing the check in
20 | // constant time.
21 | func CtIsZero(b []byte) bool {
22 | var sum byte
23 | for _, v := range b {
24 | sum |= v
25 | }
26 | return sum == 0
27 | }
28 |
--------------------------------------------------------------------------------
/util/explicitBzero.go:
--------------------------------------------------------------------------------
1 | // explicitBzero.go - Explicitly clear a memory buffer.
2 | // Copyright (C) 2017 Yawning Angel.
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Affero General Public License as
6 | // published by the Free Software Foundation, either version 3 of the
7 | // License, or (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Affero General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Affero General Public License
15 | // along with this program. If not, see .
16 |
17 | package util
18 |
19 | import "runtime"
20 |
21 | // ExplicitBzero explicitly clears out the buffer b, by filling it with 0x00
22 | // bytes.
23 | //
24 | //go:noinline
25 | func ExplicitBzero(b []byte) {
26 | for i := range b {
27 | b[i] = 0
28 | }
29 | runtime.KeepAlive(b)
30 | }
31 |
--------------------------------------------------------------------------------
/.github/workflows/macos-golang-test.yml:
--------------------------------------------------------------------------------
1 | name: MacOS Golang build and test
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ${{ matrix.OS }}
8 | strategy:
9 | matrix:
10 | OS: ["macos-14"]
11 | go-version: ["1.23.0"]
12 | fail-fast: false
13 |
14 | steps:
15 | - uses: actions/checkout@v4
16 |
17 | - name: Setup Go ${{ matrix.go-version }}
18 | uses: actions/setup-go@v5
19 | with:
20 | go-version: ${{ matrix.go-version }}
21 |
22 | - name: Display Go version
23 | shell: bash
24 | run: go version
25 |
26 | - name: Install golang dependencies
27 | shell: bash
28 | run: |
29 | export HIGHCTIDH_PORTABLE=1
30 | export CGO_ENABLED=1
31 | go get -v ./...
32 |
33 | - name: Build golang
34 | shell: bash
35 | run: |
36 | export HIGHCTIDH_PORTABLE=1
37 | export CGO_ENABLED=1
38 | go build -v ./...
39 |
40 | - name: Golang test
41 | env:
42 | CGO_LDFLAGS: -Wl,-stack_size,0x1F40000
43 | shell: bash
44 | run: |
45 | export HIGHCTIDH_PORTABLE=1
46 | export CGO_ENABLED=1
47 | go test -v ./...
48 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/katzenpost/hpqc
2 |
3 | go 1.23.0
4 |
5 | require (
6 | codeberg.org/vula/highctidh v1.0.2024092800
7 | filippo.io/edwards25519 v1.0.0
8 | filippo.io/mlkem768 v0.0.0-20240221181710-5ce91625fdc1
9 | github.com/agl/gcmsiv v0.0.0-20190418185415-e8dcd2f151dc
10 | github.com/fxamacker/cbor/v2 v2.7.0
11 | github.com/go-faster/xor v1.0.0
12 | github.com/henrydcase/nobs v0.0.0-20230313231516-25b66236df73
13 | github.com/katzenpost/chacha20 v0.0.0-20190910113340-7ce890d6a556
14 | github.com/katzenpost/chacha20poly1305 v0.0.0-20211026103954-7b6fb2fc0129
15 | github.com/katzenpost/circl v1.3.9-0.20240222183521-1cd9a34e9a0c
16 | github.com/katzenpost/sntrup4591761 v0.0.0-20231024131303-8755eb1986b8
17 | github.com/katzenpost/sphincsplus v0.0.2
18 | github.com/stretchr/testify v1.8.4
19 | gitlab.com/elixxir/crypto v0.0.9
20 | gitlab.com/xx_network/crypto v0.0.6
21 | golang.org/x/crypto v0.36.0
22 | )
23 |
24 | require (
25 | github.com/davecgh/go-spew v1.1.1 // indirect
26 | github.com/mattn/go-pointer v0.0.1 // indirect
27 | github.com/pkg/errors v0.9.1 // indirect
28 | github.com/pmezard/go-difflib v1.0.0 // indirect
29 | github.com/spf13/jwalterweatherman v1.1.0 // indirect
30 | github.com/x448/float16 v0.8.4 // indirect
31 | golang.org/x/sys v0.31.0 // indirect
32 | gopkg.in/yaml.v3 v3.0.1 // indirect
33 | )
34 |
--------------------------------------------------------------------------------
/nike/diffiehellman/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020, xx network SEZC
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are met:
5 |
6 | 1. Redistributions of source code must retain the above copyright notice, this
7 | list of conditions and the following disclaimer.
8 | 2. Redistributions in binary form must reproduce the above copyright notice,
9 | this list of conditions and the following disclaimer in the documentation and/or
10 | other materials provided with the distribution.
11 |
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22 |
--------------------------------------------------------------------------------
/nike/hybrid/ctidh.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: (c) 2024 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package hybrid
5 |
6 | import (
7 | "github.com/katzenpost/hpqc/nike"
8 | "github.com/katzenpost/hpqc/nike/ctidh/ctidh1024"
9 | "github.com/katzenpost/hpqc/nike/ctidh/ctidh2048"
10 | "github.com/katzenpost/hpqc/nike/ctidh/ctidh511"
11 | "github.com/katzenpost/hpqc/nike/ctidh/ctidh512"
12 | "github.com/katzenpost/hpqc/nike/x25519"
13 | "github.com/katzenpost/hpqc/nike/x448"
14 | "github.com/katzenpost/hpqc/rand"
15 | )
16 |
17 | var CTIDH511X25519 nike.Scheme = &Scheme{
18 | name: "CTIDH511-X25519",
19 | first: ctidh511.Scheme(),
20 | second: x25519.Scheme(rand.Reader),
21 | }
22 |
23 | var CTIDH512X25519 nike.Scheme = &Scheme{
24 | name: "CTIDH512-X25519",
25 | first: ctidh512.Scheme(),
26 | second: x25519.Scheme(rand.Reader),
27 | }
28 |
29 | var CTIDH512X448 nike.Scheme = &Scheme{
30 | name: "CTIDH512-X448",
31 | second: ctidh512.Scheme(),
32 | first: x448.Scheme(rand.Reader),
33 | }
34 |
35 | var CTIDH1024X25519 nike.Scheme = &Scheme{
36 | name: "CTIDH1024-X25519",
37 | second: ctidh1024.Scheme(),
38 | first: x25519.Scheme(rand.Reader),
39 | }
40 |
41 | var CTIDH1024X448 nike.Scheme = &Scheme{
42 | name: "CTIDH1024-X448",
43 | first: ctidh1024.Scheme(),
44 | second: x448.Scheme(rand.Reader),
45 | }
46 |
47 | var CTIDH2048X448 nike.Scheme = &Scheme{
48 | name: "CTIDH2048-X448",
49 | first: ctidh2048.Scheme(),
50 | second: x448.Scheme(rand.Reader),
51 | }
52 |
--------------------------------------------------------------------------------
/sign/schemes/schemes.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: (c) 2024 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package schemes
5 |
6 | import (
7 | "strings"
8 |
9 | "github.com/katzenpost/circl/sign/ed448"
10 | "github.com/katzenpost/circl/sign/eddilithium2"
11 | "github.com/katzenpost/circl/sign/eddilithium3"
12 |
13 | "github.com/katzenpost/hpqc/sign"
14 | "github.com/katzenpost/hpqc/sign/ed25519"
15 | "github.com/katzenpost/hpqc/sign/hybrid"
16 | "github.com/katzenpost/hpqc/sign/sphincsplus"
17 | )
18 |
19 | var potentialSchemes = [...]sign.Scheme{
20 | // post quantum
21 | sphincsplus.Scheme(),
22 |
23 | // post quantum hybrids
24 | hybrid.Ed25519Sphincs,
25 | hybrid.Ed448Sphincs,
26 | }
27 |
28 | var allSchemes = []sign.Scheme{
29 | // classical
30 | ed25519.Scheme(),
31 | ed448.Scheme(),
32 |
33 | // hybrid post quantum
34 | eddilithium2.Scheme(),
35 | eddilithium3.Scheme(),
36 | }
37 |
38 | var allSchemeNames map[string]sign.Scheme
39 |
40 | func init() {
41 | allSchemeNames = make(map[string]sign.Scheme)
42 |
43 | for _, scheme := range potentialSchemes {
44 | if scheme != nil {
45 | allSchemes = append(allSchemes, scheme)
46 | }
47 | }
48 | for _, scheme := range allSchemes {
49 | allSchemeNames[strings.ToLower(scheme.Name())] = scheme
50 | }
51 | }
52 |
53 | // ByName returns the NIKE scheme by string name.
54 | func ByName(name string) sign.Scheme {
55 | ret := allSchemeNames[strings.ToLower(name)]
56 | return ret
57 | }
58 |
59 | // All returns all signature schemes supported.
60 | func All() []sign.Scheme {
61 | a := allSchemes
62 | return a[:]
63 | }
64 |
--------------------------------------------------------------------------------
/.github/workflows/ubuntu-golang-cross-compile-test.yml:
--------------------------------------------------------------------------------
1 | name: Go cross compile
2 |
3 | on:
4 | push:
5 | pull_request:
6 | branches: [ "main" ]
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | strategy:
12 | matrix:
13 | CC: ["clang"]
14 | ARCH: ["arm32v5", "arm32v7", "i386", "arm64", "mips64", "mips64le", "mips", "mipsle", "ppc64", "ppc64le", "s390x"]
15 | go-version: [ '1.21.x', '1.24.x' ]
16 | exclude:
17 | # Exclude failing non-primary platform combinations
18 | - ARCH: "arm32v7"
19 | go-version: "1.21.x"
20 | - ARCH: "arm64"
21 | go-version: "1.21.x"
22 | - ARCH: "ppc64le"
23 | go-version: "1.21.x"
24 | - ARCH: "mipsle"
25 | go-version: "1.24.x"
26 | - ARCH: "s390x"
27 | go-version: "1.24.x"
28 | - ARCH: "mips64le"
29 | go-version: "1.21.x"
30 | fail-fast: false
31 | steps:
32 | - uses: actions/checkout@v4
33 | - name: Setup Go ${{ matrix.go-version }}
34 | uses: actions/setup-go@v5
35 | with:
36 | go-version: ${{ matrix.go-version }}
37 | - name: Display Go version
38 | run: go version
39 | - name: Install Golang deps
40 | run: sudo ./misc/install-debian-go-deps-by-arch.sh ${{ matrix.ARCH }}
41 | - name: Install Go (from go.mod)
42 | uses: actions/setup-go@v4
43 | with:
44 | go-version-file: go.mod
45 | check-latest: true
46 | - name: Run tests
47 | run: go test -v ./...
48 | env:
49 | CGO_LDFLAGS: -Wl,-z,stack-size=0x1F40000
50 |
--------------------------------------------------------------------------------
/rand/deterministic_rand_test.go:
--------------------------------------------------------------------------------
1 | // deterministic_rand_test.go - Katzenpost deterministic rand.Reader tests.
2 | // Copyright (C) 2018 David Stainton.
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Affero General Public License as
6 | // published by the Free Software Foundation, either version 3 of the
7 | // License, or (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Affero General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Affero General Public License
15 | // along with this program. If not, see .
16 |
17 | package rand
18 |
19 | import (
20 | "testing"
21 |
22 | "github.com/stretchr/testify/assert"
23 | )
24 |
25 | // XXX fix this stupid test to actually test something
26 | func TestPerm(t *testing.T) {
27 | assert := assert.New(t)
28 |
29 | skey := [32]byte{0x00}
30 | rand, err := NewDeterministicRandReader(skey[:])
31 | assert.NoError(err, "wtf")
32 | rand2, err := NewDeterministicRandReader(skey[:])
33 | assert.NoError(err, "wtf")
34 |
35 | for i := 0; i < 42; i++ {
36 | tmp := [6]byte{}
37 | tmp2 := [6]byte{}
38 | _, err = rand.Read(tmp[:])
39 | assert.NoError(err, "wtf")
40 | _, err = rand2.Read(tmp2[:])
41 | assert.NoError(err, "wtf")
42 | assert.True(tmp == tmp2)
43 | t.Logf("rand values are %x", tmp)
44 | }
45 | for i := 1; i < 42; i++ {
46 | j := rand.Int63()
47 | t.Logf("%v", j)
48 | assert.True(j >= 0)
49 | }
50 | for i := 0; i < 42; i++ {
51 | p := rand.Perm(i)
52 | t.Logf("%v %v %v", i, len(p), p)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/kem/schemes/kem_benchmark_test.go:
--------------------------------------------------------------------------------
1 | package schemes
2 |
3 | import (
4 | "bytes"
5 | "testing"
6 |
7 | "github.com/katzenpost/hpqc/kem"
8 | )
9 |
10 | func BenchmarkKeyGen(b *testing.B) {
11 | var pubkey kem.PublicKey
12 | var privkey kem.PrivateKey
13 | var err error
14 |
15 | schemes := All()
16 |
17 | for _, s := range schemes {
18 | b.Run(s.Name(), func(b *testing.B) {
19 | for i := 0; i < b.N; i++ {
20 | pubkey, privkey, err = s.GenerateKeyPair()
21 | if err != nil {
22 | b.Fatal(err)
23 | }
24 | }
25 | })
26 | }
27 |
28 | pubkey.Scheme()
29 | privkey.Scheme()
30 | }
31 |
32 | func BenchmarkEncap(b *testing.B) {
33 | schemes := All()
34 |
35 | for _, s := range schemes {
36 | pubkey, _, err := s.GenerateKeyPair()
37 | if err != nil {
38 | b.Fatal(err)
39 | }
40 |
41 | var ct []byte
42 | var ss []byte
43 |
44 | b.Run(s.Name(), func(b *testing.B) {
45 | for i := 0; i < b.N; i++ {
46 | ct, ss, err = s.Encapsulate(pubkey)
47 | if err != nil {
48 | b.Fatal(err)
49 | }
50 | }
51 | })
52 |
53 | ct2 := make([]byte, len(ct))
54 | copy(ct2, ct)
55 |
56 | ss2 := make([]byte, len(ss))
57 | copy(ss2, ss)
58 | }
59 |
60 | }
61 |
62 | func BenchmarkDecaps(b *testing.B) {
63 | schemes := All()
64 |
65 | for _, s := range schemes {
66 | pubkey, privkey, err := s.GenerateKeyPair()
67 | if err != nil {
68 | b.Fatal(err)
69 | }
70 |
71 | ct, ss, err := s.Encapsulate(pubkey)
72 | if err != nil {
73 | b.Fatal(err)
74 | }
75 |
76 | b.Run(s.Name(), func(b *testing.B) {
77 | for i := 0; i < b.N; i++ {
78 | ss2, err := s.Decapsulate(privkey, ct)
79 | if err != nil {
80 | b.Fatal(err)
81 | }
82 | if !bytes.Equal(ss, ss2) {
83 | b.Fatal("decapsulated shared secret mismatch error")
84 | }
85 | }
86 | })
87 |
88 | ct2 := make([]byte, len(ct))
89 | copy(ct2, ct)
90 |
91 | ss2 := make([]byte, len(ss))
92 | copy(ss2, ss)
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/kem/mkem/ciphertext.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (C) 2024 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | // Package mkem provides multiparty KEM construction.
5 | package mkem
6 |
7 | import (
8 | "github.com/fxamacker/cbor/v2"
9 |
10 | "github.com/katzenpost/hpqc/nike"
11 | )
12 |
13 | var (
14 | // Create reusable EncMode interface with immutable options, safe for concurrent use.
15 | ccbor cbor.EncMode
16 | )
17 |
18 | type Ciphertext struct {
19 | EphemeralPublicKey nike.PublicKey
20 | DEKCiphertexts []*[DEKSize]byte
21 | Envelope []byte
22 | }
23 |
24 | type IntermediaryCiphertext struct {
25 | EphemeralPublicKey []byte
26 | DEKCiphertexts []*[DEKSize]byte
27 | Envelope []byte
28 | }
29 |
30 | func (i *IntermediaryCiphertext) Bytes() []byte {
31 | blob, err := ccbor.Marshal(i)
32 | if err != nil {
33 | panic(err)
34 | }
35 | return blob
36 | }
37 |
38 | func (i *IntermediaryCiphertext) FromBytes(b []byte) error {
39 | err := cbor.Unmarshal(b, i)
40 | if err != nil {
41 | return err
42 | }
43 | return nil
44 | }
45 |
46 | func CiphertextFromBytes(scheme *Scheme, b []byte) (*Ciphertext, error) {
47 | ic := &IntermediaryCiphertext{}
48 | err := ic.FromBytes(b)
49 | if err != nil {
50 | return nil, err
51 | }
52 | pubkey, err := scheme.nike.UnmarshalBinaryPublicKey(ic.EphemeralPublicKey)
53 | if err != nil {
54 | return nil, err
55 | }
56 | c := &Ciphertext{
57 | EphemeralPublicKey: pubkey,
58 | DEKCiphertexts: ic.DEKCiphertexts,
59 | Envelope: ic.Envelope,
60 | }
61 | return c, nil
62 | }
63 |
64 | func (m *Ciphertext) Marshal() []byte {
65 | ic := &IntermediaryCiphertext{
66 | EphemeralPublicKey: m.EphemeralPublicKey.Bytes(),
67 | DEKCiphertexts: m.DEKCiphertexts,
68 | Envelope: m.Envelope,
69 | }
70 | return ic.Bytes()
71 | }
72 |
73 | func init() {
74 | var err error
75 | opts := cbor.CanonicalEncOptions()
76 | ccbor, err = opts.EncMode()
77 | if err != nil {
78 | panic(err)
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/sign/sphincsplus/sphincs_test.go:
--------------------------------------------------------------------------------
1 | //go:build !windows
2 |
3 | // SPDX-FileCopyrightText: (c) 2022-2024 David Stainton
4 | // SPDX-License-Identifier: AGPL-3.0-only
5 |
6 | package sphincsplus
7 |
8 | import (
9 | "testing"
10 |
11 | "github.com/stretchr/testify/require"
12 | )
13 |
14 | func TestSignatureScheme(t *testing.T) {
15 | t.Parallel()
16 |
17 | pubKey, privKey, err := Scheme().GenerateKey()
18 | require.NoError(t, err)
19 | message := []byte("i am a message")
20 | sig := Scheme().Sign(privKey, message, nil)
21 | require.True(t, Scheme().Verify(pubKey, message, sig, nil))
22 | }
23 |
24 | func TestSerialization(t *testing.T) {
25 | t.Parallel()
26 |
27 | pubKey, privKey, err := Scheme().GenerateKey()
28 | require.NoError(t, err)
29 |
30 | message := []byte("i am a message")
31 | sig := Scheme().Sign(privKey, message, nil)
32 |
33 | pubKeyBytes, err := pubKey.MarshalBinary()
34 | require.NoError(t, err)
35 |
36 | pubKey2, err := Scheme().UnmarshalBinaryPublicKey(pubKeyBytes)
37 | require.NoError(t, err)
38 |
39 | pubKey2Bytes, err := pubKey2.MarshalBinary()
40 | require.NoError(t, err)
41 | require.Equal(t, pubKey2Bytes, pubKeyBytes)
42 | require.True(t, Scheme().Verify(pubKey, message, sig, nil))
43 | }
44 |
45 | func TestSizes(t *testing.T) {
46 | t.Parallel()
47 |
48 | pubKey, privKey, err := Scheme().GenerateKey()
49 | require.NoError(t, err)
50 |
51 | message := []byte("i am a message")
52 | sig := Scheme().Sign(privKey, message, nil)
53 | require.True(t, Scheme().Verify(pubKey, message, sig, nil))
54 |
55 | privKeyBlob, err := privKey.MarshalBinary()
56 | require.NoError(t, err)
57 |
58 | pubKeyBlob, err := pubKey.MarshalBinary()
59 | require.NoError(t, err)
60 |
61 | t.Logf("privKey len %d", len(privKeyBlob))
62 | t.Logf("pubKey len %d", len(pubKeyBlob))
63 | t.Logf("sig len %d", len(sig))
64 |
65 | require.Equal(t, len(privKeyBlob), Scheme().PrivateKeySize())
66 | require.Equal(t, len(pubKeyBlob), Scheme().PublicKeySize())
67 | require.Equal(t, len(sig), Scheme().SignatureSize())
68 | }
69 |
--------------------------------------------------------------------------------
/kem/sntrup/sntrup_test.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2023 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package sntrup
5 |
6 | import (
7 | "testing"
8 |
9 | "github.com/stretchr/testify/require"
10 |
11 | "github.com/katzenpost/hpqc/util"
12 | )
13 |
14 | func TestSNTRUPKEM(t *testing.T) {
15 | s := Scheme()
16 |
17 | t.Logf("ciphertext size %d", s.CiphertextSize())
18 | t.Logf("shared key size %d", s.SharedKeySize())
19 | t.Logf("private key size %d", s.PrivateKeySize())
20 | t.Logf("public key size %d", s.PublicKeySize())
21 | t.Logf("seed size %d", s.SeedSize())
22 |
23 | pubkey1, privkey1, err := s.GenerateKeyPair()
24 | require.NoError(t, err)
25 | pubkey2, privkey2, err := s.GenerateKeyPair()
26 | require.NoError(t, err)
27 |
28 | require.False(t, pubkey1.Equal(pubkey2))
29 | require.False(t, privkey1.Equal(privkey2))
30 |
31 | pubKey1Blob, err := pubkey1.MarshalBinary()
32 | require.NoError(t, err)
33 |
34 | pubkey3, err := s.UnmarshalBinaryPublicKey(pubKey1Blob)
35 | require.NoError(t, err)
36 | require.True(t, pubkey3.Equal(pubkey1))
37 |
38 | privKey1Blob, err := privkey1.MarshalBinary()
39 | require.NoError(t, err)
40 | privkey3, err := s.UnmarshalBinaryPrivateKey(privKey1Blob)
41 | require.NoError(t, err)
42 | require.True(t, privkey3.Equal(privkey1))
43 |
44 | ct1, ss1, err := s.Encapsulate(pubkey1)
45 | require.NoError(t, err)
46 | require.False(t, util.CtIsZero(ss1))
47 | require.False(t, util.CtIsZero(ct1))
48 |
49 | ss1b, err := s.Decapsulate(privkey1, ct1)
50 | require.NoError(t, err)
51 | require.Equal(t, ss1, ss1b)
52 | t.Logf("our shared key is %x", ss1)
53 |
54 | ct2, ss2, err := s.Encapsulate(pubkey1)
55 | require.NoError(t, err)
56 | require.NotEqual(t, ct1, ct2)
57 | require.NotEqual(t, ss1, ss2)
58 |
59 | seed := make([]byte, s.SeedSize())
60 | pubkey4, privkey4 := s.DeriveKeyPair(seed)
61 | ct3, ss3, err := s.Encapsulate(pubkey4)
62 | require.NoError(t, err)
63 | ss3b, err := s.Decapsulate(privkey4, ct3)
64 | require.NoError(t, err)
65 | require.Equal(t, ss3, ss3b)
66 | }
67 |
--------------------------------------------------------------------------------
/nike/schemes/schemes.go:
--------------------------------------------------------------------------------
1 | package schemes
2 |
3 | import (
4 | "strings"
5 |
6 | "github.com/katzenpost/hpqc/nike"
7 | "github.com/katzenpost/hpqc/nike/ctidh/ctidh1024"
8 | "github.com/katzenpost/hpqc/nike/ctidh/ctidh2048"
9 | "github.com/katzenpost/hpqc/nike/ctidh/ctidh511"
10 | "github.com/katzenpost/hpqc/nike/ctidh/ctidh512"
11 | "github.com/katzenpost/hpqc/nike/hybrid"
12 | "github.com/katzenpost/hpqc/nike/x25519"
13 | "github.com/katzenpost/hpqc/nike/x448"
14 | "github.com/katzenpost/hpqc/rand"
15 | )
16 |
17 | var potentialSchemes = [...]nike.Scheme{
18 |
19 | // post quantum NIKE schemes
20 | ctidh511.Scheme(),
21 | ctidh512.Scheme(),
22 | ctidh1024.Scheme(),
23 | ctidh2048.Scheme(),
24 |
25 | // hybrid NIKE schemes
26 |
27 | // see ticket https://github.com/katzenpost/hpqc/issues/34
28 | //hybrid.CTIDH511X25519,
29 |
30 | hybrid.CTIDH512X25519,
31 | hybrid.CTIDH512X448,
32 | hybrid.CTIDH1024X25519,
33 | hybrid.CTIDH1024X448,
34 | hybrid.CTIDH2048X448,
35 |
36 | // NOBS CSIDH doesn't work on arm32
37 | // XXX TODO: deprecate and remove.
38 | hybrid.NOBS_CSIDH512X25519,
39 | }
40 |
41 | var allSchemes = []nike.Scheme{
42 |
43 | // classical NIKE schemes
44 | x25519.Scheme(rand.Reader),
45 | x448.Scheme(rand.Reader),
46 |
47 | // Classical DiffieHellman imeplementation has a bug with this ticket:
48 | // https://github.com/katzenpost/hpqc/issues/39
49 | //diffiehellman.Scheme(),
50 | }
51 |
52 | var allSchemeNames map[string]nike.Scheme
53 |
54 | func init() {
55 | allSchemeNames = make(map[string]nike.Scheme)
56 | for _, scheme := range potentialSchemes {
57 | if scheme != nil {
58 | allSchemes = append(allSchemes, scheme)
59 | }
60 | }
61 | for _, scheme := range allSchemes {
62 | allSchemeNames[strings.ToLower(scheme.Name())] = scheme
63 | }
64 | }
65 |
66 | // ByName returns the NIKE scheme by string name.
67 | func ByName(name string) nike.Scheme {
68 | return allSchemeNames[strings.ToLower(name)]
69 | }
70 |
71 | // All returns all NIKE schemes supported.
72 | func All() []nike.Scheme {
73 | a := allSchemes
74 | return a[:]
75 | }
76 |
--------------------------------------------------------------------------------
/examples/nike_and_cipher/main.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2025 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | // standalone NIKE + symmetric cipher example
5 | package main
6 |
7 | import (
8 | "bytes"
9 | "fmt"
10 |
11 | "github.com/agl/gcmsiv"
12 | "golang.org/x/crypto/blake2b"
13 |
14 | "github.com/katzenpost/hpqc/nike/schemes"
15 | "github.com/katzenpost/hpqc/rand"
16 | )
17 |
18 | func main() {
19 | // pick a NIKE scheme
20 | scheme := schemes.ByName("x25519")
21 |
22 | // Alice and Bob generate key pairs
23 | // and exchange their public keys
24 | alicePub, alicePriv, err := scheme.GenerateKeyPairFromEntropy(rand.Reader)
25 | if err != nil {
26 | panic(err)
27 | }
28 |
29 | bobPub, bobPriv, err := scheme.GenerateKeyPairFromEntropy(rand.Reader)
30 | if err != nil {
31 | panic(err)
32 | }
33 |
34 | // Alice sends a message to Bob
35 | aliceSharedSecret := scheme.DeriveSecret(alicePriv, bobPub)
36 | aliceSecretHash := blake2b.Sum256(aliceSharedSecret)
37 | aliceCipher, err := gcmsiv.NewGCMSIV(aliceSecretHash[:])
38 | if err != nil {
39 | panic(err)
40 | }
41 | // encrypt with AES-GCM-SIV:
42 | alicePlaintext := []byte("yo, what's up?")
43 | nonce := make([]byte, aliceCipher.NonceSize())
44 | _, err = rand.Reader.Read(nonce)
45 | if err != nil {
46 | panic(err)
47 | }
48 |
49 | aliceCiphertext := aliceCipher.Seal([]byte{}, nonce, alicePlaintext, []byte{})
50 |
51 | fmt.Printf("Alice's message: %s\nEncrypted as: %x\n", alicePlaintext, aliceCiphertext)
52 |
53 | // we pretend Alice sends Bob the nonce and ciphertext
54 | // Bob decrypts ciphertext from Alice
55 |
56 | bobSharedSecret := scheme.DeriveSecret(bobPriv, alicePub)
57 |
58 | // sanity check
59 | if !bytes.Equal(aliceSharedSecret, bobSharedSecret) {
60 | panic("shared secrets must be equal")
61 | }
62 |
63 | bobSecretHash := blake2b.Sum256(aliceSharedSecret)
64 | bobCipher, err := gcmsiv.NewGCMSIV(bobSecretHash[:])
65 | if err != nil {
66 | panic(err)
67 | }
68 |
69 | bobPlaintext, err := bobCipher.Open([]byte{}, nonce, aliceCiphertext, []byte{})
70 |
71 | // sanity check
72 | if !bytes.Equal(alicePlaintext, bobPlaintext) {
73 | panic("plaintexts must be equal")
74 | }
75 |
76 | fmt.Printf("Bob decrypts ciphertext into plaintext: %s\n", bobPlaintext)
77 | }
78 |
--------------------------------------------------------------------------------
/examples/kem_and_cipher/main.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2025 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | // standalone KEM + symmetric cipher example
5 | package main
6 |
7 | import (
8 | "bytes"
9 | "fmt"
10 |
11 | "github.com/agl/gcmsiv"
12 | "golang.org/x/crypto/blake2b"
13 |
14 | "github.com/katzenpost/hpqc/kem/schemes"
15 | "github.com/katzenpost/hpqc/rand"
16 | )
17 |
18 | func main() {
19 | // pick a KEM scheme
20 | scheme := schemes.ByName("Xwing")
21 |
22 | // Bob generate key pairs
23 | // and we pretend he sends his public key to Alice.
24 | // Alice doesn't need a keypair because this is
25 | // an example using a KEM and not a NIKE.
26 | bobPub, bobPriv, err := scheme.GenerateKeyPair()
27 | if err != nil {
28 | panic(err)
29 | }
30 |
31 | // Alice would like to send Bob an encrypted message
32 | aliceKEMCiphertext, aliceSharedSecret, err := scheme.Encapsulate(bobPub)
33 | if err != nil {
34 | panic(err)
35 | }
36 | aliceSecretHash := blake2b.Sum256(aliceSharedSecret)
37 | aliceCipher, err := gcmsiv.NewGCMSIV(aliceSecretHash[:])
38 | if err != nil {
39 | panic(err)
40 | }
41 | alicePlaintext := []byte("yo, what's up?")
42 | nonce := make([]byte, aliceCipher.NonceSize())
43 | _, err = rand.Reader.Read(nonce)
44 | if err != nil {
45 | panic(err)
46 | }
47 |
48 | aliceCiphertext := aliceCipher.Seal([]byte{}, nonce, alicePlaintext, []byte{})
49 |
50 | fmt.Printf("Alice's message: %s\nEncrypted as: %x\n", alicePlaintext, aliceCiphertext)
51 |
52 | // we pretend Alice sends Bob the following:
53 | // aliceKEMCiphertext, aliceCiphertext, nonce
54 |
55 | bobSharedSecret, err := scheme.Decapsulate(bobPriv, aliceKEMCiphertext)
56 | if err != nil {
57 | panic(err)
58 | }
59 | // sanity check
60 | if !bytes.Equal(aliceSharedSecret, bobSharedSecret) {
61 | panic("shared secrets must be equal")
62 | }
63 |
64 | bobSecretHash := blake2b.Sum256(aliceSharedSecret)
65 | bobCipher, err := gcmsiv.NewGCMSIV(bobSecretHash[:])
66 | if err != nil {
67 | panic(err)
68 | }
69 |
70 | bobPlaintext, err := bobCipher.Open([]byte{}, nonce, aliceCiphertext, []byte{})
71 |
72 | // sanity check
73 | if !bytes.Equal(alicePlaintext, bobPlaintext) {
74 | panic("plaintexts must be equal")
75 | }
76 |
77 | fmt.Printf("Bob decrypts ciphertext into plaintext: %s\n", bobPlaintext)
78 | }
79 |
--------------------------------------------------------------------------------
/.github/workflows/windows-msys-64bit-gcc-ucrt-msvcrt-golang-test.yml:
--------------------------------------------------------------------------------
1 | name: Windows Msys2 64bit (cygwin,msvcrt,ucrt) gcc golang build and test
2 |
3 | on: [push]
4 |
5 | jobs:
6 | windows-build-and-test-golang:
7 | runs-on: ${{ matrix.os }}
8 | strategy:
9 | matrix:
10 | OS: ["windows-2022"]
11 | CC: ["gcc"]
12 | ENVIRONMENT: ["UCRT64", "MINGW64"] # https://www.msys2.org/docs/environments/
13 | go-version: ["1.23.0"]
14 | fail-fast: false
15 | steps:
16 | - uses: actions/checkout@v4
17 |
18 | - name: Set up Msys2
19 | uses: msys2/setup-msys2@v2
20 | with:
21 | msystem: ${{ matrix.ENVIRONMENT }}
22 | install: >-
23 | base-devel
24 | mingw-w64-x86_64-toolchain
25 | mingw-w64-x86_64-pkg-config
26 | mingw-w64-x86_64-gcc
27 | mingw-w64-ucrt-x86_64-gcc
28 | mingw-w64-x86_64-go
29 | mingw-w64-ucrt-x86_64-go
30 | make
31 | git
32 | gcc
33 |
34 | - name: Setup Go ${{ matrix.go-version }}
35 | uses: actions/setup-go@v5
36 | with:
37 | go-version: ${{ matrix.go-version }}
38 |
39 | - name: Gather runtime environment
40 | shell: msys2 {0}
41 | run: |
42 | echo ${{ matrix.ENVIRONMENT }}
43 | uname -a
44 | bash --version
45 | ${{ matrix.CC }} -v
46 | go version
47 |
48 | - name: Install golang dependencies
49 | shell: msys2 {0}
50 | run: |
51 | export HIGHCTIDH_PORTABLE=1
52 | export CGO_ENABLED=1
53 | go get -v ./...
54 |
55 | - name: Build golang
56 | shell: msys2 {0}
57 | run: |
58 | export HIGHCTIDH_PORTABLE=1
59 | export CGO_ENABLED=1
60 | export GOEXPERIMENT=cgocheck2
61 | export GODEBUG=cgocheck=1
62 | export CGO_LDFLAGS="-Wl,--no-as-needed -Wl,-allow-multiple-definition"
63 | go build -v ./...
64 |
65 | - name: Golang test
66 | shell: msys2 {0}
67 | run: |
68 | export HIGHCTIDH_PORTABLE=1
69 | export CGO_ENABLED=1
70 | export GOEXPERIMENT=cgocheck2
71 | export GODEBUG=cgocheck=1
72 | export CGO_LDFLAGS="-Wl,--no-as-needed -Wl,-allow-multiple-definition"
73 | go test -v ./...
74 |
75 |
--------------------------------------------------------------------------------
/misc/install-debian-go-deps-by-arch.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e;
3 |
4 | ARCH=$1;
5 | # This BASE_PACKAGES list assumes a Docker image with golang installed
6 | BASE_PACKAGES="ca-certificates clang git make";
7 |
8 | if [ -n "$ARCH" ];
9 | then
10 | if [ "$ARCH" == "386" ] || [ "$ARCH" == "i386" ] || [ "$ARCH" == "amd64" ];
11 | then
12 | dpkg --add-architecture i386;
13 | PACKAGES="$BASE_PACKAGES libc6-i386 libc6-dev linux-libc-dev linux-libc-dev:i386 libc6-dev-i386 libc6-i386-cross libc6 libc6-dev";
14 | fi
15 |
16 | if [ "$ARCH" == "arm" ] || [ "$ARCH" == "arm32v5" ] || [ "$ARCH" == "arm32v6" ] || [ "$ARCH" == "arm32v7" ];
17 | then
18 | PACKAGES="$BASE_PACKAGES libc6-armel-cross libc6-armhf-cross libc6-dev-armel-cross libc6-dev-armhf-cross";
19 | fi
20 |
21 | if [ "$ARCH" == "arm64" ];
22 | then
23 | PACKAGES="$BASE_PACKAGES libc6-arm64-cross libc6-dev-arm64-cross";
24 | fi
25 |
26 | if [ "$ARCH" == "mips" ];
27 | then
28 | PACKAGES="$BASE_PACKAGES libc6-dev-mips-cross linux-libc-dev-mips-cross";
29 | fi
30 |
31 | if [ "$ARCH" == "mipsle" ] || [ "$ARCH" == "mipsel" ];
32 | then
33 | PACKAGES="$BASE_PACKAGES libc6-dev-mipsel-cross linux-libc-dev-mipsel-cross";
34 | fi
35 |
36 | if [ "$ARCH" == "mips64" ];
37 | then
38 | PACKAGES="$BASE_PACKAGES libc6-dev-mips64-cross linux-libc-dev-mips64-cross";
39 | fi
40 |
41 | if [ "$ARCH" == "mips64le" ] || [ "$ARCH" == "mips64el" ];
42 | then
43 | PACKAGES="$BASE_PACKAGES libc6-dev-mipsn32-mips64el-cross linux-libc-dev-mips64el-cross";
44 | fi
45 |
46 | if [ "$ARCH" == "ppc64" ];
47 | then
48 | PACKAGES="$BASE_PACKAGES libc6-dev-powerpc-ppc64-cross libc6-dev-ppc64-cross linux-libc-dev-ppc64-cross ";
49 | fi
50 |
51 | if [ "$ARCH" == "ppc64le" ];
52 | then
53 | PACKAGES="$BASE_PACKAGES libc6-dev-ppc64el-cross linux-libc-dev-ppc64el-cross";
54 | fi
55 |
56 | if [ "$ARCH" == "riscv64" ];
57 | then
58 | PACKAGES="$BASE_PACKAGES libc6-dev-riscv64-cross";
59 | fi
60 |
61 | if [ "$ARCH" == "s390x" ];
62 | then
63 | PACKAGES="$BASE_PACKAGES libc6-dev-s390x-cross";
64 | fi
65 | else
66 | echo "ARCH appears to be unset: ARCH=$ARCH";
67 | exit 1;
68 | fi
69 |
70 | apt update > /dev/null 2>&1;
71 | echo "Installing required packages for $ARCH: $PACKAGES";
72 | apt install -y --no-install-recommends $PACKAGES > /dev/null 2>&1;
73 | echo "Required packages installed";
74 |
--------------------------------------------------------------------------------
/rand/rand_test.go:
--------------------------------------------------------------------------------
1 | // rand_test.go - Random number tests.
2 | // Copyright (C) 2016 Yawning Angel.
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Affero General Public License as
6 | // published by the Free Software Foundation, either version 3 of the
7 | // License, or (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Affero General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Affero General Public License
15 | // along with this program. If not, see .
16 |
17 | package rand
18 |
19 | import (
20 | "bytes"
21 | "compress/zlib"
22 | "fmt"
23 | "io"
24 | "testing"
25 |
26 | "github.com/stretchr/testify/assert"
27 | )
28 |
29 | func ensureHighEntropy(b []byte) error {
30 | var zipBuf bytes.Buffer
31 | zipper := zlib.NewWriter(&zipBuf)
32 | if _, err := zipper.Write(b); err != nil {
33 | return err
34 | }
35 | zipper.Close()
36 |
37 | errorThresh := int(float32(len(b)) * 0.95)
38 | if zipBuf.Len()-16 < errorThresh {
39 | return fmt.Errorf("random data noticably compressed????: %v", zipBuf.Len())
40 | }
41 | return nil
42 | }
43 |
44 | func tryRandomRead(n int) error {
45 | b := make([]byte, n)
46 | rd, err := io.ReadFull(Reader, b)
47 | if err != nil {
48 | return err
49 | }
50 | if rd != len(b) {
51 | return fmt.Errorf("truncated read: %v", rd)
52 | }
53 |
54 | bCmp := make([]byte, n)
55 | _, err = io.ReadFull(Reader, bCmp)
56 | if err != nil {
57 | return err
58 | }
59 | if bytes.Equal(b, bCmp) {
60 | return fmt.Errorf("repeated calls produced identical output")
61 | }
62 |
63 | // Statistical test...
64 | return ensureHighEntropy(b[:])
65 | }
66 |
67 | func TestImprovedSyscallRand(t *testing.T) {
68 | if !usingImprovedSyscallEntropy {
69 | t.Skip("Improved (non-broken) syscall entropy not supported")
70 | }
71 |
72 | // Short read.
73 | if err := tryRandomRead(256); err != nil {
74 | t.Errorf("short: %v", err)
75 | }
76 |
77 | // Large read.
78 | if err := tryRandomRead(1024); err != nil {
79 | t.Errorf("large: %v", err)
80 | }
81 | }
82 |
83 | func TestMath(t *testing.T) {
84 | assert := assert.New(t)
85 |
86 | mrand := NewMath()
87 |
88 | // Basic statistical test.
89 | var b [1024]byte
90 | for i := range b {
91 | b[i] = byte(mrand.Intn(256))
92 | }
93 | assert.NoError(ensureHighEntropy(b[:]), "math/rand: Statistical test")
94 | }
95 |
--------------------------------------------------------------------------------
/rand/deterministic_rand_reader.go:
--------------------------------------------------------------------------------
1 | // rand.go -
2 | // Copyright (C) 2018 David Stainton.
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Affero General Public License as
6 | // published by the Free Software Foundation, either version 3 of the
7 | // License, or (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Affero General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Affero General Public License
15 | // along with this program. If not, see .
16 |
17 | package rand
18 |
19 | import (
20 | "encoding/binary"
21 | "math/rand"
22 |
23 | "github.com/katzenpost/chacha20"
24 | )
25 |
26 | // DeterministicRandReader is a random Reader whose output is a chacha20 keystream.
27 | type DeterministicRandReader struct {
28 | cipher *chacha20.Cipher
29 | key []byte
30 | }
31 |
32 | // NewDeterministicRandReader returns a DeterministicRandReader initialized with key.
33 | func NewDeterministicRandReader(key []byte) (*DeterministicRandReader, error) {
34 | var nonce [8]byte
35 | cipher, err := chacha20.New(key, nonce[:])
36 | if err != nil {
37 | return nil, err
38 | }
39 | reader := DeterministicRandReader{
40 | cipher: cipher,
41 | key: key,
42 | }
43 | return &reader, err
44 | }
45 |
46 | // Read writes the keystream into the passed byteslice and returns the number of bytes written.
47 | func (r *DeterministicRandReader) Read(data []byte) (int, error) {
48 | readLen := len(data)
49 | r.cipher.KeyStream(data)
50 | return readLen, nil
51 | }
52 |
53 | // Int63 returns a random int64 with most significant bit set to 0.
54 | func (r *DeterministicRandReader) Int63() int64 {
55 | tmp := [8]byte{}
56 | _, err := r.Read(tmp[:])
57 | if err != nil {
58 | panic(err)
59 | }
60 | // gotta chop those sign bits!
61 | tmp[7] = tmp[7] & 0x7F
62 | return int64(binary.LittleEndian.Uint64(tmp[:]))
63 | }
64 |
65 | // Seed initializes the DeterministicRandReader with nonce.
66 | func (r *DeterministicRandReader) Seed(seed int64) {
67 | var nonce [8]byte
68 | var err error
69 | count := binary.PutUvarint(nonce[:], uint64(seed))
70 | if int64(count) != seed {
71 | panic("wtf")
72 | }
73 | r.cipher, err = chacha20.New(r.key, nonce[:])
74 | if err != nil {
75 | panic(err)
76 | }
77 | }
78 |
79 | // Perm returns the shuffled slice of integers from 0 to n.
80 | func (r *DeterministicRandReader) Perm(n int) []int {
81 | p := rand.New(r)
82 | return p.Perm(n)
83 | }
84 |
--------------------------------------------------------------------------------
/rand/rand_reader.go:
--------------------------------------------------------------------------------
1 | // rand_reader.go - `crypto/rand.Reader` replacement
2 | // Copyright (C) 2016 Yawning Angel.
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Affero General Public License as
6 | // published by the Free Software Foundation, either version 3 of the
7 | // License, or (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Affero General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Affero General Public License
15 | // along with this program. If not, see .
16 |
17 | package rand
18 |
19 | import (
20 | "io"
21 |
22 | "github.com/katzenpost/hpqc/util"
23 | "golang.org/x/crypto/blake2b"
24 | )
25 |
26 | // At least as of Go 1.6.1, Go's crypto/rand does some horrific bullshit that
27 | // defeats the point of getrandom(2), namedly, it cowardly refuses to use the
28 | // syscall based entropy source if it would have blocked on the first call.
29 | //
30 | // This is rather suboptimal. The correct thing to do for something named
31 | // "crypto/rand" is to fucking BLOCK if the entropy pool isn't there and not
32 | // to pull poor quality entropy by falling back to doing blocking reads on
33 | // "/dev/urandom".
34 | //
35 | // This was brought up in https://github.com/golang/go/issues/11833
36 | // and dismissed, I think they're wrong, I'm fixing it on common systems
37 | // that I care about.
38 | //
39 | // Upstream has seen the errors of their ways and fixed this as of Go 1.9
40 | // (See: https://github.com/golang/go/issues/19274), but that isn't guaranteed
41 | // to be everywhere, and there's an advantage to whitening the output anyway.
42 |
43 | const xofEntropySize = 32
44 |
45 | var (
46 | // Reader is a replacement for crypto/rand.Reader.
47 | Reader io.Reader
48 |
49 | usingImprovedSyscallEntropy = false
50 | xofKey [xofEntropySize]byte
51 | )
52 |
53 | type nonShitRandReader struct {
54 | getentropyFn func([]byte) error
55 | }
56 |
57 | func (r *nonShitRandReader) Read(b []byte) (int, error) {
58 | blen := len(b)
59 | switch {
60 | case blen == 0:
61 | return 0, nil
62 | }
63 |
64 | // Whiten the output using BLAKE2Xb.
65 | var xofEntropy [xofEntropySize]byte
66 | xof, err := blake2b.NewXOF(uint32(len(b)), xofKey[:])
67 | if err != nil {
68 | return 0, err
69 | }
70 | defer func() {
71 | xof.Reset()
72 | util.ExplicitBzero(xofEntropy[:])
73 | }()
74 | if err := r.getentropyFn(xofEntropy[:]); err != nil {
75 | return 0, err
76 | }
77 | if _, err := xof.Write(xofEntropy[:]); err != nil {
78 | return 0, err
79 | }
80 | return xof.Read(b)
81 | }
82 |
83 | func initWhitening() {
84 | if _, err := Reader.Read(xofKey[:]); err != nil {
85 | panic("BUG: failed to initialize XOF key: " + err.Error())
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/util/pem/pem.go:
--------------------------------------------------------------------------------
1 | // pem.go - PEM file write barrier.
2 | //
3 | // Copyright (C) 2022 David Stainton.
4 | //
5 | // This program is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU Affero General Public License as
7 | // published by the Free Software Foundation, either version 3 of the
8 | // License, or (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU Affero General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU Affero General Public License
16 | // along with this program. If not, see .
17 |
18 | package pem
19 |
20 | import (
21 | "encoding/pem"
22 | "errors"
23 | "fmt"
24 | "os"
25 | "strings"
26 |
27 | "github.com/katzenpost/hpqc/util"
28 | )
29 |
30 | // KeyMaterial
31 | type KeyMaterial interface {
32 | FromBytes([]byte) error
33 |
34 | Bytes() []byte
35 |
36 | KeyType() string
37 | }
38 |
39 | func ToPEMString(key KeyMaterial) string {
40 | return string(ToPEMBytes(key))
41 | }
42 |
43 | func ToPEMBytes(key KeyMaterial) []byte {
44 | keyType := strings.ToUpper(key.KeyType())
45 | if util.CtIsZero(key.Bytes()) {
46 | panic(fmt.Sprintf("ToPEMString/%s: attempted to serialize scrubbed key", keyType))
47 | }
48 | blk := &pem.Block{
49 | Type: keyType,
50 | Bytes: key.Bytes(),
51 | }
52 | return pem.EncodeToMemory(blk)
53 | }
54 |
55 | func ToFile(f string, key KeyMaterial) error {
56 | out, err := os.OpenFile(f, os.O_WRONLY|os.O_CREATE, 0600)
57 | if err != nil {
58 | return err
59 | }
60 | outBuf := ToPEMBytes(key)
61 | writeCount, err := out.Write(outBuf)
62 | if err != nil {
63 | return err
64 | }
65 | if writeCount != len(outBuf) {
66 | return errors.New("partial write failure")
67 | }
68 | err = out.Sync()
69 | if err != nil {
70 | return err
71 | }
72 | return out.Close()
73 | }
74 |
75 | func FromPEMString(s string, key KeyMaterial) error {
76 | return FromPEMBytes([]byte(s), key)
77 | }
78 |
79 | func FromPEMBytes(b []byte, key KeyMaterial) error {
80 | keyType := strings.ToUpper(key.KeyType())
81 |
82 | blk, _ := pem.Decode(b)
83 | if blk == nil {
84 | return fmt.Errorf("failed to decode PEM data from %s PEM", keyType)
85 | }
86 | if strings.ToUpper(blk.Type) != keyType {
87 | return fmt.Errorf("attempted to decode PEM file with wrong key type %v != %v", blk.Type, keyType)
88 | }
89 | return key.FromBytes(blk.Bytes)
90 | }
91 |
92 | func FromFile(f string, key KeyMaterial) error {
93 | buf, err := os.ReadFile(f)
94 | if err != nil {
95 | return fmt.Errorf("pem.FromFile error: %s", err)
96 | }
97 | err = FromPEMBytes(buf, key)
98 | if err != nil {
99 | return fmt.Errorf("pem.FromFile failed to read from file %s, with buf len %d and err %s", f, len(buf), err)
100 | }
101 | return nil
102 | }
103 |
--------------------------------------------------------------------------------
/kem/hybrid/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019 Cloudflare. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are
5 | met:
6 |
7 | * Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 | * Neither the name of Cloudflare nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
29 | ========================================================================
30 |
31 | Copyright (c) 2009 The Go Authors. All rights reserved.
32 |
33 | Redistribution and use in source and binary forms, with or without
34 | modification, are permitted provided that the following conditions are
35 | met:
36 |
37 | * Redistributions of source code must retain the above copyright
38 | notice, this list of conditions and the following disclaimer.
39 | * Redistributions in binary form must reproduce the above
40 | copyright notice, this list of conditions and the following disclaimer
41 | in the documentation and/or other materials provided with the
42 | distribution.
43 | * Neither the name of Google Inc. nor the names of its
44 | contributors may be used to endorse or promote products derived from
45 | this software without specific prior written permission.
46 |
47 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
48 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
49 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
50 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
51 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
52 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
53 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
54 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
55 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
56 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
57 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
58 |
--------------------------------------------------------------------------------
/kem/schemes/kem_test.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (C) 2024 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package schemes
5 |
6 | import (
7 | "io/ioutil"
8 | "os"
9 | "path/filepath"
10 | "testing"
11 |
12 | "github.com/stretchr/testify/require"
13 |
14 | "github.com/katzenpost/hpqc/kem"
15 | "github.com/katzenpost/hpqc/kem/pem"
16 | "github.com/katzenpost/hpqc/rand"
17 | )
18 |
19 | func TestKEMTextUnmarshal(t *testing.T) {
20 | todo := All()
21 |
22 | dir, err := ioutil.TempDir("", "example")
23 | require.NoError(t, err)
24 |
25 | testkem := func(s kem.Scheme) {
26 |
27 | pubkey, privkey, err := s.GenerateKeyPair()
28 | require.NoError(t, err)
29 |
30 | seed := make([]byte, s.SeedSize())
31 | _, err = rand.Reader.Read(seed)
32 | require.NoError(t, err)
33 | pubkey, privkey = s.DeriveKeyPair(seed)
34 |
35 | blob1, err := pubkey.MarshalText()
36 | require.NoError(t, err)
37 |
38 | testpubkey2, err := s.UnmarshalTextPublicKey(blob1)
39 | require.NoError(t, err)
40 |
41 | blob2, err := testpubkey2.MarshalText()
42 | require.NoError(t, err)
43 |
44 | require.Equal(t, blob1, blob2)
45 |
46 | blob1, err = pubkey.MarshalBinary()
47 | require.NoError(t, err)
48 | require.Equal(t, s.PublicKeySize(), len(blob1))
49 |
50 | // test private key marshaling/unmarshaling
51 | privfile := filepath.Join(dir, "privkey.pem")
52 | err = pem.PrivateKeyToFile(privfile, privkey)
53 | require.NoError(t, err)
54 |
55 | privkey2, err := pem.FromPrivatePEMFile(privfile, s)
56 | require.NoError(t, err)
57 |
58 | blob1, err = privkey.MarshalBinary()
59 | require.NoError(t, err)
60 | require.Equal(t, s.PrivateKeySize(), len(blob1))
61 |
62 | blob2, err = privkey2.MarshalBinary()
63 | require.NoError(t, err)
64 | require.Equal(t, s.PrivateKeySize(), len(blob2))
65 |
66 | require.Equal(t, blob1, blob2)
67 | require.True(t, privkey2.Equal(privkey))
68 |
69 | err = os.Remove(privfile)
70 | require.NoError(t, err)
71 | }
72 |
73 | for _, scheme := range todo {
74 | if scheme.Name() == "DH4096_RFC3526" {
75 | t.Logf("skipping %s", scheme.Name())
76 | continue
77 | }
78 | t.Logf("testing KEM Scheme: %s", scheme.Name())
79 | t.Logf("PublicKeySize %d PrivateKeySize %d CiphertextSize %d", scheme.PublicKeySize(), scheme.PrivateKeySize(), scheme.CiphertextSize())
80 | testkem(scheme)
81 | t.Log("OK")
82 | }
83 | }
84 |
85 | func TestKEMEncapDecap(t *testing.T) {
86 | todo := All()
87 |
88 | testkem := func(s kem.Scheme) {
89 | pubkey1, privkey1, err := s.GenerateKeyPair()
90 | require.NoError(t, err)
91 |
92 | ct1, ss1, err := s.Encapsulate(pubkey1)
93 | require.NoError(t, err)
94 | require.Equal(t, s.CiphertextSize(), len(ct1))
95 | require.Equal(t, s.SharedKeySize(), len(ss1))
96 |
97 | ss2, err := s.Decapsulate(privkey1, ct1)
98 | require.NoError(t, err)
99 |
100 | require.Equal(t, ss1, ss2)
101 | }
102 |
103 | for _, scheme := range todo {
104 | t.Logf("testing KEM Scheme: %s", scheme.Name())
105 | t.Logf("PublicKeySize %d PrivateKeySize %d CiphertextSize %d", scheme.PublicKeySize(), scheme.PrivateKeySize(), scheme.CiphertextSize())
106 | testkem(scheme)
107 | t.Log("OK")
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/nike/nike.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (C) 2022-2024 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | // Package nike contains generic NIKE interfaces and many implementations.
5 | package nike
6 |
7 | import (
8 | "encoding"
9 | "io"
10 | )
11 |
12 | // Key is an interface for types encapsulating key material.
13 | type Key interface {
14 | encoding.BinaryMarshaler
15 | encoding.BinaryUnmarshaler
16 | encoding.TextMarshaler
17 | encoding.TextUnmarshaler
18 |
19 | // Reset resets the key material to all zeros.
20 | Reset()
21 |
22 | // Bytes serializes key material into a byte slice.
23 | Bytes() []byte
24 |
25 | // FromBytes loads key material from the given byte slice.
26 | FromBytes(data []byte) error
27 | }
28 |
29 | // PrivateKey is an interface for types encapsulating
30 | // private key material.
31 | type PrivateKey interface {
32 | Key
33 |
34 | Public() PublicKey
35 | }
36 |
37 | // PublicKey is an interface for types encapsulating
38 | // public key material.
39 | type PublicKey interface {
40 | Key
41 |
42 | // Blind performs a blinding operation and mutates the public
43 | // key with the blinded value.
44 | Blind(blindingFactor PrivateKey) error
45 | }
46 |
47 | // Scheme is an interface encapsulating a
48 | // non-interactive key exchange.
49 | type Scheme interface {
50 |
51 | // Name returns the name of the NIKE scheme implementation.
52 | Name() string
53 |
54 | // PublicKeySize returns the size in bytes of the public key.
55 | PublicKeySize() int
56 |
57 | // PrivateKeySize returns the size in bytes of the private key.
58 | PrivateKeySize() int
59 |
60 | // GeneratePrivateKey uses the given RNG to derive a new private key.
61 | // This can be used to deterministically generate private keys if the
62 | // entropy source is deterministic, for example an HKDF.
63 | GeneratePrivateKey(rng io.Reader) PrivateKey
64 |
65 | // GenerateKeyPair creates a new key pair.
66 | GenerateKeyPair() (PublicKey, PrivateKey, error)
67 |
68 | // GenerateKeyPairFromEntropy creates a new key pair from the given entropy source.
69 | GenerateKeyPairFromEntropy(rng io.Reader) (PublicKey, PrivateKey, error)
70 |
71 | // DeriveSecret derives a shared secret given a private key
72 | // from one party and a public key from another.
73 | DeriveSecret(PrivateKey, PublicKey) []byte
74 |
75 | // DerivePublicKey derives a public key given a private key.
76 | DerivePublicKey(PrivateKey) PublicKey
77 |
78 | // Blind performs the blinding operation against the
79 | // given group member, returning the blinded key.
80 | Blind(groupMember PublicKey, blindingFactor PrivateKey) (blindedGroupMember PublicKey)
81 |
82 | // NewEmptyPublicKey returns an uninitialized
83 | // PublicKey which is suitable to be loaded
84 | // via some serialization format via FromBytes
85 | // or FromPEMFile methods.
86 | NewEmptyPublicKey() PublicKey
87 |
88 | // NewEmptyPrivateKey returns an uninitialized
89 | // PrivateKey which is suitable to be loaded
90 | // via some serialization format via FromBytes
91 | // or FromPEMFile methods.
92 | NewEmptyPrivateKey() PrivateKey
93 |
94 | // UnmarshalBinaryPublicKey loads a public key from byte slice.
95 | UnmarshalBinaryPublicKey([]byte) (PublicKey, error)
96 |
97 | // Unmarshals a PrivateKey from the provided buffer.
98 | UnmarshalBinaryPrivateKey([]byte) (PrivateKey, error)
99 | }
100 |
--------------------------------------------------------------------------------
/nike/x25519/ecdh_test.go:
--------------------------------------------------------------------------------
1 | // ecdh.go - Adapts ecdh module to our NIKE interface.
2 | // Copyright (C) 2022 David Stainton and Yawning Angel
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Affero General Public License as
6 | // published by the Free Software Foundation, either version 3 of the
7 | // License, or (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Affero General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Affero General Public License
15 | // along with this program. If not, see .
16 |
17 | package x25519
18 |
19 | import (
20 | "testing"
21 |
22 | "github.com/stretchr/testify/assert"
23 | "github.com/stretchr/testify/require"
24 | "golang.org/x/crypto/curve25519"
25 |
26 | "github.com/katzenpost/hpqc/rand"
27 | "github.com/katzenpost/hpqc/util"
28 | )
29 |
30 | func TestEcdhNike(t *testing.T) {
31 | ecdhNike := Scheme(rand.Reader)
32 |
33 | alicePublicKey, alicePrivateKey, err := ecdhNike.GenerateKeyPair()
34 | require.NoError(t, err)
35 |
36 | tmp := alicePrivateKey.(*PrivateKey).Public()
37 | require.Equal(t, alicePublicKey.Bytes(), tmp.Bytes())
38 |
39 | bobKeypair, err := NewKeypair(rand.Reader)
40 | require.NoError(t, err)
41 |
42 | aliceS := ecdhNike.DeriveSecret(alicePrivateKey, bobKeypair.Public())
43 |
44 | bobS := Exp(alicePublicKey.Bytes(), bobKeypair.Bytes())
45 | require.Equal(t, bobS, aliceS)
46 | }
47 |
48 | func TestPrivateKey(t *testing.T) {
49 | t.Parallel()
50 | assert := assert.New(t)
51 |
52 | var shortBuffer = []byte("Short Buffer")
53 |
54 | privKey, err := NewKeypair(rand.Reader)
55 | require.NoError(t, err, "NewKeypair failed")
56 |
57 | var privKey2 PrivateKey
58 | assert.Error(privKey2.FromBytes(shortBuffer), "PrivateKey.FromBytes(short)")
59 |
60 | err = privKey2.FromBytes(privKey.Bytes())
61 | assert.NoError(err, "PrivateKey.ToBytes()->FromBytes()")
62 | assert.Equal(privKey, &privKey2, "PrivateKey.ToBytes()->FromBytes()")
63 |
64 | privKey2.Reset()
65 | assert.True(util.CtIsZero(privKey2.Bytes()), "PrivateKey.Reset()")
66 |
67 | var pubKey PublicKey
68 | assert.Error(pubKey.FromBytes(shortBuffer), "PublicKey.FromBytes(short)")
69 |
70 | err = pubKey.FromBytes(privKey.Public().Bytes())
71 | assert.NoError(err, "PrivateKey.PublicKey().Bytes->FromBytes()")
72 | assert.Equal(privKey.Public().Bytes(), pubKey.Bytes(), "PrivateKey.PublicKey().Bytes->FromBytes()")
73 | }
74 |
75 | func TestECDHOps(t *testing.T) {
76 | t.Parallel()
77 | assert := assert.New(t)
78 |
79 | aliceKeypair, err := NewKeypair(rand.Reader)
80 | require.NoError(t, err, "NewKeygen() Alice failed")
81 |
82 | var bobSk, bobPk, bobS, tmp [GroupElementLength]byte
83 | _, err = rand.Reader.Read(bobSk[:])
84 | require.NoError(t, err, "failed to generate bobSk")
85 | curve25519.ScalarBaseMult(&bobPk, &bobSk)
86 |
87 | curve25519.ScalarBaseMult(&tmp, (*[GroupElementLength]byte)(aliceKeypair))
88 | assert.Equal(aliceKeypair.Public().Bytes(), tmp[:], "ExpG() mismatch against X25519 scalar base mult")
89 |
90 | aliceS := Exp(bobPk[:], aliceKeypair[:])
91 | copy(tmp[:], aliceKeypair.Public().Bytes())
92 | curve25519.ScalarMult(&bobS, &bobSk, &tmp)
93 | assert.Equal(bobS[:], aliceS, "Exp() mismatch against X25519 scalar mult")
94 | }
95 |
--------------------------------------------------------------------------------
/kem/util/split_prf.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (C) 2023 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package util
5 |
6 | import (
7 | "github.com/go-faster/xor"
8 | "golang.org/x/crypto/blake2b"
9 | )
10 |
11 | // SplitPRF can be used with any number of KEMs
12 | // and it implement split PRF KEM combiner as:
13 | //
14 | // cct := cct1 || cct2 || cct3 || ...
15 | // return H(ss1 || cct) XOR H(ss2 || cct) XOR H(ss3 || cct)
16 | //
17 | // in order to retain IND-CCA2 security
18 | // as described in KEM Combiners https://eprint.iacr.org/2018/024.pdf
19 | // by Federico Giacon, Felix Heuer, and Bertram Poettering
20 | func SplitPRF(ss, cct [][]byte) []byte {
21 |
22 | if len(ss) != len(cct) {
23 | panic("mismatched slices")
24 | }
25 |
26 | cctcat := []byte{}
27 | for i := 0; i < len(cct); i++ {
28 | if cct[i] == nil {
29 | panic("ciphertext cannot be nil")
30 | }
31 | if len(cct[i]) == 0 {
32 | panic("ciphertext cannot be zero length")
33 | }
34 | cctcat = append(cctcat, cct[i]...)
35 | }
36 |
37 | hashes := make([][]byte, len(ss))
38 | for i := 0; i < len(ss); i++ {
39 | h, err := blake2b.New256(nil)
40 | if err != nil {
41 | panic(err)
42 | }
43 | if ss[i] == nil {
44 | panic("shared secret cannot be nil")
45 | }
46 | if len(ss[i]) == 0 {
47 | panic("shared secret cannot be zero length")
48 | }
49 | _, err = h.Write(ss[i])
50 | if err != nil {
51 | panic(err)
52 | }
53 | _, err = h.Write(cctcat)
54 | if err != nil {
55 | panic(err)
56 | }
57 | hashes[i] = h.Sum(nil)
58 | }
59 |
60 | acc := hashes[0]
61 | for i := 1; i < len(ss); i++ {
62 | out := make([]byte, 32)
63 | xor.Bytes(out, acc, hashes[i])
64 | acc = out
65 | }
66 | return acc
67 | }
68 |
69 | // PairSplitPRF is a split PRF that operates on only two KEMs.
70 | func PairSplitPRF(ss1, ss2, cct1, cct2 []byte) []byte {
71 | return SplitPRF([][]byte{ss1, ss2}, [][]byte{cct1, cct2})
72 | }
73 |
74 | // This is a simplified split PRF construction
75 | // that only works for combining two KEMs. If we
76 | // were to use this it would make our hybrid KEM combiner
77 | // NOT binary compatible with our multi KEM combiner when
78 | // it's combinging only two KEMs.
79 | // Keeping it here for posterity and just in case we only want
80 | // to combiner two KEMs we could just use this and get rid of
81 | // the other combiner. That's one possible future route to take.
82 | func nopePairSplitPRF(ss1, ss2, cct1, cct2 []byte) []byte {
83 |
84 | // implement split PRF KEM combiner as:
85 | //
86 | // func splitPRF(ss1, ss2, cct1, cct2 []byte) []byte {
87 | // cct := cct1 || cct2
88 | // return H(ss1 || cct) XOR H(ss2 || cct)
89 | // }
90 | //
91 | // Which simplifies to:
92 | //
93 | // splitPRF := PRF(ss1 || cct2) XOR PRF(ss2 || cct1)
94 | //
95 | // in order to retain IND-CCA2 security
96 | // as described in KEM Combiners
97 | // by Federico Giacon, Felix Heuer, and Bertram Poettering
98 | // https://eprint.iacr.org/2018/024.pdf
99 | //
100 |
101 | h1, err := blake2b.New256(nil)
102 | if err != nil {
103 | panic(err)
104 | }
105 | _, err = h1.Write(ss1)
106 | if err != nil {
107 | panic(err)
108 | }
109 | _, err = h1.Write(cct2)
110 | if err != nil {
111 | panic(err)
112 | }
113 | hash1 := h1.Sum(nil)
114 |
115 | h2, err := blake2b.New256(nil)
116 | if err != nil {
117 | panic(err)
118 | }
119 | _, err = h2.Write(ss2)
120 | if err != nil {
121 | panic(err)
122 | }
123 | _, err = h2.Write(cct1)
124 | if err != nil {
125 | panic(err)
126 | }
127 | hash2 := h2.Sum(nil)
128 |
129 | out := make([]byte, len(hash1))
130 | xor.Bytes(out, hash1, hash2)
131 | return out
132 | }
133 |
--------------------------------------------------------------------------------
/kem/mkem/mkem_test.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (C) 2024 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | // Package mkem provides multiparty KEM construction.
5 | package mkem
6 |
7 | import (
8 | "crypto/rand"
9 | "testing"
10 |
11 | "github.com/stretchr/testify/require"
12 |
13 | "github.com/katzenpost/hpqc/nike"
14 | "github.com/katzenpost/hpqc/nike/schemes"
15 | )
16 |
17 | func TestCiphertextMarshaling(t *testing.T) {
18 | dek := [DEKSize]byte{}
19 | _, err := rand.Reader.Read(dek[:])
20 | require.NoError(t, err)
21 | ic := &IntermediaryCiphertext{
22 | EphemeralPublicKey: []byte("hello1"),
23 | DEKCiphertexts: []*[DEKSize]byte{&dek},
24 | Envelope: []byte("hello i am ciphertext"),
25 | }
26 | blob1 := ic.Bytes()
27 |
28 | ic2 := &IntermediaryCiphertext{}
29 | err = ic2.FromBytes(blob1)
30 | require.NoError(t, err)
31 | blob2 := ic2.Bytes()
32 | require.Equal(t, blob1, blob2)
33 |
34 | ic3 := &IntermediaryCiphertext{}
35 | err = ic3.FromBytes(blob2)
36 | require.NoError(t, err)
37 | blob3 := ic3.Bytes()
38 | require.Equal(t, blob1, blob3)
39 | }
40 |
41 | func TestMKEMCorrectness(t *testing.T) {
42 | nikeScheme := schemes.ByName("CTIDH1024-X25519")
43 | s := NewScheme(nikeScheme)
44 |
45 | replica1pub, replica1priv, err := s.GenerateKeyPair()
46 | require.NoError(t, err)
47 |
48 | replica2pub, replica2priv, err := s.GenerateKeyPair()
49 | require.NoError(t, err)
50 |
51 | secret := make([]byte, 32)
52 | _, err = rand.Reader.Read(secret)
53 | require.NoError(t, err)
54 |
55 | _, ciphertext := s.Encapsulate([]nike.PublicKey{replica1pub, replica2pub}, secret)
56 |
57 | secret1, err := s.Decapsulate(replica1priv, ciphertext)
58 | require.NoError(t, err)
59 |
60 | require.Equal(t, secret, secret1)
61 |
62 | secret2, err := s.Decapsulate(replica2priv, ciphertext)
63 | require.NoError(t, err)
64 |
65 | require.Equal(t, secret, secret2)
66 | }
67 |
68 | func TestMKEMProtocol(t *testing.T) {
69 | nikeScheme := schemes.ByName("CTIDH1024-X25519")
70 | s := NewScheme(nikeScheme)
71 |
72 | // replicas create their keys and publish them
73 | replica0pub, replica0priv, err := s.GenerateKeyPair()
74 | require.NoError(t, err)
75 | replica1pub, replica1priv, err := s.GenerateKeyPair()
76 | require.NoError(t, err)
77 |
78 | // client to replica
79 | request := make([]byte, 31)
80 | _, err = rand.Reader.Read(request)
81 | require.NoError(t, err)
82 | privKey0, envelope := s.Encapsulate([]nike.PublicKey{replica0pub, replica1pub}, request)
83 |
84 | ct0 := &Ciphertext{
85 | EphemeralPublicKey: envelope.EphemeralPublicKey,
86 | DEKCiphertexts: []*[DEKSize]byte{envelope.DEKCiphertexts[0]},
87 | Envelope: envelope.Envelope,
88 | }
89 |
90 | ct1 := &Ciphertext{
91 | EphemeralPublicKey: envelope.EphemeralPublicKey,
92 | DEKCiphertexts: []*[DEKSize]byte{envelope.DEKCiphertexts[1]},
93 | Envelope: envelope.Envelope,
94 | }
95 |
96 | // replica0 decrypts message from client
97 | request0, err := s.Decapsulate(replica0priv, ct0)
98 | require.NoError(t, err)
99 | require.Equal(t, request0, request)
100 | // XXX require.Equal(t, len(ct0.DEKCiphertexts[0]), DEKSize)
101 |
102 | // replica1 decrypts message from client
103 | request1, err := s.Decapsulate(replica1priv, ct1)
104 | require.NoError(t, err)
105 | require.Equal(t, request1, request)
106 |
107 | replyPayload := []byte("hello")
108 | reply0 := s.EnvelopeReply(replica0priv, envelope.EphemeralPublicKey, replyPayload)
109 |
110 | // client decrypts reply from replica
111 | plaintext, err := s.DecryptEnvelope(privKey0, replica0pub, reply0.Envelope)
112 | require.NoError(t, err)
113 |
114 | require.Equal(t, replyPayload, plaintext)
115 | }
116 |
--------------------------------------------------------------------------------
/rand/rand_linux.go:
--------------------------------------------------------------------------------
1 | // rand_linux.go - Linux getentropy() based on getrandom().
2 | // Copyright (C) 2016 Yawning Angel.
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Affero General Public License as
6 | // published by the Free Software Foundation, either version 3 of the
7 | // License, or (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Affero General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Affero General Public License
15 | // along with this program. If not, see .
16 |
17 | package rand
18 |
19 | import (
20 | "bytes"
21 | "crypto/rand"
22 | "os"
23 | "runtime"
24 | "strconv"
25 | "syscall"
26 | "time"
27 | "unsafe"
28 | )
29 |
30 | var getrandomTrap uintptr
31 |
32 | // Mimic OpenBSD's getentropy semantics.
33 | //
34 | // This means:
35 | // * BLOCK like god intended it, if the system entropy source isn't
36 | // initialized.
37 | // * Don't ever return truncated reads, even if signal handlers are involved.
38 | // * Reject reads over 256 bytes long.
39 | func getentropy(b []byte) error {
40 | if len(b) <= 256 {
41 | var buf, buflen, flags uintptr
42 | buf = uintptr(unsafe.Pointer(&b[0]))
43 | buflen = uintptr(len(b))
44 | flags = 0
45 |
46 | r1, _, err := syscall.Syscall(getrandomTrap, buf, buflen, flags)
47 | if err != 0 {
48 | return err
49 | }
50 | if r1 == buflen {
51 | return nil
52 | }
53 | }
54 |
55 | return syscall.EIO
56 | }
57 |
58 | func waitOnUrandomSanity() error {
59 | for {
60 | // Use the /proc interface to query the entropy estimate.
61 | buf, err := os.ReadFile("/proc/sys/kernel/random/entropy_avail")
62 | if err != nil {
63 | return err
64 | }
65 | entropy, err := strconv.ParseInt(string(bytes.TrimSpace(buf)), 10, 0)
66 | if err != nil {
67 | return err
68 | }
69 |
70 | // The kernel considers an entropy pool initialized if it ever
71 | // exceeds 128 bits of entropy. Since we can't tell if this has
72 | // happened in the past for the nonblocking pool, wait till we
73 | // see the threshold has been exceeded.
74 | if entropy > 128 {
75 | return nil
76 | }
77 |
78 | // Don't busy wait.
79 | time.Sleep(1 * time.Second)
80 | }
81 | }
82 |
83 | // Detect support for getrandom(2).
84 | func initGetrandom() error {
85 | switch runtime.GOARCH {
86 | case "amd64":
87 | getrandomTrap = 318
88 | case "386":
89 | getrandomTrap = 355
90 | case "arm":
91 | getrandomTrap = 384
92 | case "arm64":
93 | getrandomTrap = 278
94 | default:
95 | // Your platform is the most special snowflake of them all.
96 | return syscall.ENOSYS
97 | }
98 |
99 | var err error
100 | var tmp [1]byte
101 | for {
102 | err = getentropy(tmp[:])
103 | switch err {
104 | case nil:
105 | return nil
106 | case syscall.EINTR:
107 | // Interrupted by a signal handler while waiting for the entropy
108 | // pool to initialize, try again.
109 | default:
110 | return err
111 | }
112 | }
113 | }
114 |
115 | func init() {
116 | if err := initGetrandom(); err == nil {
117 | // getrandom(2) appears to work, and is initialized.
118 | usingImprovedSyscallEntropy = true
119 | Reader = &nonShitRandReader{getentropy}
120 | } else {
121 | // The system is likely older than Linux 3.17, which while
122 | // prehistoric, is still used on things.
123 | //
124 | // Wait till the system entropy pool is sufficiently initialized,
125 | // such that crypto/rand.Reader returns quality results.
126 | if err = waitOnUrandomSanity(); err != nil {
127 | panic("rand: failed to get a sane /dev/urandom: " + err.Error())
128 | }
129 | Reader = rand.Reader
130 | }
131 | initWhitening()
132 | }
133 |
--------------------------------------------------------------------------------
/kem/mkem/mkem.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (C) 2024 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | // Package mkem provides multiparty KEM construction.
5 | package mkem
6 |
7 | import (
8 | "crypto/cipher"
9 | "crypto/rand"
10 | "errors"
11 |
12 | "github.com/katzenpost/chacha20poly1305"
13 | "github.com/katzenpost/hpqc/hash"
14 | "github.com/katzenpost/hpqc/nike"
15 | )
16 |
17 | // DEKSize indicates the size in bytes of the DEK
18 | // within our Ciphertext type in it's DEKCiphertexts field.
19 | const DEKSize = 60
20 |
21 | // Scheme is an MKEM scheme.
22 | type Scheme struct {
23 | nike nike.Scheme
24 | }
25 |
26 | func NewScheme(scheme nike.Scheme) *Scheme {
27 | return &Scheme{
28 | nike: scheme,
29 | }
30 | }
31 |
32 | func (s *Scheme) GenerateKeyPair() (nike.PublicKey, nike.PrivateKey, error) {
33 | pubkey, privkey, err := s.nike.GenerateKeyPair()
34 | if err != nil {
35 | return nil, nil, err
36 | }
37 | return pubkey, privkey, nil
38 | }
39 |
40 | func (s *Scheme) createCipher(key []byte) cipher.AEAD {
41 | aead, err := chacha20poly1305.New(key)
42 | if err != nil {
43 | panic(err)
44 | }
45 | return aead
46 | }
47 |
48 | func (s *Scheme) encrypt(key []byte, plaintext []byte) []byte {
49 | aead := s.createCipher(key)
50 | nonce := make([]byte, aead.NonceSize())
51 | _, err := rand.Reader.Read(nonce)
52 | if err != nil {
53 | panic(err)
54 | }
55 | return aead.Seal(nonce, nonce, plaintext, nil)
56 | }
57 |
58 | func (s *Scheme) decrypt(key []byte, ciphertext []byte) ([]byte, error) {
59 | aead := s.createCipher(key)
60 | nonce := ciphertext[:aead.NonceSize()]
61 | ciphertext = ciphertext[aead.NonceSize():]
62 | return aead.Open(nil, nonce, ciphertext, nil)
63 | }
64 |
65 | func (s *Scheme) EnvelopeReply(privkey nike.PrivateKey, pubkey nike.PublicKey, plaintext []byte) *Ciphertext {
66 | secret := hash.Sum256(s.nike.DeriveSecret(privkey, pubkey))
67 | ciphertext := s.encrypt(secret[:], plaintext)
68 | c := &Ciphertext{
69 | EphemeralPublicKey: pubkey,
70 | DEKCiphertexts: nil,
71 | Envelope: ciphertext,
72 | }
73 | return c
74 | }
75 |
76 | func (s *Scheme) DecryptEnvelope(privkey nike.PrivateKey, pubkey nike.PublicKey, envelope []byte) ([]byte, error) {
77 | secret := hash.Sum256(s.nike.DeriveSecret(privkey, pubkey))
78 | plaintext, err := s.decrypt(secret[:], envelope)
79 | if err != nil {
80 | return nil, err
81 | }
82 | return plaintext, nil
83 | }
84 |
85 | func (s *Scheme) Encapsulate(keys []nike.PublicKey, payload []byte) (nike.PrivateKey, *Ciphertext) {
86 | ephPub, ephPriv, err := s.nike.GenerateKeyPair()
87 | if err != nil {
88 | panic(err)
89 | }
90 |
91 | secrets := make([][hash.HashSize]byte, len(keys))
92 | for i := 0; i < len(keys); i++ {
93 | secrets[i] = hash.Sum256(s.nike.DeriveSecret(ephPriv, keys[i]))
94 | }
95 |
96 | msgKey := make([]byte, 32)
97 | _, err = rand.Reader.Read(msgKey)
98 | if err != nil {
99 | panic(err)
100 | }
101 | ciphertext := s.encrypt(msgKey, payload)
102 |
103 | outCiphertexts := make([]*[DEKSize]byte, len(secrets))
104 | for i := 0; i < len(secrets); i++ {
105 | outCiphertexts[i] = &[DEKSize]byte{}
106 | copy(outCiphertexts[i][:], s.encrypt(secrets[i][:], msgKey))
107 | if len(outCiphertexts[i]) != DEKSize {
108 | panic("invalid ciphertext size")
109 | }
110 | }
111 |
112 | c := &Ciphertext{
113 | EphemeralPublicKey: ephPub,
114 | DEKCiphertexts: outCiphertexts,
115 | Envelope: ciphertext,
116 | }
117 | return ephPriv, c
118 | }
119 |
120 | func (s *Scheme) Decapsulate(privkey nike.PrivateKey, ciphertext *Ciphertext) ([]byte, error) {
121 | ephSecret := hash.Sum256(s.nike.DeriveSecret(privkey, ciphertext.EphemeralPublicKey))
122 | for i := 0; i < len(ciphertext.DEKCiphertexts); i++ {
123 | msgKey, err := s.decrypt(ephSecret[:], ciphertext.DEKCiphertexts[i][:])
124 | if err != nil {
125 | continue
126 | }
127 | return s.decrypt(msgKey, ciphertext.Envelope)
128 | }
129 | return nil, errors.New("failed to trial decrypt")
130 | }
131 |
--------------------------------------------------------------------------------
/kem/mlkem768/mlkem768.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2024 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | // Package mlkem768 provides a KEM wrapper that uses our KEM interfaces.
5 | package mlkem768
6 |
7 | import (
8 | "crypto/hmac"
9 | "errors"
10 |
11 | "filippo.io/mlkem768"
12 |
13 | "github.com/katzenpost/hpqc/kem"
14 | "github.com/katzenpost/hpqc/kem/pem"
15 | )
16 |
17 | const (
18 | SeedSize = 64
19 | SharedKeySize = mlkem768.SharedKeySize
20 | CiphertextSize = mlkem768.CiphertextSize
21 | PublicKeySize = mlkem768.EncapsulationKeySize
22 | PrivateKeySize = PublicKeySize + mlkem768.DecapsulationKeySize
23 | )
24 |
25 | // tell the type checker that we obey these interfaces
26 | var _ kem.Scheme = (*scheme)(nil)
27 | var _ kem.PublicKey = (*PublicKey)(nil)
28 | var _ kem.PrivateKey = (*PrivateKey)(nil)
29 |
30 | var sch kem.Scheme = &scheme{}
31 |
32 | // Scheme returns a KEM interface.
33 | func Scheme() kem.Scheme { return sch }
34 |
35 | type PublicKey struct {
36 | scheme *scheme
37 | encapKey []byte
38 | }
39 |
40 | func (p *PublicKey) Scheme() kem.Scheme {
41 | return p.scheme
42 | }
43 |
44 | func (p *PublicKey) MarshalText() (text []byte, err error) {
45 | return pem.ToPublicPEMBytes(p), nil
46 | }
47 |
48 | func (p *PublicKey) MarshalBinary() ([]byte, error) {
49 | return p.encapKey, nil
50 | }
51 |
52 | func (p *PublicKey) Equal(pubkey kem.PublicKey) bool {
53 | if pubkey.(*PublicKey).scheme != p.scheme {
54 | return false
55 | }
56 | return hmac.Equal(pubkey.(*PublicKey).encapKey, p.encapKey)
57 | }
58 |
59 | type PrivateKey struct {
60 | scheme *scheme
61 | decapKey []byte
62 | encapKey []byte
63 | }
64 |
65 | func (p *PrivateKey) Scheme() kem.Scheme {
66 | return p.scheme
67 | }
68 |
69 | func (p *PrivateKey) MarshalBinary() ([]byte, error) {
70 | return append(p.decapKey, p.encapKey...), nil
71 | }
72 |
73 | func (p *PrivateKey) Equal(privkey kem.PrivateKey) bool {
74 | if privkey.(*PrivateKey).scheme != p.scheme {
75 | return false
76 | }
77 | return hmac.Equal(privkey.(*PrivateKey).decapKey, p.decapKey)
78 | }
79 |
80 | func (p *PrivateKey) Public() kem.PublicKey {
81 | return &PublicKey{
82 | encapKey: p.encapKey,
83 | scheme: p.scheme,
84 | }
85 | }
86 |
87 | type scheme struct {
88 | }
89 |
90 | func (s *scheme) Name() string {
91 | return "MLKEM768"
92 | }
93 |
94 | func (a *scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) {
95 | encapKey, decapKey, err := mlkem768.GenerateKey()
96 | if err != nil {
97 | return nil, nil, err
98 | }
99 | return &PublicKey{
100 | scheme: a,
101 | encapKey: encapKey,
102 | }, &PrivateKey{
103 | scheme: a,
104 | encapKey: encapKey,
105 | decapKey: decapKey,
106 | }, nil
107 | }
108 |
109 | func (s *scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) {
110 | return mlkem768.Encapsulate(pk.(*PublicKey).encapKey)
111 | }
112 |
113 | func (s *scheme) Decapsulate(myPrivkey kem.PrivateKey, ct []byte) ([]byte, error) {
114 | return mlkem768.Decapsulate(myPrivkey.(*PrivateKey).decapKey, ct)
115 | }
116 |
117 | func (s *scheme) UnmarshalBinaryPublicKey(b []byte) (kem.PublicKey, error) {
118 | if len(b) != PublicKeySize {
119 | return nil, errors.New("wrong key size")
120 | }
121 | return &PublicKey{
122 | scheme: s,
123 | encapKey: b,
124 | }, nil
125 | }
126 |
127 | func (s *scheme) UnmarshalBinaryPrivateKey(b []byte) (kem.PrivateKey, error) {
128 | if len(b) != PrivateKeySize {
129 | return nil, errors.New("wrong key size")
130 | }
131 | return &PrivateKey{
132 | scheme: s,
133 | decapKey: b[:mlkem768.DecapsulationKeySize],
134 | encapKey: b[mlkem768.DecapsulationKeySize:],
135 | }, nil
136 | }
137 |
138 | func (s *scheme) UnmarshalTextPublicKey(text []byte) (kem.PublicKey, error) {
139 | return pem.FromPublicPEMBytes(text, s)
140 | }
141 |
142 | func (s *scheme) UnmarshalTextPrivateKey(text []byte) (kem.PrivateKey, error) {
143 | return pem.FromPrivatePEMBytes(text, s)
144 | }
145 |
146 | func (s *scheme) CiphertextSize() int {
147 | return CiphertextSize
148 | }
149 |
150 | func (s *scheme) SharedKeySize() int {
151 | return SharedKeySize
152 | }
153 |
154 | func (s *scheme) PrivateKeySize() int {
155 | return PrivateKeySize
156 | }
157 |
158 | func (s *scheme) PublicKeySize() int {
159 | return PublicKeySize
160 | }
161 |
162 | func (s *scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) {
163 | if len(seed) != SeedSize {
164 | panic(kem.ErrSeedSize)
165 | }
166 | encapKey, decapKey, err := mlkem768.NewKeyFromSeed(seed)
167 | if err != nil {
168 | panic(err)
169 | }
170 | return &PublicKey{
171 | scheme: s,
172 | encapKey: encapKey,
173 | }, &PrivateKey{
174 | scheme: s,
175 | encapKey: encapKey,
176 | decapKey: decapKey,
177 | }
178 | }
179 |
180 | func (s *scheme) SeedSize() int {
181 | return SeedSize
182 | }
183 |
--------------------------------------------------------------------------------
/kem/pem/pem.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (c) 2023-2024 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package pem
5 |
6 | import (
7 | "encoding/pem"
8 | "errors"
9 | "fmt"
10 | "os"
11 | "strings"
12 |
13 | "github.com/katzenpost/hpqc/kem"
14 | "github.com/katzenpost/hpqc/util"
15 | )
16 |
17 | func ToPublicPEMString(key kem.PublicKey) string {
18 | return string(ToPublicPEMBytes(key))
19 | }
20 |
21 | func ToPublicPEMBytes(key kem.PublicKey) []byte {
22 | keyType := fmt.Sprintf("%s PUBLIC KEY", strings.ToUpper(key.Scheme().Name()))
23 | blob, err := key.MarshalBinary()
24 | if err != nil {
25 | panic(err)
26 | }
27 | if util.CtIsZero(blob) {
28 | panic(fmt.Sprintf("ToPEMString/%s: attempted to serialize scrubbed key", keyType))
29 | }
30 | blk := &pem.Block{
31 | Type: keyType,
32 | Bytes: blob,
33 | }
34 | return pem.EncodeToMemory(blk)
35 | }
36 |
37 | func PublicKeyToFile(f string, key kem.PublicKey) error {
38 | out, err := os.OpenFile(f, os.O_WRONLY|os.O_CREATE, 0600)
39 | if err != nil {
40 | return err
41 | }
42 | outBuf := ToPublicPEMBytes(key)
43 | writeCount, err := out.Write(outBuf)
44 | if err != nil {
45 | return err
46 | }
47 | if writeCount != len(outBuf) {
48 | return errors.New("partial write failure")
49 | }
50 | err = out.Sync()
51 | if err != nil {
52 | return err
53 | }
54 | return out.Close()
55 | }
56 |
57 | func FromPublicPEMString(s string, scheme kem.Scheme) (kem.PublicKey, error) {
58 | return FromPublicPEMBytes([]byte(s), scheme)
59 | }
60 |
61 | func FromPublicPEMBytes(b []byte, scheme kem.Scheme) (kem.PublicKey, error) {
62 | keyType := fmt.Sprintf("%s PUBLIC KEY", strings.ToUpper(scheme.Name()))
63 | blk, _ := pem.Decode(b)
64 | if blk == nil {
65 | return nil, fmt.Errorf("failed to decode PEM data from %s PEM", keyType)
66 | }
67 | if strings.ToUpper(blk.Type) != keyType {
68 | return nil, fmt.Errorf("attempted to decode PEM file with wrong key type %v != %v", blk.Type, keyType)
69 | }
70 | return scheme.UnmarshalBinaryPublicKey(blk.Bytes)
71 | }
72 |
73 | func FromPublicPEMToBytes(b []byte, scheme kem.Scheme) ([]byte, error) {
74 | keyType := fmt.Sprintf("%s PUBLIC KEY", strings.ToUpper(scheme.Name()))
75 | blk, _ := pem.Decode(b)
76 | if blk == nil {
77 | return nil, fmt.Errorf("failed to decode PEM data from %s PEM", keyType)
78 | }
79 | if strings.ToUpper(blk.Type) != keyType {
80 | return nil, fmt.Errorf("attempted to decode PEM file with wrong key type %v != %v", blk.Type, keyType)
81 | }
82 | return blk.Bytes, nil
83 | }
84 |
85 | func FromPublicPEMFile(f string, scheme kem.Scheme) (kem.PublicKey, error) {
86 | buf, err := os.ReadFile(f)
87 | if err != nil {
88 | return nil, fmt.Errorf("pem.FromFile error: %s", err)
89 | }
90 | return FromPublicPEMBytes(buf, scheme)
91 | }
92 |
93 | // private key
94 |
95 | func ToPrivatePEMString(key kem.PrivateKey) string {
96 | return string(ToPrivatePEMBytes(key))
97 | }
98 |
99 | func ToPrivatePEMBytes(key kem.PrivateKey) []byte {
100 | keyType := fmt.Sprintf("%s PRIVATE KEY", strings.ToUpper(key.Scheme().Name()))
101 | blob, err := key.MarshalBinary()
102 | if err != nil {
103 | panic(err)
104 | }
105 | if util.CtIsZero(blob) {
106 | panic(fmt.Sprintf("ToPEMString/%s: attempted to serialize scrubbed key", keyType))
107 | }
108 | blk := &pem.Block{
109 | Type: keyType,
110 | Bytes: blob,
111 | }
112 | return pem.EncodeToMemory(blk)
113 | }
114 |
115 | func PrivateKeyToFile(f string, key kem.PrivateKey) error {
116 | out, err := os.OpenFile(f, os.O_WRONLY|os.O_CREATE, 0600)
117 | if err != nil {
118 | return err
119 | }
120 | outBuf := ToPrivatePEMBytes(key)
121 | writeCount, err := out.Write(outBuf)
122 | if err != nil {
123 | return err
124 | }
125 | if writeCount != len(outBuf) {
126 | return errors.New("partial write failure")
127 | }
128 | err = out.Sync()
129 | if err != nil {
130 | return err
131 | }
132 | return out.Close()
133 | }
134 |
135 | func FromPrivatePEMString(s string, scheme kem.Scheme) (kem.PrivateKey, error) {
136 | return FromPrivatePEMBytes([]byte(s), scheme)
137 | }
138 |
139 | func FromPrivatePEMBytes(b []byte, scheme kem.Scheme) (kem.PrivateKey, error) {
140 | keyType := fmt.Sprintf("%s PRIVATE KEY", strings.ToUpper(scheme.Name()))
141 | blk, _ := pem.Decode(b)
142 | if blk == nil {
143 | return nil, fmt.Errorf("failed to decode PEM data from %s PEM", keyType)
144 | }
145 | if strings.ToUpper(blk.Type) != keyType {
146 | return nil, fmt.Errorf("attempted to decode PEM file with wrong key type %v != %v", blk.Type, keyType)
147 | }
148 | return scheme.UnmarshalBinaryPrivateKey(blk.Bytes)
149 | }
150 |
151 | func FromPrivatePEMFile(f string, scheme kem.Scheme) (kem.PrivateKey, error) {
152 | buf, err := os.ReadFile(f)
153 | if err != nil {
154 | return nil, fmt.Errorf("pem.FromFile error: %s", err)
155 | }
156 | return FromPrivatePEMBytes(buf, scheme)
157 | }
158 |
--------------------------------------------------------------------------------
/kem/xwing/xwing.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2024 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | // Package xwing provides the xwing KEM using a KEM wrapper
5 | // so that it obeys our KEM interfaces for Scheme, PrivateKey, PublicKey.
6 | package xwing
7 |
8 | import (
9 | "crypto/hmac"
10 | "errors"
11 |
12 | "filippo.io/mlkem768/xwing"
13 |
14 | "github.com/katzenpost/hpqc/kem"
15 | "github.com/katzenpost/hpqc/kem/pem"
16 | )
17 |
18 | const (
19 | KeySeedSize = xwing.SeedSize
20 | SharedKeySize = xwing.SharedKeySize
21 | CiphertextSize = xwing.CiphertextSize
22 | PublicKeySize = xwing.EncapsulationKeySize
23 | PrivateKeySize = PublicKeySize + xwing.DecapsulationKeySize
24 | )
25 |
26 | // tell the type checker that we obey these interfaces
27 | var _ kem.Scheme = (*scheme)(nil)
28 | var _ kem.PublicKey = (*PublicKey)(nil)
29 | var _ kem.PrivateKey = (*PrivateKey)(nil)
30 |
31 | var sch kem.Scheme = &scheme{}
32 |
33 | // Scheme returns a KEM interface.
34 | func Scheme() kem.Scheme { return sch }
35 |
36 | type PublicKey struct {
37 | scheme *scheme
38 | encapKey []byte
39 | }
40 |
41 | func (p *PublicKey) Scheme() kem.Scheme {
42 | return p.scheme
43 | }
44 |
45 | func (p *PublicKey) MarshalText() (text []byte, err error) {
46 | return pem.ToPublicPEMBytes(p), nil
47 | }
48 |
49 | func (p *PublicKey) MarshalBinary() ([]byte, error) {
50 | return p.encapKey, nil
51 | }
52 |
53 | func (p *PublicKey) Equal(pubkey kem.PublicKey) bool {
54 | if pubkey.(*PublicKey).scheme != p.scheme {
55 | return false
56 | }
57 | return hmac.Equal(pubkey.(*PublicKey).encapKey, p.encapKey)
58 | }
59 |
60 | type PrivateKey struct {
61 | scheme *scheme
62 | decapKey []byte
63 | encapKey []byte
64 | }
65 |
66 | func (p *PrivateKey) Scheme() kem.Scheme {
67 | return p.scheme
68 | }
69 |
70 | func (p *PrivateKey) MarshalBinary() ([]byte, error) {
71 | return append(p.decapKey, p.encapKey...), nil
72 | }
73 |
74 | func (p *PrivateKey) Equal(privkey kem.PrivateKey) bool {
75 | if privkey.(*PrivateKey).scheme != p.scheme {
76 | return false
77 | }
78 | return hmac.Equal(privkey.(*PrivateKey).decapKey, p.decapKey)
79 | }
80 |
81 | func (p *PrivateKey) Public() kem.PublicKey {
82 | return &PublicKey{
83 | encapKey: p.encapKey,
84 | scheme: p.scheme,
85 | }
86 | }
87 |
88 | type scheme struct {
89 | }
90 |
91 | func (s *scheme) Name() string {
92 | return "XWING"
93 | }
94 |
95 | func (a *scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) {
96 | encapKey, decapKey, err := xwing.GenerateKey()
97 | if err != nil {
98 | return nil, nil, err
99 | }
100 | return &PublicKey{
101 | scheme: a,
102 | encapKey: encapKey,
103 | }, &PrivateKey{
104 | scheme: a,
105 | encapKey: encapKey,
106 | decapKey: decapKey,
107 | }, nil
108 | }
109 |
110 | func (s *scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) {
111 | return xwing.Encapsulate(pk.(*PublicKey).encapKey)
112 | }
113 |
114 | func (s *scheme) Decapsulate(myPrivkey kem.PrivateKey, ct []byte) ([]byte, error) {
115 | return xwing.Decapsulate(myPrivkey.(*PrivateKey).decapKey, ct)
116 | }
117 |
118 | func (s *scheme) UnmarshalBinaryPublicKey(b []byte) (kem.PublicKey, error) {
119 | if len(b) != PublicKeySize {
120 | return nil, errors.New("wrong key size")
121 | }
122 | return &PublicKey{
123 | scheme: s,
124 | encapKey: b,
125 | }, nil
126 | }
127 |
128 | func (s *scheme) UnmarshalBinaryPrivateKey(b []byte) (kem.PrivateKey, error) {
129 | if len(b) != PrivateKeySize {
130 | return nil, errors.New("wrong key size")
131 | }
132 | return &PrivateKey{
133 | scheme: s,
134 | decapKey: b[:xwing.DecapsulationKeySize],
135 | encapKey: b[xwing.DecapsulationKeySize:],
136 | }, nil
137 | }
138 |
139 | func (s *scheme) UnmarshalTextPublicKey(text []byte) (kem.PublicKey, error) {
140 | return pem.FromPublicPEMBytes(text, s)
141 | }
142 |
143 | func (s *scheme) UnmarshalTextPrivateKey(text []byte) (kem.PrivateKey, error) {
144 | return pem.FromPrivatePEMBytes(text, s)
145 | }
146 |
147 | func (s *scheme) CiphertextSize() int {
148 | return CiphertextSize
149 | }
150 |
151 | func (s *scheme) SharedKeySize() int {
152 | return SharedKeySize
153 | }
154 |
155 | func (s *scheme) PrivateKeySize() int {
156 | return PrivateKeySize
157 | }
158 |
159 | func (s *scheme) PublicKeySize() int {
160 | return PublicKeySize
161 | }
162 |
163 | func (s *scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) {
164 | if len(seed) != KeySeedSize {
165 | panic(kem.ErrSeedSize)
166 | }
167 | encapKey, decapKey, err := xwing.NewKeyFromSeed(seed)
168 | if err != nil {
169 | panic(err)
170 | }
171 | return &PublicKey{
172 | scheme: s,
173 | encapKey: encapKey,
174 | }, &PrivateKey{
175 | scheme: s,
176 | encapKey: encapKey,
177 | decapKey: decapKey,
178 | }
179 | }
180 |
181 | func (s *scheme) SeedSize() int {
182 | return KeySeedSize
183 | }
184 |
--------------------------------------------------------------------------------
/sign/pem/pem.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (c) 2023-2024 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package pem
5 |
6 | import (
7 | "encoding/pem"
8 | "errors"
9 | "fmt"
10 | "os"
11 | "strings"
12 |
13 | "github.com/katzenpost/hpqc/sign"
14 | "github.com/katzenpost/hpqc/util"
15 | )
16 |
17 | func ToPublicPEMString(key sign.PublicKey) string {
18 | return string(ToPublicPEMBytes(key))
19 | }
20 |
21 | func ToPublicPEMBytes(key sign.PublicKey) []byte {
22 | keyType := fmt.Sprintf("%s PUBLIC KEY", strings.ToUpper(key.Scheme().Name()))
23 | blob, err := key.MarshalBinary()
24 | if err != nil {
25 | panic(err)
26 | }
27 | if util.CtIsZero(blob) {
28 | panic(fmt.Sprintf("ToPEMString/%s: attempted to serialize scrubbed key", keyType))
29 | }
30 | blk := &pem.Block{
31 | Type: keyType,
32 | Bytes: blob,
33 | }
34 | return pem.EncodeToMemory(blk)
35 | }
36 |
37 | func PublicKeyToFile(f string, key sign.PublicKey) error {
38 | out, err := os.OpenFile(f, os.O_WRONLY|os.O_CREATE, 0600)
39 | if err != nil {
40 | return err
41 | }
42 | outBuf := ToPublicPEMBytes(key)
43 | writeCount, err := out.Write(outBuf)
44 | if err != nil {
45 | return err
46 | }
47 | if writeCount != len(outBuf) {
48 | return errors.New("partial write failure")
49 | }
50 | err = out.Sync()
51 | if err != nil {
52 | return err
53 | }
54 | return out.Close()
55 | }
56 |
57 | func FromPublicPEMString(s string, scheme sign.Scheme) (sign.PublicKey, error) {
58 | return FromPublicPEMBytes([]byte(s), scheme)
59 | }
60 |
61 | func FromPublicPEMBytes(b []byte, scheme sign.Scheme) (sign.PublicKey, error) {
62 | keyType := fmt.Sprintf("%s PUBLIC KEY", strings.ToUpper(scheme.Name()))
63 | blk, _ := pem.Decode(b)
64 | if blk == nil {
65 | return nil, fmt.Errorf("failed to decode PEM data from %s PEM", keyType)
66 | }
67 | if strings.ToUpper(blk.Type) != keyType {
68 | return nil, fmt.Errorf("attempted to decode PEM file with wrong key type %v != %v", blk.Type, keyType)
69 | }
70 | return scheme.UnmarshalBinaryPublicKey(blk.Bytes)
71 | }
72 |
73 | func FromPublicPEMToBytes(b []byte, scheme sign.Scheme) ([]byte, error) {
74 | keyType := fmt.Sprintf("%s PUBLIC KEY", strings.ToUpper(scheme.Name()))
75 | blk, _ := pem.Decode(b)
76 | if blk == nil {
77 | return nil, fmt.Errorf("failed to decode PEM data from %s PEM", keyType)
78 | }
79 | if strings.ToUpper(blk.Type) != keyType {
80 | return nil, fmt.Errorf("attempted to decode PEM file with wrong key type %v != %v", blk.Type, keyType)
81 | }
82 | return blk.Bytes, nil
83 | }
84 |
85 | func FromPublicPEMFile(f string, scheme sign.Scheme) (sign.PublicKey, error) {
86 | buf, err := os.ReadFile(f)
87 | if err != nil {
88 | return nil, fmt.Errorf("pem.FromFile error: %s", err)
89 | }
90 | return FromPublicPEMBytes(buf, scheme)
91 | }
92 |
93 | // private key
94 |
95 | func ToPrivatePEMString(key sign.PrivateKey) string {
96 | return string(ToPrivatePEMBytes(key))
97 | }
98 |
99 | func ToPrivatePEMBytes(key sign.PrivateKey) []byte {
100 | keyType := fmt.Sprintf("%s PRIVATE KEY", strings.ToUpper(key.Scheme().Name()))
101 | blob, err := key.MarshalBinary()
102 | if err != nil {
103 | panic(err)
104 | }
105 | if util.CtIsZero(blob) {
106 | panic(fmt.Sprintf("ToPEMString/%s: attempted to serialize scrubbed key", keyType))
107 | }
108 | blk := &pem.Block{
109 | Type: keyType,
110 | Bytes: blob,
111 | }
112 | return pem.EncodeToMemory(blk)
113 | }
114 |
115 | func PrivateKeyToFile(f string, key sign.PrivateKey) error {
116 | out, err := os.OpenFile(f, os.O_WRONLY|os.O_CREATE, 0600)
117 | if err != nil {
118 | return err
119 | }
120 | outBuf := ToPrivatePEMBytes(key)
121 | writeCount, err := out.Write(outBuf)
122 | if err != nil {
123 | return err
124 | }
125 | if writeCount != len(outBuf) {
126 | return errors.New("partial write failure")
127 | }
128 | err = out.Sync()
129 | if err != nil {
130 | return err
131 | }
132 | return out.Close()
133 | }
134 |
135 | func FromPrivatePEMString(s string, scheme sign.Scheme) (sign.PrivateKey, error) {
136 | return FromPrivatePEMBytes([]byte(s), scheme)
137 | }
138 |
139 | func FromPrivatePEMBytes(b []byte, scheme sign.Scheme) (sign.PrivateKey, error) {
140 | keyType := fmt.Sprintf("%s PRIVATE KEY", strings.ToUpper(scheme.Name()))
141 | blk, _ := pem.Decode(b)
142 | if blk == nil {
143 | return nil, fmt.Errorf("failed to decode PEM data from %s PEM", keyType)
144 | }
145 | if strings.ToUpper(blk.Type) != keyType {
146 | return nil, fmt.Errorf("attempted to decode PEM file with wrong key type %v != %v", blk.Type, keyType)
147 | }
148 | return scheme.UnmarshalBinaryPrivateKey(blk.Bytes)
149 | }
150 |
151 | func FromPrivatePEMFile(f string, scheme sign.Scheme) (sign.PrivateKey, error) {
152 | buf, err := os.ReadFile(f)
153 | if err != nil {
154 | return nil, fmt.Errorf("pem.FromFile error: %s", err)
155 | }
156 | return FromPrivatePEMBytes(buf, scheme)
157 | }
158 |
--------------------------------------------------------------------------------
/nike/pem/pem.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (c) 2023-2024 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package pem
5 |
6 | import (
7 | "encoding/pem"
8 | "fmt"
9 | "os"
10 | "strings"
11 |
12 | "github.com/katzenpost/hpqc/nike"
13 | "github.com/katzenpost/hpqc/util"
14 | )
15 |
16 | // ToPublicPEMString converts a public key to its PEM-encoded string representation.
17 | func ToPublicPEMString(key nike.PublicKey, scheme nike.Scheme) string {
18 | return string(ToPublicPEMBytes(key, scheme))
19 | }
20 |
21 | // ToPublicPEMBytes converts a public key to its PEM-encoded byte slice.
22 | func ToPublicPEMBytes(key nike.PublicKey, scheme nike.Scheme) []byte {
23 | return toPEMBytes(key, fmt.Sprintf("%s PUBLIC KEY", strings.ToUpper(scheme.Name())))
24 | }
25 |
26 | // PublicKeyToFile writes a public key to a file in PEM format.
27 | func PublicKeyToFile(filename string, key nike.PublicKey, scheme nike.Scheme) error {
28 | return writePEMToFile(filename, ToPublicPEMBytes(key, scheme))
29 | }
30 |
31 | // FromPublicPEMString parses a PEM-encoded public key string.
32 | func FromPublicPEMString(data string, scheme nike.Scheme) (nike.PublicKey, error) {
33 | return FromPublicPEMBytes([]byte(data), scheme)
34 | }
35 |
36 | // FromPublicPEMBytes parses a PEM-encoded public key byte slice.
37 | func FromPublicPEMBytes(data []byte, scheme nike.Scheme) (nike.PublicKey, error) {
38 | blk, err := decodePEMBlock(data, fmt.Sprintf("%s PUBLIC KEY", strings.ToUpper(scheme.Name())))
39 | if err != nil {
40 | return nil, err
41 | }
42 | return scheme.UnmarshalBinaryPublicKey(blk.Bytes)
43 | }
44 |
45 | // FromPublicPEMFile reads and parses a public key from a PEM-encoded file.
46 | func FromPublicPEMFile(filename string, scheme nike.Scheme) (nike.PublicKey, error) {
47 | data, err := os.ReadFile(filename)
48 | if err != nil {
49 | return nil, fmt.Errorf("failed to read PEM file: %w", err)
50 | }
51 | return FromPublicPEMBytes(data, scheme)
52 | }
53 |
54 | // ToPrivatePEMString converts a private key to its PEM-encoded string representation.
55 | func ToPrivatePEMString(key nike.PrivateKey, scheme nike.Scheme) string {
56 | return string(ToPrivatePEMBytes(key, scheme))
57 | }
58 |
59 | // ToPrivatePEMBytes converts a private key to its PEM-encoded byte slice.
60 | func ToPrivatePEMBytes(key nike.PrivateKey, scheme nike.Scheme) []byte {
61 | return toPEMBytes(key, fmt.Sprintf("%s PRIVATE KEY", strings.ToUpper(scheme.Name())))
62 | }
63 |
64 | // PrivateKeyToFile writes a private key to a file in PEM format.
65 | func PrivateKeyToFile(filename string, key nike.PrivateKey, scheme nike.Scheme) error {
66 | return writePEMToFile(filename, ToPrivatePEMBytes(key, scheme))
67 | }
68 |
69 | // FromPrivatePEMString parses a PEM-encoded private key string.
70 | func FromPrivatePEMString(data string, scheme nike.Scheme) (nike.PrivateKey, error) {
71 | return FromPrivatePEMBytes([]byte(data), scheme)
72 | }
73 |
74 | // FromPrivatePEMBytes parses a PEM-encoded private key byte slice.
75 | func FromPrivatePEMBytes(data []byte, scheme nike.Scheme) (nike.PrivateKey, error) {
76 | blk, err := decodePEMBlock(data, fmt.Sprintf("%s PRIVATE KEY", strings.ToUpper(scheme.Name())))
77 | if err != nil {
78 | return nil, err
79 | }
80 | return scheme.UnmarshalBinaryPrivateKey(blk.Bytes)
81 | }
82 |
83 | // FromPrivatePEMFile reads and parses a private key from a PEM-encoded file.
84 | func FromPrivatePEMFile(filename string, scheme nike.Scheme) (nike.PrivateKey, error) {
85 | data, err := os.ReadFile(filename)
86 | if err != nil {
87 | return nil, fmt.Errorf("failed to read PEM file: %w", err)
88 | }
89 | return FromPrivatePEMBytes(data, scheme)
90 | }
91 |
92 | // Helper Functions
93 |
94 | // toPEMBytes serializes a key to a PEM-encoded byte slice.
95 | func toPEMBytes(key nike.Key, keyType string) []byte {
96 | blob, err := key.MarshalBinary()
97 | if err != nil {
98 | panic(fmt.Sprintf("failed to marshal key: %v", err))
99 | }
100 | if util.CtIsZero(blob) {
101 | panic(fmt.Sprintf("attempted to serialize a scrubbed key: %s", keyType))
102 | }
103 | return pem.EncodeToMemory(&pem.Block{
104 | Type: keyType,
105 | Bytes: blob,
106 | })
107 | }
108 |
109 | // decodePEMBlock decodes a PEM block and verifies its type.
110 | func decodePEMBlock(data []byte, expectedType string) (*pem.Block, error) {
111 | blk, _ := pem.Decode(data)
112 | if blk == nil {
113 | return nil, fmt.Errorf("failed to decode PEM block of type %s", expectedType)
114 | }
115 | if strings.ToUpper(blk.Type) != expectedType {
116 | return nil, fmt.Errorf("PEM block type mismatch: got %s, want %s", blk.Type, expectedType)
117 | }
118 | return blk, nil
119 | }
120 |
121 | // writePEMToFile writes PEM-encoded data to a file securely.
122 | func writePEMToFile(filename string, pemData []byte) error {
123 | file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
124 | if err != nil {
125 | return fmt.Errorf("failed to open file for writing: %w", err)
126 | }
127 | defer file.Close()
128 |
129 | if _, err := file.Write(pemData); err != nil {
130 | return fmt.Errorf("failed to write to file: %w", err)
131 | }
132 | if err := file.Sync(); err != nil {
133 | return fmt.Errorf("failed to sync file: %w", err)
134 | }
135 | return nil
136 | }
137 |
--------------------------------------------------------------------------------
/rand/math.go:
--------------------------------------------------------------------------------
1 | // math.go - math/rand replacement.
2 | // Copyright (C) 2017 Yawning Angel.
3 | //
4 | // This program is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Affero General Public License as
6 | // published by the Free Software Foundation, either version 3 of the
7 | // License, or (at your option) any later version.
8 | //
9 | // This program is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Affero General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Affero General Public License
15 | // along with this program. If not, see .
16 |
17 | // Package rand provides various utitilies related to generating
18 | // cryptographically secure random numbers and byte vectors.
19 | package rand
20 |
21 | import (
22 | "encoding/binary"
23 | "io"
24 | "math"
25 | "math/rand"
26 | "sync"
27 |
28 | "github.com/katzenpost/chacha20"
29 | "github.com/katzenpost/hpqc/util"
30 | )
31 |
32 | const seedSize = chacha20.KeySize
33 |
34 | var mNonce [chacha20.NonceSize]byte
35 |
36 | type randSource struct {
37 | sync.Mutex
38 | s *chacha20.Cipher
39 | off int
40 | }
41 |
42 | func (s *randSource) feedForward() {
43 | var seed [chacha20.KeySize]byte
44 | defer util.ExplicitBzero(seed[:])
45 | s.s.KeyStream(seed[:])
46 | if s.s.ReKey(seed[:], mNonce[:]) != nil {
47 | panic("chacha20 ReKey failed, not expected.")
48 | }
49 | s.off = 0
50 | }
51 |
52 | func (s *randSource) Uint64() uint64 {
53 | s.Lock()
54 | defer s.Unlock()
55 |
56 | if s.off+8 > chacha20.BlockSize-seedSize {
57 | s.feedForward()
58 | }
59 |
60 | s.off += 8
61 |
62 | var tmp [8]byte
63 | s.s.KeyStream(tmp[:])
64 | return binary.LittleEndian.Uint64(tmp[:])
65 | }
66 |
67 | func (s *randSource) Int63() int64 {
68 | ret := s.Uint64()
69 | return int64(ret & ((1 << 63) - 1))
70 | }
71 |
72 | func (s *randSource) Seed(unused int64) {
73 | var seed [chacha20.KeySize]byte
74 | defer util.ExplicitBzero(seed[:])
75 | if _, err := io.ReadFull(Reader, seed[:]); err != nil {
76 | panic("crypto/rand: failed to read entropy: " + err.Error())
77 | }
78 | if err := s.s.ReKey(seed[:], mNonce[:]); err != nil {
79 | panic("s.s.ReKey(chacha20) failed, not expected")
80 | }
81 | s.off = 0
82 | }
83 |
84 | // NewMath returns a "cryptographically secure" math/rand.Rand.
85 | func NewMath() *rand.Rand {
86 | s := new(randSource)
87 | s.s = new(chacha20.Cipher)
88 | s.Seed(0)
89 | return rand.New(s)
90 | }
91 |
92 | // Exp returns a random sample from the exponential distribution characterized
93 | // by lambda (inverse of the mean).
94 | func Exp(r *rand.Rand, lambda float64) float64 {
95 | if lambda < math.SmallestNonzeroFloat64 {
96 | panic("crypto/rand: lambda out of range")
97 | }
98 |
99 | return r.ExpFloat64() / lambda
100 | }
101 |
102 | // ExpQuantile returns the value at which the the probability of a random value
103 | // is less than or equal to the given probability for an exponential
104 | // distribution characterized by lambda.
105 | func ExpQuantile(lambda, p float64) float64 {
106 | if lambda < math.SmallestNonzeroFloat64 {
107 | panic("crypto/rand: lambda out of range")
108 | }
109 | if p < math.SmallestNonzeroFloat64 || p >= 1.0 {
110 | panic("crypto/rand: p out of range")
111 | }
112 |
113 | return -math.Log(1-p) / lambda
114 | }
115 |
116 | // Poisson returns a random sample from the poisson distribution characterized
117 | // by lambda (mean).
118 | func Poisson(r *rand.Rand, lambda float64) int {
119 | if lambda < 30.0 {
120 | return poissonSmall(r, lambda)
121 | }
122 | return poissonLarge(r, lambda)
123 | }
124 |
125 | func poissonSmall(r *rand.Rand, lambda float64) int {
126 | // Algorithm due to Donald Knuth, 1969.
127 | p, l := float64(1.0), math.Exp(-lambda)
128 | k := 0
129 | for {
130 | if s := r.Float64(); s > 0.0 {
131 | k++
132 | p = p * s
133 | }
134 | if p <= l {
135 | break
136 | }
137 | }
138 | return k - 1
139 | }
140 |
141 | func poissonLarge(r *rand.Rand, lambda float64) int {
142 | // "Rejection method PA" from "The Computer Generation of
143 | // Poisson Random Variables" by A. C. Atkinson,
144 | // Journal of the Royal Statistical Society Series C
145 | // (Applied Statistics) Vol. 28, No. 1. (1979)
146 | // The article is on pages 29-35.
147 | // The algorithm given here is on page 32.
148 |
149 | c := 0.767 - 3.36/lambda
150 | beta := math.Pi / math.Sqrt(3.0*lambda)
151 | alpha := beta * lambda
152 | k := math.Log(c) - lambda - math.Log(beta)
153 |
154 | for {
155 | u := r.Float64()
156 | if u == 0.0 {
157 | continue
158 | }
159 | x := (alpha - math.Log((1.0-u)/u)) / beta
160 | n := math.Floor(x + 0.5)
161 | if n < 0 {
162 | continue
163 | }
164 | v := r.Float64()
165 | if v == 0.0 {
166 | continue
167 | }
168 | y := alpha - beta*x
169 | temp := 1.0 + math.Exp(y)
170 | lhs := y + math.Log(v/(temp*temp))
171 | rhs := k + n*math.Log(lambda) - logFactorial(n)
172 | if lhs <= rhs {
173 | return int(n)
174 | }
175 | }
176 | }
177 |
178 | func logFactorial(n float64) float64 {
179 | // Use Stirling's approximation, since the runtime library has
180 | // the gamma function math.
181 | n = math.Floor(n)
182 | ret, _ := math.Lgamma(n + 1)
183 | return ret
184 | }
185 |
--------------------------------------------------------------------------------
/nike/schemes/schemes_test.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (C) 2024 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package schemes
5 |
6 | import (
7 | "strings"
8 | "testing"
9 |
10 | "github.com/stretchr/testify/require"
11 |
12 | "github.com/katzenpost/hpqc/nike"
13 | "github.com/katzenpost/hpqc/rand"
14 | "github.com/katzenpost/hpqc/util"
15 | "github.com/katzenpost/hpqc/nike/x25519"
16 | )
17 |
18 | func TestNIKEUnmarshaling(t *testing.T) {
19 | todo := All()
20 |
21 | testNike := func(s nike.Scheme) {
22 | pubkey1, privkey1, err := s.GenerateKeyPairFromEntropy(rand.Reader)
23 | require.NoError(t, err)
24 |
25 | pubkey1Blob, err := pubkey1.MarshalBinary()
26 | require.NoError(t, err)
27 | require.Equal(t, s.PublicKeySize(), len(pubkey1Blob))
28 |
29 | t.Logf("pubkey1Blob is len %d", len(pubkey1Blob))
30 |
31 | pubkey2, err := s.UnmarshalBinaryPublicKey(pubkey1Blob)
32 | require.NoError(t, err)
33 | refBytes := pubkey1.Bytes()
34 | require.Equal(t, refBytes, pubkey2.Bytes())
35 | pubkey1.Reset()
36 | _, err = pubkey1.MarshalBinary()
37 | require.NoError(t, err)
38 |
39 | privkey1blob, err := privkey1.MarshalBinary()
40 | require.NoError(t, err)
41 | require.Equal(t, s.PrivateKeySize(), len(privkey1blob))
42 |
43 | t.Logf("privkey1blob is len %d", len(privkey1blob))
44 | refBytes = privkey1.Bytes()
45 |
46 | privkey1.Reset()
47 | _, err = privkey1.MarshalBinary()
48 | require.NoError(t, err)
49 |
50 | privkey2, err := s.UnmarshalBinaryPrivateKey(privkey1blob)
51 | require.NoError(t, err)
52 |
53 | require.Equal(t, refBytes, privkey2.Bytes())
54 | require.Equal(t, s.PrivateKeySize(), len(privkey2.Bytes()))
55 | }
56 |
57 | for _, scheme := range todo {
58 | t.Logf("testing NIKE Scheme: %s", scheme.Name())
59 | testNike(scheme)
60 | t.Log("OK")
61 | }
62 | }
63 |
64 | func TestRoundTripBytes(t *testing.T) {
65 | todo := All()
66 |
67 | testNike := func(s nike.Scheme) {
68 | pubkey1, privkey1, err := s.GenerateKeyPairFromEntropy(rand.Reader)
69 | require.NoError(t, err)
70 | pubkey2, privkey2, err := s.GenerateKeyPairFromEntropy(rand.Reader)
71 | require.NoError(t, err)
72 | pubkey2.FromBytes(pubkey1.Bytes())
73 | require.Equal(t, pubkey1.Bytes(), pubkey2.Bytes())
74 | privkey2.FromBytes(privkey1.Bytes())
75 | require.Equal(t, privkey1.Bytes(), privkey2.Bytes())
76 | }
77 |
78 | for _, scheme := range todo {
79 | t.Logf("testing NIKE Scheme: %s", scheme.Name())
80 | testNike(scheme)
81 | t.Log("OK")
82 | }
83 | }
84 |
85 | func TestNIKEOps(t *testing.T) {
86 | todo := All()
87 |
88 | testNike := func(s nike.Scheme) {
89 | // test 1
90 | pubkey1, privkey1, err := s.GenerateKeyPairFromEntropy(rand.Reader)
91 | require.NoError(t, err)
92 |
93 | pubkey2, privkey2, err := s.GenerateKeyPairFromEntropy(rand.Reader)
94 | require.NoError(t, err)
95 |
96 | ss1 := s.DeriveSecret(privkey1, pubkey2)
97 | ss2 := s.DeriveSecret(privkey2, pubkey1)
98 |
99 | require.Equal(t, ss1, ss2)
100 |
101 | // test 2
102 | pubkey3 := privkey1.Public()
103 | require.Equal(t, pubkey1.Bytes(), pubkey3.Bytes())
104 | blob1, err := pubkey1.MarshalBinary()
105 | require.NoError(t, err)
106 | blob2, err := pubkey3.MarshalBinary()
107 | require.NoError(t, err)
108 | require.Equal(t, blob1, blob2)
109 |
110 | // test 3
111 | pubkey4 := s.DerivePublicKey(privkey1)
112 | require.Equal(t, pubkey1.Bytes(), pubkey4.Bytes())
113 |
114 | // test 4
115 | require.False(t, util.CtIsZero(pubkey1.Bytes()))
116 | pubblob1 := pubkey4.Bytes()
117 | pubkey4.Reset()
118 | pubblob2 := pubkey4.Bytes()
119 | require.False(t, util.CtIsZero(pubkey1.Bytes()))
120 | require.NotEqual(t, pubkey1.Bytes(), pubkey4.Bytes())
121 | require.True(t, util.CtIsZero(pubkey4.Bytes()))
122 | require.NotEqual(t, pubblob1, pubblob2)
123 |
124 | // test 5
125 | privBlob1 := privkey2.Bytes()
126 | privkey2.Reset()
127 | privBlob2 := privkey2.Bytes()
128 | require.NotEqual(t, privBlob1, privBlob2)
129 |
130 | if strings.Contains(s.Name(), "NOBS") {
131 | return
132 | }
133 |
134 | // blinding operations test
135 | mixPub, mixPriv, err := s.GenerateKeyPairFromEntropy(rand.Reader)
136 | require.NoError(t, err)
137 | clientPub, clientPriv, err := s.GenerateKeyPairFromEntropy(rand.Reader)
138 | require.NoError(t, err)
139 | blindingFactor := s.GeneratePrivateKey(rand.Reader)
140 | pubkey1, err = s.UnmarshalBinaryPublicKey(s.DeriveSecret(clientPriv, mixPub))
141 | require.NoError(t, err)
142 | value1 := s.Blind(pubkey1, blindingFactor)
143 | require.NoError(t, err)
144 | blinded := s.Blind(clientPub, blindingFactor)
145 | require.NoError(t, err)
146 | value2 := s.DeriveSecret(mixPriv, blinded)
147 | require.Equal(t, value1.Bytes(), value2)
148 | }
149 |
150 | for _, scheme := range todo {
151 | t.Logf("testing NIKE Scheme: %s", scheme.Name())
152 | testNike(scheme)
153 | t.Log("OK")
154 | }
155 | }
156 |
157 | func BenchmarkNIKE(b *testing.B) {
158 | benchSchemes := []nike.Scheme{
159 | x25519.Scheme(rand.Reader),
160 | }
161 | for _, scheme := range benchSchemes {
162 | scheme := scheme
163 | _, privkey1, err := scheme.GenerateKeyPairFromEntropy(rand.Reader)
164 | if err != nil {
165 | panic(err)
166 | }
167 | pubkey2, _, err := scheme.GenerateKeyPairFromEntropy(rand.Reader)
168 | if err != nil {
169 | panic(err)
170 | }
171 | b.Run(scheme.Name(), func(b *testing.B) {
172 | for i := 0; i < b.N; i++ {
173 | _ = scheme.DeriveSecret(privkey1, pubkey2)
174 | }
175 | })
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/sign/sphincsplus/sphincs.go:
--------------------------------------------------------------------------------
1 | //go:build !windows
2 |
3 | // SPDX-FileCopyrightText: (c) 2022-2024 David Stainton
4 | // SPDX-License-Identifier: AGPL-3.0-only
5 |
6 | // Package sphincsplus implements interface wrapper around a specific parameterization of Sphincs+.
7 | package sphincsplus
8 |
9 | import (
10 | "crypto"
11 | "crypto/hmac"
12 | "io"
13 |
14 | "golang.org/x/crypto/blake2b"
15 |
16 | sphincs "github.com/katzenpost/sphincsplus/ref"
17 |
18 | "github.com/katzenpost/hpqc/sign"
19 | "github.com/katzenpost/hpqc/sign/pem"
20 | )
21 |
22 | const (
23 | // KeySeedSize is the seed size used by NewKeyFromSeed to generate
24 | // a new key deterministically.
25 | KeySeedSize = 32
26 | )
27 |
28 | type scheme struct{}
29 |
30 | var sch *scheme = &scheme{}
31 |
32 | func Scheme() *scheme { return sch }
33 |
34 | var _ sign.Scheme = (*scheme)(nil)
35 | var _ sign.PublicKey = (*publicKey)(nil)
36 | var _ sign.PrivateKey = (*privateKey)(nil)
37 |
38 | func (s *scheme) Name() string {
39 | return "Sphincs+"
40 | }
41 |
42 | func (s *scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) {
43 | priv, pub := sphincs.NewKeypair()
44 | pubkey := &publicKey{
45 | scheme: Scheme(),
46 | publicKey: pub,
47 | }
48 | privkey := &privateKey{
49 | scheme: Scheme(),
50 | publicKey: pubkey,
51 | privateKey: priv,
52 | }
53 | return pubkey, privkey, nil
54 | }
55 |
56 | func (s *scheme) Sign(sk sign.PrivateKey, message []byte, opts *sign.SignatureOpts) []byte {
57 | sig, err := sk.Sign(nil, message, nil)
58 | if err != nil {
59 | panic(err)
60 | }
61 | return sig
62 | }
63 |
64 | func (s *scheme) Verify(pk sign.PublicKey, message []byte, signature []byte, opts *sign.SignatureOpts) bool {
65 | return pk.(*publicKey).Verify(signature, message)
66 | }
67 |
68 | func (s *scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) {
69 | // NOTE(david): we use the reference implementation of Sphincs+ which does not
70 | // have an API that allows for using our own entropy source or seed for key generation.
71 | // The way to fix this is to use a different implementation of Sphincs+.
72 | panic("DeriveKey not implemented")
73 | return nil, nil
74 | }
75 |
76 | func (s *scheme) UnmarshalBinaryPublicKey(b []byte) (sign.PublicKey, error) {
77 | pubKey := &publicKey{
78 | publicKey: new(sphincs.PublicKey),
79 | }
80 | err := pubKey.FromBytes(b)
81 | if err != nil {
82 | return nil, err
83 | }
84 | return pubKey, nil
85 | }
86 |
87 | // UnmarshalBinaryPrivateKey loads a private key from byte slice.
88 | func (s *scheme) UnmarshalBinaryPrivateKey(b []byte) (sign.PrivateKey, error) {
89 | privKey := &privateKey{
90 | privateKey: new(sphincs.PrivateKey),
91 | }
92 | err := privKey.FromBytes(b)
93 | if err != nil {
94 | return nil, err
95 | }
96 | return privKey, nil
97 | }
98 |
99 | func (s *scheme) PrivateKeySize() int {
100 | return sphincs.PrivateKeySize
101 | }
102 |
103 | func (s *scheme) PublicKeySize() int {
104 | return sphincs.PublicKeySize
105 | }
106 |
107 | func (s *scheme) SignatureSize() int {
108 | return sphincs.SignatureSize
109 | }
110 |
111 | func (s *scheme) SeedSize() int {
112 | return KeySeedSize
113 | }
114 |
115 | func (s *scheme) SupportsContext() bool {
116 | return false
117 | }
118 |
119 | type privateKey struct {
120 | scheme *scheme
121 | privateKey *sphincs.PrivateKey
122 | publicKey *publicKey
123 | }
124 |
125 | // sign.PublicKey interface
126 |
127 | func (p *privateKey) Scheme() sign.Scheme {
128 | return p.scheme
129 | }
130 |
131 | func (p *privateKey) Equal(key crypto.PrivateKey) bool {
132 | return hmac.Equal(key.(*privateKey).Bytes(), p.Bytes())
133 | }
134 |
135 | func (p *privateKey) Public() crypto.PublicKey {
136 | return p.publicKey
137 | }
138 |
139 | func (p *privateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) {
140 | return p.privateKey.Sign(digest), nil
141 | }
142 |
143 | func (p *privateKey) MarshalBinary() ([]byte, error) {
144 | return p.Bytes(), nil
145 | }
146 |
147 | func (p *privateKey) UnmarshalBinary(b []byte) error {
148 | return p.FromBytes(b)
149 | }
150 |
151 | // end of sign.PublicKey interface
152 |
153 | func (p *privateKey) Reset() {
154 | p.privateKey.Reset()
155 | }
156 |
157 | func (p *privateKey) Bytes() []byte {
158 | return p.privateKey.Bytes()
159 | }
160 |
161 | func (p *privateKey) FromBytes(data []byte) error {
162 | return p.privateKey.FromBytes(data)
163 | }
164 |
165 | type publicKey struct {
166 | scheme *scheme
167 | publicKey *sphincs.PublicKey
168 | }
169 |
170 | // sign.PublicKey interface
171 |
172 | func (p *publicKey) Scheme() sign.Scheme {
173 | return p.scheme
174 | }
175 |
176 | func (p *publicKey) Equal(key crypto.PublicKey) bool {
177 | return hmac.Equal(key.(*publicKey).Bytes(), p.Bytes())
178 | }
179 |
180 | func (p *publicKey) MarshalBinary() ([]byte, error) {
181 | return p.Bytes(), nil
182 | }
183 |
184 | func (p *publicKey) MarshalText() (text []byte, err error) {
185 | return pem.ToPublicPEMBytes(p), nil
186 | }
187 |
188 | // end of sign.PublicKey interface
189 |
190 | func (p *publicKey) Reset() {
191 | p.publicKey.Reset()
192 | }
193 |
194 | func (p *publicKey) Bytes() []byte {
195 | return p.publicKey.Bytes()
196 | }
197 |
198 | func (p *publicKey) FromBytes(data []byte) error {
199 | return p.publicKey.FromBytes(data)
200 | }
201 |
202 | func (p *publicKey) Verify(sig, message []byte) bool {
203 | return p.publicKey.Verify(sig, message)
204 | }
205 |
206 | func (p *publicKey) Sum256() [32]byte {
207 | return blake2b.Sum256(p.Bytes())
208 | }
209 |
--------------------------------------------------------------------------------
/sign/ed25519/eddsa_test.go:
--------------------------------------------------------------------------------
1 | // eddsa_test.go - Test eddsa wrapper signature scheme tests.
2 | //
3 | // Copyright (C) 2022 David Stainton.
4 | //
5 | // This program is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU Affero General Public License as
7 | // published by the Free Software Foundation, either version 3 of the
8 | // License, or (at your option) any later version.
9 | //
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU Affero General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU Affero General Public License
16 | // along with this program. If not, see .
17 |
18 | package ed25519
19 |
20 | import (
21 | "testing"
22 |
23 | "github.com/stretchr/testify/assert"
24 | "github.com/stretchr/testify/require"
25 |
26 | "github.com/katzenpost/hpqc/util"
27 | )
28 |
29 | func TestEddsaScheme(t *testing.T) {
30 | t.Parallel()
31 | message := []byte("hello world")
32 | pubKey, privKey, err := Scheme().GenerateKey()
33 | require.NoError(t, err)
34 | signature := Scheme().Sign(privKey, message, nil)
35 | require.Equal(t, len(signature), Scheme().SignatureSize())
36 | ok := Scheme().Verify(pubKey, message, signature, nil)
37 | require.True(t, ok)
38 | }
39 |
40 | func TestEddsaSchemeTextUnmarshaler(t *testing.T) {
41 | t.Parallel()
42 | message := []byte("hello world")
43 | pubKey, privKey, err := Scheme().GenerateKey()
44 | require.NoError(t, err)
45 |
46 | pubKeyText, err := pubKey.MarshalBinary()
47 | require.NoError(t, err)
48 |
49 | pubKey2, err := Scheme().UnmarshalBinaryPublicKey(pubKeyText)
50 | require.NoError(t, err)
51 |
52 | signature := Scheme().Sign(privKey, message, nil)
53 |
54 | ok := Scheme().Verify(pubKey, message, signature, nil)
55 | require.True(t, ok)
56 |
57 | ok = Scheme().Verify(pubKey2, message, signature, nil)
58 | require.True(t, ok)
59 | }
60 |
61 | func TestKeypair(t *testing.T) {
62 | t.Parallel()
63 |
64 | var shortBuffer = []byte("Short Buffer")
65 |
66 | _, privKey, err := Scheme().GenerateKey()
67 | require.NoError(t, err, "NewKeypair()")
68 |
69 | var privKey2 PrivateKey
70 | require.Error(t, privKey2.FromBytes(shortBuffer))
71 |
72 | privKeyBlob, err := privKey.MarshalBinary()
73 | require.NoError(t, err)
74 |
75 | err = privKey2.FromBytes(privKeyBlob)
76 | require.NoError(t, err)
77 |
78 | privKeyBlob2, err := privKey2.MarshalBinary()
79 | require.NoError(t, err)
80 |
81 | require.Equal(t, privKeyBlob2, privKeyBlob)
82 |
83 | privKey2.Reset()
84 | require.True(t, util.CtIsZero(privKey2.privKey))
85 |
86 | var pubKey PublicKey
87 | require.Error(t, pubKey.FromBytes(shortBuffer))
88 |
89 | blob, err := privKey.(*PrivateKey).PublicKey().MarshalBinary()
90 | require.NoError(t, err)
91 |
92 | err = pubKey.FromBytes(blob)
93 | require.NoError(t, err)
94 | require.Equal(t, privKey.Public(), &pubKey)
95 |
96 | pkArr := pubKey.ByteArray()
97 |
98 | blob, err = privKey.Public().(*PublicKey).MarshalBinary()
99 | require.NoError(t, err)
100 | require.Equal(t, blob, pkArr[:])
101 | }
102 |
103 | func TestEdDSAOps(t *testing.T) {
104 | t.Parallel()
105 | assert := assert.New(t)
106 |
107 | _, privKey, err := Scheme().GenerateKey()
108 | require.NoError(t, err, "NewKeypair()")
109 | pubKey := privKey.Public().(*PublicKey)
110 |
111 | msg := []byte("The year was 2081, and everybody was finally equal. They weren't only equal before God and the law. They were equal every which way. Nobody was smarter than anybody else. Nobody was better looking than anybody else. Nobody was stronger or quicker than anybody else. All this equality was due to the 211th, 212th, and 213th Amendments to the Constitution, and to the unceasing vigilance of agents of the United States Handicapper General.")
112 |
113 | sig, err := privKey.Sign(nil, msg, nil)
114 | require.NoError(t, err)
115 |
116 | assert.Equal(SignatureSize, len(sig), "Sign() length")
117 | assert.True(pubKey.Verify(sig, msg), "Verify(sig, msg)")
118 | assert.False(pubKey.Verify(sig, msg[:16]), "Verify(sig, msg[:16])")
119 | }
120 |
121 | func TestCheckEdDSA(t *testing.T) {
122 | t.Parallel()
123 | // check that EdDSA signing works like the first test vector in
124 | // https://ed25519.cr.yp.to/python/sign.input
125 | // (this is a sanity check to ensure (R,s) is computed as it should
126 | // as it was non-obvious to me that the nonce is being clamped
127 | assert := assert.New(t)
128 | vector_signed := [64]byte{229, 86, 67, 0, 195, 96, 172, 114, 144, 134, 226, 204, 128, 110, 130, 138, 132, 135, 127, 30, 184, 229, 217, 116, 216, 115, 224, 101, 34, 73, 1, 85, 95, 184, 130, 21, 144, 163, 59, 172, 198, 30, 57, 112, 28, 249, 180, 107, 210, 91, 245, 240, 89, 91, 190, 36, 101, 81, 65, 67, 142, 122, 16, 11}
129 | tsk := [64]byte{157, 97, 177, 157, 239, 253, 90, 96, 186, 132, 74, 244, 146, 236, 44, 196, 68, 73, 197, 105, 123, 50, 105, 25, 112, 59, 172, 3, 28, 174, 127, 96, 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26}
130 | tpk := [32]byte{215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26}
131 | rsk := new(PrivateKey)
132 | assert.NoError(rsk.FromBytes(tsk[:]))
133 | assert.Equal(tpk[:], rsk.PublicKey().Bytes())
134 | actual_signed, err := rsk.Sign(nil, []byte{}, nil)
135 | require.NoError(t, err)
136 | assert.Equal(vector_signed[:], actual_signed)
137 | verify_res := rsk.PublicKey().Verify(vector_signed[:], []byte{})
138 | assert.Equal(true, verify_res)
139 | // and 1 was NOT the message, so that shouldn't check out:
140 | verify_res = rsk.PublicKey().Verify(vector_signed[:], []byte{1})
141 | assert.Equal(false, verify_res)
142 | }
143 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | codeberg.org/vula/highctidh v1.0.2024092800 h1:mvAQCDPgTTwo0/w1ZztxOEKp8CnH2+q5zf+3vKR79Jc=
2 | codeberg.org/vula/highctidh v1.0.2024092800/go.mod h1:admvznk7GhsrDih/BMy7jbIv9Y86U9vj7S5FVoquF/g=
3 | filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
4 | filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
5 | filippo.io/mlkem768 v0.0.0-20240221181710-5ce91625fdc1 h1:xbdqh5aDZeO0XqW896qVjKnAqRji9nkIwmsBEEbCA10=
6 | filippo.io/mlkem768 v0.0.0-20240221181710-5ce91625fdc1/go.mod h1:mIEHrcJ2xBlJRQwnRO0ujmZ+Rt6m6eNeCPq8E3Wkths=
7 | github.com/agl/gcmsiv v0.0.0-20190418185415-e8dcd2f151dc h1:MB338WDPuyQ8qXRiSRK2NVTv0EmLYsHXZAevEiQO1+c=
8 | github.com/agl/gcmsiv v0.0.0-20190418185415-e8dcd2f151dc/go.mod h1:5joDAvk82M2Cx1X8mAL5Orvhy5lfW4BjrTCW65wbvRo=
9 | github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
10 | github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
11 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
12 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
13 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
14 | github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
15 | github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
16 | github.com/go-faster/xor v1.0.0 h1:2o8vTOgErSGHP3/7XwA5ib1FTtUsNtwCoLLBjl31X38=
17 | github.com/go-faster/xor v1.0.0/go.mod h1:x5CaDY9UKErKzqfRfFZdfu+OSTfoZny3w5Ak7UxcipQ=
18 | github.com/henrydcase/nobs v0.0.0-20230313231516-25b66236df73 h1:d3rq/Tz+RJ5h1xk6Lt3jbObJN3WhvZm7rV41OCIzUyI=
19 | github.com/henrydcase/nobs v0.0.0-20230313231516-25b66236df73/go.mod h1:ptK2MJqVLVEa/V/oK8n+MEyUDCSjSylW+jeNmCG1DJo=
20 | github.com/katzenpost/chacha20 v0.0.0-20190910113340-7ce890d6a556 h1:9gHByAWH1LydGefFGorN1ZBRZ/Oz9iozdzMvRTWpyRw=
21 | github.com/katzenpost/chacha20 v0.0.0-20190910113340-7ce890d6a556/go.mod h1:d9kxwmGOcutgP6bQwr2xaLInaW5yJsxsoPRyUIG0J/E=
22 | github.com/katzenpost/chacha20poly1305 v0.0.0-20211026103954-7b6fb2fc0129 h1:BkXIK2/3bAtqFbzea4tMVfhu8BQ9j05yCVy/z8iCz3s=
23 | github.com/katzenpost/chacha20poly1305 v0.0.0-20211026103954-7b6fb2fc0129/go.mod h1:moU3dWG1uk4xayUNdUPs2Q3dx2Zsk5i9Ysfx+ujQrwM=
24 | github.com/katzenpost/circl v1.3.9-0.20240222183521-1cd9a34e9a0c h1:FYy03rLIjdyjklBOI6YSCb3q7OubTx0dVDWYOgDsvA8=
25 | github.com/katzenpost/circl v1.3.9-0.20240222183521-1cd9a34e9a0c/go.mod h1:+EBrwiGYs9S+qZqaqxujN1CReTNCMAG6p+31KkEDeeA=
26 | github.com/katzenpost/sntrup4591761 v0.0.0-20231024131303-8755eb1986b8 h1:TsKxH0x2RUwf5rBw67k15bqVM3oVbexA9oaTZQLIy3Y=
27 | github.com/katzenpost/sntrup4591761 v0.0.0-20231024131303-8755eb1986b8/go.mod h1:Hmcrwom7jcEmGdo0CsyuJNnldPeyS+M07FuCbo7I8fw=
28 | github.com/katzenpost/sphincsplus v0.0.2 h1:W1UWejLK62Lk0uK2R08H/sWEaQrRHWCaMEKO181SoOE=
29 | github.com/katzenpost/sphincsplus v0.0.2/go.mod h1:ChO9+ojgCH1yEuplGgW4mSI1FwZWtyEmEkG1xL3w264=
30 | github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0=
31 | github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
32 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
33 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
34 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
35 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
36 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
37 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
38 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
39 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
40 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
41 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
42 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
43 | github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
44 | github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
45 | gitlab.com/elixxir/crypto v0.0.9 h1:vt7+yRvGd/G9VtpQCaXa7EKPmHTgThf2hXbzZ1vlU6I=
46 | gitlab.com/elixxir/crypto v0.0.9/go.mod h1:KV3F+kO0VVusLIKPqSKMk4HZ4yQiS12SaIsOXg9LJb0=
47 | gitlab.com/xx_network/crypto v0.0.6 h1:+C44rBhclcbWrGa5EOic5yDF3NrXAbXScCb/mXmm3Ro=
48 | gitlab.com/xx_network/crypto v0.0.6/go.mod h1:C69/+XTiqJvKkYzcyA47+LdxvAofl9AzR/Nyo36y9hs=
49 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
50 | golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
51 | golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
52 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
53 | golang.org/x/sys v0.0.0-20190902133755-9109b7679e13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
54 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
55 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
56 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
57 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
58 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
59 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
60 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
61 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
62 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
63 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
64 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
65 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
66 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
67 |
--------------------------------------------------------------------------------
/nike/ctidh/ctidh511/ctidh.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (C) 2022-2024 David Stainton.
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package ctidh511
5 |
6 | import (
7 | "encoding/base64"
8 | "io"
9 |
10 | ctidh "codeberg.org/vula/highctidh/src/ctidh511"
11 |
12 | "github.com/katzenpost/hpqc/nike"
13 | )
14 |
15 | // CTIDH implements the Nike interface using our CTIDH module.
16 | type scheme struct{}
17 |
18 | var sch nike.Scheme = &scheme{}
19 |
20 | // Scheme returns a NIKE Scheme interface.
21 | func Scheme() nike.Scheme { return sch }
22 |
23 | var _ nike.PrivateKey = (*PrivateKey)(nil)
24 | var _ nike.PublicKey = (*PublicKey)(nil)
25 | var _ nike.Scheme = (*scheme)(nil)
26 |
27 | func (e *scheme) Name() string {
28 | return "ctidh511"
29 | }
30 |
31 | // PublicKeySize returns the size in bytes of the public key.
32 | func (e *scheme) PublicKeySize() int {
33 | return ctidh.PublicKeySize
34 | }
35 |
36 | // PrivateKeySize returns the size in bytes of the private key.
37 | func (e *scheme) PrivateKeySize() int {
38 | return ctidh.PrivateKeySize
39 | }
40 |
41 | // NewEmptyPublicKey returns an uninitialized
42 | // PublicKey which is suitable to be loaded
43 | // via some serialization format via FromBytes
44 | // or FromPEMFile methods.
45 | func (e *scheme) NewEmptyPublicKey() nike.PublicKey {
46 | return &PublicKey{
47 | publicKey: ctidh.NewEmptyPublicKey(),
48 | }
49 | }
50 |
51 | // NewEmptyPrivateKey returns an uninitialized
52 | // PrivateKey which is suitable to be loaded
53 | // via some serialization format via FromBytes
54 | // or FromPEMFile methods.
55 | func (e *scheme) NewEmptyPrivateKey() nike.PrivateKey {
56 | return &PrivateKey{
57 | privateKey: ctidh.NewEmptyPrivateKey(),
58 | }
59 | }
60 |
61 | func (e *scheme) GeneratePrivateKey(rng io.Reader) nike.PrivateKey {
62 | return &PrivateKey{
63 | privateKey: ctidh.GeneratePrivateKey(rng),
64 | }
65 | }
66 |
67 | func (e *scheme) GenerateKeyPairFromEntropy(rng io.Reader) (nike.PublicKey, nike.PrivateKey, error) {
68 | privKey, pubKey := ctidh.GenerateKeyPairWithRNG(rng)
69 | return &PublicKey{
70 | publicKey: pubKey,
71 | }, &PrivateKey{
72 | privateKey: privKey,
73 | }, nil
74 | }
75 |
76 | // GenerateKeyPair creates a new key pair.
77 | func (e *scheme) GenerateKeyPair() (nike.PublicKey, nike.PrivateKey, error) {
78 | privKey, pubKey := ctidh.GenerateKeyPair()
79 | return &PublicKey{
80 | publicKey: pubKey,
81 | }, &PrivateKey{
82 | privateKey: privKey,
83 | }, nil
84 | }
85 |
86 | // DeriveSecret derives a shared secret given a private key
87 | // from one party and a public key from another.
88 | func (e *scheme) DeriveSecret(privKey nike.PrivateKey, pubKey nike.PublicKey) []byte {
89 | return ctidh.DeriveSecret(privKey.(*PrivateKey).privateKey, pubKey.(*PublicKey).publicKey)
90 | }
91 |
92 | // DerivePublicKey derives a public key given a private key.
93 | func (e *scheme) DerivePublicKey(privKey nike.PrivateKey) nike.PublicKey {
94 | return &PublicKey{
95 | publicKey: ctidh.DerivePublicKey(privKey.(*PrivateKey).privateKey),
96 | }
97 | }
98 |
99 | func (e *scheme) Blind(groupMember nike.PublicKey, blindingFactor nike.PrivateKey) nike.PublicKey {
100 | blinded, err := ctidh.Blind(
101 | blindingFactor.(*PrivateKey).privateKey,
102 | groupMember.(*PublicKey).publicKey,
103 | )
104 | if err != nil {
105 | panic(err)
106 | }
107 | return &PublicKey{
108 | publicKey: blinded,
109 | }
110 | }
111 |
112 | func (e *scheme) UnmarshalBinaryPublicKey(b []byte) (nike.PublicKey, error) {
113 | pubkey := ctidh.NewEmptyPublicKey()
114 | err := pubkey.FromBytes(b)
115 | if err != nil {
116 | return nil, err
117 | }
118 | return &PublicKey{
119 | publicKey: pubkey,
120 | }, nil
121 | }
122 |
123 | func (e *scheme) UnmarshalBinaryPrivateKey(b []byte) (nike.PrivateKey, error) {
124 | privkey := ctidh.NewEmptyPrivateKey()
125 | err := privkey.FromBytes(b)
126 | if err != nil {
127 | return nil, err
128 | }
129 | return &PrivateKey{
130 | privateKey: privkey,
131 | }, nil
132 | }
133 |
134 | type PublicKey struct {
135 | publicKey *ctidh.PublicKey
136 | }
137 |
138 | func (p *PublicKey) Blind(blindingFactor nike.PrivateKey) error {
139 | return p.publicKey.Blind(blindingFactor.(*PrivateKey).privateKey)
140 | }
141 |
142 | func (p *PublicKey) Reset() {
143 | p.publicKey.Reset()
144 | }
145 |
146 | func (p *PublicKey) Bytes() []byte {
147 | return p.publicKey.Bytes()
148 | }
149 |
150 | func (p *PublicKey) FromBytes(data []byte) error {
151 | return p.publicKey.FromBytes(data)
152 | }
153 |
154 | // MarshalBinary is an implementation of a method on the
155 | // BinaryMarshaler interface defined in https://golang.org/pkg/encoding/
156 | func (p *PublicKey) MarshalBinary() ([]byte, error) {
157 | return p.Bytes(), nil
158 | }
159 |
160 | // UnmarshalBinary is an implementation of a method on the
161 | // BinaryUnmarshaler interface defined in https://golang.org/pkg/encoding/
162 | func (p *PublicKey) UnmarshalBinary(data []byte) error {
163 | return p.FromBytes(data)
164 | }
165 |
166 | // MarshalText is an implementation of a method on the
167 | // TextMarshaler interface defined in https://golang.org/pkg/encoding/
168 | func (p *PublicKey) MarshalText() ([]byte, error) {
169 | return []byte(base64.StdEncoding.EncodeToString(p.Bytes())), nil
170 | }
171 |
172 | // UnmarshalText is an implementation of a method on the
173 | // TextUnmarshaler interface defined in https://golang.org/pkg/encoding/
174 | func (p *PublicKey) UnmarshalText(data []byte) error {
175 | raw, err := base64.StdEncoding.DecodeString(string(data))
176 | if err != nil {
177 | return err
178 | }
179 | return p.FromBytes(raw)
180 | }
181 |
182 | type PrivateKey struct {
183 | privateKey *ctidh.PrivateKey
184 | }
185 |
186 | func (p *PrivateKey) Public() nike.PublicKey {
187 | return &PublicKey{
188 | publicKey: p.privateKey.Public(),
189 | }
190 | }
191 |
192 | func (p *PrivateKey) Reset() {
193 | p.privateKey.Reset()
194 | }
195 |
196 | func (p *PrivateKey) Bytes() []byte {
197 | return p.privateKey.Bytes()
198 | }
199 |
200 | func (p *PrivateKey) FromBytes(data []byte) error {
201 | return p.privateKey.FromBytes(data)
202 | }
203 |
204 | func (p *PrivateKey) MarshalBinary() ([]byte, error) {
205 | return p.privateKey.MarshalBinary()
206 | }
207 |
208 | func (p *PrivateKey) MarshalText() ([]byte, error) {
209 | return p.privateKey.MarshalText()
210 | }
211 |
212 | func (p *PrivateKey) UnmarshalBinary(data []byte) error {
213 | return p.privateKey.UnmarshalBinary(data)
214 | }
215 |
216 | func (p *PrivateKey) UnmarshalText(data []byte) error {
217 | return p.privateKey.UnmarshalText(data)
218 | }
219 |
--------------------------------------------------------------------------------
/nike/ctidh/ctidh512/ctidh.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (C) 2022-2024 David Stainton.
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package ctidh512
5 |
6 | import (
7 | "encoding/base64"
8 | "io"
9 |
10 | ctidh "codeberg.org/vula/highctidh/src/ctidh512"
11 |
12 | "github.com/katzenpost/hpqc/nike"
13 | )
14 |
15 | // CTIDH implements the Nike interface using our CTIDH module.
16 | type scheme struct{}
17 |
18 | var sch nike.Scheme = &scheme{}
19 |
20 | // Scheme returns a NIKE Scheme interface.
21 | func Scheme() nike.Scheme { return sch }
22 |
23 | var _ nike.PrivateKey = (*PrivateKey)(nil)
24 | var _ nike.PublicKey = (*PublicKey)(nil)
25 | var _ nike.Scheme = (*scheme)(nil)
26 |
27 | func (e *scheme) Name() string {
28 | return "ctidh512"
29 | }
30 |
31 | // PublicKeySize returns the size in bytes of the public key.
32 | func (e *scheme) PublicKeySize() int {
33 | return ctidh.PublicKeySize
34 | }
35 |
36 | // PrivateKeySize returns the size in bytes of the private key.
37 | func (e *scheme) PrivateKeySize() int {
38 | return ctidh.PrivateKeySize
39 | }
40 |
41 | // NewEmptyPublicKey returns an uninitialized
42 | // PublicKey which is suitable to be loaded
43 | // via some serialization format via FromBytes
44 | // or FromPEMFile methods.
45 | func (e *scheme) NewEmptyPublicKey() nike.PublicKey {
46 | return &PublicKey{
47 | publicKey: ctidh.NewEmptyPublicKey(),
48 | }
49 | }
50 |
51 | // NewEmptyPrivateKey returns an uninitialized
52 | // PrivateKey which is suitable to be loaded
53 | // via some serialization format via FromBytes
54 | // or FromPEMFile methods.
55 | func (e *scheme) NewEmptyPrivateKey() nike.PrivateKey {
56 | return &PrivateKey{
57 | privateKey: ctidh.NewEmptyPrivateKey(),
58 | }
59 | }
60 |
61 | func (e *scheme) GeneratePrivateKey(rng io.Reader) nike.PrivateKey {
62 | return &PrivateKey{
63 | privateKey: ctidh.GeneratePrivateKey(rng),
64 | }
65 | }
66 |
67 | func (e *scheme) GenerateKeyPairFromEntropy(rng io.Reader) (nike.PublicKey, nike.PrivateKey, error) {
68 | privKey, pubKey := ctidh.GenerateKeyPairWithRNG(rng)
69 | return &PublicKey{
70 | publicKey: pubKey,
71 | }, &PrivateKey{
72 | privateKey: privKey,
73 | }, nil
74 | }
75 |
76 | // GenerateKeyPair creates a new key pair.
77 | func (e *scheme) GenerateKeyPair() (nike.PublicKey, nike.PrivateKey, error) {
78 | privKey, pubKey := ctidh.GenerateKeyPair()
79 | return &PublicKey{
80 | publicKey: pubKey,
81 | }, &PrivateKey{
82 | privateKey: privKey,
83 | }, nil
84 | }
85 |
86 | // DeriveSecret derives a shared secret given a private key
87 | // from one party and a public key from another.
88 | func (e *scheme) DeriveSecret(privKey nike.PrivateKey, pubKey nike.PublicKey) []byte {
89 | return ctidh.DeriveSecret(privKey.(*PrivateKey).privateKey, pubKey.(*PublicKey).publicKey)
90 | }
91 |
92 | // DerivePublicKey derives a public key given a private key.
93 | func (e *scheme) DerivePublicKey(privKey nike.PrivateKey) nike.PublicKey {
94 | return &PublicKey{
95 | publicKey: ctidh.DerivePublicKey(privKey.(*PrivateKey).privateKey),
96 | }
97 | }
98 |
99 | func (e *scheme) Blind(groupMember nike.PublicKey, blindingFactor nike.PrivateKey) nike.PublicKey {
100 | blinded, err := ctidh.Blind(
101 | blindingFactor.(*PrivateKey).privateKey,
102 | groupMember.(*PublicKey).publicKey,
103 | )
104 | if err != nil {
105 | panic(err)
106 | }
107 | return &PublicKey{
108 | publicKey: blinded,
109 | }
110 | }
111 |
112 | func (e *scheme) UnmarshalBinaryPublicKey(b []byte) (nike.PublicKey, error) {
113 | pubkey := ctidh.NewEmptyPublicKey()
114 | err := pubkey.FromBytes(b)
115 | if err != nil {
116 | return nil, err
117 | }
118 | return &PublicKey{
119 | publicKey: pubkey,
120 | }, nil
121 | }
122 |
123 | func (e *scheme) UnmarshalBinaryPrivateKey(b []byte) (nike.PrivateKey, error) {
124 | privkey := ctidh.NewEmptyPrivateKey()
125 | err := privkey.FromBytes(b)
126 | if err != nil {
127 | return nil, err
128 | }
129 | return &PrivateKey{
130 | privateKey: privkey,
131 | }, nil
132 | }
133 |
134 | type PublicKey struct {
135 | publicKey *ctidh.PublicKey
136 | }
137 |
138 | func (p *PublicKey) Blind(blindingFactor nike.PrivateKey) error {
139 | return p.publicKey.Blind(blindingFactor.(*PrivateKey).privateKey)
140 | }
141 |
142 | func (p *PublicKey) Reset() {
143 | p.publicKey.Reset()
144 | }
145 |
146 | func (p *PublicKey) Bytes() []byte {
147 | return p.publicKey.Bytes()
148 | }
149 |
150 | func (p *PublicKey) FromBytes(data []byte) error {
151 | return p.publicKey.FromBytes(data)
152 | }
153 |
154 | // MarshalBinary is an implementation of a method on the
155 | // BinaryMarshaler interface defined in https://golang.org/pkg/encoding/
156 | func (p *PublicKey) MarshalBinary() ([]byte, error) {
157 | return p.Bytes(), nil
158 | }
159 |
160 | // UnmarshalBinary is an implementation of a method on the
161 | // BinaryUnmarshaler interface defined in https://golang.org/pkg/encoding/
162 | func (p *PublicKey) UnmarshalBinary(data []byte) error {
163 | return p.FromBytes(data)
164 | }
165 |
166 | // MarshalText is an implementation of a method on the
167 | // TextMarshaler interface defined in https://golang.org/pkg/encoding/
168 | func (p *PublicKey) MarshalText() ([]byte, error) {
169 | return []byte(base64.StdEncoding.EncodeToString(p.Bytes())), nil
170 | }
171 |
172 | // UnmarshalText is an implementation of a method on the
173 | // TextUnmarshaler interface defined in https://golang.org/pkg/encoding/
174 | func (p *PublicKey) UnmarshalText(data []byte) error {
175 | raw, err := base64.StdEncoding.DecodeString(string(data))
176 | if err != nil {
177 | return err
178 | }
179 | return p.FromBytes(raw)
180 | }
181 |
182 | type PrivateKey struct {
183 | privateKey *ctidh.PrivateKey
184 | }
185 |
186 | func (p *PrivateKey) Public() nike.PublicKey {
187 | return &PublicKey{
188 | publicKey: p.privateKey.Public(),
189 | }
190 | }
191 |
192 | func (p *PrivateKey) Reset() {
193 | p.privateKey.Reset()
194 | }
195 |
196 | func (p *PrivateKey) Bytes() []byte {
197 | return p.privateKey.Bytes()
198 | }
199 |
200 | func (p *PrivateKey) FromBytes(data []byte) error {
201 | return p.privateKey.FromBytes(data)
202 | }
203 |
204 | func (p *PrivateKey) MarshalBinary() ([]byte, error) {
205 | return p.privateKey.MarshalBinary()
206 | }
207 |
208 | func (p *PrivateKey) MarshalText() ([]byte, error) {
209 | return p.privateKey.MarshalText()
210 | }
211 |
212 | func (p *PrivateKey) UnmarshalBinary(data []byte) error {
213 | return p.privateKey.UnmarshalBinary(data)
214 | }
215 |
216 | func (p *PrivateKey) UnmarshalText(data []byte) error {
217 | return p.privateKey.UnmarshalText(data)
218 | }
219 |
--------------------------------------------------------------------------------
/nike/ctidh/ctidh1024/ctidh.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (C) 2022-2024 David Stainton.
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package ctidh1024
5 |
6 | import (
7 | "encoding/base64"
8 | "io"
9 |
10 | ctidh "codeberg.org/vula/highctidh/src/ctidh1024"
11 |
12 | "github.com/katzenpost/hpqc/nike"
13 | )
14 |
15 | // CTIDH implements the Nike interface using our CTIDH module.
16 | type scheme struct{}
17 |
18 | var sch nike.Scheme = &scheme{}
19 |
20 | // Scheme returns a NIKE Scheme interface.
21 | func Scheme() nike.Scheme { return sch }
22 |
23 | var _ nike.PrivateKey = (*PrivateKey)(nil)
24 | var _ nike.PublicKey = (*PublicKey)(nil)
25 | var _ nike.Scheme = (*scheme)(nil)
26 |
27 | func (e *scheme) Name() string {
28 | return "ctidh1024"
29 | }
30 |
31 | // PublicKeySize returns the size in bytes of the public key.
32 | func (e *scheme) PublicKeySize() int {
33 | return ctidh.PublicKeySize
34 | }
35 |
36 | // PrivateKeySize returns the size in bytes of the private key.
37 | func (e *scheme) PrivateKeySize() int {
38 | return ctidh.PrivateKeySize
39 | }
40 |
41 | // NewEmptyPublicKey returns an uninitialized
42 | // PublicKey which is suitable to be loaded
43 | // via some serialization format via FromBytes
44 | // or FromPEMFile methods.
45 | func (e *scheme) NewEmptyPublicKey() nike.PublicKey {
46 | return &PublicKey{
47 | publicKey: ctidh.NewEmptyPublicKey(),
48 | }
49 | }
50 |
51 | // NewEmptyPrivateKey returns an uninitialized
52 | // PrivateKey which is suitable to be loaded
53 | // via some serialization format via FromBytes
54 | // or FromPEMFile methods.
55 | func (e *scheme) NewEmptyPrivateKey() nike.PrivateKey {
56 | return &PrivateKey{
57 | privateKey: ctidh.NewEmptyPrivateKey(),
58 | }
59 | }
60 |
61 | func (e *scheme) GeneratePrivateKey(rng io.Reader) nike.PrivateKey {
62 | return &PrivateKey{
63 | privateKey: ctidh.GeneratePrivateKey(rng),
64 | }
65 | }
66 |
67 | func (e *scheme) GenerateKeyPairFromEntropy(rng io.Reader) (nike.PublicKey, nike.PrivateKey, error) {
68 | privKey, pubKey := ctidh.GenerateKeyPairWithRNG(rng)
69 | return &PublicKey{
70 | publicKey: pubKey,
71 | }, &PrivateKey{
72 | privateKey: privKey,
73 | }, nil
74 | }
75 |
76 | // GenerateKeyPair creates a new key pair.
77 | func (e *scheme) GenerateKeyPair() (nike.PublicKey, nike.PrivateKey, error) {
78 | privKey, pubKey := ctidh.GenerateKeyPair()
79 | return &PublicKey{
80 | publicKey: pubKey,
81 | }, &PrivateKey{
82 | privateKey: privKey,
83 | }, nil
84 | }
85 |
86 | // DeriveSecret derives a shared secret given a private key
87 | // from one party and a public key from another.
88 | func (e *scheme) DeriveSecret(privKey nike.PrivateKey, pubKey nike.PublicKey) []byte {
89 | return ctidh.DeriveSecret(privKey.(*PrivateKey).privateKey, pubKey.(*PublicKey).publicKey)
90 | }
91 |
92 | // DerivePublicKey derives a public key given a private key.
93 | func (e *scheme) DerivePublicKey(privKey nike.PrivateKey) nike.PublicKey {
94 | return &PublicKey{
95 | publicKey: ctidh.DerivePublicKey(privKey.(*PrivateKey).privateKey),
96 | }
97 | }
98 |
99 | func (e *scheme) Blind(groupMember nike.PublicKey, blindingFactor nike.PrivateKey) nike.PublicKey {
100 | blinded, err := ctidh.Blind(
101 | blindingFactor.(*PrivateKey).privateKey,
102 | groupMember.(*PublicKey).publicKey,
103 | )
104 | if err != nil {
105 | panic(err)
106 | }
107 | return &PublicKey{
108 | publicKey: blinded,
109 | }
110 | }
111 |
112 | func (e *scheme) UnmarshalBinaryPublicKey(b []byte) (nike.PublicKey, error) {
113 | pubkey := ctidh.NewEmptyPublicKey()
114 | err := pubkey.FromBytes(b)
115 | if err != nil {
116 | return nil, err
117 | }
118 | return &PublicKey{
119 | publicKey: pubkey,
120 | }, nil
121 | }
122 |
123 | func (e *scheme) UnmarshalBinaryPrivateKey(b []byte) (nike.PrivateKey, error) {
124 | privkey := ctidh.NewEmptyPrivateKey()
125 | err := privkey.FromBytes(b)
126 | if err != nil {
127 | return nil, err
128 | }
129 | return &PrivateKey{
130 | privateKey: privkey,
131 | }, nil
132 | }
133 |
134 | type PublicKey struct {
135 | publicKey *ctidh.PublicKey
136 | }
137 |
138 | func (p *PublicKey) Blind(blindingFactor nike.PrivateKey) error {
139 | return p.publicKey.Blind(blindingFactor.(*PrivateKey).privateKey)
140 | }
141 |
142 | func (p *PublicKey) Reset() {
143 | p.publicKey.Reset()
144 | }
145 |
146 | func (p *PublicKey) Bytes() []byte {
147 | return p.publicKey.Bytes()
148 | }
149 |
150 | func (p *PublicKey) FromBytes(data []byte) error {
151 | return p.publicKey.FromBytes(data)
152 | }
153 |
154 | // MarshalBinary is an implementation of a method on the
155 | // BinaryMarshaler interface defined in https://golang.org/pkg/encoding/
156 | func (p *PublicKey) MarshalBinary() ([]byte, error) {
157 | return p.Bytes(), nil
158 | }
159 |
160 | // UnmarshalBinary is an implementation of a method on the
161 | // BinaryUnmarshaler interface defined in https://golang.org/pkg/encoding/
162 | func (p *PublicKey) UnmarshalBinary(data []byte) error {
163 | return p.FromBytes(data)
164 | }
165 |
166 | // MarshalText is an implementation of a method on the
167 | // TextMarshaler interface defined in https://golang.org/pkg/encoding/
168 | func (p *PublicKey) MarshalText() ([]byte, error) {
169 | return []byte(base64.StdEncoding.EncodeToString(p.Bytes())), nil
170 | }
171 |
172 | // UnmarshalText is an implementation of a method on the
173 | // TextUnmarshaler interface defined in https://golang.org/pkg/encoding/
174 | func (p *PublicKey) UnmarshalText(data []byte) error {
175 | raw, err := base64.StdEncoding.DecodeString(string(data))
176 | if err != nil {
177 | return err
178 | }
179 | return p.FromBytes(raw)
180 | }
181 |
182 | type PrivateKey struct {
183 | privateKey *ctidh.PrivateKey
184 | }
185 |
186 | func (p *PrivateKey) Public() nike.PublicKey {
187 | return &PublicKey{
188 | publicKey: p.privateKey.Public(),
189 | }
190 | }
191 |
192 | func (p *PrivateKey) Reset() {
193 | p.privateKey.Reset()
194 | }
195 |
196 | func (p *PrivateKey) Bytes() []byte {
197 | return p.privateKey.Bytes()
198 | }
199 |
200 | func (p *PrivateKey) FromBytes(data []byte) error {
201 | return p.privateKey.FromBytes(data)
202 | }
203 |
204 | func (p *PrivateKey) MarshalBinary() ([]byte, error) {
205 | return p.privateKey.MarshalBinary()
206 | }
207 |
208 | func (p *PrivateKey) MarshalText() ([]byte, error) {
209 | return p.privateKey.MarshalText()
210 | }
211 |
212 | func (p *PrivateKey) UnmarshalBinary(data []byte) error {
213 | return p.privateKey.UnmarshalBinary(data)
214 | }
215 |
216 | func (p *PrivateKey) UnmarshalText(data []byte) error {
217 | return p.privateKey.UnmarshalText(data)
218 | }
219 |
--------------------------------------------------------------------------------
/nike/ctidh/ctidh2048/ctidh.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: Copyright (C) 2022-2024 David Stainton.
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package ctidh2048
5 |
6 | import (
7 | "encoding/base64"
8 | "io"
9 |
10 | ctidh "codeberg.org/vula/highctidh/src/ctidh2048"
11 |
12 | "github.com/katzenpost/hpqc/nike"
13 | )
14 |
15 | // CTIDH implements the Nike interface using our CTIDH module.
16 | type scheme struct{}
17 |
18 | var sch nike.Scheme = &scheme{}
19 |
20 | // Scheme returns a NIKE Scheme interface.
21 | func Scheme() nike.Scheme { return sch }
22 |
23 | var _ nike.PrivateKey = (*PrivateKey)(nil)
24 | var _ nike.PublicKey = (*PublicKey)(nil)
25 | var _ nike.Scheme = (*scheme)(nil)
26 |
27 | func (e *scheme) Name() string {
28 | return "ctidh2048"
29 | }
30 |
31 | // PublicKeySize returns the size in bytes of the public key.
32 | func (e *scheme) PublicKeySize() int {
33 | return ctidh.PublicKeySize
34 | }
35 |
36 | // PrivateKeySize returns the size in bytes of the private key.
37 | func (e *scheme) PrivateKeySize() int {
38 | return ctidh.PrivateKeySize
39 | }
40 |
41 | // NewEmptyPublicKey returns an uninitialized
42 | // PublicKey which is suitable to be loaded
43 | // via some serialization format via FromBytes
44 | // or FromPEMFile methods.
45 | func (e *scheme) NewEmptyPublicKey() nike.PublicKey {
46 | return &PublicKey{
47 | publicKey: ctidh.NewEmptyPublicKey(),
48 | }
49 | }
50 |
51 | // NewEmptyPrivateKey returns an uninitialized
52 | // PrivateKey which is suitable to be loaded
53 | // via some serialization format via FromBytes
54 | // or FromPEMFile methods.
55 | func (e *scheme) NewEmptyPrivateKey() nike.PrivateKey {
56 | return &PrivateKey{
57 | privateKey: ctidh.NewEmptyPrivateKey(),
58 | }
59 | }
60 |
61 | func (e *scheme) GeneratePrivateKey(rng io.Reader) nike.PrivateKey {
62 | return &PrivateKey{
63 | privateKey: ctidh.GeneratePrivateKey(rng),
64 | }
65 | }
66 |
67 | func (e *scheme) GenerateKeyPairFromEntropy(rng io.Reader) (nike.PublicKey, nike.PrivateKey, error) {
68 | privKey, pubKey := ctidh.GenerateKeyPairWithRNG(rng)
69 | return &PublicKey{
70 | publicKey: pubKey,
71 | }, &PrivateKey{
72 | privateKey: privKey,
73 | }, nil
74 | }
75 |
76 | // GenerateKeyPair creates a new key pair.
77 | func (e *scheme) GenerateKeyPair() (nike.PublicKey, nike.PrivateKey, error) {
78 | privKey, pubKey := ctidh.GenerateKeyPair()
79 | return &PublicKey{
80 | publicKey: pubKey,
81 | }, &PrivateKey{
82 | privateKey: privKey,
83 | }, nil
84 | }
85 |
86 | // DeriveSecret derives a shared secret given a private key
87 | // from one party and a public key from another.
88 | func (e *scheme) DeriveSecret(privKey nike.PrivateKey, pubKey nike.PublicKey) []byte {
89 | return ctidh.DeriveSecret(privKey.(*PrivateKey).privateKey, pubKey.(*PublicKey).publicKey)
90 | }
91 |
92 | // DerivePublicKey derives a public key given a private key.
93 | func (e *scheme) DerivePublicKey(privKey nike.PrivateKey) nike.PublicKey {
94 | return &PublicKey{
95 | publicKey: ctidh.DerivePublicKey(privKey.(*PrivateKey).privateKey),
96 | }
97 | }
98 |
99 | func (e *scheme) Blind(groupMember nike.PublicKey, blindingFactor nike.PrivateKey) nike.PublicKey {
100 | blinded, err := ctidh.Blind(
101 | blindingFactor.(*PrivateKey).privateKey,
102 | groupMember.(*PublicKey).publicKey,
103 | )
104 | if err != nil {
105 | panic(err)
106 | }
107 | return &PublicKey{
108 | publicKey: blinded,
109 | }
110 | }
111 |
112 | func (e *scheme) UnmarshalBinaryPublicKey(b []byte) (nike.PublicKey, error) {
113 | pubkey := ctidh.NewEmptyPublicKey()
114 | err := pubkey.FromBytes(b)
115 | if err != nil {
116 | return nil, err
117 | }
118 | return &PublicKey{
119 | publicKey: pubkey,
120 | }, nil
121 | }
122 |
123 | func (e *scheme) UnmarshalBinaryPrivateKey(b []byte) (nike.PrivateKey, error) {
124 | privkey := ctidh.NewEmptyPrivateKey()
125 | err := privkey.FromBytes(b)
126 | if err != nil {
127 | return nil, err
128 | }
129 | return &PrivateKey{
130 | privateKey: privkey,
131 | }, nil
132 | }
133 |
134 | type PublicKey struct {
135 | publicKey *ctidh.PublicKey
136 | }
137 |
138 | func (p *PublicKey) Blind(blindingFactor nike.PrivateKey) error {
139 | return p.publicKey.Blind(blindingFactor.(*PrivateKey).privateKey)
140 | }
141 |
142 | func (p *PublicKey) Reset() {
143 | p.publicKey.Reset()
144 | }
145 |
146 | func (p *PublicKey) Bytes() []byte {
147 | return p.publicKey.Bytes()
148 | }
149 |
150 | func (p *PublicKey) FromBytes(data []byte) error {
151 | return p.publicKey.FromBytes(data)
152 | }
153 |
154 | // MarshalBinary is an implementation of a method on the
155 | // BinaryMarshaler interface defined in https://golang.org/pkg/encoding/
156 | func (p *PublicKey) MarshalBinary() ([]byte, error) {
157 | return p.Bytes(), nil
158 | }
159 |
160 | // UnmarshalBinary is an implementation of a method on the
161 | // BinaryUnmarshaler interface defined in https://golang.org/pkg/encoding/
162 | func (p *PublicKey) UnmarshalBinary(data []byte) error {
163 | return p.FromBytes(data)
164 | }
165 |
166 | // MarshalText is an implementation of a method on the
167 | // TextMarshaler interface defined in https://golang.org/pkg/encoding/
168 | func (p *PublicKey) MarshalText() ([]byte, error) {
169 | return []byte(base64.StdEncoding.EncodeToString(p.Bytes())), nil
170 | }
171 |
172 | // UnmarshalText is an implementation of a method on the
173 | // TextUnmarshaler interface defined in https://golang.org/pkg/encoding/
174 | func (p *PublicKey) UnmarshalText(data []byte) error {
175 | raw, err := base64.StdEncoding.DecodeString(string(data))
176 | if err != nil {
177 | return err
178 | }
179 | return p.FromBytes(raw)
180 | }
181 |
182 | type PrivateKey struct {
183 | privateKey *ctidh.PrivateKey
184 | }
185 |
186 | func (p *PrivateKey) Public() nike.PublicKey {
187 | return &PublicKey{
188 | publicKey: p.privateKey.Public(),
189 | }
190 | }
191 |
192 | func (p *PrivateKey) Reset() {
193 | p.privateKey.Reset()
194 | }
195 |
196 | func (p *PrivateKey) Bytes() []byte {
197 | return p.privateKey.Bytes()
198 | }
199 |
200 | func (p *PrivateKey) FromBytes(data []byte) error {
201 | return p.privateKey.FromBytes(data)
202 | }
203 |
204 | func (p *PrivateKey) MarshalBinary() ([]byte, error) {
205 | return p.privateKey.MarshalBinary()
206 | }
207 |
208 | func (p *PrivateKey) MarshalText() ([]byte, error) {
209 | return p.privateKey.MarshalText()
210 | }
211 |
212 | func (p *PrivateKey) UnmarshalBinary(data []byte) error {
213 | return p.privateKey.UnmarshalBinary(data)
214 | }
215 |
216 | func (p *PrivateKey) UnmarshalText(data []byte) error {
217 | return p.privateKey.UnmarshalText(data)
218 | }
219 |
--------------------------------------------------------------------------------
/kem/schemes/schemes.go:
--------------------------------------------------------------------------------
1 | package schemes
2 |
3 | import (
4 | "strings"
5 |
6 | "github.com/katzenpost/circl/kem/frodo/frodo640shake"
7 | "github.com/katzenpost/circl/kem/kyber/kyber768"
8 | "github.com/katzenpost/circl/kem/mceliece/mceliece348864"
9 | "github.com/katzenpost/circl/kem/mceliece/mceliece348864f"
10 | "github.com/katzenpost/circl/kem/mceliece/mceliece460896"
11 | "github.com/katzenpost/circl/kem/mceliece/mceliece460896f"
12 | "github.com/katzenpost/circl/kem/mceliece/mceliece6688128"
13 | "github.com/katzenpost/circl/kem/mceliece/mceliece6688128f"
14 | "github.com/katzenpost/circl/kem/mceliece/mceliece6960119"
15 | "github.com/katzenpost/circl/kem/mceliece/mceliece6960119f"
16 | "github.com/katzenpost/circl/kem/mceliece/mceliece8192128"
17 | "github.com/katzenpost/circl/kem/mceliece/mceliece8192128f"
18 |
19 | "github.com/katzenpost/hpqc/kem"
20 | "github.com/katzenpost/hpqc/kem/adapter"
21 | "github.com/katzenpost/hpqc/kem/combiner"
22 | "github.com/katzenpost/hpqc/kem/hybrid"
23 | "github.com/katzenpost/hpqc/kem/mlkem768"
24 | "github.com/katzenpost/hpqc/kem/sntrup"
25 | "github.com/katzenpost/hpqc/kem/xwing"
26 | "github.com/katzenpost/hpqc/nike/ctidh/ctidh1024"
27 | "github.com/katzenpost/hpqc/nike/ctidh/ctidh2048"
28 | "github.com/katzenpost/hpqc/nike/ctidh/ctidh511"
29 | "github.com/katzenpost/hpqc/nike/ctidh/ctidh512"
30 | "github.com/katzenpost/hpqc/nike/x25519"
31 | "github.com/katzenpost/hpqc/nike/x448"
32 | "github.com/katzenpost/hpqc/rand"
33 | )
34 |
35 | var potentialSchemes = [...]kem.Scheme{
36 |
37 | // PQ KEMs
38 |
39 | adapter.FromNIKE(ctidh511.Scheme()),
40 | adapter.FromNIKE(ctidh512.Scheme()),
41 | adapter.FromNIKE(ctidh1024.Scheme()),
42 | adapter.FromNIKE(ctidh2048.Scheme()),
43 |
44 | // hybrid KEMs
45 |
46 | combiner.New(
47 | "CTIDH512-X25519",
48 | []kem.Scheme{
49 | adapter.FromNIKE(ctidh512.Scheme()),
50 | adapter.FromNIKE(x25519.Scheme(rand.Reader)),
51 | },
52 | ),
53 | combiner.New(
54 | "CTIDH1024-X448",
55 | []kem.Scheme{
56 | adapter.FromNIKE(ctidh1024.Scheme()),
57 | adapter.FromNIKE(x448.Scheme(rand.Reader)),
58 | },
59 | ),
60 | }
61 |
62 | var allSchemes = []kem.Scheme{
63 |
64 | // classical KEM schemes (converted from NIKE via hashed elgamal construction)
65 |
66 | // Classical DiffieHellman imeplementation has a bug with this ticket:
67 | // https://github.com/katzenpost/hpqc/issues/39
68 | //adapter.FromNIKE(diffiehellman.Scheme()),
69 |
70 | adapter.FromNIKE(x25519.Scheme(rand.Reader)),
71 | adapter.FromNIKE(x448.Scheme(rand.Reader)),
72 |
73 | // post quantum KEM schemes
74 |
75 | mlkem768.Scheme(),
76 | sntrup.Scheme(),
77 | frodo640shake.Scheme(),
78 | mceliece348864.Scheme(),
79 | mceliece348864f.Scheme(),
80 | mceliece460896.Scheme(),
81 | mceliece460896f.Scheme(),
82 | mceliece6688128.Scheme(),
83 | mceliece6688128f.Scheme(),
84 | mceliece6960119.Scheme(),
85 | mceliece6960119f.Scheme(),
86 | mceliece8192128.Scheme(),
87 | mceliece8192128f.Scheme(),
88 |
89 | // hybrid KEM schemes
90 |
91 | xwing.Scheme(),
92 |
93 | // XXX TODO: must soon deprecate use of "hybrid.New" in favour of "combiner.New".
94 | // We'd also like to remove Kyber now that we have mlkem768.
95 | hybrid.New(
96 | "Kyber768-X25519",
97 | adapter.FromNIKE(x25519.Scheme(rand.Reader)),
98 | kyber768.Scheme(),
99 | ),
100 |
101 | // If Xwing is not the PQ Hybrid KEM you are looking for then we recommend
102 | // using our secure generic KEM combiner:
103 | combiner.New(
104 | "MLKEM768-X25519",
105 | []kem.Scheme{
106 | adapter.FromNIKE(x25519.Scheme(rand.Reader)),
107 | mlkem768.Scheme(),
108 | },
109 | ),
110 | combiner.New(
111 | "MLKEM768-X448",
112 | []kem.Scheme{
113 | adapter.FromNIKE(x448.Scheme(rand.Reader)),
114 | mlkem768.Scheme(),
115 | },
116 | ),
117 |
118 | combiner.New(
119 | "FrodoKEM-640-SHAKE-X448",
120 | []kem.Scheme{
121 | adapter.FromNIKE(x448.Scheme(rand.Reader)),
122 | frodo640shake.Scheme(),
123 | },
124 | ),
125 | combiner.New(
126 | "sntrup4591761-X448",
127 | []kem.Scheme{
128 | adapter.FromNIKE(x448.Scheme(rand.Reader)),
129 | sntrup.Scheme(),
130 | },
131 | ),
132 |
133 | // all the Classic McEliece's from our fork of circl
134 | combiner.New(
135 | "mceliece348864-X25519",
136 | []kem.Scheme{
137 | adapter.FromNIKE(x25519.Scheme(rand.Reader)),
138 | mceliece348864.Scheme(),
139 | },
140 | ),
141 | combiner.New(
142 | "mceliece348864f-X25519",
143 | []kem.Scheme{
144 | adapter.FromNIKE(x25519.Scheme(rand.Reader)),
145 | mceliece348864f.Scheme(),
146 | },
147 | ),
148 | combiner.New(
149 | "mceliece460896-X25519",
150 | []kem.Scheme{
151 | adapter.FromNIKE(x25519.Scheme(rand.Reader)),
152 | mceliece460896.Scheme(),
153 | },
154 | ),
155 | combiner.New(
156 | "mceliece460896f-X25519",
157 | []kem.Scheme{
158 | adapter.FromNIKE(x25519.Scheme(rand.Reader)),
159 | mceliece460896f.Scheme(),
160 | },
161 | ),
162 | combiner.New(
163 | "mceliece6688128-X25519",
164 | []kem.Scheme{
165 | adapter.FromNIKE(x25519.Scheme(rand.Reader)),
166 | mceliece6688128.Scheme(),
167 | },
168 | ),
169 | combiner.New(
170 | "mceliece6688128f-X25519",
171 | []kem.Scheme{
172 | adapter.FromNIKE(x25519.Scheme(rand.Reader)),
173 | mceliece6688128f.Scheme(),
174 | },
175 | ),
176 | combiner.New(
177 | "mceliece6960119-X25519",
178 | []kem.Scheme{
179 | adapter.FromNIKE(x25519.Scheme(rand.Reader)),
180 | mceliece6960119.Scheme(),
181 | },
182 | ),
183 | combiner.New(
184 | "mceliece6960119f-X25519",
185 | []kem.Scheme{
186 | adapter.FromNIKE(x25519.Scheme(rand.Reader)),
187 | mceliece6960119f.Scheme(),
188 | },
189 | ),
190 | combiner.New(
191 | "mceliece8192128-X25519",
192 | []kem.Scheme{
193 | adapter.FromNIKE(x25519.Scheme(rand.Reader)),
194 | mceliece8192128.Scheme(),
195 | },
196 | ),
197 | combiner.New(
198 | "mceliece8192128f-X25519",
199 | []kem.Scheme{
200 | adapter.FromNIKE(x25519.Scheme(rand.Reader)),
201 | mceliece8192128f.Scheme(),
202 | },
203 | ),
204 | }
205 |
206 | var allSchemeNames map[string]kem.Scheme
207 |
208 | func init() {
209 | allSchemeNames = make(map[string]kem.Scheme)
210 | for _, scheme := range potentialSchemes {
211 | if scheme != nil {
212 | allSchemes = append(allSchemes, scheme)
213 | }
214 | }
215 | for _, scheme := range allSchemes {
216 | allSchemeNames[strings.ToLower(scheme.Name())] = scheme
217 | }
218 | }
219 |
220 | // ByName returns the NIKE scheme by string name.
221 | func ByName(name string) kem.Scheme {
222 | ret := allSchemeNames[strings.ToLower(name)]
223 | return ret
224 | }
225 |
226 | // All returns all NIKE schemes supported.
227 | func All() []kem.Scheme {
228 | a := allSchemes
229 | return a[:]
230 | }
231 |
--------------------------------------------------------------------------------
/sign/interfaces.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2019 Cloudflare. All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are
6 | met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above
11 | copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the
13 | distribution.
14 | * Neither the name of Cloudflare nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
30 | ========================================================================
31 |
32 | Copyright (c) 2009 The Go Authors. All rights reserved.
33 |
34 | Redistribution and use in source and binary forms, with or without
35 | modification, are permitted provided that the following conditions are
36 | met:
37 |
38 | * Redistributions of source code must retain the above copyright
39 | notice, this list of conditions and the following disclaimer.
40 | * Redistributions in binary form must reproduce the above
41 | copyright notice, this list of conditions and the following disclaimer
42 | in the documentation and/or other materials provided with the
43 | distribution.
44 | * Neither the name of Google Inc. nor the names of its
45 | contributors may be used to endorse or promote products derived from
46 | this software without specific prior written permission.
47 |
48 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
49 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
50 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
51 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
52 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
53 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
54 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
55 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
56 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
57 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
58 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59 |
60 | */
61 |
62 | package sign
63 |
64 | import (
65 | "crypto"
66 | "encoding"
67 | "errors"
68 | )
69 |
70 | type SignatureOpts struct {
71 | // If non-empty, includes the given context in the signature if supported
72 | // and will cause an error during signing otherwise.
73 | Context string
74 | }
75 |
76 | // A public key is used to verify a signature set by the corresponding private
77 | // key.
78 | type PublicKey interface {
79 | encoding.BinaryMarshaler
80 | encoding.TextMarshaler
81 | crypto.PublicKey
82 |
83 | // Returns the signature scheme for this public key.
84 | Scheme() Scheme
85 | Equal(crypto.PublicKey) bool
86 | }
87 |
88 | // A private key allows one to create signatures.
89 | type PrivateKey interface {
90 | crypto.Signer
91 | crypto.PrivateKey
92 | encoding.BinaryMarshaler
93 | encoding.BinaryUnmarshaler
94 |
95 | // Returns the signature scheme for this private key.
96 | Scheme() Scheme
97 | Equal(crypto.PrivateKey) bool
98 | }
99 |
100 | // A Scheme represents a specific instance of a signature scheme.
101 | type Scheme interface {
102 | // Name of the scheme.
103 | Name() string
104 |
105 | // GenerateKey creates a new key-pair.
106 | GenerateKey() (PublicKey, PrivateKey, error)
107 |
108 | // Creates a signature using the PrivateKey on the given message and
109 | // returns the signature. opts are additional options which can be nil.
110 | //
111 | // Panics if key is nil or wrong type or opts context is not supported.
112 | Sign(sk PrivateKey, message []byte, opts *SignatureOpts) []byte
113 |
114 | // Checks whether the given signature is a valid signature set by
115 | // the private key corresponding to the given public key on the
116 | // given message. opts are additional options which can be nil.
117 | //
118 | // Panics if key is nil or wrong type or opts context is not supported.
119 | Verify(pk PublicKey, message []byte, signature []byte, opts *SignatureOpts) bool
120 |
121 | // Deterministically derives a keypair from a seed. If you're unsure,
122 | // you're better off using GenerateKey().
123 | //
124 | // Panics if seed is not of length SeedSize().
125 | DeriveKey(seed []byte) (PublicKey, PrivateKey)
126 |
127 | // Unmarshals a PublicKey from the provided buffer.
128 | UnmarshalBinaryPublicKey([]byte) (PublicKey, error)
129 |
130 | // Unmarshals a PublicKey from the provided buffer.
131 | UnmarshalBinaryPrivateKey([]byte) (PrivateKey, error)
132 |
133 | // Size of binary marshalled public keys.
134 | PublicKeySize() int
135 |
136 | // Size of binary marshalled public keys.
137 | PrivateKeySize() int
138 |
139 | // Size of signatures.
140 | SignatureSize() int
141 |
142 | // Size of seeds.
143 | SeedSize() int
144 |
145 | // Returns whether contexts are supported.
146 | SupportsContext() bool
147 | }
148 |
149 | var (
150 | // ErrTypeMismatch is the error used if types of, for instance, private
151 | // and public keys don't match.
152 | ErrTypeMismatch = errors.New("types mismatch")
153 |
154 | // ErrSeedSize is the error used if the provided seed is of the wrong
155 | // size.
156 | ErrSeedSize = errors.New("wrong seed size")
157 |
158 | // ErrPubKeySize is the error used if the provided public key is of
159 | // the wrong size.
160 | ErrPubKeySize = errors.New("wrong size for public key")
161 |
162 | // ErrPrivKeySize is the error used if the provided private key is of
163 | // the wrong size.
164 | ErrPrivKeySize = errors.New("wrong size for private key")
165 |
166 | // ErrContextNotSupported is the error used if a context is not
167 | // supported.
168 | ErrContextNotSupported = errors.New("context not supported")
169 | )
170 |
--------------------------------------------------------------------------------
/kem/interfaces.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2019 Cloudflare. All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are
6 | met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above
11 | copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the
13 | distribution.
14 | * Neither the name of Cloudflare nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
30 | ========================================================================
31 |
32 | Copyright (c) 2009 The Go Authors. All rights reserved.
33 |
34 | Redistribution and use in source and binary forms, with or without
35 | modification, are permitted provided that the following conditions are
36 | met:
37 |
38 | * Redistributions of source code must retain the above copyright
39 | notice, this list of conditions and the following disclaimer.
40 | * Redistributions in binary form must reproduce the above
41 | copyright notice, this list of conditions and the following disclaimer
42 | in the documentation and/or other materials provided with the
43 | distribution.
44 | * Neither the name of Google Inc. nor the names of its
45 | contributors may be used to endorse or promote products derived from
46 | this software without specific prior written permission.
47 |
48 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
49 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
50 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
51 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
52 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
53 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
54 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
55 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
56 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
57 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
58 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59 |
60 | */
61 |
62 | // Package kem provides a unified interface for KEM schemes.
63 | //
64 | // A register of schemes is available in the package
65 | //
66 | // github.com/katzenpost/hpqc/kem/schemes
67 | package kem
68 |
69 | import (
70 | "encoding"
71 | "errors"
72 | )
73 |
74 | // A KEM public key
75 | type PublicKey interface {
76 | encoding.TextMarshaler
77 | encoding.BinaryMarshaler
78 |
79 | // Returns the scheme for this public key
80 | Scheme() Scheme
81 |
82 | Equal(PublicKey) bool
83 | }
84 |
85 | // A KEM private key
86 | type PrivateKey interface {
87 | encoding.BinaryMarshaler
88 |
89 | // Returns the scheme for this private key
90 | Scheme() Scheme
91 |
92 | // Equal returns true if the two keys are equal.
93 | Equal(PrivateKey) bool
94 |
95 | // Public returns the public key related to this private key.
96 | Public() PublicKey
97 | }
98 |
99 | // A Scheme represents a specific instance of a KEM.
100 | type Scheme interface {
101 | // Name of the scheme
102 | Name() string
103 |
104 | // GenerateKeyPair creates a new key pair.
105 | GenerateKeyPair() (PublicKey, PrivateKey, error)
106 |
107 | // Encapsulate generates a shared key ss for the public key and
108 | // encapsulates it into a ciphertext ct.
109 | Encapsulate(pk PublicKey) (ct, ss []byte, err error)
110 |
111 | // Returns the shared key encapsulated in ciphertext ct for the
112 | // private key sk.
113 | Decapsulate(sk PrivateKey, ct []byte) ([]byte, error)
114 |
115 | // UnmarshalBinaryPublicKey unmarshals a PublicKey from the provided buffer.
116 | UnmarshalBinaryPublicKey([]byte) (PublicKey, error)
117 |
118 | // UnmarshalBinaryPrivateKey unmarshals a PrivateKey from the provided buffer.
119 | UnmarshalBinaryPrivateKey([]byte) (PrivateKey, error)
120 |
121 | // UnmarshalTextPublicKey unmarshals a PublicKey from the provided text.
122 | UnmarshalTextPublicKey([]byte) (PublicKey, error)
123 |
124 | // UnmarshalTextPrivateKey unmarshals a PrivateKey from the provided text.
125 | UnmarshalTextPrivateKey([]byte) (PrivateKey, error)
126 |
127 | // Size of encapsulated keys.
128 | CiphertextSize() int
129 |
130 | // Size of established shared keys.
131 | SharedKeySize() int
132 |
133 | // Size of packed private keys.
134 | PrivateKeySize() int
135 |
136 | // Size of packed public keys.
137 | PublicKeySize() int
138 |
139 | // DeriveKeyPair deterministicallly derives a pair of keys from a seed.
140 | // Panics if the length of seed is not equal to the value returned by
141 | // SeedSize.
142 | DeriveKeyPair(seed []byte) (PublicKey, PrivateKey)
143 |
144 | // Size of seed used in DeriveKey
145 | SeedSize() int
146 | }
147 |
148 | var (
149 | // ErrTypeMismatch is the error used if types of, for instance, private
150 | // and public keys don't match
151 | ErrTypeMismatch = errors.New("types mismatch")
152 |
153 | // ErrSeedSize is the error used if the provided seed is of the wrong
154 | // size.
155 | ErrSeedSize = errors.New("wrong seed size")
156 |
157 | // ErrPubKeySize is the error used if the provided public key is of
158 | // the wrong size.
159 | ErrPubKeySize = errors.New("wrong size for public key")
160 |
161 | // ErrCiphertextSize is the error used if the provided ciphertext
162 | // is of the wrong size.
163 | ErrCiphertextSize = errors.New("wrong size for ciphertext")
164 |
165 | // ErrPrivKeySize is the error used if the provided private key is of
166 | // the wrong size.
167 | ErrPrivKeySize = errors.New("wrong size for private key")
168 |
169 | // ErrPubKey is the error used if the provided public key is invalid.
170 | ErrPubKey = errors.New("invalid public key")
171 |
172 | // ErrCipherText is the error used if the provided ciphertext is invalid.
173 | ErrCipherText = errors.New("invalid ciphertext")
174 | )
175 |
--------------------------------------------------------------------------------
/sign/schemes/schemes_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2019 Cloudflare. All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are
6 | met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above
11 | copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the
13 | distribution.
14 | * Neither the name of Cloudflare nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
30 | ========================================================================
31 |
32 | Copyright (c) 2009 The Go Authors. All rights reserved.
33 |
34 | Redistribution and use in source and binary forms, with or without
35 | modification, are permitted provided that the following conditions are
36 | met:
37 |
38 | * Redistributions of source code must retain the above copyright
39 | notice, this list of conditions and the following disclaimer.
40 | * Redistributions in binary form must reproduce the above
41 | copyright notice, this list of conditions and the following disclaimer
42 | in the documentation and/or other materials provided with the
43 | distribution.
44 | * Neither the name of Google Inc. nor the names of its
45 | contributors may be used to endorse or promote products derived from
46 | this software without specific prior written permission.
47 |
48 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
49 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
50 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
51 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
52 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
53 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
54 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
55 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
56 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
57 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
58 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59 |
60 | */
61 |
62 | package schemes_test
63 |
64 | import (
65 | "fmt"
66 | "testing"
67 |
68 | "github.com/katzenpost/hpqc/sign"
69 | "github.com/katzenpost/hpqc/sign/schemes"
70 | )
71 |
72 | func TestCaseSensitivity(t *testing.T) {
73 | if schemes.ByName("ed25519") != schemes.ByName("Ed25519") {
74 | t.Fatal()
75 | }
76 | }
77 |
78 | func TestApi(t *testing.T) {
79 | allSchemes := schemes.All()
80 | for _, scheme := range allSchemes {
81 | scheme := scheme
82 | t.Run(scheme.Name(), func(t *testing.T) {
83 | if scheme == nil {
84 | t.Fatal()
85 | }
86 |
87 | t.Logf("%s Signature size is %d\n", scheme.Name(), scheme.SignatureSize())
88 | t.Logf("%s Public Key size is %d\n", scheme.Name(), scheme.PublicKeySize())
89 |
90 | pk, sk, err := scheme.GenerateKey()
91 | if err != nil {
92 | t.Fatal()
93 | }
94 |
95 | packedPk, err := pk.MarshalBinary()
96 | if err != nil {
97 | t.Fatal()
98 | }
99 |
100 | if len(packedPk) != scheme.PublicKeySize() {
101 | t.Fatal()
102 | }
103 |
104 | packedSk, err := sk.MarshalBinary()
105 | if err != nil {
106 | t.Fatal(err)
107 | }
108 |
109 | if len(packedSk) != scheme.PrivateKeySize() {
110 | t.Fatal()
111 | }
112 |
113 | pk2, err := scheme.UnmarshalBinaryPublicKey(packedPk)
114 | if err != nil {
115 | t.Fatal(err)
116 | }
117 |
118 | sk2, err := scheme.UnmarshalBinaryPrivateKey(packedSk)
119 | if err != nil {
120 | t.Fatal(err)
121 | }
122 |
123 | if !sk.Equal(sk2) {
124 | t.Fatal()
125 | }
126 |
127 | if !pk.Equal(pk2) {
128 | t.Fatal()
129 | }
130 |
131 | msg := []byte(fmt.Sprintf("Signing with %s", scheme.Name()))
132 | opts := &sign.SignatureOpts{}
133 | if scheme.SupportsContext() {
134 | opts.Context = "A context"
135 | }
136 | sig := scheme.Sign(sk, msg, opts)
137 |
138 | if scheme.SignatureSize() != len(sig) {
139 | t.Fatal()
140 | }
141 |
142 | if !scheme.Verify(pk2, msg, sig, opts) {
143 | t.Fatal()
144 | }
145 |
146 | if scheme.SupportsContext() {
147 | opts2 := opts
148 | opts2.Context = "Wrong context"
149 | if scheme.Verify(pk2, msg, sig, opts2) {
150 | t.Fatal()
151 | }
152 | }
153 |
154 | sig[0]++
155 | if scheme.Verify(pk2, msg, sig, opts) {
156 | t.Fatal()
157 | }
158 |
159 | scheme2 := schemes.ByName(scheme.Name())
160 | if scheme2 == nil || scheme2 != scheme {
161 | t.Fatal()
162 | }
163 |
164 | if pk.Scheme() != scheme {
165 | t.Fatal()
166 | }
167 |
168 | if sk.Scheme() != scheme {
169 | t.Fatal()
170 | }
171 | })
172 | }
173 | }
174 |
175 | func BenchmarkGenerateKeyPair(b *testing.B) {
176 | allSchemes := schemes.All()
177 | for _, scheme := range allSchemes {
178 | scheme := scheme
179 | b.Run(scheme.Name(), func(b *testing.B) {
180 | for i := 0; i < b.N; i++ {
181 | _, _, _ = scheme.GenerateKey()
182 | }
183 | })
184 | }
185 | }
186 |
187 | func BenchmarkSign(b *testing.B) {
188 | allSchemes := schemes.All()
189 | opts := &sign.SignatureOpts{}
190 | for _, scheme := range allSchemes {
191 | msg := []byte(fmt.Sprintf("Signing with %s", scheme.Name()))
192 | scheme := scheme
193 | _, sk, _ := scheme.GenerateKey()
194 | b.Run(scheme.Name(), func(b *testing.B) {
195 | for i := 0; i < b.N; i++ {
196 | _ = scheme.Sign(sk, msg, opts)
197 | }
198 | })
199 | }
200 | }
201 |
202 | func BenchmarkVerify(b *testing.B) {
203 | allSchemes := schemes.All()
204 | opts := &sign.SignatureOpts{}
205 | for _, scheme := range allSchemes {
206 | msg := []byte(fmt.Sprintf("Signing with %s", scheme.Name()))
207 | scheme := scheme
208 | pk, sk, _ := scheme.GenerateKey()
209 | sig := scheme.Sign(sk, msg, opts)
210 | b.Run(scheme.Name(), func(b *testing.B) {
211 | for i := 0; i < b.N; i++ {
212 | _ = scheme.Verify(pk, msg, sig, opts)
213 | }
214 | })
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/sign/hybrid/hybrid.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: (c) 2024 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package hybrid
5 |
6 | import (
7 | "crypto"
8 | "crypto/hmac"
9 | "io"
10 |
11 | "github.com/katzenpost/hpqc/sign"
12 | "github.com/katzenpost/hpqc/sign/pem"
13 | )
14 |
15 | // Scheme is for hybrid signature schemes.
16 | type Scheme struct {
17 | name string
18 | first sign.Scheme
19 | second sign.Scheme
20 | }
21 |
22 | var _ sign.Scheme = (*Scheme)(nil)
23 | var _ sign.PrivateKey = (*PrivateKey)(nil)
24 | var _ sign.PublicKey = (*PublicKey)(nil)
25 |
26 | // New creates a new hybrid signature scheme given the two signature schemes,
27 | // assumign one of them is classical and the other post quantum.
28 | func New(name string, first sign.Scheme, second sign.Scheme) sign.Scheme {
29 | return &Scheme{
30 | name: name,
31 | first: first,
32 | second: second,
33 | }
34 | }
35 |
36 | func (s *Scheme) Name() string {
37 | return s.name
38 | }
39 |
40 | func (s *Scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) {
41 | pub1, priv1, err := s.first.GenerateKey()
42 | if err != nil {
43 | return nil, nil, err
44 | }
45 | pub2, priv2, err := s.second.GenerateKey()
46 | if err != nil {
47 | return nil, nil, err
48 | }
49 | return &PublicKey{
50 | scheme: s,
51 | first: pub1,
52 | second: pub2,
53 | }, &PrivateKey{
54 | scheme: s,
55 | first: priv1,
56 | second: priv2,
57 | }, nil
58 | }
59 |
60 | func (s *Scheme) Sign(sk sign.PrivateKey, message []byte, opts *sign.SignatureOpts) []byte {
61 | return append(s.first.Sign(sk.(*PrivateKey).first, message, opts),
62 | s.second.Sign(sk.(*PrivateKey).second, message, opts)...)
63 | }
64 |
65 | func (s *Scheme) Verify(pk sign.PublicKey, message []byte, signature []byte, opts *sign.SignatureOpts) bool {
66 | if len(signature) != s.SignatureSize() {
67 | panic("incorrect signature size")
68 | }
69 | if !s.first.Verify(pk.(*PublicKey).first, message, signature[:s.first.SignatureSize()], opts) {
70 | return false
71 | }
72 | if !s.second.Verify(pk.(*PublicKey).second, message, signature[s.first.SignatureSize():], opts) {
73 | return false
74 | }
75 | return true
76 | }
77 |
78 | func (s *Scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) {
79 | if len(seed) != s.SeedSize() {
80 | panic("wrong seed size")
81 | }
82 | pub1, priv1 := s.first.DeriveKey(seed[:s.first.SeedSize()])
83 | pub2, priv2 := s.second.DeriveKey(seed[s.first.SeedSize():])
84 | return &PublicKey{
85 | scheme: s,
86 | first: pub1,
87 | second: pub2,
88 | }, &PrivateKey{
89 | scheme: s,
90 | first: priv1,
91 | second: priv2,
92 | }
93 | }
94 |
95 | func (s *Scheme) UnmarshalBinaryPublicKey(b []byte) (sign.PublicKey, error) {
96 | pub1, err := s.first.UnmarshalBinaryPublicKey(b[:s.first.PublicKeySize()])
97 | if err != nil {
98 | return nil, err
99 | }
100 | pub2, err := s.second.UnmarshalBinaryPublicKey(b[s.first.PublicKeySize():])
101 | if err != nil {
102 | return nil, err
103 | }
104 | return &PublicKey{
105 | scheme: s,
106 | first: pub1,
107 | second: pub2,
108 | }, nil
109 | }
110 |
111 | func (s *Scheme) UnmarshalBinaryPrivateKey(b []byte) (sign.PrivateKey, error) {
112 | priv1, err := s.first.UnmarshalBinaryPrivateKey(b[:s.first.PrivateKeySize()])
113 | if err != nil {
114 | return nil, err
115 | }
116 | priv2, err := s.second.UnmarshalBinaryPrivateKey(b[s.first.PrivateKeySize():])
117 | if err != nil {
118 | return nil, err
119 | }
120 | return &PrivateKey{
121 | scheme: s,
122 | first: priv1,
123 | second: priv2,
124 | }, nil
125 | }
126 |
127 | func (s *Scheme) PublicKeySize() int {
128 | return s.first.PublicKeySize() + s.second.PublicKeySize()
129 | }
130 |
131 | func (s *Scheme) PrivateKeySize() int {
132 | return s.first.PrivateKeySize() + s.second.PrivateKeySize()
133 | }
134 |
135 | func (s *Scheme) SignatureSize() int {
136 | return s.first.SignatureSize() + s.second.SignatureSize()
137 | }
138 |
139 | func (s *Scheme) SeedSize() int {
140 | return s.first.SeedSize() + s.second.SeedSize()
141 | }
142 |
143 | func (s *Scheme) SupportsContext() bool {
144 | if !s.first.SupportsContext() {
145 | return false
146 | }
147 | if !s.second.SupportsContext() {
148 | return false
149 | }
150 | return true
151 | }
152 |
153 | // PrivateKey is the private key in hybrid signature scheme.
154 | type PrivateKey struct {
155 | scheme *Scheme
156 | publicKey *PublicKey
157 | first sign.PrivateKey
158 | second sign.PrivateKey
159 | }
160 |
161 | func (p *PrivateKey) Scheme() sign.Scheme {
162 | return p.scheme
163 | }
164 |
165 | func (p *PrivateKey) Equal(key crypto.PrivateKey) bool {
166 | blob1, err := p.MarshalBinary()
167 | if err != nil {
168 | panic(err)
169 | }
170 | blob2, err := key.(*PrivateKey).MarshalBinary()
171 | if err != nil {
172 | panic(err)
173 | }
174 | return hmac.Equal(blob1, blob2)
175 | }
176 |
177 | func (p *PrivateKey) Public() crypto.PublicKey {
178 | panic("not implemented")
179 | }
180 |
181 | func (p *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) {
182 | sig1, err := p.first.Sign(rand, digest, opts)
183 | if err != nil {
184 | return nil, err
185 | }
186 | sig2, err := p.second.Sign(rand, digest, opts)
187 | if err != nil {
188 | return nil, err
189 | }
190 | return append(sig1, sig2...), nil
191 | }
192 |
193 | func (p *PrivateKey) MarshalBinary() ([]byte, error) {
194 | blob1, err := p.first.MarshalBinary()
195 | if err != nil {
196 | return nil, err
197 | }
198 | blob2, err := p.second.MarshalBinary()
199 | if err != nil {
200 | return nil, err
201 | }
202 | return append(blob1, blob2...), nil
203 | }
204 |
205 | func (p *PrivateKey) UnmarshalBinary(b []byte) error {
206 | err := p.first.UnmarshalBinary(b[:p.first.Scheme().PrivateKeySize()])
207 | if err != nil {
208 | return err
209 | }
210 | return p.second.UnmarshalBinary(b[p.first.Scheme().PrivateKeySize():])
211 | }
212 |
213 | // PublicKey is the public key in hybrid signature scheme.
214 | type PublicKey struct {
215 | scheme *Scheme
216 | first sign.PublicKey
217 | second sign.PublicKey
218 | }
219 |
220 | func (p *PublicKey) Scheme() sign.Scheme {
221 | return p.scheme
222 | }
223 |
224 | func (p *PublicKey) Equal(key crypto.PublicKey) bool {
225 | blob1, err := p.MarshalBinary()
226 | if err != nil {
227 | panic(err)
228 | }
229 | var blob2 []byte
230 | switch v := key.(type) {
231 | case []byte:
232 | blob2 = v
233 | case *PublicKey:
234 | blob2, err = v.MarshalBinary()
235 | if err != nil {
236 | panic(err)
237 | }
238 | default:
239 | panic("type assertion failed")
240 | }
241 | return hmac.Equal(blob1, blob2)
242 | }
243 |
244 | func (p *PublicKey) MarshalBinary() ([]byte, error) {
245 | blob1, err := p.first.MarshalBinary()
246 | if err != nil {
247 | return nil, err
248 | }
249 | blob2, err := p.second.MarshalBinary()
250 | if err != nil {
251 | return nil, err
252 | }
253 | return append(blob1, blob2...), nil
254 | }
255 |
256 | func (p *PublicKey) MarshalText() (text []byte, err error) {
257 | return pem.ToPublicPEMBytes(p), nil
258 | }
259 |
--------------------------------------------------------------------------------
/kem/hybrid/hybrid.go:
--------------------------------------------------------------------------------
1 | // Package hybrid defines several hybrid classical/quantum KEMs.
2 | //
3 | // KEMs are combined by hashing of shared secrets, cipher texts,
4 | // public keys, etc, see
5 | //
6 | // https://eprint.iacr.org/2018/024.pdf
7 | //
8 | // For deriving a KEM keypair deterministically and encapsulating
9 | // deterministically, we expand a single seed to both using Blake2b hash and then XOF,
10 | // so that a non-uniform seed (such as a shared secret generated by a hybrid
11 | // KEM where one of the KEMs is weak) doesn't impact just one of the KEMs.
12 |
13 | package hybrid
14 |
15 | import (
16 | "errors"
17 | "fmt"
18 |
19 | "github.com/katzenpost/hpqc/kem"
20 | "github.com/katzenpost/hpqc/kem/pem"
21 | "github.com/katzenpost/hpqc/kem/util"
22 | "golang.org/x/crypto/blake2b"
23 | )
24 |
25 | var (
26 | ErrUninitialized = errors.New("public or private key not initialized")
27 | )
28 |
29 | var _ kem.PrivateKey = (*PrivateKey)(nil)
30 | var _ kem.PublicKey = (*PublicKey)(nil)
31 | var _ kem.Scheme = (*Scheme)(nil)
32 |
33 | // Public key of a hybrid KEM.
34 | type PublicKey struct {
35 | scheme *Scheme
36 | first kem.PublicKey
37 | second kem.PublicKey
38 | }
39 |
40 | // Private key of a hybrid KEM.
41 | type PrivateKey struct {
42 | scheme *Scheme
43 | first kem.PrivateKey
44 | second kem.PrivateKey
45 | }
46 |
47 | // Scheme for a hybrid KEM.
48 | type Scheme struct {
49 | name string
50 | first kem.Scheme
51 | second kem.Scheme
52 | }
53 |
54 | // New creates a new hybrid KEM given the first and second KEMs.
55 | func New(name string, first kem.Scheme, second kem.Scheme) *Scheme {
56 | return &Scheme{
57 | name: name,
58 | first: first,
59 | second: second,
60 | }
61 | }
62 |
63 | func (sch *Scheme) Name() string { return sch.name }
64 | func (sch *Scheme) PublicKeySize() int {
65 | return sch.first.PublicKeySize() + sch.second.PublicKeySize()
66 | }
67 |
68 | func (sch *Scheme) PrivateKeySize() int {
69 | return sch.first.PrivateKeySize() + sch.second.PrivateKeySize()
70 | }
71 |
72 | func (sch *Scheme) SeedSize() int {
73 | return sch.first.SeedSize() + sch.second.SeedSize()
74 | }
75 |
76 | func (sch *Scheme) SharedKeySize() int {
77 | return blake2b.Size256
78 | }
79 |
80 | func (sch *Scheme) CiphertextSize() int {
81 | return sch.first.CiphertextSize() + sch.second.CiphertextSize()
82 | }
83 |
84 | func (sk *PrivateKey) Scheme() kem.Scheme { return sk.scheme }
85 | func (pk *PublicKey) Scheme() kem.Scheme { return pk.scheme }
86 |
87 | func (sk *PrivateKey) MarshalBinary() ([]byte, error) {
88 | if sk.first == nil || sk.second == nil {
89 | return nil, ErrUninitialized
90 | }
91 | first, err := sk.first.MarshalBinary()
92 | if err != nil {
93 | return nil, err
94 | }
95 | second, err := sk.second.MarshalBinary()
96 | if err != nil {
97 | return nil, err
98 | }
99 | return append(first, second...), nil
100 | }
101 |
102 | func (sk *PublicKey) MarshalText() (text []byte, err error) {
103 | return pem.ToPublicPEMBytes(sk), nil
104 | }
105 |
106 | func (sk *PrivateKey) Equal(other kem.PrivateKey) bool {
107 | oth, ok := other.(*PrivateKey)
108 | if !ok {
109 | return false
110 | }
111 | return sk.first.Equal(oth.first) && sk.second.Equal(oth.second)
112 | }
113 |
114 | func (sk *PrivateKey) Public() kem.PublicKey {
115 | return &PublicKey{sk.scheme, sk.first.Public(), sk.second.Public()}
116 | }
117 |
118 | func (pk *PublicKey) Equal(other kem.PublicKey) bool {
119 | oth, ok := other.(*PublicKey)
120 | if !ok {
121 | return false
122 | }
123 | return pk.first.Equal(oth.first) && pk.second.Equal(oth.second)
124 | }
125 |
126 | func (pk *PublicKey) MarshalBinary() ([]byte, error) {
127 | if pk.first == nil || pk.second == nil {
128 | return nil, ErrUninitialized
129 | }
130 | first, err := pk.first.MarshalBinary()
131 | if err != nil {
132 | return nil, err
133 | }
134 | second, err := pk.second.MarshalBinary()
135 | if err != nil {
136 | return nil, err
137 | }
138 | return append(first, second...), nil
139 | }
140 |
141 | func (sch *Scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) {
142 | pk1, sk1, err := sch.first.GenerateKeyPair()
143 | if err != nil {
144 | return nil, nil, err
145 | }
146 | pk2, sk2, err := sch.second.GenerateKeyPair()
147 | if err != nil {
148 | return nil, nil, err
149 | }
150 |
151 | return &PublicKey{sch, pk1, pk2}, &PrivateKey{sch, sk1, sk2}, nil
152 | }
153 |
154 | func (sch *Scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) {
155 | if len(seed) != sch.first.SeedSize()+sch.second.SeedSize() {
156 | panic(fmt.Sprintf("seed size must be %d", sch.first.SeedSize()+sch.second.SeedSize()))
157 | }
158 |
159 | pk1, sk1 := sch.first.DeriveKeyPair(seed[:sch.first.SeedSize()])
160 | pk2, sk2 := sch.second.DeriveKeyPair(seed[sch.first.SeedSize():])
161 |
162 | return &PublicKey{sch, pk1, pk2}, &PrivateKey{sch, sk1, sk2}
163 | }
164 |
165 | func (sch *Scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) {
166 | pub, ok := pk.(*PublicKey)
167 | if !ok {
168 | return nil, nil, kem.ErrTypeMismatch
169 | }
170 |
171 | ct1, ss1, err := sch.first.Encapsulate(pub.first)
172 | if err != nil {
173 | return nil, nil, err
174 | }
175 |
176 | ct2, ss2, err := sch.second.Encapsulate(pub.second)
177 | if err != nil {
178 | return nil, nil, err
179 | }
180 |
181 | return append(ct1, ct2...), util.PairSplitPRF(ss1, ss2, ct1, ct2), nil
182 | }
183 |
184 | func (sch *Scheme) EncapsulateDeterministically(publicKey kem.PublicKey, seed []byte) (ct, ss []byte, err error) {
185 | panic("not implemented")
186 | }
187 |
188 | func (sch *Scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) {
189 | if len(ct) != sch.CiphertextSize() {
190 | return nil, kem.ErrCiphertextSize
191 | }
192 |
193 | priv, ok := sk.(*PrivateKey)
194 | if !ok {
195 | return nil, kem.ErrTypeMismatch
196 | }
197 |
198 | firstSize := sch.first.CiphertextSize()
199 | ss1, err := sch.first.Decapsulate(priv.first, ct[:firstSize])
200 | if err != nil {
201 | return nil, err
202 | }
203 | ss2, err := sch.second.Decapsulate(priv.second, ct[firstSize:])
204 | if err != nil {
205 | return nil, err
206 | }
207 |
208 | return util.PairSplitPRF(ss1, ss2, ct[:firstSize], ct[firstSize:]), nil
209 | }
210 |
211 | func (sch *Scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) {
212 | if len(buf) != sch.PublicKeySize() {
213 | return nil, kem.ErrPubKeySize
214 | }
215 | firstSize := sch.first.PublicKeySize()
216 | pk1, err := sch.first.UnmarshalBinaryPublicKey(buf[:firstSize])
217 | if err != nil {
218 | return nil, err
219 | }
220 | pk2, err := sch.second.UnmarshalBinaryPublicKey(buf[firstSize:])
221 | if err != nil {
222 | return nil, err
223 | }
224 | return &PublicKey{sch, pk1, pk2}, nil
225 | }
226 |
227 | func (sch *Scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) {
228 | if len(buf) != sch.PrivateKeySize() {
229 | return nil, kem.ErrPrivKeySize
230 | }
231 | firstSize := sch.first.PrivateKeySize()
232 | sk1, err := sch.first.UnmarshalBinaryPrivateKey(buf[:firstSize])
233 | if err != nil {
234 | return nil, err
235 | }
236 | sk2, err := sch.second.UnmarshalBinaryPrivateKey(buf[firstSize:])
237 | if err != nil {
238 | return nil, err
239 | }
240 | return &PrivateKey{sch, sk1, sk2}, nil
241 | }
242 |
243 | func (a *Scheme) UnmarshalTextPublicKey(text []byte) (kem.PublicKey, error) {
244 | return pem.FromPublicPEMBytes(text, a)
245 | }
246 |
247 | func (a *Scheme) UnmarshalTextPrivateKey(text []byte) (kem.PrivateKey, error) {
248 | return pem.FromPrivatePEMBytes(text, a)
249 | }
250 |
--------------------------------------------------------------------------------
/nike/csidh/csidh.go:
--------------------------------------------------------------------------------
1 | //go:build amd64 || arm64
2 |
3 | // csidh.go - Adapts csidh module to our NIKE interface.
4 | // Copyright (C) 2022 David Stainton.
5 | //
6 | // This program is free software: you can redistribute it and/or modify
7 | // it under the terms of the GNU Affero General Public License as
8 | // published by the Free Software Foundation, either version 3 of the
9 | // License, or (at your option) any later version.
10 | //
11 | // This program is distributed in the hope that it will be useful,
12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | // GNU Affero General Public License for more details.
15 | //
16 | // You should have received a copy of the GNU Affero General Public License
17 | // along with this program. If not, see .
18 |
19 | package csidh
20 |
21 | import (
22 | "encoding/base64"
23 | "errors"
24 | "io"
25 |
26 | "github.com/henrydcase/nobs/dh/csidh"
27 |
28 | "github.com/katzenpost/hpqc/nike"
29 | "github.com/katzenpost/hpqc/rand"
30 | )
31 |
32 | // CSIDHScheme is the nobs CSIDH-512 NIKE.
33 | var NOBS_CSIDH512Scheme nike.Scheme
34 |
35 | var _ nike.PrivateKey = (*PrivateKey)(nil)
36 | var _ nike.PublicKey = (*PublicKey)(nil)
37 | var _ nike.Scheme = (*CsidhNike)(nil)
38 |
39 | type CsidhNike struct{}
40 |
41 | func (e *CsidhNike) Name() string {
42 | return "CSIDH-512-nobs"
43 | }
44 |
45 | func (e *CsidhNike) PublicKeySize() int {
46 | return csidh.PublicKeySize
47 | }
48 |
49 | func (e *CsidhNike) PrivateKeySize() int {
50 | return csidh.PrivateKeySize
51 | }
52 |
53 | func (e *CsidhNike) GeneratePrivateKey(rng io.Reader) nike.PrivateKey {
54 | privateKey := new(csidh.PrivateKey)
55 | err := csidh.GeneratePrivateKey(privateKey, rand.Reader)
56 | if err != nil {
57 | panic(err)
58 | }
59 | return &PrivateKey{
60 | privateKey: privateKey,
61 | }
62 | }
63 |
64 | func (e *CsidhNike) GenerateKeyPairFromEntropy(rng io.Reader) (nike.PublicKey, nike.PrivateKey, error) {
65 | privateKey := new(csidh.PrivateKey)
66 | err := csidh.GeneratePrivateKey(privateKey, rng)
67 | if err != nil {
68 | return nil, nil, err
69 | }
70 | privKey := &PrivateKey{
71 | privateKey: privateKey,
72 | }
73 | publicKey := e.DerivePublicKey(privKey)
74 | return publicKey, privKey, nil
75 | }
76 |
77 | func (e *CsidhNike) GenerateKeyPair() (nike.PublicKey, nike.PrivateKey, error) {
78 | privateKey := new(csidh.PrivateKey)
79 | err := csidh.GeneratePrivateKey(privateKey, rand.Reader)
80 | if err != nil {
81 | return nil, nil, err
82 | }
83 | privKey := &PrivateKey{
84 | privateKey: privateKey,
85 | }
86 | publicKey := e.DerivePublicKey(privKey)
87 | return publicKey, privKey, nil
88 | }
89 |
90 | func (e *CsidhNike) DeriveSecret(privKey nike.PrivateKey, pubKey nike.PublicKey) []byte {
91 | sharedSecret := &[64]byte{}
92 | ok := csidh.DeriveSecret(sharedSecret, pubKey.(*PublicKey).publicKey, privKey.(*PrivateKey).privateKey, rand.Reader)
93 | if !ok {
94 | panic("csidh.DeriveSecret failed!")
95 | }
96 | return sharedSecret[:]
97 | }
98 |
99 | func (e *CsidhNike) DerivePublicKey(privKey nike.PrivateKey) nike.PublicKey {
100 | pubKey := new(csidh.PublicKey)
101 | csidh.GeneratePublicKey(pubKey, privKey.(*PrivateKey).privateKey, rand.Reader)
102 | return &PublicKey{
103 | publicKey: pubKey,
104 | }
105 | }
106 |
107 | func (e CsidhNike) Blind(groupMember nike.PublicKey, blindingFactor nike.PrivateKey) (blindedGroupMember nike.PublicKey) {
108 | panic("Blind operation not implemented")
109 | }
110 |
111 | func (e *CsidhNike) NewEmptyPublicKey() nike.PublicKey {
112 | return &PublicKey{
113 | publicKey: new(csidh.PublicKey),
114 | }
115 | }
116 |
117 | func (e *CsidhNike) NewEmptyPrivateKey() nike.PrivateKey {
118 | return &PrivateKey{
119 | privateKey: new(csidh.PrivateKey),
120 | }
121 | }
122 |
123 | func (e *CsidhNike) UnmarshalBinaryPublicKey(b []byte) (nike.PublicKey, error) {
124 | pubKey := new(csidh.PublicKey)
125 | ok := pubKey.Import(b)
126 | if !ok {
127 | return nil, errors.New("CSIDH public key import failure")
128 | }
129 | return &PublicKey{
130 | publicKey: pubKey,
131 | }, nil
132 | }
133 |
134 | func (e *CsidhNike) UnmarshalBinaryPrivateKey(b []byte) (nike.PrivateKey, error) {
135 | privKey := new(csidh.PrivateKey)
136 | ok := privKey.Import(b)
137 | if !ok {
138 | return nil, errors.New("CSIDH private key import failure")
139 | }
140 | return &PrivateKey{
141 | privateKey: privKey,
142 | }, nil
143 | }
144 |
145 | type PublicKey struct {
146 | publicKey *csidh.PublicKey
147 | }
148 |
149 | func (p *PublicKey) Blind(blindingFactor nike.PrivateKey) error {
150 | panic("Blind operation not implemented")
151 | }
152 |
153 | func (p *PublicKey) Reset() {
154 | zeros := make([]byte, csidh.PublicKeySize)
155 | err := p.FromBytes(zeros)
156 | if err != nil {
157 | panic(err)
158 | }
159 | }
160 |
161 | func (p *PublicKey) Bytes() []byte {
162 | s := make([]byte, csidh.PublicKeySize)
163 | p.publicKey.Export(s)
164 | return s
165 | }
166 |
167 | func (p *PublicKey) FromBytes(b []byte) error {
168 | p.publicKey = new(csidh.PublicKey)
169 | ok := p.publicKey.Import(b)
170 | if !ok {
171 | return errors.New("csidh public key import failure")
172 | }
173 | return nil
174 | }
175 |
176 | // MarshalBinary is an implementation of a method on the
177 | // BinaryMarshaler interface defined in https://golang.org/pkg/encoding/
178 | func (p *PublicKey) MarshalBinary() ([]byte, error) {
179 | return p.Bytes(), nil
180 | }
181 |
182 | // UnmarshalBinary is an implementation of a method on the
183 | // BinaryUnmarshaler interface defined in https://golang.org/pkg/encoding/
184 | func (p *PublicKey) UnmarshalBinary(data []byte) error {
185 | return p.FromBytes(data)
186 | }
187 |
188 | // MarshalText is an implementation of a method on the
189 | // TextMarshaler interface defined in https://golang.org/pkg/encoding/
190 | func (p *PublicKey) MarshalText() ([]byte, error) {
191 | return []byte(base64.StdEncoding.EncodeToString(p.Bytes())), nil
192 | }
193 |
194 | // UnmarshalText is an implementation of a method on the
195 | // TextUnmarshaler interface defined in https://golang.org/pkg/encoding/
196 | func (p *PublicKey) UnmarshalText(data []byte) error {
197 | raw, err := base64.StdEncoding.DecodeString(string(data))
198 | if err != nil {
199 | return err
200 | }
201 | return p.FromBytes(raw)
202 | }
203 |
204 | type PrivateKey struct {
205 | privateKey *csidh.PrivateKey
206 | }
207 |
208 | func (p *PrivateKey) Public() nike.PublicKey {
209 | return NOBS_CSIDH512Scheme.DerivePublicKey(p)
210 | }
211 |
212 | func (p *PrivateKey) Reset() {
213 | zeros := make([]byte, csidh.PrivateKeySize)
214 | err := p.FromBytes(zeros)
215 | if err != nil {
216 | panic(err)
217 | }
218 | }
219 |
220 | func (p *PrivateKey) Bytes() []byte {
221 | if p.privateKey == nil {
222 | panic("p.privateKey == nil")
223 | }
224 | s := make([]byte, csidh.PrivateKeySize)
225 | p.privateKey.Export(s)
226 | return s
227 | }
228 |
229 | func (p *PrivateKey) FromBytes(b []byte) error {
230 | p.privateKey = new(csidh.PrivateKey)
231 | ok := p.privateKey.Import(b)
232 | if !ok {
233 | return errors.New("csidh private key import failure")
234 | }
235 | return nil
236 | }
237 |
238 | func (p *PrivateKey) MarshalBinary() ([]byte, error) {
239 | s := make([]byte, csidh.PrivateKeySize)
240 | ok := p.privateKey.Export(s)
241 | if !ok {
242 | return nil, errors.New("MarshalBinary fail")
243 | }
244 | return s, nil
245 | }
246 |
247 | func (p *PrivateKey) UnmarshalBinary(data []byte) error {
248 | return p.FromBytes(data)
249 | }
250 |
251 | func (p *PrivateKey) MarshalText() ([]byte, error) {
252 | return []byte(base64.StdEncoding.EncodeToString(p.Bytes())), nil
253 | }
254 |
255 | func (p *PrivateKey) UnmarshalText(data []byte) error {
256 | raw, err := base64.StdEncoding.DecodeString(string(data))
257 | if err != nil {
258 | return err
259 | }
260 | return p.FromBytes(raw)
261 | }
262 |
263 | func init() {
264 | NOBS_CSIDH512Scheme = new(CsidhNike)
265 | }
266 |
--------------------------------------------------------------------------------
/nike/x448/x448.go:
--------------------------------------------------------------------------------
1 | // SPDX-FileCopyrightText: © 2024 David Stainton
2 | // SPDX-License-Identifier: AGPL-3.0-only
3 |
4 | package x448
5 |
6 | import (
7 | "encoding/base64"
8 | "errors"
9 | "io"
10 |
11 | "github.com/katzenpost/circl/dh/x448"
12 |
13 | "github.com/katzenpost/hpqc/nike"
14 | "github.com/katzenpost/hpqc/rand"
15 | "github.com/katzenpost/hpqc/util"
16 | )
17 |
18 | const (
19 | // GroupElementLength is the length of a ECDH group element in bytes.
20 | GroupElementLength = 56
21 |
22 | // PublicKeySize is the size of a serialized PublicKey in bytes.
23 | PublicKeySize = GroupElementLength
24 |
25 | // PrivateKeySize is the size of a serialized PrivateKey in bytes.
26 | PrivateKeySize = GroupElementLength
27 | )
28 |
29 | var (
30 | // ErrBlindDataSizeInvalid indicates that the blinding data size was invalid.
31 | ErrBlindDataSizeInvalid error = errors.New("x448: blinding data size invalid")
32 |
33 | errInvalidKey = errors.New("x448: invalid key")
34 | )
35 |
36 | var _ nike.PrivateKey = (*PrivateKey)(nil)
37 | var _ nike.PublicKey = (*PublicKey)(nil)
38 | var _ nike.Scheme = (*scheme)(nil)
39 |
40 | // EcdhNike implements the Nike interface using our ecdh module.
41 | type scheme struct {
42 | rng io.Reader
43 | }
44 |
45 | // Scheme instantiates a new X448 scheme given a CSPRNG.
46 | func Scheme(rng io.Reader) *scheme {
47 | return &scheme{
48 | rng: rng,
49 | }
50 | }
51 |
52 | func (e *scheme) GeneratePrivateKey(rng io.Reader) nike.PrivateKey {
53 | privKey, err := NewKeypair(rng)
54 | if err != nil {
55 | panic(err)
56 | }
57 | return privKey
58 | }
59 |
60 | func (e *scheme) GenerateKeyPairFromEntropy(rng io.Reader) (nike.PublicKey, nike.PrivateKey, error) {
61 | privKey, err := NewKeypair(rng)
62 | if err != nil {
63 | return nil, nil, err
64 | }
65 | return privKey.Public(), privKey, nil
66 | }
67 |
68 | func (e *scheme) GenerateKeyPair() (nike.PublicKey, nike.PrivateKey, error) {
69 | return e.GenerateKeyPairFromEntropy(e.rng)
70 | }
71 |
72 | func (e *scheme) Name() string {
73 | return "x448"
74 | }
75 |
76 | // PublicKeySize returns the size in bytes of the public key.
77 | func (e *scheme) PublicKeySize() int {
78 | return PublicKeySize
79 | }
80 |
81 | // PrivateKeySize returns the size in bytes of the private key.
82 | func (e *scheme) PrivateKeySize() int {
83 | return PublicKeySize
84 | }
85 |
86 | // NewEmptyPublicKey returns an uninitialized
87 | // PublicKey which is suitable to be loaded
88 | // via some serialization format via FromBytes
89 | // or FromPEMFile methods.
90 | func (e *scheme) NewEmptyPublicKey() nike.PublicKey {
91 | return &PublicKey{
92 | pubBytes: new(x448.Key),
93 | }
94 | }
95 |
96 | // NewEmptyPrivateKey returns an uninitialized
97 | // PrivateKey which is suitable to be loaded
98 | // via some serialization format via FromBytes
99 | // or FromPEMFile methods.
100 | func (e *scheme) NewEmptyPrivateKey() nike.PrivateKey {
101 | return &PrivateKey{
102 | privBytes: new(x448.Key),
103 | }
104 | }
105 |
106 | // DeriveSecret derives a shared secret given a private key
107 | // from one party and a public key from another.
108 | func (e *scheme) DeriveSecret(privKey nike.PrivateKey, pubKey nike.PublicKey) []byte {
109 | sharedSecret := Exp(privKey.(*PrivateKey).privBytes, (pubKey.(*PublicKey)).pubBytes)
110 | return sharedSecret[:]
111 | }
112 |
113 | // DerivePublicKey derives a public key given a private key.
114 | func (e *scheme) DerivePublicKey(privKey nike.PrivateKey) nike.PublicKey {
115 | return privKey.(*PrivateKey).Public()
116 | }
117 |
118 | func (e *scheme) Blind(groupMember nike.PublicKey, blindingFactor nike.PrivateKey) nike.PublicKey {
119 | sharedSecret := Exp(blindingFactor.(*PrivateKey).privBytes, groupMember.(*PublicKey).pubBytes)
120 | pubKey := new(PublicKey)
121 | err := pubKey.FromBytes(sharedSecret)
122 | if err != nil {
123 | panic(err)
124 | }
125 |
126 | return pubKey
127 | }
128 |
129 | // UnmarshalBinaryPublicKey loads a public key from byte slice.
130 | func (e *scheme) UnmarshalBinaryPublicKey(b []byte) (nike.PublicKey, error) {
131 | pubKey := new(PublicKey)
132 | err := pubKey.FromBytes(b)
133 | if err != nil {
134 | return nil, err
135 | }
136 | return pubKey, nil
137 | }
138 |
139 | // UnmarshalBinaryPrivateKey loads a private key from byte slice.
140 | func (e *scheme) UnmarshalBinaryPrivateKey(b []byte) (nike.PrivateKey, error) {
141 | privKey := new(PrivateKey)
142 | err := privKey.FromBytes(b)
143 | if err != nil {
144 | return nil, err
145 | }
146 | return privKey, err
147 | }
148 |
149 | type PrivateKey struct {
150 | privBytes *x448.Key
151 | }
152 |
153 | func NewKeypair(rng io.Reader) (nike.PrivateKey, error) {
154 | privkey := new(x448.Key)
155 | count, err := rng.Read(privkey[:])
156 | if err != nil {
157 | return nil, err
158 | }
159 | if count != GroupElementLength {
160 | return nil, errors.New("read wrong number of bytes from rng")
161 | }
162 | return &PrivateKey{
163 | privBytes: privkey,
164 | }, nil
165 | }
166 |
167 | func (p *PrivateKey) Public() nike.PublicKey {
168 | pubKey := Scheme(rand.Reader).NewEmptyPublicKey()
169 | expG(pubKey.(*PublicKey).pubBytes, p.privBytes)
170 | return pubKey
171 | }
172 |
173 | func (p *PrivateKey) Reset() {
174 | zeros := make([]byte, PrivateKeySize)
175 | err := p.FromBytes(zeros)
176 | if err != nil {
177 | panic(err)
178 | }
179 | }
180 |
181 | func (p *PrivateKey) Bytes() []byte {
182 | b := make([]byte, PublicKeySize)
183 | copy(b, p.privBytes[:])
184 | return b
185 | }
186 |
187 | func (p *PrivateKey) FromBytes(data []byte) error {
188 | if len(data) != PrivateKeySize {
189 | return errInvalidKey
190 | }
191 |
192 | p.privBytes = new(x448.Key)
193 | copy(p.privBytes[:], data)
194 |
195 | return nil
196 | }
197 |
198 | func (p *PrivateKey) MarshalBinary() ([]byte, error) {
199 | return p.Bytes(), nil
200 | }
201 |
202 | func (p *PrivateKey) MarshalText() ([]byte, error) {
203 | return []byte(base64.StdEncoding.EncodeToString(p.Bytes())), nil
204 | }
205 |
206 | func (p *PrivateKey) UnmarshalBinary(data []byte) error {
207 | return p.FromBytes(data)
208 | }
209 |
210 | func (p *PrivateKey) UnmarshalText(data []byte) error {
211 | raw, err := base64.StdEncoding.DecodeString(string(data))
212 | if err != nil {
213 | return err
214 | }
215 | return p.FromBytes(raw)
216 | }
217 |
218 | type PublicKey struct {
219 | pubBytes *x448.Key
220 | }
221 |
222 | func (p *PublicKey) Blind(blindingFactor nike.PrivateKey) error {
223 | _, ok := blindingFactor.(*PrivateKey)
224 | if !ok {
225 | return errors.New("blindingFactor nike.PrivateKey must be the same concrete type as x448.PublicKey")
226 | }
227 | pubBytes := Exp(blindingFactor.(*PrivateKey).privBytes, p.pubBytes)
228 | copy(p.pubBytes[:], pubBytes)
229 | util.ExplicitBzero(pubBytes)
230 | return nil
231 | }
232 |
233 | func (p *PublicKey) Reset() {
234 | zeros := make([]byte, PublicKeySize)
235 | err := p.FromBytes(zeros)
236 | if err != nil {
237 | panic(err)
238 | }
239 | }
240 |
241 | func (p *PublicKey) Bytes() []byte {
242 | b := make([]byte, PublicKeySize)
243 | copy(b, p.pubBytes[:])
244 | return b
245 | }
246 |
247 | func (p *PublicKey) FromBytes(data []byte) error {
248 | if len(data) != PublicKeySize {
249 | return errInvalidKey
250 | }
251 |
252 | p.pubBytes = new(x448.Key)
253 | copy(p.pubBytes[:], data)
254 |
255 | return nil
256 | }
257 |
258 | func (p *PublicKey) MarshalBinary() ([]byte, error) {
259 | return p.Bytes(), nil
260 | }
261 |
262 | func (p *PublicKey) MarshalText() ([]byte, error) {
263 | return []byte(base64.StdEncoding.EncodeToString(p.Bytes())), nil
264 | }
265 |
266 | func (p *PublicKey) UnmarshalBinary(data []byte) error {
267 | return p.FromBytes(data)
268 | }
269 |
270 | func (p *PublicKey) UnmarshalText(data []byte) error {
271 | raw, err := base64.StdEncoding.DecodeString(string(data))
272 | if err != nil {
273 | return err
274 | }
275 | return p.FromBytes(raw)
276 | }
277 |
278 | // Exp returns the group element, the result of x^y, over the ECDH group.
279 | func Exp(x, y *x448.Key) []byte {
280 | sharedSecret := new(x448.Key)
281 | ok := x448.Shared(sharedSecret, x, y)
282 | if !ok {
283 | panic("x448.Shared failed")
284 | }
285 | return sharedSecret[:]
286 | }
287 |
288 | func expG(dst, y *x448.Key) {
289 | x448.KeyGen(dst, y)
290 | }
291 |
--------------------------------------------------------------------------------