├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── dh ├── csidh │ ├── consts.go │ ├── csidh.go │ ├── csidh_test.go │ ├── curve.go │ ├── curve_test.go │ ├── doc.go │ ├── fp511.go │ ├── fp511_amd64.go │ ├── fp511_amd64.s │ ├── fp511_generic.go │ ├── fp511_test.go │ ├── testdata │ │ ├── csidh_testvectors.dat │ │ └── csidh_testvectors_small.dat │ └── utils_test.go ├── doc.go └── sidh │ ├── common │ ├── doc.go │ ├── params.go │ ├── types.go │ └── utils.go │ ├── doc.go │ ├── internal │ ├── doc.go │ ├── p434 │ │ ├── arith_amd64.s │ │ ├── arith_amd64_test.go │ │ ├── arith_decl.go │ │ ├── arith_generic.go │ │ ├── arith_test.go │ │ ├── core.go │ │ ├── curve.go │ │ ├── curve_test.go │ │ ├── doc.go │ │ ├── fp2.go │ │ ├── fp2_test.go │ │ ├── params.go │ │ └── params_test.go │ ├── p503 │ │ ├── arith_amd64.s │ │ ├── arith_amd64_test.go │ │ ├── arith_arm64.s │ │ ├── arith_decl.go │ │ ├── arith_generic.go │ │ ├── arith_test.go │ │ ├── core.go │ │ ├── curve.go │ │ ├── curve_test.go │ │ ├── doc.go │ │ ├── fp2.go │ │ ├── fp2_test.go │ │ ├── params.go │ │ └── params_test.go │ ├── p751 │ │ ├── arith_amd64.s │ │ ├── arith_amd64_test.go │ │ ├── arith_arm64.s │ │ ├── arith_decl.go │ │ ├── arith_generic.go │ │ ├── arith_test.go │ │ ├── core.go │ │ ├── curve.go │ │ ├── curve_test.go │ │ ├── doc.go │ │ ├── fp2.go │ │ ├── fp2_test.go │ │ ├── params.go │ │ └── params_test.go │ └── templates │ │ ├── arith_decl.gotemp │ │ ├── arith_generic.gotemp │ │ ├── arith_test.gotemp │ │ ├── core.gotemp │ │ ├── curve.gotemp │ │ ├── curve_test.gotemp │ │ ├── fp2.gotemp │ │ ├── fp2_test.gotemp │ │ └── gen.go │ ├── sidh.go │ ├── sidh_test.go │ ├── sike.go │ ├── sike_test.go │ └── testdata │ ├── PQCkemKAT_374.rsp │ ├── PQCkemKAT_434.rsp │ └── PQCkemKAT_644.rsp ├── drbg ├── ctr_drbg.go ├── ctr_drbg_test.go └── internal │ └── aes │ ├── LICENSE │ ├── aes_test.go │ ├── asm_amd64.s │ ├── asm_arm64.s │ ├── cipher.go │ ├── cipher_asm.go │ ├── cipher_noasm.go │ ├── const.go │ ├── generic.go │ └── tools.go ├── etc ├── PQCkemKAT_434.rsp ├── PQCkemKAT_644.rsp ├── csidh_testvectors.dat ├── dockers │ └── debian-buster-aarch64 │ │ ├── Dockerfile_1_13 │ │ └── Dockerfile_1_14 └── sliding_window_strat_calc.py ├── go.mod ├── go.sum ├── hash ├── sha3 │ ├── doc.go │ ├── keccakf.go │ ├── keccakf_amd64.go │ ├── keccakf_amd64.s │ ├── sha3.go │ ├── sha3_test.go │ ├── shake.go │ ├── testdata │ │ └── keccakKats.json.deflate │ ├── xor.go │ ├── xor_generic.go │ └── xor_unaligned.go └── sm3 │ ├── compress.go │ ├── sm3.go │ └── sm3_test.go ├── kem └── mkem │ ├── Makefile │ ├── README.md │ ├── cmd │ └── bench.go │ ├── csidh.go │ ├── csidh_test.go │ ├── go.mod │ ├── sike.go │ └── sike_test.go └── utils ├── cpu.go ├── cpuid_amd64.go └── cpuid_amd64.s /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [henrydcase] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tls_vendor 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: go 3 | go: 4 | - 1.13.x 5 | - 1.14.x 6 | - master 7 | 8 | matrix: 9 | include: 10 | - name: "Go on x86-64" 11 | os: linux 12 | script: 13 | - make clean 14 | - NOASM=0 make test 15 | - NOASM=1 make test 16 | - name: "Go 1.13 on ARM64" 17 | services: docker 18 | os: linux 19 | script: 20 | - docker run --rm --privileged multiarch/qemu-user-static:register --reset 21 | - docker run --rm -v `pwd`:`pwd` -w `pwd` "flowher/debian-buster-aarch64-go-1.13" /bin/bash -c "NOASM=0 make test" 22 | - docker run --rm -v `pwd`:`pwd` -w `pwd` "flowher/debian-buster-aarch64-go-1.13" /bin/bash -c "NOASM=1 make test" 23 | - name: "Go 1.14 on ARM64" 24 | services: docker 25 | os: linux 26 | script: 27 | - docker run --rm --privileged multiarch/qemu-user-static:register --reset 28 | - docker run --rm -v `pwd`:`pwd` -w `pwd` "flowher/debian-buster-aarch64-go-1.14" /bin/bash -c "NOASM=0 make test" 29 | - docker run --rm -v `pwd`:`pwd` -w `pwd` "flowher/debian-buster-aarch64-go-1.14" /bin/bash -c "NOASM=1 make test" 30 | - name: "Coverage" 31 | os: linux 32 | script: 33 | - NOASM=0 make cover 34 | - NOASM=1 make cover 35 | 36 | 37 | after_script: 38 | - bash <(curl -s https://codecov.io/bash) -t 8a75b459-85b1-4e5d-aa12-4dd705914700 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Kris Kwiatkowski, 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 met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIEDi 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | ==== 25 | 26 | Copyright (c) 2009 The Go Authors. All rights reserved. 27 | 28 | Redistribution and use in source and binary forms, with or without 29 | modification, are permitted provided that the following conditions are 30 | met: 31 | 32 | * Redistributions of source code must retain the above copyright 33 | notice, this list of conditions and the following disclaimer. 34 | * Redistributions in binary form must reproduce the above 35 | copyright notice, this list of conditions and the following disclaimer 36 | in the documentation and/or other materials provided with the 37 | distribution. 38 | * Neither the name of Google Inc. nor the names of its 39 | contributors may be used to endorse or promote products derived from 40 | this software without specific prior written permission. 41 | 42 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 43 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 44 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 45 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 46 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 47 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 48 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 49 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 50 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 51 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 52 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 53 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # I'm sure there is better way. But I would need to find it first 2 | MK_FILE_PATH = $(lastword $(MAKEFILE_LIST)) 3 | PRJ_DIR = $(abspath $(dir $(MK_FILE_PATH))) 4 | GO ?= go 5 | VENDOR_DIR = tls_vendor 6 | OPTS ?= -v 7 | NOASM ?= 8 | TEST_PATH ?= ./... 9 | GOCACHE ?= off 10 | BENCH_OPTS ?= -v -bench=. -run="^_" -benchmem 11 | TEST_PATH ?= ./... 12 | DBG = 1 13 | OPTS_ENV = 14 | OPTS_TAGS ?= -tags noasm 15 | ARCH = $(shell uname -m) 16 | 17 | ifeq ($(ARCH), ppc64le) 18 | NOASM+=1 19 | endif 20 | 21 | ifeq ($(ARCH), riscv64) 22 | NOASM+=1 23 | OPTS_TAGS+= -timeout 0 24 | endif 25 | 26 | ifeq ($(NOASM),1) 27 | OPTS+=$(OPTS_TAGS) 28 | endif 29 | 30 | ifeq ($(PPROF),1) 31 | BENCH_OPTS+= -cpuprofile=cpu.out -memprofile=mem0.out 32 | endif 33 | 34 | ifeq ($(DBG),1) 35 | DBG_FLAGS+= #-m # escape analysis 36 | DBG_FLAGS+= -l # no inline 37 | DBG_FLAGS+= -N # debug symbols 38 | #OPTS+=-gcflags=all="$(DBG_FLAGS)" 39 | OPTS+=-gcflags "$(DBG_FLAGS)" 40 | OPTS_ENV+= GOTRACEBACK=crash # enable core dumps 41 | endif 42 | 43 | test: 44 | $(OPTS_ENV) $(GO) test $(OPTS) $(TEST_PATH) 45 | 46 | cover: 47 | $(GO) test \ 48 | -coverprofile=coverage.txt -covermode=atomic $(OPTS) $(TEST_PATH) 49 | 50 | bench: 51 | $(GO) test $(BENCH_OPTS) $(TEST_PATH) 52 | 53 | clean: 54 | rm -rf $(VENDOR_DIR) 55 | rm -rf coverage.txt 56 | 57 | vendor-sidh-for-tls: clean 58 | mkdir -p $(VENDOR_DIR)/github_com/henrydcase/nobs/ 59 | rsync -a . $(VENDOR_DIR)/github_com/henrydcase/nobs/ --exclude=$(VENDOR_DIR) --exclude=.git --exclude=.travis.yml --exclude=README.md 60 | find $(VENDOR_DIR) -type f -print0 -name "*.go" | xargs -0 sed -i 's/github\.com/github_com/g' 61 | 62 | gen: clean 63 | $(GO) generate -v ./... 64 | $(GO) mod tidy 65 | 66 | pprof-cpu: 67 | $(GO) tool pprof cpu.out 68 | 69 | pprof-mem: 70 | $(GO) tool pprof mem0.out 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NOBS Crypto 2 | 3 | Collection of some cryptographic implementations in Go. 4 | 5 | Library is rarely updated on as-needed bases. Open for requests. 6 | -------------------------------------------------------------------------------- /dh/csidh/consts.go: -------------------------------------------------------------------------------- 1 | package csidh 2 | 3 | const ( 4 | // pbits is a bitsize of prime p 5 | pbits = 511 6 | // primeCount number of Elkies primes used for constructing p 7 | primeCount = 74 8 | // (2*5+1)^74 is roughly 2^256 9 | expMax = int8(5) 10 | // size of the limbs, pretty much hardcoded to 64-bit words 11 | limbBitSize = 64 12 | // size of the limbs in bytes 13 | limbByteSize = limbBitSize >> 3 14 | // Number of limbs for a field element 15 | numWords = 8 16 | // PrivateKeySize is a size of cSIDH/512 private key in bytes. 17 | PrivateKeySize = 37 18 | // PublicKeySize is a size of cSIDH/512 public key in bytes. 19 | PublicKeySize = 64 20 | // SharedSecretSize is a size of cSIDH/512 shared secret in bytes. 21 | SharedSecretSize = 64 22 | ) 23 | 24 | var ( 25 | // Elkies primes up to 374 + prime 587 26 | // p = 4 * product(primes) - 1 27 | primes = [primeCount]uint64{ 28 | 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, 0x0017, 0x001D, 0x001F, 0x0025, 29 | 0x0029, 0x002B, 0x002F, 0x0035, 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 30 | 0x0059, 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, 0x0083, 0x0089, 0x008B, 31 | 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 32 | 0x00C7, 0x00D3, 0x00DF, 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, 33 | 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, 0x0139, 0x013D, 0x014B, 34 | 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, 0x016F, 0x0175, 0x024B} 35 | 36 | p = fp{ 37 | 0x1B81B90533C6C87B, 0xC2721BF457ACA835, 38 | 0x516730CC1F0B4F25, 0xA7AAC6C567F35507, 39 | 0x5AFBFCC69322C9CD, 0xB42D083AEDC88C42, 40 | 0xFC8AB0D15E3E4C4A, 0x65B48E8F740F89BF, 41 | } 42 | 43 | /* Montgomery R = 2^512 mod p */ 44 | one = fp{ 45 | 0xC8FC8DF598726F0A, 0x7B1BC81750A6AF95, 46 | 0x5D319E67C1E961B4, 0xB0AA7275301955F1, 47 | 0x4A080672D9BA6C64, 0x97A5EF8A246EE77B, 48 | 0x06EA9E5D4383676A, 0x3496E2E117E0EC80, 49 | } 50 | 51 | // 2 in Montgomery domain 52 | two = fp{ 53 | 0x767762E5FD1E1599, 0x33C5743A49A0B6F6, 54 | 0x68FC0C0364C77443, 0xB9AA1E24F83F56DB, 55 | 0x3914101F20520EFB, 0x7B1ED6D95B1542B4, 56 | 0x114A8BE928C8828A, 0x03793732BBB24F40, 57 | } 58 | 59 | // -2 in Montgomery domain 60 | twoNeg = fp{ 61 | 0xA50A561F36A8B2E2, 0x8EACA7BA0E0BF13E, 62 | 0xE86B24C8BA43DAE2, 0xEE00A8A06FB3FE2B, 63 | 0x21E7ECA772D0BAD1, 0x390E316192B3498E, 64 | 0xEB4024E83575C9C0, 0x623B575CB85D3A7F, 65 | } 66 | 67 | // 4 in Montgomery domain 68 | four = fp{ 69 | 0xECEEC5CBFA3C2B32, 0x678AE87493416DEC, 70 | 0xD1F81806C98EE886, 0x73543C49F07EADB6, 71 | 0x7228203E40A41DF7, 0xF63DADB2B62A8568, 72 | 0x229517D251910514, 0x06F26E6577649E80, 73 | } 74 | 75 | // 4 * sqrt(p) 76 | fourSqrtP = fp{ 77 | 0x17895E71E1A20B3F, 0x38D0CD95F8636A56, 78 | 0x142B9541E59682CD, 0x856F1399D91D6592, 79 | 0x0000000000000002, 80 | } 81 | 82 | // -p^-1 mod 2^64 83 | pNegInv = fp{ 84 | 0x66c1301f632e294d, 85 | } 86 | 87 | // (p-1)/2. Used as exponent, hence not in 88 | // montgomery domain 89 | pMin1By2 = fp{ 90 | 0x8DC0DC8299E3643D, 0xE1390DFA2BD6541A, 91 | 0xA8B398660F85A792, 0xD3D56362B3F9AA83, 92 | 0x2D7DFE63499164E6, 0x5A16841D76E44621, 93 | 0xFE455868AF1F2625, 0x32DA4747BA07C4DF, 94 | } 95 | 96 | // p-1 mod 2^64. Used as exponent, hence not 97 | // in montgomery domain 98 | pMin1 = fp{ 99 | 0x1B81B90533C6C879, 0xC2721BF457ACA835, 100 | 0x516730CC1F0B4F25, 0xA7AAC6C567F35507, 101 | 0x5AFBFCC69322C9CD, 0xB42D083AEDC88C42, 102 | 0xFC8AB0D15E3E4C4A, 0x65B48E8F740F89BF, 103 | } 104 | ) 105 | -------------------------------------------------------------------------------- /dh/csidh/curve.go: -------------------------------------------------------------------------------- 1 | package csidh 2 | 3 | // xAdd implements differential arithmetic in P^1 for Montgomery 4 | // curves E(x): x^3 + A*x^2 + x by using x-coordinate only arithmetic. 5 | // x(PaQ) = x(P) + x(Q) by using x(P-Q) 6 | // This algorithms is correctly defined only for cases when 7 | // P!=inf, Q!=inf, P!=Q and P!=-Q. 8 | func xAdd(PaQ, P, Q, PdQ *point) { 9 | var t0, t1, t2, t3 fp 10 | addRdc(&t0, &P.x, &P.z) 11 | subRdc(&t1, &P.x, &P.z) 12 | addRdc(&t2, &Q.x, &Q.z) 13 | subRdc(&t3, &Q.x, &Q.z) 14 | mulRdc(&t0, &t0, &t3) 15 | mulRdc(&t1, &t1, &t2) 16 | addRdc(&t2, &t0, &t1) 17 | subRdc(&t3, &t0, &t1) 18 | mulRdc(&t2, &t2, &t2) // sqr 19 | mulRdc(&t3, &t3, &t3) // sqr 20 | mulRdc(&PaQ.x, &PdQ.z, &t2) 21 | mulRdc(&PaQ.z, &PdQ.x, &t3) 22 | } 23 | 24 | // xDbl implements point doubling on a Montgomery curve 25 | // E(x): x^3 + A*x^2 + x by using x-coordinate onlyh arithmetic. 26 | // x(Q) = [2]*x(P) 27 | // It is correctly defined for all P != inf. 28 | func xDbl(Q, P, A *point) { 29 | var t0, t1, t2 fp 30 | addRdc(&t0, &P.x, &P.z) 31 | mulRdc(&t0, &t0, &t0) // sqr 32 | subRdc(&t1, &P.x, &P.z) 33 | mulRdc(&t1, &t1, &t1) // sqr 34 | subRdc(&t2, &t0, &t1) 35 | mulRdc(&t1, &four, &t1) 36 | mulRdc(&t1, &t1, &A.z) 37 | mulRdc(&Q.x, &t0, &t1) 38 | addRdc(&t0, &A.z, &A.z) 39 | addRdc(&t0, &t0, &A.x) 40 | mulRdc(&t0, &t0, &t2) 41 | addRdc(&t0, &t0, &t1) 42 | mulRdc(&Q.z, &t0, &t2) 43 | } 44 | 45 | // xDblAdd implements combined doubling of point P 46 | // and addition of points P and Q on a Montgomery curve 47 | // E(x): x^3 + A*x^2 + x by using x-coordinate onlyh arithmetic. 48 | // x(PaP) = x(2*P) 49 | // x(PaQ) = x(P+Q) 50 | func xDblAdd(PaP, PaQ, P, Q, PdQ *point, A24 *coeff) { 51 | var t0, t1, t2 fp 52 | 53 | addRdc(&t0, &P.x, &P.z) 54 | subRdc(&t1, &P.x, &P.z) 55 | mulRdc(&PaP.x, &t0, &t0) 56 | subRdc(&t2, &Q.x, &Q.z) 57 | addRdc(&PaQ.x, &Q.x, &Q.z) 58 | mulRdc(&t0, &t0, &t2) 59 | mulRdc(&PaP.z, &t1, &t1) 60 | mulRdc(&t1, &t1, &PaQ.x) 61 | subRdc(&t2, &PaP.x, &PaP.z) 62 | mulRdc(&PaP.z, &PaP.z, &A24.c) 63 | mulRdc(&PaP.x, &PaP.x, &PaP.z) 64 | mulRdc(&PaQ.x, &A24.a, &t2) 65 | subRdc(&PaQ.z, &t0, &t1) 66 | addRdc(&PaP.z, &PaP.z, &PaQ.x) 67 | addRdc(&PaQ.x, &t0, &t1) 68 | mulRdc(&PaP.z, &PaP.z, &t2) 69 | mulRdc(&PaQ.z, &PaQ.z, &PaQ.z) 70 | mulRdc(&PaQ.x, &PaQ.x, &PaQ.x) 71 | mulRdc(&PaQ.z, &PaQ.z, &PdQ.x) 72 | mulRdc(&PaQ.x, &PaQ.x, &PdQ.z) 73 | } 74 | 75 | // cswappoint swaps P1 with P2 in constant time. The 'choice' 76 | // parameter must have a value of either 1 (results 77 | // in swap) or 0 (results in no-swap). 78 | func cswappoint(P1, P2 *point, choice uint8) { 79 | cswap512(&P1.x, &P2.x, choice) 80 | cswap512(&P1.z, &P2.z, choice) 81 | } 82 | 83 | // xMul implements point multiplication with left-to-right Montgomery 84 | // adder. co is A coefficient of x^3 + A*x^2 + x curve. k must be > 0 85 | // 86 | // Non-constant time! 87 | func xMul(kP, P *point, co *coeff, k *fp) { 88 | var A24 coeff 89 | var Q point 90 | var j uint 91 | var A = point{x: co.a, z: co.c} 92 | var R = *P 93 | 94 | // Precompyte A24 = (A+2C:4C) => (A24.x = A.x+2A.z; A24.z = 4*A.z) 95 | addRdc(&A24.a, &co.c, &co.c) 96 | addRdc(&A24.a, &A24.a, &co.a) 97 | mulRdc(&A24.c, &co.c, &four) 98 | 99 | // Skip initial 0 bits. 100 | for j = 511; j > 0; j-- { 101 | // performance hit from making it constant-time is actually 102 | // quite big, so... unsafe branch for now 103 | if uint8(k[j>>6]>>(j&63)&1) != 0 { 104 | break 105 | } 106 | } 107 | 108 | xDbl(&Q, P, &A) 109 | prevBit := uint8(1) 110 | for i := j; i > 0; { 111 | i-- 112 | bit := uint8(k[i>>6] >> (i & 63) & 1) 113 | cswappoint(&Q, &R, prevBit^bit) 114 | xDblAdd(&Q, &R, &Q, &R, P, &A24) 115 | prevBit = bit 116 | } 117 | cswappoint(&Q, &R, uint8(k[0]&1)) 118 | *kP = Q 119 | } 120 | 121 | // xIso computes the isogeny with kernel point kern of a given order 122 | // kernOrder. Returns the new curve coefficient co and the image img. 123 | // 124 | // During computation function switches between Montgomery and twisted 125 | // Edwards curves in order to compute image curve parameters faster. 126 | // This technique is described by Meyer and Reith in ia.cr/2018/782. 127 | // 128 | // Non-constant time. 129 | func xIso(img *point, co *coeff, kern *point, kernOrder uint64) { 130 | var t0, t1, t2, S, D fp 131 | var Q, prod point 132 | var coEd coeff 133 | var M = [3]point{*kern} 134 | 135 | // Compute twisted Edwards coefficients 136 | // coEd.a = co.a + 2*co.c 137 | // coEd.c = co.a - 2*co.c 138 | // coEd.a*X^2 + Y^2 = 1 + coEd.c*X^2*Y^2 139 | addRdc(&coEd.c, &co.c, &co.c) 140 | addRdc(&coEd.a, &co.a, &coEd.c) 141 | subRdc(&coEd.c, &co.a, &coEd.c) 142 | 143 | // Transfer point to twisted Edwards YZ-coordinates 144 | // (X:Z)->(Y:Z) = (X-Z : X+Z) 145 | addRdc(&S, &img.x, &img.z) 146 | subRdc(&D, &img.x, &img.z) 147 | 148 | subRdc(&prod.x, &kern.x, &kern.z) 149 | addRdc(&prod.z, &kern.x, &kern.z) 150 | 151 | mulRdc(&t1, &prod.x, &S) 152 | mulRdc(&t0, &prod.z, &D) 153 | addRdc(&Q.x, &t0, &t1) 154 | subRdc(&Q.z, &t0, &t1) 155 | 156 | xDbl(&M[1], kern, &point{x: co.a, z: co.c}) 157 | 158 | // NOTE: Not constant time. 159 | for i := uint64(1); i < kernOrder>>1; i++ { 160 | if i >= 2 { 161 | xAdd(&M[i%3], &M[(i-1)%3], kern, &M[(i-2)%3]) 162 | } 163 | subRdc(&t1, &M[i%3].x, &M[i%3].z) 164 | addRdc(&t0, &M[i%3].x, &M[i%3].z) 165 | mulRdc(&prod.x, &prod.x, &t1) 166 | mulRdc(&prod.z, &prod.z, &t0) 167 | mulRdc(&t1, &t1, &S) 168 | mulRdc(&t0, &t0, &D) 169 | addRdc(&t2, &t0, &t1) 170 | mulRdc(&Q.x, &Q.x, &t2) 171 | subRdc(&t2, &t0, &t1) 172 | mulRdc(&Q.z, &Q.z, &t2) 173 | } 174 | 175 | mulRdc(&Q.x, &Q.x, &Q.x) 176 | mulRdc(&Q.z, &Q.z, &Q.z) 177 | mulRdc(&img.x, &img.x, &Q.x) 178 | mulRdc(&img.z, &img.z, &Q.z) 179 | 180 | // coEd.a^kernOrder and coEd.c^kernOrder 181 | modExpRdc64(&coEd.a, &coEd.a, kernOrder) 182 | modExpRdc64(&coEd.c, &coEd.c, kernOrder) 183 | 184 | // prod^8 185 | mulRdc(&prod.x, &prod.x, &prod.x) 186 | mulRdc(&prod.x, &prod.x, &prod.x) 187 | mulRdc(&prod.x, &prod.x, &prod.x) 188 | mulRdc(&prod.z, &prod.z, &prod.z) 189 | mulRdc(&prod.z, &prod.z, &prod.z) 190 | mulRdc(&prod.z, &prod.z, &prod.z) 191 | 192 | // Compute image curve params 193 | mulRdc(&coEd.c, &coEd.c, &prod.x) 194 | mulRdc(&coEd.a, &coEd.a, &prod.z) 195 | 196 | // Convert curve coefficients back to Montgomery 197 | addRdc(&co.a, &coEd.a, &coEd.c) 198 | subRdc(&co.c, &coEd.a, &coEd.c) 199 | addRdc(&co.a, &co.a, &co.a) 200 | } 201 | 202 | // montEval evaluates x^3 + Ax^2 + x. 203 | func montEval(res, A, x *fp) { 204 | var t fp 205 | 206 | *res = *x 207 | mulRdc(res, res, res) 208 | mulRdc(&t, A, x) 209 | addRdc(res, res, &t) 210 | addRdc(res, res, &one) 211 | mulRdc(res, res, x) 212 | } 213 | -------------------------------------------------------------------------------- /dh/csidh/doc.go: -------------------------------------------------------------------------------- 1 | // Package csidh implements cSIDH key exchange, isogeny-based scheme 2 | // resulting from the group action. Implementation uses only prime 3 | // field of a size 512-bits and uses Ed some performance improvements 4 | // by using twisted Edwards curves in the isogeny image curve 5 | // computations. This work has been described by M. Meyer and S. Reith 6 | // in the ia.cr/2018/782. Original cSIDH paper can be found in the 7 | // ia.cr/2018/383. 8 | // 9 | // It is experimental implementation, not meant to be secure. Have fun! 10 | // 11 | package csidh 12 | -------------------------------------------------------------------------------- /dh/csidh/fp511_amd64.go: -------------------------------------------------------------------------------- 1 | // +build amd64,!noasm 2 | 3 | package csidh 4 | 5 | import "math/bits" 6 | 7 | //go:noescape 8 | func mul512(a, b *fp, c uint64) 9 | 10 | //go:noescape 11 | func mul576(a *[9]uint64, b *fp, c uint64) 12 | 13 | //go:noescape 14 | func cswap512(x, y *fp, choice uint8) 15 | 16 | //go:noescape 17 | func mulBmiAsm(res, x, y *fp) 18 | 19 | // mulRdc performs montgomery multiplication r = x * y mod P. 20 | // Returned result r is already reduced and in Montgomery domain. 21 | func mulRdc(r, x, y *fp) { 22 | var t fp 23 | var c uint64 24 | 25 | if hasADXandBMI2 { 26 | mulBmiAsm(r, x, y) 27 | } else { 28 | mulGeneric(r, x, y) 29 | } 30 | 31 | // if p <= r < 2p then r = r-p 32 | t[0], c = bits.Sub64(r[0], p[0], 0) 33 | t[1], c = bits.Sub64(r[1], p[1], c) 34 | t[2], c = bits.Sub64(r[2], p[2], c) 35 | t[3], c = bits.Sub64(r[3], p[3], c) 36 | t[4], c = bits.Sub64(r[4], p[4], c) 37 | t[5], c = bits.Sub64(r[5], p[5], c) 38 | t[6], c = bits.Sub64(r[6], p[6], c) 39 | t[7], c = bits.Sub64(r[7], p[7], c) 40 | 41 | var w = 0 - c 42 | r[0] = ctPick64(w, r[0], t[0]) 43 | r[1] = ctPick64(w, r[1], t[1]) 44 | r[2] = ctPick64(w, r[2], t[2]) 45 | r[3] = ctPick64(w, r[3], t[3]) 46 | r[4] = ctPick64(w, r[4], t[4]) 47 | r[5] = ctPick64(w, r[5], t[5]) 48 | r[6] = ctPick64(w, r[6], t[6]) 49 | r[7] = ctPick64(w, r[7], t[7]) 50 | } 51 | -------------------------------------------------------------------------------- /dh/csidh/fp511_generic.go: -------------------------------------------------------------------------------- 1 | // +build noasm arm64 2 | 3 | package csidh 4 | 5 | import "math/bits" 6 | 7 | // mul576 implements schoolbook multiplication of 8 | // 64x512-bit integer. Returns result modulo 2^512. 9 | // r = m1*m2 10 | func mul512(r, m1 *fp, m2 uint64) { 11 | var c, h, l uint64 12 | 13 | c, r[0] = bits.Mul64(m2, m1[0]) 14 | 15 | h, l = bits.Mul64(m2, m1[1]) 16 | r[1], c = bits.Add64(l, c, 0) 17 | c = h + c 18 | 19 | h, l = bits.Mul64(m2, m1[2]) 20 | r[2], c = bits.Add64(l, c, 0) 21 | c = h + c 22 | 23 | h, l = bits.Mul64(m2, m1[3]) 24 | r[3], c = bits.Add64(l, c, 0) 25 | c = h + c 26 | 27 | h, l = bits.Mul64(m2, m1[4]) 28 | r[4], c = bits.Add64(l, c, 0) 29 | c = h + c 30 | 31 | h, l = bits.Mul64(m2, m1[5]) 32 | r[5], c = bits.Add64(l, c, 0) 33 | c = h + c 34 | 35 | h, l = bits.Mul64(m2, m1[6]) 36 | r[6], c = bits.Add64(l, c, 0) 37 | c = h + c 38 | 39 | _, l = bits.Mul64(m2, m1[7]) 40 | r[7], _ = bits.Add64(l, c, 0) 41 | } 42 | 43 | // mul576 implements schoolbook multiplication of 44 | // 64x512-bit integer. Returns 576-bit result of 45 | // multiplication. 46 | // r = m1*m2 47 | func mul576(r *[9]uint64, m1 *fp, m2 uint64) { 48 | var c, h, l uint64 49 | 50 | c, r[0] = bits.Mul64(m2, m1[0]) 51 | 52 | h, l = bits.Mul64(m2, m1[1]) 53 | r[1], c = bits.Add64(l, c, 0) 54 | c = h + c 55 | 56 | h, l = bits.Mul64(m2, m1[2]) 57 | r[2], c = bits.Add64(l, c, 0) 58 | c = h + c 59 | 60 | h, l = bits.Mul64(m2, m1[3]) 61 | r[3], c = bits.Add64(l, c, 0) 62 | c = h + c 63 | 64 | h, l = bits.Mul64(m2, m1[4]) 65 | r[4], c = bits.Add64(l, c, 0) 66 | c = h + c 67 | 68 | h, l = bits.Mul64(m2, m1[5]) 69 | r[5], c = bits.Add64(l, c, 0) 70 | c = h + c 71 | 72 | h, l = bits.Mul64(m2, m1[6]) 73 | r[6], c = bits.Add64(l, c, 0) 74 | c = h + c 75 | 76 | h, l = bits.Mul64(m2, m1[7]) 77 | r[7], c = bits.Add64(l, c, 0) 78 | r[8], c = bits.Add64(h, c, 0) 79 | r[8] += c 80 | } 81 | 82 | // cswap512 implements constant time swap operation. 83 | // If choice = 0, leave x,y unchanged. If choice = 1, set x,y = y,x. 84 | // If choice is neither 0 nor 1 then behaviour is undefined. 85 | func cswap512(x, y *fp, choice uint8) { 86 | var tmp uint64 87 | mask64 := 0 - uint64(choice) 88 | 89 | for i := 0; i < numWords; i++ { 90 | tmp = mask64 & (x[i] ^ y[i]) 91 | x[i] = tmp ^ x[i] 92 | y[i] = tmp ^ y[i] 93 | } 94 | } 95 | 96 | // mulRdc performs montgomery multiplication r = x * y mod P. 97 | // Returned result r is already reduced and in Montgomery domain. 98 | func mulRdc(r, x, y *fp) { 99 | var t fp 100 | var c uint64 101 | 102 | mulGeneric(r, x, y) 103 | 104 | // if p <= r < 2p then r = r-p 105 | t[0], c = bits.Sub64(r[0], p[0], 0) 106 | t[1], c = bits.Sub64(r[1], p[1], c) 107 | t[2], c = bits.Sub64(r[2], p[2], c) 108 | t[3], c = bits.Sub64(r[3], p[3], c) 109 | t[4], c = bits.Sub64(r[4], p[4], c) 110 | t[5], c = bits.Sub64(r[5], p[5], c) 111 | t[6], c = bits.Sub64(r[6], p[6], c) 112 | t[7], c = bits.Sub64(r[7], p[7], c) 113 | 114 | var w = uint64(0 - uint64(c)) 115 | r[0] = ctPick64(w, r[0], t[0]) 116 | r[1] = ctPick64(w, r[1], t[1]) 117 | r[2] = ctPick64(w, r[2], t[2]) 118 | r[3] = ctPick64(w, r[3], t[3]) 119 | r[4] = ctPick64(w, r[4], t[4]) 120 | r[5] = ctPick64(w, r[5], t[5]) 121 | r[6] = ctPick64(w, r[6], t[6]) 122 | r[7] = ctPick64(w, r[7], t[7]) 123 | } 124 | -------------------------------------------------------------------------------- /dh/csidh/utils_test.go: -------------------------------------------------------------------------------- 1 | package csidh 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | mrand "math/rand" 7 | ) 8 | 9 | var ( 10 | // Number of interations 11 | numIter = 10 12 | // Modulus 13 | modulus, _ = new(big.Int).SetString(fp2S(p), 16) 14 | // Zero in fp 15 | zeroFp512 = fp{} 16 | // One in fp 17 | oneFp512 = fp{1, 0, 0, 0, 0, 0, 0, 0} 18 | // file with KAT vectors 19 | katFile = "testdata/csidh_testvectors.dat" 20 | ) 21 | 22 | // Converts dst to Montgomery if "toMont==true" or from Montgomery domain otherwise. 23 | func toMont(dst *big.Int, toMont bool) { 24 | var bigP, bigR big.Int 25 | 26 | intSetU64(&bigP, p[:]) 27 | bigR.SetUint64(1) 28 | bigR.Lsh(&bigR, 512) 29 | 30 | if !toMont { 31 | bigR.ModInverse(&bigR, &bigP) 32 | } 33 | dst.Mul(dst, &bigR) 34 | dst.Mod(dst, &bigP) 35 | } 36 | 37 | func fp2S(v fp) string { 38 | var str string 39 | for i := 0; i < 8; i++ { 40 | str = fmt.Sprintf("%016x", v[i]) + str 41 | } 42 | return str 43 | } 44 | 45 | // zeroize fp. 46 | func zero(v *fp) { 47 | for i := range *v { 48 | v[i] = 0 49 | } 50 | } 51 | 52 | // returns random value in a range (0,p). 53 | func randomFp() fp { 54 | var u fp 55 | for i := 0; i < 8; i++ { 56 | u[i] = mrand.Uint64() 57 | } 58 | return u 59 | } 60 | 61 | // return x==y for fp. 62 | func eqFp(l, r *fp) bool { 63 | for idx := range l { 64 | if l[idx] != r[idx] { 65 | return false 66 | } 67 | } 68 | return true 69 | } 70 | 71 | // return x==y for point. 72 | func ceqpoint(l, r *point) bool { 73 | return eqFp(&l.x, &r.x) && eqFp(&l.z, &r.z) 74 | } 75 | 76 | // Converts src to big.Int. Function assumes that src is a slice of uint64 77 | // values encoded in little-endian byte order. 78 | func intSetU64(dst *big.Int, src []uint64) { 79 | var tmp big.Int 80 | 81 | dst.SetUint64(0) 82 | for i := range src { 83 | tmp.SetUint64(src[i]) 84 | tmp.Lsh(&tmp, uint(i*64)) 85 | dst.Add(dst, &tmp) 86 | } 87 | } 88 | 89 | // Converts src to an array of uint64 values encoded in little-endian 90 | // byte order. 91 | func intGetU64(src *big.Int) []uint64 { 92 | var tmp, mod big.Int 93 | dst := make([]uint64, (src.BitLen()/64)+1) 94 | 95 | u64 := uint64(0) 96 | u64-- 97 | mod.SetUint64(u64) 98 | for i := 0; i < (src.BitLen()/64)+1; i++ { 99 | tmp.Set(src) 100 | tmp.Rsh(&tmp, uint(i)*64) 101 | tmp.And(&tmp, &mod) 102 | dst[i] = tmp.Uint64() 103 | } 104 | return dst 105 | } 106 | 107 | // Returns projective coordinate X of normalized EC 'point' (point.x / point.z). 108 | func toNormX(point *point) big.Int { 109 | var bigP, bigDnt, bigDor big.Int 110 | 111 | intSetU64(&bigP, p[:]) 112 | intSetU64(&bigDnt, point.x[:]) 113 | intSetU64(&bigDor, point.z[:]) 114 | 115 | bigDor.ModInverse(&bigDor, &bigP) 116 | bigDnt.Mul(&bigDnt, &bigDor) 117 | bigDnt.Mod(&bigDnt, &bigP) 118 | return bigDnt 119 | } 120 | 121 | // Converts string to fp element in Montgomery domain of cSIDH-512. 122 | func toFp(num string) fp { 123 | var tmp big.Int 124 | var ok bool 125 | var ret fp 126 | 127 | _, ok = tmp.SetString(num, 0) 128 | if !ok { 129 | panic("Can't parse a number") 130 | } 131 | toMont(&tmp, true) 132 | copy(ret[:], intGetU64(&tmp)) 133 | return ret 134 | } 135 | -------------------------------------------------------------------------------- /dh/doc.go: -------------------------------------------------------------------------------- 1 | package dh 2 | -------------------------------------------------------------------------------- /dh/sidh/common/doc.go: -------------------------------------------------------------------------------- 1 | // Package common provides types, variables, constants and functions commonly used in SIDH or SIKE. 2 | package common 3 | -------------------------------------------------------------------------------- /dh/sidh/common/params.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import "fmt" 4 | 5 | // Keeps mapping: SIDH prime field ID to domain parameters 6 | var sidhParams = make(map[uint8]SidhParams) 7 | 8 | // Params returns domain parameters corresponding to finite field and identified by 9 | // `id` provieded by the caller. Function panics in case `id` wasn't registered earlier. 10 | func Params(id uint8) *SidhParams { 11 | if val, ok := sidhParams[id]; ok { 12 | return &val 13 | } 14 | panic("sidh: SIDH Params ID unregistered") 15 | } 16 | 17 | // Registers SIDH parameters for particular field. 18 | func Register(id uint8, p *SidhParams) { 19 | if _, ok := sidhParams[id]; ok { 20 | msg := fmt.Sprintf("sidh: Field with id %d already registered", id) 21 | panic(msg) 22 | } 23 | sidhParams[id] = *p 24 | } 25 | -------------------------------------------------------------------------------- /dh/sidh/common/types.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | const ( 4 | // corresponds to words in P751 5 | FpMaxWords = 12 6 | // corresponds to byte size of P751 SIDH private key for B 7 | MaxSidhPrivateKeyBsz = 48 8 | // corresponds to byte size of P751 SIKE private key for B 9 | MaxSikePrivateKeyBsz = MaxSidhPrivateKeyBsz + MaxMsgBsz 10 | // corresponds to SIKE max length of 'n' (see 1.4 of SIKE spec in NIST PQC round 1) 11 | MaxMsgBsz = 40 12 | // corresponds to byte size of shared secret generated by SIKEp751 13 | MaxSharedSecretBsz = 188 14 | // correponds to by size of the P751 public key 15 | MaxPublicKeySz = 3 * FpMaxWords * 64 16 | // correponds to by size of the ciphertext produced by SIKE/P751 17 | MaxCiphertextBsz = MaxMsgBsz + MaxPublicKeySz 18 | ) 19 | 20 | // Id's correspond to bitlength of the prime field characteristic 21 | // Currently Fp751 is the only one supported by this implementation 22 | const ( 23 | Fp503 uint8 = iota 24 | Fp751 25 | Fp434 26 | ) 27 | 28 | // Representation of an element of the base field F_p. 29 | // 30 | // No particular meaning is assigned to the representation -- it could represent 31 | // an element in Montgomery form, or not. Tracking the meaning of the field 32 | // element is left to higher types. 33 | type Fp [FpMaxWords]uint64 34 | 35 | // Represents an intermediate product of two elements of the base field F_p. 36 | type FpX2 [2 * FpMaxWords]uint64 37 | 38 | // Represents an element of the extended field Fp^2 = Fp(x+i) 39 | type Fp2 struct { 40 | A Fp 41 | B Fp 42 | } 43 | 44 | type DomainParams struct { 45 | // P, Q and R=P-Q base points 46 | AffineP, AffineQ, AffineR Fp2 47 | // Size of a compuatation strategy for x-torsion group 48 | IsogenyStrategy []uint32 49 | // Max size of secret key for x-torsion group 50 | SecretBitLen uint 51 | // Max size of secret key for x-torsion group 52 | SecretByteLen uint 53 | } 54 | 55 | type SidhParams struct { 56 | ID uint8 57 | // Bytelen of P 58 | Bytelen int 59 | // The public key size, in bytes. 60 | PublicKeySize int 61 | // The shared secret size, in bytes. 62 | SharedSecretSize int 63 | // 2- and 3-torsion group parameter definitions 64 | A, B DomainParams 65 | // Precomputed identity element in the Fp2 in Montgomery domain 66 | OneFp2 Fp2 67 | // Precomputed 1/2 in the Fp2 in Montgomery domain 68 | HalfFp2 Fp2 69 | // Length of SIKE secret message. Must be one of {24,32,40}, 70 | // depending on size of prime field used (see [SIKE], 1.4 and 5.1) 71 | MsgLen int 72 | // Length of SIKE ephemeral KEM key (see [SIKE], 1.4 and 5.1) 73 | KemSize int 74 | // Byte size of ciphertext that KEM produces 75 | CiphertextSize int 76 | // Defines A,C constant for starting curve Cy^2 = x^3 + Ax^2 + x 77 | InitCurve ProjectiveCurveParameters 78 | } 79 | 80 | // Stores curve projective parameters equivalent to A/C. Meaning of the 81 | // values depends on the context. When working with isogenies over 82 | // subgroup that are powers of: 83 | // * three then (A:C) ~ (A+2C:A-2C) 84 | // * four then (A:C) ~ (A+2C: 4C) 85 | // See Appendix A of SIKE for more details 86 | type CurveCoefficientsEquiv struct { 87 | A Fp2 88 | C Fp2 89 | } 90 | 91 | // A point on the projective line P^1(F_{p^2}). 92 | // 93 | // This represents a point on the Kummer line of a Montgomery curve. The 94 | // curve is specified by a ProjectiveCurveParameters struct. 95 | type ProjectivePoint struct { 96 | X Fp2 97 | Z Fp2 98 | } 99 | 100 | // A point on the projective line P^1(F_{p^2}). 101 | // 102 | // This is used to work projectively with the curve coefficients. 103 | type ProjectiveCurveParameters struct { 104 | A Fp2 105 | C Fp2 106 | } 107 | -------------------------------------------------------------------------------- /dh/sidh/common/utils.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | // Constant time select. 4 | // if pick == 1 (out = in1) 5 | // if pick == 0 (out = in2) 6 | // else out is undefined. 7 | func Cpick(pick int, out, in1, in2 []byte) { 8 | var which = byte((int8(pick << 7)) >> 7) 9 | for i := range out { 10 | out[i] = (in1[i] & which) | (in2[i] & ^which) 11 | } 12 | } 13 | 14 | // Read 2*bytelen(p) bytes into the given ExtensionFieldElement. 15 | // 16 | // It is an error to call this function if the input byte slice is less than 2*bytelen(p) bytes long. 17 | func BytesToFp2(fp2 *Fp2, input []byte, bytelen int) { 18 | if len(input) < 2*bytelen { 19 | panic("input byte slice too short") 20 | } 21 | 22 | for i := 0; i < bytelen; i++ { 23 | j := i / 8 24 | k := uint64(i % 8) 25 | fp2.A[j] |= uint64(input[i]) << (8 * k) 26 | fp2.B[j] |= uint64(input[i+bytelen]) << (8 * k) 27 | } 28 | } 29 | 30 | // Convert the input to wire format. 31 | // 32 | // The output byte slice must be at least 2*bytelen(p) bytes long. 33 | func Fp2ToBytes(output []byte, fp2 *Fp2, bytelen int) { 34 | if len(output) < 2*bytelen { 35 | panic("output byte slice too short") 36 | } 37 | 38 | // convert to bytes in little endian form 39 | for i := 0; i < bytelen; i++ { 40 | // set i = j*8 + k 41 | tmp := i / 8 42 | k := uint64(i % 8) 43 | output[i] = byte(fp2.A[tmp] >> (8 * k)) 44 | output[i+bytelen] = byte(fp2.B[tmp] >> (8 * k)) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /dh/sidh/doc.go: -------------------------------------------------------------------------------- 1 | // Package sidh provides implementation of experimental post-quantum 2 | // Supersingular Isogeny Diffie-Hellman (SIDH) as well as Supersingular 3 | // Isogeny Key Encapsulation (SIKE). 4 | // 5 | // Code is based on implementation from https://github.com/cloudflare/circl 6 | // 7 | // References: 8 | // - [SIDH] https://eprint.iacr.org/2011/506 9 | // - [SIKE] http://www.sike.org/files/SIDH-spec.pdf 10 | // 11 | package sidh 12 | -------------------------------------------------------------------------------- /dh/sidh/internal/doc.go: -------------------------------------------------------------------------------- 1 | package sidh 2 | -------------------------------------------------------------------------------- /dh/sidh/internal/p434/arith_amd64_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | // +build amd64,!noasm 5 | 6 | package p434 7 | 8 | import ( 9 | "reflect" 10 | "testing" 11 | "testing/quick" 12 | 13 | "github.com/henrydcase/nobs/dh/sidh/common" 14 | "golang.org/x/sys/cpu" 15 | ) 16 | 17 | type OptimFlag uint 18 | 19 | const ( 20 | // Indicates that optimisation which uses MUL instruction should be used 21 | kUse_MUL OptimFlag = 1 << 0 22 | // Indicates that optimisation which uses MULX, ADOX and ADCX instructions should be used 23 | kUse_MULXandADxX = 1 << 1 24 | ) 25 | 26 | func resetCpuFeatures() { 27 | HasADXandBMI2 = cpu.X86.HasBMI2 && cpu.X86.HasADX 28 | } 29 | 30 | // Utility function used for testing Mul implementations. Tests caller provided 31 | // mulFunc against mul() 32 | func testMul(t *testing.T, f1, f2 OptimFlag) { 33 | doMulTest := func(multiplier, multiplicant common.Fp) bool { 34 | defer resetCpuFeatures() 35 | var resMulRef, resMulOptim common.FpX2 36 | 37 | // Compute multiplier*multiplicant with first implementation 38 | HasADXandBMI2 = (kUse_MULXandADxX & f1) == kUse_MULXandADxX 39 | mulP434(&resMulOptim, &multiplier, &multiplicant) 40 | 41 | // Compute multiplier*multiplicant with second implementation 42 | HasADXandBMI2 = (kUse_MULXandADxX & f2) == kUse_MULXandADxX 43 | mulP434(&resMulRef, &multiplier, &multiplicant) 44 | 45 | // Compare results 46 | return reflect.DeepEqual(resMulRef, resMulOptim) 47 | } 48 | 49 | if err := quick.Check(doMulTest, quickCheckConfig); err != nil { 50 | t.Error(err) 51 | } 52 | } 53 | 54 | // Utility function used for testing REDC implementations. Tests caller provided 55 | // redcFunc against redc() 56 | func testRedc(t *testing.T, f1, f2 OptimFlag) { 57 | doRedcTest := func(aRR common.FpX2) bool { 58 | defer resetCpuFeatures() 59 | var resRedcF1, resRedcF2 common.Fp 60 | var aRRcpy = aRR 61 | 62 | // Compute redc with first implementation 63 | HasADXandBMI2 = (kUse_MULXandADxX & f1) == kUse_MULXandADxX 64 | rdcP434(&resRedcF1, &aRR) 65 | 66 | // Compute redc with second implementation 67 | HasADXandBMI2 = (kUse_MULXandADxX & f2) == kUse_MULXandADxX 68 | rdcP434(&resRedcF2, &aRRcpy) 69 | 70 | // Compare results 71 | return reflect.DeepEqual(resRedcF2, resRedcF1) 72 | } 73 | 74 | if err := quick.Check(doRedcTest, quickCheckConfig); err != nil { 75 | t.Error(err) 76 | } 77 | } 78 | 79 | // Ensures correctness of implementation of mul operation which uses MULX and ADOX/ADCX 80 | func TestMulWithMULXADxX(t *testing.T) { 81 | defer resetCpuFeatures() 82 | if !HasADXandBMI2 { 83 | t.Skip("MULX, ADCX and ADOX not supported by the platform") 84 | } 85 | testMul(t, kUse_MULXandADxX, kUse_MUL) 86 | } 87 | 88 | // Ensures correctness of Montgomery reduction implementation which uses MULX 89 | // and ADCX/ADOX. 90 | func TestRedcWithMULXADxX(t *testing.T) { 91 | defer resetCpuFeatures() 92 | if !HasADXandBMI2 { 93 | t.Skip("MULX, ADCX and ADOX not supported by the platform") 94 | } 95 | testRedc(t, kUse_MULXandADxX, kUse_MUL) 96 | } 97 | -------------------------------------------------------------------------------- /dh/sidh/internal/p434/arith_decl.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | // +build amd64,!noasm 5 | 6 | package p434 7 | 8 | import ( 9 | . "github.com/henrydcase/nobs/dh/sidh/common" 10 | ) 11 | 12 | // If choice = 0, leave x,y unchanged. If choice = 1, set x,y = y,x. 13 | // If choice is neither 0 nor 1 then behaviour is undefined. 14 | // This function executes in constant time. 15 | //go:noescape 16 | func cswapP434(x, y *Fp, choice uint8) 17 | 18 | // Compute z = x + y (mod p). 19 | //go:noescape 20 | func addP434(z, x, y *Fp) 21 | 22 | // Compute z = x - y (mod p). 23 | //go:noescape 24 | func subP434(z, x, y *Fp) 25 | 26 | // Compute z = x + y, without reducing mod p. 27 | //go:noescape 28 | func adlP434(z, x, y *FpX2) 29 | 30 | // Compute z = x - y, without reducing mod p. 31 | //go:noescape 32 | func sulP434(z, x, y *FpX2) 33 | 34 | // Reduce a field element in [0, 2*p) to one in [0,p). 35 | //go:noescape 36 | func modP434(x *Fp) 37 | 38 | // Computes z = x * y. 39 | //go:noescape 40 | func mulP434(z *FpX2, x, y *Fp) 41 | 42 | // Computes the Montgomery reduction z = x R^{-1} (mod 2*p). On return value 43 | // of x may be changed. z=x not allowed. 44 | //go:noescape 45 | func rdcP434(z *Fp, x *FpX2) 46 | -------------------------------------------------------------------------------- /dh/sidh/internal/p434/arith_generic.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | // +build noasm,arm64 !amd64 5 | 6 | package p434 7 | 8 | import ( 9 | "math/bits" 10 | 11 | "github.com/henrydcase/nobs/dh/sidh/common" 12 | ) 13 | 14 | // Compute z = x + y (mod p). 15 | func addP434(z, x, y *common.Fp) { 16 | var carry uint64 17 | 18 | // z=x+y % P434 19 | for i := 0; i < FpWords; i++ { 20 | z[i], carry = bits.Add64(x[i], y[i], carry) 21 | } 22 | 23 | // z = z - P434x2 24 | carry = 0 25 | for i := 0; i < FpWords; i++ { 26 | z[i], carry = bits.Sub64(z[i], P434x2[i], carry) 27 | } 28 | 29 | // if z<0 add P434x2 back 30 | mask := uint64(0 - carry) 31 | carry = 0 32 | for i := 0; i < FpWords; i++ { 33 | z[i], carry = bits.Add64(z[i], P434x2[i]&mask, carry) 34 | } 35 | } 36 | 37 | // Compute z = x - y (mod p). 38 | func subP434(z, x, y *common.Fp) { 39 | var borrow uint64 40 | 41 | for i := 0; i < FpWords; i++ { 42 | z[i], borrow = bits.Sub64(x[i], y[i], borrow) 43 | } 44 | 45 | mask := uint64(0 - borrow) 46 | borrow = 0 47 | 48 | for i := 0; i < FpWords; i++ { 49 | z[i], borrow = bits.Add64(z[i], P434x2[i]&mask, borrow) 50 | } 51 | } 52 | 53 | // Conditionally swaps bits in x and y in constant time. 54 | // mask indicates bits to be swapped (set bits are swapped) 55 | // For details see "Hackers Delight, 2.20" 56 | // 57 | // Implementation doesn't actually depend on a prime field. 58 | func cswapP434(x, y *common.Fp, mask uint8) { 59 | var tmp, mask64 uint64 60 | 61 | mask64 = 0 - uint64(mask) 62 | for i := 0; i < FpWords; i++ { 63 | tmp = mask64 & (x[i] ^ y[i]) 64 | x[i] = tmp ^ x[i] 65 | y[i] = tmp ^ y[i] 66 | } 67 | } 68 | 69 | // Perform Montgomery reduction: set z = x R^{-1} (mod 2*p) 70 | // with R=2^(FpWords*64). Destroys the input value. 71 | func rdcP434(z *common.Fp, x *common.FpX2) { 72 | var carry, t, u, v uint64 73 | var hi, lo uint64 74 | var count int 75 | 76 | count = P434p1Zeros 77 | 78 | for i := 0; i < FpWords; i++ { 79 | for j := 0; j < i; j++ { 80 | if j < (i - count + 1) { 81 | hi, lo = bits.Mul64(z[j], P434p1[i-j]) 82 | v, carry = bits.Add64(lo, v, 0) 83 | u, carry = bits.Add64(hi, u, carry) 84 | t += carry 85 | } 86 | } 87 | v, carry = bits.Add64(v, x[i], 0) 88 | u, carry = bits.Add64(u, 0, carry) 89 | t += carry 90 | 91 | z[i] = v 92 | v = u 93 | u = t 94 | t = 0 95 | } 96 | 97 | for i := FpWords; i < 2*FpWords-1; i++ { 98 | if count > 0 { 99 | count-- 100 | } 101 | for j := i - FpWords + 1; j < FpWords; j++ { 102 | if j < (FpWords - count) { 103 | hi, lo = bits.Mul64(z[j], P434p1[i-j]) 104 | v, carry = bits.Add64(lo, v, 0) 105 | u, carry = bits.Add64(hi, u, carry) 106 | t += carry 107 | } 108 | } 109 | v, carry = bits.Add64(v, x[i], 0) 110 | u, carry = bits.Add64(u, 0, carry) 111 | 112 | t += carry 113 | z[i-FpWords] = v 114 | v = u 115 | u = t 116 | t = 0 117 | } 118 | v, _ = bits.Add64(v, x[2*FpWords-1], 0) 119 | z[FpWords-1] = v 120 | } 121 | 122 | // Compute z = x * y. 123 | func mulP434(z *common.FpX2, x, y *common.Fp) { 124 | var u, v, t uint64 125 | var hi, lo uint64 126 | var carry uint64 127 | 128 | for i := uint64(0); i < FpWords; i++ { 129 | for j := uint64(0); j <= i; j++ { 130 | hi, lo = bits.Mul64(x[j], y[i-j]) 131 | v, carry = bits.Add64(lo, v, 0) 132 | u, carry = bits.Add64(hi, u, carry) 133 | t += carry 134 | } 135 | z[i] = v 136 | v = u 137 | u = t 138 | t = 0 139 | } 140 | 141 | for i := FpWords; i < (2*FpWords)-1; i++ { 142 | for j := i - FpWords + 1; j < FpWords; j++ { 143 | hi, lo = bits.Mul64(x[j], y[i-j]) 144 | v, carry = bits.Add64(lo, v, 0) 145 | u, carry = bits.Add64(hi, u, carry) 146 | t += carry 147 | } 148 | z[i] = v 149 | v = u 150 | u = t 151 | t = 0 152 | } 153 | z[2*FpWords-1] = v 154 | } 155 | 156 | // Compute z = x + y, without reducing mod p. 157 | func adlP434(z, x, y *common.FpX2) { 158 | var carry uint64 159 | for i := 0; i < 2*FpWords; i++ { 160 | z[i], carry = bits.Add64(x[i], y[i], carry) 161 | } 162 | } 163 | 164 | // Reduce a field element in [0, 2*p) to one in [0,p). 165 | func modP434(x *common.Fp) { 166 | var borrow, mask uint64 167 | for i := 0; i < FpWords; i++ { 168 | x[i], borrow = bits.Sub64(x[i], P434[i], borrow) 169 | } 170 | 171 | // Sets all bits if borrow = 1 172 | mask = 0 - borrow 173 | borrow = 0 174 | for i := 0; i < FpWords; i++ { 175 | x[i], borrow = bits.Add64(x[i], P434[i]&mask, borrow) 176 | } 177 | } 178 | 179 | // Compute z = x - y, without reducing mod p. 180 | func sulP434(z, x, y *common.FpX2) { 181 | var borrow, mask uint64 182 | for i := 0; i < 2*FpWords; i++ { 183 | z[i], borrow = bits.Sub64(x[i], y[i], borrow) 184 | } 185 | 186 | // Sets all bits if borrow = 1 187 | mask = 0 - borrow 188 | borrow = 0 189 | for i := FpWords; i < 2*FpWords; i++ { 190 | z[i], borrow = bits.Add64(z[i], P434[i-FpWords]&mask, borrow) 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /dh/sidh/internal/p434/arith_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package p434 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/henrydcase/nobs/dh/sidh/common" 10 | ) 11 | 12 | // Package-level storage for this field element is intended to deter 13 | // compiler optimizations. 14 | var ( 15 | benchmarkFp common.Fp 16 | benchmarkFpX2 common.FpX2 17 | bench_x = common.Fp{17026702066521327207, 5108203422050077993, 10225396685796065916, 11153620995215874678, 6531160855165088358, 15302925148404145445, 1248821577836769963, 9789766903037985294, 7493111552032041328, 10838999828319306046, 18103257655515297935, 27403304611634} 18 | bench_y = common.Fp{4227467157325093378, 10699492810770426363, 13500940151395637365, 12966403950118934952, 16517692605450415877, 13647111148905630666, 14223628886152717087, 7167843152346903316, 15855377759596736571, 4300673881383687338, 6635288001920617779, 30486099554235} 19 | bench_z = common.FpX2{1595347748594595712, 10854920567160033970, 16877102267020034574, 12435724995376660096, 3757940912203224231, 8251999420280413600, 3648859773438820227, 17622716832674727914, 11029567000887241528, 11216190007549447055, 17606662790980286987, 4720707159513626555, 12887743598335030915, 14954645239176589309, 14178817688915225254, 1191346797768989683, 12629157932334713723, 6348851952904485603, 16444232588597434895, 7809979927681678066, 14642637672942531613, 3092657597757640067, 10160361564485285723, 240071237} 20 | ) 21 | 22 | func TestFpCswap(t *testing.T) { 23 | var one = common.Fp{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} 24 | var two = common.Fp{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2} 25 | 26 | var x = one 27 | var y = two 28 | 29 | cswapP434(&x, &y, 0) 30 | for i := 0; i < FpWords; i++ { 31 | if (x[i] != one[i]) || (y[i] != two[i]) { 32 | t.Error("Found", x, "expected", two) 33 | } 34 | } 35 | 36 | cswapP434(&x, &y, 1) 37 | for i := 0; i < FpWords; i++ { 38 | if (x[i] != two[i]) || (y[i] != one[i]) { 39 | t.Error("Found", x, "expected", two) 40 | } 41 | } 42 | } 43 | 44 | // Benchmarking for field arithmetic 45 | func BenchmarkMul(b *testing.B) { 46 | for n := 0; n < b.N; n++ { 47 | mulP434(&benchmarkFpX2, &bench_x, &bench_y) 48 | } 49 | } 50 | 51 | func BenchmarkRdc(b *testing.B) { 52 | z := bench_z 53 | 54 | // This benchmark actually computes garbage, because 55 | // rdcP434 mangles its input, but since it's 56 | // constant-time that shouldn't matter for the benchmarks. 57 | for n := 0; n < b.N; n++ { 58 | rdcP434(&benchmarkFp, &z) 59 | } 60 | } 61 | 62 | func BenchmarkAdd(b *testing.B) { 63 | for n := 0; n < b.N; n++ { 64 | addP434(&benchmarkFp, &bench_x, &bench_y) 65 | } 66 | } 67 | 68 | func BenchmarkSub(b *testing.B) { 69 | for n := 0; n < b.N; n++ { 70 | subP434(&benchmarkFp, &bench_x, &bench_y) 71 | } 72 | } 73 | 74 | func BenchmarkCswap(b *testing.B) { 75 | x, y := bench_x, bench_y 76 | for n := 0; n < b.N; n++ { 77 | cswapP434(&x, &y, 1) 78 | cswapP434(&x, &y, 0) 79 | } 80 | } 81 | 82 | func BenchmarkMod(b *testing.B) { 83 | x := bench_x 84 | for n := 0; n < b.N; n++ { 85 | modP434(&x) 86 | } 87 | } 88 | 89 | func BenchmarkX2AddLazy(b *testing.B) { 90 | x, y, z := bench_z, bench_z, bench_z 91 | for n := 0; n < b.N; n++ { 92 | adlP434(&x, &y, &z) 93 | } 94 | } 95 | 96 | func BenchmarkX2SubLazy(b *testing.B) { 97 | x, y, z := bench_z, bench_z, bench_z 98 | for n := 0; n < b.N; n++ { 99 | sulP434(&x, &y, &z) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /dh/sidh/internal/p434/curve_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package p434 5 | 6 | import ( 7 | "bytes" 8 | "testing" 9 | 10 | . "github.com/henrydcase/nobs/dh/sidh/common" 11 | ) 12 | 13 | func vartimeEqProjFp2(lhs, rhs *ProjectivePoint) bool { 14 | var t0, t1 Fp2 15 | mul(&t0, &lhs.X, &rhs.Z) 16 | mul(&t1, &lhs.Z, &rhs.X) 17 | return vartimeEqFp2(&t0, &t1) 18 | } 19 | 20 | func toAffine(point *ProjectivePoint) *Fp2 { 21 | var affineX Fp2 22 | inv(&affineX, &point.Z) 23 | mul(&affineX, &affineX, &point.X) 24 | return &affineX 25 | } 26 | 27 | func Test_jInvariant(t *testing.T) { 28 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 29 | var jbufRes = make([]byte, params.SharedSecretSize) 30 | var jbufExp = make([]byte, params.SharedSecretSize) 31 | var jInv Fp2 32 | 33 | Jinvariant(&curve, &jInv) 34 | FromMontgomery(&jInv, &jInv) 35 | Fp2ToBytes(jbufRes, &jInv, params.Bytelen) 36 | 37 | jInv = expectedJ 38 | FromMontgomery(&jInv, &jInv) 39 | Fp2ToBytes(jbufExp, &jInv, params.Bytelen) 40 | 41 | if !bytes.Equal(jbufRes[:], jbufExp[:]) { 42 | t.Error("Computed incorrect j-invariant: found\n", jbufRes, "\nexpected\n", jbufExp) 43 | } 44 | } 45 | 46 | func TestProjectivePointVartimeEq(t *testing.T) { 47 | var xP ProjectivePoint 48 | 49 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 50 | xQ := xP 51 | 52 | // Scale xQ, which results in the same projective point 53 | mul(&xQ.X, &xQ.X, &curveA) 54 | mul(&xQ.Z, &xQ.Z, &curveA) 55 | if !vartimeEqProjFp2(&xP, &xQ) { 56 | t.Error("Expected the scaled point to be equal to the original") 57 | } 58 | } 59 | 60 | func TestPointMulVersusSage(t *testing.T) { 61 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 62 | var cparams = CalcCurveParamsEquiv4(&curve) 63 | var xP ProjectivePoint 64 | 65 | // x 2 66 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 67 | Pow2k(&xP, &cparams, 1) 68 | afxQ := toAffine(&xP) 69 | if !vartimeEqFp2(afxQ, &affineXP2) { 70 | t.Error("\nExpected\n", affineXP2, "\nfound\n", afxQ) 71 | } 72 | 73 | // x 4 74 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 75 | Pow2k(&xP, &cparams, 2) 76 | afxQ = toAffine(&xP) 77 | if !vartimeEqFp2(afxQ, &affineXP4) { 78 | t.Error("\nExpected\n", affineXP4, "\nfound\n", afxQ) 79 | } 80 | } 81 | 82 | func TestPointMul9VersusSage(t *testing.T) { 83 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 84 | var cparams = CalcCurveParamsEquiv3(&curve) 85 | var xP ProjectivePoint 86 | 87 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 88 | Pow3k(&xP, &cparams, 2) 89 | afxQ := toAffine(&xP) 90 | if !vartimeEqFp2(afxQ, &affineXP9) { 91 | t.Error("\nExpected\n", affineXP9, "\nfound\n", afxQ) 92 | } 93 | } 94 | 95 | func BenchmarkThreePointLadder(b *testing.B) { 96 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 97 | for n := 0; n < b.N; n++ { 98 | ScalarMul3Pt(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], uint(len(scalar3Pt)*8), scalar3Pt[:]) 99 | } 100 | } 101 | 102 | func BenchmarkTraverseTreeSharedKeyA(b *testing.B) { 103 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 104 | var xR = ProjectivePoint{X: affineXP, Z: params.OneFp2} 105 | for n := 0; n < b.N; n++ { 106 | traverseTreeSharedKeyA(&curve, &xR) 107 | } 108 | } 109 | 110 | func BenchmarkPow2k(b *testing.B) { 111 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 112 | var xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 113 | var cparams = CalcCurveParamsEquiv4(&curve) 114 | for n := 0; n < b.N; n++ { 115 | Pow2k(&xP, &cparams, 2) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /dh/sidh/internal/p434/doc.go: -------------------------------------------------------------------------------- 1 | package p434 2 | -------------------------------------------------------------------------------- /dh/sidh/internal/p434/fp2.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package p434 5 | 6 | import ( 7 | "github.com/henrydcase/nobs/dh/sidh/common" 8 | ) 9 | 10 | // Montgomery multiplication. Input values must be already 11 | // in Montgomery domain. 12 | func mulP(dest, lhs, rhs *common.Fp) { 13 | var ab common.FpX2 14 | mulP434(&ab, lhs, rhs) // = a*b*R*R 15 | rdcP434(dest, &ab) // = a*b*R mod p 16 | } 17 | 18 | // Set dest = x^((p-3)/4). If x is square, this is 1/sqrt(x). 19 | // Uses variation of sliding-window algorithm from with window size 20 | // of 5 and least to most significant bit sliding (left-to-right) 21 | // See HAC 14.85 for general description. 22 | // 23 | // Allowed to overlap x with dest. 24 | // All values in Montgomery domains 25 | // Set dest = x^(2^k), for k >= 1, by repeated squarings. 26 | func p34(dest, x *common.Fp) { 27 | var lookup [16]common.Fp 28 | 29 | // This performs sum(powStrategy) + 1 squarings and len(lookup) + len(mulStrategy) 30 | // multiplications. 31 | powStrategy := []uint8{3, 10, 7, 5, 6, 5, 3, 8, 4, 7, 5, 6, 4, 5, 9, 6, 3, 11, 5, 5, 2, 8, 4, 7, 7, 8, 5, 6, 4, 8, 5, 2, 10, 6, 5, 4, 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1} 32 | mulStrategy := []uint8{2, 15, 9, 8, 14, 12, 2, 8, 5, 15, 8, 15, 6, 6, 3, 2, 0, 10, 9, 13, 1, 12, 3, 7, 1, 10, 8, 11, 2, 15, 14, 1, 11, 12, 14, 3, 11, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0} 33 | initialMul := uint8(8) 34 | 35 | // Precompute lookup table of odd multiples of x for window 36 | // size k=5. 37 | var xx common.Fp 38 | mulP(&xx, x, x) 39 | lookup[0] = *x 40 | for i := 1; i < 16; i++ { 41 | mulP(&lookup[i], &lookup[i-1], &xx) 42 | } 43 | 44 | // Now lookup = {x, x^3, x^5, ... } 45 | // so that lookup[i] = x^{2*i + 1} 46 | // so that lookup[k/2] = x^k, for odd k 47 | *dest = lookup[initialMul] 48 | for i := uint8(0); i < uint8(len(powStrategy)); i++ { 49 | mulP(dest, dest, dest) 50 | for j := uint8(1); j < powStrategy[i]; j++ { 51 | mulP(dest, dest, dest) 52 | } 53 | mulP(dest, dest, &lookup[mulStrategy[i]]) 54 | } 55 | } 56 | 57 | func add(dest, lhs, rhs *common.Fp2) { 58 | addP434(&dest.A, &lhs.A, &rhs.A) 59 | addP434(&dest.B, &lhs.B, &rhs.B) 60 | } 61 | 62 | func sub(dest, lhs, rhs *common.Fp2) { 63 | subP434(&dest.A, &lhs.A, &rhs.A) 64 | subP434(&dest.B, &lhs.B, &rhs.B) 65 | } 66 | 67 | func mul(dest, lhs, rhs *common.Fp2) { 68 | var bMinA, cMinD common.Fp 69 | var ac, bd common.FpX2 70 | var adPlusBc common.FpX2 71 | var acMinBd common.FpX2 72 | 73 | // Let (a,b,c,d) = (lhs.a,lhs.b,rhs.a,rhs.b). 74 | // 75 | // (a + bi)*(c + di) = (a*c - b*d) + (a*d + b*c)i 76 | // 77 | // Use Karatsuba's trick: note that 78 | // 79 | // (b - a)*(c - d) = (b*c + a*d) - a*c - b*d 80 | // 81 | // so (a*d + b*c) = (b-a)*(c-d) + a*c + b*d. 82 | mulP434(&ac, &lhs.A, &rhs.A) // = a*c*R*R 83 | mulP434(&bd, &lhs.B, &rhs.B) // = b*d*R*R 84 | subP434(&bMinA, &lhs.B, &lhs.A) // = (b-a)*R 85 | subP434(&cMinD, &rhs.A, &rhs.B) // = (c-d)*R 86 | mulP434(&adPlusBc, &bMinA, &cMinD) // = (b-a)*(c-d)*R*R 87 | adlP434(&adPlusBc, &adPlusBc, &ac) // = ((b-a)*(c-d) + a*c)*R*R 88 | adlP434(&adPlusBc, &adPlusBc, &bd) // = ((b-a)*(c-d) + a*c + b*d)*R*R 89 | rdcP434(&dest.B, &adPlusBc) // = (a*d + b*c)*R mod p 90 | sulP434(&acMinBd, &ac, &bd) // = (a*c - b*d)*R*R 91 | rdcP434(&dest.A, &acMinBd) // = (a*c - b*d)*R mod p 92 | } 93 | 94 | // Set dest = 1/x 95 | // 96 | // Allowed to overlap dest with x. 97 | // 98 | // Returns dest to allow chaining operations. 99 | func inv(dest, x *common.Fp2) { 100 | var e1, e2 common.FpX2 101 | var f1, f2 common.Fp 102 | 103 | // We want to compute 104 | // 105 | // 1 1 (a - bi) (a - bi) 106 | // -------- = -------- -------- = ----------- 107 | // (a + bi) (a + bi) (a - bi) (a^2 + b^2) 108 | // 109 | // Letting c = 1/(a^2 + b^2), this is 110 | // 111 | // 1/(a+bi) = a*c - b*ci. 112 | 113 | mulP434(&e1, &x.A, &x.A) // = a*a*R*R 114 | mulP434(&e2, &x.B, &x.B) // = b*b*R*R 115 | adlP434(&e1, &e1, &e2) // = (a^2 + b^2)*R*R 116 | rdcP434(&f1, &e1) // = (a^2 + b^2)*R mod p 117 | // Now f1 = a^2 + b^2 118 | 119 | mulP(&f2, &f1, &f1) 120 | p34(&f2, &f2) 121 | mulP(&f2, &f2, &f2) 122 | mulP(&f2, &f2, &f1) 123 | 124 | mulP434(&e1, &x.A, &f2) 125 | rdcP434(&dest.A, &e1) 126 | 127 | subP434(&f1, &common.Fp{}, &x.B) 128 | mulP434(&e1, &f1, &f2) 129 | rdcP434(&dest.B, &e1) 130 | } 131 | 132 | func sqr(dest, x *common.Fp2) { 133 | var a2, aPlusB, aMinusB common.Fp 134 | var a2MinB2, ab2 common.FpX2 135 | 136 | a := &x.A 137 | b := &x.B 138 | 139 | // (a + bi)*(a + bi) = (a^2 - b^2) + 2abi. 140 | addP434(&a2, a, a) // = a*R + a*R = 2*a*R 141 | addP434(&aPlusB, a, b) // = a*R + b*R = (a+b)*R 142 | subP434(&aMinusB, a, b) // = a*R - b*R = (a-b)*R 143 | mulP434(&a2MinB2, &aPlusB, &aMinusB) // = (a+b)*(a-b)*R*R = (a^2 - b^2)*R*R 144 | mulP434(&ab2, &a2, b) // = 2*a*b*R*R 145 | rdcP434(&dest.A, &a2MinB2) // = (a^2 - b^2)*R mod p 146 | rdcP434(&dest.B, &ab2) // = 2*a*b*R mod p 147 | } 148 | 149 | // In case choice == 1, performs following swap in constant time: 150 | // xPx <-> xQx 151 | // xPz <-> xQz 152 | // Otherwise returns xPx, xPz, xQx, xQz unchanged 153 | func cswap(xPx, xPz, xQx, xQz *common.Fp2, choice uint8) { 154 | cswapP434(&xPx.A, &xQx.A, choice) 155 | cswapP434(&xPx.B, &xQx.B, choice) 156 | cswapP434(&xPz.A, &xQz.A, choice) 157 | cswapP434(&xPz.B, &xQz.B, choice) 158 | } 159 | 160 | // Converts in.A and in.B to Montgomery domain and stores 161 | // in 'out' 162 | // out.A = in.A * R mod p 163 | // out.B = in.B * R mod p 164 | // Performs v = v*R^2*R^(-1) mod p, for both in.A and in.B 165 | func ToMontgomery(out, in *common.Fp2) { 166 | var aRR common.FpX2 167 | 168 | // a*R*R 169 | mulP434(&aRR, &in.A, &P434R2) 170 | // a*R mod p 171 | rdcP434(&out.A, &aRR) 172 | mulP434(&aRR, &in.B, &P434R2) 173 | rdcP434(&out.B, &aRR) 174 | } 175 | 176 | // Converts in.A and in.B from Montgomery domain and stores 177 | // in 'out' 178 | // out.A = in.A mod p 179 | // out.B = in.B mod p 180 | // 181 | // After returning from the call 'in' is not modified. 182 | func FromMontgomery(out, in *common.Fp2) { 183 | var aR common.FpX2 184 | 185 | // convert from montgomery domain 186 | copy(aR[:], in.A[:]) 187 | rdcP434(&out.A, &aR) // = a mod p in [0, 2p) 188 | modP434(&out.A) // = a mod p in [0, p) 189 | for i := range aR { 190 | aR[i] = 0 191 | } 192 | copy(aR[:], in.B[:]) 193 | rdcP434(&out.B, &aR) 194 | modP434(&out.B) 195 | } 196 | -------------------------------------------------------------------------------- /dh/sidh/internal/p434/fp2_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package p434 5 | 6 | import ( 7 | "math/rand" 8 | "reflect" 9 | "testing" 10 | "testing/quick" 11 | 12 | "github.com/henrydcase/nobs/dh/sidh/common" 13 | ) 14 | 15 | type testParams struct { 16 | Point common.ProjectivePoint 17 | Cparam common.ProjectiveCurveParameters 18 | ExtElem common.Fp2 19 | } 20 | 21 | // Returns true if lhs = rhs. Takes variable time. 22 | func vartimeEqFp2(lhs, rhs *common.Fp2) bool { 23 | a := *lhs 24 | b := *rhs 25 | 26 | modP434(&a.A) 27 | modP434(&a.B) 28 | modP434(&b.A) 29 | modP434(&b.B) 30 | 31 | eq := true 32 | for i := 0; i < FpWords && eq; i++ { 33 | eq = eq && (a.A[i] == b.A[i]) 34 | eq = eq && (a.B[i] == b.B[i]) 35 | } 36 | return eq 37 | } 38 | 39 | func (testParams) generateFp2(rand *rand.Rand) common.Fp2 { 40 | // Generation strategy: low limbs taken from [0,2^64); high limb 41 | // taken from smaller range 42 | // 43 | // Size hint is ignored since all elements are fixed size. 44 | // 45 | // Field elements taken in range [0,2p). Emulate this by capping 46 | // the high limb by the top digit of 2*p-1: 47 | // 48 | // sage: (2*p-1).digits(2^64)[-1] 49 | // 50 | // This still allows generating values >= 2p, but hopefully that 51 | // excess is OK (and if it's not, we'll find out, because it's for 52 | // testing...) 53 | highLimb := rand.Uint64() % P434x2[FpWords-1] 54 | fpElementGen := func() (fp common.Fp) { 55 | for i := 0; i < (FpWords - 1); i++ { 56 | fp[i] = rand.Uint64() 57 | } 58 | fp[FpWords-1] = highLimb 59 | return fp 60 | } 61 | return common.Fp2{A: fpElementGen(), B: fpElementGen()} 62 | } 63 | 64 | func (c testParams) Generate(rand *rand.Rand, size int) reflect.Value { 65 | return reflect.ValueOf( 66 | testParams{ 67 | common.ProjectivePoint{ 68 | X: c.generateFp2(rand), 69 | Z: c.generateFp2(rand), 70 | }, 71 | common.ProjectiveCurveParameters{ 72 | A: c.generateFp2(rand), 73 | C: c.generateFp2(rand), 74 | }, 75 | c.generateFp2(rand), 76 | }) 77 | } 78 | 79 | func TestOne(t *testing.T) { 80 | var tmp common.Fp2 81 | 82 | mul(&tmp, ¶ms.OneFp2, ¶ms.A.AffineP) 83 | if !vartimeEqFp2(&tmp, ¶ms.A.AffineP) { 84 | t.Error("Not equal 1") 85 | } 86 | } 87 | 88 | func TestFp2ToBytesRoundTrip(t *testing.T) { 89 | roundTrips := func(x testParams) bool { 90 | var xBytes = make([]byte, 2*params.Bytelen) 91 | var xPrime common.Fp2 92 | 93 | common.Fp2ToBytes(xBytes[:], &x.ExtElem, params.Bytelen) 94 | common.BytesToFp2(&xPrime, xBytes[:], params.Bytelen) 95 | return vartimeEqFp2(&xPrime, &x.ExtElem) 96 | } 97 | 98 | if err := quick.Check(roundTrips, quickCheckConfig); err != nil { 99 | t.Error(err) 100 | } 101 | } 102 | 103 | func TestFp2MulDistributesOverAdd(t *testing.T) { 104 | mulDistributesOverAdd := func(x, y, z testParams) bool { 105 | // Compute t1 = (x+y)*z 106 | t1 := new(common.Fp2) 107 | add(t1, &x.ExtElem, &y.ExtElem) 108 | mul(t1, t1, &z.ExtElem) 109 | 110 | // Compute t2 = x*z + y*z 111 | t2 := new(common.Fp2) 112 | t3 := new(common.Fp2) 113 | mul(t2, &x.ExtElem, &z.ExtElem) 114 | mul(t3, &y.ExtElem, &z.ExtElem) 115 | add(t2, t2, t3) 116 | 117 | return vartimeEqFp2(t1, t2) 118 | } 119 | 120 | if err := quick.Check(mulDistributesOverAdd, quickCheckConfig); err != nil { 121 | t.Error(err) 122 | } 123 | } 124 | 125 | func TestFp2MulIsAssociative(t *testing.T) { 126 | isAssociative := func(x, y, z testParams) bool { 127 | // Compute t1 = (x*y)*z 128 | t1 := new(common.Fp2) 129 | mul(t1, &x.ExtElem, &y.ExtElem) 130 | mul(t1, t1, &z.ExtElem) 131 | 132 | // Compute t2 = (y*z)*x 133 | t2 := new(common.Fp2) 134 | mul(t2, &y.ExtElem, &z.ExtElem) 135 | mul(t2, t2, &x.ExtElem) 136 | 137 | return vartimeEqFp2(t1, t2) 138 | } 139 | 140 | if err := quick.Check(isAssociative, quickCheckConfig); err != nil { 141 | t.Error(err) 142 | } 143 | } 144 | 145 | func TestFp2SquareMatchesMul(t *testing.T) { 146 | sqrMatchesMul := func(x testParams) bool { 147 | // Compute t1 = (x*x) 148 | t1 := new(common.Fp2) 149 | mul(t1, &x.ExtElem, &x.ExtElem) 150 | 151 | // Compute t2 = x^2 152 | t2 := new(common.Fp2) 153 | sqr(t2, &x.ExtElem) 154 | 155 | return vartimeEqFp2(t1, t2) 156 | } 157 | 158 | if err := quick.Check(sqrMatchesMul, quickCheckConfig); err != nil { 159 | t.Error(err) 160 | } 161 | } 162 | 163 | func TestFp2Inv(t *testing.T) { 164 | inverseIsCorrect := func(x testParams) bool { 165 | z := new(common.Fp2) 166 | inv(z, &x.ExtElem) 167 | 168 | // Now z = (1/x), so (z * x) * x == x 169 | mul(z, z, &x.ExtElem) 170 | mul(z, z, &x.ExtElem) 171 | 172 | return vartimeEqFp2(z, &x.ExtElem) 173 | } 174 | 175 | // This is more expensive; run fewer tests 176 | var quickCheckConfig = &quick.Config{MaxCount: (1 << 11)} 177 | if err := quick.Check(inverseIsCorrect, quickCheckConfig); err != nil { 178 | t.Error(err) 179 | } 180 | } 181 | 182 | func TestFp2Batch3Inv(t *testing.T) { 183 | batchInverseIsCorrect := func(x1, x2, x3 testParams) bool { 184 | var x1Inv, x2Inv, x3Inv common.Fp2 185 | inv(&x1Inv, &x1.ExtElem) 186 | inv(&x2Inv, &x2.ExtElem) 187 | inv(&x3Inv, &x3.ExtElem) 188 | 189 | var y1, y2, y3 common.Fp2 190 | Fp2Batch3Inv(&x1.ExtElem, &x2.ExtElem, &x3.ExtElem, &y1, &y2, &y3) 191 | 192 | return (vartimeEqFp2(&x1Inv, &y1) && vartimeEqFp2(&x2Inv, &y2) && vartimeEqFp2(&x3Inv, &y3)) 193 | } 194 | 195 | // This is more expensive; run fewer tests 196 | var quickCheckConfig = &quick.Config{MaxCount: (1 << 8)} 197 | if err := quick.Check(batchInverseIsCorrect, quickCheckConfig); err != nil { 198 | t.Error(err) 199 | } 200 | } 201 | 202 | func BenchmarkFp2Mul(b *testing.B) { 203 | z := &common.Fp2{A: bench_x, B: bench_y} 204 | w := new(common.Fp2) 205 | 206 | for n := 0; n < b.N; n++ { 207 | mul(w, z, z) 208 | } 209 | } 210 | 211 | func BenchmarkFp2Inv(b *testing.B) { 212 | z := &common.Fp2{A: bench_x, B: bench_y} 213 | w := new(common.Fp2) 214 | 215 | for n := 0; n < b.N; n++ { 216 | inv(w, z) 217 | } 218 | } 219 | 220 | func BenchmarkFp2Square(b *testing.B) { 221 | z := &common.Fp2{A: bench_x, B: bench_y} 222 | w := new(common.Fp2) 223 | 224 | for n := 0; n < b.N; n++ { 225 | sqr(w, z) 226 | } 227 | } 228 | 229 | func BenchmarkFp2Add(b *testing.B) { 230 | z := &common.Fp2{A: bench_x, B: bench_y} 231 | w := new(common.Fp2) 232 | 233 | for n := 0; n < b.N; n++ { 234 | add(w, z, z) 235 | } 236 | } 237 | 238 | func BenchmarkFp2Sub(b *testing.B) { 239 | z := &common.Fp2{A: bench_x, B: bench_y} 240 | w := new(common.Fp2) 241 | 242 | for n := 0; n < b.N; n++ { 243 | sub(w, z, z) 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /dh/sidh/internal/p434/params_test.go: -------------------------------------------------------------------------------- 1 | package p434 2 | 3 | // Contains values used by tests 4 | import ( 5 | "testing/quick" 6 | 7 | . "github.com/henrydcase/nobs/dh/sidh/common" 8 | ) 9 | 10 | // Values omputed using Sage 11 | var ( 12 | expectedJ = Fp2{ 13 | A: Fp{0x38ECC0A0F53BACB4, 0xF987759E90A6C0DD, 0xC3007B353AE699F6, 0xB2B7E62A4F182414, 0xA65A854B34034F1B, 0xC71EAD20BE427422, 0xFC94F0D8DD51}, 14 | B: Fp{0xFC3B47615764A089, 0x9D32DF1BA8CF22E5, 0x7B895EF92F44C690, 0xE83667F85BBFA475, 0xD44627DCF539CA71, 0x9619A0E7D6657401, 0x4BC5BF1D9B01}} 15 | 16 | curveA = Fp2{ 17 | A: Fp{0x13A5A42C36E5E170, 0xC801DC4104E2C1DC, 0xB102AE39A7E24F31, 0x2FB616EA2E824C97, 0xB97073B55448AA67, 0x607266F7204D90DA, 0x1E98FE9739F27}, 18 | B: Fp{0x000000000000742C, 0x0000000000000000, 0x0000000000000000, 0xB90FF404FC000000, 0xD801A4FB559FACD4, 0xE93254545F77410C, 0x0ECEEA7BD2EDA}} 19 | 20 | curveC = Fp2{ 21 | A: Fp{0x8CBBA3505E5EDAB2, 0xB1DE7B91FBB77718, 0x6957392BFDC9BEB0, 0xC258E527E05FDDDE, 0x8C5FC7ADF5E50AE9, 0x1B2149FBEC2F4D18, 0x19FC2A5C79942}, 22 | B: Fp{0x000000000000E858, 0x0000000000000000, 0x0000000000000000, 0x721FE809F8000000, 0xB00349F6AB3F59A9, 0xD264A8A8BEEE8219, 0x1D9DD4F7A5DB5}} 23 | 24 | affineXP = Fp2{ 25 | A: Fp{0x775C29CA29E5FC3F, 0xCAB15BD1A1AB2754, 0x2C7F5B5DC58096EB, 0x2EE7B0B5A789355A, 0xBBD7BC749FF4D74E, 0x1373A265C9A9D58B, 0x5C183CE99B13}, 26 | B: Fp{0x38CDA704EB4D517C, 0x2F8BA33C91C147D4, 0x4D17E97F04A8D431, 0x5DB8F238AE1B099F, 0x44DC758CE879824C, 0x7E95F1151F6DFA3C, 0xB59F64352B87}} 27 | 28 | affineXP2 = Fp2{ 29 | A: Fp{0x2A5C658FD540804D, 0xA27CDB81FA7C6A5C, 0x6C36B6EB38B1B562, 0xC08642D636AF9A51, 0x36B2323A1279F346, 0x530BF3E8726D8B71, 0x61E38F638919}, 30 | B: Fp{0x5D835C52A68FC93D, 0x9E8FAF973A68306C, 0xB3C28FE9D155F61C, 0xCCE6FA22BC1A1FBF, 0xEAB44D8952802BA5, 0xEAAC0F259AAC3A8F, 0x959B242CE01A}} 31 | 32 | affineXP4 = Fp2{ 33 | A: Fp{0xF824931762C6DC4A, 0xA9B0FD30136F4B50, 0xAF041BBAB14DC6B1, 0x0AD52F55527A9BA2, 0x282B236D61F08C59, 0x5D3D7EC0C5EB9DCB, 0x10BBDDEA44BF7}, 34 | B: Fp{0x77D92493AF97245B, 0xD717FEC838D464C6, 0xCAACD67DB3BF965D, 0x82D59FB89CDC0711, 0xF13CAE433F39CDE1, 0x9B55DFB11A585FFA, 0x0DC8BA1C054D3}} 35 | 36 | affineXP9 = Fp2{ 37 | A: Fp{0x1F6F0785353A02C0, 0xCCB1B8524A63E37F, 0xB283C636B1FDD74C, 0xB76DBFF592DE6FF5, 0x15750EE706F18226, 0x50791362F26E459C, 0x1EA2A9074423}, 38 | B: Fp{0x945C6909DA5039A3, 0x349CFD24FD84FDAF, 0x2FD2F391F2E26E75, 0xEF73E8A634EBDC76, 0x59DDA2622AC22A6C, 0xE0370B80E15F61F4, 0xB302956A0276}} 39 | 40 | // Inputs for testing 3-point-ladder 41 | threePointLadderInputs = []ProjectivePoint{ 42 | // x(P) 43 | { 44 | X: Fp2{ 45 | A: Fp{0x43941FA9244C059E, 0xD1F337D076941189, 0x6B6A8B3A8763C96A, 0x6DF569708D6C9482, 0x487EE5707A52F4AA, 0xDE396F6E2559689E, 0xE5EE3895A8991469, 0x2B0946695790A8}, 46 | B: Fp{0xAB552C0FDAED092E, 0x7DF895E43E7DCB1C, 0x35C700E761920C4B, 0xCC5807DD70DC117A, 0x0884039A5A8DB18A, 0xD04620B3D0738052, 0xA200835605138F10, 0x3FF2E59B2FDC6A}}, 47 | Z: params.OneFp2, 48 | }, 49 | // x(Q) 50 | { 51 | X: Fp2{ 52 | A: Fp{0x77015826982BA1FD, 0x44024489673471E4, 0x1CAA2A5F4D5DA63B, 0xA183C07E50738C01, 0x8B97782D4E1A0DE6, 0x9B819522FBC38280, 0x0BDA46A937FB7B8A, 0x3B3614305914DF}, 53 | B: Fp{0xBF0366E97B3168D9, 0xAA522AC3879CEF0F, 0x0AF5EC975BD035C8, 0x1F26FEE7BBAC165C, 0xA0EE6A637724A6AB, 0xFB52101E36BA3A38, 0xD29CF5E376E17376, 0x1374A50DF57071}}, 54 | Z: params.OneFp2, 55 | }, 56 | // x(P-Q) 57 | { 58 | X: Fp2{ 59 | A: Fp{0xD99279BBD41EA559, 0x35CF18E72F578214, 0x90473B1DC77F73E8, 0xBFFEA930B25D7F66, 0xFD558EA177B900B2, 0x7CFAD273A782A23E, 0x6B1F610822E0F611, 0x26D2D2EF9619B5}, 60 | B: Fp{0x534F83651CBCC75D, 0x591FB4757AED5D08, 0x0B04353D40BED542, 0x829A94703AAC9139, 0x0F9C2E6D7663EB5B, 0x5D2D0F90C283F746, 0x34C872AA12A7676E, 0x0ECDB605FBFA16}}, 61 | Z: params.OneFp2, 62 | }, 63 | } 64 | scalar3Pt = [...]uint8{0x9f, 0x3b, 0xe7, 0xf9, 0xf4, 0x7c, 0xe6, 0xce, 0x79, 0x3e, 0x3d, 0x9f, 0x9f, 0x3b, 0xe7, 0xf9, 0xf4, 0x7c, 0xe6, 0xce, 0x79, 0x3e, 0x3d, 0x9f} 65 | ) 66 | 67 | var quickCheckConfig = &quick.Config{ 68 | MaxCount: (1 << 15), 69 | } 70 | -------------------------------------------------------------------------------- /dh/sidh/internal/p503/arith_amd64_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | // +build amd64,!noasm 5 | 6 | package p503 7 | 8 | import ( 9 | "reflect" 10 | "testing" 11 | "testing/quick" 12 | 13 | "github.com/henrydcase/nobs/dh/sidh/common" 14 | "golang.org/x/sys/cpu" 15 | ) 16 | 17 | type OptimFlag uint 18 | 19 | const ( 20 | // Indicates that optimisation which uses MUL instruction should be used 21 | kUse_MUL OptimFlag = 1 << 0 22 | // Indicates that optimisation which uses MULX instruction should be used 23 | kUse_MULX = 1 << 1 24 | // Indicates that optimisation which uses MULX, ADOX and ADCX instructions should be used 25 | kUse_MULXandADxX = 1 << 2 26 | ) 27 | 28 | func resetCpuFeatures() { 29 | HasBMI2 = cpu.X86.HasBMI2 30 | HasADXandBMI2 = cpu.X86.HasBMI2 && cpu.X86.HasADX 31 | } 32 | 33 | // Utility function used for testing Mul implementations. Tests caller provided 34 | // mulFunc against mul() 35 | func testMul(t *testing.T, f1, f2 OptimFlag) { 36 | doMulTest := func(multiplier, multiplicant common.Fp) bool { 37 | defer resetCpuFeatures() 38 | var resMulRef, resMulOptim common.FpX2 39 | 40 | // Compute multiplier*multiplicant with first implementation 41 | HasBMI2 = (kUse_MULX & f1) == kUse_MULX 42 | HasADXandBMI2 = (kUse_MULXandADxX & f1) == kUse_MULXandADxX 43 | mulP503(&resMulOptim, &multiplier, &multiplicant) 44 | 45 | // Compute multiplier*multiplicant with second implementation 46 | HasBMI2 = (kUse_MULX & f2) == kUse_MULX 47 | HasADXandBMI2 = (kUse_MULXandADxX & f2) == kUse_MULXandADxX 48 | mulP503(&resMulRef, &multiplier, &multiplicant) 49 | 50 | // Compare results 51 | return reflect.DeepEqual(resMulRef, resMulOptim) 52 | } 53 | 54 | if err := quick.Check(doMulTest, quickCheckConfig); err != nil { 55 | t.Error(err) 56 | } 57 | } 58 | 59 | // Utility function used for testing REDC implementations. Tests caller provided 60 | // redcFunc against redc() 61 | func testRedc(t *testing.T, f1, f2 OptimFlag) { 62 | doRedcTest := func(aRR common.FpX2) bool { 63 | defer resetCpuFeatures() 64 | var resRedcF1, resRedcF2 common.Fp 65 | var aRRcpy = aRR 66 | 67 | // Compute redc with first implementation 68 | HasBMI2 = (kUse_MULX & f1) == kUse_MULX 69 | HasADXandBMI2 = (kUse_MULXandADxX & f1) == kUse_MULXandADxX 70 | rdcP503(&resRedcF1, &aRR) 71 | 72 | // Compute redc with second implementation 73 | HasBMI2 = (kUse_MULX & f2) == kUse_MULX 74 | HasADXandBMI2 = (kUse_MULXandADxX & f2) == kUse_MULXandADxX 75 | rdcP503(&resRedcF2, &aRRcpy) 76 | 77 | // Compare results 78 | return reflect.DeepEqual(resRedcF2, resRedcF1) 79 | } 80 | 81 | if err := quick.Check(doRedcTest, quickCheckConfig); err != nil { 82 | t.Error(err) 83 | } 84 | } 85 | 86 | // Ensures correctness of implementation of mul operation which uses MULX 87 | func TestMulWithMULX(t *testing.T) { 88 | defer resetCpuFeatures() 89 | if !HasBMI2 { 90 | t.Skip("MULX not supported by the platform") 91 | } 92 | testMul(t, kUse_MULX, kUse_MUL) 93 | } 94 | 95 | // Ensures correctness of implementation of mul operation which uses MULX and ADOX/ADCX 96 | func TestMulWithMULXADxX(t *testing.T) { 97 | defer resetCpuFeatures() 98 | if !HasADXandBMI2 { 99 | t.Skip("MULX, ADCX and ADOX not supported by the platform") 100 | } 101 | testMul(t, kUse_MULXandADxX, kUse_MUL) 102 | } 103 | 104 | // Ensures correctness of implementation of mul operation which uses MULX and ADOX/ADCX 105 | func TestMulWithMULXADxXAgainstMULX(t *testing.T) { 106 | defer resetCpuFeatures() 107 | if !HasADXandBMI2 { 108 | t.Skip("MULX, ADCX and ADOX not supported by the platform") 109 | } 110 | testMul(t, kUse_MULX, kUse_MULXandADxX) 111 | } 112 | 113 | // Ensures correctness of Montgomery reduction implementation which uses MULX 114 | func TestRedcWithMULX(t *testing.T) { 115 | defer resetCpuFeatures() 116 | if !HasBMI2 { 117 | t.Skip("MULX not supported by the platform") 118 | } 119 | testRedc(t, kUse_MULX, kUse_MUL) 120 | } 121 | 122 | // Ensures correctness of Montgomery reduction implementation which uses MULX 123 | // and ADCX/ADOX. 124 | func TestRedcWithMULXADxX(t *testing.T) { 125 | defer resetCpuFeatures() 126 | if !HasADXandBMI2 { 127 | t.Skip("MULX, ADCX and ADOX not supported by the platform") 128 | } 129 | testRedc(t, kUse_MULXandADxX, kUse_MUL) 130 | } 131 | 132 | // Ensures correctness of Montgomery reduction implementation which uses MULX 133 | // and ADCX/ADOX. 134 | func TestRedcWithMULXADxXAgainstMULX(t *testing.T) { 135 | defer resetCpuFeatures() 136 | if !HasADXandBMI2 { 137 | t.Skip("MULX, ADCX and ADOX not supported by the platform") 138 | } 139 | testRedc(t, kUse_MULXandADxX, kUse_MULX) 140 | } 141 | -------------------------------------------------------------------------------- /dh/sidh/internal/p503/arith_decl.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | // +build amd64,!noasm arm64,!noasm 5 | 6 | package p503 7 | 8 | import ( 9 | . "github.com/henrydcase/nobs/dh/sidh/common" 10 | ) 11 | 12 | // If choice = 0, leave x,y unchanged. If choice = 1, set x,y = y,x. 13 | // If choice is neither 0 nor 1 then behaviour is undefined. 14 | // This function executes in constant time. 15 | //go:noescape 16 | func cswapP503(x, y *Fp, choice uint8) 17 | 18 | // Compute z = x + y (mod p). 19 | //go:noescape 20 | func addP503(z, x, y *Fp) 21 | 22 | // Compute z = x - y (mod p). 23 | //go:noescape 24 | func subP503(z, x, y *Fp) 25 | 26 | // Compute z = x + y, without reducing mod p. 27 | //go:noescape 28 | func adlP503(z, x, y *FpX2) 29 | 30 | // Compute z = x - y, without reducing mod p. 31 | //go:noescape 32 | func sulP503(z, x, y *FpX2) 33 | 34 | // Reduce a field element in [0, 2*p) to one in [0,p). 35 | //go:noescape 36 | func modP503(x *Fp) 37 | 38 | // Computes z = x * y. 39 | //go:noescape 40 | func mulP503(z *FpX2, x, y *Fp) 41 | 42 | // Computes the Montgomery reduction z = x R^{-1} (mod 2*p). On return value 43 | // of x may be changed. z=x not allowed. 44 | //go:noescape 45 | func rdcP503(z *Fp, x *FpX2) 46 | -------------------------------------------------------------------------------- /dh/sidh/internal/p503/arith_generic.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | // +build noasm !amd64,!arm64 5 | 6 | package p503 7 | 8 | import ( 9 | "math/bits" 10 | 11 | "github.com/henrydcase/nobs/dh/sidh/common" 12 | ) 13 | 14 | // Compute z = x + y (mod p). 15 | func addP503(z, x, y *common.Fp) { 16 | var carry uint64 17 | 18 | // z=x+y % P503 19 | for i := 0; i < FpWords; i++ { 20 | z[i], carry = bits.Add64(x[i], y[i], carry) 21 | } 22 | 23 | // z = z - P503x2 24 | carry = 0 25 | for i := 0; i < FpWords; i++ { 26 | z[i], carry = bits.Sub64(z[i], P503x2[i], carry) 27 | } 28 | 29 | // if z<0 add P503x2 back 30 | mask := uint64(0 - carry) 31 | carry = 0 32 | for i := 0; i < FpWords; i++ { 33 | z[i], carry = bits.Add64(z[i], P503x2[i]&mask, carry) 34 | } 35 | } 36 | 37 | // Compute z = x - y (mod p). 38 | func subP503(z, x, y *common.Fp) { 39 | var borrow uint64 40 | 41 | for i := 0; i < FpWords; i++ { 42 | z[i], borrow = bits.Sub64(x[i], y[i], borrow) 43 | } 44 | 45 | mask := uint64(0 - borrow) 46 | borrow = 0 47 | 48 | for i := 0; i < FpWords; i++ { 49 | z[i], borrow = bits.Add64(z[i], P503x2[i]&mask, borrow) 50 | } 51 | } 52 | 53 | // Conditionally swaps bits in x and y in constant time. 54 | // mask indicates bits to be swapped (set bits are swapped) 55 | // For details see "Hackers Delight, 2.20" 56 | // 57 | // Implementation doesn't actually depend on a prime field. 58 | func cswapP503(x, y *common.Fp, mask uint8) { 59 | var tmp, mask64 uint64 60 | 61 | mask64 = 0 - uint64(mask) 62 | for i := 0; i < FpWords; i++ { 63 | tmp = mask64 & (x[i] ^ y[i]) 64 | x[i] = tmp ^ x[i] 65 | y[i] = tmp ^ y[i] 66 | } 67 | } 68 | 69 | // Perform Montgomery reduction: set z = x R^{-1} (mod 2*p) 70 | // with R=2^(FpWords*64). Destroys the input value. 71 | func rdcP503(z *common.Fp, x *common.FpX2) { 72 | var carry, t, u, v uint64 73 | var hi, lo uint64 74 | var count int 75 | 76 | count = P503p1Zeros 77 | 78 | for i := 0; i < FpWords; i++ { 79 | for j := 0; j < i; j++ { 80 | if j < (i - count + 1) { 81 | hi, lo = bits.Mul64(z[j], P503p1[i-j]) 82 | v, carry = bits.Add64(lo, v, 0) 83 | u, carry = bits.Add64(hi, u, carry) 84 | t += carry 85 | } 86 | } 87 | v, carry = bits.Add64(v, x[i], 0) 88 | u, carry = bits.Add64(u, 0, carry) 89 | t += carry 90 | 91 | z[i] = v 92 | v = u 93 | u = t 94 | t = 0 95 | } 96 | 97 | for i := FpWords; i < 2*FpWords-1; i++ { 98 | if count > 0 { 99 | count-- 100 | } 101 | for j := i - FpWords + 1; j < FpWords; j++ { 102 | if j < (FpWords - count) { 103 | hi, lo = bits.Mul64(z[j], P503p1[i-j]) 104 | v, carry = bits.Add64(lo, v, 0) 105 | u, carry = bits.Add64(hi, u, carry) 106 | t += carry 107 | } 108 | } 109 | v, carry = bits.Add64(v, x[i], 0) 110 | u, carry = bits.Add64(u, 0, carry) 111 | 112 | t += carry 113 | z[i-FpWords] = v 114 | v = u 115 | u = t 116 | t = 0 117 | } 118 | v, _ = bits.Add64(v, x[2*FpWords-1], 0) 119 | z[FpWords-1] = v 120 | } 121 | 122 | // Compute z = x * y. 123 | func mulP503(z *common.FpX2, x, y *common.Fp) { 124 | var u, v, t uint64 125 | var hi, lo uint64 126 | var carry uint64 127 | 128 | for i := uint64(0); i < FpWords; i++ { 129 | for j := uint64(0); j <= i; j++ { 130 | hi, lo = bits.Mul64(x[j], y[i-j]) 131 | v, carry = bits.Add64(lo, v, 0) 132 | u, carry = bits.Add64(hi, u, carry) 133 | t += carry 134 | } 135 | z[i] = v 136 | v = u 137 | u = t 138 | t = 0 139 | } 140 | 141 | for i := FpWords; i < (2*FpWords)-1; i++ { 142 | for j := i - FpWords + 1; j < FpWords; j++ { 143 | hi, lo = bits.Mul64(x[j], y[i-j]) 144 | v, carry = bits.Add64(lo, v, 0) 145 | u, carry = bits.Add64(hi, u, carry) 146 | t += carry 147 | } 148 | z[i] = v 149 | v = u 150 | u = t 151 | t = 0 152 | } 153 | z[2*FpWords-1] = v 154 | } 155 | 156 | // Compute z = x + y, without reducing mod p. 157 | func adlP503(z, x, y *common.FpX2) { 158 | var carry uint64 159 | for i := 0; i < 2*FpWords; i++ { 160 | z[i], carry = bits.Add64(x[i], y[i], carry) 161 | } 162 | } 163 | 164 | // Reduce a field element in [0, 2*p) to one in [0,p). 165 | func modP503(x *common.Fp) { 166 | var borrow, mask uint64 167 | for i := 0; i < FpWords; i++ { 168 | x[i], borrow = bits.Sub64(x[i], P503[i], borrow) 169 | } 170 | 171 | // Sets all bits if borrow = 1 172 | mask = 0 - borrow 173 | borrow = 0 174 | for i := 0; i < FpWords; i++ { 175 | x[i], borrow = bits.Add64(x[i], P503[i]&mask, borrow) 176 | } 177 | } 178 | 179 | // Compute z = x - y, without reducing mod p. 180 | func sulP503(z, x, y *common.FpX2) { 181 | var borrow, mask uint64 182 | for i := 0; i < 2*FpWords; i++ { 183 | z[i], borrow = bits.Sub64(x[i], y[i], borrow) 184 | } 185 | 186 | // Sets all bits if borrow = 1 187 | mask = 0 - borrow 188 | borrow = 0 189 | for i := FpWords; i < 2*FpWords; i++ { 190 | z[i], borrow = bits.Add64(z[i], P503[i-FpWords]&mask, borrow) 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /dh/sidh/internal/p503/arith_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package p503 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/henrydcase/nobs/dh/sidh/common" 10 | ) 11 | 12 | // Package-level storage for this field element is intended to deter 13 | // compiler optimizations. 14 | var ( 15 | benchmarkFp common.Fp 16 | benchmarkFpX2 common.FpX2 17 | bench_x = common.Fp{17026702066521327207, 5108203422050077993, 10225396685796065916, 11153620995215874678, 6531160855165088358, 15302925148404145445, 1248821577836769963, 9789766903037985294, 7493111552032041328, 10838999828319306046, 18103257655515297935, 27403304611634} 18 | bench_y = common.Fp{4227467157325093378, 10699492810770426363, 13500940151395637365, 12966403950118934952, 16517692605450415877, 13647111148905630666, 14223628886152717087, 7167843152346903316, 15855377759596736571, 4300673881383687338, 6635288001920617779, 30486099554235} 19 | bench_z = common.FpX2{1595347748594595712, 10854920567160033970, 16877102267020034574, 12435724995376660096, 3757940912203224231, 8251999420280413600, 3648859773438820227, 17622716832674727914, 11029567000887241528, 11216190007549447055, 17606662790980286987, 4720707159513626555, 12887743598335030915, 14954645239176589309, 14178817688915225254, 1191346797768989683, 12629157932334713723, 6348851952904485603, 16444232588597434895, 7809979927681678066, 14642637672942531613, 3092657597757640067, 10160361564485285723, 240071237} 20 | ) 21 | 22 | func TestFpCswap(t *testing.T) { 23 | var one = common.Fp{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} 24 | var two = common.Fp{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2} 25 | 26 | var x = one 27 | var y = two 28 | 29 | cswapP503(&x, &y, 0) 30 | for i := 0; i < FpWords; i++ { 31 | if (x[i] != one[i]) || (y[i] != two[i]) { 32 | t.Error("Found", x, "expected", two) 33 | } 34 | } 35 | 36 | cswapP503(&x, &y, 1) 37 | for i := 0; i < FpWords; i++ { 38 | if (x[i] != two[i]) || (y[i] != one[i]) { 39 | t.Error("Found", x, "expected", two) 40 | } 41 | } 42 | } 43 | 44 | // Benchmarking for field arithmetic 45 | func BenchmarkMul(b *testing.B) { 46 | for n := 0; n < b.N; n++ { 47 | mulP503(&benchmarkFpX2, &bench_x, &bench_y) 48 | } 49 | } 50 | 51 | func BenchmarkRdc(b *testing.B) { 52 | z := bench_z 53 | 54 | // This benchmark actually computes garbage, because 55 | // rdcP503 mangles its input, but since it's 56 | // constant-time that shouldn't matter for the benchmarks. 57 | for n := 0; n < b.N; n++ { 58 | rdcP503(&benchmarkFp, &z) 59 | } 60 | } 61 | 62 | func BenchmarkAdd(b *testing.B) { 63 | for n := 0; n < b.N; n++ { 64 | addP503(&benchmarkFp, &bench_x, &bench_y) 65 | } 66 | } 67 | 68 | func BenchmarkSub(b *testing.B) { 69 | for n := 0; n < b.N; n++ { 70 | subP503(&benchmarkFp, &bench_x, &bench_y) 71 | } 72 | } 73 | 74 | func BenchmarkCswap(b *testing.B) { 75 | x, y := bench_x, bench_y 76 | for n := 0; n < b.N; n++ { 77 | cswapP503(&x, &y, 1) 78 | cswapP503(&x, &y, 0) 79 | } 80 | } 81 | 82 | func BenchmarkMod(b *testing.B) { 83 | x := bench_x 84 | for n := 0; n < b.N; n++ { 85 | modP503(&x) 86 | } 87 | } 88 | 89 | func BenchmarkX2AddLazy(b *testing.B) { 90 | x, y, z := bench_z, bench_z, bench_z 91 | for n := 0; n < b.N; n++ { 92 | adlP503(&x, &y, &z) 93 | } 94 | } 95 | 96 | func BenchmarkX2SubLazy(b *testing.B) { 97 | x, y, z := bench_z, bench_z, bench_z 98 | for n := 0; n < b.N; n++ { 99 | sulP503(&x, &y, &z) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /dh/sidh/internal/p503/curve_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package p503 5 | 6 | import ( 7 | "bytes" 8 | "testing" 9 | 10 | . "github.com/henrydcase/nobs/dh/sidh/common" 11 | ) 12 | 13 | func vartimeEqProjFp2(lhs, rhs *ProjectivePoint) bool { 14 | var t0, t1 Fp2 15 | mul(&t0, &lhs.X, &rhs.Z) 16 | mul(&t1, &lhs.Z, &rhs.X) 17 | return vartimeEqFp2(&t0, &t1) 18 | } 19 | 20 | func toAffine(point *ProjectivePoint) *Fp2 { 21 | var affineX Fp2 22 | inv(&affineX, &point.Z) 23 | mul(&affineX, &affineX, &point.X) 24 | return &affineX 25 | } 26 | 27 | func Test_jInvariant(t *testing.T) { 28 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 29 | var jbufRes = make([]byte, params.SharedSecretSize) 30 | var jbufExp = make([]byte, params.SharedSecretSize) 31 | var jInv Fp2 32 | 33 | Jinvariant(&curve, &jInv) 34 | FromMontgomery(&jInv, &jInv) 35 | Fp2ToBytes(jbufRes, &jInv, params.Bytelen) 36 | 37 | jInv = expectedJ 38 | FromMontgomery(&jInv, &jInv) 39 | Fp2ToBytes(jbufExp, &jInv, params.Bytelen) 40 | 41 | if !bytes.Equal(jbufRes[:], jbufExp[:]) { 42 | t.Error("Computed incorrect j-invariant: found\n", jbufRes, "\nexpected\n", jbufExp) 43 | } 44 | } 45 | 46 | func TestProjectivePointVartimeEq(t *testing.T) { 47 | var xP ProjectivePoint 48 | 49 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 50 | xQ := xP 51 | 52 | // Scale xQ, which results in the same projective point 53 | mul(&xQ.X, &xQ.X, &curveA) 54 | mul(&xQ.Z, &xQ.Z, &curveA) 55 | if !vartimeEqProjFp2(&xP, &xQ) { 56 | t.Error("Expected the scaled point to be equal to the original") 57 | } 58 | } 59 | 60 | func TestPointMulVersusSage(t *testing.T) { 61 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 62 | var cparams = CalcCurveParamsEquiv4(&curve) 63 | var xP ProjectivePoint 64 | 65 | // x 2 66 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 67 | Pow2k(&xP, &cparams, 1) 68 | afxQ := toAffine(&xP) 69 | if !vartimeEqFp2(afxQ, &affineXP2) { 70 | t.Error("\nExpected\n", affineXP2, "\nfound\n", afxQ) 71 | } 72 | 73 | // x 4 74 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 75 | Pow2k(&xP, &cparams, 2) 76 | afxQ = toAffine(&xP) 77 | if !vartimeEqFp2(afxQ, &affineXP4) { 78 | t.Error("\nExpected\n", affineXP4, "\nfound\n", afxQ) 79 | } 80 | } 81 | 82 | func TestPointMul9VersusSage(t *testing.T) { 83 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 84 | var cparams = CalcCurveParamsEquiv3(&curve) 85 | var xP ProjectivePoint 86 | 87 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 88 | Pow3k(&xP, &cparams, 2) 89 | afxQ := toAffine(&xP) 90 | if !vartimeEqFp2(afxQ, &affineXP9) { 91 | t.Error("\nExpected\n", affineXP9, "\nfound\n", afxQ) 92 | } 93 | } 94 | 95 | func BenchmarkThreePointLadder(b *testing.B) { 96 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 97 | for n := 0; n < b.N; n++ { 98 | ScalarMul3Pt(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], uint(len(scalar3Pt)*8), scalar3Pt[:]) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /dh/sidh/internal/p503/doc.go: -------------------------------------------------------------------------------- 1 | // Package p503 provides implementation of field arithmetic used in SIDH and SIKE. 2 | package p503 3 | -------------------------------------------------------------------------------- /dh/sidh/internal/p503/fp2.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package p503 5 | 6 | import ( 7 | "github.com/henrydcase/nobs/dh/sidh/common" 8 | ) 9 | 10 | // Montgomery multiplication. Input values must be already 11 | // in Montgomery domain. 12 | func mulP(dest, lhs, rhs *common.Fp) { 13 | var ab common.FpX2 14 | mulP503(&ab, lhs, rhs) // = a*b*R*R 15 | rdcP503(dest, &ab) // = a*b*R mod p 16 | } 17 | 18 | // Set dest = x^((p-3)/4). If x is square, this is 1/sqrt(x). 19 | // Uses variation of sliding-window algorithm from with window size 20 | // of 5 and least to most significant bit sliding (left-to-right) 21 | // See HAC 14.85 for general description. 22 | // 23 | // Allowed to overlap x with dest. 24 | // All values in Montgomery domains 25 | // Set dest = x^(2^k), for k >= 1, by repeated squarings. 26 | func p34(dest, x *common.Fp) { 27 | var lookup [16]common.Fp 28 | 29 | // This performs sum(powStrategy) + 1 squarings and len(lookup) + len(mulStrategy) 30 | // multiplications. 31 | powStrategy := []uint8{12, 5, 5, 2, 7, 11, 3, 8, 4, 11, 4, 7, 5, 6, 3, 7, 5, 7, 2, 12, 5, 6, 4, 6, 8, 6, 4, 7, 5, 5, 8, 5, 8, 5, 5, 8, 9, 3, 6, 2, 10, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3} 32 | mulStrategy := []uint8{12, 11, 10, 0, 1, 8, 3, 7, 1, 8, 3, 6, 7, 14, 2, 14, 14, 9, 0, 13, 9, 15, 5, 12, 7, 13, 7, 15, 6, 7, 9, 0, 5, 7, 6, 8, 8, 3, 7, 0, 10, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 3} 33 | initialMul := uint8(0) 34 | 35 | // Precompute lookup table of odd multiples of x for window 36 | // size k=5. 37 | var xx common.Fp 38 | mulP(&xx, x, x) 39 | lookup[0] = *x 40 | for i := 1; i < 16; i++ { 41 | mulP(&lookup[i], &lookup[i-1], &xx) 42 | } 43 | 44 | // Now lookup = {x, x^3, x^5, ... } 45 | // so that lookup[i] = x^{2*i + 1} 46 | // so that lookup[k/2] = x^k, for odd k 47 | *dest = lookup[initialMul] 48 | for i := uint8(0); i < uint8(len(powStrategy)); i++ { 49 | mulP(dest, dest, dest) 50 | for j := uint8(1); j < powStrategy[i]; j++ { 51 | mulP(dest, dest, dest) 52 | } 53 | mulP(dest, dest, &lookup[mulStrategy[i]]) 54 | } 55 | } 56 | 57 | func add(dest, lhs, rhs *common.Fp2) { 58 | addP503(&dest.A, &lhs.A, &rhs.A) 59 | addP503(&dest.B, &lhs.B, &rhs.B) 60 | } 61 | 62 | func sub(dest, lhs, rhs *common.Fp2) { 63 | subP503(&dest.A, &lhs.A, &rhs.A) 64 | subP503(&dest.B, &lhs.B, &rhs.B) 65 | } 66 | 67 | func mul(dest, lhs, rhs *common.Fp2) { 68 | var bMinA, cMinD common.Fp 69 | var ac, bd common.FpX2 70 | var adPlusBc common.FpX2 71 | var acMinBd common.FpX2 72 | 73 | // Let (a,b,c,d) = (lhs.a,lhs.b,rhs.a,rhs.b). 74 | // 75 | // (a + bi)*(c + di) = (a*c - b*d) + (a*d + b*c)i 76 | // 77 | // Use Karatsuba's trick: note that 78 | // 79 | // (b - a)*(c - d) = (b*c + a*d) - a*c - b*d 80 | // 81 | // so (a*d + b*c) = (b-a)*(c-d) + a*c + b*d. 82 | mulP503(&ac, &lhs.A, &rhs.A) // = a*c*R*R 83 | mulP503(&bd, &lhs.B, &rhs.B) // = b*d*R*R 84 | subP503(&bMinA, &lhs.B, &lhs.A) // = (b-a)*R 85 | subP503(&cMinD, &rhs.A, &rhs.B) // = (c-d)*R 86 | mulP503(&adPlusBc, &bMinA, &cMinD) // = (b-a)*(c-d)*R*R 87 | adlP503(&adPlusBc, &adPlusBc, &ac) // = ((b-a)*(c-d) + a*c)*R*R 88 | adlP503(&adPlusBc, &adPlusBc, &bd) // = ((b-a)*(c-d) + a*c + b*d)*R*R 89 | rdcP503(&dest.B, &adPlusBc) // = (a*d + b*c)*R mod p 90 | sulP503(&acMinBd, &ac, &bd) // = (a*c - b*d)*R*R 91 | rdcP503(&dest.A, &acMinBd) // = (a*c - b*d)*R mod p 92 | } 93 | 94 | // Set dest = 1/x 95 | // 96 | // Allowed to overlap dest with x. 97 | // 98 | // Returns dest to allow chaining operations. 99 | func inv(dest, x *common.Fp2) { 100 | var e1, e2 common.FpX2 101 | var f1, f2 common.Fp 102 | 103 | // We want to compute 104 | // 105 | // 1 1 (a - bi) (a - bi) 106 | // -------- = -------- -------- = ----------- 107 | // (a + bi) (a + bi) (a - bi) (a^2 + b^2) 108 | // 109 | // Letting c = 1/(a^2 + b^2), this is 110 | // 111 | // 1/(a+bi) = a*c - b*ci. 112 | 113 | mulP503(&e1, &x.A, &x.A) // = a*a*R*R 114 | mulP503(&e2, &x.B, &x.B) // = b*b*R*R 115 | adlP503(&e1, &e1, &e2) // = (a^2 + b^2)*R*R 116 | rdcP503(&f1, &e1) // = (a^2 + b^2)*R mod p 117 | // Now f1 = a^2 + b^2 118 | 119 | mulP(&f2, &f1, &f1) 120 | p34(&f2, &f2) 121 | mulP(&f2, &f2, &f2) 122 | mulP(&f2, &f2, &f1) 123 | 124 | mulP503(&e1, &x.A, &f2) 125 | rdcP503(&dest.A, &e1) 126 | 127 | subP503(&f1, &common.Fp{}, &x.B) 128 | mulP503(&e1, &f1, &f2) 129 | rdcP503(&dest.B, &e1) 130 | } 131 | 132 | func sqr(dest, x *common.Fp2) { 133 | var a2, aPlusB, aMinusB common.Fp 134 | var a2MinB2, ab2 common.FpX2 135 | 136 | a := &x.A 137 | b := &x.B 138 | 139 | // (a + bi)*(a + bi) = (a^2 - b^2) + 2abi. 140 | addP503(&a2, a, a) // = a*R + a*R = 2*a*R 141 | addP503(&aPlusB, a, b) // = a*R + b*R = (a+b)*R 142 | subP503(&aMinusB, a, b) // = a*R - b*R = (a-b)*R 143 | mulP503(&a2MinB2, &aPlusB, &aMinusB) // = (a+b)*(a-b)*R*R = (a^2 - b^2)*R*R 144 | mulP503(&ab2, &a2, b) // = 2*a*b*R*R 145 | rdcP503(&dest.A, &a2MinB2) // = (a^2 - b^2)*R mod p 146 | rdcP503(&dest.B, &ab2) // = 2*a*b*R mod p 147 | } 148 | 149 | // In case choice == 1, performs following swap in constant time: 150 | // xPx <-> xQx 151 | // xPz <-> xQz 152 | // Otherwise returns xPx, xPz, xQx, xQz unchanged 153 | func cswap(xPx, xPz, xQx, xQz *common.Fp2, choice uint8) { 154 | cswapP503(&xPx.A, &xQx.A, choice) 155 | cswapP503(&xPx.B, &xQx.B, choice) 156 | cswapP503(&xPz.A, &xQz.A, choice) 157 | cswapP503(&xPz.B, &xQz.B, choice) 158 | } 159 | 160 | // Converts in.A and in.B to Montgomery domain and stores 161 | // in 'out' 162 | // out.A = in.A * R mod p 163 | // out.B = in.B * R mod p 164 | // Performs v = v*R^2*R^(-1) mod p, for both in.A and in.B 165 | func ToMontgomery(out, in *common.Fp2) { 166 | var aRR common.FpX2 167 | 168 | // a*R*R 169 | mulP503(&aRR, &in.A, &P503R2) 170 | // a*R mod p 171 | rdcP503(&out.A, &aRR) 172 | mulP503(&aRR, &in.B, &P503R2) 173 | rdcP503(&out.B, &aRR) 174 | } 175 | 176 | // Converts in.A and in.B from Montgomery domain and stores 177 | // in 'out' 178 | // out.A = in.A mod p 179 | // out.B = in.B mod p 180 | // 181 | // After returning from the call 'in' is not modified. 182 | func FromMontgomery(out, in *common.Fp2) { 183 | var aR common.FpX2 184 | 185 | // convert from montgomery domain 186 | copy(aR[:], in.A[:]) 187 | rdcP503(&out.A, &aR) // = a mod p in [0, 2p) 188 | modP503(&out.A) // = a mod p in [0, p) 189 | for i := range aR { 190 | aR[i] = 0 191 | } 192 | copy(aR[:], in.B[:]) 193 | rdcP503(&out.B, &aR) 194 | modP503(&out.B) 195 | } 196 | -------------------------------------------------------------------------------- /dh/sidh/internal/p503/fp2_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package p503 5 | 6 | import ( 7 | "math/rand" 8 | "reflect" 9 | "testing" 10 | "testing/quick" 11 | 12 | "github.com/henrydcase/nobs/dh/sidh/common" 13 | ) 14 | 15 | type testParams struct { 16 | Point common.ProjectivePoint 17 | Cparam common.ProjectiveCurveParameters 18 | ExtElem common.Fp2 19 | } 20 | 21 | // Returns true if lhs = rhs. Takes variable time. 22 | func vartimeEqFp2(lhs, rhs *common.Fp2) bool { 23 | a := *lhs 24 | b := *rhs 25 | 26 | modP503(&a.A) 27 | modP503(&a.B) 28 | modP503(&b.A) 29 | modP503(&b.B) 30 | 31 | eq := true 32 | for i := 0; i < FpWords && eq; i++ { 33 | eq = eq && (a.A[i] == b.A[i]) 34 | eq = eq && (a.B[i] == b.B[i]) 35 | } 36 | return eq 37 | } 38 | 39 | func (testParams) generateFp2(rand *rand.Rand) common.Fp2 { 40 | // Generation strategy: low limbs taken from [0,2^64); high limb 41 | // taken from smaller range 42 | // 43 | // Size hint is ignored since all elements are fixed size. 44 | // 45 | // Field elements taken in range [0,2p). Emulate this by capping 46 | // the high limb by the top digit of 2*p-1: 47 | // 48 | // sage: (2*p-1).digits(2^64)[-1] 49 | // 50 | // This still allows generating values >= 2p, but hopefully that 51 | // excess is OK (and if it's not, we'll find out, because it's for 52 | // testing...) 53 | highLimb := rand.Uint64() % P503x2[FpWords-1] 54 | fpElementGen := func() (fp common.Fp) { 55 | for i := 0; i < (FpWords - 1); i++ { 56 | fp[i] = rand.Uint64() 57 | } 58 | fp[FpWords-1] = highLimb 59 | return fp 60 | } 61 | return common.Fp2{A: fpElementGen(), B: fpElementGen()} 62 | } 63 | 64 | func (c testParams) Generate(rand *rand.Rand, size int) reflect.Value { 65 | return reflect.ValueOf( 66 | testParams{ 67 | common.ProjectivePoint{ 68 | X: c.generateFp2(rand), 69 | Z: c.generateFp2(rand), 70 | }, 71 | common.ProjectiveCurveParameters{ 72 | A: c.generateFp2(rand), 73 | C: c.generateFp2(rand), 74 | }, 75 | c.generateFp2(rand), 76 | }) 77 | } 78 | 79 | func TestOne(t *testing.T) { 80 | var tmp common.Fp2 81 | 82 | mul(&tmp, ¶ms.OneFp2, ¶ms.A.AffineP) 83 | if !vartimeEqFp2(&tmp, ¶ms.A.AffineP) { 84 | t.Error("Not equal 1") 85 | } 86 | } 87 | 88 | func TestFp2ToBytesRoundTrip(t *testing.T) { 89 | roundTrips := func(x testParams) bool { 90 | var xBytes = make([]byte, 2*params.Bytelen) 91 | var xPrime common.Fp2 92 | 93 | common.Fp2ToBytes(xBytes[:], &x.ExtElem, params.Bytelen) 94 | common.BytesToFp2(&xPrime, xBytes[:], params.Bytelen) 95 | return vartimeEqFp2(&xPrime, &x.ExtElem) 96 | } 97 | 98 | if err := quick.Check(roundTrips, quickCheckConfig); err != nil { 99 | t.Error(err) 100 | } 101 | } 102 | 103 | func TestFp2MulDistributesOverAdd(t *testing.T) { 104 | mulDistributesOverAdd := func(x, y, z testParams) bool { 105 | // Compute t1 = (x+y)*z 106 | t1 := new(common.Fp2) 107 | add(t1, &x.ExtElem, &y.ExtElem) 108 | mul(t1, t1, &z.ExtElem) 109 | 110 | // Compute t2 = x*z + y*z 111 | t2 := new(common.Fp2) 112 | t3 := new(common.Fp2) 113 | mul(t2, &x.ExtElem, &z.ExtElem) 114 | mul(t3, &y.ExtElem, &z.ExtElem) 115 | add(t2, t2, t3) 116 | 117 | return vartimeEqFp2(t1, t2) 118 | } 119 | 120 | if err := quick.Check(mulDistributesOverAdd, quickCheckConfig); err != nil { 121 | t.Error(err) 122 | } 123 | } 124 | 125 | func TestFp2MulIsAssociative(t *testing.T) { 126 | isAssociative := func(x, y, z testParams) bool { 127 | // Compute t1 = (x*y)*z 128 | t1 := new(common.Fp2) 129 | mul(t1, &x.ExtElem, &y.ExtElem) 130 | mul(t1, t1, &z.ExtElem) 131 | 132 | // Compute t2 = (y*z)*x 133 | t2 := new(common.Fp2) 134 | mul(t2, &y.ExtElem, &z.ExtElem) 135 | mul(t2, t2, &x.ExtElem) 136 | 137 | return vartimeEqFp2(t1, t2) 138 | } 139 | 140 | if err := quick.Check(isAssociative, quickCheckConfig); err != nil { 141 | t.Error(err) 142 | } 143 | } 144 | 145 | func TestFp2SquareMatchesMul(t *testing.T) { 146 | sqrMatchesMul := func(x testParams) bool { 147 | // Compute t1 = (x*x) 148 | t1 := new(common.Fp2) 149 | mul(t1, &x.ExtElem, &x.ExtElem) 150 | 151 | // Compute t2 = x^2 152 | t2 := new(common.Fp2) 153 | sqr(t2, &x.ExtElem) 154 | 155 | return vartimeEqFp2(t1, t2) 156 | } 157 | 158 | if err := quick.Check(sqrMatchesMul, quickCheckConfig); err != nil { 159 | t.Error(err) 160 | } 161 | } 162 | 163 | func TestFp2Inv(t *testing.T) { 164 | inverseIsCorrect := func(x testParams) bool { 165 | z := new(common.Fp2) 166 | inv(z, &x.ExtElem) 167 | 168 | // Now z = (1/x), so (z * x) * x == x 169 | mul(z, z, &x.ExtElem) 170 | mul(z, z, &x.ExtElem) 171 | 172 | return vartimeEqFp2(z, &x.ExtElem) 173 | } 174 | 175 | // This is more expensive; run fewer tests 176 | var quickCheckConfig = &quick.Config{MaxCount: (1 << 11)} 177 | if err := quick.Check(inverseIsCorrect, quickCheckConfig); err != nil { 178 | t.Error(err) 179 | } 180 | } 181 | 182 | func TestFp2Batch3Inv(t *testing.T) { 183 | batchInverseIsCorrect := func(x1, x2, x3 testParams) bool { 184 | var x1Inv, x2Inv, x3Inv common.Fp2 185 | inv(&x1Inv, &x1.ExtElem) 186 | inv(&x2Inv, &x2.ExtElem) 187 | inv(&x3Inv, &x3.ExtElem) 188 | 189 | var y1, y2, y3 common.Fp2 190 | Fp2Batch3Inv(&x1.ExtElem, &x2.ExtElem, &x3.ExtElem, &y1, &y2, &y3) 191 | 192 | return (vartimeEqFp2(&x1Inv, &y1) && vartimeEqFp2(&x2Inv, &y2) && vartimeEqFp2(&x3Inv, &y3)) 193 | } 194 | 195 | // This is more expensive; run fewer tests 196 | var quickCheckConfig = &quick.Config{MaxCount: (1 << 8)} 197 | if err := quick.Check(batchInverseIsCorrect, quickCheckConfig); err != nil { 198 | t.Error(err) 199 | } 200 | } 201 | 202 | func BenchmarkFp2Mul(b *testing.B) { 203 | z := &common.Fp2{A: bench_x, B: bench_y} 204 | w := new(common.Fp2) 205 | 206 | for n := 0; n < b.N; n++ { 207 | mul(w, z, z) 208 | } 209 | } 210 | 211 | func BenchmarkFp2Inv(b *testing.B) { 212 | z := &common.Fp2{A: bench_x, B: bench_y} 213 | w := new(common.Fp2) 214 | 215 | for n := 0; n < b.N; n++ { 216 | inv(w, z) 217 | } 218 | } 219 | 220 | func BenchmarkFp2Square(b *testing.B) { 221 | z := &common.Fp2{A: bench_x, B: bench_y} 222 | w := new(common.Fp2) 223 | 224 | for n := 0; n < b.N; n++ { 225 | sqr(w, z) 226 | } 227 | } 228 | 229 | func BenchmarkFp2Add(b *testing.B) { 230 | z := &common.Fp2{A: bench_x, B: bench_y} 231 | w := new(common.Fp2) 232 | 233 | for n := 0; n < b.N; n++ { 234 | add(w, z, z) 235 | } 236 | } 237 | 238 | func BenchmarkFp2Sub(b *testing.B) { 239 | z := &common.Fp2{A: bench_x, B: bench_y} 240 | w := new(common.Fp2) 241 | 242 | for n := 0; n < b.N; n++ { 243 | sub(w, z, z) 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /dh/sidh/internal/p503/params_test.go: -------------------------------------------------------------------------------- 1 | package p503 2 | 3 | // Contains values used by tests 4 | import ( 5 | "testing/quick" 6 | 7 | . "github.com/henrydcase/nobs/dh/sidh/common" 8 | ) 9 | 10 | // Values omputed using Sage 11 | var ( 12 | // j = 3674553797500778604587777859668542828244523188705960771798425843588160903687122861541242595678107095655647237100722594066610650373491179241544334443939077738732728884873568393760629500307797547379838602108296735640313894560419*i + 3127495302417548295242630557836520229396092255080675419212556702820583041296798857582303163183558315662015469648040494128968509467224910895884358424271180055990446576645240058960358037224785786494172548090318531038910933793845 13 | expectedJ = Fp2{ 14 | A: Fp{0x2c441d03b72e27c, 0xf2c6748151dbf84, 0x3a774f6191070e, 0xa7c6212c9c800ba6, 0x23921b5cf09abc27, 0x9e1baefbb3cd4265, 0x8cd6a289f12e10dc, 0x3fa364128cf87e}, 15 | B: Fp{0xe7497ac2bf6b0596, 0x629ee01ad23bd039, 0x95ee11587a119fa7, 0x572fb28a24772269, 0x3c00410b6c71567e, 0xe681e83a345f8a34, 0x65d21b1d96bd2d52, 0x7889a47e58901}, 16 | } 17 | 18 | // A = 8752234765512331234913716743014562460822083005386252003333602919474238975785850965349950219277942402920758585086620525443539725921333735154674119646075*i + 6339624979889725406021454983012408976766782818694212228554611573314701271183857175866122275755278397694585249002282183018114967373119429936587424396917 19 | curveA = Fp2{ 20 | A: Fp{0xd9816986a543095f, 0xa78cb1d7217bec21, 0x9595dc97b74ea70, 0x9120a1da6b42797d, 0x59ef9d903f74e47c, 0x4c58a4cdc45b6d0b, 0x816d5213aaf7ee6d, 0x3892fee6bb7343}, 21 | B: Fp{0x28c5288acbedf11b, 0x2143a438c86f6c68, 0x7cb5c4ae9c4c8e34, 0xb478aea445eed48b, 0x24d5c175776db478, 0x234582f8676c0ebe, 0x56234267b625fb08, 0x2c6e58d84b1192}} 22 | 23 | // C = 10458464853790890798085664692909194316288127038910691163573355876336993883402795907795767791362493831987298578966325154262747805705783782806176495638177*i + 7770984753616185271325854825309278833018655051139367603077592443785629339985729818288672809062782315510526648882226172896710704020683893684611137718845 24 | curveC = Fp2{ 25 | A: Fp{0xe05948236f2f913b, 0xc45da9ad1219a255, 0x7a568972a32fc1d0, 0x30f00bdd7071c3b1, 0x3b761b8dac2c98bc, 0x760f21b2179737b6, 0x13217e6656a13476, 0x2606b798e685aa}, 26 | B: Fp{0x1c0171f78820052e, 0x440b7f7087e57140, 0xe0510c07b31b0e96, 0xd0cf489b2ac4aea9, 0x4fb328f1c1fdf783, 0xb3b4912342951cb7, 0x70a4b64e81961c42, 0x33eed63cf07181}} 27 | // x(P) = 9720237205826983370867050298878715935679372786589878620121159082290288918688002583435964840822877971257659901481591644347943354235932355923042390796255*i + 634577413124118560098123299804750904956499531431297942628887930019161512075536652691244843248133437326050395005054997679717801535474938466995392156605 28 | affineXP = Fp2{ 29 | A: Fp{0xb606d954d407faf2, 0x58a1ef6cd213a203, 0x9823b55033e62f7b, 0x59cafc060d5e25a1, 0x529685f1753526fc, 0xc2eac3d219989c7d, 0xc5e30c75dfd343a0, 0x378285adc968a0}, 30 | B: Fp{0x6670f36db977b9da, 0xa07e2fdda5e1a7f0, 0xf367a7a722aed87d, 0x6c269e06d595cd10, 0x8379aa6092d87700, 0x57276ce3557ee7ae, 0xac8107bfbcd28993, 0x3d6f98869617a7}} 31 | 32 | affineXP2 = Fp2{ 33 | A: Fp{0x4e1133c2b3855902, 0x875a775c67597fbb, 0xd17eb74254141abb, 0x1d5a464a4f3391f5, 0x24405c332811d007, 0x7e47e3eb489a7372, 0x65b130dfd9efe605, 0xfa69fac179803}, 34 | B: Fp{0x329f5322e1be51ee, 0x9004dca8132ebd6f, 0x7cd87e447ca8a7b6, 0x10a6ec02c38ce69e, 0x8cef2ed7d112ac46, 0x5f385a9fc4b57cd7, 0x68a366354fe7a32e, 0x2223c1455486ac}} 35 | 36 | affineXP4 = Fp2{ 37 | A: Fp{0x4eb695d34b46be8f, 0xfb5e76c58585f2d2, 0xa41f8aafa6dbb531, 0x4db82f5db5cfd144, 0x14dab0e3200cbba0, 0x430381706a279f81, 0xdf6707a57161f81, 0x44740f17197c3}, 38 | B: Fp{0xa2473705cdb6d4e9, 0xfa3cd67b9c15502c, 0xf0928166d0c5cee1, 0x6150aba0c874faaa, 0x6c0b18d6d92f9034, 0xcff71d340fc1e72e, 0x19a47027af917587, 0x25ed4bad443b8f}} 39 | 40 | affineXP9 = Fp2{ 41 | A: Fp{0x112da30e288217e0, 0x5b336d527320a5f7, 0xbbf4d9403b68e3c6, 0x55eccb31c40b359c, 0x8907129ab69b3203, 0x69cc8c750125a915, 0xa41a38e6f530c0e1, 0xbe68e23af1b8d}, 42 | B: Fp{0x472c603765964213, 0xe4e64995b0769754, 0x4515583c74a6dd24, 0xff7c57f5818363a2, 0xbeaeb24662a92177, 0x8a54fa61fbf24c68, 0xa85542049eb45e12, 0x2b54caf655e285}} 43 | 44 | // Inputs for testing 3-point-ladder 45 | threePointLadderInputs = []ProjectivePoint{ 46 | // x(P) 47 | { 48 | X: Fp2{ 49 | A: Fp{0x43941FA9244C059E, 0xD1F337D076941189, 0x6B6A8B3A8763C96A, 0x6DF569708D6C9482, 0x487EE5707A52F4AA, 0xDE396F6E2559689E, 0xE5EE3895A8991469, 0x2B0946695790A8}, 50 | B: Fp{0xAB552C0FDAED092E, 0x7DF895E43E7DCB1C, 0x35C700E761920C4B, 0xCC5807DD70DC117A, 0x0884039A5A8DB18A, 0xD04620B3D0738052, 0xA200835605138F10, 0x3FF2E59B2FDC6A}}, 51 | Z: params.OneFp2, 52 | }, 53 | // x(Q) 54 | { 55 | X: Fp2{ 56 | A: Fp{0x77015826982BA1FD, 0x44024489673471E4, 0x1CAA2A5F4D5DA63B, 0xA183C07E50738C01, 0x8B97782D4E1A0DE6, 0x9B819522FBC38280, 0x0BDA46A937FB7B8A, 0x3B3614305914DF}, 57 | B: Fp{0xBF0366E97B3168D9, 0xAA522AC3879CEF0F, 0x0AF5EC975BD035C8, 0x1F26FEE7BBAC165C, 0xA0EE6A637724A6AB, 0xFB52101E36BA3A38, 0xD29CF5E376E17376, 0x1374A50DF57071}}, 58 | Z: params.OneFp2, 59 | }, 60 | // x(P-Q) 61 | { 62 | X: Fp2{ 63 | A: Fp{0xD99279BBD41EA559, 0x35CF18E72F578214, 0x90473B1DC77F73E8, 0xBFFEA930B25D7F66, 0xFD558EA177B900B2, 0x7CFAD273A782A23E, 0x6B1F610822E0F611, 0x26D2D2EF9619B5}, 64 | B: Fp{0x534F83651CBCC75D, 0x591FB4757AED5D08, 0x0B04353D40BED542, 0x829A94703AAC9139, 0x0F9C2E6D7663EB5B, 0x5D2D0F90C283F746, 0x34C872AA12A7676E, 0x0ECDB605FBFA16}}, 65 | Z: params.OneFp2, 66 | }, 67 | } 68 | scalar3Pt = [...]uint8{0x9f, 0x3b, 0xe7, 0xf9, 0xf4, 0x7c, 0xe6, 0xce, 0x79, 0x3e, 0x3d, 0x9f, 0x9f, 0x3b, 0xe7, 0xf9, 0xf4, 0x7c, 0xe6, 0xce, 0x79, 0x3e, 0x3d, 0x9f} 69 | ) 70 | 71 | var quickCheckConfig = &quick.Config{ 72 | MaxCount: (1 << 15), 73 | } 74 | -------------------------------------------------------------------------------- /dh/sidh/internal/p751/arith_amd64_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | // +build amd64,!noasm 5 | 6 | package p751 7 | 8 | import ( 9 | "reflect" 10 | "testing" 11 | "testing/quick" 12 | 13 | "github.com/henrydcase/nobs/dh/sidh/common" 14 | "golang.org/x/sys/cpu" 15 | ) 16 | 17 | type OptimFlag uint 18 | 19 | const ( 20 | // Indicates that optimisation which uses MUL instruction should be used 21 | kUse_MUL OptimFlag = 1 << 0 22 | // Indicates that optimisation which uses MULX instruction should be used 23 | kUse_MULX = 1 << 1 24 | // Indicates that optimisation which uses MULX, ADOX and ADCX instructions should be used 25 | kUse_MULXandADxX = 1 << 2 26 | ) 27 | 28 | func resetCpuFeatures() { 29 | HasBMI2 = cpu.X86.HasBMI2 30 | HasADXandBMI2 = cpu.X86.HasBMI2 && cpu.X86.HasADX 31 | } 32 | 33 | // Utility function used for testing Mul implementations. Tests caller provided 34 | // mulFunc against mul() 35 | func testMul(t *testing.T, f1, f2 OptimFlag) { 36 | doMulTest := func(multiplier, multiplicant common.Fp) bool { 37 | defer resetCpuFeatures() 38 | var resMulRef, resMulOptim common.FpX2 39 | 40 | // Compute multiplier*multiplicant with first implementation 41 | HasBMI2 = (kUse_MULX & f1) == kUse_MULX 42 | HasADXandBMI2 = (kUse_MULXandADxX & f1) == kUse_MULXandADxX 43 | mulP751(&resMulOptim, &multiplier, &multiplicant) 44 | 45 | // Compute multiplier*multiplicant with second implementation 46 | HasBMI2 = (kUse_MULX & f2) == kUse_MULX 47 | HasADXandBMI2 = (kUse_MULXandADxX & f2) == kUse_MULXandADxX 48 | mulP751(&resMulRef, &multiplier, &multiplicant) 49 | 50 | // Compare results 51 | return reflect.DeepEqual(resMulRef, resMulOptim) 52 | } 53 | 54 | if err := quick.Check(doMulTest, quickCheckConfig); err != nil { 55 | t.Error(err) 56 | } 57 | } 58 | 59 | // Utility function used for testing REDC implementations. Tests caller provided 60 | // redcFunc against redc() 61 | func testRedc(t *testing.T, f1, f2 OptimFlag) { 62 | doRedcTest := func(aRR common.FpX2) bool { 63 | defer resetCpuFeatures() 64 | var resRedcF1, resRedcF2 common.Fp 65 | var aRRcpy = aRR 66 | 67 | // Compute redc with first implementation 68 | HasBMI2 = (kUse_MULX & f1) == kUse_MULX 69 | HasADXandBMI2 = (kUse_MULXandADxX & f1) == kUse_MULXandADxX 70 | rdcP751(&resRedcF1, &aRR) 71 | 72 | // Compute redc with second implementation 73 | HasBMI2 = (kUse_MULX & f2) == kUse_MULX 74 | HasADXandBMI2 = (kUse_MULXandADxX & f2) == kUse_MULXandADxX 75 | rdcP751(&resRedcF2, &aRRcpy) 76 | 77 | // Compare results 78 | return reflect.DeepEqual(resRedcF2, resRedcF1) 79 | } 80 | 81 | if err := quick.Check(doRedcTest, quickCheckConfig); err != nil { 82 | t.Error(err) 83 | } 84 | } 85 | 86 | // Ensures correctness of implementation of mul operation which uses MULX 87 | func TestMulWithMULX(t *testing.T) { 88 | defer resetCpuFeatures() 89 | if !HasBMI2 { 90 | t.Skip("MULX not supported by the platform") 91 | } 92 | testMul(t, kUse_MULX, kUse_MUL) 93 | } 94 | 95 | // Ensures correctness of implementation of mul operation which uses MULX and ADOX/ADCX 96 | func TestMulWithMULXADxX(t *testing.T) { 97 | defer resetCpuFeatures() 98 | if !HasADXandBMI2 { 99 | t.Skip("MULX, ADCX and ADOX not supported by the platform") 100 | } 101 | testMul(t, kUse_MULXandADxX, kUse_MUL) 102 | } 103 | 104 | // Ensures correctness of implementation of mul operation which uses MULX and ADOX/ADCX 105 | func TestMulWithMULXADxXAgainstMULX(t *testing.T) { 106 | defer resetCpuFeatures() 107 | if !HasADXandBMI2 { 108 | t.Skip("MULX, ADCX and ADOX not supported by the platform") 109 | } 110 | testMul(t, kUse_MULX, kUse_MULXandADxX) 111 | } 112 | 113 | // Ensures correctness of Montgomery reduction implementation which uses MULX 114 | func TestRedcWithMULX(t *testing.T) { 115 | defer resetCpuFeatures() 116 | if !HasBMI2 { 117 | t.Skip("MULX not supported by the platform") 118 | } 119 | testRedc(t, kUse_MULX, kUse_MUL) 120 | } 121 | 122 | // Ensures correctness of Montgomery reduction implementation which uses MULX 123 | // and ADCX/ADOX. 124 | func TestRedcWithMULXADxX(t *testing.T) { 125 | defer resetCpuFeatures() 126 | if !HasADXandBMI2 { 127 | t.Skip("MULX, ADCX and ADOX not supported by the platform") 128 | } 129 | testRedc(t, kUse_MULXandADxX, kUse_MUL) 130 | } 131 | 132 | // Ensures correctness of Montgomery reduction implementation which uses MULX 133 | // and ADCX/ADOX. 134 | func TestRedcWithMULXADxXAgainstMULX(t *testing.T) { 135 | defer resetCpuFeatures() 136 | if !HasADXandBMI2 { 137 | t.Skip("MULX, ADCX and ADOX not supported by the platform") 138 | } 139 | testRedc(t, kUse_MULXandADxX, kUse_MULX) 140 | } 141 | -------------------------------------------------------------------------------- /dh/sidh/internal/p751/arith_decl.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | // +build amd64,!noasm arm64,!noasm 5 | 6 | package p751 7 | 8 | import ( 9 | . "github.com/henrydcase/nobs/dh/sidh/common" 10 | ) 11 | 12 | // If choice = 0, leave x,y unchanged. If choice = 1, set x,y = y,x. 13 | // If choice is neither 0 nor 1 then behaviour is undefined. 14 | // This function executes in constant time. 15 | //go:noescape 16 | func cswapP751(x, y *Fp, choice uint8) 17 | 18 | // Compute z = x + y (mod p). 19 | //go:noescape 20 | func addP751(z, x, y *Fp) 21 | 22 | // Compute z = x - y (mod p). 23 | //go:noescape 24 | func subP751(z, x, y *Fp) 25 | 26 | // Compute z = x + y, without reducing mod p. 27 | //go:noescape 28 | func adlP751(z, x, y *FpX2) 29 | 30 | // Compute z = x - y, without reducing mod p. 31 | //go:noescape 32 | func sulP751(z, x, y *FpX2) 33 | 34 | // Reduce a field element in [0, 2*p) to one in [0,p). 35 | //go:noescape 36 | func modP751(x *Fp) 37 | 38 | // Computes z = x * y. 39 | //go:noescape 40 | func mulP751(z *FpX2, x, y *Fp) 41 | 42 | // Computes the Montgomery reduction z = x R^{-1} (mod 2*p). On return value 43 | // of x may be changed. z=x not allowed. 44 | //go:noescape 45 | func rdcP751(z *Fp, x *FpX2) 46 | -------------------------------------------------------------------------------- /dh/sidh/internal/p751/arith_generic.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | // +build noasm !amd64,!arm64 5 | 6 | package p751 7 | 8 | import ( 9 | "math/bits" 10 | 11 | "github.com/henrydcase/nobs/dh/sidh/common" 12 | ) 13 | 14 | // Compute z = x + y (mod p). 15 | func addP751(z, x, y *common.Fp) { 16 | var carry uint64 17 | 18 | // z=x+y % P751 19 | for i := 0; i < FpWords; i++ { 20 | z[i], carry = bits.Add64(x[i], y[i], carry) 21 | } 22 | 23 | // z = z - P751x2 24 | carry = 0 25 | for i := 0; i < FpWords; i++ { 26 | z[i], carry = bits.Sub64(z[i], P751x2[i], carry) 27 | } 28 | 29 | // if z<0 add P751x2 back 30 | mask := uint64(0 - carry) 31 | carry = 0 32 | for i := 0; i < FpWords; i++ { 33 | z[i], carry = bits.Add64(z[i], P751x2[i]&mask, carry) 34 | } 35 | } 36 | 37 | // Compute z = x - y (mod p). 38 | func subP751(z, x, y *common.Fp) { 39 | var borrow uint64 40 | 41 | for i := 0; i < FpWords; i++ { 42 | z[i], borrow = bits.Sub64(x[i], y[i], borrow) 43 | } 44 | 45 | mask := uint64(0 - borrow) 46 | borrow = 0 47 | 48 | for i := 0; i < FpWords; i++ { 49 | z[i], borrow = bits.Add64(z[i], P751x2[i]&mask, borrow) 50 | } 51 | } 52 | 53 | // Conditionally swaps bits in x and y in constant time. 54 | // mask indicates bits to be swapped (set bits are swapped) 55 | // For details see "Hackers Delight, 2.20" 56 | // 57 | // Implementation doesn't actually depend on a prime field. 58 | func cswapP751(x, y *common.Fp, mask uint8) { 59 | var tmp, mask64 uint64 60 | 61 | mask64 = 0 - uint64(mask) 62 | for i := 0; i < FpWords; i++ { 63 | tmp = mask64 & (x[i] ^ y[i]) 64 | x[i] = tmp ^ x[i] 65 | y[i] = tmp ^ y[i] 66 | } 67 | } 68 | 69 | // Perform Montgomery reduction: set z = x R^{-1} (mod 2*p) 70 | // with R=2^(FpWords*64). Destroys the input value. 71 | func rdcP751(z *common.Fp, x *common.FpX2) { 72 | var carry, t, u, v uint64 73 | var hi, lo uint64 74 | var count int 75 | 76 | count = P751p1Zeros 77 | 78 | for i := 0; i < FpWords; i++ { 79 | for j := 0; j < i; j++ { 80 | if j < (i - count + 1) { 81 | hi, lo = bits.Mul64(z[j], P751p1[i-j]) 82 | v, carry = bits.Add64(lo, v, 0) 83 | u, carry = bits.Add64(hi, u, carry) 84 | t += carry 85 | } 86 | } 87 | v, carry = bits.Add64(v, x[i], 0) 88 | u, carry = bits.Add64(u, 0, carry) 89 | t += carry 90 | 91 | z[i] = v 92 | v = u 93 | u = t 94 | t = 0 95 | } 96 | 97 | for i := FpWords; i < 2*FpWords-1; i++ { 98 | if count > 0 { 99 | count-- 100 | } 101 | for j := i - FpWords + 1; j < FpWords; j++ { 102 | if j < (FpWords - count) { 103 | hi, lo = bits.Mul64(z[j], P751p1[i-j]) 104 | v, carry = bits.Add64(lo, v, 0) 105 | u, carry = bits.Add64(hi, u, carry) 106 | t += carry 107 | } 108 | } 109 | v, carry = bits.Add64(v, x[i], 0) 110 | u, carry = bits.Add64(u, 0, carry) 111 | 112 | t += carry 113 | z[i-FpWords] = v 114 | v = u 115 | u = t 116 | t = 0 117 | } 118 | v, _ = bits.Add64(v, x[2*FpWords-1], 0) 119 | z[FpWords-1] = v 120 | } 121 | 122 | // Compute z = x * y. 123 | func mulP751(z *common.FpX2, x, y *common.Fp) { 124 | var u, v, t uint64 125 | var hi, lo uint64 126 | var carry uint64 127 | 128 | for i := uint64(0); i < FpWords; i++ { 129 | for j := uint64(0); j <= i; j++ { 130 | hi, lo = bits.Mul64(x[j], y[i-j]) 131 | v, carry = bits.Add64(lo, v, 0) 132 | u, carry = bits.Add64(hi, u, carry) 133 | t += carry 134 | } 135 | z[i] = v 136 | v = u 137 | u = t 138 | t = 0 139 | } 140 | 141 | for i := FpWords; i < (2*FpWords)-1; i++ { 142 | for j := i - FpWords + 1; j < FpWords; j++ { 143 | hi, lo = bits.Mul64(x[j], y[i-j]) 144 | v, carry = bits.Add64(lo, v, 0) 145 | u, carry = bits.Add64(hi, u, carry) 146 | t += carry 147 | } 148 | z[i] = v 149 | v = u 150 | u = t 151 | t = 0 152 | } 153 | z[2*FpWords-1] = v 154 | } 155 | 156 | // Compute z = x + y, without reducing mod p. 157 | func adlP751(z, x, y *common.FpX2) { 158 | var carry uint64 159 | for i := 0; i < 2*FpWords; i++ { 160 | z[i], carry = bits.Add64(x[i], y[i], carry) 161 | } 162 | } 163 | 164 | // Reduce a field element in [0, 2*p) to one in [0,p). 165 | func modP751(x *common.Fp) { 166 | var borrow, mask uint64 167 | for i := 0; i < FpWords; i++ { 168 | x[i], borrow = bits.Sub64(x[i], P751[i], borrow) 169 | } 170 | 171 | // Sets all bits if borrow = 1 172 | mask = 0 - borrow 173 | borrow = 0 174 | for i := 0; i < FpWords; i++ { 175 | x[i], borrow = bits.Add64(x[i], P751[i]&mask, borrow) 176 | } 177 | } 178 | 179 | // Compute z = x - y, without reducing mod p. 180 | func sulP751(z, x, y *common.FpX2) { 181 | var borrow, mask uint64 182 | for i := 0; i < 2*FpWords; i++ { 183 | z[i], borrow = bits.Sub64(x[i], y[i], borrow) 184 | } 185 | 186 | // Sets all bits if borrow = 1 187 | mask = 0 - borrow 188 | borrow = 0 189 | for i := FpWords; i < 2*FpWords; i++ { 190 | z[i], borrow = bits.Add64(z[i], P751[i-FpWords]&mask, borrow) 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /dh/sidh/internal/p751/arith_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package p751 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/henrydcase/nobs/dh/sidh/common" 10 | ) 11 | 12 | // Package-level storage for this field element is intended to deter 13 | // compiler optimizations. 14 | var ( 15 | benchmarkFp common.Fp 16 | benchmarkFpX2 common.FpX2 17 | bench_x = common.Fp{17026702066521327207, 5108203422050077993, 10225396685796065916, 11153620995215874678, 6531160855165088358, 15302925148404145445, 1248821577836769963, 9789766903037985294, 7493111552032041328, 10838999828319306046, 18103257655515297935, 27403304611634} 18 | bench_y = common.Fp{4227467157325093378, 10699492810770426363, 13500940151395637365, 12966403950118934952, 16517692605450415877, 13647111148905630666, 14223628886152717087, 7167843152346903316, 15855377759596736571, 4300673881383687338, 6635288001920617779, 30486099554235} 19 | bench_z = common.FpX2{1595347748594595712, 10854920567160033970, 16877102267020034574, 12435724995376660096, 3757940912203224231, 8251999420280413600, 3648859773438820227, 17622716832674727914, 11029567000887241528, 11216190007549447055, 17606662790980286987, 4720707159513626555, 12887743598335030915, 14954645239176589309, 14178817688915225254, 1191346797768989683, 12629157932334713723, 6348851952904485603, 16444232588597434895, 7809979927681678066, 14642637672942531613, 3092657597757640067, 10160361564485285723, 240071237} 20 | ) 21 | 22 | func TestFpCswap(t *testing.T) { 23 | var one = common.Fp{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} 24 | var two = common.Fp{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2} 25 | 26 | var x = one 27 | var y = two 28 | 29 | cswapP751(&x, &y, 0) 30 | for i := 0; i < FpWords; i++ { 31 | if (x[i] != one[i]) || (y[i] != two[i]) { 32 | t.Error("Found", x, "expected", two) 33 | } 34 | } 35 | 36 | cswapP751(&x, &y, 1) 37 | for i := 0; i < FpWords; i++ { 38 | if (x[i] != two[i]) || (y[i] != one[i]) { 39 | t.Error("Found", x, "expected", two) 40 | } 41 | } 42 | } 43 | 44 | // Benchmarking for field arithmetic 45 | func BenchmarkMul(b *testing.B) { 46 | for n := 0; n < b.N; n++ { 47 | mulP751(&benchmarkFpX2, &bench_x, &bench_y) 48 | } 49 | } 50 | 51 | func BenchmarkRdc(b *testing.B) { 52 | z := bench_z 53 | 54 | // This benchmark actually computes garbage, because 55 | // rdcP751 mangles its input, but since it's 56 | // constant-time that shouldn't matter for the benchmarks. 57 | for n := 0; n < b.N; n++ { 58 | rdcP751(&benchmarkFp, &z) 59 | } 60 | } 61 | 62 | func BenchmarkAdd(b *testing.B) { 63 | for n := 0; n < b.N; n++ { 64 | addP751(&benchmarkFp, &bench_x, &bench_y) 65 | } 66 | } 67 | 68 | func BenchmarkSub(b *testing.B) { 69 | for n := 0; n < b.N; n++ { 70 | subP751(&benchmarkFp, &bench_x, &bench_y) 71 | } 72 | } 73 | 74 | func BenchmarkCswap(b *testing.B) { 75 | x, y := bench_x, bench_y 76 | for n := 0; n < b.N; n++ { 77 | cswapP751(&x, &y, 1) 78 | cswapP751(&x, &y, 0) 79 | } 80 | } 81 | 82 | func BenchmarkMod(b *testing.B) { 83 | x := bench_x 84 | for n := 0; n < b.N; n++ { 85 | modP751(&x) 86 | } 87 | } 88 | 89 | func BenchmarkX2AddLazy(b *testing.B) { 90 | x, y, z := bench_z, bench_z, bench_z 91 | for n := 0; n < b.N; n++ { 92 | adlP751(&x, &y, &z) 93 | } 94 | } 95 | 96 | func BenchmarkX2SubLazy(b *testing.B) { 97 | x, y, z := bench_z, bench_z, bench_z 98 | for n := 0; n < b.N; n++ { 99 | sulP751(&x, &y, &z) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /dh/sidh/internal/p751/curve_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package p751 5 | 6 | import ( 7 | "bytes" 8 | "testing" 9 | 10 | . "github.com/henrydcase/nobs/dh/sidh/common" 11 | ) 12 | 13 | func vartimeEqProjFp2(lhs, rhs *ProjectivePoint) bool { 14 | var t0, t1 Fp2 15 | mul(&t0, &lhs.X, &rhs.Z) 16 | mul(&t1, &lhs.Z, &rhs.X) 17 | return vartimeEqFp2(&t0, &t1) 18 | } 19 | 20 | func toAffine(point *ProjectivePoint) *Fp2 { 21 | var affineX Fp2 22 | inv(&affineX, &point.Z) 23 | mul(&affineX, &affineX, &point.X) 24 | return &affineX 25 | } 26 | 27 | func Test_jInvariant(t *testing.T) { 28 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 29 | var jbufRes = make([]byte, params.SharedSecretSize) 30 | var jbufExp = make([]byte, params.SharedSecretSize) 31 | var jInv Fp2 32 | 33 | Jinvariant(&curve, &jInv) 34 | FromMontgomery(&jInv, &jInv) 35 | Fp2ToBytes(jbufRes, &jInv, params.Bytelen) 36 | 37 | jInv = expectedJ 38 | FromMontgomery(&jInv, &jInv) 39 | Fp2ToBytes(jbufExp, &jInv, params.Bytelen) 40 | 41 | if !bytes.Equal(jbufRes[:], jbufExp[:]) { 42 | t.Error("Computed incorrect j-invariant: found\n", jbufRes, "\nexpected\n", jbufExp) 43 | } 44 | } 45 | 46 | func TestProjectivePointVartimeEq(t *testing.T) { 47 | var xP ProjectivePoint 48 | 49 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 50 | xQ := xP 51 | 52 | // Scale xQ, which results in the same projective point 53 | mul(&xQ.X, &xQ.X, &curveA) 54 | mul(&xQ.Z, &xQ.Z, &curveA) 55 | if !vartimeEqProjFp2(&xP, &xQ) { 56 | t.Error("Expected the scaled point to be equal to the original") 57 | } 58 | } 59 | 60 | func TestPointMulVersusSage(t *testing.T) { 61 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 62 | var cparams = CalcCurveParamsEquiv4(&curve) 63 | var xP ProjectivePoint 64 | 65 | // x 2 66 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 67 | Pow2k(&xP, &cparams, 1) 68 | afxQ := toAffine(&xP) 69 | if !vartimeEqFp2(afxQ, &affineXP2) { 70 | t.Error("\nExpected\n", affineXP2, "\nfound\n", afxQ) 71 | } 72 | 73 | // x 4 74 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 75 | Pow2k(&xP, &cparams, 2) 76 | afxQ = toAffine(&xP) 77 | if !vartimeEqFp2(afxQ, &affineXP4) { 78 | t.Error("\nExpected\n", affineXP4, "\nfound\n", afxQ) 79 | } 80 | } 81 | 82 | func TestPointMul9VersusSage(t *testing.T) { 83 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 84 | var cparams = CalcCurveParamsEquiv3(&curve) 85 | var xP ProjectivePoint 86 | 87 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 88 | Pow3k(&xP, &cparams, 2) 89 | afxQ := toAffine(&xP) 90 | if !vartimeEqFp2(afxQ, &affineXP9) { 91 | t.Error("\nExpected\n", affineXP9, "\nfound\n", afxQ) 92 | } 93 | } 94 | 95 | func BenchmarkThreePointLadder(b *testing.B) { 96 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 97 | for n := 0; n < b.N; n++ { 98 | ScalarMul3Pt(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], uint(len(scalar3Pt)*8), scalar3Pt[:]) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /dh/sidh/internal/p751/doc.go: -------------------------------------------------------------------------------- 1 | // Package p751 provides implementation of field arithmetic used in SIDH and SIKE. 2 | package p751 3 | -------------------------------------------------------------------------------- /dh/sidh/internal/p751/fp2.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package p751 5 | 6 | import ( 7 | "github.com/henrydcase/nobs/dh/sidh/common" 8 | ) 9 | 10 | // Montgomery multiplication. Input values must be already 11 | // in Montgomery domain. 12 | func mulP(dest, lhs, rhs *common.Fp) { 13 | var ab common.FpX2 14 | mulP751(&ab, lhs, rhs) // = a*b*R*R 15 | rdcP751(dest, &ab) // = a*b*R mod p 16 | } 17 | 18 | // Set dest = x^((p-3)/4). If x is square, this is 1/sqrt(x). 19 | // Uses variation of sliding-window algorithm from with window size 20 | // of 5 and least to most significant bit sliding (left-to-right) 21 | // See HAC 14.85 for general description. 22 | // 23 | // Allowed to overlap x with dest. 24 | // All values in Montgomery domains 25 | // Set dest = x^(2^k), for k >= 1, by repeated squarings. 26 | func p34(dest, x *common.Fp) { 27 | var lookup [16]common.Fp 28 | 29 | // This performs sum(powStrategy) + 1 squarings and len(lookup) + len(mulStrategy) 30 | // multiplications. 31 | powStrategy := []uint8{5, 7, 6, 2, 10, 4, 6, 9, 8, 5, 9, 4, 7, 5, 5, 4, 8, 3, 9, 5, 5, 4, 10, 4, 6, 6, 6, 5, 8, 9, 3, 4, 9, 4, 5, 6, 6, 2, 9, 4, 5, 5, 5, 7, 7, 9, 4, 6, 4, 8, 5, 8, 6, 6, 2, 9, 7, 4, 8, 8, 8, 4, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2} 32 | mulStrategy := []uint8{15, 11, 10, 0, 15, 3, 3, 3, 4, 4, 9, 7, 11, 11, 5, 3, 12, 2, 10, 8, 5, 2, 8, 3, 5, 4, 11, 4, 0, 9, 2, 1, 12, 7, 5, 14, 15, 0, 14, 5, 6, 4, 5, 13, 6, 9, 7, 15, 1, 14, 11, 15, 12, 5, 0, 10, 9, 7, 7, 10, 14, 6, 11, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 1} 33 | initialMul := uint8(13) 34 | 35 | // Precompute lookup table of odd multiples of x for window 36 | // size k=5. 37 | var xx common.Fp 38 | mulP(&xx, x, x) 39 | lookup[0] = *x 40 | for i := 1; i < 16; i++ { 41 | mulP(&lookup[i], &lookup[i-1], &xx) 42 | } 43 | 44 | // Now lookup = {x, x^3, x^5, ... } 45 | // so that lookup[i] = x^{2*i + 1} 46 | // so that lookup[k/2] = x^k, for odd k 47 | *dest = lookup[initialMul] 48 | for i := uint8(0); i < uint8(len(powStrategy)); i++ { 49 | mulP(dest, dest, dest) 50 | for j := uint8(1); j < powStrategy[i]; j++ { 51 | mulP(dest, dest, dest) 52 | } 53 | mulP(dest, dest, &lookup[mulStrategy[i]]) 54 | } 55 | } 56 | 57 | func add(dest, lhs, rhs *common.Fp2) { 58 | addP751(&dest.A, &lhs.A, &rhs.A) 59 | addP751(&dest.B, &lhs.B, &rhs.B) 60 | } 61 | 62 | func sub(dest, lhs, rhs *common.Fp2) { 63 | subP751(&dest.A, &lhs.A, &rhs.A) 64 | subP751(&dest.B, &lhs.B, &rhs.B) 65 | } 66 | 67 | func mul(dest, lhs, rhs *common.Fp2) { 68 | var bMinA, cMinD common.Fp 69 | var ac, bd common.FpX2 70 | var adPlusBc common.FpX2 71 | var acMinBd common.FpX2 72 | 73 | // Let (a,b,c,d) = (lhs.a,lhs.b,rhs.a,rhs.b). 74 | // 75 | // (a + bi)*(c + di) = (a*c - b*d) + (a*d + b*c)i 76 | // 77 | // Use Karatsuba's trick: note that 78 | // 79 | // (b - a)*(c - d) = (b*c + a*d) - a*c - b*d 80 | // 81 | // so (a*d + b*c) = (b-a)*(c-d) + a*c + b*d. 82 | mulP751(&ac, &lhs.A, &rhs.A) // = a*c*R*R 83 | mulP751(&bd, &lhs.B, &rhs.B) // = b*d*R*R 84 | subP751(&bMinA, &lhs.B, &lhs.A) // = (b-a)*R 85 | subP751(&cMinD, &rhs.A, &rhs.B) // = (c-d)*R 86 | mulP751(&adPlusBc, &bMinA, &cMinD) // = (b-a)*(c-d)*R*R 87 | adlP751(&adPlusBc, &adPlusBc, &ac) // = ((b-a)*(c-d) + a*c)*R*R 88 | adlP751(&adPlusBc, &adPlusBc, &bd) // = ((b-a)*(c-d) + a*c + b*d)*R*R 89 | rdcP751(&dest.B, &adPlusBc) // = (a*d + b*c)*R mod p 90 | sulP751(&acMinBd, &ac, &bd) // = (a*c - b*d)*R*R 91 | rdcP751(&dest.A, &acMinBd) // = (a*c - b*d)*R mod p 92 | } 93 | 94 | // Set dest = 1/x 95 | // 96 | // Allowed to overlap dest with x. 97 | // 98 | // Returns dest to allow chaining operations. 99 | func inv(dest, x *common.Fp2) { 100 | var e1, e2 common.FpX2 101 | var f1, f2 common.Fp 102 | 103 | // We want to compute 104 | // 105 | // 1 1 (a - bi) (a - bi) 106 | // -------- = -------- -------- = ----------- 107 | // (a + bi) (a + bi) (a - bi) (a^2 + b^2) 108 | // 109 | // Letting c = 1/(a^2 + b^2), this is 110 | // 111 | // 1/(a+bi) = a*c - b*ci. 112 | 113 | mulP751(&e1, &x.A, &x.A) // = a*a*R*R 114 | mulP751(&e2, &x.B, &x.B) // = b*b*R*R 115 | adlP751(&e1, &e1, &e2) // = (a^2 + b^2)*R*R 116 | rdcP751(&f1, &e1) // = (a^2 + b^2)*R mod p 117 | // Now f1 = a^2 + b^2 118 | 119 | mulP(&f2, &f1, &f1) 120 | p34(&f2, &f2) 121 | mulP(&f2, &f2, &f2) 122 | mulP(&f2, &f2, &f1) 123 | 124 | mulP751(&e1, &x.A, &f2) 125 | rdcP751(&dest.A, &e1) 126 | 127 | subP751(&f1, &common.Fp{}, &x.B) 128 | mulP751(&e1, &f1, &f2) 129 | rdcP751(&dest.B, &e1) 130 | } 131 | 132 | func sqr(dest, x *common.Fp2) { 133 | var a2, aPlusB, aMinusB common.Fp 134 | var a2MinB2, ab2 common.FpX2 135 | 136 | a := &x.A 137 | b := &x.B 138 | 139 | // (a + bi)*(a + bi) = (a^2 - b^2) + 2abi. 140 | addP751(&a2, a, a) // = a*R + a*R = 2*a*R 141 | addP751(&aPlusB, a, b) // = a*R + b*R = (a+b)*R 142 | subP751(&aMinusB, a, b) // = a*R - b*R = (a-b)*R 143 | mulP751(&a2MinB2, &aPlusB, &aMinusB) // = (a+b)*(a-b)*R*R = (a^2 - b^2)*R*R 144 | mulP751(&ab2, &a2, b) // = 2*a*b*R*R 145 | rdcP751(&dest.A, &a2MinB2) // = (a^2 - b^2)*R mod p 146 | rdcP751(&dest.B, &ab2) // = 2*a*b*R mod p 147 | } 148 | 149 | // In case choice == 1, performs following swap in constant time: 150 | // xPx <-> xQx 151 | // xPz <-> xQz 152 | // Otherwise returns xPx, xPz, xQx, xQz unchanged 153 | func cswap(xPx, xPz, xQx, xQz *common.Fp2, choice uint8) { 154 | cswapP751(&xPx.A, &xQx.A, choice) 155 | cswapP751(&xPx.B, &xQx.B, choice) 156 | cswapP751(&xPz.A, &xQz.A, choice) 157 | cswapP751(&xPz.B, &xQz.B, choice) 158 | } 159 | 160 | // Converts in.A and in.B to Montgomery domain and stores 161 | // in 'out' 162 | // out.A = in.A * R mod p 163 | // out.B = in.B * R mod p 164 | // Performs v = v*R^2*R^(-1) mod p, for both in.A and in.B 165 | func ToMontgomery(out, in *common.Fp2) { 166 | var aRR common.FpX2 167 | 168 | // a*R*R 169 | mulP751(&aRR, &in.A, &P751R2) 170 | // a*R mod p 171 | rdcP751(&out.A, &aRR) 172 | mulP751(&aRR, &in.B, &P751R2) 173 | rdcP751(&out.B, &aRR) 174 | } 175 | 176 | // Converts in.A and in.B from Montgomery domain and stores 177 | // in 'out' 178 | // out.A = in.A mod p 179 | // out.B = in.B mod p 180 | // 181 | // After returning from the call 'in' is not modified. 182 | func FromMontgomery(out, in *common.Fp2) { 183 | var aR common.FpX2 184 | 185 | // convert from montgomery domain 186 | copy(aR[:], in.A[:]) 187 | rdcP751(&out.A, &aR) // = a mod p in [0, 2p) 188 | modP751(&out.A) // = a mod p in [0, p) 189 | for i := range aR { 190 | aR[i] = 0 191 | } 192 | copy(aR[:], in.B[:]) 193 | rdcP751(&out.B, &aR) 194 | modP751(&out.B) 195 | } 196 | -------------------------------------------------------------------------------- /dh/sidh/internal/p751/fp2_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package p751 5 | 6 | import ( 7 | "math/rand" 8 | "reflect" 9 | "testing" 10 | "testing/quick" 11 | 12 | "github.com/henrydcase/nobs/dh/sidh/common" 13 | ) 14 | 15 | type testParams struct { 16 | Point common.ProjectivePoint 17 | Cparam common.ProjectiveCurveParameters 18 | ExtElem common.Fp2 19 | } 20 | 21 | // Returns true if lhs = rhs. Takes variable time. 22 | func vartimeEqFp2(lhs, rhs *common.Fp2) bool { 23 | a := *lhs 24 | b := *rhs 25 | 26 | modP751(&a.A) 27 | modP751(&a.B) 28 | modP751(&b.A) 29 | modP751(&b.B) 30 | 31 | eq := true 32 | for i := 0; i < FpWords && eq; i++ { 33 | eq = eq && (a.A[i] == b.A[i]) 34 | eq = eq && (a.B[i] == b.B[i]) 35 | } 36 | return eq 37 | } 38 | 39 | func (testParams) generateFp2(rand *rand.Rand) common.Fp2 { 40 | // Generation strategy: low limbs taken from [0,2^64); high limb 41 | // taken from smaller range 42 | // 43 | // Size hint is ignored since all elements are fixed size. 44 | // 45 | // Field elements taken in range [0,2p). Emulate this by capping 46 | // the high limb by the top digit of 2*p-1: 47 | // 48 | // sage: (2*p-1).digits(2^64)[-1] 49 | // 50 | // This still allows generating values >= 2p, but hopefully that 51 | // excess is OK (and if it's not, we'll find out, because it's for 52 | // testing...) 53 | highLimb := rand.Uint64() % P751x2[FpWords-1] 54 | fpElementGen := func() (fp common.Fp) { 55 | for i := 0; i < (FpWords - 1); i++ { 56 | fp[i] = rand.Uint64() 57 | } 58 | fp[FpWords-1] = highLimb 59 | return fp 60 | } 61 | return common.Fp2{A: fpElementGen(), B: fpElementGen()} 62 | } 63 | 64 | func (c testParams) Generate(rand *rand.Rand, size int) reflect.Value { 65 | return reflect.ValueOf( 66 | testParams{ 67 | common.ProjectivePoint{ 68 | X: c.generateFp2(rand), 69 | Z: c.generateFp2(rand), 70 | }, 71 | common.ProjectiveCurveParameters{ 72 | A: c.generateFp2(rand), 73 | C: c.generateFp2(rand), 74 | }, 75 | c.generateFp2(rand), 76 | }) 77 | } 78 | 79 | func TestOne(t *testing.T) { 80 | var tmp common.Fp2 81 | 82 | mul(&tmp, ¶ms.OneFp2, ¶ms.A.AffineP) 83 | if !vartimeEqFp2(&tmp, ¶ms.A.AffineP) { 84 | t.Error("Not equal 1") 85 | } 86 | } 87 | 88 | func TestFp2ToBytesRoundTrip(t *testing.T) { 89 | roundTrips := func(x testParams) bool { 90 | var xBytes = make([]byte, 2*params.Bytelen) 91 | var xPrime common.Fp2 92 | 93 | common.Fp2ToBytes(xBytes[:], &x.ExtElem, params.Bytelen) 94 | common.BytesToFp2(&xPrime, xBytes[:], params.Bytelen) 95 | return vartimeEqFp2(&xPrime, &x.ExtElem) 96 | } 97 | 98 | if err := quick.Check(roundTrips, quickCheckConfig); err != nil { 99 | t.Error(err) 100 | } 101 | } 102 | 103 | func TestFp2MulDistributesOverAdd(t *testing.T) { 104 | mulDistributesOverAdd := func(x, y, z testParams) bool { 105 | // Compute t1 = (x+y)*z 106 | t1 := new(common.Fp2) 107 | add(t1, &x.ExtElem, &y.ExtElem) 108 | mul(t1, t1, &z.ExtElem) 109 | 110 | // Compute t2 = x*z + y*z 111 | t2 := new(common.Fp2) 112 | t3 := new(common.Fp2) 113 | mul(t2, &x.ExtElem, &z.ExtElem) 114 | mul(t3, &y.ExtElem, &z.ExtElem) 115 | add(t2, t2, t3) 116 | 117 | return vartimeEqFp2(t1, t2) 118 | } 119 | 120 | if err := quick.Check(mulDistributesOverAdd, quickCheckConfig); err != nil { 121 | t.Error(err) 122 | } 123 | } 124 | 125 | func TestFp2MulIsAssociative(t *testing.T) { 126 | isAssociative := func(x, y, z testParams) bool { 127 | // Compute t1 = (x*y)*z 128 | t1 := new(common.Fp2) 129 | mul(t1, &x.ExtElem, &y.ExtElem) 130 | mul(t1, t1, &z.ExtElem) 131 | 132 | // Compute t2 = (y*z)*x 133 | t2 := new(common.Fp2) 134 | mul(t2, &y.ExtElem, &z.ExtElem) 135 | mul(t2, t2, &x.ExtElem) 136 | 137 | return vartimeEqFp2(t1, t2) 138 | } 139 | 140 | if err := quick.Check(isAssociative, quickCheckConfig); err != nil { 141 | t.Error(err) 142 | } 143 | } 144 | 145 | func TestFp2SquareMatchesMul(t *testing.T) { 146 | sqrMatchesMul := func(x testParams) bool { 147 | // Compute t1 = (x*x) 148 | t1 := new(common.Fp2) 149 | mul(t1, &x.ExtElem, &x.ExtElem) 150 | 151 | // Compute t2 = x^2 152 | t2 := new(common.Fp2) 153 | sqr(t2, &x.ExtElem) 154 | 155 | return vartimeEqFp2(t1, t2) 156 | } 157 | 158 | if err := quick.Check(sqrMatchesMul, quickCheckConfig); err != nil { 159 | t.Error(err) 160 | } 161 | } 162 | 163 | func TestFp2Inv(t *testing.T) { 164 | inverseIsCorrect := func(x testParams) bool { 165 | z := new(common.Fp2) 166 | inv(z, &x.ExtElem) 167 | 168 | // Now z = (1/x), so (z * x) * x == x 169 | mul(z, z, &x.ExtElem) 170 | mul(z, z, &x.ExtElem) 171 | 172 | return vartimeEqFp2(z, &x.ExtElem) 173 | } 174 | 175 | // This is more expensive; run fewer tests 176 | var quickCheckConfig = &quick.Config{MaxCount: (1 << 11)} 177 | if err := quick.Check(inverseIsCorrect, quickCheckConfig); err != nil { 178 | t.Error(err) 179 | } 180 | } 181 | 182 | func TestFp2Batch3Inv(t *testing.T) { 183 | batchInverseIsCorrect := func(x1, x2, x3 testParams) bool { 184 | var x1Inv, x2Inv, x3Inv common.Fp2 185 | inv(&x1Inv, &x1.ExtElem) 186 | inv(&x2Inv, &x2.ExtElem) 187 | inv(&x3Inv, &x3.ExtElem) 188 | 189 | var y1, y2, y3 common.Fp2 190 | Fp2Batch3Inv(&x1.ExtElem, &x2.ExtElem, &x3.ExtElem, &y1, &y2, &y3) 191 | 192 | return (vartimeEqFp2(&x1Inv, &y1) && vartimeEqFp2(&x2Inv, &y2) && vartimeEqFp2(&x3Inv, &y3)) 193 | } 194 | 195 | // This is more expensive; run fewer tests 196 | var quickCheckConfig = &quick.Config{MaxCount: (1 << 8)} 197 | if err := quick.Check(batchInverseIsCorrect, quickCheckConfig); err != nil { 198 | t.Error(err) 199 | } 200 | } 201 | 202 | func BenchmarkFp2Mul(b *testing.B) { 203 | z := &common.Fp2{A: bench_x, B: bench_y} 204 | w := new(common.Fp2) 205 | 206 | for n := 0; n < b.N; n++ { 207 | mul(w, z, z) 208 | } 209 | } 210 | 211 | func BenchmarkFp2Inv(b *testing.B) { 212 | z := &common.Fp2{A: bench_x, B: bench_y} 213 | w := new(common.Fp2) 214 | 215 | for n := 0; n < b.N; n++ { 216 | inv(w, z) 217 | } 218 | } 219 | 220 | func BenchmarkFp2Square(b *testing.B) { 221 | z := &common.Fp2{A: bench_x, B: bench_y} 222 | w := new(common.Fp2) 223 | 224 | for n := 0; n < b.N; n++ { 225 | sqr(w, z) 226 | } 227 | } 228 | 229 | func BenchmarkFp2Add(b *testing.B) { 230 | z := &common.Fp2{A: bench_x, B: bench_y} 231 | w := new(common.Fp2) 232 | 233 | for n := 0; n < b.N; n++ { 234 | add(w, z, z) 235 | } 236 | } 237 | 238 | func BenchmarkFp2Sub(b *testing.B) { 239 | z := &common.Fp2{A: bench_x, B: bench_y} 240 | w := new(common.Fp2) 241 | 242 | for n := 0; n < b.N; n++ { 243 | sub(w, z, z) 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /dh/sidh/internal/templates/arith_decl.gotemp: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | // +build amd64,!noasm{{if .OPT_ARM}} arm64,!noasm{{end}} 5 | 6 | package {{ .PACKAGE}} 7 | 8 | import ( 9 | . "github.com/henrydcase/nobs/dh/sidh/common" 10 | ) 11 | 12 | // If choice = 0, leave x,y unchanged. If choice = 1, set x,y = y,x. 13 | // If choice is neither 0 nor 1 then behaviour is undefined. 14 | // This function executes in constant time. 15 | //go:noescape 16 | func cswap{{ .FIELD}}(x, y *Fp, choice uint8) 17 | 18 | // Compute z = x + y (mod p). 19 | //go:noescape 20 | func add{{ .FIELD}}(z, x, y *Fp) 21 | 22 | // Compute z = x - y (mod p). 23 | //go:noescape 24 | func sub{{ .FIELD}}(z, x, y *Fp) 25 | 26 | // Compute z = x + y, without reducing mod p. 27 | //go:noescape 28 | func adl{{ .FIELD}}(z, x, y *FpX2) 29 | 30 | // Compute z = x - y, without reducing mod p. 31 | //go:noescape 32 | func sul{{ .FIELD}}(z, x, y *FpX2) 33 | 34 | // Reduce a field element in [0, 2*p) to one in [0,p). 35 | //go:noescape 36 | func mod{{ .FIELD}}(x *Fp) 37 | 38 | // Computes z = x * y. 39 | //go:noescape 40 | func mul{{ .FIELD}}(z *FpX2, x, y *Fp) 41 | 42 | // Computes the Montgomery reduction z = x R^{-1} (mod 2*p). On return value 43 | // of x may be changed. z=x not allowed. 44 | //go:noescape 45 | func rdc{{ .FIELD}}(z *Fp, x *FpX2) 46 | -------------------------------------------------------------------------------- /dh/sidh/internal/templates/arith_generic.gotemp: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | // +build {{if .OPT_ARM}}noasm !amd64,!arm64{{else}}noasm,arm64 !amd64{{end}} 5 | 6 | package {{ .PACKAGE}} 7 | 8 | import ( 9 | "math/bits" 10 | 11 | "github.com/henrydcase/nobs/dh/sidh/common" 12 | ) 13 | 14 | // Compute z = x + y (mod p). 15 | func add{{ .FIELD }}(z, x, y *common.Fp) { 16 | var carry uint64 17 | 18 | // z=x+y % {{ .FIELD }} 19 | for i := 0; i < FpWords; i++ { 20 | z[i], carry = bits.Add64(x[i], y[i], carry) 21 | } 22 | 23 | // z = z - {{ .FIELD}}x2 24 | carry = 0 25 | for i := 0; i < FpWords; i++ { 26 | z[i], carry = bits.Sub64(z[i], {{ .FIELD}}x2[i], carry) 27 | } 28 | 29 | // if z<0 add {{ .FIELD}}x2 back 30 | mask := uint64(0 - carry) 31 | carry = 0 32 | for i := 0; i < FpWords; i++ { 33 | z[i], carry = bits.Add64(z[i], {{ .FIELD}}x2[i]&mask, carry) 34 | } 35 | } 36 | 37 | // Compute z = x - y (mod p). 38 | func sub{{ .FIELD }}(z, x, y *common.Fp) { 39 | var borrow uint64 40 | 41 | for i := 0; i < FpWords; i++ { 42 | z[i], borrow = bits.Sub64(x[i], y[i], borrow) 43 | } 44 | 45 | mask := uint64(0 - borrow) 46 | borrow = 0 47 | 48 | for i := 0; i < FpWords; i++ { 49 | z[i], borrow = bits.Add64(z[i], {{ .FIELD}}x2[i]&mask, borrow) 50 | } 51 | } 52 | 53 | // Conditionally swaps bits in x and y in constant time. 54 | // mask indicates bits to be swapped (set bits are swapped) 55 | // For details see "Hackers Delight, 2.20" 56 | // 57 | // Implementation doesn't actually depend on a prime field. 58 | func cswap{{ .FIELD }}(x, y *common.Fp, mask uint8) { 59 | var tmp, mask64 uint64 60 | 61 | mask64 = 0 - uint64(mask) 62 | for i := 0; i < FpWords; i++ { 63 | tmp = mask64 & (x[i] ^ y[i]) 64 | x[i] = tmp ^ x[i] 65 | y[i] = tmp ^ y[i] 66 | } 67 | } 68 | 69 | // Perform Montgomery reduction: set z = x R^{-1} (mod 2*p) 70 | // with R=2^(FpWords*64). Destroys the input value. 71 | func rdc{{ .FIELD }}(z *common.Fp, x *common.FpX2) { 72 | var carry, t, u, v uint64 73 | var hi, lo uint64 74 | var count int 75 | 76 | count = {{ .FIELD}}p1Zeros 77 | 78 | for i := 0; i < FpWords; i++ { 79 | for j := 0; j < i; j++ { 80 | if j < (i - count + 1) { 81 | hi, lo = bits.Mul64(z[j], {{ .FIELD }}p1[i-j]) 82 | v, carry = bits.Add64(lo, v, 0) 83 | u, carry = bits.Add64(hi, u, carry) 84 | t += carry 85 | } 86 | } 87 | v, carry = bits.Add64(v, x[i], 0) 88 | u, carry = bits.Add64(u, 0, carry) 89 | t += carry 90 | 91 | z[i] = v 92 | v = u 93 | u = t 94 | t = 0 95 | } 96 | 97 | for i := FpWords; i < 2*FpWords-1; i++ { 98 | if count > 0 { 99 | count-- 100 | } 101 | for j := i - FpWords + 1; j < FpWords; j++ { 102 | if j < (FpWords - count) { 103 | hi, lo = bits.Mul64(z[j], {{ .FIELD }}p1[i-j]) 104 | v, carry = bits.Add64(lo, v, 0) 105 | u, carry = bits.Add64(hi, u, carry) 106 | t += carry 107 | } 108 | } 109 | v, carry = bits.Add64(v, x[i], 0) 110 | u, carry = bits.Add64(u, 0, carry) 111 | 112 | t += carry 113 | z[i-FpWords] = v 114 | v = u 115 | u = t 116 | t = 0 117 | } 118 | v, _ = bits.Add64(v, x[2*FpWords-1], 0) 119 | z[FpWords-1] = v 120 | } 121 | 122 | // Compute z = x * y. 123 | func mul{{ .FIELD }}(z *common.FpX2, x, y *common.Fp) { 124 | var u, v, t uint64 125 | var hi, lo uint64 126 | var carry uint64 127 | 128 | for i := uint64(0); i < FpWords; i++ { 129 | for j := uint64(0); j <= i; j++ { 130 | hi, lo = bits.Mul64(x[j], y[i-j]) 131 | v, carry = bits.Add64(lo, v, 0) 132 | u, carry = bits.Add64(hi, u, carry) 133 | t += carry 134 | } 135 | z[i] = v 136 | v = u 137 | u = t 138 | t = 0 139 | } 140 | 141 | for i := FpWords; i < (2*FpWords)-1; i++ { 142 | for j := i - FpWords + 1; j < FpWords; j++ { 143 | hi, lo = bits.Mul64(x[j], y[i-j]) 144 | v, carry = bits.Add64(lo, v, 0) 145 | u, carry = bits.Add64(hi, u, carry) 146 | t += carry 147 | } 148 | z[i] = v 149 | v = u 150 | u = t 151 | t = 0 152 | } 153 | z[2*FpWords-1] = v 154 | } 155 | 156 | // Compute z = x + y, without reducing mod p. 157 | func adl{{ .FIELD }}(z, x, y *common.FpX2) { 158 | var carry uint64 159 | for i := 0; i < 2*FpWords; i++ { 160 | z[i], carry = bits.Add64(x[i], y[i], carry) 161 | } 162 | } 163 | 164 | // Reduce a field element in [0, 2*p) to one in [0,p). 165 | func mod{{ .FIELD }}(x *common.Fp) { 166 | var borrow, mask uint64 167 | for i := 0; i < FpWords; i++ { 168 | x[i], borrow = bits.Sub64(x[i], {{ .FIELD }}[i], borrow) 169 | } 170 | 171 | // Sets all bits if borrow = 1 172 | mask = 0 - borrow 173 | borrow = 0 174 | for i := 0; i < FpWords; i++ { 175 | x[i], borrow = bits.Add64(x[i], {{ .FIELD }}[i]&mask, borrow) 176 | } 177 | } 178 | 179 | // Compute z = x - y, without reducing mod p. 180 | func sul{{ .FIELD }}(z, x, y *common.FpX2) { 181 | var borrow, mask uint64 182 | for i := 0; i < 2*FpWords; i++ { 183 | z[i], borrow = bits.Sub64(x[i], y[i], borrow) 184 | } 185 | 186 | // Sets all bits if borrow = 1 187 | mask = 0 - borrow 188 | borrow = 0 189 | for i := FpWords; i < 2*FpWords; i++ { 190 | z[i], borrow = bits.Add64(z[i], {{ .FIELD }}[i-FpWords]&mask, borrow) 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /dh/sidh/internal/templates/arith_test.gotemp: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package {{ .PACKAGE}} 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/henrydcase/nobs/dh/sidh/common" 10 | ) 11 | 12 | // Package-level storage for this field element is intended to deter 13 | // compiler optimizations. 14 | var ( 15 | benchmarkFp common.Fp 16 | benchmarkFpX2 common.FpX2 17 | bench_x = common.Fp{17026702066521327207, 5108203422050077993, 10225396685796065916, 11153620995215874678, 6531160855165088358, 15302925148404145445, 1248821577836769963, 9789766903037985294, 7493111552032041328, 10838999828319306046, 18103257655515297935, 27403304611634} 18 | bench_y = common.Fp{4227467157325093378, 10699492810770426363, 13500940151395637365, 12966403950118934952, 16517692605450415877, 13647111148905630666, 14223628886152717087, 7167843152346903316, 15855377759596736571, 4300673881383687338, 6635288001920617779, 30486099554235} 19 | bench_z = common.FpX2{1595347748594595712, 10854920567160033970, 16877102267020034574, 12435724995376660096, 3757940912203224231, 8251999420280413600, 3648859773438820227, 17622716832674727914, 11029567000887241528, 11216190007549447055, 17606662790980286987, 4720707159513626555, 12887743598335030915, 14954645239176589309, 14178817688915225254, 1191346797768989683, 12629157932334713723, 6348851952904485603, 16444232588597434895, 7809979927681678066, 14642637672942531613, 3092657597757640067, 10160361564485285723, 240071237} 20 | ) 21 | 22 | func TestFpCswap(t *testing.T) { 23 | var one = common.Fp{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} 24 | var two = common.Fp{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2} 25 | 26 | var x = one 27 | var y = two 28 | 29 | cswap{{ .FIELD}}(&x, &y, 0) 30 | for i := 0; i < FpWords; i++ { 31 | if (x[i] != one[i]) || (y[i] != two[i]) { 32 | t.Error("Found", x, "expected", two) 33 | } 34 | } 35 | 36 | cswap{{ .FIELD}}(&x, &y, 1) 37 | for i := 0; i < FpWords; i++ { 38 | if (x[i] != two[i]) || (y[i] != one[i]) { 39 | t.Error("Found", x, "expected", two) 40 | } 41 | } 42 | } 43 | 44 | // Benchmarking for field arithmetic 45 | func BenchmarkMul(b *testing.B) { 46 | for n := 0; n < b.N; n++ { 47 | mul{{ .FIELD}}(&benchmarkFpX2, &bench_x, &bench_y) 48 | } 49 | } 50 | 51 | func BenchmarkRdc(b *testing.B) { 52 | z := bench_z 53 | 54 | // This benchmark actually computes garbage, because 55 | // rdc{{ .FIELD}} mangles its input, but since it's 56 | // constant-time that shouldn't matter for the benchmarks. 57 | for n := 0; n < b.N; n++ { 58 | rdc{{ .FIELD}}(&benchmarkFp, &z) 59 | } 60 | } 61 | 62 | func BenchmarkAdd(b *testing.B) { 63 | for n := 0; n < b.N; n++ { 64 | add{{ .FIELD}}(&benchmarkFp, &bench_x, &bench_y) 65 | } 66 | } 67 | 68 | func BenchmarkSub(b *testing.B) { 69 | for n := 0; n < b.N; n++ { 70 | sub{{ .FIELD}}(&benchmarkFp, &bench_x, &bench_y) 71 | } 72 | } 73 | 74 | func BenchmarkCswap(b *testing.B) { 75 | x, y := bench_x, bench_y 76 | for n := 0; n < b.N; n++ { 77 | cswap{{ .FIELD}}(&x, &y, 1) 78 | cswap{{ .FIELD}}(&x, &y, 0) 79 | } 80 | } 81 | 82 | func BenchmarkMod(b *testing.B) { 83 | x := bench_x 84 | for n := 0; n < b.N; n++ { 85 | mod{{ .FIELD}}(&x) 86 | } 87 | } 88 | 89 | func BenchmarkX2AddLazy(b *testing.B) { 90 | x, y, z := bench_z, bench_z, bench_z 91 | for n := 0; n < b.N; n++ { 92 | adl{{ .FIELD}}(&x, &y, &z) 93 | } 94 | } 95 | 96 | func BenchmarkX2SubLazy(b *testing.B) { 97 | x, y, z := bench_z, bench_z, bench_z 98 | for n := 0; n < b.N; n++ { 99 | sul{{ .FIELD}}(&x, &y, &z) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /dh/sidh/internal/templates/curve_test.gotemp: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package {{ .PACKAGE}} 5 | 6 | import ( 7 | "bytes" 8 | "testing" 9 | 10 | . "github.com/henrydcase/nobs/dh/sidh/common" 11 | ) 12 | 13 | func vartimeEqProjFp2(lhs, rhs *ProjectivePoint) bool { 14 | var t0, t1 Fp2 15 | mul(&t0, &lhs.X, &rhs.Z) 16 | mul(&t1, &lhs.Z, &rhs.X) 17 | return vartimeEqFp2(&t0, &t1) 18 | } 19 | 20 | func toAffine(point *ProjectivePoint) *Fp2 { 21 | var affineX Fp2 22 | inv(&affineX, &point.Z) 23 | mul(&affineX, &affineX, &point.X) 24 | return &affineX 25 | } 26 | 27 | func Test_jInvariant(t *testing.T) { 28 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 29 | var jbufRes = make([]byte, params.SharedSecretSize) 30 | var jbufExp = make([]byte, params.SharedSecretSize) 31 | var jInv Fp2 32 | 33 | Jinvariant(&curve, &jInv) 34 | FromMontgomery(&jInv, &jInv) 35 | Fp2ToBytes(jbufRes, &jInv, params.Bytelen) 36 | 37 | jInv = expectedJ 38 | FromMontgomery(&jInv, &jInv) 39 | Fp2ToBytes(jbufExp, &jInv, params.Bytelen) 40 | 41 | if !bytes.Equal(jbufRes[:], jbufExp[:]) { 42 | t.Error("Computed incorrect j-invariant: found\n", jbufRes, "\nexpected\n", jbufExp) 43 | } 44 | } 45 | 46 | func TestProjectivePointVartimeEq(t *testing.T) { 47 | var xP ProjectivePoint 48 | 49 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 50 | xQ := xP 51 | 52 | // Scale xQ, which results in the same projective point 53 | mul(&xQ.X, &xQ.X, &curveA) 54 | mul(&xQ.Z, &xQ.Z, &curveA) 55 | if !vartimeEqProjFp2(&xP, &xQ) { 56 | t.Error("Expected the scaled point to be equal to the original") 57 | } 58 | } 59 | 60 | func TestPointMulVersusSage(t *testing.T) { 61 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 62 | var cparams = CalcCurveParamsEquiv4(&curve) 63 | var xP ProjectivePoint 64 | 65 | // x 2 66 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 67 | Pow2k(&xP, &cparams, 1) 68 | afxQ := toAffine(&xP) 69 | if !vartimeEqFp2(afxQ, &affineXP2) { 70 | t.Error("\nExpected\n", affineXP2, "\nfound\n", afxQ) 71 | } 72 | 73 | // x 4 74 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 75 | Pow2k(&xP, &cparams, 2) 76 | afxQ = toAffine(&xP) 77 | if !vartimeEqFp2(afxQ, &affineXP4) { 78 | t.Error("\nExpected\n", affineXP4, "\nfound\n", afxQ) 79 | } 80 | } 81 | 82 | func TestPointMul9VersusSage(t *testing.T) { 83 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 84 | var cparams = CalcCurveParamsEquiv3(&curve) 85 | var xP ProjectivePoint 86 | 87 | xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} 88 | Pow3k(&xP, &cparams, 2) 89 | afxQ := toAffine(&xP) 90 | if !vartimeEqFp2(afxQ, &affineXP9) { 91 | t.Error("\nExpected\n", affineXP9, "\nfound\n", afxQ) 92 | } 93 | } 94 | 95 | func BenchmarkThreePointLadder(b *testing.B) { 96 | var curve = ProjectiveCurveParameters{A: curveA, C: curveC} 97 | for n := 0; n < b.N; n++ { 98 | ScalarMul3Pt(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], uint(len(scalar3Pt)*8), scalar3Pt[:]) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /dh/sidh/internal/templates/fp2.gotemp: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package {{ .PACKAGE}} 5 | 6 | import ( 7 | "github.com/henrydcase/nobs/dh/sidh/common" 8 | ) 9 | 10 | // Montgomery multiplication. Input values must be already 11 | // in Montgomery domain. 12 | func mulP(dest, lhs, rhs *common.Fp) { 13 | var ab common.FpX2 14 | mul{{ .FIELD}}(&ab, lhs, rhs) // = a*b*R*R 15 | rdc{{ .FIELD}}(dest, &ab) // = a*b*R mod p 16 | } 17 | 18 | // Set dest = x^((p-3)/4). If x is square, this is 1/sqrt(x). 19 | // Uses variation of sliding-window algorithm from with window size 20 | // of 5 and least to most significant bit sliding (left-to-right) 21 | // See HAC 14.85 for general description. 22 | // 23 | // Allowed to overlap x with dest. 24 | // All values in Montgomery domains 25 | // Set dest = x^(2^k), for k >= 1, by repeated squarings. 26 | func p34(dest, x *common.Fp) { 27 | var lookup [16]common.Fp 28 | 29 | // This performs sum(powStrategy) + 1 squarings and len(lookup) + len(mulStrategy) 30 | // multiplications. 31 | powStrategy := {{ .P34_POW_STRATEGY}} 32 | mulStrategy := {{ .P34_MUL_STRATEGY}} 33 | initialMul := uint8({{ .P34_INITIAL_MUL}}) 34 | 35 | // Precompute lookup table of odd multiples of x for window 36 | // size k=5. 37 | var xx common.Fp 38 | mulP(&xx, x, x) 39 | lookup[0] = *x 40 | for i := 1; i < 16; i++ { 41 | mulP(&lookup[i], &lookup[i-1], &xx) 42 | } 43 | 44 | // Now lookup = {x, x^3, x^5, ... } 45 | // so that lookup[i] = x^{2*i + 1} 46 | // so that lookup[k/2] = x^k, for odd k 47 | *dest = lookup[initialMul] 48 | for i := uint8(0); i < uint8(len(powStrategy)); i++ { 49 | mulP(dest, dest, dest) 50 | for j := uint8(1); j < powStrategy[i]; j++ { 51 | mulP(dest, dest, dest) 52 | } 53 | mulP(dest, dest, &lookup[mulStrategy[i]]) 54 | } 55 | } 56 | 57 | func add(dest, lhs, rhs *common.Fp2) { 58 | add{{ .FIELD}}(&dest.A, &lhs.A, &rhs.A) 59 | add{{ .FIELD}}(&dest.B, &lhs.B, &rhs.B) 60 | } 61 | 62 | func sub(dest, lhs, rhs *common.Fp2) { 63 | sub{{ .FIELD}}(&dest.A, &lhs.A, &rhs.A) 64 | sub{{ .FIELD}}(&dest.B, &lhs.B, &rhs.B) 65 | } 66 | 67 | func mul(dest, lhs, rhs *common.Fp2) { 68 | var bMinA, cMinD common.Fp 69 | var ac, bd common.FpX2 70 | var adPlusBc common.FpX2 71 | var acMinBd common.FpX2 72 | 73 | // Let (a,b,c,d) = (lhs.a,lhs.b,rhs.a,rhs.b). 74 | // 75 | // (a + bi)*(c + di) = (a*c - b*d) + (a*d + b*c)i 76 | // 77 | // Use Karatsuba's trick: note that 78 | // 79 | // (b - a)*(c - d) = (b*c + a*d) - a*c - b*d 80 | // 81 | // so (a*d + b*c) = (b-a)*(c-d) + a*c + b*d. 82 | mul{{ .FIELD}}(&ac, &lhs.A, &rhs.A) // = a*c*R*R 83 | mul{{ .FIELD}}(&bd, &lhs.B, &rhs.B) // = b*d*R*R 84 | sub{{ .FIELD}}(&bMinA, &lhs.B, &lhs.A) // = (b-a)*R 85 | sub{{ .FIELD}}(&cMinD, &rhs.A, &rhs.B) // = (c-d)*R 86 | mul{{ .FIELD}}(&adPlusBc, &bMinA, &cMinD) // = (b-a)*(c-d)*R*R 87 | adl{{ .FIELD}}(&adPlusBc, &adPlusBc, &ac) // = ((b-a)*(c-d) + a*c)*R*R 88 | adl{{ .FIELD}}(&adPlusBc, &adPlusBc, &bd) // = ((b-a)*(c-d) + a*c + b*d)*R*R 89 | rdc{{ .FIELD}}(&dest.B, &adPlusBc) // = (a*d + b*c)*R mod p 90 | sul{{ .FIELD}}(&acMinBd, &ac, &bd) // = (a*c - b*d)*R*R 91 | rdc{{ .FIELD}}(&dest.A, &acMinBd) // = (a*c - b*d)*R mod p 92 | } 93 | 94 | // Set dest = 1/x 95 | // 96 | // Allowed to overlap dest with x. 97 | // 98 | // Returns dest to allow chaining operations. 99 | func inv(dest, x *common.Fp2) { 100 | var e1, e2 common.FpX2 101 | var f1, f2 common.Fp 102 | 103 | // We want to compute 104 | // 105 | // 1 1 (a - bi) (a - bi) 106 | // -------- = -------- -------- = ----------- 107 | // (a + bi) (a + bi) (a - bi) (a^2 + b^2) 108 | // 109 | // Letting c = 1/(a^2 + b^2), this is 110 | // 111 | // 1/(a+bi) = a*c - b*ci. 112 | 113 | mul{{ .FIELD}}(&e1, &x.A, &x.A) // = a*a*R*R 114 | mul{{ .FIELD}}(&e2, &x.B, &x.B) // = b*b*R*R 115 | adl{{ .FIELD}}(&e1, &e1, &e2) // = (a^2 + b^2)*R*R 116 | rdc{{ .FIELD}}(&f1, &e1) // = (a^2 + b^2)*R mod p 117 | // Now f1 = a^2 + b^2 118 | 119 | mulP(&f2, &f1, &f1) 120 | p34(&f2, &f2) 121 | mulP(&f2, &f2, &f2) 122 | mulP(&f2, &f2, &f1) 123 | 124 | mul{{ .FIELD}}(&e1, &x.A, &f2) 125 | rdc{{ .FIELD}}(&dest.A, &e1) 126 | 127 | sub{{ .FIELD}}(&f1, &common.Fp{}, &x.B) 128 | mul{{ .FIELD}}(&e1, &f1, &f2) 129 | rdc{{ .FIELD}}(&dest.B, &e1) 130 | } 131 | 132 | func sqr(dest, x *common.Fp2) { 133 | var a2, aPlusB, aMinusB common.Fp 134 | var a2MinB2, ab2 common.FpX2 135 | 136 | a := &x.A 137 | b := &x.B 138 | 139 | // (a + bi)*(a + bi) = (a^2 - b^2) + 2abi. 140 | add{{ .FIELD}}(&a2, a, a) // = a*R + a*R = 2*a*R 141 | add{{ .FIELD}}(&aPlusB, a, b) // = a*R + b*R = (a+b)*R 142 | sub{{ .FIELD}}(&aMinusB, a, b) // = a*R - b*R = (a-b)*R 143 | mul{{ .FIELD}}(&a2MinB2, &aPlusB, &aMinusB) // = (a+b)*(a-b)*R*R = (a^2 - b^2)*R*R 144 | mul{{ .FIELD}}(&ab2, &a2, b) // = 2*a*b*R*R 145 | rdc{{ .FIELD}}(&dest.A, &a2MinB2) // = (a^2 - b^2)*R mod p 146 | rdc{{ .FIELD}}(&dest.B, &ab2) // = 2*a*b*R mod p 147 | } 148 | 149 | // In case choice == 1, performs following swap in constant time: 150 | // xPx <-> xQx 151 | // xPz <-> xQz 152 | // Otherwise returns xPx, xPz, xQx, xQz unchanged 153 | func cswap(xPx, xPz, xQx, xQz *common.Fp2, choice uint8) { 154 | cswap{{ .FIELD}}(&xPx.A, &xQx.A, choice) 155 | cswap{{ .FIELD}}(&xPx.B, &xQx.B, choice) 156 | cswap{{ .FIELD}}(&xPz.A, &xQz.A, choice) 157 | cswap{{ .FIELD}}(&xPz.B, &xQz.B, choice) 158 | } 159 | 160 | // Converts in.A and in.B to Montgomery domain and stores 161 | // in 'out' 162 | // out.A = in.A * R mod p 163 | // out.B = in.B * R mod p 164 | // Performs v = v*R^2*R^(-1) mod p, for both in.A and in.B 165 | func ToMontgomery(out, in *common.Fp2) { 166 | var aRR common.FpX2 167 | 168 | // a*R*R 169 | mul{{ .FIELD}}(&aRR, &in.A, &{{ .FIELD}}R2) 170 | // a*R mod p 171 | rdc{{ .FIELD}}(&out.A, &aRR) 172 | mul{{ .FIELD}}(&aRR, &in.B, &{{ .FIELD}}R2) 173 | rdc{{ .FIELD}}(&out.B, &aRR) 174 | } 175 | 176 | // Converts in.A and in.B from Montgomery domain and stores 177 | // in 'out' 178 | // out.A = in.A mod p 179 | // out.B = in.B mod p 180 | // 181 | // After returning from the call 'in' is not modified. 182 | func FromMontgomery(out, in *common.Fp2) { 183 | var aR common.FpX2 184 | 185 | // convert from montgomery domain 186 | copy(aR[:], in.A[:]) 187 | rdc{{ .FIELD}}(&out.A, &aR) // = a mod p in [0, 2p) 188 | mod{{ .FIELD}}(&out.A) // = a mod p in [0, p) 189 | for i := range aR { 190 | aR[i] = 0 191 | } 192 | copy(aR[:], in.B[:]) 193 | rdc{{ .FIELD}}(&out.B, &aR) 194 | mod{{ .FIELD}}(&out.B) 195 | } 196 | -------------------------------------------------------------------------------- /dh/sidh/internal/templates/fp2_test.gotemp: -------------------------------------------------------------------------------- 1 | // Code generated by go generate; DO NOT EDIT. 2 | // This file was generated by robots. 3 | 4 | package {{ .PACKAGE}} 5 | 6 | import ( 7 | "math/rand" 8 | "reflect" 9 | "testing" 10 | "testing/quick" 11 | 12 | "github.com/henrydcase/nobs/dh/sidh/common" 13 | ) 14 | 15 | type testParams struct { 16 | Point common.ProjectivePoint 17 | Cparam common.ProjectiveCurveParameters 18 | ExtElem common.Fp2 19 | } 20 | 21 | // Returns true if lhs = rhs. Takes variable time. 22 | func vartimeEqFp2(lhs, rhs *common.Fp2) bool { 23 | a := *lhs 24 | b := *rhs 25 | 26 | mod{{ .FIELD}}(&a.A) 27 | mod{{ .FIELD}}(&a.B) 28 | mod{{ .FIELD}}(&b.A) 29 | mod{{ .FIELD}}(&b.B) 30 | 31 | eq := true 32 | for i := 0; i < FpWords && eq; i++ { 33 | eq = eq && (a.A[i] == b.A[i]) 34 | eq = eq && (a.B[i] == b.B[i]) 35 | } 36 | return eq 37 | } 38 | 39 | func (testParams) generateFp2(rand *rand.Rand) common.Fp2 { 40 | // Generation strategy: low limbs taken from [0,2^64); high limb 41 | // taken from smaller range 42 | // 43 | // Size hint is ignored since all elements are fixed size. 44 | // 45 | // Field elements taken in range [0,2p). Emulate this by capping 46 | // the high limb by the top digit of 2*p-1: 47 | // 48 | // sage: (2*p-1).digits(2^64)[-1] 49 | // 50 | // This still allows generating values >= 2p, but hopefully that 51 | // excess is OK (and if it's not, we'll find out, because it's for 52 | // testing...) 53 | highLimb := rand.Uint64() % {{ .FIELD}}x2[FpWords-1] 54 | fpElementGen := func() (fp common.Fp) { 55 | for i := 0; i < (FpWords - 1); i++ { 56 | fp[i] = rand.Uint64() 57 | } 58 | fp[FpWords-1] = highLimb 59 | return fp 60 | } 61 | return common.Fp2{A: fpElementGen(), B: fpElementGen()} 62 | } 63 | 64 | func (c testParams) Generate(rand *rand.Rand, size int) reflect.Value { 65 | return reflect.ValueOf( 66 | testParams{ 67 | common.ProjectivePoint{ 68 | X: c.generateFp2(rand), 69 | Z: c.generateFp2(rand), 70 | }, 71 | common.ProjectiveCurveParameters{ 72 | A: c.generateFp2(rand), 73 | C: c.generateFp2(rand), 74 | }, 75 | c.generateFp2(rand), 76 | }) 77 | } 78 | 79 | func TestOne(t *testing.T) { 80 | var tmp common.Fp2 81 | 82 | mul(&tmp, ¶ms.OneFp2, ¶ms.A.AffineP) 83 | if !vartimeEqFp2(&tmp, ¶ms.A.AffineP) { 84 | t.Error("Not equal 1") 85 | } 86 | } 87 | 88 | func TestFp2ToBytesRoundTrip(t *testing.T) { 89 | roundTrips := func(x testParams) bool { 90 | var xBytes = make([]byte, 2*params.Bytelen) 91 | var xPrime common.Fp2 92 | 93 | common.Fp2ToBytes(xBytes[:], &x.ExtElem, params.Bytelen) 94 | common.BytesToFp2(&xPrime, xBytes[:], params.Bytelen) 95 | return vartimeEqFp2(&xPrime, &x.ExtElem) 96 | } 97 | 98 | if err := quick.Check(roundTrips, quickCheckConfig); err != nil { 99 | t.Error(err) 100 | } 101 | } 102 | 103 | func TestFp2MulDistributesOverAdd(t *testing.T) { 104 | mulDistributesOverAdd := func(x, y, z testParams) bool { 105 | // Compute t1 = (x+y)*z 106 | t1 := new(common.Fp2) 107 | add(t1, &x.ExtElem, &y.ExtElem) 108 | mul(t1, t1, &z.ExtElem) 109 | 110 | // Compute t2 = x*z + y*z 111 | t2 := new(common.Fp2) 112 | t3 := new(common.Fp2) 113 | mul(t2, &x.ExtElem, &z.ExtElem) 114 | mul(t3, &y.ExtElem, &z.ExtElem) 115 | add(t2, t2, t3) 116 | 117 | return vartimeEqFp2(t1, t2) 118 | } 119 | 120 | if err := quick.Check(mulDistributesOverAdd, quickCheckConfig); err != nil { 121 | t.Error(err) 122 | } 123 | } 124 | 125 | func TestFp2MulIsAssociative(t *testing.T) { 126 | isAssociative := func(x, y, z testParams) bool { 127 | // Compute t1 = (x*y)*z 128 | t1 := new(common.Fp2) 129 | mul(t1, &x.ExtElem, &y.ExtElem) 130 | mul(t1, t1, &z.ExtElem) 131 | 132 | // Compute t2 = (y*z)*x 133 | t2 := new(common.Fp2) 134 | mul(t2, &y.ExtElem, &z.ExtElem) 135 | mul(t2, t2, &x.ExtElem) 136 | 137 | return vartimeEqFp2(t1, t2) 138 | } 139 | 140 | if err := quick.Check(isAssociative, quickCheckConfig); err != nil { 141 | t.Error(err) 142 | } 143 | } 144 | 145 | func TestFp2SquareMatchesMul(t *testing.T) { 146 | sqrMatchesMul := func(x testParams) bool { 147 | // Compute t1 = (x*x) 148 | t1 := new(common.Fp2) 149 | mul(t1, &x.ExtElem, &x.ExtElem) 150 | 151 | // Compute t2 = x^2 152 | t2 := new(common.Fp2) 153 | sqr(t2, &x.ExtElem) 154 | 155 | return vartimeEqFp2(t1, t2) 156 | } 157 | 158 | if err := quick.Check(sqrMatchesMul, quickCheckConfig); err != nil { 159 | t.Error(err) 160 | } 161 | } 162 | 163 | func TestFp2Inv(t *testing.T) { 164 | inverseIsCorrect := func(x testParams) bool { 165 | z := new(common.Fp2) 166 | inv(z, &x.ExtElem) 167 | 168 | // Now z = (1/x), so (z * x) * x == x 169 | mul(z, z, &x.ExtElem) 170 | mul(z, z, &x.ExtElem) 171 | 172 | return vartimeEqFp2(z, &x.ExtElem) 173 | } 174 | 175 | // This is more expensive; run fewer tests 176 | var quickCheckConfig = &quick.Config{MaxCount: (1 << 11)} 177 | if err := quick.Check(inverseIsCorrect, quickCheckConfig); err != nil { 178 | t.Error(err) 179 | } 180 | } 181 | 182 | func TestFp2Batch3Inv(t *testing.T) { 183 | batchInverseIsCorrect := func(x1, x2, x3 testParams) bool { 184 | var x1Inv, x2Inv, x3Inv common.Fp2 185 | inv(&x1Inv, &x1.ExtElem) 186 | inv(&x2Inv, &x2.ExtElem) 187 | inv(&x3Inv, &x3.ExtElem) 188 | 189 | var y1, y2, y3 common.Fp2 190 | Fp2Batch3Inv(&x1.ExtElem, &x2.ExtElem, &x3.ExtElem, &y1, &y2, &y3) 191 | 192 | return (vartimeEqFp2(&x1Inv, &y1) && vartimeEqFp2(&x2Inv, &y2) && vartimeEqFp2(&x3Inv, &y3)) 193 | } 194 | 195 | // This is more expensive; run fewer tests 196 | var quickCheckConfig = &quick.Config{MaxCount: (1 << 8)} 197 | if err := quick.Check(batchInverseIsCorrect, quickCheckConfig); err != nil { 198 | t.Error(err) 199 | } 200 | } 201 | 202 | func BenchmarkFp2Mul(b *testing.B) { 203 | z := &common.Fp2{A: bench_x, B: bench_y} 204 | w := new(common.Fp2) 205 | 206 | for n := 0; n < b.N; n++ { 207 | mul(w, z, z) 208 | } 209 | } 210 | 211 | func BenchmarkFp2Inv(b *testing.B) { 212 | z := &common.Fp2{A: bench_x, B: bench_y} 213 | w := new(common.Fp2) 214 | 215 | for n := 0; n < b.N; n++ { 216 | inv(w, z) 217 | } 218 | } 219 | 220 | func BenchmarkFp2Square(b *testing.B) { 221 | z := &common.Fp2{A: bench_x, B: bench_y} 222 | w := new(common.Fp2) 223 | 224 | for n := 0; n < b.N; n++ { 225 | sqr(w, z) 226 | } 227 | } 228 | 229 | func BenchmarkFp2Add(b *testing.B) { 230 | z := &common.Fp2{A: bench_x, B: bench_y} 231 | w := new(common.Fp2) 232 | 233 | for n := 0; n < b.N; n++ { 234 | add(w, z, z) 235 | } 236 | } 237 | 238 | func BenchmarkFp2Sub(b *testing.B) { 239 | z := &common.Fp2{A: bench_x, B: bench_y} 240 | w := new(common.Fp2) 241 | 242 | for n := 0; n < b.N; n++ { 243 | sub(w, z, z) 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /dh/sidh/internal/templates/gen.go: -------------------------------------------------------------------------------- 1 | // The following directive is necessary to make the package coherent: 2 | 3 | // +build ignore 4 | 5 | // This program generates contributors.go. It can be invoked by running 6 | // go generate 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "os" 12 | "strings" 13 | "text/template" 14 | ) 15 | 16 | var p34 = map[string]struct { 17 | pow_strategy string 18 | mul_strategy string 19 | mul_initial int 20 | }{ 21 | "P434": { 22 | pow_strategy: "[]uint8{3, 10, 7, 5, 6, 5, 3, 8, 4, 7, 5, 6, 4, 5, 9, 6, 3, 11, 5, 5, 2, 8, 4, 7, 7, 8, 5, 6, 4, 8, 5, 2, 10, 6, 5, 4, 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1}", 23 | mul_strategy: "[]uint8{2, 15, 9, 8, 14, 12, 2, 8, 5, 15, 8, 15, 6, 6, 3, 2, 0, 10, 9, 13, 1, 12, 3, 7, 1, 10, 8, 11, 2, 15, 14, 1, 11, 12, 14, 3, 11, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0}", 24 | mul_initial: 8, 25 | }, 26 | "P503": { 27 | pow_strategy: "[]uint8{12, 5, 5, 2, 7, 11, 3, 8, 4, 11, 4, 7, 5, 6, 3, 7, 5, 7, 2, 12, 5, 6, 4, 6, 8, 6, 4, 7, 5, 5, 8, 5, 8, 5, 5, 8, 9, 3, 6, 2, 10, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3}", 28 | mul_strategy: "[]uint8{12, 11, 10, 0, 1, 8, 3, 7, 1, 8, 3, 6, 7, 14, 2, 14, 14, 9, 0, 13, 9, 15, 5, 12, 7, 13, 7, 15, 6, 7, 9, 0, 5, 7, 6, 8, 8, 3, 7, 0, 10, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 3}", 29 | mul_initial: 0, 30 | }, 31 | "P751": { 32 | pow_strategy: "[]uint8{5, 7, 6, 2, 10, 4, 6, 9, 8, 5, 9, 4, 7, 5, 5, 4, 8, 3, 9, 5, 5, 4, 10, 4, 6, 6, 6, 5, 8, 9, 3, 4, 9, 4, 5, 6, 6, 2, 9, 4, 5, 5, 5, 7, 7, 9, 4, 6, 4, 8, 5, 8, 6, 6, 2, 9, 7, 4, 8, 8, 8, 4, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2}", 33 | mul_strategy: "[]uint8{15, 11, 10, 0, 15, 3, 3, 3, 4, 4, 9, 7, 11, 11, 5, 3, 12, 2, 10, 8, 5, 2, 8, 3, 5, 4, 11, 4, 0, 9, 2, 1, 12, 7, 5, 14, 15, 0, 14, 5, 6, 4, 5, 13, 6, 9, 7, 15, 1, 14, 11, 15, 12, 5, 0, 10, 9, 7, 7, 10, 14, 6, 11, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 1}", 34 | mul_initial: 13, 35 | }, 36 | } 37 | 38 | // P434 optimized implementation for 39 | // ARM64 is not ported yet 40 | var opt_arm = map[string]bool{ 41 | "P434": false, 42 | "P503": true, 43 | "P751": true, 44 | } 45 | 46 | // Generates an 'fileNameBase.go' from 'fileNameBase.gotemp' file 47 | // for a given finite 'field'. Maps placeholders to 'values'. 48 | func gen(field, fileNameBase string, values interface{}) { 49 | // name of the output .go file 50 | outFileName := fileNameBase + ".go" 51 | out, err := os.Create(outFileName) 52 | if err != nil { 53 | panic("Cannot open file") 54 | } 55 | 56 | // Template files are located in ../templates and have 57 | // extension .gotemp 58 | templateFile := "../templates/" + fileNameBase + ".gotemp" 59 | t, err := template.ParseFiles(templateFile) 60 | if err != nil { 61 | panic(fmt.Sprintf("Cannot open template file %s", templateFile)) 62 | } 63 | 64 | t.Execute(out, values) 65 | err = out.Close() 66 | if err != nil { 67 | panic("Cant close generated file") 68 | } 69 | } 70 | 71 | func main() { 72 | field := os.Args[1] 73 | 74 | s := struct { 75 | FIELD string 76 | PACKAGE string 77 | P34_POW_STRATEGY string 78 | P34_MUL_STRATEGY string 79 | P34_INITIAL_MUL int 80 | OPT_ARM bool 81 | }{ 82 | FIELD: field, 83 | PACKAGE: strings.ToLower(field), 84 | P34_POW_STRATEGY: p34[field].pow_strategy, 85 | P34_MUL_STRATEGY: p34[field].mul_strategy, 86 | P34_INITIAL_MUL: p34[field].mul_initial, 87 | OPT_ARM: opt_arm[field], 88 | } 89 | 90 | targets := map[string]interface{}{ 91 | "arith_decl": s, 92 | "arith_generic": s, 93 | "curve": s, 94 | "fp2": s, 95 | "core": s, 96 | 97 | // tests 98 | "arith_test": s, 99 | "fp2_test": s, 100 | "curve_test": s, 101 | } 102 | 103 | for v, s := range targets { 104 | gen(field, v, s) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /drbg/ctr_drbg.go: -------------------------------------------------------------------------------- 1 | // This is initial implementation of CTR_DRBG with AES-256. Code is tested 2 | // and functionaly correct. Nevertheless it will be changed 3 | // 4 | // TODO: Following things still need to be done 5 | // * Add other AES key lengts 6 | // * Validate sizes from table 3 of SP800-90A 7 | // * Improve reseeding so that code returns an error when reseed is needed 8 | // * Add case with derivation function (maybe) 9 | // * Code cleanup 10 | // * Add rest of the test vectors from CAVP 11 | 12 | package drbg 13 | 14 | import ( 15 | "github.com/henrydcase/nobs/drbg/internal/aes" 16 | "github.com/henrydcase/nobs/utils" 17 | ) 18 | 19 | // Constants below correspond to AES-256, which is currently 20 | // the only block cipher supported. 21 | const ( 22 | BlockLen = 16 23 | KeyLen = 32 24 | SeedLen = BlockLen + KeyLen 25 | ) 26 | 27 | type CtrDrbg struct { 28 | v [BlockLen]byte 29 | key [KeyLen]byte 30 | counter uint 31 | strength uint 32 | resistance bool 33 | blockEnc aes.IAES 34 | tmpBlk [3 * BlockLen]byte 35 | } 36 | 37 | func NewCtrDrbg() *CtrDrbg { 38 | if utils.X86.HasAES { 39 | return &CtrDrbg{blockEnc: &aes.AESAsm{}} 40 | } 41 | return &CtrDrbg{blockEnc: &aes.AES{}} 42 | } 43 | 44 | func (c *CtrDrbg) inc() { 45 | for i := BlockLen - 1; i >= 0; i-- { 46 | if c.v[i] == 0xff { 47 | c.v[i] = 0x00 48 | } else { 49 | c.v[i]++ 50 | break 51 | } 52 | } 53 | } 54 | 55 | func (c *CtrDrbg) Init(entropy, personalization []byte) bool { 56 | var lsz int 57 | var seedBuf [SeedLen]byte 58 | 59 | // Minimum entropy input (SP800-90A, 10.2.1) 60 | if len(entropy) < int(c.strength/8) { 61 | return false 62 | } 63 | 64 | // Security strength for AES-256 as per SP800-57, 5.6.1 65 | c.strength = 256 66 | 67 | lsz = len(entropy) 68 | if lsz > SeedLen { 69 | lsz = SeedLen 70 | } 71 | copy(seedBuf[:], entropy[:lsz]) 72 | 73 | lsz = len(personalization) 74 | if lsz > SeedLen { 75 | lsz = SeedLen 76 | } 77 | 78 | for i := 0; i < lsz; i++ { 79 | seedBuf[i] ^= personalization[i] 80 | } 81 | 82 | c.blockEnc.SetKey(c.key[:]) 83 | c.update(seedBuf[:]) 84 | c.counter = 1 85 | return true 86 | } 87 | 88 | func (c *CtrDrbg) update(data []byte) { 89 | if len(data) != SeedLen { 90 | panic("Provided data is not equal to strength/8") 91 | } 92 | 93 | // deliberatelly not using len(c.tmpBlk) 94 | for i := 0; i < 3*BlockLen; i += BlockLen { 95 | c.inc() 96 | c.blockEnc.SetKey(c.key[:]) 97 | c.blockEnc.Encrypt(c.tmpBlk[i:], c.v[:]) 98 | } 99 | 100 | for i := 0; i < 3*BlockLen; i++ { 101 | c.tmpBlk[i] ^= data[i] 102 | } 103 | 104 | copy(c.key[:], c.tmpBlk[:KeyLen]) 105 | copy(c.v[:], c.tmpBlk[KeyLen:]) 106 | } 107 | 108 | func (c *CtrDrbg) Reseed(entropy, data []byte) { 109 | var seedBuf [SeedLen]byte 110 | var lsz int 111 | 112 | lsz = len(entropy) 113 | if lsz > SeedLen { 114 | lsz = SeedLen 115 | } 116 | copy(seedBuf[:], entropy[:lsz]) 117 | 118 | lsz = len(data) 119 | if lsz > SeedLen { 120 | lsz = SeedLen 121 | } 122 | 123 | for i := 0; i < lsz; i++ { 124 | seedBuf[i] ^= data[i] 125 | } 126 | 127 | c.update(seedBuf[:]) 128 | c.counter = 1 129 | } 130 | 131 | func (c *CtrDrbg) ReadWithAdditionalData(out, ad []byte) (n int, err error) { 132 | var seedBuf [SeedLen]byte 133 | // TODO: check reseed_counter > reseed_interval 134 | 135 | if len(ad) > 0 { 136 | // pad additional data with zeros if needed 137 | copy(seedBuf[:], ad) 138 | c.update(seedBuf[:]) 139 | } 140 | 141 | // Number of blocks to write minus last one 142 | blocks := len(out) / BlockLen 143 | for i := 0; i < blocks; i++ { 144 | c.inc() 145 | c.blockEnc.SetKey(c.key[:]) 146 | c.blockEnc.Encrypt(out[i*BlockLen:], c.v[:]) 147 | } 148 | 149 | // Copy remainder - case for out being not block aligned 150 | c.blockEnc.Encrypt(c.tmpBlk[:], c.v[:]) 151 | copy(out[blocks*BlockLen:], c.tmpBlk[:len(out)%BlockLen]) 152 | 153 | c.update(seedBuf[:]) 154 | c.counter += 1 155 | return len(out), nil 156 | } 157 | 158 | // Read reads data from DRBG. Size of data is determined by 159 | // out buffer. 160 | func (c *CtrDrbg) Read(out []byte) (n int, err error) { 161 | return c.ReadWithAdditionalData(out, nil) 162 | } 163 | -------------------------------------------------------------------------------- /drbg/ctr_drbg_test.go: -------------------------------------------------------------------------------- 1 | package drbg 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "testing" 7 | ) 8 | 9 | func S2H(s string) []byte { 10 | hex, e := hex.DecodeString(s) 11 | if e != nil { 12 | panic("Can't import private key") 13 | } 14 | return hex 15 | } 16 | 17 | func TestNominal(t *testing.T) { 18 | var entropy [16]byte 19 | var data [48]byte 20 | 21 | c := NewCtrDrbg() 22 | if !c.Init(entropy[:], nil) { 23 | t.FailNow() 24 | } 25 | 26 | c.ReadWithAdditionalData(entropy[0:16], data[:]) 27 | 28 | exp := S2H("16BA361FA14563FB1E8BCF88932F9FA7") 29 | if !bytes.Equal(exp, entropy[:]) { 30 | t.FailNow() 31 | } 32 | } 33 | 34 | // TODO: should parse *.req file from here: https://raw.githubusercontent.com/coruus/nist-testvectors/master/csrc.nist.gov/groups/STM/cavp/documents/drbg/drbgtestvectors/drbgvectors_pr_false/CTR_DRBG.rsp 35 | var vectors = []struct { 36 | EntropyInput []byte 37 | PersonalizationString []byte 38 | EntropyInputReseed []byte 39 | AdditionalInputReseed []byte 40 | AdditionalInput1 []byte 41 | AdditionalInput2 []byte 42 | ReturnedBits []byte 43 | }{ 44 | // With Reseeding 45 | { 46 | S2H("99903165903fea49c2db26ed675e44cc14cb2c1f28b836b203240b02771e831146ffc4335373bb344688c5c950670291"), 47 | []byte{}, 48 | S2H("b4ee99fa9e0eddaf4a3612013cd636c4af69177b43eebb3c58a305b9979b68b5cc820504f6c029aad78a5d29c66e84a0"), 49 | S2H("2d8c5c28b05696e74774eb69a10f01c5fabc62691ddf7848a8004bb5eeb4d2c5febe1aa01f4d557b23d7e9a0e4e90655"), 50 | S2H("0dc9cde42ac6e856f01a55f219c614de90c659260948db5053d414bab0ec2e13e995120c3eb5aafc25dc4bdcef8ace24"), 51 | S2H("711be6c035013189f362211889248ca8a3268e63a7eb26836d915810a680ac4a33cd1180811a31a0f44f08db3dd64f91"), 52 | S2H("11c7a0326ea737baa7a993d510fafee5374e7bbe17ef0e3e29f50fa68aac2124b017d449768491cac06d136d691a4e80785739f9aaedf311bba752a3268cc531"), 53 | }, 54 | 55 | { 56 | S2H("ffad10100025a879672ff50374b286712f457dd01441d76ac1a1cd15c7390dd93179a2f5920d198bf34a1b76fbc21289"), 57 | S2H("1d2be6f25e88fa30c4ef42e4d54efd957dec231fa00143ca47580be666a8c143a916c90b3819a0a7ea914e3c9a2e7a3f"), 58 | S2H("6c1a089cae313363bc76a780139eb4f2f2048b1f6b07896c5c412bff0385440fc43b73facbb79e3a252fa01fe17ab391"), 59 | []byte{}, 60 | []byte{}, 61 | []byte{}, 62 | S2H("e053c7d4bd9099ef6a99f190a5fd80219437d642006672338da6e0fe73ca4d24ffa51151bfbdac78d8a2f6255046edf57a04626e9977139c6933274299f3bdff"), 63 | }, 64 | } 65 | 66 | func TestVector(t *testing.T) { 67 | 68 | for i := range vectors { 69 | result := make([]byte, len(vectors[i].ReturnedBits)) 70 | c := NewCtrDrbg() 71 | if !c.Init(vectors[i].EntropyInput[:], vectors[i].PersonalizationString) { 72 | t.Error("Init failed") 73 | } 74 | 75 | if len(vectors[i].EntropyInputReseed) > 0 { 76 | c.Reseed(vectors[i].EntropyInputReseed[:], vectors[i].AdditionalInputReseed[:]) 77 | } 78 | c.ReadWithAdditionalData(result[:], vectors[i].AdditionalInput1) 79 | c.ReadWithAdditionalData(result[:], vectors[i].AdditionalInput2) 80 | 81 | if !bytes.Equal(vectors[i].ReturnedBits[:], result[:]) { 82 | t.Errorf("KAT failed \nexp: %X\ngot: %X\n", vectors[i].ReturnedBits, result[:]) 83 | } 84 | 85 | } 86 | } 87 | 88 | func BenchmarkInit(b *testing.B) { 89 | c := NewCtrDrbg() 90 | for i := 0; i < b.N; i++ { 91 | c.Init(vectors[0].EntropyInput[:], vectors[0].PersonalizationString) 92 | } 93 | } 94 | 95 | func BenchmarkRead(b *testing.B) { 96 | var result [16 * 10]byte 97 | c := NewCtrDrbg() 98 | c.Init(vectors[0].EntropyInput[:], vectors[0].PersonalizationString) 99 | for i := 0; i < b.N; i++ { 100 | c.ReadWithAdditionalData(result[:], vectors[0].AdditionalInput1) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /drbg/internal/aes/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. 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 Google Inc. 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 | -------------------------------------------------------------------------------- /drbg/internal/aes/asm_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build amd64, !noasm 6 | 7 | #include "textflag.h" 8 | 9 | // func encryptBlockAsm(nr int, xk *uint32, dst, src *byte) 10 | TEXT ·encryptBlockAsm(SB),NOSPLIT,$0 11 | MOVQ nr+0(FP), CX 12 | MOVQ xk+8(FP), AX 13 | MOVQ dst+16(FP), DX 14 | MOVQ src+24(FP), BX 15 | MOVUPS 0(AX), X1 16 | MOVUPS 0(BX), X0 17 | ADDQ $16, AX 18 | PXOR X1, X0 19 | SUBQ $12, CX 20 | JE Lenc196 21 | JB Lenc128 22 | Lenc256: 23 | MOVUPS 0(AX), X1 24 | AESENC X1, X0 25 | MOVUPS 16(AX), X1 26 | AESENC X1, X0 27 | ADDQ $32, AX 28 | Lenc196: 29 | MOVUPS 0(AX), X1 30 | AESENC X1, X0 31 | MOVUPS 16(AX), X1 32 | AESENC X1, X0 33 | ADDQ $32, AX 34 | Lenc128: 35 | MOVUPS 0(AX), X1 36 | AESENC X1, X0 37 | MOVUPS 16(AX), X1 38 | AESENC X1, X0 39 | MOVUPS 32(AX), X1 40 | AESENC X1, X0 41 | MOVUPS 48(AX), X1 42 | AESENC X1, X0 43 | MOVUPS 64(AX), X1 44 | AESENC X1, X0 45 | MOVUPS 80(AX), X1 46 | AESENC X1, X0 47 | MOVUPS 96(AX), X1 48 | AESENC X1, X0 49 | MOVUPS 112(AX), X1 50 | AESENC X1, X0 51 | MOVUPS 128(AX), X1 52 | AESENC X1, X0 53 | MOVUPS 144(AX), X1 54 | AESENCLAST X1, X0 55 | MOVUPS X0, 0(DX) 56 | RET 57 | 58 | // func decryptBlockAsm(nr int, xk *uint32, dst, src *byte) 59 | TEXT ·decryptBlockAsm(SB),NOSPLIT,$0 60 | MOVQ nr+0(FP), CX 61 | MOVQ xk+8(FP), AX 62 | MOVQ dst+16(FP), DX 63 | MOVQ src+24(FP), BX 64 | MOVUPS 0(AX), X1 65 | MOVUPS 0(BX), X0 66 | ADDQ $16, AX 67 | PXOR X1, X0 68 | SUBQ $12, CX 69 | JE Ldec196 70 | JB Ldec128 71 | Ldec256: 72 | MOVUPS 0(AX), X1 73 | AESDEC X1, X0 74 | MOVUPS 16(AX), X1 75 | AESDEC X1, X0 76 | ADDQ $32, AX 77 | Ldec196: 78 | MOVUPS 0(AX), X1 79 | AESDEC X1, X0 80 | MOVUPS 16(AX), X1 81 | AESDEC X1, X0 82 | ADDQ $32, AX 83 | Ldec128: 84 | MOVUPS 0(AX), X1 85 | AESDEC X1, X0 86 | MOVUPS 16(AX), X1 87 | AESDEC X1, X0 88 | MOVUPS 32(AX), X1 89 | AESDEC X1, X0 90 | MOVUPS 48(AX), X1 91 | AESDEC X1, X0 92 | MOVUPS 64(AX), X1 93 | AESDEC X1, X0 94 | MOVUPS 80(AX), X1 95 | AESDEC X1, X0 96 | MOVUPS 96(AX), X1 97 | AESDEC X1, X0 98 | MOVUPS 112(AX), X1 99 | AESDEC X1, X0 100 | MOVUPS 128(AX), X1 101 | AESDEC X1, X0 102 | MOVUPS 144(AX), X1 103 | AESDECLAST X1, X0 104 | MOVUPS X0, 0(DX) 105 | RET 106 | 107 | // func expandKeyAsm(nr int, key *byte, enc, dec *uint32) { 108 | // Note that round keys are stored in uint128 format, not uint32 109 | TEXT ·expandKeyAsm(SB),NOSPLIT,$0 110 | MOVQ nr+0(FP), CX 111 | MOVQ key+8(FP), AX 112 | MOVQ enc+16(FP), BX 113 | MOVQ dec+24(FP), DX 114 | MOVUPS (AX), X0 115 | // enc 116 | MOVUPS X0, (BX) 117 | ADDQ $16, BX 118 | PXOR X4, X4 // _expand_key_* expect X4 to be zero 119 | CMPL CX, $12 120 | JE Lexp_enc196 121 | JB Lexp_enc128 122 | Lexp_enc256: 123 | MOVUPS 16(AX), X2 124 | MOVUPS X2, (BX) 125 | ADDQ $16, BX 126 | AESKEYGENASSIST $0x01, X2, X1 127 | CALL _expand_key_256a<>(SB) 128 | AESKEYGENASSIST $0x01, X0, X1 129 | CALL _expand_key_256b<>(SB) 130 | AESKEYGENASSIST $0x02, X2, X1 131 | CALL _expand_key_256a<>(SB) 132 | AESKEYGENASSIST $0x02, X0, X1 133 | CALL _expand_key_256b<>(SB) 134 | AESKEYGENASSIST $0x04, X2, X1 135 | CALL _expand_key_256a<>(SB) 136 | AESKEYGENASSIST $0x04, X0, X1 137 | CALL _expand_key_256b<>(SB) 138 | AESKEYGENASSIST $0x08, X2, X1 139 | CALL _expand_key_256a<>(SB) 140 | AESKEYGENASSIST $0x08, X0, X1 141 | CALL _expand_key_256b<>(SB) 142 | AESKEYGENASSIST $0x10, X2, X1 143 | CALL _expand_key_256a<>(SB) 144 | AESKEYGENASSIST $0x10, X0, X1 145 | CALL _expand_key_256b<>(SB) 146 | AESKEYGENASSIST $0x20, X2, X1 147 | CALL _expand_key_256a<>(SB) 148 | AESKEYGENASSIST $0x20, X0, X1 149 | CALL _expand_key_256b<>(SB) 150 | AESKEYGENASSIST $0x40, X2, X1 151 | CALL _expand_key_256a<>(SB) 152 | JMP Lexp_dec 153 | Lexp_enc196: 154 | MOVQ 16(AX), X2 155 | AESKEYGENASSIST $0x01, X2, X1 156 | CALL _expand_key_192a<>(SB) 157 | AESKEYGENASSIST $0x02, X2, X1 158 | CALL _expand_key_192b<>(SB) 159 | AESKEYGENASSIST $0x04, X2, X1 160 | CALL _expand_key_192a<>(SB) 161 | AESKEYGENASSIST $0x08, X2, X1 162 | CALL _expand_key_192b<>(SB) 163 | AESKEYGENASSIST $0x10, X2, X1 164 | CALL _expand_key_192a<>(SB) 165 | AESKEYGENASSIST $0x20, X2, X1 166 | CALL _expand_key_192b<>(SB) 167 | AESKEYGENASSIST $0x40, X2, X1 168 | CALL _expand_key_192a<>(SB) 169 | AESKEYGENASSIST $0x80, X2, X1 170 | CALL _expand_key_192b<>(SB) 171 | JMP Lexp_dec 172 | Lexp_enc128: 173 | AESKEYGENASSIST $0x01, X0, X1 174 | CALL _expand_key_128<>(SB) 175 | AESKEYGENASSIST $0x02, X0, X1 176 | CALL _expand_key_128<>(SB) 177 | AESKEYGENASSIST $0x04, X0, X1 178 | CALL _expand_key_128<>(SB) 179 | AESKEYGENASSIST $0x08, X0, X1 180 | CALL _expand_key_128<>(SB) 181 | AESKEYGENASSIST $0x10, X0, X1 182 | CALL _expand_key_128<>(SB) 183 | AESKEYGENASSIST $0x20, X0, X1 184 | CALL _expand_key_128<>(SB) 185 | AESKEYGENASSIST $0x40, X0, X1 186 | CALL _expand_key_128<>(SB) 187 | AESKEYGENASSIST $0x80, X0, X1 188 | CALL _expand_key_128<>(SB) 189 | AESKEYGENASSIST $0x1b, X0, X1 190 | CALL _expand_key_128<>(SB) 191 | AESKEYGENASSIST $0x36, X0, X1 192 | CALL _expand_key_128<>(SB) 193 | Lexp_dec: 194 | // dec 195 | SUBQ $16, BX 196 | MOVUPS (BX), X1 197 | MOVUPS X1, (DX) 198 | DECQ CX 199 | Lexp_dec_loop: 200 | MOVUPS -16(BX), X1 201 | AESIMC X1, X0 202 | MOVUPS X0, 16(DX) 203 | SUBQ $16, BX 204 | ADDQ $16, DX 205 | DECQ CX 206 | JNZ Lexp_dec_loop 207 | MOVUPS -16(BX), X0 208 | MOVUPS X0, 16(DX) 209 | RET 210 | 211 | TEXT _expand_key_128<>(SB),NOSPLIT,$0 212 | PSHUFD $0xff, X1, X1 213 | SHUFPS $0x10, X0, X4 214 | PXOR X4, X0 215 | SHUFPS $0x8c, X0, X4 216 | PXOR X4, X0 217 | PXOR X1, X0 218 | MOVUPS X0, (BX) 219 | ADDQ $16, BX 220 | RET 221 | 222 | TEXT _expand_key_192a<>(SB),NOSPLIT,$0 223 | PSHUFD $0x55, X1, X1 224 | SHUFPS $0x10, X0, X4 225 | PXOR X4, X0 226 | SHUFPS $0x8c, X0, X4 227 | PXOR X4, X0 228 | PXOR X1, X0 229 | 230 | MOVAPS X2, X5 231 | MOVAPS X2, X6 232 | PSLLDQ $0x4, X5 233 | PSHUFD $0xff, X0, X3 234 | PXOR X3, X2 235 | PXOR X5, X2 236 | 237 | MOVAPS X0, X1 238 | SHUFPS $0x44, X0, X6 239 | MOVUPS X6, (BX) 240 | SHUFPS $0x4e, X2, X1 241 | MOVUPS X1, 16(BX) 242 | ADDQ $32, BX 243 | RET 244 | 245 | TEXT _expand_key_192b<>(SB),NOSPLIT,$0 246 | PSHUFD $0x55, X1, X1 247 | SHUFPS $0x10, X0, X4 248 | PXOR X4, X0 249 | SHUFPS $0x8c, X0, X4 250 | PXOR X4, X0 251 | PXOR X1, X0 252 | 253 | MOVAPS X2, X5 254 | PSLLDQ $0x4, X5 255 | PSHUFD $0xff, X0, X3 256 | PXOR X3, X2 257 | PXOR X5, X2 258 | 259 | MOVUPS X0, (BX) 260 | ADDQ $16, BX 261 | RET 262 | 263 | TEXT _expand_key_256a<>(SB),NOSPLIT,$0 264 | JMP _expand_key_128<>(SB) 265 | 266 | TEXT _expand_key_256b<>(SB),NOSPLIT,$0 267 | PSHUFD $0xaa, X1, X1 268 | SHUFPS $0x10, X2, X4 269 | PXOR X4, X2 270 | SHUFPS $0x8c, X2, X4 271 | PXOR X4, X2 272 | PXOR X1, X2 273 | 274 | MOVUPS X2, (BX) 275 | ADDQ $16, BX 276 | RET 277 | -------------------------------------------------------------------------------- /drbg/internal/aes/cipher.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // +build noasm amd64 arm64 ppc64le riscv64 5 | 6 | package aes 7 | 8 | import ( 9 | "strconv" 10 | ) 11 | 12 | // The AES block size in bytes. 13 | const BlockSize = 16 14 | 15 | // A cipher is an instance of AES encryption using a particular key. 16 | type AES struct { 17 | enc [32 + 28]uint32 18 | dec [32 + 28]uint32 19 | keyLen int 20 | } 21 | 22 | // AES interface 23 | type IAES interface { 24 | SetKey(key []byte) error 25 | Encrypt(dst, src []byte) 26 | Decrypt(dst, src []byte) 27 | } 28 | 29 | type KeySizeError int 30 | 31 | func (k KeySizeError) Error() string { 32 | return "crypto/aes: invalid key size " + strconv.Itoa(int(k)) 33 | } 34 | 35 | func NewCipher() *AES { 36 | return new(AES) 37 | } 38 | 39 | // NewCipher creates and returns a new cipher.Block. 40 | // The key argument should be the AES key, 41 | // either 16, 24, or 32 bytes to select 42 | // AES-128, AES-192, or AES-256. 43 | func (c *AES) SetKey(key []byte) error { 44 | k := len(key) 45 | 46 | switch k { 47 | default: 48 | return KeySizeError(k) 49 | case 16, 24, 32: 50 | break 51 | } 52 | for i, _ := range c.enc { 53 | c.enc[i] = 0 54 | } 55 | for i, _ := range c.dec { 56 | c.dec[i] = 0 57 | } 58 | c.keyLen = k 59 | expandKeyGo(key, c.enc[:c.keyLen+28], c.dec[:c.keyLen+28]) 60 | return nil 61 | } 62 | 63 | func (c *AES) BlockSize() int { return BlockSize } 64 | 65 | func (c *AES) Encrypt(dst, src []byte) { 66 | if len(src) < BlockSize { 67 | panic("crypto/aes: input not full block") 68 | } 69 | if len(dst) < BlockSize { 70 | panic("crypto/aes: output not full block") 71 | } 72 | if InexactOverlap(dst[:BlockSize], src[:BlockSize]) { 73 | panic("crypto/aes: invalid buffer overlap") 74 | } 75 | encryptBlockGo(c.enc[:c.keyLen+28], dst, src) 76 | } 77 | 78 | func (c *AES) Decrypt(dst, src []byte) { 79 | if len(src) < BlockSize { 80 | panic("crypto/aes: input not full block") 81 | } 82 | if len(dst) < BlockSize { 83 | panic("crypto/aes: output not full block") 84 | } 85 | if InexactOverlap(dst[:BlockSize], src[:BlockSize]) { 86 | panic("crypto/aes: invalid buffer overlap") 87 | } 88 | decryptBlockGo(c.dec[:c.keyLen+28], dst, src) 89 | } 90 | -------------------------------------------------------------------------------- /drbg/internal/aes/cipher_asm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // +build amd64, !noasm 5 | 6 | package aes 7 | 8 | import ( 9 | "github.com/henrydcase/nobs/utils" 10 | ) 11 | 12 | // defined in asm_*.s 13 | 14 | //go:noescape 15 | func encryptBlockAsm(nr int, xk *uint32, dst, src *byte) 16 | 17 | //go:noescape 18 | func decryptBlockAsm(nr int, xk *uint32, dst, src *byte) 19 | 20 | //go:noescape 21 | func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32) 22 | 23 | type AESAsm struct { 24 | enc [32 + 28]uint32 25 | dec [32 + 28]uint32 26 | } 27 | 28 | func (c *AESAsm) SetKey(key []byte) error { 29 | var rounds int 30 | switch len(key) { 31 | case 128 / 8: 32 | rounds = 10 33 | case 192 / 8: 34 | rounds = 12 35 | case 256 / 8: 36 | rounds = 14 37 | } 38 | 39 | expandKeyAsm(rounds, &key[0], &c.enc[0], &c.dec[0]) 40 | return nil 41 | } 42 | 43 | func (c *AESAsm) BlockSize() int { return BlockSize } 44 | 45 | func (c *AESAsm) Encrypt(dst, src []byte) { 46 | if len(src) < BlockSize { 47 | panic("crypto/aes: input not full block") 48 | } 49 | if len(dst) < BlockSize { 50 | panic("crypto/aes: output not full block") 51 | } 52 | if InexactOverlap(dst[:BlockSize], src[:BlockSize]) { 53 | panic("crypto/aes: invalid buffer overlap") 54 | } 55 | encryptBlockAsm(len(c.enc)/4-1, &c.enc[0], &dst[0], &src[0]) 56 | } 57 | 58 | func (c *AESAsm) Decrypt(dst, src []byte) { 59 | if len(src) < BlockSize { 60 | panic("crypto/aes: input not full block") 61 | } 62 | if len(dst) < BlockSize { 63 | panic("crypto/aes: output not full block") 64 | } 65 | if InexactOverlap(dst[:BlockSize], src[:BlockSize]) { 66 | panic("crypto/aes: invalid buffer overlap") 67 | } 68 | decryptBlockAsm(len(c.dec)/4-1, &c.dec[0], &dst[0], &src[0]) 69 | } 70 | 71 | // expandKey is used by BenchmarkExpand to ensure that the asm implementation 72 | // of key expansion is used for the benchmark when it is available. 73 | func expandKey(key []byte, enc, dec []uint32) { 74 | if utils.X86.HasAES { 75 | rounds := 10 // rounds needed for AES128 76 | switch len(key) { 77 | case 192 / 8: 78 | rounds = 12 79 | case 256 / 8: 80 | rounds = 14 81 | } 82 | expandKeyAsm(rounds, &key[0], &enc[0], &dec[0]) 83 | } else { 84 | expandKeyGo(key, enc, dec) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /drbg/internal/aes/cipher_noasm.go: -------------------------------------------------------------------------------- 1 | // +build noasm ppc64le riscv64 2 | 3 | package aes 4 | 5 | import( 6 | "errors" 7 | ) 8 | 9 | type AESAsm struct { 10 | } 11 | 12 | func (a *AESAsm) SetKey(key []byte) error { 13 | panic("NotImplemented") 14 | return errors.New("ErrNotImplemented") 15 | } 16 | 17 | func (a *AESAsm) Encrypt(dst, src []byte) { 18 | panic("NotImplemented") 19 | } 20 | 21 | func (a *AESAsm) Decrypt(dst, src []byte) { 22 | panic("NotImplemented") 23 | } 24 | 25 | func expandKey(key []byte, enc, dec []uint32) { 26 | expandKeyGo(key, enc, dec) 27 | } 28 | -------------------------------------------------------------------------------- /drbg/internal/aes/tools.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !appengine 6 | 7 | // Package subtle implements functions that are often useful in cryptographic 8 | // code but require careful thought to use correctly. 9 | // 10 | // This is a mirror of golang.org/x/crypto/internal/subtle. 11 | package aes 12 | 13 | import "unsafe" 14 | 15 | // AnyOverlap reports whether x and y share memory at any (not necessarily 16 | // corresponding) index. The memory beyond the slice length is ignored. 17 | func AnyOverlap(x, y []byte) bool { 18 | return len(x) > 0 && len(y) > 0 && 19 | uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) && 20 | uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1])) 21 | } 22 | 23 | // InexactOverlap reports whether x and y share memory at any non-corresponding 24 | // index. The memory beyond the slice length is ignored. Note that x and y can 25 | // have different lengths and still not have any inexact overlap. 26 | // 27 | // InexactOverlap can be used to implement the requirements of the crypto/cipher 28 | // AEAD, Block, BlockMode and Stream interfaces. 29 | func InexactOverlap(x, y []byte) bool { 30 | if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] { 31 | return false 32 | } 33 | return AnyOverlap(x, y) 34 | } 35 | -------------------------------------------------------------------------------- /etc/dockers/debian-buster-aarch64/Dockerfile_1_13: -------------------------------------------------------------------------------- 1 | FROM multiarch/debian-debootstrap:arm64-buster 2 | 3 | RUN apt-get upgrade -y 4 | RUN apt-get update -qq 5 | USER root 6 | RUN apt-get install -y make wget ca-certificates 7 | RUN wget https://dl.google.com/go/go1.13.linux-arm64.tar.gz 8 | RUN tar -xzf go1.13.linux-arm64.tar.gz 9 | RUN mv go /usr/local/ 10 | RUN ln -s /usr/local/go/bin/go /usr/bin/ 11 | RUN rm -rf /var/lib/apt/lists/* go1.13.linux-arm64.tar.gz 12 | -------------------------------------------------------------------------------- /etc/dockers/debian-buster-aarch64/Dockerfile_1_14: -------------------------------------------------------------------------------- 1 | FROM multiarch/debian-debootstrap:arm64-buster 2 | 3 | RUN apt-get upgrade -y 4 | RUN apt-get update -qq 5 | USER root 6 | RUN apt-get install -y make wget ca-certificates 7 | RUN wget https://dl.google.com/go/go1.14.linux-arm64.tar.gz 8 | RUN tar -xzf go1.14.linux-arm64.tar.gz 9 | RUN mv go /usr/local/ 10 | RUN ln -s /usr/local/go/bin/go /usr/bin/ 11 | RUN rm -rf /var/lib/apt/lists/* go1.14.linux-arm64.tar.gz 12 | -------------------------------------------------------------------------------- /etc/sliding_window_strat_calc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | # Calculates (P-3)/4: Least significant bit is first 4 | 5 | # Configuration 6 | # kWindowSize and kP34 must be specified 7 | # 8 | # P434 9 | #kP34 = [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] 10 | # P503 11 | #kP34 = [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] 12 | # P751 13 | kP34 = [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] 14 | kWindowSize = 5 15 | 16 | 17 | table_pow = [] 18 | table_mul = [] 19 | 20 | i=0 21 | while i=len(kP34): 33 | break 34 | 35 | # Calculate length of a {1,0}, so that it starts & ends 36 | # with 1 and len() <= kWindowSize 37 | if kP34[i] == 1: 38 | # max string len is either 5 or len(kP34) 39 | if i+kWindowSize>=len(kP34): 40 | string_max_len = len(kP34)-i 41 | else: 42 | string_max_len = kWindowSize 43 | 44 | # look backward for first occurence of 1 45 | tmp = i + string_max_len - 1 46 | while tmp>=i: 47 | if kP34[tmp] == 1: 48 | break 49 | tmp = tmp - 1 50 | e_string_len = tmp - i + 1 51 | 52 | # calculate number from bit-string of size e_string_len 53 | tmp = 0 54 | num = 0 55 | for x in kP34[i:i+e_string_len]: 56 | num = num | (x << e_string_len - tmp - 1) 57 | tmp = tmp + 1 58 | table_mul.append(num/2) 59 | 60 | # set new number of pow2k operations 61 | count_pow2k = count_pow2k + e_string_len 62 | 63 | # advance i 64 | i = i + e_string_len 65 | 66 | else: 67 | raise "Wrong number in kP34 at possition " % i 68 | 69 | table_pow.append(count_pow2k) 70 | 71 | print("POW: "); print(table_pow) 72 | print("MUL: "); print(table_mul) 73 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/henrydcase/nobs 2 | 3 | go 1.12 4 | 5 | require golang.org/x/sys v0.1.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= 2 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 3 | -------------------------------------------------------------------------------- /hash/sha3/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Kris Kwiatkowski. All rights reserved. 2 | // Copyright 2014 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // Package sha3 implements the Keccak-p[1600, 24] permuation. 7 | // The 1600 stands for width of the permutation - number of 8 | // bits that are permuted at a time, and 24 stands for number 9 | // of rounds (iterations) of the permuation. 10 | // Package implementds derivatives of the Keccak permuation, 11 | // like SHA-3 fixed-output-length hash, SHAKE which is an 12 | // extendable-output-functions (XOF) and cSHAKE - a XOF with 13 | // domain separation. 14 | // 15 | // The SHA-3 and SHAKE are documented in FIPS-PUB-202 [1] and 16 | // cSHAKE specification can be found in NIST-SP-800-185 [2]. 17 | // 18 | // Implementation was initially based on 19 | // https://godoc.org/golang.org/x/crypto/sha3 20 | package sha3 // import "github.com/henrydcase/nobs/hash/sha3" 21 | -------------------------------------------------------------------------------- /hash/sha3/keccakf_amd64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build amd64,!appengine,!gccgo,!noasm 6 | 7 | package sha3 8 | 9 | // This function is implemented in keccakf_amd64.s. 10 | 11 | //go:noescape 12 | func keccakF1600(a *[25]uint64) 13 | -------------------------------------------------------------------------------- /hash/sha3/shake.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sha3 6 | 7 | // SHAKE128 and SHAKE256 are FIPS approved XOFs. The cSHAKE128/256 8 | // are SHAKE-based XOFs supporting domain separation. 9 | import ( 10 | "encoding/binary" 11 | "io" 12 | ) 13 | 14 | // ShakeHash defines the interface to hash functions that 15 | // support arbitrary-length output. 16 | type ShakeHash interface { 17 | // Write absorbs more data into the hash's state. It panics if input is 18 | // written to it after output has been read from it. 19 | io.Writer 20 | 21 | // Read reads more output from the hash; reading affects the hash's 22 | // state. (ShakeHash.Read is thus very different from Hash.Sum) 23 | // It never returns an error. 24 | io.Reader 25 | 26 | // Clone returns a copy of the ShakeHash in its current state. 27 | Clone() ShakeHash 28 | 29 | // Reset resets the ShakeHash to its initial state. 30 | Reset() 31 | } 32 | 33 | // cSHAKE specific context 34 | type cshakeState struct { 35 | state // SHA-3 state context and Read/Write operations 36 | 37 | // initBlock is the cSHAKE specific initialization set of bytes. It is initialized 38 | // by newCShake function and stores concatenation of N followed by S, encoded 39 | // by the method specified in 3.3 of [1] and padded with bytepad function. 40 | // Used by Reset() to restore initial state. 41 | initBlock []byte 42 | } 43 | 44 | // Consts for configuring initial SHA-3 state 45 | const ( 46 | sfxShake = 0x1f 47 | sfxCShake = 0x04 48 | rate128 = 168 49 | rate256 = 136 50 | ) 51 | 52 | func bytepad(input []byte, w int) []byte { 53 | // leftEncode always returns max 9 bytes 54 | buf := make([]byte, 0, 9+len(input)+w) 55 | buf = append(buf, leftEncode(uint64(w))...) 56 | buf = append(buf, input...) 57 | padlen := w - (len(buf) % w) 58 | return append(buf, make([]byte, padlen)...) 59 | } 60 | 61 | func leftEncode(value uint64) []byte { 62 | var b [9]byte 63 | binary.BigEndian.PutUint64(b[1:], value) 64 | // Trim all but last leading zero bytes 65 | i := byte(1) 66 | for i < 8 && b[i] == 0 { 67 | i++ 68 | } 69 | // Prepend number of encoded bytes 70 | b[i-1] = 9 - i 71 | return b[i-1:] 72 | } 73 | 74 | func newCShake(N, S []byte, sfx byte, shaId uint8) ShakeHash { 75 | c := cshakeState{state: state{sfx: sfx, desc: Sha3Desc[shaId]}} 76 | 77 | // leftEncode returns max 9 bytes 78 | b := make([]byte, 0, 9*2+len(N)+len(S)) 79 | b = append(b, leftEncode(uint64(len(N)*8))...) 80 | b = append(b, N...) 81 | b = append(b, leftEncode(uint64(len(S)*8))...) 82 | b = append(b, S...) 83 | c.initBlock = bytepad(b, c.BlockSize()) 84 | c.Write(c.initBlock) 85 | return &c 86 | } 87 | 88 | // Reset resets the hash to initial state. 89 | func (c *cshakeState) Reset() { 90 | c.state.Reset() 91 | c.Write(c.initBlock) 92 | } 93 | 94 | // Clone returns copy of a cSHAKE context within its current state. 95 | func (c *cshakeState) Clone() ShakeHash { 96 | b := make([]byte, len(c.initBlock)) 97 | copy(b, c.initBlock) 98 | return &cshakeState{state: c.state, initBlock: b} 99 | } 100 | 101 | // Clone returns copy of SHAKE context within its current state. 102 | func (c *state) Clone() ShakeHash { 103 | dup := *c 104 | return &dup 105 | } 106 | 107 | // NewShake128 creates a new SHAKE128 variable-output-length ShakeHash. 108 | // Its generic security strength is 128 bits against all attacks if at 109 | // least 32 bytes of its output are used. 110 | func NewShake128() ShakeHash { 111 | return &state{sfx: sfxShake, desc: Sha3Desc[SHAKE128]} 112 | } 113 | 114 | // NewShake256 creates a new SHAKE256 variable-output-length ShakeHash. 115 | // Its generic security strength is 256 bits against all attacks if 116 | // at least 64 bytes of its output are used. 117 | func NewShake256() ShakeHash { 118 | return &state{sfx: sfxShake, desc: Sha3Desc[SHAKE256]} 119 | } 120 | 121 | // NewCShake128 creates a new instance of cSHAKE128 variable-output-length ShakeHash, 122 | // a customizable variant of SHAKE128. 123 | // N is used to define functions based on cSHAKE, it can be empty when plain cSHAKE is 124 | // desired. S is a customization byte string used for domain separation - two cSHAKE 125 | // computations on same input with different S yield unrelated outputs. 126 | // When N and S are both empty, this is equivalent to NewShake128. 127 | func NewCShake128(N, S []byte) ShakeHash { 128 | if len(N) == 0 && len(S) == 0 { 129 | return NewShake128() 130 | } 131 | return newCShake(N, S, sfxCShake, SHAKE128) 132 | } 133 | 134 | // NewCShake256 creates a new instance of cSHAKE256 variable-output-length ShakeHash, 135 | // a customizable variant of SHAKE256. 136 | // N is used to define functions based on cSHAKE, it can be empty when plain cSHAKE is 137 | // desired. S is a customization byte string used for domain separation - two cSHAKE 138 | // computations on same input with different S yield unrelated outputs. 139 | // When N and S are both empty, this is equivalent to NewShake256. 140 | func NewCShake256(N, S []byte) ShakeHash { 141 | if len(N) == 0 && len(S) == 0 { 142 | return NewShake256() 143 | } 144 | return newCShake(N, S, sfxCShake, SHAKE256) 145 | } 146 | 147 | // ShakeSum128 writes an arbitrary-length digest of data into hash. 148 | func ShakeSum128(hash, data []byte) { 149 | h := NewShake128() 150 | h.Write(data) 151 | h.Read(hash) 152 | } 153 | 154 | // ShakeSum256 writes an arbitrary-length digest of data into hash. 155 | func ShakeSum256(hash, data []byte) { 156 | h := NewShake256() 157 | h.Write(data) 158 | h.Read(hash) 159 | } 160 | -------------------------------------------------------------------------------- /hash/sha3/testdata/keccakKats.json.deflate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriskwiatkowski/nobs/25b66236df735a1717f18dbc348c87dd288ad5f5/hash/sha3/testdata/keccakKats.json.deflate -------------------------------------------------------------------------------- /hash/sha3/xor.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !amd64,!386,!ppc64le appengine 6 | 7 | package sha3 8 | 9 | // A storageBuf is an aligned array of maxRate bytes. 10 | type storageBuf [maxRate]byte 11 | 12 | func (b *storageBuf) asBytes() *[maxRate]byte { 13 | return (*[maxRate]byte)(b) 14 | } 15 | 16 | var ( 17 | xorIn = xorInGeneric 18 | copyOut = copyOutGeneric 19 | xorInUnaligned = xorInGeneric 20 | copyOutUnaligned = copyOutGeneric 21 | ) 22 | 23 | const xorImplementationUnaligned = "generic" 24 | -------------------------------------------------------------------------------- /hash/sha3/xor_generic.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sha3 6 | 7 | import "encoding/binary" 8 | 9 | // xorInGeneric xors the bytes in buf into the state; it 10 | // makes no non-portable assumptions about memory layout 11 | // or alignment. 12 | func xorInGeneric(d *state, buf []byte) { 13 | n := len(buf) / 8 14 | 15 | for i := 0; i < n; i++ { 16 | a := binary.LittleEndian.Uint64(buf) 17 | d.a[i] ^= a 18 | buf = buf[8:] 19 | } 20 | } 21 | 22 | // copyOutGeneric copies ulint64s to a byte buffer. 23 | func copyOutGeneric(d *state, b []byte) { 24 | for i := 0; len(b) >= 8; i++ { 25 | binary.LittleEndian.PutUint64(b, d.a[i]) 26 | b = b[8:] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /hash/sha3/xor_unaligned.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build amd64 386 ppc64le 6 | // +build !appengine 7 | 8 | package sha3 9 | 10 | import ( 11 | "unsafe" 12 | ) 13 | 14 | // A storageBuf is an aligned array of maxRate bytes. 15 | type storageBuf [maxRate / 8]uint64 16 | 17 | func (b *storageBuf) asBytes() *[maxRate]byte { 18 | return (*[maxRate]byte)(unsafe.Pointer(b)) 19 | } 20 | 21 | func xorInUnaligned(d *state, buf []byte) { 22 | n := len(buf) 23 | bw := (*[maxRate / 8]uint64)(unsafe.Pointer(&buf[0]))[: n/8 : n/8] 24 | if n >= 72 { 25 | d.a[0] ^= bw[0] 26 | d.a[1] ^= bw[1] 27 | d.a[2] ^= bw[2] 28 | d.a[3] ^= bw[3] 29 | d.a[4] ^= bw[4] 30 | d.a[5] ^= bw[5] 31 | d.a[6] ^= bw[6] 32 | d.a[7] ^= bw[7] 33 | d.a[8] ^= bw[8] 34 | } 35 | if n >= 104 { 36 | d.a[9] ^= bw[9] 37 | d.a[10] ^= bw[10] 38 | d.a[11] ^= bw[11] 39 | d.a[12] ^= bw[12] 40 | } 41 | if n >= 136 { 42 | d.a[13] ^= bw[13] 43 | d.a[14] ^= bw[14] 44 | d.a[15] ^= bw[15] 45 | d.a[16] ^= bw[16] 46 | } 47 | if n >= 144 { 48 | d.a[17] ^= bw[17] 49 | } 50 | if n >= 168 { 51 | d.a[18] ^= bw[18] 52 | d.a[19] ^= bw[19] 53 | d.a[20] ^= bw[20] 54 | } 55 | } 56 | 57 | func copyOutUnaligned(d *state, buf []byte) { 58 | ab := (*[maxRate]uint8)(unsafe.Pointer(&d.a[0])) 59 | copy(buf, ab[:]) 60 | } 61 | 62 | // TODO: remove this assignment 63 | var ( 64 | xorIn = xorInUnaligned 65 | copyOut = copyOutUnaligned 66 | ) 67 | 68 | const xorImplementationUnaligned = "unaligned" 69 | -------------------------------------------------------------------------------- /hash/sm3/sm3.go: -------------------------------------------------------------------------------- 1 | // Package sm3 implements the SM-3 hash algorithm as defined in "SM3 Hash 2 | // function draft-shen-sm3-hash-01" draft 3 | // 4 | package sm3 5 | 6 | import ( 7 | "hash" 8 | ) 9 | 10 | const ( 11 | init0 = 0x7380166F 12 | init1 = 0x4914B2B9 13 | init2 = 0x172442D7 14 | init3 = 0xDA8A0600 15 | init4 = 0xA96F30BC 16 | init5 = 0x163138AA 17 | init6 = 0xE38DEE4D 18 | init7 = 0xB0FB0E4E 19 | ) 20 | 21 | // The size of a SM-3 checksum in bytes. 22 | const Size = 32 23 | 24 | // The blocksize of SM-3 in bytes. 25 | const BlockSize int = 64 26 | 27 | // digest represents the partial evaluation of a checksum. 28 | type digest struct { 29 | h [8]uint32 30 | len uint64 31 | b [BlockSize]byte 32 | } 33 | 34 | func New() hash.Hash { 35 | d := new(digest) 36 | d.Reset() 37 | return d 38 | } 39 | 40 | func (d *digest) BlockSize() int { return BlockSize } 41 | 42 | func (d *digest) Size() int { return Size } 43 | 44 | func (d *digest) Init() { d.Reset() } 45 | 46 | func (d *digest) Reset() { 47 | d.h[0] = init0 48 | d.h[1] = init1 49 | d.h[2] = init2 50 | d.h[3] = init3 51 | d.h[4] = init4 52 | d.h[5] = init5 53 | d.h[6] = init6 54 | d.h[7] = init7 55 | d.len = 0 56 | } 57 | 58 | func (d *digest) Write(input []byte) (nn int, err error) { 59 | 60 | // current possition in the buffer 61 | idx := int(d.len & uint64((d.BlockSize() - 1))) 62 | d.len += uint64(len(input)) 63 | 64 | if len(input)+idx < d.BlockSize() { 65 | copy(d.b[idx:], input) 66 | return 67 | } 68 | 69 | c := d.BlockSize() - idx 70 | copy(d.b[idx:], input[:c]) 71 | d.compress(d.b[:], 1) 72 | 73 | input = input[c:] 74 | nblocks := int(len(input) / d.BlockSize()) 75 | d.compress(input[:], nblocks) 76 | 77 | // this eventually could be done in d.compress 78 | copy(d.b[:], input[nblocks*d.BlockSize():]) 79 | return len(input), nil 80 | } 81 | 82 | func (d *digest) Sum(in []byte) []byte { 83 | var output [32]byte 84 | 85 | // Copy context so that caller can keep updating 86 | dc := *d 87 | 88 | dc.Write(in) 89 | 90 | idx := int(dc.len & uint64(dc.BlockSize()-1)) 91 | for i := idx + 1; i < len(dc.b); i++ { 92 | dc.b[i] = 0 93 | } 94 | dc.b[idx] = 0x80 95 | if idx >= 56 { 96 | dc.compress(dc.b[:], 1) 97 | for i := range dc.b { 98 | dc.b[i] = 0 99 | } 100 | } 101 | 102 | // add total bits 103 | store64Be(dc.b[56:], dc.len*8) 104 | 105 | dc.compress(dc.b[:], 1) 106 | for i := 0; i < Size/4; i++ { 107 | store32Be(output[4*i:], dc.h[i]) 108 | } 109 | return output[:] 110 | } 111 | -------------------------------------------------------------------------------- /kem/mkem/Makefile: -------------------------------------------------------------------------------- 1 | all: run-cycles 2 | 3 | GO=go 4 | PARAMS= 5 | 6 | test: 7 | $(GO) test -run=. 8 | 9 | ns: 10 | $(GO) test -c 11 | 12 | cycles: 13 | $(GO) build cmd/bench.go 14 | 15 | run-ns: ns 16 | ./mkem.test -test.run="notest" -test.bench=BenchmarkMultiEncaps -test.cpu=1 ${PARAMS} 17 | ./mkem.test -test.run="notest" -test.bench=BenchmarkEncaps -test.cpu=1 ${PARAMS} 18 | ./mkem.test -test.run="notest" -test.bench=BenchmarkEncrypt_CSIDH_p512 -test.cpu=1 ${PARAMS} 19 | ./mkem.test -test.run="notest" -test.bench=BenchmarkMultiEncrypt_CSIDH_100keys -test.cpu=1 ${PARAMS} 20 | 21 | run-cycles: cycles 22 | ./bench 23 | 24 | run: cycles ns run-cycles run-ns 25 | 26 | .PHONY: ns test run-cycles run-ns run 27 | -------------------------------------------------------------------------------- /kem/mkem/README.md: -------------------------------------------------------------------------------- 1 | # mKEM for cSIDH/p512 2 | 3 | Implementation of multi-KEM for SIKE and multi-PKE for cSIDH. 4 | 5 | ## Implementation 6 | 7 | The implementation is done in Go. Compilation requires go 1.12 or newer to compile with ``GO111MODULE=on``. Implementation is based on cSIDH and SIDH from NOBS NOBS [library](github.com/henrydcase/nobs). 8 | 9 | ## Running and benchmarking 10 | 11 | To run all benchmarks use following command 12 | ``` 13 | make run 14 | ``` 15 | 16 | It is possible to run only subset of benchmarks. The command ``make run-cycles`` will calculate CPU cycles for encryption/encapsulation to N users. The command ``make run-ns`` will produce results in nanoseconds. 17 | 18 | 19 | ### Results 20 | Benchmarks has been run on i7-8665U (Whiskey Lake) @ 1.90GHz 21 | 22 | * CPU cycle count 23 | 24 | ``` 25 | Test name: | Cycle count: 26 | --------------------------|-------------- 27 | bench_SIKEp434_KEM | 1720710678 28 | bench_SIKEp503_KEM | 2411750152 29 | bench_SIKEp751_KEM | 7225841287 30 | bench_SIKEp434_mKEM | 783353356 31 | bench_SIKEp503_mKEM | 1100170053 32 | bench_SIKEp751_mKEM | 3304027422 33 | bench_CSIDH_PKE | 38200232832 34 | bench_CSIDH_mPKE | 19203013803 35 | ``` 36 | 37 | * Time in ns 38 | 39 | ``` 40 | ./mkem.test -test.run="notest" -test.bench=BenchmarkMultiEncaps -test.cpu=1 41 | 42 | BenchmarkMultiEncaps_100keys/P-434 3 357838360 ns/op 43 | BenchmarkMultiEncaps_100keys/P-503 2 503749852 ns/op 44 | BenchmarkMultiEncaps_100keys/P-751 1 1514791804 ns/op 45 | 46 | 47 | ./mkem.test -test.run="notest" -test.bench=BenchmarkEncaps -test.cpu=1 48 | BenchmarkEncaps/P-434 151 7888643 ns/op 49 | BenchmarkEncaps/P-503 100 11150691 ns/op 50 | BenchmarkEncaps/P-751 36 34494941 ns/op 51 | 52 | ./mkem.test -test.run="notest" -test.bench=BenchmarkEncrypt_CSIDH_p512 -test.cpu=1 53 | BenchmarkEncrypt_CSIDH_p512 7 185828599 ns/op 54 | 55 | ./mkem.test -test.run="notest" -test.bench=BenchmarkMultiEncrypt_CSIDH_100keys -test.cpu=1 56 | BenchmarkMultiEncrypt_CSIDH_100keys 1 1025902914 ns/op 57 | ``` 58 | 59 | ### Paper 60 | Details of this construction has been presented at [ASIACRYPT 2020](https://www.youtube.com/watch?v=0ijRmXt01Ww) and are described in the [ia.cr/2020/1107](ia.cr/2020/1107) 61 | -------------------------------------------------------------------------------- /kem/mkem/cmd/bench.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | "mkem" 7 | 8 | "github.com/dterei/gotsc" 9 | "github.com/henrydcase/nobs/dh/csidh" 10 | "github.com/henrydcase/nobs/dh/sidh" 11 | "github.com/henrydcase/nobs/dh/sidh/common" 12 | "github.com/henrydcase/nobs/drbg" 13 | ) 14 | 15 | const ( 16 | // number of recipients in mKEM (number of public keys created in init) 17 | nRecipients = 10 18 | // number of loops in bench function. each loop creates nRecipients ciphertexts 19 | nLoops = 3 20 | ) 21 | 22 | type benchFunc func(int) 23 | 24 | var sPKE mkem.PKE 25 | var mPKE mkem.MultiPKE 26 | var KEMp434 sidh.KEM 27 | var KEMp503 sidh.KEM 28 | var KEMp751 sidh.KEM 29 | var mKEMp434 mkem.MultiKEM 30 | var mKEMp503 mkem.MultiKEM 31 | var mKEMp751 mkem.MultiKEM 32 | 33 | var testSKS_csidh []csidh.PrivateKey 34 | var testPKS_csidh []csidh.PublicKey 35 | var testPKS_SIDHp434 []*sidh.PublicKey 36 | var testPKS_SIDHp503 []*sidh.PublicKey 37 | var testPKS_SIDHp751 []*sidh.PublicKey 38 | 39 | var MessgaeTest [16]byte 40 | var ct [common.MaxCiphertextBsz]byte 41 | var ss [common.MaxSharedSecretBsz]byte 42 | 43 | var rng *drbg.CtrDrbg 44 | 45 | func init() { 46 | var tmp [32]byte 47 | 48 | rand.Read(tmp[:]) 49 | rng = drbg.NewCtrDrbg() 50 | if !rng.Init(tmp[:], nil) { 51 | panic("Can't initialize DRBG") 52 | } 53 | 54 | sPKE.Allocate(rng) 55 | mPKE.Allocate(nRecipients, rng) 56 | 57 | testSKS_csidh = make([]csidh.PrivateKey, len(mPKE.Cts)) 58 | testPKS_csidh = make([]csidh.PublicKey, len(mPKE.Cts)) 59 | 60 | for i, _ := range mPKE.Cts { 61 | csidh.GeneratePrivateKey(&testSKS_csidh[i], mPKE.Rng) 62 | csidh.GeneratePublicKey(&testPKS_csidh[i], &testSKS_csidh[i], mPKE.Rng) 63 | } 64 | 65 | // create public keys for SIKE 66 | init_sike(common.Fp434, &testPKS_SIDHp434, &KEMp434) 67 | init_sike(common.Fp503, &testPKS_SIDHp503, &KEMp503) 68 | init_sike(common.Fp751, &testPKS_SIDHp751, &KEMp751) 69 | 70 | mKEMp434.Allocate(common.Fp434, nRecipients, rng) 71 | mKEMp503.Allocate(common.Fp503, nRecipients, rng) 72 | mKEMp751.Allocate(common.Fp751, nRecipients, rng) 73 | } 74 | 75 | func init_sike(id uint8, pks *[]*sidh.PublicKey, kem *sidh.KEM) { 76 | kem.Allocate(id, rng) 77 | sks := make([]*sidh.PrivateKey, nRecipients) 78 | *pks = make([]*sidh.PublicKey, nRecipients) 79 | for i := 0; i < nRecipients; i++ { 80 | sks[i] = sidh.NewPrivateKey(id, sidh.KeyVariantSike) 81 | (*pks)[i] = sidh.NewPublicKey(id, sidh.KeyVariantSike) 82 | _ = sks[i].Generate(rng) 83 | sks[i].GeneratePublicKey((*pks)[i]) 84 | } 85 | } 86 | 87 | func bench_CSIDH_PKE(n int) { 88 | for i := 0; i < n*nRecipients; i++ { 89 | _ = sPKE.Enc(&testPKS_csidh[i%nRecipients], &MessgaeTest) 90 | } 91 | } 92 | 93 | func bench_CSIDH_mPKE(n int) { 94 | for i := 0; i < n; i++ { 95 | mPKE.Encrypt(testPKS_csidh[:], &MessgaeTest) 96 | } 97 | } 98 | 99 | func bench_SIKE_KEM(KEM *sidh.KEM, pkeys []*sidh.PublicKey, n int) { 100 | for i := 0; i < n*nRecipients; i++ { 101 | _ = KEM.Encapsulate(ct[:], ss[:], pkeys[i%nRecipients]) 102 | } 103 | } 104 | 105 | func bench_SIKE_mKEM(KEM *mkem.MultiKEM, pkeys []*sidh.PublicKey, n int) { 106 | for i := 0; i < n; i++ { 107 | _ = KEM.Encapsulate(ss[:], pkeys) 108 | } 109 | } 110 | 111 | // runs FUT and prints out average cycle count for encryption to N users 112 | func count_cycles(test_name string, f benchFunc, n int) { 113 | tsc := gotsc.TSCOverhead() 114 | t0 := gotsc.BenchStart() 115 | f(n) 116 | t1 := gotsc.BenchEnd() 117 | avg := (t1 - t0 - tsc) / uint64(n) 118 | fmt.Println(test_name+" |", avg) 119 | } 120 | 121 | type alg struct { 122 | name string 123 | f benchFunc 124 | } 125 | 126 | func main() { 127 | 128 | bench_SIKEp434_KEM := func(n int) { bench_SIKE_KEM(&KEMp434, testPKS_SIDHp434, n) } 129 | 130 | bench_SIKEp434_mKEM := func(n int) { bench_SIKE_mKEM(&mKEMp434, testPKS_SIDHp434, n) } 131 | 132 | fmt.Println("Test name: | Cycle count:") 133 | fmt.Println("--------------------------|--------------") 134 | algs := []struct { 135 | name string 136 | f benchFunc 137 | }{ 138 | {"bench_SIKEp434_KEM ", bench_SIKEp434_KEM}, 139 | {"bench_SIKEp434_mKEM ", bench_SIKEp434_mKEM}, 140 | {"bench_CSIDH_PKE ", bench_CSIDH_PKE}, 141 | {"bench_CSIDH_mPKE ", bench_CSIDH_mPKE}, 142 | } 143 | 144 | // run all algorithms and count CPU cycles for each invocation 145 | for i := 0; i < len(algs); i++ { 146 | count_cycles(algs[i].name, algs[i].f, nLoops) 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /kem/mkem/csidh.go: -------------------------------------------------------------------------------- 1 | package mkem 2 | 3 | import ( 4 | "github.com/henrydcase/nobs/dh/csidh" 5 | "github.com/henrydcase/nobs/drbg" 6 | "github.com/henrydcase/nobs/hash/sha3" 7 | ) 8 | 9 | const ( 10 | SharedSecretSz = 64 11 | PublicKeySz = 64 12 | ) 13 | 14 | // Used for storing cipertext 15 | type ciphertext struct { 16 | // public key 17 | U [64]byte 18 | // private key 19 | V [64]byte 20 | } 21 | 22 | type PKE struct { 23 | Rng *drbg.CtrDrbg 24 | H sha3.ShakeHash 25 | } 26 | 27 | type MultiPKE struct { 28 | PKE 29 | // stores ephemeral/internal public key 30 | Ct0 [PublicKeySz]byte 31 | // stores list of ciphertexts ct[i] 32 | Cts [][SharedSecretSz]byte 33 | } 34 | 35 | // Allocates PKE 36 | func (c *PKE) Allocate(rng *drbg.CtrDrbg) { 37 | c.Rng = rng 38 | 39 | // Function H used in Algorithm 16 and 18 40 | c.H = sha3.NewShake128() 41 | } 42 | 43 | // Allocates MultiPKE 44 | func (c *MultiPKE) Allocate(recipients_nb uint, rng *drbg.CtrDrbg) { 45 | c.PKE.Allocate(rng) 46 | c.Cts = make([][SharedSecretSz]byte, recipients_nb) 47 | } 48 | 49 | // PKE encryption 50 | func (c *PKE) Enc(pk *csidh.PublicKey, pt *[16]byte) (ct ciphertext) { 51 | var ss [64]byte 52 | var pkA csidh.PublicKey 53 | var skA csidh.PrivateKey 54 | 55 | csidh.GeneratePrivateKey(&skA, c.Rng) 56 | csidh.DeriveSecret(&ss, pk, &skA, c.Rng) 57 | 58 | c.H.Reset() 59 | c.H.Write(ss[:]) 60 | c.H.Read(ss[:16]) 61 | for i := 0; i < 16; i++ { 62 | ct.V[i] = pt[i] ^ ss[i] 63 | } 64 | 65 | csidh.GeneratePublicKey(&pkA, &skA, c.Rng) 66 | pkA.Export(ct.U[:]) 67 | return 68 | } 69 | 70 | // PKE decryption 71 | func (c *PKE) Dec(sk *csidh.PrivateKey, ct *ciphertext) (pt [16]byte) { 72 | var ss [64]byte 73 | var pk csidh.PublicKey 74 | 75 | pk.Import(ct.U[:]) 76 | csidh.DeriveSecret(&ss, &pk, sk, c.Rng) 77 | 78 | c.H.Reset() 79 | c.H.Write(ss[:]) 80 | c.H.Read(ss[:16]) 81 | for i := 0; i < 16; i++ { 82 | pt[i] = ct.V[i] ^ ss[i] 83 | } 84 | return 85 | } 86 | 87 | // mPKE encryption 88 | func (c *MultiPKE) Encrypt(keys []csidh.PublicKey, pt *[16]byte) { 89 | var ss [64]byte 90 | var pkA csidh.PublicKey 91 | var skA csidh.PrivateKey 92 | 93 | csidh.GeneratePrivateKey(&skA, c.Rng) 94 | for i, pk := range keys { 95 | csidh.DeriveSecret(&ss, &pk, &skA, c.Rng) 96 | 97 | c.H.Write(ss[:]) 98 | c.H.Read(ss[:16]) 99 | c.H.Reset() 100 | for j := 0; j < 16; j++ { 101 | c.Cts[i][j] = pt[j] ^ ss[j] 102 | } 103 | } 104 | 105 | csidh.GeneratePublicKey(&pkA, &skA, c.Rng) 106 | pkA.Export(c.Ct0[:]) 107 | return 108 | } 109 | -------------------------------------------------------------------------------- /kem/mkem/csidh_test.go: -------------------------------------------------------------------------------- 1 | package mkem 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "testing" 7 | 8 | "github.com/henrydcase/nobs/dh/csidh" 9 | "github.com/henrydcase/nobs/drbg" 10 | ) 11 | 12 | var sPKE PKE 13 | var mPKE MultiPKE 14 | 15 | var testSKS []csidh.PrivateKey 16 | var testPKS []csidh.PublicKey 17 | 18 | // helper 19 | func Ok(t testing.TB, f bool, msg string) { 20 | t.Helper() 21 | if !f { 22 | t.Error(msg) 23 | } 24 | } 25 | 26 | func init() { 27 | var tmp [32]byte 28 | var rng *drbg.CtrDrbg 29 | 30 | rand.Read(tmp[:]) 31 | rng = drbg.NewCtrDrbg() 32 | if !rng.Init(tmp[:], nil) { 33 | panic("Can't initialize DRBG") 34 | } 35 | 36 | sPKE.Allocate(rng) 37 | mPKE.Allocate(10, rng) 38 | 39 | testSKS = make([]csidh.PrivateKey, len(mPKE.Cts)) 40 | testPKS = make([]csidh.PublicKey, len(mPKE.Cts)) 41 | 42 | for i, _ := range mPKE.Cts { 43 | csidh.GeneratePrivateKey(&testSKS[i], mPKE.Rng) 44 | csidh.GeneratePublicKey(&testPKS[i], &testSKS[i], mPKE.Rng) 45 | } 46 | 47 | } 48 | 49 | func getCiphertext(ct *ciphertext, mPKE *MultiPKE, i int) { 50 | copy(ct.U[:], mPKE.Ct0[:]) 51 | copy(ct.V[:], mPKE.Cts[i][:]) 52 | } 53 | 54 | func TestSinglePKE(t *testing.T) { 55 | var pk csidh.PublicKey 56 | var sk csidh.PrivateKey 57 | 58 | csidh.GeneratePrivateKey(&sk, sPKE.Rng) 59 | csidh.GeneratePublicKey(&pk, &sk, sPKE.Rng) 60 | 61 | var msg [16]byte 62 | ct := sPKE.Enc(&pk, &msg) 63 | pt := sPKE.Dec(&sk, &ct) 64 | Ok(t, bytes.Equal(pt[:], msg[:]), "Decryption failed") 65 | 66 | // Do it twice to ensure it works with same key pair 67 | ct = sPKE.Enc(&pk, &msg) 68 | pt = sPKE.Dec(&sk, &ct) 69 | Ok(t, bytes.Equal(pt[:], msg[:]), 70 | "Decryption failed") 71 | 72 | } 73 | 74 | func TestMultiPKE(t *testing.T) { 75 | var msg [16]byte 76 | var ct ciphertext 77 | 78 | pks := make([]csidh.PublicKey, len(mPKE.Cts)) 79 | sks := make([]csidh.PrivateKey, len(mPKE.Cts)) 80 | 81 | // mct.Cts = make([][SharedSecretSz]byte) 82 | 83 | for i, _ := range mPKE.Cts { 84 | csidh.GeneratePrivateKey(&sks[i], mPKE.Rng) 85 | csidh.GeneratePublicKey(&pks[i], &sks[i], mPKE.Rng) 86 | } 87 | 88 | mPKE.Encrypt(pks[:], &msg) 89 | for i := 0; i < len(mPKE.Cts); i++ { 90 | getCiphertext(&ct, &mPKE, i) 91 | pt := sPKE.Dec(&sks[i], &ct) 92 | Ok(t, bytes.Equal(pt[:], msg[:]), 93 | "Multi decryption failed") 94 | } 95 | } 96 | 97 | var MessgaeTest [16]byte 98 | 99 | func BenchmarkEncrypt_CSIDH_p512(b *testing.B) { 100 | for i := 0; i < b.N; i++ { 101 | _ = sPKE.Enc(&testPKS[0], &MessgaeTest) 102 | } 103 | } 104 | 105 | func BenchmarkMultiEncrypt_CSIDH_100keys(b *testing.B) { 106 | for i := 0; i < b.N; i++ { 107 | mPKE.Encrypt(testPKS[:], &MessgaeTest) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /kem/mkem/go.mod: -------------------------------------------------------------------------------- 1 | module mkem 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/dterei/gotsc v0.0.0-20160722215413-e78f872945c6 7 | github.com/henrydcase/nobs v0.0.0-20200516223741-2500d74484f2 8 | golang.org/x/sys v0.1.0 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /utils/cpu.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | type x86 struct { 4 | // Signals support for MULX which is in BMI2 5 | HasBMI2 bool 6 | 7 | // Signals support for ADX 8 | HasADX bool 9 | 10 | // Signals support for AES 11 | HasAES bool 12 | 13 | // Signals support for RDSEED 14 | HasRDSEED bool 15 | } 16 | 17 | var X86 x86 18 | -------------------------------------------------------------------------------- /utils/cpuid_amd64.go: -------------------------------------------------------------------------------- 1 | // +build amd64,!noasm 2 | 3 | // Sets capabilities flags for x86 according to information received from 4 | // CPUID. It was written in accordance with 5 | // "Intel® 64 and IA-32 Architectures Developer's Manual: Vol. 2A". 6 | // https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-2a-manual.html 7 | 8 | package utils 9 | 10 | // Performs CPUID and returns values of registers 11 | // go:nosplit 12 | func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) 13 | 14 | // Returns true in case bit 'n' in 'bits' is set, otherwise false 15 | func bitn(bits uint32, n uint8) bool { 16 | return (bits>>n)&1 == 1 17 | } 18 | 19 | func init() { 20 | // CPUID returns max possible input that can be requested 21 | max, _, _, _ := cpuid(0, 0) 22 | if max < 7 { 23 | return 24 | } 25 | 26 | _, ecx, _, _ := cpuid(1, 0) 27 | X86.HasAES = bitn(ecx, 25) 28 | 29 | _, ecx, _, _ = cpuid(7, 0) 30 | X86.HasBMI2 = bitn(ecx, 8) 31 | X86.HasADX = bitn(ecx, 19) 32 | X86.HasRDSEED = bitn(ecx, 18) 33 | } 34 | -------------------------------------------------------------------------------- /utils/cpuid_amd64.s: -------------------------------------------------------------------------------- 1 | // +build amd64,!noasm 2 | 3 | #include "textflag.h" 4 | 5 | TEXT ·cpuid(SB), NOSPLIT, $0-4 6 | MOVL eaxArg+0(FP), AX 7 | MOVL ecxArg+4(FP), CX 8 | CPUID 9 | MOVL AX, eax+8(FP) 10 | MOVL BX, ebx+12(FP) 11 | MOVL CX, ecx+16(FP) 12 | MOVL DX, edx+20(FP) 13 | RET 14 | --------------------------------------------------------------------------------