├── .github └── workflows │ ├── archive.yml │ ├── ghpages.yml │ └── publish.yml ├── .gitignore ├── .gitmodules ├── .note.xml ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE.md ├── Makefile ├── README.md ├── draft-irtf-cfrg-voprf.md ├── poc ├── .gitignore ├── Makefile ├── groups.sage ├── oprf.sage ├── ristretto_decaf.sage ├── test_drng.sage ├── test_oprf.sage └── vectors │ └── allVectors.json └── releases ├── draft-irtf-cfrg-voprf-01.txt ├── draft-irtf-cfrg-voprf-03.txt ├── draft-sullivan-cfrg-voprf-00.txt └── draft-sullivan-cfrg-voprf-03.txt /.github/workflows/archive.yml: -------------------------------------------------------------------------------- 1 | name: "Archive Issues and Pull Requests" 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * 0,2,4' 6 | repository_dispatch: 7 | types: [archive] 8 | 9 | jobs: 10 | build: 11 | name: "Archive Issues and Pull Requests" 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: "Checkout" 15 | uses: actions/checkout@v2 16 | 17 | - name: "Update Archive" 18 | uses: martinthomson/i-d-template@v1 19 | with: 20 | make: archive 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | 24 | - name: "Update GitHub Pages" 25 | uses: martinthomson/i-d-template@v1 26 | with: 27 | make: gh-archive 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | 31 | - name: "Save Archive" 32 | uses: actions/upload-artifact@v2 33 | with: 34 | path: archive.json 35 | -------------------------------------------------------------------------------- /.github/workflows/ghpages.yml: -------------------------------------------------------------------------------- 1 | name: "Update Editor's Copy" 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - README.md 7 | - CONTRIBUTING.md 8 | - LICENSE.md 9 | - .gitignore 10 | pull_request: 11 | paths-ignore: 12 | - README.md 13 | - CONTRIBUTING.md 14 | - LICENSE.md 15 | - .gitignore 16 | 17 | jobs: 18 | build: 19 | name: "Update Editor's Copy" 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: "Checkout" 23 | uses: actions/checkout@v2 24 | 25 | - name: "Cache Setup" 26 | id: cache-setup 27 | run: | 28 | mkdir -p "$HOME"/.cache/xml2rfc 29 | echo "::set-output name=path::$HOME/.cache/xml2rfc" 30 | date -u "+::set-output name=date::%FT%T" 31 | 32 | - name: "Cache References" 33 | uses: actions/cache@v2 34 | with: 35 | path: ${{ steps.cache-setup.outputs.path }} 36 | key: refcache-${{ steps.cache-setup.outputs.date }} 37 | restore-keys: | 38 | refcache-${{ steps.cache-setup.outputs.date }} 39 | refcache- 40 | 41 | - name: "Build Drafts" 42 | uses: martinthomson/i-d-template@v1 43 | 44 | - name: "Update GitHub Pages" 45 | uses: martinthomson/i-d-template@v1 46 | if: ${{ github.event_name == 'push' }} 47 | with: 48 | make: gh-pages 49 | env: 50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 51 | 52 | - name: "Save HTML" 53 | uses: actions/upload-artifact@v2 54 | with: 55 | path: "*.html" 56 | 57 | - name: "Save Text" 58 | uses: actions/upload-artifact@v2 59 | with: 60 | path: "*.txt" 61 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: "Publish New Draft Version" 2 | 3 | on: 4 | push: 5 | tags: 6 | - "draft-*" 7 | 8 | jobs: 9 | build: 10 | name: "Publish New Draft Version" 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: "Checkout" 14 | uses: actions/checkout@v2 15 | 16 | # See https://github.com/actions/checkout/issues/290 17 | - name: "Get Tag Annotations" 18 | run: git fetch -f origin ${{ github.ref }}:${{ github.ref }} 19 | 20 | - name: "Cache Setup" 21 | id: cache-setup 22 | run: | 23 | mkdir -p "$HOME"/.cache/xml2rfc 24 | echo "::set-output name=path::$HOME/.cache/xml2rfc" 25 | date -u "+::set-output name=date::%FT%T" 26 | 27 | - name: "Cache References" 28 | uses: actions/cache@v2 29 | with: 30 | path: ${{ steps.cache-setup.outputs.path }} 31 | key: refcache-${{ steps.date.outputs.date }} 32 | restore-keys: | 33 | refcache-${{ steps.date.outputs.date }} 34 | refcache- 35 | 36 | - name: "Upload to Datatracker" 37 | uses: martinthomson/i-d-template@v1 38 | with: 39 | make: upload 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.pdf 3 | *.redxml 4 | *.swp 5 | *.txt 6 | *.upload 7 | *~ 8 | .refcache 9 | .tags 10 | .targets.mk 11 | /*-[0-9][0-9].xml 12 | archive.json 13 | report.xml 14 | venv/ 15 | lib 16 | draft-irtf-cfrg-voprf.xml 17 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "poc/h2c"] 2 | path = poc/h2c 3 | url = git@github.com:cfrg/draft-irtf-cfrg-hash-to-curve.git 4 | -------------------------------------------------------------------------------- /.note.xml: -------------------------------------------------------------------------------- 1 | 2 | Source for this draft and an issue tracker can be found at 3 | . 4 | 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: xenial 3 | 4 | services: 5 | - docker 6 | 7 | env: 8 | DRAFT_DIR: /home/idci/draft 9 | 10 | before_install: 11 | - docker --version 12 | - docker pull martinthomson/i-d-template 13 | 14 | script: 15 | - docker run -d -v "$PWD:/tmp/draft" --tmpfs "$DRAFT_DIR:rw,exec" --name idci 16 | martinthomson/i-d-template sleep 300 17 | - docker exec idci cp -rn /tmp/draft /home/idci 18 | - docker exec -w "$DRAFT_DIR" -e CI=true -e TRAVIS 19 | -e TRAVIS_REPO_SLUG -e TRAVIS_BRANCH -e TRAVIS_TAG -e TRAVIS_PULL_REQUEST 20 | idci make CLONE_ARGS='--reference /home/idci/git-reference' 21 | - docker exec idci ls -l /home/idci/draft/lib 22 | - if [ "${TRAVIS_TAG#draft-}" == "${TRAVIS_TAG}" ]; then 23 | docker exec -w "$DRAFT_DIR" -e CI=true -e GH_TOKEN -e TRAVIS 24 | -e TRAVIS_REPO_SLUG -e TRAVIS_BRANCH -e TRAVIS_TAG -e TRAVIS_PULL_REQUEST 25 | idci make ghpages; 26 | fi 27 | 28 | deploy: 29 | provider: script 30 | script: 31 | - docker exec -w "$DRAFT_DIR" -e CI=true -e GH_TOKEN -e TRAVIS 32 | -e TRAVIS_REPO_SLUG -e TRAVIS_BRANCH -e TRAVIS_TAG -e TRAVIS_PULL_REQUEST 33 | idci make upload 34 | skip_cleanup: true 35 | on: 36 | tags: true 37 | 38 | after_script: 39 | - docker container rm -f idci 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This repository relates to activities in the Internet Engineering Task Force 4 | ([IETF](https://www.ietf.org/)). All material in this repository is considered 5 | Contributions to the IETF Standards Process, as defined in the intellectual 6 | property policies of IETF currently designated as 7 | [BCP 78](https://www.rfc-editor.org/info/bcp78), 8 | [BCP 79](https://www.rfc-editor.org/info/bcp79) and the 9 | [IETF Trust Legal Provisions (TLP) Relating to IETF Documents](http://trustee.ietf.org/trust-legal-provisions.html). 10 | 11 | Any edit, commit, pull request, issue, comment or other change made to this 12 | repository constitutes Contributions to the IETF Standards Process 13 | (https://www.ietf.org/). 14 | 15 | You agree to comply with all applicable IETF policies and procedures, including, 16 | BCP 78, 79, the TLP, and the TLP rules regarding code components (e.g. being 17 | subject to a Simplified BSD License) in Contributions. 18 | 19 | 20 | ## Other Resources 21 | 22 | Discussion of this work occurs on the 23 | [cfrg working group mailing list](https://mailarchive.ietf.org/arch/browse/cfrg/) 24 | ([subscribe](https://www.ietf.org/mailman/listinfo/cfrg)). In addition to 25 | contributions in GitHub, you are encouraged to participate in discussions there. 26 | 27 | **Note**: Some working groups adopt a policy whereby substantive discussion of 28 | technical issues needs to occur on the mailing list. 29 | 30 | You might also like to familiarize yourself with other 31 | [working group documents](https://datatracker.ietf.org/wg/cfrg/documents/). 32 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | See the 4 | [guidelines for contributions](https://github.com/cfrg/draft-irtf-cfrg-voprf/blob/master/CONTRIBUTING.md). 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIBDIR := lib 2 | include $(LIBDIR)/main.mk 3 | 4 | $(LIBDIR)/main.mk: 5 | ifneq (,$(shell grep "path *= *$(LIBDIR)" .gitmodules 2>/dev/null)) 6 | git submodule sync 7 | git submodule update $(CLONE_ARGS) --init 8 | else 9 | git clone -q --depth 10 $(CLONE_ARGS) \ 10 | -b main https://github.com/martinthomson/i-d-template $(LIBDIR) 11 | endif 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Oblivious Pseudorandom Functions (OPRFs) using Prime-Order Groups 2 | 3 | This is the working area for the individual Internet-Draft, "Oblivious Pseudorandom Functions (OPRFs) using Prime-Order Groups". 4 | 5 | * [Editor's Copy](https://cfrg.github.io/draft-irtf-cfrg-voprf/#go.draft-irtf-cfrg-voprf.html) 6 | * [Individual Draft](https://tools.ietf.org/html/draft-irtf-cfrg-voprf) 7 | * [Compare Editor's Copy to Individual Draft](https://cfrg.github.io/draft-irtf-cfrg-voprf/#go.draft-irtf-cfrg-voprf.diff) 8 | 9 | ## Building the Draft 10 | 11 | Formatted text and HTML versions of the draft can be built using `make`. 12 | 13 | ```sh 14 | $ make 15 | ``` 16 | 17 | This requires that you have the necessary software installed. See 18 | [the instructions](https://github.com/martinthomson/i-d-template/blob/master/doc/SETUP.md). 19 | 20 | ## Existing Implementations 21 | 22 | | Implementation | Language | Version | Modes | 23 | | ------------------------------------------------------------------------- |:-----------|:---------|:-------| 24 | | [**Reference**](https://github.com/cfrg/draft-irtf-cfrg-voprf/tree/draft-irtf-cfrg-voprf-09/poc) | Sage/Python | draft-10 | All | 25 | | [oprf-ts](https://github.com/privacyresearchgroup/oprf-ts) | TypeScript | draft-07 | All | 26 | | [voprf-ts](https://github.com/cloudflare/voprf-ts) | TypeScript | draft-10 | All | 27 | | [voprf](https://github.com/bytemare/voprf) | Go | draft-10 | All | 28 | | [CIRCL](https://github.com/cloudflare/circl) | Go | draft-10 | All | 29 | | [voprf](https://github.com/novifinancial/voprf) | Rust | main | All | 30 | | [BoringSSL](https://boringssl.googlesource.com/boringssl/+/refs/heads/master/crypto/trust_token/) | C | draft-04 | All | 31 | | [ecc](https://github.com/aldenml/ecc) | C | draft-21 | All | 32 | 33 | ### Other Integrations 34 | 35 | | Implementation | Language | Version | Modes | Notes | 36 | | ------------------------------------------------------------------------- |:---------|:---------|:-------|:------| 37 | | [opaque-ke](https://github.com/novifinancial/opaque-ke/) | Rust | draft-06 | Base | As a component for OPAQUE | 38 | | [opaque](https://github.com/bytemare/opaque) | Go | draft-10 | Base | As a component for OPAQUE | 39 | | [libopaque](https://github.com/stef/libopaque) | C | draft-20 | Base | As a component for OPAQUE | 40 | 41 | Submit a PR if you have a compliant implementation! 42 | 43 | ## Contributing 44 | 45 | See the 46 | [guidelines for contributions](https://github.com/cfrg/draft-irtf-cfrg-voprf/blob/master/CONTRIBUTING.md). 47 | -------------------------------------------------------------------------------- /poc/.gitignore: -------------------------------------------------------------------------------- 1 | h2c 2 | *.sage.py 3 | __pycache__/ 4 | clear_h_bls12381g2.sage 5 | common.sage 6 | curves.sage 7 | ell2_generic.sage 8 | ell2_opt_3mod4.sage 9 | ell2_opt_5mod8.sage 10 | ell2edw_generic.sage 11 | generic_map.sage 12 | h2c_suite.sage 13 | hash_to_field.py 14 | iso_values.sage 15 | map_check.sage 16 | sagelib/ 17 | sswu_generic.sage 18 | sswu_opt_3mod4.sage 19 | sswu_opt_5mod8.sage 20 | sswu_opt_9mod16.sage 21 | suite_25519.sage 22 | suite_448.sage 23 | suite_bls12381g1.sage 24 | suite_bls12381g2.sage 25 | suite_p256.sage 26 | suite_p384.sage 27 | suite_p521.sage 28 | suite_secp256k1.sage 29 | svdw_generic.sage 30 | sqrt.sage 31 | sswu_optimized.sage 32 | test.sage 33 | test_vectors.sage 34 | z_selection.sage 35 | z_values.sage -------------------------------------------------------------------------------- /poc/Makefile: -------------------------------------------------------------------------------- 1 | SAGEFILES := $(basename $(notdir $(wildcard *.sage))) 2 | PYFILES := $(addprefix sagelib/, $(addsuffix .py,$(SAGEFILES))) 3 | .PRECIOUS: $(PYFILES) 4 | 5 | .PHONY: pyfiles 6 | pyfiles: sagelib/__init__.py $(PYFILES) 7 | 8 | sagelib/__init__.py: 9 | mkdir -p sagelib 10 | echo pass > sagelib/__init__.py 11 | 12 | sagelib/%.py: %.sage 13 | @echo "Parsing $<" 14 | @sage --preparse $< 15 | @mv $<.py $@ 16 | 17 | setup: 18 | cp h2c/poc/hash_to_field.py . 19 | cp h2c/poc/*.sage . 20 | 21 | test: pyfiles 22 | sage test_oprf.sage 23 | 24 | vectors: pyfiles 25 | @echo "Removing vectors folder, if present" 26 | @rm -rf vectors 27 | @echo "Creating vectors folder" 28 | @mkdir -p vectors 29 | sage test_oprf.sage 30 | 31 | .PHONY: clean 32 | clean: 33 | rm -rf sagelib *.pyc *.sage.py *.log __pycache__ 34 | 35 | .PHONY: distclean 36 | distclean: clean 37 | rm -rf vectors ascii -------------------------------------------------------------------------------- /poc/groups.sage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sage 2 | # vim: syntax=python 3 | 4 | import sys 5 | import hashlib 6 | import struct 7 | 8 | from hash_to_field import I2OSP, OS2IP, expand_message_xmd, expand_message_xof, XMDExpander, hash_to_field 9 | 10 | try: 11 | from sagelib.suite_p256 import p256_sswu_ro, p256_order, p256_p, p256_F, p256_A, p256_B 12 | from sagelib.suite_p384 import p384_sswu_ro, p384_order, p384_p, p384_F, p384_A, p384_B 13 | from sagelib.suite_p521 import p521_sswu_ro, p521_order, p521_p, p521_F, p521_A, p521_B 14 | from sagelib.common import sgn0 15 | from sagelib.ristretto_decaf import Ed25519Point, Ed448GoldilocksPoint 16 | except ImportError as e: 17 | sys.exit("Error loading preprocessed sage files. Try running `make setup && make clean pyfiles`. Full error: " + e) 18 | 19 | # little-endian version of I2OSP 20 | def I2OSP_le(val, length): 21 | val = int(val) 22 | if val < 0 or val >= (1 << (8 * length)): 23 | raise ValueError("bad I2OSP call: val=%d length=%d" % (val, length)) 24 | ret = [0] * length 25 | val_ = val 26 | for idx in range(0, length): 27 | ret[idx] = val_ & 0xff 28 | val_ = val_ >> 8 29 | ret = struct.pack("=" + "B" * length, *ret) 30 | assert OS2IP_le(ret, True) == val 31 | return ret 32 | 33 | # little-endian version of OS2IP 34 | def OS2IP_le(octets, skip_assert=False): 35 | ret = 0 36 | for octet in reversed(struct.unpack("=" + "B" * len(octets), octets)): 37 | ret = ret << 8 38 | ret += octet 39 | if not skip_assert: 40 | assert octets == I2OSP_le(ret, len(octets)) 41 | return ret 42 | 43 | class Group(object): 44 | def __init__(self, name): 45 | self.name = name 46 | 47 | def generator(self): 48 | raise "Not implemented" 49 | 50 | def identity(self): 51 | raise "Not implemented" 52 | 53 | def order(self): 54 | raise "Not implemented" 55 | 56 | def serialize(self, element): 57 | raise "Not implemented" 58 | 59 | def deserialize(self, encoded): 60 | raise "Not implemented" 61 | 62 | def serialize_scalar(self, scalar): 63 | raise "Not implemented" 64 | 65 | def element_byte_length(self): 66 | raise "Not implemented" 67 | 68 | def scalar_byte_length(self): 69 | raise "Not implemented" 70 | 71 | def hash_to_group(self, x): 72 | raise "Not implemented" 73 | 74 | def hash_to_scalar(self, x): 75 | raise "Not implemented" 76 | 77 | def random_scalar(self, rng): 78 | return rng.randint(1, self.order() - 1) 79 | 80 | def scalar_mult(self, x, y): 81 | raise "Not implemented" 82 | 83 | def __str__(self): 84 | return self.name 85 | 86 | class GroupNISTCurve(Group): 87 | def __init__(self, name, suite, F, A, B, p, order, gx, gy, L, H, expander, k): 88 | Group.__init__(self, name) 89 | self.F = F 90 | EC = EllipticCurve(F, [F(A), F(B)]) 91 | self.curve = EC 92 | self.gx = gx 93 | self.gy = gy 94 | self.p = p 95 | self.a = A 96 | self.b = B 97 | self.group_order = order 98 | self.h2c_suite = suite 99 | self.G = EC(F(gx), F(gy)) 100 | self.m = F.degree() 101 | self.L = L 102 | self.k = k 103 | self.H = H 104 | self.expander = expander 105 | self.field_bytes_length = int(ceil(len(self.p.bits()) / 8)) 106 | 107 | def generator(self): 108 | return self.G 109 | 110 | def order(self): 111 | return self.group_order 112 | 113 | def identity(self): 114 | return self.curve(0) 115 | 116 | def serialize(self, element): 117 | x, y = element[0], element[1] 118 | sgn = sgn0(y) 119 | byte = 2 if sgn == 0 else 3 120 | return I2OSP(byte, 1) + I2OSP(x, self.field_bytes_length) 121 | 122 | # this is using point compression 123 | def deserialize(self, encoded): 124 | # 0x02 | 0x03 || x 125 | pve = encoded[0] == 0x02 126 | nve = encoded[0] == 0x03 127 | assert(pve or nve) 128 | assert(len(encoded) % 2 != 0) 129 | element_length = (len(encoded) - 1) / 2 130 | x = OS2IP(encoded[1:]) 131 | y2 = x^3 + self.a*x + self.b 132 | y = y2.sqrt() 133 | parity = 0 if pve else 1 134 | if sgn0(y) != parity: 135 | y = -y 136 | return self.curve(self.F(x), self.F(y)) 137 | 138 | def serialize_scalar(self, scalar): 139 | return I2OSP(scalar % self.order(), self.scalar_byte_length()) 140 | 141 | def element_byte_length(self): 142 | return int(1 + self.field_bytes_length) 143 | 144 | def scalar_byte_length(self): 145 | return int(self.field_bytes_length) 146 | 147 | def hash_to_group(self, msg, dst): 148 | self.h2c_suite.expand._dst = dst 149 | return self.h2c_suite(msg) 150 | 151 | def hash_to_scalar(self, msg, dst): 152 | expander = self.expander(dst, self.H, self.k) 153 | return hash_to_field(msg, 1, self.order(), self.m, self.L, expander)[0][0] 154 | 155 | def scalar_mult(self, x, y): 156 | return x * y 157 | 158 | class GroupP256(GroupNISTCurve): 159 | def __init__(self): 160 | # See FIPS 186-3, section D.2.3 161 | gx = 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296 162 | gy = 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5 163 | GroupNISTCurve.__init__(self, "P256_XMD:SHA-256_SSWU_RO_", p256_sswu_ro, p256_F, p256_A, p256_B, p256_p, p256_order, gx, gy, 48, hashlib.sha256, XMDExpander, 128) 164 | 165 | class GroupP384(GroupNISTCurve): 166 | def __init__(self): 167 | # See FIPS 186-3, section D.2.4 168 | gx = 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7 169 | gy = 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f 170 | GroupNISTCurve.__init__(self, "P384_XMD:SHA-384_SSWU_RO_", p384_sswu_ro, p384_F, p384_A, p384_B, p384_p, p384_order, gx, gy, 72, hashlib.sha384, XMDExpander, 192) 171 | 172 | class GroupP521(GroupNISTCurve): 173 | def __init__(self): 174 | # See FIPS 186-3, section D.2.5 175 | gx = 0xc6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66 176 | gy = 0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650 177 | GroupNISTCurve.__init__(self, "P521_XMD:SHA-512_SSWU_RO_", p521_sswu_ro, p521_F, p521_A, p521_B, p521_p, p521_order, gx, gy, 98, hashlib.sha512, XMDExpander, 256) 178 | 179 | class GroupRistretto255(Group): 180 | def __init__(self): 181 | Group.__init__(self, "ristretto255") 182 | self.k = 128 183 | self.L = 48 184 | self.field_bytes_length = 32 185 | 186 | def generator(self): 187 | return Ed25519Point().base() 188 | 189 | def order(self): 190 | return Ed25519Point().order 191 | 192 | def identity(self): 193 | return Ed25519Point().identity() 194 | 195 | def serialize(self, element): 196 | return element.encode() 197 | 198 | def deserialize(self, encoded): 199 | return Ed25519Point().decode(encoded) 200 | 201 | def serialize_scalar(self, scalar): 202 | return I2OSP(scalar % self.order(), self.scalar_byte_length())[::-1] 203 | 204 | def element_byte_length(self): 205 | return self.field_bytes_length 206 | 207 | def scalar_byte_length(self): 208 | return self.field_bytes_length 209 | 210 | def hash_to_group(self, msg, dst): 211 | return Ed25519Point().hash_to_group(msg, dst) 212 | 213 | def hash_to_scalar(self, msg, dst): 214 | uniform_bytes = expand_message_xmd(msg, dst, 64, hashlib.sha512, self.k) 215 | return OS2IP_le(uniform_bytes) % self.order() 216 | 217 | def scalar_mult(self, x, y): 218 | return x * y 219 | 220 | class GroupDecaf448(Group): 221 | def __init__(self): 222 | Group.__init__(self, "decaf448") 223 | self.k = 224 224 | self.L = 84 225 | self.field_bytes_length = 56 226 | 227 | def generator(self): 228 | return Ed448GoldilocksPoint().base() 229 | 230 | def order(self): 231 | return Ed448GoldilocksPoint().order 232 | 233 | def identity(self): 234 | return Ed448GoldilocksPoint().identity() 235 | 236 | def serialize(self, element): 237 | return element.encode() 238 | 239 | def deserialize(self, encoded): 240 | return Ed448GoldilocksPoint().decode(encoded) 241 | 242 | def serialize_scalar(self, scalar): 243 | return I2OSP(scalar % self.order(), self.scalar_byte_length())[::-1] 244 | 245 | def element_byte_length(self): 246 | return self.field_bytes_length 247 | 248 | def scalar_byte_length(self): 249 | return self.field_bytes_length 250 | 251 | def hash_to_group(self, msg, dst): 252 | return Ed448GoldilocksPoint().hash_to_group(msg, dst) 253 | 254 | def hash_to_scalar(self, msg, dst): 255 | uniform_bytes = expand_message_xof(msg, dst, int(64), hashlib.shake_256, self.k) 256 | return OS2IP_le(uniform_bytes) % self.order() 257 | 258 | def scalar_mult(self, x, y): 259 | return x * y 260 | -------------------------------------------------------------------------------- /poc/oprf.sage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sage 2 | # vim: syntax=python 3 | 4 | import sys 5 | import hashlib 6 | 7 | from collections import namedtuple 8 | from hash_to_field import I2OSP 9 | 10 | try: 11 | from sagelib.groups import GroupP256, GroupP384, GroupP521, GroupRistretto255, GroupDecaf448 12 | except ImportError as e: 13 | sys.exit("Error loading preprocessed sage files. Try running `make setup && make clean pyfiles`. Full error: " + e) 14 | 15 | _as_bytes = lambda x: x if isinstance(x, bytes) else bytes(x, "utf-8") 16 | 17 | Ciphersuite = namedtuple("Ciphersuite", ["name", "identifier", "group", "H", "hash"]) 18 | 19 | ciphersuite_ristretto255_sha512 = "ristretto255-SHA512" 20 | ciphersuite_decaf448_shake256 = "decaf448-SHAKE256" 21 | ciphersuite_p256_sha256 = "P256-SHA256" 22 | ciphersuite_p384_sha384 = "P384-SHA384" 23 | ciphersuite_p521_sha512 = "P521-SHA512" 24 | 25 | oprf_ciphersuites = { 26 | ciphersuite_ristretto255_sha512: Ciphersuite("OPRF(ristretto255, SHA-512)", ciphersuite_ristretto255_sha512, GroupRistretto255(), hashlib.sha512, lambda x : hashlib.sha512(x).digest()), 27 | ciphersuite_decaf448_shake256: Ciphersuite("OPRF(decaf448, SHAKE256)", ciphersuite_decaf448_shake256, GroupDecaf448(), hashlib.shake_256, lambda x : hashlib.shake_256(x).digest(int(64))), 28 | ciphersuite_p256_sha256: Ciphersuite("OPRF(P-256, SHA-256)", ciphersuite_p256_sha256, GroupP256(), hashlib.sha256, lambda x : hashlib.sha256(x).digest()), 29 | ciphersuite_p384_sha384: Ciphersuite("OPRF(P-384, SHA-384)", ciphersuite_p384_sha384, GroupP384(), hashlib.sha384, lambda x : hashlib.sha384(x).digest()), 30 | ciphersuite_p521_sha512: Ciphersuite("OPRF(P-521, SHA-512)", ciphersuite_p521_sha512, GroupP521(), hashlib.sha512, lambda x : hashlib.sha512(x).digest()), 31 | } 32 | 33 | def identifer_to_suite(identifier): 34 | if identifier not in oprf_ciphersuites: 35 | raise Exception("Unknown ciphersuite") 36 | return oprf_ciphersuites[identifier] 37 | 38 | class Context(object): 39 | def __init__(self, version, mode, identifier): 40 | self.suite = identifer_to_suite(identifier) 41 | self.mode = mode 42 | self.identifier = identifier 43 | self.context_string = _as_bytes(version) + I2OSP(self.mode, 1) + _as_bytes("-") + _as_bytes(identifier) 44 | 45 | def group_domain_separation_tag(self): 46 | return _as_bytes("HashToGroup-") + self.context_string 47 | 48 | def scalar_domain_separation_tag(self): 49 | return _as_bytes("HashToScalar-") + self.context_string 50 | 51 | def domain_separation_tag(self, prefix): 52 | return _as_bytes(prefix) + self.context_string 53 | 54 | class Evaluation(object): 55 | def __init__(self, evaluated_element, proof): 56 | self.evaluated_element = evaluated_element 57 | self.proof = proof 58 | 59 | class OPRFClientContext(Context): 60 | def __init__(self, version, mode, suite): 61 | Context.__init__(self, version, mode, suite) 62 | 63 | def blind(self, x, rng): 64 | blind = ZZ(self.suite.group.random_scalar(rng)) 65 | input_element = self.suite.group.hash_to_group(x, self.group_domain_separation_tag()) 66 | if input_element == self.suite.group.identity(): 67 | raise Exception("InvalidInputError") 68 | blinded_element = blind * input_element 69 | return blind, blinded_element 70 | 71 | def unblind(self, blind, evaluated_element, blinded_element, proof): 72 | blind_inv = inverse_mod(blind, self.suite.group.order()) 73 | N = blind_inv * evaluated_element 74 | unblinded_element = self.suite.group.serialize(N) 75 | return unblinded_element 76 | 77 | def finalize(self, x, blind, evaluated_element, blinded_element, proof, info): 78 | unblinded_element = self.unblind(blind, evaluated_element, blinded_element, proof) 79 | finalize_input = I2OSP(len(x), 2) + x \ 80 | + I2OSP(len(unblinded_element), 2) + unblinded_element \ 81 | + _as_bytes("Finalize") 82 | 83 | return self.suite.hash(finalize_input) 84 | 85 | class OPRFServerContext(Context): 86 | def __init__(self, version, mode, suite, skS, pkS): 87 | Context.__init__(self, version, mode, suite) 88 | self.skS = skS 89 | self.pkS = pkS 90 | 91 | def internal_evaluate(self, blinded_element): 92 | evaluated_element = self.skS * blinded_element 93 | return evaluated_element 94 | 95 | def blind_evaluate(self, blinded_element, info, rng): 96 | evaluated_element = self.internal_evaluate(blinded_element) 97 | return evaluated_element, None, None 98 | 99 | def evaluate_without_proof(self, blinded_element, info): 100 | return self.internal_evaluate(blinded_element) 101 | 102 | def evaluate(self, x, info): 103 | input_element = self.suite.group.hash_to_group(x, self.group_domain_separation_tag()) 104 | if input_element == self.suite.group.identity(): 105 | raise Exception("InvalidInputError") 106 | evaluated_element = self.internal_evaluate(input_element) 107 | issued_element = self.suite.group.serialize(evaluated_element) 108 | finalize_input = I2OSP(len(x), 2) + x \ 109 | + I2OSP(len(issued_element), 2) + issued_element \ 110 | + _as_bytes("Finalize") 111 | 112 | return self.suite.hash(finalize_input) 113 | 114 | class Verifiable(object): 115 | def compute_composites_inner(self, k, B, Cs, Ds): 116 | assert(len(Cs) == len(Ds)) 117 | 118 | seedDST = _as_bytes("Seed-") + self.context_string 119 | Bm = self.suite.group.serialize(B) 120 | 121 | h1_input = I2OSP(len(Bm), 2) + Bm \ 122 | + I2OSP(len(seedDST), 2) + seedDST 123 | seed = self.suite.hash(h1_input) 124 | 125 | M = self.suite.group.identity() 126 | Z = self.suite.group.identity() 127 | 128 | for i in range(len(Cs)): 129 | Ci = self.suite.group.serialize(Cs[i]) 130 | Di = self.suite.group.serialize(Ds[i]) 131 | h2_input = I2OSP(len(seed), 2) + seed \ 132 | + I2OSP(i, 2) \ 133 | + I2OSP(len(Ci), 2) + Ci \ 134 | + I2OSP(len(Di), 2) + Di \ 135 | + _as_bytes("Composite") 136 | 137 | di = self.suite.group.hash_to_scalar(h2_input, self.scalar_domain_separation_tag()) 138 | M = (di * Cs[i]) + M 139 | 140 | if k == None: 141 | Z = (di * Ds[i]) + Z 142 | 143 | if k != None: 144 | Z = k * M 145 | 146 | return [M, Z] 147 | 148 | def compute_composites_fast(self, k, B, Cs, Ds): 149 | return self.compute_composites_inner(k, B, Cs, Ds) 150 | 151 | def compute_composites(self, B, Cs, Ds): 152 | return self.compute_composites_inner(None, B, Cs, Ds) 153 | 154 | class VOPRFClientContext(OPRFClientContext,Verifiable): 155 | def __init__(self, version, mode, suite, pkS): 156 | OPRFClientContext.__init__(self, version, mode, suite) 157 | self.pkS = pkS 158 | 159 | def verify_proof(self, A, B, Cs, Ds, proof): 160 | a = self.compute_composites(B, Cs, Ds) 161 | 162 | M = a[0] 163 | Z = a[1] 164 | t2 = (proof[1] * A) + (proof[0] * B) 165 | t3 = (proof[1] * M) + (proof[0] * Z) 166 | 167 | Bm = self.suite.group.serialize(B) 168 | a0 = self.suite.group.serialize(M) 169 | a1 = self.suite.group.serialize(Z) 170 | a2 = self.suite.group.serialize(t2) 171 | a3 = self.suite.group.serialize(t3) 172 | 173 | h2s_input = I2OSP(len(Bm), 2) + Bm \ 174 | + I2OSP(len(a0), 2) + a0 \ 175 | + I2OSP(len(a1), 2) + a1 \ 176 | + I2OSP(len(a2), 2) + a2 \ 177 | + I2OSP(len(a3), 2) + a3 \ 178 | + _as_bytes("Challenge") 179 | 180 | c = self.suite.group.hash_to_scalar(h2s_input, self.scalar_domain_separation_tag()) 181 | 182 | assert(c == proof[0]) 183 | return c == proof[0] 184 | 185 | def unblind(self, blind, evaluated_element, blinded_element, proof): 186 | G = self.suite.group.generator() 187 | if not self.verify_proof(G, self.pkS, [blinded_element], [evaluated_element], proof): 188 | raise Exception("VerifyError") 189 | 190 | blind_inv = inverse_mod(blind, self.suite.group.order()) 191 | N = blind_inv * evaluated_element 192 | unblinded_element = self.suite.group.serialize(N) 193 | return unblinded_element 194 | 195 | def unblind_batch(self, blinds, evaluated_elements, blinded_elements, proof): 196 | assert(len(blinds) == len(evaluated_elements)) 197 | assert(len(evaluated_elements) == len(blinded_elements)) 198 | 199 | G = self.suite.group.generator() 200 | if not self.verify_proof(G, self.pkS, blinded_elements, evaluated_elements, proof): 201 | raise Exception("VerifyError") 202 | 203 | unblinded_elements = [] 204 | for i, evaluated_element in enumerate(evaluated_elements): 205 | blind_inv = inverse_mod(blinds[i], self.suite.group.order()) 206 | N = blind_inv * evaluated_element 207 | unblinded_element = self.suite.group.serialize(N) 208 | unblinded_elements.append(unblinded_element) 209 | 210 | return unblinded_elements 211 | 212 | def finalize(self, x, blind, evaluated_element, blinded_element, proof, info): 213 | unblinded_element = self.unblind(blind, evaluated_element, blinded_element, proof) 214 | finalize_input = I2OSP(len(x), 2) + x \ 215 | + I2OSP(len(unblinded_element), 2) + unblinded_element \ 216 | + _as_bytes("Finalize") 217 | 218 | return self.suite.hash(finalize_input) 219 | 220 | def finalize_batch(self, xs, blinds, evaluated_elements, blinded_elements, proof, info): 221 | assert(len(blinds) == len(evaluated_elements)) 222 | assert(len(evaluated_elements) == len(blinded_elements)) 223 | 224 | unblinded_elements = self.unblind_batch(blinds, evaluated_elements, blinded_elements, proof) 225 | 226 | outputs = [] 227 | for i, unblinded_element in enumerate(unblinded_elements): 228 | finalize_input = I2OSP(len(xs[i]), 2) + xs[i] \ 229 | + I2OSP(len(unblinded_element), 2) + unblinded_element \ 230 | + _as_bytes("Finalize") 231 | 232 | digest = self.suite.hash(finalize_input) 233 | outputs.append(digest) 234 | 235 | return outputs 236 | 237 | class VOPRFServerContext(OPRFServerContext,Verifiable): 238 | def __init__(self, version, mode, suite, skS, pkS): 239 | OPRFServerContext.__init__(self, version, mode, suite, skS, pkS) 240 | 241 | def generate_proof(self, k, A, B, Cs, Ds, rng): 242 | a = self.compute_composites_fast(k, B, Cs, Ds) 243 | 244 | r = ZZ(self.suite.group.random_scalar(rng)) 245 | M = a[0] 246 | Z = a[1] 247 | t2 = r * A 248 | t3 = r * M 249 | 250 | Bm = self.suite.group.serialize(B) 251 | a0 = self.suite.group.serialize(M) 252 | a1 = self.suite.group.serialize(Z) 253 | a2 = self.suite.group.serialize(t2) 254 | a3 = self.suite.group.serialize(t3) 255 | 256 | h2s_input = I2OSP(len(Bm), 2) + Bm \ 257 | + I2OSP(len(a0), 2) + a0 \ 258 | + I2OSP(len(a1), 2) + a1 \ 259 | + I2OSP(len(a2), 2) + a2 \ 260 | + I2OSP(len(a3), 2) + a3 \ 261 | + _as_bytes("Challenge") 262 | 263 | c = self.suite.group.hash_to_scalar(h2s_input, self.scalar_domain_separation_tag()) 264 | s = (r - c * k) % self.suite.group.order() 265 | 266 | return [c, s], r 267 | 268 | def internal_evaluate(self, blinded_element): 269 | evaluated_element = self.skS * blinded_element 270 | return evaluated_element 271 | 272 | def blind_evaluate(self, blinded_element, info, rng): 273 | evaluated_element = self.internal_evaluate(blinded_element) 274 | proof, r = self.generate_proof(self.skS, self.suite.group.generator(), self.pkS, [blinded_element], [evaluated_element], rng) 275 | return evaluated_element, proof, r 276 | 277 | def evaluate_without_proof(self, blinded_element, info): 278 | return self.internal_evaluate(blinded_element) 279 | 280 | def blind_evaluate_batch(self, blinded_elements, info, rng): 281 | evaluated_elements = [] 282 | for blinded_element in blinded_elements: 283 | evaluated_element = self.skS * blinded_element 284 | evaluated_elements.append(evaluated_element) 285 | 286 | proof, r = self.generate_proof(self.skS, self.suite.group.generator(), self.pkS, blinded_elements, evaluated_elements, rng) 287 | return evaluated_elements, proof, r 288 | 289 | class POPRFClientContext(VOPRFClientContext): 290 | def __init__(self, version, mode, suite, pkS): 291 | VOPRFClientContext.__init__(self, version, mode, suite, pkS) 292 | self.pkS = pkS 293 | 294 | def blind(self, x, info, rng): 295 | context = _as_bytes("Info") + I2OSP(len(info), 2) + info 296 | t = self.suite.group.hash_to_scalar(context, self.scalar_domain_separation_tag()) 297 | G = self.suite.group.generator() 298 | tweaked_key = (G * t) + self.pkS 299 | if tweaked_key == self.suite.group.identity(): 300 | raise Exception("InvalidInputError") 301 | 302 | blind = ZZ(self.suite.group.random_scalar(rng)) 303 | input_element = self.suite.group.hash_to_group(x, self.group_domain_separation_tag()) 304 | if input_element == self.suite.group.identity(): 305 | raise Exception("InvalidInputError") 306 | 307 | blinded_element = blind * input_element 308 | return blind, blinded_element, tweaked_key 309 | 310 | def unblind(self, blind, evaluated_element, blinded_element, proof, tweaked_key): 311 | G = self.suite.group.generator() 312 | if not self.verify_proof(G, tweaked_key, [evaluated_element], [blinded_element], proof): 313 | raise Exception("Proof verification failed") 314 | 315 | blind_inv = inverse_mod(blind, self.suite.group.order()) 316 | N = blind_inv * evaluated_element 317 | unblinded_element = self.suite.group.serialize(N) 318 | return unblinded_element 319 | 320 | def unblind_batch(self, blinds, evaluated_elements, blinded_elements, proof, tweaked_key): 321 | assert(len(blinds) == len(evaluated_elements)) 322 | assert(len(evaluated_elements) == len(blinded_elements)) 323 | 324 | G = self.suite.group.generator() 325 | if not self.verify_proof(G, tweaked_key, evaluated_elements, blinded_elements, proof): 326 | raise Exception("Proof verification failed") 327 | 328 | unblinded_elements = [] 329 | for i, evaluated_element in enumerate(evaluated_elements): 330 | blind_inv = inverse_mod(blinds[i], self.suite.group.order()) 331 | N = blind_inv * evaluated_element 332 | unblinded_element = self.suite.group.serialize(N) 333 | unblinded_elements.append(unblinded_element) 334 | 335 | return unblinded_elements 336 | 337 | def finalize(self, x, blind, evaluated_element, blinded_element, proof, info, tweaked_key): 338 | unblinded_element = self.unblind(blind, evaluated_element, blinded_element, proof, tweaked_key) 339 | finalize_input = I2OSP(len(x), 2) + x \ 340 | + I2OSP(len(info), 2) + info \ 341 | + I2OSP(len(unblinded_element), 2) + unblinded_element \ 342 | + _as_bytes("Finalize") 343 | 344 | return self.suite.hash(finalize_input) 345 | 346 | def finalize_batch(self, xs, blinds, evaluated_elements, blinded_elements, proof, info, tweaked_key): 347 | assert(len(blinds) == len(evaluated_elements)) 348 | assert(len(evaluated_elements) == len(blinded_elements)) 349 | 350 | unblinded_elements = self.unblind_batch(blinds, evaluated_elements, blinded_elements, proof, tweaked_key) 351 | 352 | outputs = [] 353 | for i, unblinded_element in enumerate(unblinded_elements): 354 | finalize_input = I2OSP(len(xs[i]), 2) + xs[i] \ 355 | + I2OSP(len(info), 2) + info \ 356 | + I2OSP(len(unblinded_element), 2) + unblinded_element \ 357 | + _as_bytes("Finalize") 358 | 359 | digest = self.suite.hash(finalize_input) 360 | outputs.append(digest) 361 | 362 | return outputs 363 | 364 | class POPRFServerContext(VOPRFServerContext): 365 | def __init__(self, version, mode, suite, skS, pkS): 366 | VOPRFServerContext.__init__(self, version, mode, suite, skS, pkS) 367 | 368 | def internal_evaluate(self, blinded_element, info): 369 | context = _as_bytes("Info") + I2OSP(len(info), 2) + info 370 | t = self.suite.group.hash_to_scalar(context, self.scalar_domain_separation_tag()) 371 | k = self.skS + t 372 | if int(k) == 0: 373 | raise Exception("InverseError") 374 | k_inv = inverse_mod(k, self.suite.group.order()) 375 | evaluated_element = k_inv * blinded_element 376 | 377 | return evaluated_element, k 378 | 379 | def blind_evaluate(self, blinded_element, info, rng): 380 | evaluated_element, k = self.internal_evaluate(blinded_element, info) 381 | G = self.suite.group.generator() 382 | U = k * G 383 | proof, r = self.generate_proof(k, G, U, [evaluated_element], [blinded_element], rng) 384 | return evaluated_element, proof, r 385 | 386 | def evaluate_without_proof(self, blinded_element, info): 387 | evaluated_element, _ = self.internal_evaluate(blinded_element, info) 388 | return evaluated_element 389 | 390 | def blind_evaluate_batch(self, blinded_elements, info, rng): 391 | context = _as_bytes("Info") + I2OSP(len(info), 2) + info 392 | t = self.suite.group.hash_to_scalar(context, self.scalar_domain_separation_tag()) 393 | 394 | evaluated_elements = [] 395 | for blinded_element in blinded_elements: 396 | k = self.skS + t 397 | if int(k) == 0: 398 | raise Exception("InverseError") 399 | k_inv = inverse_mod(k, self.suite.group.order()) 400 | evaluated_element = k_inv * blinded_element 401 | evaluated_elements.append(evaluated_element) 402 | 403 | G = self.suite.group.generator() 404 | U = k * G 405 | proof, r = self.generate_proof(k, G, U, evaluated_elements, blinded_elements, rng) 406 | return evaluated_elements, proof, r 407 | 408 | def evaluate(self, x, info): 409 | input_element = self.suite.group.hash_to_group(x, self.group_domain_separation_tag()) 410 | evaluated_element = self.evaluate_without_proof(input_element, info) 411 | issued_element = self.suite.group.serialize(evaluated_element) 412 | 413 | finalize_input = I2OSP(len(x), 2) + x \ 414 | + I2OSP(len(info), 2) + info \ 415 | + I2OSP(len(issued_element), 2) + issued_element \ 416 | + _as_bytes("Finalize") 417 | 418 | return self.suite.hash(finalize_input) 419 | 420 | MODE_OPRF = 0x00 421 | MODE_VOPRF = 0x01 422 | MODE_POPRF = 0x02 423 | VERSION = "OPRFV1-" 424 | 425 | def DeriveKeyPair(mode, identifier, seed, info): 426 | ctx = Context(VERSION, mode, identifier) 427 | suite = identifer_to_suite(identifier) 428 | deriveInput = seed + I2OSP(len(info), 2) + info 429 | counter = 0 430 | skS = ZZ(0) 431 | while ZZ(skS) == ZZ(0): 432 | if counter > 255: 433 | raise Exception("DeriveKeyPairError") 434 | hashInput = deriveInput + I2OSP(counter, 1) 435 | skS = suite.group.hash_to_scalar(hashInput, ctx.domain_separation_tag("DeriveKeyPair")) 436 | counter = counter + 1 437 | pkS = skS * suite.group.generator() 438 | return skS, pkS 439 | 440 | def SetupOPRFServer(identifier, skS): 441 | return OPRFServerContext(VERSION, MODE_OPRF, identifier, skS, None) 442 | 443 | def SetupOPRFClient(identifier): 444 | return OPRFClientContext(VERSION, MODE_OPRF, identifier) 445 | 446 | def SetupVOPRFServer(identifier, skS, pkS): 447 | return VOPRFServerContext(VERSION, MODE_VOPRF, identifier, skS, pkS) 448 | 449 | def SetupVOPRFClient(identifier, pkS): 450 | return VOPRFClientContext(VERSION, MODE_VOPRF, identifier, pkS) 451 | 452 | def SetupPOPRFServer(identifier, skS, pkS): 453 | return POPRFServerContext(VERSION, MODE_POPRF, identifier, skS, pkS) 454 | 455 | def SetupPOPRFClient(identifier, pkS): 456 | return POPRFClientContext(VERSION, MODE_POPRF, identifier, pkS) 457 | -------------------------------------------------------------------------------- /poc/ristretto_decaf.sage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sage 2 | # vim: syntax=python 3 | 4 | import binascii 5 | import random 6 | import hashlib 7 | from hash_to_field import hash_to_field, expand_message_xmd, expand_message_xof 8 | 9 | class InvalidEncodingException(Exception): pass 10 | 11 | # Inspired by Mike Hamburg's library. Thanks, Mike 12 | def lobit(x): return int(x) & 1 13 | def negative(x): return lobit(x) 14 | def enc_le(x,n): return bytearray([int(x)>>(8*i) & 0xFF for i in range(n)]) 15 | def dec_le(x): return sum(b<<(8*i) for i,b in enumerate(x)) 16 | 17 | if sys.version_info[0] == 3: 18 | xrange = range 19 | _as_bytes = lambda x: x if isinstance(x, bytes) else bytes(x, "utf-8") 20 | _strxor = lambda str1, str2: bytes( s1 ^ s2 for (s1, s2) in zip(str1, str2) ) 21 | else: 22 | _as_bytes = lambda x: x 23 | _strxor = lambda str1, str2: ''.join( chr(ord(s1) ^ ord(s2)) for (s1, s2) in zip(str1, str2) ) 24 | 25 | def xsqrt(x,exn=InvalidEncodingException("Not on curve")): 26 | """Return sqrt(x)""" 27 | if not is_square(x): raise exn 28 | s = sqrt(x) 29 | if negative(s): s=-s 30 | return s 31 | 32 | def isqrt(x,exn=InvalidEncodingException("Not on curve")): 33 | """Return 1/sqrt(x)""" 34 | if x==0: return 0 35 | if not is_square(x): raise exn 36 | s = sqrt(x) 37 | return 1/s 38 | 39 | def isqrt_i(x): 40 | """Return 1/sqrt(x) or 1/sqrt(zeta * x)""" 41 | if x==0: return True,0 42 | gen = x.parent(-1) 43 | while is_square(gen): gen = sqrt(gen) 44 | if is_square(x): return True,1/sqrt(x) 45 | else: return False,1/sqrt(x*gen) 46 | 47 | class QuotientEdwardsPoint(object): 48 | """Abstract class for point an a quotiented Edwards curve; needs F,a,d,cofactor to work""" 49 | def __init__(self,x=0,y=1): 50 | x = self.x = self.F(x) 51 | y = self.y = self.F(y) 52 | if y^2 + self.a*x^2 != 1 + self.d*x^2*y^2: 53 | raise NotOnCurveException(str(self)) 54 | 55 | def __repr__(self): 56 | return "%s(0x%x,0x%x)" % (self.__class__.__name__, self.x, self.y) 57 | 58 | def __iter__(self): 59 | yield self.x 60 | yield self.y 61 | 62 | def __add__(self,other): 63 | x,y = self 64 | X,Y = other 65 | a,d = self.a,self.d 66 | return self.__class__( 67 | (x*Y+y*X)/(1+d*x*y*X*Y), 68 | (y*Y-a*x*X)/(1-d*x*y*X*Y) 69 | ) 70 | 71 | def __neg__(self): return self.__class__(-self.x,self.y) 72 | def __sub__(self,other): return self + (-other) 73 | def __rmul__(self,other): return self*other 74 | def __eq__(self,other): 75 | """NB: this is the only method that is different from the usual one, as per draft""" 76 | x,y = self 77 | X,Y = other 78 | return x*Y == X*y or (self.cofactor==8 and -self.a*x*X == y*Y) 79 | def __ne__(self,other): return not (self==other) 80 | 81 | def __mul__(self,exp): 82 | exp = int(exp) 83 | if exp < 0: exp,self = -exp,-self 84 | total = self.__class__() 85 | work = self 86 | while exp != 0: 87 | if exp & 1: total += work 88 | work += work 89 | exp >>= 1 90 | return total 91 | 92 | def xyzt(self): 93 | x,y = self 94 | z = self.F.random_element() 95 | return x*z,y*z,z,x*y*z 96 | 97 | def torque(self): 98 | """Apply cofactor group, except keeping the point even""" 99 | if self.cofactor == 8: 100 | if self.a == -1: return self.__class__(self.y*self.i, self.x*self.i) 101 | if self.a == 1: return self.__class__(-self.y, self.x) 102 | else: 103 | return self.__class__(-self.x, -self.y) 104 | 105 | # Utility functions 106 | @classmethod 107 | def bytesToGf(cls,bytes,mustBeProper=True,mustBePositive=False,maskHiBits=False): 108 | """Convert little-endian bytes to field element, sanity check length""" 109 | if len(bytes) != cls.encLen and mustBeProper: 110 | raise InvalidEncodingException("wrong length %d" % len(bytes)) 111 | s = dec_le(bytes) 112 | if mustBeProper and s >= cls.F.order(): 113 | raise InvalidEncodingException("%d out of range!" % s) 114 | bitlen = int(ceil(N(log(cls.F.order(),2.)))) 115 | if maskHiBits: s &= 2^bitlen-1 116 | s = cls.F(s) 117 | if mustBePositive and negative(s): 118 | raise InvalidEncodingException("%d is negative!" % s) 119 | return s 120 | 121 | @classmethod 122 | def gfToBytes(cls,x,mustBePositive=False): 123 | """Convert little-endian bytes to field element, sanity check length""" 124 | if negative(x) and mustBePositive: x = -x 125 | return enc_le(x,cls.encLen) 126 | 127 | class DecafPoint(QuotientEdwardsPoint): 128 | """Tweaked for compatibility with Ristretto, as in draft""" 129 | def encode(self): 130 | """Unoptimized specification for encoding""" 131 | a,d = self.a,self.d 132 | x,y = self 133 | if x==0 or y==0: return(self.gfToBytes(0)) 134 | 135 | if self.cofactor==8 and negative(x*y*self.isoMagic): 136 | x,y = self.torque() 137 | 138 | sr = xsqrt(1-a*x^2) 139 | altx = x*y*self.isoMagic / sr 140 | if negative(altx): s = (1+sr)/x 141 | else: s = (1-sr)/x 142 | 143 | return self.gfToBytes(s,mustBePositive=True) 144 | 145 | @classmethod 146 | def decode(cls,s): 147 | """Unoptimized specification for decoding""" 148 | a,d = cls.a,cls.d 149 | s = cls.bytesToGf(s,mustBePositive=True) 150 | 151 | if s==0: return cls() 152 | t = xsqrt(s^4 + 2*(a-2*d)*s^2 + 1) 153 | altx = 2*s*cls.isoMagic/t 154 | if negative(altx): t = -t 155 | x = 2*s / (1+a*s^2) 156 | y = (1-a*s^2) / t 157 | 158 | if cls.cofactor==8 and (negative(x*y*cls.isoMagic) or y==0): 159 | raise InvalidEncodingException("x*y is invalid: %d, %d" % (x,y)) 160 | 161 | return cls(x,y) 162 | 163 | @classmethod 164 | def fromJacobiQuartic(cls,s,t,sgn=1): 165 | """Convert point from its Jacobi Quartic representation""" 166 | a,d = cls.a,cls.d 167 | if s==0: return cls() 168 | x = 2*s / (1+a*s^2) 169 | y = (1-a*s^2) / t 170 | return cls(x,sgn*y) 171 | 172 | @classmethod 173 | def map(cls,r0): 174 | a,d = cls.a,cls.d 175 | r0 = cls.bytesToGf(r0,mustBeProper=False,maskHiBits=True) 176 | r = cls.qnr * r0^2 177 | den = (d*r-(d-a))*((d-a)*r-d) 178 | num = (r+1)*(a-2*d) 179 | 180 | iss,isri = isqrt_i(num*den) 181 | if iss: sgn,twiddle = 1,1 182 | else: sgn,twiddle = -1,r0*cls.qnr 183 | isri *= twiddle 184 | s = isri*num 185 | t = -sgn*isri*s*(r-1)*(a-2*d)^2 - 1 186 | if negative(s) == iss: s = -s 187 | return cls.fromJacobiQuartic(s,t) 188 | 189 | def hash_to_group(self, msg, dst): 190 | u = expand_message_xof(msg, dst, int(112), hashlib.shake_256, 224) 191 | P1 = self.map(u[0:56]) 192 | P2 = self.map(u[56:112]) 193 | P = P1 + P2 194 | return P 195 | 196 | def hash_to_scalar(self, msg, dst=""): 197 | # this is taking the edwards parameters defined in draft-irtf-cfrg-hash-to-curve 198 | # hash_to_field(msg, count, dst, modulus, degree (m), blen (l), expand_fn, hash_fn, security_param): 199 | return hash_to_field(msg, 1, dst, self.order, 1, 64, expand_message_xof, hashlib.shake_256, 224)[0][0] 200 | 201 | class RistrettoPoint(QuotientEdwardsPoint): 202 | def encodeSpec(self): 203 | """Unoptimized specification for encoding""" 204 | x,y = self 205 | if self.cofactor==8 and (negative(x*y) or y==0): (x,y) = self.torque() 206 | if y == -1: y = 1 # Avoid divide by 0; doesn't affect impl 207 | 208 | if negative(x): x,y = -x,-y 209 | s = xsqrt(self.mneg*(1-y)/(1+y),exn=Exception("Unimplemented: point is odd: " + str(self))) 210 | return self.gfToBytes(s) 211 | 212 | @classmethod 213 | def decodeSpec(cls,s): 214 | """Unoptimized specification for decoding""" 215 | s = cls.bytesToGf(s,mustBePositive=True) 216 | 217 | a,d = cls.a,cls.d 218 | x = xsqrt(4*s^2 / (a*d*(1+a*s^2)^2 - (1-a*s^2)^2)) 219 | y = (1+a*s^2) / (1-a*s^2) 220 | 221 | if cls.cofactor==8 and (negative(x*y) or y==0): 222 | raise InvalidEncodingException("x*y has high bit") 223 | 224 | return cls(x,y) 225 | 226 | def encode(self): 227 | """Encode, optimized version""" 228 | a,d,mneg = self.a,self.d,self.mneg 229 | x,y,z,t = self.xyzt() 230 | 231 | if self.cofactor==8: 232 | u1 = mneg*(z+y)*(z-y) 233 | u2 = x*y # = t*z 234 | isr = isqrt(u1*u2^2) 235 | i1 = isr*u1 # sqrt(mneg*(z+y)*(z-y))/(x*y) 236 | i2 = isr*u2 # 1/sqrt(a*(y+z)*(y-z)) 237 | z_inv = i1*i2*t # 1/z 238 | 239 | if negative(t*z_inv): 240 | if a==-1: 241 | x,y = y*self.i,x*self.i 242 | den_inv = self.magic * i1 243 | else: 244 | x,y = -y,x 245 | den_inv = self.i * self.magic * i1 246 | 247 | else: 248 | den_inv = i2 249 | 250 | if negative(x*z_inv): y = -y 251 | s = (z-y) * den_inv 252 | else: 253 | num = mneg*(z+y)*(z-y) 254 | isr = isqrt(num*y^2) 255 | if negative(isr^2*num*y*t): y = -y 256 | s = isr*y*(z-y) 257 | 258 | return self.gfToBytes(s,mustBePositive=True) 259 | 260 | @classmethod 261 | def decode(cls,s): 262 | """Decode, optimized version""" 263 | s = cls.bytesToGf(s,mustBePositive=True) 264 | 265 | a,d = cls.a,cls.d 266 | yden = 1-a*s^2 267 | ynum = 1+a*s^2 268 | yden_sqr = yden^2 269 | xden_sqr = a*d*ynum^2 - yden_sqr 270 | 271 | isr = isqrt(xden_sqr * yden_sqr) 272 | 273 | xden_inv = isr * yden 274 | yden_inv = xden_inv * isr * xden_sqr 275 | 276 | x = 2*s*xden_inv 277 | if negative(x): x = -x 278 | y = ynum * yden_inv 279 | 280 | if cls.cofactor==8 and (negative(x*y) or y==0): 281 | raise InvalidEncodingException("x*y is invalid: %d, %d" % (x,y)) 282 | 283 | return cls(x,y) 284 | 285 | @classmethod 286 | def fromJacobiQuartic(cls,s,t,sgn=1): 287 | """Convert point from its Jacobi Quartic representation""" 288 | a,d = cls.a,cls.d 289 | assert s^4 - 2*cls.a*(1-2*d/(d-a))*s^2 + 1 == t^2 290 | x = 2*s*cls.magic / t 291 | y = (1+a*s^2) / (1-a*s^2) 292 | return cls(sgn*x,y) 293 | 294 | @classmethod 295 | def map(cls, r0): 296 | a,d = cls.a,cls.d 297 | r0 = cls.bytesToGf(r0,mustBeProper=False,maskHiBits=True) 298 | r = cls.qnr * r0^2 299 | den = (d*r-a)*(a*r-d) 300 | num = cls.a*(r+1)*(a+d)*(d-a) 301 | 302 | iss,isri = isqrt_i(num*den) 303 | if iss: sgn,twiddle = 1,1 304 | else: sgn,twiddle = -1,r0*cls.qnr 305 | isri *= twiddle 306 | s = isri*num 307 | t = -sgn*isri*s*(r-1)*(d+a)^2 - 1 308 | if negative(s) == iss: s = -s 309 | return cls.fromJacobiQuartic(s,t) 310 | 311 | def hash_to_group(self, msg, dst): 312 | u = expand_message_xmd(msg, dst, int(64), hashlib.sha512, 128) 313 | P1 = self.map(u[0:32]) 314 | P2 = self.map(u[32:64]) 315 | P = P1 + P2 316 | return P 317 | 318 | def hash_to_scalar(self, msg, dst=""): 319 | # this is taking the edwards parameters defined in draft-irtf-cfrg-hash-to-curve 320 | # hash_to_field(msg, count, dst, modulus, degree (m), blen (l), expand_fn, hash_fn, security_param): 321 | return hash_to_field(msg, 1, dst, self.order, 1, 48, expand_message_xmd, hashlib.sha512, 128)[0][0] 322 | 323 | class Ed25519Point(RistrettoPoint): 324 | name = "ristretto255" 325 | F = GF(2^255-19) 326 | P = F.order() 327 | order = 2^252 + 27742317777372353535851937790883648493 328 | d = F(-121665/121666) 329 | a = F(-1) 330 | i = sqrt(F(-1)) 331 | mneg = F(1) 332 | qnr = i 333 | magic = isqrt(a*d-1) 334 | cofactor = 8 335 | encLen = 32 336 | 337 | @classmethod 338 | def base(cls): 339 | return cls( 15112221349535400772501151409588531511454012693041857206046113283949847762202, 46316835694926478169428394003475163141307993866256225615783033603165251855960 340 | ) 341 | @classmethod 342 | def identity(cls): 343 | return cls( 0, 1) 344 | 345 | class Ed448GoldilocksPoint(DecafPoint): 346 | name = "decaf448" 347 | F = GF(2^448-2^224-1) 348 | P = F.order() 349 | order = 2^446-13818066809895115352007386748515426880336692474882178609894547503885 350 | d = F(-39081) 351 | a = F(1) 352 | qnr = -1 353 | cofactor = 4 354 | encLen = 56 355 | isoD = F(39082/39081) 356 | isoMagic = isqrt(a*isoD-1) 357 | 358 | @classmethod 359 | def base(cls): 360 | return 2*cls( 361 | 224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710, 298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660 362 | ) 363 | @classmethod 364 | def identity(cls): 365 | return cls( 0, 1) 366 | 367 | def testVectorsRistretto(cls): 368 | print("Testing with test Vectors on %s" % cls.__name__) 369 | P = cls.base() 370 | Q = cls(0) 371 | R = bytearray(32) 372 | for i in range(16): 373 | assert Q.encode() == R 374 | Q += P 375 | R = bytearray(Q.encode()) 376 | 377 | def testVectorsDecaf(cls): 378 | print("Testing with test Vectors on %s" % cls.__name__) 379 | P = cls.base() 380 | Q = cls(0) 381 | R = bytearray(56) 382 | for i in range(16): 383 | assert Q.encode() == R 384 | Q += P 385 | R = bytearray(Q.encode()) 386 | 387 | def testMapRistretto(cls): 388 | print ("Testing one way map on %s" % cls.__name__) 389 | r = bytearray.fromhex("5d1be09e3d0c82fc538112490e35701979d99e06ca3e2b5b54bffe8b4dc772c14d98b696a1bbfb5ca32c436cc61c16563790306c79eaca7705668b47dffe5bb6") 390 | P1 = cls.map(r[0:32]) 391 | P2 = cls.map(r[32:64]) 392 | P = P1 + P2 393 | exp = bytearray.fromhex("3066f82a1a747d45120d1740f14358531a8f04bbffe6a819f86dfe50f44a0a46") 394 | assert P.encode() == exp 395 | r = bytearray.fromhex("165d697a1ef3d5cf3c38565beefcf88c0f282b8e7dbd28544c483432f1cec7675debea8ebb4e5fe7d6f6e5db15f15587ac4d4d4a1de7191e0c1ca6664abcc413") 396 | P1 = cls.map(r[0:32]) 397 | P2 = cls.map(r[32:64]) 398 | P = P1 + P2 399 | exp = bytearray.fromhex("ae81e7dedf20a497e10c304a765c1767a42d6e06029758d2d7e8ef7cc4c41179") 400 | assert P.encode() == exp 401 | 402 | def testMapDecaf(cls): 403 | print ("Testing one way map on %s" % cls.__name__) 404 | r = bytearray.fromhex("cbb8c991fd2f0b7e1913462d6463e4fd2ce4ccdd28274dc2ca1f4165d5ee6cdccea57be3416e166fd06718a31af45a2f8e987e301be59ae6673e963001dbbda80df47014a21a26d6c7eb4ebe0312aa6fffb8d1b26bc62ca40ed51f8057a635a02c2b8c83f48fa6a2d70f58a1185902c0") 405 | P1 = cls.map(r[0:56]) 406 | P2 = cls.map(r[56:112]) 407 | P = P1 + P2 408 | exp = bytearray.fromhex("0c709c9607dbb01c94513358745b7c23953d03b33e39c7234e268d1d6e24f34014ccbc2216b965dd231d5327e591dc3c0e8844ccfd568848") 409 | assert P.encode() == exp 410 | r = bytearray.fromhex("4dec58199a35f531a5f0a9f71a53376d7b4bdd6bbd2904234a8ea65bbacbce2a542291378157a8f4be7b6a092672a34d85e473b26ccfbd4cdc6739783dc3f4f6ee3537b7aed81df898c7ea0ae89a15b5559596c2a5eeacf8b2b362f3db2940e3798b63203cae77c4683ebaed71533e51") 411 | P1 = cls.map(r[0:56]) 412 | P2 = cls.map(r[56:112]) 413 | P = P1 + P2 414 | exp = bytearray.fromhex("f4ccb31d263731ab88bed634304956d2603174c66da38742053fa37dd902346c3862155d68db63be87439e3d68758ad7268e239d39c4fd3b") 415 | assert P.encode() == exp 416 | 417 | def test(): 418 | testVectorsRistretto(Ed25519Point) 419 | testVectorsDecaf(Ed448GoldilocksPoint) 420 | testMapRistretto(Ed25519Point) 421 | testMapDecaf(Ed448GoldilocksPoint) 422 | -------------------------------------------------------------------------------- /poc/test_drng.sage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sage 2 | # vim: syntax=python 3 | 4 | import random 5 | import hashlib 6 | 7 | class TestDRNG(object): 8 | def __init__(self, seed): 9 | self.seed = int.from_bytes(hashlib.sha256(seed).digest(), 'big') 10 | 11 | def randint(self, l, h): 12 | random.seed(self.seed) 13 | val = random.randint(l, h) 14 | self.seed = int.from_bytes(hashlib.sha256(int(val % 2^32).to_bytes(4, 'big')).digest(), 'big') 15 | return val -------------------------------------------------------------------------------- /poc/test_oprf.sage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sage 2 | # vim: syntax=python 3 | 4 | import sys 5 | import json 6 | 7 | try: 8 | from sagelib.test_drng import TestDRNG 9 | from sagelib.oprf \ 10 | import DeriveKeyPair, \ 11 | SetupOPRFServer, SetupOPRFClient, MODE_OPRF, \ 12 | SetupVOPRFServer, SetupVOPRFClient, MODE_VOPRF, \ 13 | SetupPOPRFServer, SetupPOPRFClient, MODE_POPRF, \ 14 | _as_bytes, \ 15 | ciphersuite_ristretto255_sha512, \ 16 | ciphersuite_decaf448_shake256, \ 17 | ciphersuite_p256_sha256, \ 18 | ciphersuite_p384_sha384, \ 19 | ciphersuite_p521_sha512 20 | except ImportError as e: 21 | sys.exit("Error loading preprocessed sage files. Try running `make setup && make clean pyfiles`. Full error: " + e) 22 | 23 | def to_hex_string(octet_string): 24 | if isinstance(octet_string, str): 25 | return "".join("{:02x}".format(ord(c)) for c in octet_string) 26 | assert isinstance(octet_string, (bytes, bytearray)) 27 | return "".join("{:02x}".format(c) for c in octet_string) 28 | 29 | def to_hex(octet_string): 30 | if isinstance(octet_string, list): 31 | return ",".join([to_hex_string(x) for x in octet_string]) 32 | return to_hex_string(octet_string) 33 | 34 | test_suites = [ 35 | ciphersuite_ristretto255_sha512, 36 | ciphersuite_decaf448_shake256, 37 | ciphersuite_p256_sha256, 38 | ciphersuite_p384_sha384, 39 | ciphersuite_p521_sha512 40 | ] 41 | 42 | class Protocol(object): 43 | def __init__(self, identifier, mode, info): 44 | self.inputs = [b'\x00', b'\x5A'*17] 45 | self.identifier = identifier 46 | self.mode = mode 47 | self.info = info 48 | self.key_info = _as_bytes("test key") 49 | 50 | self.seed = b'\xA3' * 32 51 | skS, pkS = DeriveKeyPair(self.mode, self.identifier, self.seed, self.key_info) 52 | if mode == MODE_OPRF: 53 | self.server = SetupOPRFServer(identifier, skS) 54 | self.client = SetupOPRFClient(identifier) 55 | elif mode == MODE_VOPRF: 56 | self.server = SetupVOPRFServer(identifier, skS, pkS) 57 | self.client = SetupVOPRFClient(identifier, pkS) 58 | elif mode == MODE_POPRF: 59 | self.server = SetupPOPRFServer(identifier, skS, pkS) 60 | self.client = SetupPOPRFClient(identifier, pkS) 61 | else: 62 | raise Exception("bad mode") 63 | self.suite = self.client.suite 64 | 65 | def run(self): 66 | group = self.client.suite.group 67 | client = self.client 68 | server = self.server 69 | 70 | def create_test_vector_for_input(x, info): 71 | rng = TestDRNG("test vector seed".encode('utf-8')) 72 | if self.mode == MODE_POPRF: 73 | blind, blinded_element, tweaked_key = client.blind(x, info, rng) 74 | evaluated_element, proof, proof_randomness = server.blind_evaluate(blinded_element, info, rng) 75 | output = client.finalize(x, blind, evaluated_element, blinded_element, proof, info, tweaked_key) 76 | else: 77 | blind, blinded_element = client.blind(x, rng) 78 | evaluated_element, proof, proof_randomness = server.blind_evaluate(blinded_element, info, rng) 79 | output = client.finalize(x, blind, evaluated_element, blinded_element, proof, info) 80 | 81 | assert(output == server.evaluate(x, info)) 82 | 83 | vector = {} 84 | vector["Blind"] = to_hex(group.serialize_scalar(blind)) 85 | vector["BlindedElement"] = to_hex(group.serialize(blinded_element)) 86 | vector["EvaluationElement"] = to_hex(group.serialize(evaluated_element)) 87 | 88 | if self.mode == MODE_VOPRF or self.mode == MODE_POPRF: 89 | vector["Proof"] = { 90 | "proof": to_hex(group.serialize_scalar(proof[0]) + group.serialize_scalar(proof[1])), 91 | "r": to_hex(group.serialize_scalar(proof_randomness)), 92 | } 93 | 94 | vector["Input"] = to_hex(x) 95 | if self.mode == MODE_POPRF: 96 | vector["Info"] = to_hex(info) 97 | vector["Output"] = to_hex(output) 98 | vector["Batch"] = int(1) 99 | 100 | return vector 101 | 102 | def create_batched_test_vector_for_inputs(xs, info): 103 | blinds = [] 104 | blinded_elements = [] 105 | tweaked_key = None 106 | rng = TestDRNG("test vector seed".encode('utf-8')) 107 | for x in xs: 108 | if self.mode == MODE_POPRF: 109 | blind, blinded_element, tweaked_key = client.blind(x, info, rng) 110 | blinds.append(blind) 111 | blinded_elements.append(blinded_element) 112 | else: 113 | blind, blinded_element = client.blind(x, rng) 114 | blinds.append(blind) 115 | blinded_elements.append(blinded_element) 116 | 117 | evaluated_elements, proof, proof_randomness = server.blind_evaluate_batch(blinded_elements, info, rng) 118 | 119 | if self.mode == MODE_POPRF: 120 | outputs = client.finalize_batch(xs, blinds, evaluated_elements, blinded_elements, proof, info, tweaked_key) 121 | else: 122 | outputs = client.finalize_batch(xs, blinds, evaluated_elements, blinded_elements, proof, info) 123 | 124 | for i, output in enumerate(outputs): 125 | assert(output == server.evaluate(xs[i], info)) 126 | 127 | vector = {} 128 | vector["Blind"] = ",".join([to_hex(group.serialize_scalar(blind)) for blind in blinds]) 129 | vector["BlindedElement"] = to_hex(list(map(lambda e : group.serialize(e), blinded_elements))) 130 | vector["EvaluationElement"] = to_hex(list(map(lambda e : group.serialize(e), evaluated_elements))) 131 | 132 | if self.mode == MODE_VOPRF or self.mode == MODE_POPRF: 133 | vector["Proof"] = { 134 | "proof": to_hex(group.serialize_scalar(proof[0]) + group.serialize_scalar(proof[1])), 135 | "r": to_hex(group.serialize_scalar(proof_randomness)), 136 | } 137 | 138 | vector["Input"] = to_hex(xs) 139 | if self.mode == MODE_POPRF: 140 | vector["Info"] = to_hex(info) 141 | vector["Output"] = to_hex(outputs) 142 | vector["Batch"] = int(len(xs)) 143 | 144 | return vector 145 | 146 | vectors = [create_test_vector_for_input(x, self.info) for x in self.inputs] 147 | if self.mode == MODE_VOPRF or self.mode == MODE_POPRF: 148 | vectors.append(create_batched_test_vector_for_inputs(self.inputs, self.info)) 149 | 150 | vecSuite = {} 151 | vecSuite["identifier"] = self.identifier 152 | vecSuite["mode"] = int(self.mode) 153 | vecSuite["hash"] = self.suite.H().name.upper() 154 | vecSuite["keyInfo"] = to_hex(self.key_info) 155 | vecSuite["seed"] = to_hex(self.seed) 156 | vecSuite["skSm"] = to_hex(group.serialize_scalar(server.skS)) 157 | vecSuite["groupDST"] = to_hex(client.group_domain_separation_tag()) 158 | if self.mode == MODE_VOPRF or self.mode == MODE_POPRF: 159 | vecSuite["pkSm"] = to_hex(group.serialize(server.pkS)) 160 | vecSuite["vectors"] = vectors 161 | 162 | return vecSuite 163 | 164 | def wrap_write(fh, arg, *args): 165 | line_length = 68 166 | string = " ".join( [arg] + list(args)) 167 | for hunk in (string[0+i:line_length+i] for i in range(0, len(string), line_length)): 168 | if hunk and len(hunk.strip()) > 0: 169 | fh.write(hunk + "\n") 170 | 171 | def write_blob(fh, name, blob): 172 | wrap_write(fh, name + ' = ' + to_hex(blob)) 173 | 174 | def write_value(fh, name, value): 175 | wrap_write(fh, name + ' = ' + value) 176 | 177 | def write_oprf_vector(fh, vector): 178 | fh.write("~~~\n") 179 | write_value(fh, "Seed", vector["seed"]) 180 | write_value(fh, "KeyInfo", vector["keyInfo"]) 181 | write_value(fh, "skSm", vector["skSm"]) 182 | fh.write("~~~\n") 183 | fh.write("\n") 184 | for i, v in enumerate(vector["vectors"]): 185 | fh.write("#### Test Vector " + str(i+1) + ", Batch Size " + str(v["Batch"]) + "\n") 186 | fh.write("\n") 187 | fh.write("~~~\n") 188 | write_value(fh, "Input", v["Input"]) 189 | write_value(fh, "Blind", v["Blind"]) 190 | write_value(fh, "BlindedElement", v["BlindedElement"]) 191 | write_value(fh, "EvaluationElement", v["EvaluationElement"]) 192 | write_value(fh, "Output", v["Output"]) 193 | fh.write("~~~\n") 194 | fh.write("\n") 195 | 196 | def write_voprf_vector(fh, vector): 197 | fh.write("~~~\n") 198 | write_value(fh, "Seed", vector["seed"]) 199 | write_value(fh, "KeyInfo", vector["keyInfo"]) 200 | write_value(fh, "skSm", vector["skSm"]) 201 | write_value(fh, "pkSm", vector["pkSm"]) 202 | fh.write("~~~\n") 203 | fh.write("\n") 204 | for i, v in enumerate(vector["vectors"]): 205 | fh.write("#### Test Vector " + str(i+1) + ", Batch Size " + str(v["Batch"]) + "\n") 206 | fh.write("\n") 207 | fh.write("~~~\n") 208 | write_value(fh, "Input", v["Input"]) 209 | write_value(fh, "Blind", v["Blind"]) 210 | write_value(fh, "BlindedElement", v["BlindedElement"]) 211 | write_value(fh, "EvaluationElement", v["EvaluationElement"]) 212 | write_value(fh, "Proof", v["Proof"]["proof"]) 213 | write_value(fh, "ProofRandomScalar", v["Proof"]["r"]) 214 | write_value(fh, "Output", v["Output"]) 215 | fh.write("~~~\n") 216 | fh.write("\n") 217 | 218 | def write_poprf_vector(fh, vector): 219 | fh.write("~~~\n") 220 | write_value(fh, "Seed", vector["seed"]) 221 | write_value(fh, "KeyInfo", vector["keyInfo"]) 222 | write_value(fh, "skSm", vector["skSm"]) 223 | write_value(fh, "pkSm", vector["pkSm"]) 224 | fh.write("~~~\n") 225 | fh.write("\n") 226 | for i, v in enumerate(vector["vectors"]): 227 | fh.write("#### Test Vector " + str(i+1) + ", Batch Size " + str(v["Batch"]) + "\n") 228 | fh.write("\n") 229 | fh.write("~~~\n") 230 | write_value(fh, "Input", v["Input"]) 231 | write_value(fh, "Info", v["Info"]) 232 | write_value(fh, "Blind", v["Blind"]) 233 | write_value(fh, "BlindedElement", v["BlindedElement"]) 234 | write_value(fh, "EvaluationElement", v["EvaluationElement"]) 235 | write_value(fh, "Proof", v["Proof"]["proof"]) 236 | write_value(fh, "ProofRandomScalar", v["Proof"]["r"]) 237 | write_value(fh, "Output", v["Output"]) 238 | fh.write("~~~\n") 239 | fh.write("\n") 240 | 241 | mode_map = { 242 | MODE_OPRF: "OPRF", 243 | MODE_VOPRF: "VOPRF", 244 | MODE_POPRF: "POPRF", 245 | } 246 | 247 | def main(path="vectors"): 248 | allVectors = {} 249 | for identifier in test_suites: 250 | suiteVectors = {} 251 | for mode in [MODE_OPRF, MODE_VOPRF, MODE_POPRF]: 252 | protocol = Protocol(identifier, mode, _as_bytes("test info")) 253 | suiteVectors[str(mode)] = protocol.run() 254 | allVectors[identifier] = suiteVectors 255 | 256 | flatVectors = [] 257 | for suite in allVectors: 258 | for mode in allVectors[suite]: 259 | flatVectors.append(allVectors[suite][mode]) 260 | with open(path + "/allVectors.json", 'wt') as f: 261 | json.dump(flatVectors, f, sort_keys=True, indent=2) 262 | f.write("\n") 263 | 264 | with open(path + "/allVectors.txt", 'wt') as f: 265 | for suite in allVectors: 266 | f.write("## " + suite + "\n") 267 | f.write("\n") 268 | for mode in allVectors[suite]: 269 | if mode == str(MODE_OPRF): 270 | f.write("### OPRF Mode\n") 271 | f.write("\n") 272 | write_oprf_vector(f, allVectors[suite][mode]) 273 | elif mode == str(MODE_VOPRF): 274 | f.write("### VOPRF Mode\n") 275 | f.write("\n") 276 | write_voprf_vector(f, allVectors[suite][mode]) 277 | else: 278 | f.write("### POPRF Mode\n") 279 | f.write("\n") 280 | write_poprf_vector(f, allVectors[suite][mode]) 281 | 282 | if __name__ == "__main__": 283 | main() 284 | -------------------------------------------------------------------------------- /poc/vectors/allVectors.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "groupDST": "48617368546f47726f75702d4f50524656312d002d72697374726574746f3235352d534841353132", 4 | "hash": "SHA512", 5 | "identifier": "ristretto255-SHA512", 6 | "keyInfo": "74657374206b6579", 7 | "mode": 0, 8 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 9 | "skSm": "5ebcea5ee37023ccb9fc2d2019f9d7737be85591ae8652ffa9ef0f4d37063b0e", 10 | "vectors": [ 11 | { 12 | "Batch": 1, 13 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706", 14 | "BlindedElement": "609a0ae68c15a3cf6903766461307e5c8bb2f95e7e6550e1ffa2dc99e412803c", 15 | "EvaluationElement": "7ec6578ae5120958eb2db1745758ff379e77cb64fe77b0b2d8cc917ea0869c7e", 16 | "Input": "00", 17 | "Output": "527759c3d9366f277d8c6020418d96bb393ba2afb20ff90df23fb7708264e2f3ab9135e3bd69955851de4b1f9fe8a0973396719b7912ba9ee8aa7d0b5e24bcf6" 18 | }, 19 | { 20 | "Batch": 1, 21 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706", 22 | "BlindedElement": "da27ef466870f5f15296299850aa088629945a17d1f5b7f5ff043f76b3c06418", 23 | "EvaluationElement": "b4cbf5a4f1eeda5a63ce7b77c7d23f461db3fcab0dd28e4e17cecb5c90d02c25", 24 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 25 | "Output": "f4a74c9c592497375e796aa837e907b1a045d34306a749db9f34221f7e750cb4f2a6413a6bf6fa5e19ba6348eb673934a722a7ede2e7621306d18951e7cf2c73" 26 | } 27 | ] 28 | }, 29 | { 30 | "groupDST": "48617368546f47726f75702d4f50524656312d012d72697374726574746f3235352d534841353132", 31 | "hash": "SHA512", 32 | "identifier": "ristretto255-SHA512", 33 | "keyInfo": "74657374206b6579", 34 | "mode": 1, 35 | "pkSm": "c803e2cc6b05fc15064549b5920659ca4a77b2cca6f04f6b357009335476ad4e", 36 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 37 | "skSm": "e6f73f344b79b379f1a0dd37e07ff62e38d9f71345ce62ae3a9bc60b04ccd909", 38 | "vectors": [ 39 | { 40 | "Batch": 1, 41 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706", 42 | "BlindedElement": "863f330cc1a1259ed5a5998a23acfd37fb4351a793a5b3c090b642ddc439b945", 43 | "EvaluationElement": "aa8fa048764d5623868679402ff6108d2521884fa138cd7f9c7669a9a014267e", 44 | "Input": "00", 45 | "Output": "b58cfbe118e0cb94d79b5fd6a6dafb98764dff49c14e1770b566e42402da1a7da4d8527693914139caee5bd03903af43a491351d23b430948dd50cde10d32b3c", 46 | "Proof": { 47 | "proof": "ddef93772692e535d1a53903db24367355cc2cc78de93b3be5a8ffcc6985dd066d4346421d17bf5117a2a1ff0fcb2a759f58a539dfbe857a40bce4cf49ec600d", 48 | "r": "222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0e" 49 | } 50 | }, 51 | { 52 | "Batch": 1, 53 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706", 54 | "BlindedElement": "cc0b2a350101881d8a4cba4c80241d74fb7dcbfde4a61fde2f91443c2bf9ef0c", 55 | "EvaluationElement": "60a59a57208d48aca71e9e850d22674b611f752bed48b36f7a91b372bd7ad468", 56 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 57 | "Output": "8a9a2f3c7f085b65933594309041fc1898d42d0858e59f90814ae90571a6df60356f4610bf816f27afdd84f47719e480906d27ecd994985890e5f539e7ea74b6", 58 | "Proof": { 59 | "proof": "401a0da6264f8cf45bb2f5264bc31e109155600babb3cd4e5af7d181a2c9dc0a67154fabf031fd936051dec80b0b6ae29c9503493dde7393b722eafdf5a50b02", 60 | "r": "222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0e" 61 | } 62 | }, 63 | { 64 | "Batch": 2, 65 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706,222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0e", 66 | "BlindedElement": "863f330cc1a1259ed5a5998a23acfd37fb4351a793a5b3c090b642ddc439b945,90a0145ea9da29254c3a56be4fe185465ebb3bf2a1801f7124bbbadac751e654", 67 | "EvaluationElement": "aa8fa048764d5623868679402ff6108d2521884fa138cd7f9c7669a9a014267e,cc5ac221950a49ceaa73c8db41b82c20372a4c8d63e5dded2db920b7eee36a2a", 68 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 69 | "Output": "b58cfbe118e0cb94d79b5fd6a6dafb98764dff49c14e1770b566e42402da1a7da4d8527693914139caee5bd03903af43a491351d23b430948dd50cde10d32b3c,8a9a2f3c7f085b65933594309041fc1898d42d0858e59f90814ae90571a6df60356f4610bf816f27afdd84f47719e480906d27ecd994985890e5f539e7ea74b6", 70 | "Proof": { 71 | "proof": "cc203910175d786927eeb44ea847328047892ddf8590e723c37205cb74600b0a5ab5337c8eb4ceae0494c2cf89529dcf94572ed267473d567aeed6ab873dee08", 72 | "r": "419c4f4f5052c53c45f3da494d2b67b220d02118e0857cdbcf037f9ea84bbe0c" 73 | } 74 | } 75 | ] 76 | }, 77 | { 78 | "groupDST": "48617368546f47726f75702d4f50524656312d022d72697374726574746f3235352d534841353132", 79 | "hash": "SHA512", 80 | "identifier": "ristretto255-SHA512", 81 | "keyInfo": "74657374206b6579", 82 | "mode": 2, 83 | "pkSm": "c647bef38497bc6ec077c22af65b696efa43bff3b4a1975a3e8e0a1c5a79d631", 84 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 85 | "skSm": "145c79c108538421ac164ecbe131942136d5570b16d8bf41a24d4337da981e07", 86 | "vectors": [ 87 | { 88 | "Batch": 1, 89 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706", 90 | "BlindedElement": "c8713aa89241d6989ac142f22dba30596db635c772cbf25021fdd8f3d461f715", 91 | "EvaluationElement": "1a4b860d808ff19624731e67b5eff20ceb2df3c3c03b906f5693e2078450d874", 92 | "Info": "7465737420696e666f", 93 | "Input": "00", 94 | "Output": "ca688351e88afb1d841fde4401c79efebb2eb75e7998fa9737bd5a82a152406d38bd29f680504e54fd4587eddcf2f37a2617ac2fbd2993f7bdf45442ace7d221", 95 | "Proof": { 96 | "proof": "41ad1a291aa02c80b0915fbfbb0c0afa15a57e2970067a602ddb9e8fd6b7100de32e1ecff943a36f0b10e3dae6bd266cdeb8adf825d86ef27dbc6c0e30c52206", 97 | "r": "222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0e" 98 | } 99 | }, 100 | { 101 | "Batch": 1, 102 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706", 103 | "BlindedElement": "f0f0b209dd4d5f1844dac679acc7761b91a2e704879656cb7c201e82a99ab07d", 104 | "EvaluationElement": "8c3c9d064c334c6991e99f286ea2301d1bde170b54003fb9c44c6d7bd6fc1540", 105 | "Info": "7465737420696e666f", 106 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 107 | "Output": "7c6557b276a137922a0bcfc2aa2b35dd78322bd500235eb6d6b6f91bc5b56a52de2d65612d503236b321f5d0bebcbc52b64b92e426f29c9b8b69f52de98ae507", 108 | "Proof": { 109 | "proof": "4c39992d55ffba38232cdac88fe583af8a85441fefd7d1d4a8d0394cd1de77018bf135c174f20281b3341ab1f453fe72b0293a7398703384bed822bfdeec8908", 110 | "r": "222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0e" 111 | } 112 | }, 113 | { 114 | "Batch": 2, 115 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706,222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0e", 116 | "BlindedElement": "c8713aa89241d6989ac142f22dba30596db635c772cbf25021fdd8f3d461f715,423a01c072e06eb1cce96d23acce06e1ea64a609d7ec9e9023f3049f2d64e50c", 117 | "EvaluationElement": "1a4b860d808ff19624731e67b5eff20ceb2df3c3c03b906f5693e2078450d874,aa1f16e903841036e38075da8a46655c94fc92341887eb5819f46312adfc0504", 118 | "Info": "7465737420696e666f", 119 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 120 | "Output": "ca688351e88afb1d841fde4401c79efebb2eb75e7998fa9737bd5a82a152406d38bd29f680504e54fd4587eddcf2f37a2617ac2fbd2993f7bdf45442ace7d221,7c6557b276a137922a0bcfc2aa2b35dd78322bd500235eb6d6b6f91bc5b56a52de2d65612d503236b321f5d0bebcbc52b64b92e426f29c9b8b69f52de98ae507", 121 | "Proof": { 122 | "proof": "43fdb53be399cbd3561186ae480320caa2b9f36cca0e5b160c4a677b8bbf4301b28f12c36aa8e11e5a7ef551da0781e863a6dc8c0b2bf5a149c9e00621f02006", 123 | "r": "419c4f4f5052c53c45f3da494d2b67b220d02118e0857cdbcf037f9ea84bbe0c" 124 | } 125 | } 126 | ] 127 | }, 128 | { 129 | "groupDST": "48617368546f47726f75702d4f50524656312d002d64656361663434382d5348414b45323536", 130 | "hash": "SHAKE_256", 131 | "identifier": "decaf448-SHAKE256", 132 | "keyInfo": "74657374206b6579", 133 | "mode": 0, 134 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 135 | "skSm": "e8b1375371fd11ebeb224f832dcc16d371b4188951c438f751425699ed29ecc80c6c13e558ccd67634fd82eac94aa8d1f0d7fee990695d1e", 136 | "vectors": [ 137 | { 138 | "Batch": 1, 139 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112", 140 | "BlindedElement": "e0ae01c4095f08e03b19baf47ffdc19cb7d98e583160522a3c7d6a0b2111cd93a126a46b7b41b730cd7fc943d4e28e590ed33ae475885f6c", 141 | "EvaluationElement": "50ce4e60eed006e22e7027454b5a4b8319eb2bc8ced609eb19eb3ad42fb19e06ba12d382cbe7ae342a0cad6ead0ef8f91f00bb7f0cd9c0a2", 142 | "Input": "00", 143 | "Output": "37d3f7922d9388a15b561de5829bbf654c4089ede89c0ce0f3f85bcdba09e382ce0ab3507e021f9e79706a1798ffeac68ebd5cf62e5eb9838c7068351d97ae37" 144 | }, 145 | { 146 | "Batch": 1, 147 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112", 148 | "BlindedElement": "86a88dc5c6331ecfcb1d9aacb50a68213803c462e377577cacc00af28e15f0ddbc2e3d716f2f39ef95f3ec1314a2c64d940a9f295d8f13bb", 149 | "EvaluationElement": "162e9fa6e9d527c3cd734a31bf122a34dbd5bcb7bb23651f1768a7a9274cc116c03b58afa6f0dede3994a60066c76370e7328e7062fd5819", 150 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 151 | "Output": "a2a652290055cb0f6f8637a249ee45e32ef4667db0b4c80c0a70d2a64164d01525cfdad5d870a694ec77972b9b6ec5d2596a5223e5336913f945101f0137f55e" 152 | } 153 | ] 154 | }, 155 | { 156 | "groupDST": "48617368546f47726f75702d4f50524656312d012d64656361663434382d5348414b45323536", 157 | "hash": "SHAKE_256", 158 | "identifier": "decaf448-SHAKE256", 159 | "keyInfo": "74657374206b6579", 160 | "mode": 1, 161 | "pkSm": "945fc518c47695cf65217ace04b86ac5e4cbe26ca649d52854bb16c494ce09069d6add96b20d4b0ae311a87c9a73e3a146b525763ab2f955", 162 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 163 | "skSm": "e3c01519a076a326a0eb566343e9b21c115fa18e6e85577ddbe890b33104fcc2835ddfb14a928dc3f5d79b936e17c76b99e0bf6a1680930e", 164 | "vectors": [ 165 | { 166 | "Batch": 1, 167 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112", 168 | "BlindedElement": "7261bbc335c664ba788f1b1a1a4cd5190cc30e787ef277665ac1d314f8861e3ec11854ce3ddd42035d9e0f5cddde324c332d8c880abc00eb", 169 | "EvaluationElement": "ca1491a526c28d880806cf0fb0122222392cf495657be6e4c9d203bceffa46c86406caf8217859d3fb259077af68e5d41b3699410781f467", 170 | "Input": "00", 171 | "Output": "e2ac40b634f36cccd8262b285adff7c9dcc19cd308564a5f4e581d1a8535773b86fa4fc9f2203c370763695c5093aea4a7aedec4488b1340ba3bf663a23098c1", 172 | "Proof": { 173 | "proof": "f84bbeee47aedf43558dae4b95b3853635a9fc1a9ea7eac9b454c64c66c4f49cd1c72711c7ac2e06c681e16ea693d5500bbd7b56455df52f69e00b76b4126961e1562fdbaaac40b7701065cbeece3febbfe09e00160f81775d36daed99d8a2a10be0759e01b7ee81217203416c9db208", 174 | "r": "b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b" 175 | } 176 | }, 177 | { 178 | "Batch": 1, 179 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112", 180 | "BlindedElement": "88287e553939090b888ddc15913e1807dc4757215555e1c3a79488ef311594729c7fa74c772a732b78440b7d66d0aa35f3bb316f1d93e1b2", 181 | "EvaluationElement": "c00978c73e8e4ee1d447ab0d3ad1754055e72cc85c08e3a0db170909a9c61cbff1f1e7015f289e3038b0f341faea5d7780c130106065c231", 182 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 183 | "Output": "862952380e07ec840d9f6e6f909c5a25d16c3dacb586d89a181b4aa7380c959baa8c480fe8e6c64e089d68ea7aeeb5817bd524d7577905b5bab487690048c941", 184 | "Proof": { 185 | "proof": "7a2831a6b237e11ac1657d440df93bc5ce00f552e6020a99d5c956ffc4d07b5ade3e82ecdc257fd53d76239e733e0a1313e84ce16cc0d82734806092a693d7e8d3c420c2cb6ccd5d0ca32514fb78e9ad0973ebdcb52eba438fc73948d76339ee710121d83e2fe6f001cfdf551aff9f36", 186 | "r": "b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b" 187 | } 188 | }, 189 | { 190 | "Batch": 2, 191 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112,b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b", 192 | "BlindedElement": "7261bbc335c664ba788f1b1a1a4cd5190cc30e787ef277665ac1d314f8861e3ec11854ce3ddd42035d9e0f5cddde324c332d8c880abc00eb,2e15f393c035492a1573627a3606e528c6294c767c8d43b8c691ef70a52cc7dc7d1b53fe458350a270abb7c231b87ba58266f89164f714d9", 193 | "EvaluationElement": "ca1491a526c28d880806cf0fb0122222392cf495657be6e4c9d203bceffa46c86406caf8217859d3fb259077af68e5d41b3699410781f467,8ec68e9871b296e81c55647ce64a04fe75d19932f1400544cd601468c60f998408bbb546601d4a636e8be279e558d70b95c8d4a4f61892be", 194 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 195 | "Output": "e2ac40b634f36cccd8262b285adff7c9dcc19cd308564a5f4e581d1a8535773b86fa4fc9f2203c370763695c5093aea4a7aedec4488b1340ba3bf663a23098c1,862952380e07ec840d9f6e6f909c5a25d16c3dacb586d89a181b4aa7380c959baa8c480fe8e6c64e089d68ea7aeeb5817bd524d7577905b5bab487690048c941", 196 | "Proof": { 197 | "proof": "167d922f0a6ffa845eed07f8aa97b6ac746d902ecbeb18f49c009adc0521eab1e4d275b74a2dc266b7a194c854e85e7eb54a9a36376dfc04ec7f3bd55fc9618c3970cb548e064f8a2f06183a5702933dbc3e4c25a73438f2108ee1981c306181003c7ea92fce963ec7b4ba4f270e6d38", 198 | "r": "63798726803c9451ba405f00ef3acb633ddf0c420574a2ec6cbf28f840800e355c9fbaac10699686de2724ed22e797a00f3bd93d105a7f23" 199 | } 200 | } 201 | ] 202 | }, 203 | { 204 | "groupDST": "48617368546f47726f75702d4f50524656312d022d64656361663434382d5348414b45323536", 205 | "hash": "SHAKE_256", 206 | "identifier": "decaf448-SHAKE256", 207 | "keyInfo": "74657374206b6579", 208 | "mode": 2, 209 | "pkSm": "6c9d12723a5bbcf305522cc04b4a34d9ced2e12831826018ea7b5dcf5452647ad262113059bf0f6e4354319951b9d513c74f29cb0eec38c1", 210 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 211 | "skSm": "792a10dcbd3ba4a52a054f6f39186623208695301e7adb9634b74709ab22de402990eb143fd7c67ac66be75e0609705ecea800992aac8e19", 212 | "vectors": [ 213 | { 214 | "Batch": 1, 215 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112", 216 | "BlindedElement": "161183c13c6cb33b0e4f9b7365f8c5c12d13c72f8b62d276ca09368d093dce9b42198276b9e9d870ac392dda53efd28d1b7e6e8c060cdc42", 217 | "EvaluationElement": "06ec89dfde25bb2a6f0145ac84b91ac277b35de39ad1d6f402a8e46414952ce0d9ea1311a4ece283e2b01558c7078b040cfaa40dd63b3e6c", 218 | "Info": "7465737420696e666f", 219 | "Input": "00", 220 | "Output": "4423f6dcc1740688ea201de57d76824d59cd6b859e1f9884b7eebc49b0b971358cf9cb075df1536a8ea31bcf55c3e31c2ba9cfa8efe54448d17091daeb9924ed", 221 | "Proof": { 222 | "proof": "66caee75bf2460429f620f6ad3e811d524cb8ddd848a435fc5d89af48877abf6506ee341a0b6f67c2d76cd021e5f3d1c9abe5aa9f0dce016da746135fedba2af41ed1d01659bfd6180d96bc1b7f320c0cb6926011ce392ecca748662564892bae66516acaac6ca39aadf6fcca95af406", 223 | "r": "b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b" 224 | } 225 | }, 226 | { 227 | "Batch": 1, 228 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112", 229 | "BlindedElement": "12082b6a381c6c51e85d00f2a3d828cdeab3f5cb19a10b9c014c33826764ab7e7cfb8b4ff6f411bddb2d64e62a472af1cd816e5b712790c6", 230 | "EvaluationElement": "f2919b7eedc05ab807c221fce2b12c4ae9e19e6909c4784564b690d1972d2994ca623f273afc67444d84ea40cbc58fcdab7945f321a52848", 231 | "Info": "7465737420696e666f", 232 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 233 | "Output": "8691905500510843902c44bdd9730ab9dc3925aa58ff9dd42765a2baf633126de0c3adb93bef5652f38e5827b6396e87643960163a560fc4ac9738c8de4e4a8d", 234 | "Proof": { 235 | "proof": "a295677c54d1bc4286330907fc2490a7de163da26f9ce03a462a452fea422b19ade296ba031359b3b6841e48455d20519ad01b4ac4f0b92e76d3cf16fbef0a3f72791a8401ef2d7081d361e502e96b2c60608b9fa566f43d4611c2f161d83aabef7f8017332b26ed1daaf80440772022", 236 | "r": "b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b" 237 | } 238 | }, 239 | { 240 | "Batch": 2, 241 | "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa3833a26e9388336361686ff1f83df55046504dfecad8549ba112,b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b", 242 | "BlindedElement": "161183c13c6cb33b0e4f9b7365f8c5c12d13c72f8b62d276ca09368d093dce9b42198276b9e9d870ac392dda53efd28d1b7e6e8c060cdc42,fc8847d43fb4cea4e408f585661a8f2867533fa91d22155d3127a22f18d3b007add480f7d300bca93fa47fe87ae06a57b7d0f0d4c30b12f0", 243 | "EvaluationElement": "06ec89dfde25bb2a6f0145ac84b91ac277b35de39ad1d6f402a8e46414952ce0d9ea1311a4ece283e2b01558c7078b040cfaa40dd63b3e6c,2e74c626d07de49b1c8c21d87120fd78105f485e36816af9bde3e3efbeef76815326062fd333925b66c5ce5a20f100bf01770c16609f990a", 244 | "Info": "7465737420696e666f", 245 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 246 | "Output": "4423f6dcc1740688ea201de57d76824d59cd6b859e1f9884b7eebc49b0b971358cf9cb075df1536a8ea31bcf55c3e31c2ba9cfa8efe54448d17091daeb9924ed,8691905500510843902c44bdd9730ab9dc3925aa58ff9dd42765a2baf633126de0c3adb93bef5652f38e5827b6396e87643960163a560fc4ac9738c8de4e4a8d", 247 | "Proof": { 248 | "proof": "fd94db736f97ea4efe9d0d4ad2933072697a6bbeb32834057b23edf7c7009f011dfa72157f05d2a507c2bbf0b54cad99ab99de05921c021fda7d70e65bcecdb05f9a30154127ace983c74d10fd910b554c5e95f6bd1565fd1f3dbbe3c523ece5c72d57a559b7be1368c4786db4a3c910", 249 | "r": "63798726803c9451ba405f00ef3acb633ddf0c420574a2ec6cbf28f840800e355c9fbaac10699686de2724ed22e797a00f3bd93d105a7f23" 250 | } 251 | } 252 | ] 253 | }, 254 | { 255 | "groupDST": "48617368546f47726f75702d4f50524656312d002d503235362d534841323536", 256 | "hash": "SHA256", 257 | "identifier": "P256-SHA256", 258 | "keyInfo": "74657374206b6579", 259 | "mode": 0, 260 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 261 | "skSm": "159749d750713afe245d2d39ccfaae8381c53ce92d098a9375ee70739c7ac0bf", 262 | "vectors": [ 263 | { 264 | "Batch": 1, 265 | "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 266 | "BlindedElement": "03723a1e5c09b8b9c18d1dcbca29e8007e95f14f4732d9346d490ffc195110368d", 267 | "EvaluationElement": "030de02ffec47a1fd53efcdd1c6faf5bdc270912b8749e783c7ca75bb412958832", 268 | "Input": "00", 269 | "Output": "a0b34de5fa4c5b6da07e72af73cc507cceeb48981b97b7285fc375345fe495dd" 270 | }, 271 | { 272 | "Batch": 1, 273 | "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 274 | "BlindedElement": "03cc1df781f1c2240a64d1c297b3f3d16262ef5d4cf102734882675c26231b0838", 275 | "EvaluationElement": "03a0395fe3828f2476ffcd1f4fe540e5a8489322d398be3c4e5a869db7fcb7c52c", 276 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 277 | "Output": "c748ca6dd327f0ce85f4ae3a8cd6d4d5390bbb804c9e12dcf94f853fece3dcce" 278 | } 279 | ] 280 | }, 281 | { 282 | "groupDST": "48617368546f47726f75702d4f50524656312d012d503235362d534841323536", 283 | "hash": "SHA256", 284 | "identifier": "P256-SHA256", 285 | "keyInfo": "74657374206b6579", 286 | "mode": 1, 287 | "pkSm": "03e17e70604bcabe198882c0a1f27a92441e774224ed9c702e51dd17038b102462", 288 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 289 | "skSm": "ca5d94c8807817669a51b196c34c1b7f8442fde4334a7121ae4736364312fca6", 290 | "vectors": [ 291 | { 292 | "Batch": 1, 293 | "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 294 | "BlindedElement": "02dd05901038bb31a6fae01828fd8d0e49e35a486b5c5d4b4994013648c01277da", 295 | "EvaluationElement": "0209f33cab60cf8fe69239b0afbcfcd261af4c1c5632624f2e9ba29b90ae83e4a2", 296 | "Input": "00", 297 | "Output": "0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a645a1", 298 | "Proof": { 299 | "proof": "e7c2b3c5c954c035949f1f74e6bce2ed539a3be267d1481e9ddb178533df4c2664f69d065c604a4fd953e100b856ad83804eb3845189babfa5a702090d6fc5fa", 300 | "r": "f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" 301 | } 302 | }, 303 | { 304 | "Batch": 1, 305 | "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 306 | "BlindedElement": "03cd0f033e791c4d79dfa9c6ed750f2ac009ec46cd4195ca6fd3800d1e9b887dbd", 307 | "EvaluationElement": "030d2985865c693bf7af47ba4d3a3813176576383d19aff003ef7b0784a0d83cf1", 308 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 309 | "Output": "771e10dcd6bcd3664e23b8f2a710cfaaa8357747c4a8cbba03133967b5c24f18", 310 | "Proof": { 311 | "proof": "2787d729c57e3d9512d3aa9e8708ad226bc48e0f1750b0767aaff73482c44b8d2873d74ec88aebd3504961acea16790a05c542d9fbff4fe269a77510db00abab", 312 | "r": "f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" 313 | } 314 | }, 315 | { 316 | "Batch": 2, 317 | "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364,f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1", 318 | "BlindedElement": "02dd05901038bb31a6fae01828fd8d0e49e35a486b5c5d4b4994013648c01277da,03462e9ae64cae5b83ba98a6b360d942266389ac369b923eb3d557213b1922f8ab", 319 | "EvaluationElement": "0209f33cab60cf8fe69239b0afbcfcd261af4c1c5632624f2e9ba29b90ae83e4a2,02bb24f4d838414aef052a8f044a6771230ca69c0a5677540fff738dd31bb69771", 320 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 321 | "Output": "0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a645a1,771e10dcd6bcd3664e23b8f2a710cfaaa8357747c4a8cbba03133967b5c24f18", 322 | "Proof": { 323 | "proof": "bdcc351707d02a72ce49511c7db990566d29d6153ad6f8982fad2b435d6ce4d60da1e6b3fa740811bde34dd4fe0aa1b5fe6600d0440c9ddee95ea7fad7a60cf2", 324 | "r": "350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba51943c8026877963" 325 | } 326 | } 327 | ] 328 | }, 329 | { 330 | "groupDST": "48617368546f47726f75702d4f50524656312d022d503235362d534841323536", 331 | "hash": "SHA256", 332 | "identifier": "P256-SHA256", 333 | "keyInfo": "74657374206b6579", 334 | "mode": 2, 335 | "pkSm": "030d7ff077fddeec965db14b794f0cc1ba9019b04a2f4fcc1fa525dedf72e2a3e3", 336 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 337 | "skSm": "6ad2173efa689ef2c27772566ad7ff6e2d59b3b196f00219451fb2c89ee4dae2", 338 | "vectors": [ 339 | { 340 | "Batch": 1, 341 | "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 342 | "BlindedElement": "031563e127099a8f61ed51eeede05d747a8da2be329b40ba1f0db0b2bd9dd4e2c0", 343 | "EvaluationElement": "02c5e5300c2d9e6ba7f3f4ad60500ad93a0157e6288eb04b67e125db024a2c74d2", 344 | "Info": "7465737420696e666f", 345 | "Input": "00", 346 | "Output": "193a92520bd8fd1f37accb918040a57108daa110dc4f659abe212636d245c592", 347 | "Proof": { 348 | "proof": "f8a33690b87736c854eadfcaab58a59b8d9c03b569110b6f31f8bf7577f3fbb85a8a0c38468ccde1ba942be501654adb106167c8eb178703ccb42bccffb9231a", 349 | "r": "f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" 350 | } 351 | }, 352 | { 353 | "Batch": 1, 354 | "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 355 | "BlindedElement": "021a440ace8ca667f261c10ac7686adc66a12be31e3520fca317643a1eee9dcd4d", 356 | "EvaluationElement": "0208ca109cbae44f4774fc0bdd2783efdcb868cb4523d52196f700210e777c5de3", 357 | "Info": "7465737420696e666f", 358 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 359 | "Output": "1e6d164cfd835d88a31401623549bf6b9b306628ef03a7962921d62bc5ffce8c", 360 | "Proof": { 361 | "proof": "043a8fb7fc7fd31e35770cabda4753c5bf0ecc1e88c68d7d35a62bf2631e875af4613641be2d1875c31d1319d191c4bbc0d04875f4fd03c31d3d17dd8e069b69", 362 | "r": "f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" 363 | } 364 | }, 365 | { 366 | "Batch": 2, 367 | "Blind": "3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364,f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1", 368 | "BlindedElement": "031563e127099a8f61ed51eeede05d747a8da2be329b40ba1f0db0b2bd9dd4e2c0,03ca4ff41c12fadd7a0bc92cf856732b21df652e01a3abdf0fa8847da053db213c", 369 | "EvaluationElement": "02c5e5300c2d9e6ba7f3f4ad60500ad93a0157e6288eb04b67e125db024a2c74d2,02f0b6bcd467343a8d8555a99dc2eed0215c71898c5edb77a3d97ddd0dbad478e8", 370 | "Info": "7465737420696e666f", 371 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 372 | "Output": "193a92520bd8fd1f37accb918040a57108daa110dc4f659abe212636d245c592,1e6d164cfd835d88a31401623549bf6b9b306628ef03a7962921d62bc5ffce8c", 373 | "Proof": { 374 | "proof": "8fbd85a32c13aba79db4b42e762c00687d6dbf9c8cb97b2a225645ccb00d9d7580b383c885cdfd07df448d55e06f50f6173405eee5506c0ed0851ff718d13e68", 375 | "r": "350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba51943c8026877963" 376 | } 377 | } 378 | ] 379 | }, 380 | { 381 | "groupDST": "48617368546f47726f75702d4f50524656312d002d503338342d534841333834", 382 | "hash": "SHA384", 383 | "identifier": "P384-SHA384", 384 | "keyInfo": "74657374206b6579", 385 | "mode": 0, 386 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 387 | "skSm": "dfe7ddc41a4646901184f2b432616c8ba6d452f9bcd0c4f75a5150ef2b2ed02ef40b8b92f60ae591bcabd72a6518f188", 388 | "vectors": [ 389 | { 390 | "Batch": 1, 391 | "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 392 | "BlindedElement": "02a36bc90e6db34096346eaf8b7bc40ee1113582155ad3797003ce614c835a874343701d3f2debbd80d97cbe45de6e5f1f", 393 | "EvaluationElement": "03af2a4fc94770d7a7bf3187ca9cc4faf3732049eded2442ee50fbddda58b70ae2999366f72498cdbc43e6f2fc184afe30", 394 | "Input": "00", 395 | "Output": "ed84ad3f31a552f0456e58935fcc0a3039db42e7f356dcb32aa6d487b6b815a07d5813641fb1398c03ddab5763874357" 396 | }, 397 | { 398 | "Batch": 1, 399 | "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 400 | "BlindedElement": "02def6f418e3484f67a124a2ce1bfb19de7a4af568ede6a1ebb2733882510ddd43d05f2b1ab5187936a55e50a847a8b900", 401 | "EvaluationElement": "034e9b9a2960b536f2ef47d8608b21597ba400d5abfa1825fd21c36b75f927f396bf3716c96129d1fa4a77fa1d479c8d7b", 402 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 403 | "Output": "dd4f29da869ab9355d60617b60da0991e22aaab243a3460601e48b075859d1c526d36597326f1b985778f781a1682e75" 404 | } 405 | ] 406 | }, 407 | { 408 | "groupDST": "48617368546f47726f75702d4f50524656312d012d503338342d534841333834", 409 | "hash": "SHA384", 410 | "identifier": "P384-SHA384", 411 | "keyInfo": "74657374206b6579", 412 | "mode": 1, 413 | "pkSm": "031d689686c611991b55f1a1d8f4305ccd6cb719446f660a30db61b7aa87b46acf59b7c0d4a9077b3da21c25dd482229a0", 414 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 415 | "skSm": "051646b9e6e7a71ae27c1e1d0b87b4381db6d3595eeeb1adb41579adbf992f4278f9016eafc944edaa2b43183581779d", 416 | "vectors": [ 417 | { 418 | "Batch": 1, 419 | "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 420 | "BlindedElement": "02d338c05cbecb82de13d6700f09cb61190543a7b7e2c6cd4fca56887e564ea82653b27fdad383995ea6d02cf26d0e24d9", 421 | "EvaluationElement": "02a7bba589b3e8672aa19e8fd258de2e6aae20101c8d761246de97a6b5ee9cf105febce4327a326255a3c604f63f600ef6", 422 | "Input": "00", 423 | "Output": "3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d026b4a622beab60220bf19078bca35a529b35c", 424 | "Proof": { 425 | "proof": "bfc6cf3859127f5fe25548859856d6b7fa1c7459f0ba5712a806fc091a3000c42d8ba34ff45f32a52e40533efd2a03bc87f3bf4f9f58028297ccb9ccb18ae7182bcd1ef239df77e3be65ef147f3acf8bc9cbfc5524b702263414f043e3b7ca2e", 426 | "r": "803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" 427 | } 428 | }, 429 | { 430 | "Batch": 1, 431 | "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 432 | "BlindedElement": "02f27469e059886f221be5f2cca03d2bdc61e55221721c3b3e56fc012e36d31ae5f8dc058109591556a6dbd3a8c69c433b", 433 | "EvaluationElement": "03f16f903947035400e96b7f531a38d4a07ac89a80f89d86a1bf089c525a92c7f4733729ca30c56ce78b1ab4f7d92db8b4", 434 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 435 | "Output": "b91c70ea3d4d62ba922eb8a7d03809a441e1c3c7af915cbc2226f485213e895942cd0f8580e6d99f82221e66c40d274f", 436 | "Proof": { 437 | "proof": "d005d6daaad7571414c1e0c75f7e57f2113ca9f4604e84bc90f9be52da896fff3bee496dcde2a578ae9df315032585f801fb21c6080ac05672b291e575a40295b306d967717b28e08fcc8ad1cab47845d16af73b3e643ddcc191208e71c64630", 438 | "r": "803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" 439 | } 440 | }, 441 | { 442 | "Batch": 2, 443 | "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364,803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1", 444 | "BlindedElement": "02d338c05cbecb82de13d6700f09cb61190543a7b7e2c6cd4fca56887e564ea82653b27fdad383995ea6d02cf26d0e24d9,02fa02470d7f151018b41e82223c32fad824de6ad4b5ce9f8e9f98083c9a726de9a1fc39d7a0cb6f4f188dd9cea01474cd", 445 | "EvaluationElement": "02a7bba589b3e8672aa19e8fd258de2e6aae20101c8d761246de97a6b5ee9cf105febce4327a326255a3c604f63f600ef6,028e9e115625ff4c2f07bf87ce3fd73fc77994a7a0c1df03d2a630a3d845930e2e63a165b114d98fe34e61b68d23c0b50a", 446 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 447 | "Output": "3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d026b4a622beab60220bf19078bca35a529b35c,b91c70ea3d4d62ba922eb8a7d03809a441e1c3c7af915cbc2226f485213e895942cd0f8580e6d99f82221e66c40d274f", 448 | "Proof": { 449 | "proof": "6d8dcbd2fc95550a02211fb78afd013933f307d21e7d855b0b1ed0af78076d8137ad8b0a1bfa05676d325249c1dbb9a52bd81b1c2b7b0efc77cf7b278e1c947f6283f1d4c513053fc0ad19e026fb0c30654b53d9cea4b87b037271b5d2e2d0ea", 450 | "r": "a097e722ed2427de86966910acba9f5c350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba51943c8026877963" 451 | } 452 | } 453 | ] 454 | }, 455 | { 456 | "groupDST": "48617368546f47726f75702d4f50524656312d022d503338342d534841333834", 457 | "hash": "SHA384", 458 | "identifier": "P384-SHA384", 459 | "keyInfo": "74657374206b6579", 460 | "mode": 2, 461 | "pkSm": "02f00f0f1de81e5d6cf18140d4926ffdc9b1898c48dc49657ae36eb1e45deb8b951aaf1f10c82d2eaa6d02aafa3f10d2b6", 462 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 463 | "skSm": "5b2690d6954b8fbb159f19935d64133f12770c00b68422559c65431942d721ff79d47d7a75906c30b7818ec0f38b7fb2", 464 | "vectors": [ 465 | { 466 | "Batch": 1, 467 | "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 468 | "BlindedElement": "03859b36b95e6564faa85cd3801175eda2949707f6aa0640ad093cbf8ad2f58e762f08b56b2a1b42a64953aaf49cbf1ae3", 469 | "EvaluationElement": "0220710e2e00306453f5b4f574cb6a512453f35c45080d09373e190c19ce5b185914fbf36582d7e0754bb7c8b683205b91", 470 | "Info": "7465737420696e666f", 471 | "Input": "00", 472 | "Output": "0188653cfec38119a6c7dd7948b0f0720460b4310e40824e048bf82a16527303ed449a08caf84272c3bbc972ede797df", 473 | "Proof": { 474 | "proof": "82a17ef41c8b57f1e3122311b4d5cd39a63df0f67443ef18d961f9b659c1601ced8d3c64b294f604319ca80230380d437a49c7af0d620e22116669c008ebb767d90283d573b49cdb49e3725889620924c2c4b047a2a6225a3ba27e640ebddd33", 475 | "r": "803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" 476 | } 477 | }, 478 | { 479 | "Batch": 1, 480 | "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 481 | "BlindedElement": "03f7efcb4aaf000263369d8a0621cb96b81b3206e99876de2a00699ed4c45acf3969cd6e2319215395955d3f8d8cc1c712", 482 | "EvaluationElement": "034993c818369927e74b77c400376fd1ae29b6ac6c6ddb776cf10e4fbc487826531b3cf0b7c8ca4d92c7af90c9def85ce6", 483 | "Info": "7465737420696e666f", 484 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 485 | "Output": "ff2a527a21cc43b251a567382677f078c6e356336aec069dea8ba36995343ca3b33bb5d6cf15be4d31a7e6d75b30d3f5", 486 | "Proof": { 487 | "proof": "693471b5dff0cd6a5c00ea34d7bf127b2795164e3bdb5f39a1e5edfbd13e443bc516061cd5b8449a473c2ceeccada9f3e5b57302e3d7bc5e28d38d6e3a3056e1e73b6cc030f5180f8a1ffa45aa923ee66d2ad0a07b500f2acc7fb99b5506465c", 488 | "r": "803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" 489 | } 490 | }, 491 | { 492 | "Batch": 2, 493 | "Blind": "504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364,803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1", 494 | "BlindedElement": "03859b36b95e6564faa85cd3801175eda2949707f6aa0640ad093cbf8ad2f58e762f08b56b2a1b42a64953aaf49cbf1ae3,021a65d618d645f1a20bc33b06deaa7e73d6d634c8a56a3d02b53a732b69a5c53c5a207ea33d5afdcde9a22d59726bce51", 495 | "EvaluationElement": "0220710e2e00306453f5b4f574cb6a512453f35c45080d09373e190c19ce5b185914fbf36582d7e0754bb7c8b683205b91,02017657b315ec65ef861505e596c8645d94685dd7602cdd092a8f1c1c0194a5d0485fe47d071d972ab514370174cc23f5", 496 | "Info": "7465737420696e666f", 497 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 498 | "Output": "0188653cfec38119a6c7dd7948b0f0720460b4310e40824e048bf82a16527303ed449a08caf84272c3bbc972ede797df,ff2a527a21cc43b251a567382677f078c6e356336aec069dea8ba36995343ca3b33bb5d6cf15be4d31a7e6d75b30d3f5", 499 | "Proof": { 500 | "proof": "4a0b2fe96d5b2a046a0447fe079b77859ef11a39a3520d6ff7c626aad9b473b724fb0cf188974ec961710a62162a83e97e0baa9eeada73397032d928b3e97b1ea92ad9458208302be3681b8ba78bcc17745bac00f84e0fdc98a6a8cba009c080", 501 | "r": "a097e722ed2427de86966910acba9f5c350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba51943c8026877963" 502 | } 503 | } 504 | ] 505 | }, 506 | { 507 | "groupDST": "48617368546f47726f75702d4f50524656312d002d503532312d534841353132", 508 | "hash": "SHA512", 509 | "identifier": "P521-SHA512", 510 | "keyInfo": "74657374206b6579", 511 | "mode": 0, 512 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 513 | "skSm": "0153441b8faedb0340439036d6aed06d1217b34c42f17f8db4c5cc610a4a955d698a688831b16d0dc7713a1aa3611ec60703bffc7dc9c84e3ed673b3dbe1d5fccea6", 514 | "vectors": [ 515 | { 516 | "Batch": 1, 517 | "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 518 | "BlindedElement": "0300e78bf846b0e1e1a3c320e353d758583cd876df56100a3a1e62bacba470fa6e0991be1be80b721c50c5fd0c672ba764457acc18c6200704e9294fbf28859d916351", 519 | "EvaluationElement": "030166371cf827cb2fb9b581f97907121a16e2dc5d8b10ce9f0ede7f7d76a0d047657735e8ad07bcda824907b3e5479bd72cdef6b839b967ba5c58b118b84d26f2ba07", 520 | "Input": "00", 521 | "Output": "26232de6fff83f812adadadb6cc05d7bbeee5dca043dbb16b03488abb9981d0a1ef4351fad52dbd7e759649af393348f7b9717566c19a6b8856284d69375c809" 522 | }, 523 | { 524 | "Batch": 1, 525 | "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 526 | "BlindedElement": "0300c28e57e74361d87e0c1874e5f7cc1cc796d61f9cad50427cf54655cdb455613368d42b27f94bf66f59f53c816db3e95e68e1b113443d66a99b3693bab88afb556b", 527 | "EvaluationElement": "0301ad453607e12d0cc11a3359332a40c3a254eaa1afc64296528d55bed07ba322e72e22cf3bcb50570fd913cb54f7f09c17aff8787af75f6a7faf5640cbb2d9620a6e", 528 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 529 | "Output": "ad1f76ef939042175e007738906ac0336bbd1d51e287ebaa66901abdd324ea3ffa40bfc5a68e7939c2845e0fd37a5a6e76dadb9907c6cc8579629757fd4d04ba" 530 | } 531 | ] 532 | }, 533 | { 534 | "groupDST": "48617368546f47726f75702d4f50524656312d012d503532312d534841353132", 535 | "hash": "SHA512", 536 | "identifier": "P521-SHA512", 537 | "keyInfo": "74657374206b6579", 538 | "mode": 1, 539 | "pkSm": "0301505d646f6e4c9102451eb39730c4ba1c4087618641edbdba4a60896b07fd0c9414ce553cbf25b81dfcca50a8f6724ab7a2bc4d0cf736967a287bb6084cc0678ac0", 540 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 541 | "skSm": "015c7fc1b4a0b1390925bae915bd9f3d72009d44d9241b962428aad5d13f22803311e7102632a39addc61ea440810222715c9d2f61f03ea424ec9ab1fe5e31cf9238", 542 | "vectors": [ 543 | { 544 | "Batch": 1, 545 | "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 546 | "BlindedElement": "0301d6e4fb545e043ddb6aee5d5ceeee1b44102615ab04430c27dd0f56988dedcb1df32ef384f160e0e76e718605f14f3f582f9357553d153b996795b4b3628a4f6380", 547 | "EvaluationElement": "03013fdeaf887f3d3d283a79e696a54b66ff0edcb559265e204a958acf840e0930cc147e2a6835148d8199eebc26c03e9394c9762a1c991dde40bca0f8ca003eefb045", 548 | "Input": "00", 549 | "Output": "5e003d9b2fb540b3d4bab5fedd154912246da1ee5e557afd8f56415faa1a0fadff6517da802ee254437e4f60907b4cda146e7ba19e249eef7be405549f62954b", 550 | "Proof": { 551 | "proof": "0077fcc8ec6d059d7759b0a61f871e7c1dadc65333502e09a51994328f79e5bda3357b9a4f410a1760a3612c2f8f27cb7cb032951c047cc66da60da583df7b247edd0188e5eb99c71799af1d80d643af16ffa1545acd9e9233fbb370455b10eb257ea12a1667c1b4ee5b0ab7c93d50ae89602006960f083ca9adc4f6276c0ad60440393c", 552 | "r": "015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" 553 | } 554 | }, 555 | { 556 | "Batch": 1, 557 | "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 558 | "BlindedElement": "03005b05e656cb609ce5ff5faf063bb746d662d67bbd07c062638396f52f0392180cf2365cabb0ece8e19048961d35eeae5d5fa872328dce98df076ee154dd191c615e", 559 | "EvaluationElement": "0301b19fcf482b1fff04754e282292ed736c5f0aa080d4f42663cd3a416c6596f03129e8e096d8671fe5b0d19838312c511d2ce08d431e43e3ef06199d8cab7426238d", 560 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 561 | "Output": "fa15eebba81ecf40954f7135cb76f69ef22c6bae394d1a4362f9b03066b54b6604d39f2e53369ca6762a3d9787e230e832aa85955af40ecb8deebb009a8cf474", 562 | "Proof": { 563 | "proof": "01ec9fece444caa6a57032e8963df0e945286f88fbdf233fb5101f0924f7ea89c47023f5f72f240e61991fd33a299b5b38c45a5e2dd1a67b072e59dfe86708a359c701e38d383c60cf6969463bcf13251bedad47b7941f52e409a3591398e27924410b18a301c0e19f527cad504fa08388050ac634e1b05c5216d337742f2754e1fc502f", 564 | "r": "015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" 565 | } 566 | }, 567 | { 568 | "Batch": 2, 569 | "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364,015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1", 570 | "BlindedElement": "0301d6e4fb545e043ddb6aee5d5ceeee1b44102615ab04430c27dd0f56988dedcb1df32ef384f160e0e76e718605f14f3f582f9357553d153b996795b4b3628a4f6380,0301403b597538b939b450c93586ba275f9711ba07e42364bac1d5769c6824a8b55be6f9a536df46d952b11ab2188363b3d6737635d9543d4dba14a6e19421b9245bf5", 571 | "EvaluationElement": "03013fdeaf887f3d3d283a79e696a54b66ff0edcb559265e204a958acf840e0930cc147e2a6835148d8199eebc26c03e9394c9762a1c991dde40bca0f8ca003eefb045,03001f96424497e38c46c904978c2fa1636c5c3dd2e634a85d8a7265977c5dce1f02c7e6c118479f0751767b91a39cce6561998258591b5d7c1bb02445a9e08e4f3e8d", 572 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 573 | "Output": "5e003d9b2fb540b3d4bab5fedd154912246da1ee5e557afd8f56415faa1a0fadff6517da802ee254437e4f60907b4cda146e7ba19e249eef7be405549f62954b,fa15eebba81ecf40954f7135cb76f69ef22c6bae394d1a4362f9b03066b54b6604d39f2e53369ca6762a3d9787e230e832aa85955af40ecb8deebb009a8cf474", 574 | "Proof": { 575 | "proof": "00b4d215c8405e57c7a4b53398caf55f1f1623aaeb22408ddb9ea29130909b3f95dbb1ff366e81e86e918f9f2fd8b80dbb344cd498c9499d112905e585417e0068c600fe5dea18b389ef6c4cc062935607b8ccbbb9a84fba3143868a3e8a58efa0bf6ca642804d09dc06e980f64837811227c4267b217f1099a4e28b0854f4e5ee659796", 576 | "r": "01ec21c7bb69b0734cb48dfd68433dd93b0fa097e722ed2427de86966910acba9f5c350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba51943c8026877963" 577 | } 578 | } 579 | ] 580 | }, 581 | { 582 | "groupDST": "48617368546f47726f75702d4f50524656312d022d503532312d534841353132", 583 | "hash": "SHA512", 584 | "identifier": "P521-SHA512", 585 | "keyInfo": "74657374206b6579", 586 | "mode": 2, 587 | "pkSm": "0301de8ceb9ffe9237b1bba87c320ea0bebcfc3447fe6f278065c6c69886d692d1126b79b6844f829940ace9b52a5e26882cf7cbc9e57503d4cca3cd834584729f812a", 588 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 589 | "skSm": "014893130030ce69cf714f536498a02ff6b396888f9bb507985c32928c4427d6d39de10ef509aca4240e8569e3a88debc0d392e3361bcd934cb9bdd59e339dff7b27", 590 | "vectors": [ 591 | { 592 | "Batch": 1, 593 | "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 594 | "BlindedElement": "020095cff9d7ecf65bdfee4ea92d6e748d60b02de34ad98094f82e25d33a8bf50138ccc2cc633556f1a97d7ea9438cbb394df612f041c485a515849d5ebb2238f2f0e2", 595 | "EvaluationElement": "0301408e9c5be3ffcc1c16e5ae8f8aa68446223b0804b11962e856af5a6d1c65ebbb5db7278c21db4e8cc06d89a35b6804fb1738a295b691638af77aa1327253f26d01", 596 | "Info": "7465737420696e666f", 597 | "Input": "00", 598 | "Output": "808ae5b87662eaaf0b39151dd85991b94c96ef214cb14a68bf5c143954882d330da8953a80eea20788e552bc8bbbfff3100e89f9d6e341197b122c46a208733b", 599 | "Proof": { 600 | "proof": "0106a89a61eee9dd2417d2849a8e2167bc5f56e3aed5a3ff23e22511fa1b37a29ed44d1bbfd6907d99cfbc558a56aec709282415a864a281e49dc53792a4a638a0660034306d64be12a94dcea5a6d664cf76681911c8b9a84d49bf12d4893307ec14436bd05f791f82446c0de4be6c582d373627b51886f76c4788256e3da7ec8fa18a86", 601 | "r": "015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" 602 | } 603 | }, 604 | { 605 | "Batch": 1, 606 | "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364", 607 | "BlindedElement": "030112ea89cf9cf589496189eafc5f9eb13c9f9e170d6ecde7c5b940541cb1a9c5cfeec908b67efe16b81ca00d0ce216e34b3d5f46a658d3fd8573d671bdb6515ed508", 608 | "EvaluationElement": "0200ebc49df1e6fa61f412e6c391e6f074400ecdd2f56c4a8c03fe0f91d9b551f40d4b5258fd891952e8c9b28003bcfa365122e54a5714c8949d5d202767b31b4bf1f6", 609 | "Info": "7465737420696e666f", 610 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 611 | "Output": "27032e24b1a52a82ab7f4646f3c5df0f070f499db98b9c5df33972bd5af5762c3638afae7912a6c1acdb1ae2ab2fa670bd5486c645a0e55412e08d33a4a0d6e3", 612 | "Proof": { 613 | "proof": "0082162c71a7765005cae202d4bd14b84dae63c29067e886b82506992bd994a1c3aac0c1c5309222fe1af8287b6443ed6df5c2e0b0991faddd3564c73c7597aecd9a003b1f1e3c65f28e58ab4e767cfb4adbcaf512441645f4c2aed8bf67d132d966006d35fa71a34145414bf3572c1de1a46c266a344dd9e22e7fb1e90ffba1caf556d9", 614 | "r": "015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1" 615 | } 616 | }, 617 | { 618 | "Batch": 2, 619 | "Blind": "00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad364,015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1", 620 | "BlindedElement": "020095cff9d7ecf65bdfee4ea92d6e748d60b02de34ad98094f82e25d33a8bf50138ccc2cc633556f1a97d7ea9438cbb394df612f041c485a515849d5ebb2238f2f0e2,0201a328cf9f3fdeb86b6db242dd4cbb436b3a488b70b72d2fbbd1e5f50d7b0878b157d6f278c6a95c488f3ad52d6898a421658a82fe7ceb000b01aedea7967522d525", 621 | "EvaluationElement": "0301408e9c5be3ffcc1c16e5ae8f8aa68446223b0804b11962e856af5a6d1c65ebbb5db7278c21db4e8cc06d89a35b6804fb1738a295b691638af77aa1327253f26d01,020062ab51ac3aa829e0f5b7ae50688bcf5f63a18a83a6e0da538666b8d50c7ea2b4ef31f4ac669302318dbebe46660acdda695da30c22cee7ca21f6984a720504502e", 622 | "Info": "7465737420696e666f", 623 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 624 | "Output": "808ae5b87662eaaf0b39151dd85991b94c96ef214cb14a68bf5c143954882d330da8953a80eea20788e552bc8bbbfff3100e89f9d6e341197b122c46a208733b,27032e24b1a52a82ab7f4646f3c5df0f070f499db98b9c5df33972bd5af5762c3638afae7912a6c1acdb1ae2ab2fa670bd5486c645a0e55412e08d33a4a0d6e3", 625 | "Proof": { 626 | "proof": "00731738844f739bca0cca9d1c8bea204bed4fd00285785738b985763741de5cdfa275152d52b6a2fdf7792ef3779f39ba34581e56d62f78ecad5b7f8083f384961501cd4b43713253c022692669cf076b1d382ecd8293c1de69ea569737f37a24772ab73517983c1e3db5818754ba1f008076267b8058b6481949ae346cdc17a8455fe2", 627 | "r": "01ec21c7bb69b0734cb48dfd68433dd93b0fa097e722ed2427de86966910acba9f5c350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba51943c8026877963" 628 | } 629 | } 630 | ] 631 | } 632 | ] 633 | -------------------------------------------------------------------------------- /releases/draft-sullivan-cfrg-voprf-00.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Network Working Group N. Sullivan 6 | Internet-Draft Cloudflare 7 | Intended status: Informational C. Wood 8 | Expires: September 6, 2018 Apple Inc. 9 | March 05, 2018 10 | 11 | 12 | Verifiable Oblivious Pseudorandom Functions (VOPRFs) 13 | draft-sullivan-cfrg-voprf-00 14 | 15 | Abstract 16 | 17 | A Verifiable Oblivious Pseudorandom Function (VOPRF) is a two-party 18 | protocol for computing the output of a PRF that is symmetrically 19 | verifiable. In summary, the PRF key holder learns nothing of the 20 | input while simultaneously providing proof that its private key was 21 | used during execution. VOPRFs are useful for computing one-time 22 | unlinkable tokens that are verifiable by secret key holders. This 23 | document specifies a VOPRF construction based on Elliptic Curves. 24 | 25 | Status of This Memo 26 | 27 | This Internet-Draft is submitted in full conformance with the 28 | provisions of BCP 78 and BCP 79. 29 | 30 | Internet-Drafts are working documents of the Internet Engineering 31 | Task Force (IETF). Note that other groups may also distribute 32 | working documents as Internet-Drafts. The list of current Internet- 33 | Drafts is at https://datatracker.ietf.org/drafts/current/. 34 | 35 | Internet-Drafts are draft documents valid for a maximum of six months 36 | and may be updated, replaced, or obsoleted by other documents at any 37 | time. It is inappropriate to use Internet-Drafts as reference 38 | material or to cite them other than as "work in progress." 39 | 40 | This Internet-Draft will expire on September 6, 2018. 41 | 42 | Copyright Notice 43 | 44 | Copyright (c) 2018 IETF Trust and the persons identified as the 45 | document authors. All rights reserved. 46 | 47 | This document is subject to BCP 78 and the IETF Trust's Legal 48 | Provisions Relating to IETF Documents 49 | (https://trustee.ietf.org/license-info) in effect on the date of 50 | publication of this document. Please review these documents 51 | carefully, as they describe your rights and restrictions with respect 52 | to this document. Code Components extracted from this document must 53 | 54 | 55 | 56 | Sullivan & Wood Expires September 6, 2018 [Page 1] 57 | 58 | Internet-Draft VOPRFs March 2018 59 | 60 | 61 | include Simplified BSD License text as described in Section 4.e of 62 | the Trust Legal Provisions and are provided without warranty as 63 | described in the Simplified BSD License. 64 | 65 | Table of Contents 66 | 67 | 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2 68 | 1.1. Terminology . . . . . . . . . . . . . . . . . . . . . . . 3 69 | 1.2. Requirements . . . . . . . . . . . . . . . . . . . . . . 3 70 | 2. Background . . . . . . . . . . . . . . . . . . . . . . . . . 4 71 | 3. Security Properties . . . . . . . . . . . . . . . . . . . . . 4 72 | 4. Elliptic Curve VOPRF Protocol . . . . . . . . . . . . . . . . 5 73 | 4.1. Algorithmic Details . . . . . . . . . . . . . . . . . . . 6 74 | 4.1.1. ECVOPRF_Blind . . . . . . . . . . . . . . . . . . . . 6 75 | 4.1.2. ECVOPRF_Sign . . . . . . . . . . . . . . . . . . . . 7 76 | 4.1.3. ECVOPRF_Unblind . . . . . . . . . . . . . . . . . . . 7 77 | 4.1.4. ECVOPRF_Finalize . . . . . . . . . . . . . . . . . . 8 78 | 5. NIZK Discrete Logarithm Equality Proof . . . . . . . . . . . 8 79 | 5.1. DLEQ_Generate . . . . . . . . . . . . . . . . . . . . . . 9 80 | 5.2. DLEQ_Verify . . . . . . . . . . . . . . . . . . . . . . . 9 81 | 5.3. Group and Hash Function Instantiations . . . . . . . . . 9 82 | 6. Security Considerations . . . . . . . . . . . . . . . . . . . 11 83 | 6.1. Timing Leaks . . . . . . . . . . . . . . . . . . . . . . 12 84 | 7. Privacy Considerations . . . . . . . . . . . . . . . . . . . 12 85 | 7.1. Key Consistency . . . . . . . . . . . . . . . . . . . . . 12 86 | 8. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 12 87 | 9. Contributors . . . . . . . . . . . . . . . . . . . . . . . . 12 88 | 10. Normative References . . . . . . . . . . . . . . . . . . . . 12 89 | Appendix A. Test Vectors . . . . . . . . . . . . . . . . . . . . 13 90 | Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 16 91 | 92 | 1. Introduction 93 | 94 | A pseudorandom function (PRF) F(k, x) is an efficiently computable 95 | function with secret key k on input x. Roughly, F is pseudorandom if 96 | the output y = F(k, x) is indistinguishable from uniformly sampling 97 | any element in F's range for random choice of k. An oblivious PRF 98 | (OPRF) is a two-party protocol between a prover P and verifier V 99 | where P holds a PRF key k and V holds some input x. The protocol 100 | allows both parties to cooperate in computing F(k, x) with P's secret 101 | key k and V's input x such that: V learns F(k, x) without learning 102 | anything about k; and P does not learn anything about x. A 103 | Verifiable OPRF (VOPRF) is an OPRF wherein P can prove to V that F(k, 104 | x) was computed using key k, which is bound to a trusted public key Y 105 | = kG. Informally, this is done by presenting a non-interactive zero- 106 | knowledge (NIZK) proof of equality between (G, Y) and (Z, M), where Z 107 | = kM for some point M. 108 | 109 | 110 | 111 | 112 | Sullivan & Wood Expires September 6, 2018 [Page 2] 113 | 114 | Internet-Draft VOPRFs March 2018 115 | 116 | 117 | VOPRFs are useful for producing tokens that are verifiable by V. 118 | This may be needed, for example, if V wants assurance that P did not 119 | use a unique key in its computation, i.e., if V wants key consistency 120 | from P. This property is necessary in some applications, e.g., the 121 | Privacy Pass protocol [PrivacyPass], wherein this VOPRF is used to 122 | generate one-time authentic tokens to bypass CAPTCHA challenges. 123 | 124 | This document introduces a VOPRF protocol built on Elliptic Curves, 125 | called ECVOPRF. It describes the protocol, its security properties, 126 | and provides preliminary test vectors for experimentation. This rest 127 | of document is structured as follows: 128 | 129 | o Section Section 2: Describe background, related related, and use 130 | cases of VOPRF protocols. 131 | 132 | o Section Section 3: Discuss security properties of VOPRFs. 133 | 134 | o Section Section 4: Specify a VOPRF protocol based on elliptic 135 | curves. 136 | 137 | o Section Section 5: Specify the NIZK discrete logarithm equality 138 | construction used for verifying protocol outputs. 139 | 140 | 1.1. Terminology 141 | 142 | The following terms are used throughout this document. 143 | 144 | o PRF: Pseudorandom Function. 145 | 146 | o OPRF: Oblivious PRF. 147 | 148 | o VOPRF: Verifiable Oblivious Pseudorandom Function. 149 | 150 | o Verifier (V): Protocol initiator when computing F(k, x). 151 | 152 | o Prover (P): Holder of secret key k. 153 | 154 | o NIZK: Non-interactive zero knowledge. 155 | 156 | o DLEQ: Discrete Logarithm Equality. 157 | 158 | 1.2. Requirements 159 | 160 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 161 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this 162 | document are to be interpreted as described in [RFC2119]. 163 | 164 | 165 | 166 | 167 | 168 | Sullivan & Wood Expires September 6, 2018 [Page 3] 169 | 170 | Internet-Draft VOPRFs March 2018 171 | 172 | 173 | 2. Background 174 | 175 | VOPRFs are functionally related to RSA-based blind signature schemes, 176 | e.g., [ChaumBlindSignature]. Such a scheme works as follows. Let m 177 | be a message to be signed by a server. It is assumed to be a member 178 | of the RSA group. Also, let N be the RSA modulus, and e and d be the 179 | public and private keys, respectively. A prover P and verifier V 180 | engage in the following protocol given input m. 181 | 182 | 1. V generates a random blinding element r from the RSA group, and 183 | compute m' = m^r (mod N). Send m' to the P. 184 | 185 | 2. P uses m' to compute s' = (m')^d (mod N), and sends s' to the V. 186 | 187 | 3. V removes the blinding factor r to obtain the original signature 188 | as s = (s')^(r^-1) (mod N). 189 | 190 | By the properties of RSA, s is clearly a valid signature for m. OPRF 191 | protocols are the symmetric equivalent to blind signatures in the 192 | same way that PRFs are the symmetric equivalent traditional digital 193 | signatures. This is discussed more in the following section. 194 | 195 | 3. Security Properties 196 | 197 | The security properties of a VOPRF protocol with functionality y = 198 | F(k, x) include those of a standard PRF. Specifically: 199 | 200 | o Given value x, it is infeasible to compute y = F(k, x) without 201 | knowledge of k. 202 | 203 | o Output y = F(k, x) is indistinguishable from a random value in the 204 | domain of F. 205 | 206 | Additionally, we require the following additional properties: 207 | 208 | o Non-malleable: Given (x, y = F(k, x)), V must not be able to 209 | generate (x', y') where x' != x and y' = F(k, x'). 210 | 211 | o Verifiable: V must only complete execution of the protocol if it 212 | asserts that P used its secret key k, associated with public key Y 213 | = kG, in execution. 214 | 215 | o Oblivious: P must learn nothing about V's input, and V must learn 216 | nothing about P's private key. 217 | 218 | o Unlinkable: If V reveals x to P, P cannot link x to the protocol 219 | instance in which y = F(k, x) was computed. 220 | 221 | 222 | 223 | 224 | Sullivan & Wood Expires September 6, 2018 [Page 4] 225 | 226 | Internet-Draft VOPRFs March 2018 227 | 228 | 229 | 4. Elliptic Curve VOPRF Protocol 230 | 231 | In this section we describe the ECVOPRF protocol. Let GG be a prime- 232 | order subgroup of an elliptic curve over base field GF(p) for prime 233 | p, with two distinct hash functions H_1 and H_2, where H_1 maps 234 | arbitrary input onto GG and H_2 maps arbitrary input to a fixed- 235 | length output, e.g., SHA256. All hash functions in the protocol are 236 | assumed to be random oracles. Let L be the security parameter. Let 237 | k be the prover's (P) secret key, and Y = kG be its corresponding 238 | public key for some generator G taken from the group GG. Let x be 239 | the verifier's (V) input to the VOPRF protocol. (Commonly, it is a 240 | random L-bit string, though this is not required.) ECVOPRF begins 241 | with V randomly blinding its input for the signer. The latter then 242 | applies its secret key to the blinded value and returns the result. 243 | To finish the computation, V then removes its blind and hashes the 244 | result using H_2 to yield an output. This flow is illustrated below. 245 | 246 | Verifier Prover 247 | ------------------------------------ 248 | r <-$ G 249 | M = rH_1(x) 250 | M 251 | -------> 252 | Z = kM 253 | D = DLEQ_Generate(Z/M == Y/G) 254 | Z,D 255 | <------- 256 | b = DLEQ_Verify(M, Z, D, Y) 257 | Output H_2(x, Zr^(-1)) if b=1, else "error" 258 | 259 | DLEQ(Z/M == Y/G) is described in Section Section 5. Intuitively, the 260 | DLEQ proof allows P to prove to V in NIZK that the same key k is the 261 | exponent of both Y and M. In other words, computing the discrete 262 | logarithm of Y and Z (with respect to G and M, respectively) results 263 | in the same value. The committed value Y should be public before the 264 | protocol is initiated. 265 | 266 | The actual PRF function computed is as follows: 267 | 268 | F(k, x) = H_2(x, N) = H_2(x, kH_1(x)) 269 | 270 | Note that V finishes this computation upon receiving kH_1(x) from P. 271 | The output from P is not the PRF value. 272 | 273 | This protocol may be decomposed into a series of steps, as described 274 | below: 275 | 276 | 277 | 278 | 279 | 280 | Sullivan & Wood Expires September 6, 2018 [Page 5] 281 | 282 | Internet-Draft VOPRFs March 2018 283 | 284 | 285 | o ECVOPRF_Blind(x): Compute and return a blind, r, and blinded 286 | representation of x, denoted M. 287 | 288 | o ECVOPRF_Sign(M): Sign input M using secret key k to produce Z, 289 | generate a proof D of DLEQ(Z/M == Y/G), and output (Z, D). 290 | 291 | o ECVOPRF_Unblind((Z, D), r, Y, G, M): Unblind blinded signature Z 292 | with blind r, yielding N. Output N if D is a valid proof. 293 | Otherwise, output an error. 294 | 295 | o ECVOPRF_Finalize(N): Finalize N to produce PRF output F(k, x). 296 | 297 | Protocol correctness requires that, for any key k, input x, and (r, 298 | M) = ECVOPRF_Blind(x), it must be true that: 299 | 300 | ECVOPRF_Finalize(x, ECVOPRF_Unblind(ECVOPRF_Sign(M), M, r)) = F(k, x) 301 | 302 | with overwhelming probability. 303 | 304 | 4.1. Algorithmic Details 305 | 306 | This section provides algorithms for each step in the VOPRF protocol. 307 | 308 | 1. V computes X = H_1(x) and a random element r (blinding factor) 309 | from GF(p), and computes M = rX. 310 | 311 | 2. V sends M to P. 312 | 313 | 3. P computes Z = kM = rkX, and D = DLEQ(Z/M == Y/G). 314 | 315 | 4. P sends (Z, D) to V. 316 | 317 | 5. V verifies D using Y. If invalid, V outputs an error. 318 | 319 | 6. V unblinds Z to compute N = r^(-1)Z = kX. 320 | 321 | 7. V outputs the pair H_2(x, N). 322 | 323 | 4.1.1. ECVOPRF_Blind 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | Sullivan & Wood Expires September 6, 2018 [Page 6] 337 | 338 | Internet-Draft VOPRFs March 2018 339 | 340 | 341 | Input: 342 | 343 | x - V's PRF input. 344 | 345 | Output: 346 | 347 | r - Random scalar in [1, p - 1]. 348 | M - Blinded representation of x using blind r, a point in GG. 349 | 350 | Steps: 351 | 352 | 1. r <-$ GF(p) 353 | 2. M := rH_1(x) 354 | 5. Output (r, M) 355 | 356 | 4.1.2. ECVOPRF_Sign 357 | 358 | Input: 359 | 360 | M - Point in G. 361 | 362 | Output: 363 | 364 | Z - Scalar multiplication of k and M, point in GG. 365 | D - DLEQ proof that log_G(Y) == log_M(Z). 366 | 367 | Steps: 368 | 369 | 1. Z := kM 370 | 2. D = DLEQ_Generate(Y, G, M, Z) 371 | 2. Output (Z, D) 372 | 373 | 4.1.3. ECVOPRF_Unblind 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | Sullivan & Wood Expires September 6, 2018 [Page 7] 393 | 394 | Internet-Draft VOPRFs March 2018 395 | 396 | 397 | Input: 398 | 399 | Z - Point in GG. 400 | D - DLEQ proof that log_G(Y) == log_M(Z). 401 | M - Blinded representation of x using blind r, a point in G. 402 | r - Random scalar in [1, p - 1]. 403 | 404 | Output: 405 | 406 | N - Unblinded signature, point in GG. 407 | 408 | Steps: 409 | 410 | 1. N := (-r)Z 411 | 2. If DLEQ_Verify(G, Y, M, Z, D) output N 412 | 3. Output "error" 413 | 414 | 4.1.4. ECVOPRF_Finalize 415 | 416 | Input: 417 | 418 | x - PRF input string. 419 | N - Point in GG, or "error". 420 | 421 | Output: 422 | 423 | y - Random element in {0,1}^L, or "error" 424 | 425 | Steps: 426 | 427 | 1. If N == "error", output "error". 428 | 2. y := H_2(x, N) 429 | 3. Output y 430 | 431 | 5. NIZK Discrete Logarithm Equality Proof 432 | 433 | In some cases, it may be desirable for the V to have proof that P 434 | used its private key to compute Z from M. This is done by proving 435 | log_G(Y) == log_M(Z). This may be used, for example, to ensure that 436 | P uses the same private key for computing the VOPRF output and does 437 | not attempt to "tag" individual verifiers with select keys. This 438 | proof must not reveal the P's long-term private key to V. 439 | Consequently, we extend the protocol in the previous section with a 440 | (non-interactive) discrete logarithm equality (DLEQ) algorithm built 441 | on a Chaum-Pedersen [ChaumPedersen] proof. This proof is divided 442 | into two procedures: DLEQ_Generate and DLEQ_Verify. These are 443 | specified below. 444 | 445 | 446 | 447 | 448 | Sullivan & Wood Expires September 6, 2018 [Page 8] 449 | 450 | Internet-Draft VOPRFs March 2018 451 | 452 | 453 | 5.1. DLEQ_Generate 454 | 455 | Input: 456 | 457 | G: Generator of group GG with prime order p. 458 | Y: Signer public key. 459 | M: Point in GG. 460 | Z: Point in GG. 461 | H_3: A hash function from GG to a bitstring of length L modeled as a random oracle. 462 | 463 | Output: 464 | 465 | D: DLEQ proof (c, s). 466 | 467 | Steps: 468 | 469 | 1. r <-$ GF(p) 470 | 2. A = rG and B = rM. 471 | 2. c = H_3(G,Y,M,Z,A,B) 472 | 3. s = (r - ck) (mod p) 473 | 4. Output D = (c, s) 474 | 475 | 5.2. DLEQ_Verify 476 | 477 | Input: 478 | 479 | G: Generator of group GG with prime order p. 480 | Y: Signer public key. 481 | M: Point in GG. 482 | Z: Point in GG. 483 | D: DLEQ proof (c, s). 484 | 485 | Output: 486 | 487 | True if log_G(Y) == log_M(Z), False otherwise. 488 | 489 | Steps: 490 | 491 | 1. A' = (sG + cY) 492 | 2. B' = (sM + cZ) 493 | 3. c' = H_3(G,E,M,Z,A',B') 494 | 4. Output c == c' 495 | 496 | 5.3. Group and Hash Function Instantiations 497 | 498 | This section specifies supported VOPRF group and hash function 499 | instantiations. 500 | 501 | 502 | 503 | 504 | Sullivan & Wood Expires September 6, 2018 [Page 9] 505 | 506 | Internet-Draft VOPRFs March 2018 507 | 508 | 509 | EC-VOPRF-P256-SHA256: 510 | 511 | o G: P-256 512 | 513 | o H_1: ((TODO: choose from [I-D.draft-sullivan-cfrg-hash-to-curve] 514 | 515 | o H_2: SHA256 516 | 517 | o H_3: SHA256 518 | 519 | EC-VOPRF-P256-SHA512: 520 | 521 | o G: P-256 522 | 523 | o H_1: ((TODO: choose from [I-D.draft-sullivan-cfrg-hash-to-curve] 524 | 525 | o H_2: SHA512 526 | 527 | o H_3: SHA512 528 | 529 | EC-VOPRF-P384-SHA256: 530 | 531 | o G: P-384 532 | 533 | o H_1: ((TODO: choose from [I-D.draft-sullivan-cfrg-hash-to-curve] 534 | 535 | o H_2: SHA256 536 | 537 | o H_3: SHA256 538 | 539 | EC-VOPRF-P384-SHA512: 540 | 541 | o G: P-384 542 | 543 | o H_1: ((TODO: choose from [I-D.draft-sullivan-cfrg-hash-to-curve] 544 | 545 | o H_2: SHA512 546 | 547 | o H_3: SHA512 548 | 549 | EC-VOPRF-CURVE25519-SHA256: 550 | 551 | o G: Curve25519 [RFC7748] 552 | 553 | o H_1: ((TODO: choose from [I-D.draft-sullivan-cfrg-hash-to-curve] 554 | 555 | o H_2: SHA256 556 | 557 | 558 | 559 | 560 | Sullivan & Wood Expires September 6, 2018 [Page 10] 561 | 562 | Internet-Draft VOPRFs March 2018 563 | 564 | 565 | o H_3: SHA256 566 | 567 | EC-VOPRF-CURVE25519-SHA512: 568 | 569 | o G: Curve25519 [RFC7748] 570 | 571 | o H_1: ((TODO: choose from [I-D.draft-sullivan-cfrg-hash-to-curve] 572 | 573 | o H_2: SHA512 574 | 575 | o H_3: SHA512 576 | 577 | EC-VOPRF-CURVE448-SHA256: 578 | 579 | o G: Curve448 [RFC7748] 580 | 581 | o H_1: ((TODO: choose from [I-D.draft-sullivan-cfrg-hash-to-curve] 582 | 583 | o H_2: SHA256 584 | 585 | o H_3: SHA256 586 | 587 | EC-VOPRF-CURVE448-SHA512: 588 | 589 | o G: Curve448 [RFC7748] 590 | 591 | o H_1: ((TODO: choose from [I-D.draft-sullivan-cfrg-hash-to-curve] 592 | 593 | o H_2: SHA512 594 | 595 | o H_3: SHA512 596 | 597 | 6. Security Considerations 598 | 599 | Security of the protocol depends on P's secrecy of k. Best practices 600 | recommend P regularly rotate k so as to keep its window of compromise 601 | small. Moreover, it each key should be generated from a source of 602 | safe, cryptographic randomness. 603 | 604 | Another critical aspect of this protocol is reliance on 605 | [I-D.draft-sullivan-cfrg-hash-to-curve] for mapping arbitrary input 606 | to points on a curve. Security requires this mapping be pre-image 607 | and collision resistant. 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | Sullivan & Wood Expires September 6, 2018 [Page 11] 617 | 618 | Internet-Draft VOPRFs March 2018 619 | 620 | 621 | 6.1. Timing Leaks 622 | 623 | To ensure no information is leaked during protocol execution, all 624 | operations that use secret data MUST be constant time. Operations 625 | that SHOULD be constant time include: H_1() (hashing arbitrary 626 | strings to curves) and DLEQ_Generate(). 627 | [I-D.draft-sullivan-cfrg-hash-to-curve] describes various algorithms 628 | for constant-time implementations of H_1. 629 | 630 | 7. Privacy Considerations 631 | 632 | 7.1. Key Consistency 633 | 634 | DLEQ proofs are essential to the protocol to allow V to check that 635 | P's designated private key was used in the computation. A side 636 | effect of this property is that it prevents P from using unique key 637 | for select verifiers as a way of "tagging" them. If all verifiers 638 | expect use of a certain private key, e.g., by locating P's public key 639 | key published from a trusted registry, then P cannot present unique 640 | keys to an individual verifier. 641 | 642 | 8. Acknowledgments 643 | 644 | This document resulted from the work of the Privacy Pass team 645 | [PrivacyPass]. 646 | 647 | 9. Contributors 648 | 649 | Alex Davidson contributed to earlier versions of this document. 650 | 651 | 10. Normative References 652 | 653 | [ChaumBlindSignature] 654 | "Blind Signatures for Untraceable Payments", n.d., 655 | . 657 | 658 | [ChaumPedersen] 659 | "Wallet Databases with Observers", n.d., 660 | . 661 | 662 | [I-D.draft-sullivan-cfrg-hash-to-curve] 663 | "Hashing to Elliptic Curves", n.d., 664 | . 666 | 667 | 668 | 669 | 670 | 671 | 672 | Sullivan & Wood Expires September 6, 2018 [Page 12] 673 | 674 | Internet-Draft VOPRFs March 2018 675 | 676 | 677 | [PrivacyPass] 678 | "Privacy Pass", n.d., 679 | . 680 | 681 | [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 682 | Requirement Levels", BCP 14, RFC 2119, 683 | DOI 10.17487/RFC2119, March 1997, 684 | . 685 | 686 | [RFC7748] Langley, A., Hamburg, M., and S. Turner, "Elliptic Curves 687 | for Security", RFC 7748, DOI 10.17487/RFC7748, January 688 | 2016, . 689 | 690 | [RFC8032] Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital 691 | Signature Algorithm (EdDSA)", RFC 8032, 692 | DOI 10.17487/RFC8032, January 2017, 693 | . 694 | 695 | Appendix A. Test Vectors 696 | 697 | This section includes test vectors for the primary VOPRF protocol, 698 | excluding DLEQ output. 699 | 700 | ((TODO: add DLEQ vectors)) 701 | 702 | P-224 703 | X: 0403cd8bc2f2f3c4c647e063627ca9c9ac246e3e3ec74ab76d32d3e999c522d60ff7aa1c8b0e4 \ 704 | X: 0403cd8bc2f2f3c4c647e063627ca9c9ac246e3e3ec74ab76d32d3e999c522d60ff7aa1c8b0e4 705 | r: c4cf3c0b3a334f805d3ce3c3b4d007fbbdaf078a42a8cbdc833e54a9 706 | M: 046b2b8482c36e65f87528415e210cff8561c1c8e07600a159893973365617ee2c1c33eb0662d \ 707 | M: 046b2b8482c36e65f87528415e210cff8561c1c8e07600a159893973365617ee2c1c33eb0662d 708 | k: a364119e1c932a534a8d440fef2169a0e4c458d702eca56746655845 709 | Z: 04ed11656b4981e39242b170025bf8d5314bef75006e6c4c9afcdb9a85e21fb5fcf9055eb95d3 \ 710 | Z: 04ed11656b4981e39242b170025bf8d5314bef75006e6c4c9afcdb9a85e21fb5fcf9055eb95d3 711 | Y: 04fd80db5301a54ee2cbc688d47cbcae9eb84f5d246e3da3e2b03e9be228ed6c57a936b6b5faf \ 712 | Y: 04fd80db5301a54ee2cbc688d47cbcae9eb84f5d246e3da3e2b03e9be228ed6c57a936b6b5faf 713 | 714 | P-224 715 | X: 0429e41b7e1a58e178afc522d0fb4a6d17ae883e6fd439931cf1e81456ab7ed6445dbe0a231be \ 716 | X: 0429e41b7e1a58e178afc522d0fb4a6d17ae883e6fd439931cf1e81456ab7ed6445dbe0a231be 717 | r: 86a27e1bd51ac91eae32089015bf903fe21da8d79725edcc4dc30566 718 | M: 04d8c8ffaa92b21aa1cc6056710bd445371e8afebd9ef0530c68cd0d09536423f78382e4f6b20 \ 719 | M: 04d8c8ffaa92b21aa1cc6056710bd445371e8afebd9ef0530c68cd0d09536423f78382e4f6b20 720 | k: ab449c896261dc3bd1f20d87272e6c8184a2252a439f0b3140078c3d 721 | Z: 048ac9722189b596ffe5cb986332e89008361e68f77f12a931543f63eaa01fabf6f63d5d4b3b6 \ 722 | Z: 048ac9722189b596ffe5cb986332e89008361e68f77f12a931543f63eaa01fabf6f63d5d4b3b6 723 | Y: 046e83dff2c9b6f9e88f1091f355ad6fa637bdbd829072411ea2d74a5bf3501ccf3bcc2789d48 \ 724 | Y: 046e83dff2c9b6f9e88f1091f355ad6fa637bdbd829072411ea2d74a5bf3501ccf3bcc2789d48 725 | 726 | 727 | 728 | Sullivan & Wood Expires September 6, 2018 [Page 13] 729 | 730 | Internet-Draft VOPRFs March 2018 731 | 732 | 733 | P-256 734 | X: 041b0e84c521f8dcd530d59a692d4ffa1ca05b8ba7ce22a884a511f93919ac121cc91dd588228 \ 735 | X: 041b0e84c521f8dcd530d59a692d4ffa1ca05b8ba7ce22a884a511f93919ac121cc91dd588228 736 | r: a3ec1dc3303a316fc06565ace0a8910da65cf498ea3884c4349b6c4fc9a2f99a 737 | M: 04794c5a54236782088594ccdb1975e93b05ff742674cc400cb101f55c0f37e877c5ada0d72fb \ 738 | M: 04794c5a54236782088594ccdb1975e93b05ff742674cc400cb101f55c0f37e877c5ada0d72fb 739 | k: 9c103b889808a8f4cb6d76ea8b634416a286be7fa4a14e94f1478ada7f172ec3 740 | Z: 0484cfda0fdcba7693672fe5e78f4c429c096ece730789e8d00ec1f7be33a6515f186dcf7aa38 \ 741 | Z: 0484cfda0fdcba7693672fe5e78f4c429c096ece730789e8d00ec1f7be33a6515f186dcf7aa38 742 | Y: 044ff2e31de9fda542c2c63314e2bce5ce2d5ccb8332dbe1115ff5740e5e60bb867994e196ead \ 743 | Y: 044ff2e31de9fda542c2c63314e2bce5ce2d5ccb8332dbe1115ff5740e5e60bb867994e196ead 744 | 745 | P-256 746 | X: 043ea9d81b99ac0db002ad2823f7cab28af18f83419cce6800f3d786cc00b6fd030858d073916 \ 747 | X: 043ea9d81b99ac0db002ad2823f7cab28af18f83419cce6800f3d786cc00b6fd030858d073916 748 | r: ed7294b42792760825645b635e9d92ef5a3baa70879dd59fdb1802d4a44271b2 749 | M: 04ec894e496d0297756a17365f866d9449e6ebc51852ab1ffa57bc29c843ef003b116f5ef1f60 \ 750 | M: 04ec894e496d0297756a17365f866d9449e6ebc51852ab1ffa57bc29c843ef003b116f5ef1f60 751 | k: a324338a7254415dbedcd1855abd2503b4e5268124358d014dac4fc2c722cd67 752 | Z: 04a477c5fefd9bc6bcd8e893a1b0c6dc73b0bd23ebe952dcad810de73b8a99f5e1e216a833b32 \ 753 | Z: 04a477c5fefd9bc6bcd8e893a1b0c6dc73b0bd23ebe952dcad810de73b8a99f5e1e216a833b32 754 | Y: 04ffe55e2a95a21e1605c1ed11ac6bea93f00fa15a6b27e90adad470ad27f0e0fe5b8607b4689 \ 755 | Y: 04ffe55e2a95a21e1605c1ed11ac6bea93f00fa15a6b27e90adad470ad27f0e0fe5b8607b4689 756 | 757 | P-384 758 | X: 04c0b51e5dcd6a309c77bb5720bf9850279e8142b6127952595ab9092578de810a13795bceff3 \ 759 | d358f0480a61469f17ad62ebaecd0f817c1e9c7d41d536ab410e7a2b5d7a7905d1bef5499b654b0e \ 760 | d358f0480a61469f17ad62ebaecd0f817c1e9c7d41d536ab410e7a2b5d7a7905d1bef5499b654b0e 761 | r: 889b5e4812d683c4df735971240741ff869ccf77e10c2e97bef67d6fe6b8350abe59ec8fe2bfa \ 762 | r: 889b5e4812d683c4df735971240741ff869ccf77e10c2e97bef67d6fe6b8350abe59ec8fe2bfa 763 | M: 044e2d86fa6e53ebba7f2a9b661a2de884a8ccc68e29b68586d517eb66e8b4b7dac334c6e769d \ 764 | 485d672fac3a0311877572254754e318077aec3631208c6b503c5cdfe57716e1232da64cebe46f0d \ 765 | 485d672fac3a0311877572254754e318077aec3631208c6b503c5cdfe57716e1232da64cebe46f0d 766 | k: b8c854a33c8c564d0598b1ac179546acdccad671265cff1ea5a329279272e8d21c94b7e5b6bea \ 767 | k: b8c854a33c8c564d0598b1ac179546acdccad671265cff1ea5a329279272e8d21c94b7e5b6bea 768 | Z: 047bf23eef00e83e6cb6fb9ade5e5995cf81abb8dc73a570ff4cb7be48f21281edfed9bf76cc2 \ 769 | 88b35d2df615ff711ed2a1fb85cd0b22812438665cdd300039685b3f593f4b574f9e8b294982c2a2 \ 770 | 88b35d2df615ff711ed2a1fb85cd0b22812438665cdd300039685b3f593f4b574f9e8b294982c2a2 771 | Y: 04ab4886ecf7e489a0be8529ff4b537941c95ba4ce570db537dcfad5cabc064c43f1b0a1d1b89 \ 772 | 101facd93f2f9a8b5f28431489be4664f446af8a51cc7c4221f633adb4f8f2f2a073dfd83ddf8d77 \ 773 | 101facd93f2f9a8b5f28431489be4664f446af8a51cc7c4221f633adb4f8f2f2a073dfd83ddf8d77 774 | 775 | P-384 776 | X: 047511a846277a2009f37b3583f14c8ea3af17e3a146e0e737fdc1260b6d4a18ff01f21ec3bbc \ 777 | e39e1cade76d455feadc1bb16f65cd54042e1bc5aba1dee2434f59d00698a963b902148750240f8f \ 778 | e39e1cade76d455feadc1bb16f65cd54042e1bc5aba1dee2434f59d00698a963b902148750240f8f 779 | r: e514ef9b3ea87eafdb78da48e642daa79f036ac00228997ab8da6ac198fb888cd2fec84d52010 \ 780 | r: e514ef9b3ea87eafdb78da48e642daa79f036ac00228997ab8da6ac198fb888cd2fec84d52010 781 | 782 | 783 | 784 | Sullivan & Wood Expires September 6, 2018 [Page 14] 785 | 786 | Internet-Draft VOPRFs March 2018 787 | 788 | 789 | M: 04fd9b68973b0fcefcf4458b4faa1c3815bdad8975b7fb0bfc4c1db7e3f169fb3a26ddabe1b25 \ 790 | c4a23cf8a2faeb12c18f06f2227e87ede6039f55a61ef0c89ca3c582e2864fe130ea0c709f92519d \ 791 | c4a23cf8a2faeb12c18f06f2227e87ede6039f55a61ef0c89ca3c582e2864fe130ea0c709f92519d 792 | k: bcc73da3b2adace9c4f4c32eeadef57436c97f8d395614e78aa91523e1e5d7f551ebb55e87da2 \ 793 | k: bcc73da3b2adace9c4f4c32eeadef57436c97f8d395614e78aa91523e1e5d7f551ebb55e87da2 794 | Z: 042d885d2945cde40e490dd8497975eaeb54e4e10c5986a9688c9de915b16cf43572fd155e159 \ 795 | 9e2233a75056a72b54d30092e30bb2edc70e0d90da934c27362e0e6303bafae12f13bf3d5be322e6 \ 796 | 9e2233a75056a72b54d30092e30bb2edc70e0d90da934c27362e0e6303bafae12f13bf3d5be322e6 797 | Y: 044833fba4973c1c6eae6745850866ebbb23783ea0d4d8b867e2c93acb2f01fd3d36d9cb5c491 \ 798 | ff9440c8c8e325db326bf88ddf0ba6008158a67999e18cd378d701d1f8a6a7b088dc261c85b6a78b \ 799 | ff9440c8c8e325db326bf88ddf0ba6008158a67999e18cd378d701d1f8a6a7b088dc261c85b6a78b 800 | 801 | P-521 802 | X: 040039d290b20c604b5c59cb85dfacd90cbf9f5e275ee8c38a8ff80df0872e8e1dd214a9ec3b2 \ 803 | 2c8d75bf634739afdc09acc342542abacf35bf2a6488d084825c5d96003be29e201e75c1b78667f5 \ 804 | a64cc7207722796b225b49edaaf457fcafff4f644252ebe8057291d317f30109f1526aacbfff2308 \ 805 | a64cc7207722796b225b49edaaf457fcafff4f644252ebe8057291d317f30109f1526aacbfff2308 806 | r: 010606612666705556ac3c28dde30f134e930b0c31bfc9715f0812e0b99f0212dc427e344cb97 \ 807 | r: 010606612666705556ac3c28dde30f134e930b0c31bfc9715f0812e0b99f0212dc427e344cb97 808 | M: 040065366112a0598e4e5997e79e42f287f7202e5d956bef29890e963169d9eaab8d21501283c \ 809 | 47dd37aca1710c8b5f456b1c044c8582ba6feef3edc997fecef7d4f40180ceb9bbbe3ab1907ea2d1 \ 810 | 21ec00156848e04e323744d86444111fc09a21ca316df2cae925a0bb079d0faa2474ec8d5a96e6fa \ 811 | 21ec00156848e04e323744d86444111fc09a21ca316df2cae925a0bb079d0faa2474ec8d5a96e6fa 812 | k: 01297d92cfe6895269aa5406f2ba6cbfffbba66a11ab0db34655213624fa238c50e27177aea5d \ 813 | k: 01297d92cfe6895269aa5406f2ba6cbfffbba66a11ab0db34655213624fa238c50e27177aea5d 814 | Z: 040151d2dc5290ebd47065680dcb4db350c4d81346680c5589f94acfb1e28418585e7f2cbfa11 \ 815 | 5945d9f7b98157ea8c2ab190c6a47b939502c2f793b77ceff671f5e60086fdd1ebf960f29bf5d590 \ 816 | f8f7511d248df22d964637e2286adab4654991d338691f4673a006ff116e61afe65c914b27c3ef4c \ 817 | f8f7511d248df22d964637e2286adab4654991d338691f4673a006ff116e61afe65c914b27c3ef4c 818 | Y: 04009534bd720bd4ebe703968a8496eec352711a81b7fe594a72ef318c2ce582b41880262a1c6 \ 819 | 05079231de91e71b1301d1be4e9618e96081ccfd4f6cab92f52b860e01beec2c58cb01713e941035 \ 820 | adbe882ab4f3eaa31e27a96d210d35f6161b1487dd28d8da4a11a915182752b1450a89aad2a013c2 \ 821 | adbe882ab4f3eaa31e27a96d210d35f6161b1487dd28d8da4a11a915182752b1450a89aad2a013c2 822 | 823 | P-521 824 | X: 04012ea416842dfad169a9eb860b0af2af3c0140e1918ccd043650d83ad261633f20c5ca02c1b \ 825 | ffb857ab72814cf46cfc16ac9ba79887044709f72480358c4b990e46010a62336bb57b87b494b064 \ 826 | 4d2b6a385f3d5b5da29e22cae33c624f561513a5e8e6669b4e99704c56157dde83994a3c0800a64b \ 827 | 4d2b6a385f3d5b5da29e22cae33c624f561513a5e8e6669b4e99704c56157dde83994a3c0800a64b 828 | r: 019d02efd97add5facc5defbb63fd74daaacda04ae7321abec0da1551b4cc980b8ce6855a28a1 \ 829 | r: 019d02efd97add5facc5defbb63fd74daaacda04ae7321abec0da1551b4cc980b8ce6855a28a1 830 | M: 040066e3d0b5b9758c9288a725ce6724fdc3bd797a8222f07233897a5916dc167531ebc6a4710 \ 831 | cbb240684c9a02eb82214b009d636f24abb8e409e78ff1f02a1dbfb90069056693e96acd760887f9 \ 832 | 6c9b1f487441b7142fb13a67deb7332194ff454b3aac89f9cf02c338dae69a700bd26844881e6106 \ 833 | 6c9b1f487441b7142fb13a67deb7332194ff454b3aac89f9cf02c338dae69a700bd26844881e6106 834 | k: 018eeea896de104bf1e772155836f6ceddab0b4c2e3e4c33ba08a6fd6db0291cfb15faff0b3c7 \ 835 | k: 018eeea896de104bf1e772155836f6ceddab0b4c2e3e4c33ba08a6fd6db0291cfb15faff0b3c7 836 | Z: 04016825ea754324d5761ada130a1b87b03b5e2a6b0f403343925c67df39bbf85bc782909124d \ 837 | 838 | 839 | 840 | Sullivan & Wood Expires September 6, 2018 [Page 15] 841 | 842 | Internet-Draft VOPRFs March 2018 843 | 844 | 845 | d297a1edfb049efa7ce61c626c0ad99d8cf462abcce1ee1967d8a355011e2c5a7ce621fc822a7d95 \ 846 | bf7359d938ee4a5c3431e7dd270b7fb6e95fda29cf460d89454763bb0db9b8b705503170a9ac1c7a \ 847 | bf7359d938ee4a5c3431e7dd270b7fb6e95fda29cf460d89454763bb0db9b8b705503170a9ac1c7a 848 | Y: 04006b0413e2686c4bb62340706de7723471080093422f02dd125c3e72f3507b9200d11481468 \ 849 | 74bbaa5b6108b834c892eeebab4e21f3707ee20c303ebc1e34fcd3c701f2171131ee7c5f07c1ccad \ 850 | 240183d777181259761741343959d476bbc2591a1af0a516e6403a6b81423234746d7a2e8c2ce60a \ 851 | 240183d777181259761741343959d476bbc2591a1af0a516e6403a6b81423234746d7a2e8c2ce60a 852 | 853 | Authors' Addresses 854 | 855 | Nick Sullivan 856 | Cloudflare 857 | 101 Townsend St 858 | San Francisco 859 | United States of America 860 | 861 | Email: nick@cloudflare.com 862 | 863 | 864 | Christopher A. Wood 865 | Apple Inc. 866 | One Apple Park Way 867 | Cupertino, California 95014 868 | United States of America 869 | 870 | Email: cawood@apple.com 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | Sullivan & Wood Expires September 6, 2018 [Page 16] 897 | -------------------------------------------------------------------------------- /releases/draft-sullivan-cfrg-voprf-03.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Network Working Group A. Davidson 6 | Internet-Draft N. Sullivan 7 | Intended status: Informational Cloudflare 8 | Expires: September 12, 2019 C. Wood 9 | Apple Inc. 10 | March 11, 2019 11 | 12 | 13 | Oblivious Pseudorandom Functions (OPRFs) using Prime-Order Groups 14 | draft-sullivan-cfrg-voprf-03 15 | 16 | Abstract 17 | 18 | An Oblivious Pseudorandom Function (OPRF) is a two-party protocol for 19 | computing the output of a PRF. One party (the server) holds the PRF 20 | secret key, and the other (the client) holds the PRF input. The 21 | 'obliviousness' property ensures that the server does not learn 22 | anything about the client's input during the evaluation. The client 23 | should also not learn anything about the server's secret PRF key. 24 | Optionally, OPRFs can also satisfy a notion 'verifiability' (VOPRF). 25 | In this setting, the client can verify that the server's output is 26 | indeed the result of evaluating the underlying PRF with just a public 27 | key. This document specifies OPRF and VOPRF constructions 28 | instantiated within prime-order groups, including elliptic curves. 29 | 30 | Status of This Memo 31 | 32 | This Internet-Draft is submitted in full conformance with the 33 | provisions of BCP 78 and BCP 79. 34 | 35 | Internet-Drafts are working documents of the Internet Engineering 36 | Task Force (IETF). Note that other groups may also distribute 37 | working documents as Internet-Drafts. The list of current Internet- 38 | Drafts is at https://datatracker.ietf.org/drafts/current/. 39 | 40 | Internet-Drafts are draft documents valid for a maximum of six months 41 | and may be updated, replaced, or obsoleted by other documents at any 42 | time. It is inappropriate to use Internet-Drafts as reference 43 | material or to cite them other than as "work in progress." 44 | 45 | This Internet-Draft will expire on September 12, 2019. 46 | 47 | Copyright Notice 48 | 49 | Copyright (c) 2019 IETF Trust and the persons identified as the 50 | document authors. All rights reserved. 51 | 52 | 53 | 54 | 55 | 56 | Davidson, et al. Expires September 12, 2019 [Page 1] 57 | 58 | Internet-Draft OPRFs March 2019 59 | 60 | 61 | This document is subject to BCP 78 and the IETF Trust's Legal 62 | Provisions Relating to IETF Documents 63 | (https://trustee.ietf.org/license-info) in effect on the date of 64 | publication of this document. Please review these documents 65 | carefully, as they describe your rights and restrictions with respect 66 | to this document. Code Components extracted from this document must 67 | include Simplified BSD License text as described in Section 4.e of 68 | the Trust Legal Provisions and are provided without warranty as 69 | described in the Simplified BSD License. 70 | 71 | Table of Contents 72 | 73 | 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 74 | 1.1. Terminology . . . . . . . . . . . . . . . . . . . . . . . 4 75 | 1.2. Requirements . . . . . . . . . . . . . . . . . . . . . . 5 76 | 2. Background . . . . . . . . . . . . . . . . . . . . . . . . . 5 77 | 3. Security Properties . . . . . . . . . . . . . . . . . . . . . 6 78 | 4. OPRF Protocol . . . . . . . . . . . . . . . . . . . . . . . . 6 79 | 4.1. Protocol correctness . . . . . . . . . . . . . . . . . . 8 80 | 4.2. Instantiations of GG . . . . . . . . . . . . . . . . . . 8 81 | 4.3. OPRF algorithms . . . . . . . . . . . . . . . . . . . . . 9 82 | 4.3.1. OPRF_Setup . . . . . . . . . . . . . . . . . . . . . 9 83 | 4.3.2. OPRF_Blind . . . . . . . . . . . . . . . . . . . . . 10 84 | 4.3.3. OPRF_Sign . . . . . . . . . . . . . . . . . . . . . . 10 85 | 4.3.4. OPRF_Unblind . . . . . . . . . . . . . . . . . . . . 11 86 | 4.3.5. OPRF_Finalize . . . . . . . . . . . . . . . . . . . . 11 87 | 4.4. VOPRF algorithms . . . . . . . . . . . . . . . . . . . . 11 88 | 4.4.1. VOPRF_Setup . . . . . . . . . . . . . . . . . . . . . 12 89 | 4.4.2. VOPRF_Blind . . . . . . . . . . . . . . . . . . . . . 12 90 | 4.4.3. VOPRF_Sign . . . . . . . . . . . . . . . . . . . . . 13 91 | 4.4.4. VOPRF_Unblind . . . . . . . . . . . . . . . . . . . . 13 92 | 4.4.5. VOPRF_Finalize . . . . . . . . . . . . . . . . . . . 13 93 | 4.5. Utility algorithms . . . . . . . . . . . . . . . . . . . 14 94 | 4.5.1. bin2scalar . . . . . . . . . . . . . . . . . . . . . 14 95 | 4.6. Efficiency gains with pre-processing and additive 96 | blinding . . . . . . . . . . . . . . . . . . . . . . . . 14 97 | 4.6.1. OPRF_Preprocess . . . . . . . . . . . . . . . . . . . 15 98 | 4.6.2. OPRF_Blind . . . . . . . . . . . . . . . . . . . . . 15 99 | 4.6.3. OPRF_Unblind . . . . . . . . . . . . . . . . . . . . 16 100 | 5. NIZK Discrete Logarithm Equality Proof . . . . . . . . . . . 16 101 | 5.1. DLEQ_Generate . . . . . . . . . . . . . . . . . . . . . . 16 102 | 5.2. DLEQ_Verify . . . . . . . . . . . . . . . . . . . . . . . 17 103 | 6. Batched VOPRF evaluation . . . . . . . . . . . . . . . . . . 17 104 | 6.1. Batched DLEQ algorithms . . . . . . . . . . . . . . . . . 18 105 | 6.1.1. Batched_DLEQ_Generate . . . . . . . . . . . . . . . . 18 106 | 6.1.2. Batched_DLEQ_Verify . . . . . . . . . . . . . . . . . 19 107 | 6.2. Modified protocol execution . . . . . . . . . . . . . . . 20 108 | 6.3. PRNG and resampling . . . . . . . . . . . . . . . . . . . 20 109 | 110 | 111 | 112 | Davidson, et al. Expires September 12, 2019 [Page 2] 113 | 114 | Internet-Draft OPRFs March 2019 115 | 116 | 117 | 7. Supported ciphersuites . . . . . . . . . . . . . . . . . . . 20 118 | 7.1. ECVOPRF-P256-HKDF-SHA256-SSWU: . . . . . . . . . . . . . 20 119 | 7.2. ECVOPRF-RISTRETTO-HKDF-SHA512-Elligator2: . . . . . . . . 21 120 | 8. Security Considerations . . . . . . . . . . . . . . . . . . . 21 121 | 8.1. Timing Leaks . . . . . . . . . . . . . . . . . . . . . . 21 122 | 8.2. Hashing to curves . . . . . . . . . . . . . . . . . . . . 22 123 | 8.3. Verifiability (key consistency) . . . . . . . . . . . . . 22 124 | 9. Applications . . . . . . . . . . . . . . . . . . . . . . . . 22 125 | 9.1. Privacy Pass . . . . . . . . . . . . . . . . . . . . . . 22 126 | 9.2. Private Password Checker . . . . . . . . . . . . . . . . 23 127 | 9.2.1. Parameter Commitments . . . . . . . . . . . . . . . . 23 128 | 10. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . 23 129 | 11. Normative References . . . . . . . . . . . . . . . . . . . . 23 130 | Appendix A. Test Vectors . . . . . . . . . . . . . . . . . . . . 25 131 | Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 27 132 | 133 | 1. Introduction 134 | 135 | A pseudorandom function (PRF) F(k, x) is an efficiently computable 136 | function with secret key k on input x. Roughly, F is pseudorandom if 137 | the output y = F(k, x) is indistinguishable from uniformly sampling 138 | any element in F's range for random choice of k. An oblivious PRF 139 | (OPRF) is a two-party protocol between a prover P and verifier V 140 | where P holds a PRF key k and V holds some input x. The protocol 141 | allows both parties to cooperate in computing F(k, x) with P's secret 142 | key k and V's input x such that: V learns F(k, x) without learning 143 | anything about k; and P does not learn anything about x. A 144 | Verifiable OPRF (VOPRF) is an OPRF wherein P can prove to V that F(k, 145 | x) was computed using key k, which is bound to a trusted public key Y 146 | = kG. Informally, this is done by presenting a non-interactive zero- 147 | knowledge (NIZK) proof of equality between (G, Y) and (Z, M), where Z 148 | = kM for some point M. 149 | 150 | OPRFs have been shown to be useful for constructing: password- 151 | protected secret sharing schemes [JKK14]; privacy-preserving password 152 | stores [SJKS17]; and password-authenticated key exchange or PAKE 153 | [OPAQUE]. VOPRFs are useful for producing tokens that are verifiable 154 | by V. This may be needed, for example, if V wants assurance that P 155 | did not use a unique key in its computation, i.e., if V wants key 156 | consistency from P. This property is necessary in some applications, 157 | e.g., the Privacy Pass protocol [PrivacyPass], wherein this VOPRF is 158 | used to generate one-time authentication tokens to bypass CAPTCHA 159 | challenges. VOPRFs have also been used for password-protected secret 160 | sharing schemes e.g. [JKKX16]. 161 | 162 | This document introduces an OPRF protocol built in prime-order 163 | groups, applying to finite fields of prime-order and also elliptic 164 | curve (EC) settings. The protocol has the option of being extended 165 | 166 | 167 | 168 | Davidson, et al. Expires September 12, 2019 [Page 3] 169 | 170 | Internet-Draft OPRFs March 2019 171 | 172 | 173 | to a VOPRF with the addition of a NIZK proof for proving discrete log 174 | equality relations. This proof demonstrates correctness of the 175 | computation using a known public key that serves as a commitment to 176 | the server's secret key. In the EC setting, we will refer to the 177 | protocol as ECOPRF (or ECVOPRF if verifiability is concerned). The 178 | document describes the protocol, its security properties, and 179 | provides preliminary test vectors for experimentation. The rest of 180 | the document is structured as follows: 181 | 182 | o Section Section 2: Describe background, related work, and use 183 | cases of OPRF/VOPRF protocols. 184 | 185 | o Section Section 3: Discuss security properties of OPRFs/VOPRFs. 186 | 187 | o Section Section 4: Specify an authentication protocol from OPRF 188 | functionality, based in prime-order groups (with an optional 189 | verifiable mode). Algorithms are stated formally for OPRFs in 190 | Section 4.3 and for VOPRFs in Section 4.4. 191 | 192 | o Section Section 5: Specify the NIZK discrete logarithm equality 193 | (DLEQ) construction used for constructing the VOPRF protocol. 194 | 195 | o Section Section 6: Specifies how the DLEQ proof mechanism can be 196 | batched for multiple VOPRF invocations, and how this changes the 197 | protocol execution. 198 | 199 | o Section Section 7: Considers explicit instantiations of the 200 | protocol in the elliptic curve setting. 201 | 202 | o Section Section 8: Discusses the security considerations for the 203 | OPRF and VOPRF protocol. 204 | 205 | o Section Section 9: Discusses some existing applications of OPRF 206 | and VOPRF protocols. 207 | 208 | o Section Appendix A: Specifies test vectors for implementations in 209 | the elliptic curve setting. 210 | 211 | 1.1. Terminology 212 | 213 | The following terms are used throughout this document. 214 | 215 | o PRF: Pseudorandom Function. 216 | 217 | o OPRF: Oblivious PRF. 218 | 219 | o VOPRF: Verifiable Oblivious Pseudorandom Function. 220 | 221 | 222 | 223 | 224 | Davidson, et al. Expires September 12, 2019 [Page 4] 225 | 226 | Internet-Draft OPRFs March 2019 227 | 228 | 229 | o ECVOPRF: A VOPRF built on Elliptic Curves. 230 | 231 | o Verifier (V): Protocol initiator when computing F(k, x). 232 | 233 | o Prover (P): Holder of secret key k. 234 | 235 | o NIZK: Non-interactive zero knowledge. 236 | 237 | o DLEQ: Discrete Logarithm Equality. 238 | 239 | 1.2. Requirements 240 | 241 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 242 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this 243 | document are to be interpreted as described in [RFC2119]. 244 | 245 | 2. Background 246 | 247 | OPRFs are functionally related to RSA-based blind signature schemes, 248 | e.g., [ChaumBlindSignature]. Briefly, a blind signature scheme works 249 | as follows. Let m be a message to be signed by a server. It is 250 | assumed to be a member of the RSA group. Also, let N be the RSA 251 | modulus, and e and d be the public and private keys, respectively. A 252 | prover P and verifier V engage in the following protocol given input 253 | m. 254 | 255 | 1. V generates a random blinding element r from the RSA group, and 256 | compute m' = m^r (mod N). Send m' to the P. 257 | 258 | 2. P uses m' to compute s' = (m')^d (mod N), and sends s' to the V. 259 | 260 | 3. V removes the blinding factor r to obtain the original signature 261 | as s = (s')^(r^-1) (mod N). 262 | 263 | By the properties of RSA, s is clearly a valid signature for m. OPRF 264 | protocols can be used to provide a symmetric equivalent to blind 265 | signatures. Essentially the client learns y = PRF(k,x) for some 266 | input x of their choice, from a server that holds k. Since the 267 | security of an OPRF means that x is hidden in the interaction, then 268 | the client can later reveal x to the server along with y. 269 | 270 | The server can verify that y is computed correctly by recomputing the 271 | PRF on x using k. In doing so, the client provides knowledge of a 272 | 'signature' y for their value x. However, the verification procedure 273 | is symmetric since it requires knowledge of k. This is discussed 274 | more in the following section. 275 | 276 | 277 | 278 | 279 | 280 | Davidson, et al. Expires September 12, 2019 [Page 5] 281 | 282 | Internet-Draft OPRFs March 2019 283 | 284 | 285 | 3. Security Properties 286 | 287 | The security properties of an OPRF protocol with functionality y = 288 | F(k, x) include those of a standard PRF. Specifically: 289 | 290 | o Given value x, it is infeasible to compute y = F(k, x) without 291 | knowledge of k. 292 | 293 | o The output distribution of y = F(k, x) is indistinguishable from 294 | the uniform distribution in the domain of the function F. 295 | 296 | Additionally, we require the following additional properties: 297 | 298 | o Non-malleable: Given (x, y = F(k, x)), V must not be able to 299 | generate (x', y') where x' != x and y' = F(k, x'). 300 | 301 | o Oblivious: P must learn nothing about V's input, and V must learn 302 | nothing about P's private key. 303 | 304 | o Unlinkable: If V reveals x to P, P cannot link x to the protocol 305 | instance in which y = F(k, x) was computed. 306 | 307 | Optionally, for any protocol that satisfies the above properties, 308 | there is an additional security property: 309 | 310 | o Verifiable: V must only complete execution of the protocol if it 311 | can successfully assert that P used its secret key k. 312 | 313 | In practice, the notion of verifiability requires that P commits to 314 | the key k before the actual protocol execution takes place. Then V 315 | verifies that P has used k in the protocol using this commitment. 316 | 317 | 4. OPRF Protocol 318 | 319 | In this section we describe the OPRF protocol. Let GG be a prime- 320 | order additive subgroup, with two distinct hash functions H_1 and 321 | H_2, where H_1 maps arbitrary input onto GG and H_2 maps arbitrary 322 | input to a fixed-length output, e.g., SHA256. All hash functions in 323 | the protocol are modelled as random oracles. Let L be the security 324 | parameter. Let k be the prover's (P) secret key, and Y = kG be its 325 | corresponding 'public key' for some generator G taken from the group 326 | GG. This public key is also referred to as a commitment to the key 327 | k. Let x be the verifier's (V) input to the OPRF protocol. 328 | (Commonly, it is a random L-bit string, though this is not required.) 329 | 330 | The OPRF protocol begins with V blinding its input for the signer 331 | such that it appears uniformly distributed GG. The latter then 332 | applies its secret key to the blinded value and returns the result. 333 | 334 | 335 | 336 | Davidson, et al. Expires September 12, 2019 [Page 6] 337 | 338 | Internet-Draft OPRFs March 2019 339 | 340 | 341 | To finish the computation, V then removes its blind and hashes the 342 | result using H_2 to yield an output. This flow is illustrated below. 343 | 344 | Verifier Prover 345 | ------------------------------------ 346 | r <-$ GG 347 | M = rH_1(x) 348 | M 349 | -------> 350 | Z = kM 351 | [D = DLEQ_Generate(k,G,Y,M,Z)] 352 | Z[,D] 353 | <------- 354 | [b = DLEQ_Verify(G,Y,M,Z,D)] 355 | N = Zr^(-1) 356 | Output H_2(x, N) [if b=1, else "error"] 357 | 358 | Steps that are enclosed in square brackets (DLEQ_Generate and 359 | DLEQ_Verify) are optional for achieving verifiability. These are 360 | described in Section Section 5. In the verifiable mode, we assume 361 | that P has previously committed to their choice of key k with some 362 | values (G,Y=kG) and these are publicly known by V. Notice that 363 | revealing (G,Y) does not reveal k by the well-known hardness of the 364 | discrete log problem. 365 | 366 | Strictly speaking, the actual PRF function that is computed is: 367 | 368 | F(k, x) = N = kH_1(x) 369 | 370 | It is clear that this is a PRF H_1(x) maps x to a random element in 371 | GG, and GG is cyclic. This output is computed when the client 372 | computes Zr^(-1) by the commutativity of the multiplication. The 373 | client finishes the computation by outputting H_2(x,N). Note that 374 | the output from P is not the PRF value because the actual input x is 375 | blinded by r. 376 | 377 | This protocol may be decomposed into a series of steps, as described 378 | below: 379 | 380 | o OPRF_Setup(l): Generate am integer k of sufficient bit-length l 381 | and output k. 382 | 383 | o OPRF_Blind(x): Compute and return a blind, r, and blinded 384 | representation of x in GG, denoted M. 385 | 386 | o OPRF_Sign(k,M,h): Sign input M using secret key k to produce Z, 387 | the input h is optional and equal to the cofactor of an elliptic 388 | curve. If h is not provided then it defaults to 1. 389 | 390 | 391 | 392 | Davidson, et al. Expires September 12, 2019 [Page 7] 393 | 394 | Internet-Draft OPRFs March 2019 395 | 396 | 397 | o OPRF_Unblind(r,Z): Unblind blinded signature Z with blind r, 398 | yielding N and output N. 399 | 400 | o OPRF_Finalize(x,N): Finalize N to produce the output H_2(x, N). 401 | 402 | For verifiability we modify the algorithms of VOPRF_Setup, VOPRF_Sign 403 | and VOPRF_Unblind to be the following: 404 | 405 | o VOPRF_Setup(l): Generate an integer k of sufficient bit-length l 406 | and output (k, (G,Y)) where Y = kG for some generator G in GG. 407 | 408 | o VOPRF_Sign(k,(G,Y),M,h): Sign input M using secret key k to 409 | produce Z. Generate a NIZK proof D = DLEQ_Generate(k,G,Y,M,Z), 410 | and output (Z, D). The optional cofactor h can also be provided 411 | as in OPRF_Sign. 412 | 413 | o VOPRF_Unblind(r,G,Y,M,(Z,D)): Unblind blinded signature Z with 414 | blind r, yielding N. Output N if 1 = DLEQ_Verify(G,Y,M,Z,D). 415 | Otherwise, output "error". 416 | 417 | We leave the rest of the OPRF algorithms unmodified. When referring 418 | explicitly to VOPRF execution, we replace 'OPRF' in all method names 419 | with 'VOPRF'. 420 | 421 | 4.1. Protocol correctness 422 | 423 | Protocol correctness requires that, for any key k, input x, and (r, 424 | M) = OPRF_Blind(x), it must be true that: 425 | 426 | OPRF_Finalize(x, OPRF_Unblind(r,M,OPRF_Sign(k,M))) = H_2(x, F(k,x)) 427 | 428 | with overwhelming probability. Likewise, in the verifiable setting, 429 | we require that: 430 | 431 | VOPRF_Finalize(x, VOPRF_Unblind(r,(G,Y),M,(VOPRF_Sign(k,(G,Y),M)))) = H_2(x, F(k,x)) 432 | 433 | with overwhelming probability, where (r, M) = VOPRF_Blind(x). 434 | 435 | 4.2. Instantiations of GG 436 | 437 | As we remarked above, GG is a subgroup with associated prime-order p. 438 | While we choose to write operations in the setting where GG comes 439 | equipped with an additive operation, we could also define the 440 | operations in the multiplicative setting. In the multiplicative 441 | setting we can choose GG to be a prime-order subgroup of a finite 442 | field FF_p. For example, let p be some large prime (e.g. > 2048 443 | bits) where p = 2q+1 for some other prime q. Then the subgroup of 444 | squares of FF_p (elements u^2 where u is an element of FF_p) is 445 | 446 | 447 | 448 | Davidson, et al. Expires September 12, 2019 [Page 8] 449 | 450 | Internet-Draft OPRFs March 2019 451 | 452 | 453 | cyclic, and we can pick a generator of this subgroup by picking g 454 | from FF_p (ignoring the identity element). 455 | 456 | For practicality of the protocol, it is preferable to focus on the 457 | cases where GG is an additive subgroup so that we can instantiate the 458 | OPRF in the elliptic curve setting. This amounts to choosing GG to 459 | be a prime-order subgroup of an elliptic curve over base field GF(p) 460 | for prime p. There are also other settings where GG is a prime-order 461 | subgroup of an elliptic curve over a base field of non-prime order, 462 | these include the work of Ristretto [RISTRETTO] and Decaf [DECAF]. 463 | 464 | We will use p > 0 generally for constructing the base field GF(p), 465 | not just those where p is prime. To reiterate, we focus only on the 466 | additive case, and so we focus only on the cases where GF(p) is 467 | indeed the base field. 468 | 469 | 4.3. OPRF algorithms 470 | 471 | This section provides algorithms for each step in the OPRF protocol. 472 | We describe the VOPRF analogues in Section 4.4. We provide generic 473 | utility algorithms in Section 4.5. 474 | 475 | 1. P samples a uniformly random key k <- {0,1}^l for sufficient 476 | length l, and interprets it as an integer. 477 | 478 | 2. V computes X = H_1(x) and a random element r (blinding factor) 479 | from GF(p), and computes M = rX. 480 | 481 | 3. V sends M to P. 482 | 483 | 4. P computes Z = kM = rkX. 484 | 485 | 5. In the elliptic curve setting, P multiplies Z by the cofactor 486 | (denoted h) of the elliptic curve. 487 | 488 | 6. P sends Z to V. 489 | 490 | 7. V unblinds Z to compute N = r^(-1)Z = kX. 491 | 492 | 8. V outputs the pair H_2(x, N). 493 | 494 | 4.3.1. OPRF_Setup 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | Davidson, et al. Expires September 12, 2019 [Page 9] 505 | 506 | Internet-Draft OPRFs March 2019 507 | 508 | 509 | Input: 510 | 511 | l: Some suitable choice of key-length (e.g. as described in {{NIST}}). 512 | 513 | Output: 514 | 515 | k: A key chosen from {0,1}^l and interpreted as an integer value. 516 | 517 | Steps: 518 | 519 | 1. Sample k_bin <-$ {0,1}^l 520 | 2. Output k <- bin2scalar(k_bin, l) 521 | 522 | 4.3.2. OPRF_Blind 523 | 524 | Input: 525 | 526 | x: V's PRF input. 527 | 528 | Output: 529 | 530 | r: Random scalar in [1, p - 1]. 531 | M: Blinded representation of x using blind r, an element in GG. 532 | 533 | Steps: 534 | 535 | 1. r <-$ GF(p) 536 | 2. M := rH_1(x) 537 | 3. Output (r, M) 538 | 539 | 4.3.3. OPRF_Sign 540 | 541 | Input: 542 | 543 | k: Signer secret key. 544 | M: An element in GG. 545 | h: optional cofactor (defaults to 1). 546 | 547 | Output: 548 | 549 | Z: Scalar multiplication of the point M by k, element in GG. 550 | 551 | Steps: 552 | 553 | 1. Z := kM 554 | 2. Z <- hZ 555 | 3. Output Z 556 | 557 | 558 | 559 | 560 | Davidson, et al. Expires September 12, 2019 [Page 10] 561 | 562 | Internet-Draft OPRFs March 2019 563 | 564 | 565 | 4.3.4. OPRF_Unblind 566 | 567 | Input: 568 | 569 | r: Random scalar in [1, p - 1]. 570 | Z: An element in GG. 571 | 572 | Output: 573 | 574 | N: Unblinded signature, element in GG. 575 | 576 | Steps: 577 | 578 | 1. N := (1/r)Z 579 | 2. Output N 580 | 581 | 4.3.5. OPRF_Finalize 582 | 583 | Input: 584 | 585 | x: PRF input string. 586 | N: An element in GG. 587 | 588 | Output: 589 | 590 | y: Random element in {0,1}^L. 591 | 592 | Steps: 593 | 594 | 1. y := H_2(x, N) 595 | 2. Output y 596 | 597 | 4.4. VOPRF algorithms 598 | 599 | The steps in the VOPRF setting are written as: 600 | 601 | 1. P samples a uniformly random key k <- {0,1}^l for sufficient 602 | length l, and interprets it as an integer. 603 | 604 | 2. P commits to k by computing (G,Y) for Y=kG and where G is a 605 | generator of GG. P makes (G,Y) publicly available. 606 | 607 | 3. V computes X = H_1(x) and a random element r (blinding factor) 608 | from GF(p), and computes M = rX. 609 | 610 | 4. V sends M to P. 611 | 612 | 5. P computes Z = kM = rkX, and D = DLEQ_Generate(k,G,Y,M,Z). 613 | 614 | 615 | 616 | Davidson, et al. Expires September 12, 2019 [Page 11] 617 | 618 | Internet-Draft OPRFs March 2019 619 | 620 | 621 | 6. P sends (Z, D) to V. 622 | 623 | 7. V ensures that 1 = DLEQ_Verify(G,Y,M,Z,D). If not, V outputs an 624 | error. 625 | 626 | 8. V unblinds Z to compute N = r^(-1)Z = kX. 627 | 628 | 9. V outputs the pair H_2(x, N). 629 | 630 | 4.4.1. VOPRF_Setup 631 | 632 | Input: 633 | 634 | G: Public generator of GG. 635 | l: Some suitable choice of key-length (e.g. as described in {{NIST}}). 636 | 637 | Output: 638 | 639 | k: A key chosen from {0,1}^l and interpreted as an integer value. 640 | (G,Y): A pair of curve points, where Y=kG. 641 | 642 | Steps: 643 | 644 | 1. k <- OPRF_Setup(l) 645 | 2. Y := kG 646 | 3. Output (k, (G,Y)) 647 | 648 | 4.4.2. VOPRF_Blind 649 | 650 | Input: 651 | 652 | x: V's PRF input. 653 | 654 | Output: 655 | 656 | r: Random scalar in [1, p - 1]. 657 | M: Blinded representation of x using blind r, an element in GG. 658 | 659 | Steps: 660 | 661 | 1. r <-$ GF(p) 662 | 2. M := rH_1(x) 663 | 3. Output (r, M) 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | Davidson, et al. Expires September 12, 2019 [Page 12] 673 | 674 | Internet-Draft OPRFs March 2019 675 | 676 | 677 | 4.4.3. VOPRF_Sign 678 | 679 | Input: 680 | 681 | k: Signer secret key. 682 | G: Public generator of group GG. 683 | Y: Signer public key (= kG). 684 | M: An element in GG. 685 | h: optional cofactor (defaults to 1). 686 | 687 | Output: 688 | 689 | Z: Scalar multiplication of the point M by k, element in GG. 690 | D: DLEQ proof that log_G(Y) == log_M(Z). 691 | 692 | Steps: 693 | 694 | 1. Z := kM 695 | 2. Z <- hZ 696 | 3. D = DLEQ_Generate(k,G,Y,M,Z) 697 | 4. Output (Z, D) 698 | 699 | 4.4.4. VOPRF_Unblind 700 | 701 | Input: 702 | 703 | r: Random scalar in [1, p - 1]. 704 | G: Public generator of group GG. 705 | Y: Signer public key. 706 | M: Blinded representation of x using blind r, an element in GG. 707 | Z: An element in GG. 708 | D: D = DLEQ_Generate(k,G,Y,M,Z). 709 | 710 | Output: 711 | 712 | N: Unblinded signature, element in GG. 713 | 714 | Steps: 715 | 716 | 1. N := (1/r)Z 717 | 2. If 1 = DLEQ_Verify(G,Y,M,Z,D), output N 718 | 3. Output "error" 719 | 720 | 4.4.5. VOPRF_Finalize 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | Davidson, et al. Expires September 12, 2019 [Page 13] 729 | 730 | Internet-Draft OPRFs March 2019 731 | 732 | 733 | Input: 734 | 735 | x: PRF input string. 736 | N: An element in GG, or "error". 737 | 738 | Output: 739 | 740 | y: Random element in {0,1}^L, or "error" 741 | 742 | Steps: 743 | 744 | 1. If N == "error", output "error". 745 | 2. y := H_2(x, N) 746 | 3. Output y 747 | 748 | 4.5. Utility algorithms 749 | 750 | 4.5.1. bin2scalar 751 | 752 | This algorithm converts a binary string to an integer modulo p. 753 | 754 | Input: 755 | 756 | s: binary string (little-endian) 757 | l: length of binary string 758 | p: modulus 759 | 760 | Output: 761 | 762 | z: An integer modulo p 763 | 764 | Steps: 765 | 766 | 1. sVec <- vec(s) (converts s to a column vector of dimension l) 767 | 2. p2Vec <- (2^0, 2^1, ..., 2^{l-1}) (row vector of dimension l) 768 | 3. z <- p2Vec * sVec (mod p) 769 | 4. Output z 770 | 771 | 4.6. Efficiency gains with pre-processing and additive blinding 772 | 773 | In the [OPAQUE] draft, it is noted that it may be more efficient to 774 | use additive blinding rather than multiplicative if the client can 775 | preprocess some values. For example, computing rH_1(x) is an example 776 | of multiplicative blinding. A valid way of computing additive 777 | blinding would be to instead compute H_1(x)+rG, where G is the common 778 | generator for the group. 779 | 780 | 781 | 782 | 783 | 784 | Davidson, et al. Expires September 12, 2019 [Page 14] 785 | 786 | Internet-Draft OPRFs March 2019 787 | 788 | 789 | If the client preprocesses values of the form rG, then computing 790 | H_1(x)+rG is more efficient than computing rH_1(x) (one addition 791 | against log_2(r)). Therefore, it may be advantageous to define the 792 | OPRF and VOPRF protocols using additive blinding rather than 793 | multiplicative blinding. In fact the only algorithms that need to 794 | change are OPRF_Blind and OPRF_Unblind (and similarly for the VOPRF 795 | variants). 796 | 797 | We define the additive blinding variants of the above algorithms 798 | below along with a new algorithm OPRF_Preprocess that defines how 799 | preprocessing is carried out. The equivalent algorithms for VOPRF 800 | are almost identical and so we do not redefine them here. Notice 801 | that the only computation that changes is for V, the necessary 802 | computation of P does not change. 803 | 804 | 4.6.1. OPRF_Preprocess 805 | 806 | Input: 807 | 808 | G: Public generator of GG 809 | 810 | Output: 811 | 812 | r: Random scalar in [1, p-1] 813 | rG: An element in GG. 814 | rY: An element in GG. 815 | 816 | Steps: 817 | 818 | 1. r <-$ GF(p) 819 | 2. Output (r, rG, rY) 820 | 821 | 4.6.2. OPRF_Blind 822 | 823 | Input: 824 | 825 | x: V's PRF input. 826 | rG: Preprocessed element of GG. 827 | 828 | Output: 829 | 830 | M: Blinded representation of x using blind r, an element in GG. 831 | 832 | Steps: 833 | 834 | 1. M := H_1(x)+rG 835 | 2. Output M 836 | 837 | 838 | 839 | 840 | Davidson, et al. Expires September 12, 2019 [Page 15] 841 | 842 | Internet-Draft OPRFs March 2019 843 | 844 | 845 | 4.6.3. OPRF_Unblind 846 | 847 | Input: 848 | 849 | rY: Preprocessed element of GG. 850 | M: Blinded representation of x using rG, an element in GG. 851 | Z: An element in GG. 852 | 853 | Output: 854 | 855 | N: Unblinded signature, element in GG. 856 | 857 | Steps: 858 | 859 | 1. N := Z-rY 860 | 2. Output N 861 | 862 | Notice that OPRF_Unblind computes (Z-rY) = k(H_1(x)+rG) - rkG = 863 | kH_1(x) by the commutativity of scalar multiplication in GG. This is 864 | the same output as in the original OPRF_Unblind algorithm. 865 | 866 | 5. NIZK Discrete Logarithm Equality Proof 867 | 868 | For the VOPRF protocol we require that V is able to verify that P has 869 | used its private key k to evaluate the PRF. We can do this by 870 | showing that the original commitment (G,Y) output by VOPRF_Setup(l) 871 | satisfies log_G(Y) == log_M(Z) where Z is the output of 872 | VOPRF_Sign(k,(G,Y),M). 873 | 874 | This may be used, for example, to ensure that P uses the same private 875 | key for computing the VOPRF output and does not attempt to "tag" 876 | individual verifiers with select keys. This proof must not reveal 877 | the P's long-term private key to V. 878 | 879 | Consequently, this allows extending the OPRF protocol with a (non- 880 | interactive) discrete logarithm equality (DLEQ) algorithm built on a 881 | Chaum-Pedersen [ChaumPedersen] proof. This proof is divided into two 882 | procedures: DLEQ_Generate and DLEQ_Verify. These are specified 883 | below. 884 | 885 | 5.1. DLEQ_Generate 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | Davidson, et al. Expires September 12, 2019 [Page 16] 897 | 898 | Internet-Draft OPRFs March 2019 899 | 900 | 901 | Input: 902 | 903 | k: Signer secret key. 904 | G: Public generator of GG. 905 | Y: Signer public key (= kG). 906 | M: An element in GG. 907 | Z: An element in GG. 908 | H_3: A hash function from GG to {0,1}^L, modelled as a random oracle. 909 | 910 | Output: 911 | 912 | D: DLEQ proof (c, s). 913 | 914 | Steps: 915 | 916 | 1. r <-$ GF(p) 917 | 2. A := rG and B := rM. 918 | 3. c <- H_3(G,Y,M,Z,A,B) 919 | 4. s := (r - ck) (mod p) 920 | 5. Output D := (c, s) 921 | 922 | 5.2. DLEQ_Verify 923 | 924 | Input: 925 | 926 | G: Public generator of GG. 927 | Y: Signer public key. 928 | M: An element in GG. 929 | Z: An element in GG. 930 | D: DLEQ proof (c, s). 931 | 932 | Output: 933 | 934 | True if log_G(Y) == log_M(Z), False otherwise. 935 | 936 | Steps: 937 | 938 | 1. A' := (sG + cY) 939 | 2. B' := (sM + cZ) 940 | 3. c' <- H_3(G,Y,M,Z,A',B') 941 | 4. Output c == c' 942 | 943 | 6. Batched VOPRF evaluation 944 | 945 | Common applications (e.g. [PrivacyPass]) require V to obtain 946 | multiple PRF evaluations from P. In the VOPRF case, this would also 947 | require generation and verification of a DLEQ proof for each Zi 948 | received by V. This is costly, both in terms of computation and 949 | 950 | 951 | 952 | Davidson, et al. Expires September 12, 2019 [Page 17] 953 | 954 | Internet-Draft OPRFs March 2019 955 | 956 | 957 | communication. To get around this, applications use a 'batching' 958 | procedure for generating and verifying DLEQ proofs for a finite 959 | number of PRF evaluation pairs (Mi,Zi). For n PRF evaluations: 960 | 961 | o Proof generation is slightly more expensive from 2n modular 962 | exponentiations to 2n+2. 963 | 964 | o Proof verification is much more efficient, from 4m modular 965 | exponentiations to 2n+4. 966 | 967 | o Communications falls from 2n to 2 group elements. 968 | 969 | Therefore, since P is usually a powerful server, we can tolerate a 970 | slight increase in proof generation complexity for much more 971 | efficient communication and proof verification. 972 | 973 | In this section, we describe algorithms for batching the DLEQ 974 | generation and verification procedure. For these algorithms we 975 | require a pseudorandom generator PRNG: {0,1}^a x ZZ -> ({0,1}^b)^n 976 | that takes a seed of length a and an integer n as input, and outputs 977 | n elements in {0,1}^b. 978 | 979 | 6.1. Batched DLEQ algorithms 980 | 981 | 6.1.1. Batched_DLEQ_Generate 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | Davidson, et al. Expires September 12, 2019 [Page 18] 1009 | 1010 | Internet-Draft OPRFs March 2019 1011 | 1012 | 1013 | Input: 1014 | 1015 | k: Signer secret key. 1016 | G: Public generator of group GG. 1017 | Y: Signer public key (= kG). 1018 | n: Number of PRF evaluations. 1019 | [Mi]: An array of points in GG of length n. 1020 | [Zi]: An array of points in GG of length n. 1021 | PRNG: A pseudorandom generator of the form above. 1022 | salt: An integer salt value for each PRNG invocation 1023 | info: A string value for splitting the domain of the PRNG 1024 | H_4: A hash function from GG^(2n+2) to {0,1}^a, modelled as a random oracle. 1025 | 1026 | Output: 1027 | 1028 | D: DLEQ proof (c, s). 1029 | 1030 | Steps: 1031 | 1032 | 1. seed <- H_4(G,Y,[Mi,Zi])) 1033 | 2. d1,...dn <- PRNG(seed,salt,info,n) 1034 | 3. c1,...,cn := (int)d1,...,(int)dn 1035 | 4. M := c1M1 + ... + cnMn 1036 | 5. Z := c1Z1 + ... + cnZn 1037 | 6. Output D <- DLEQ_Generate(k,G,Y,M,Z) 1038 | 1039 | 6.1.2. Batched_DLEQ_Verify 1040 | 1041 | Input: 1042 | 1043 | G: Public generator of group GG. 1044 | Y: Signer public key. 1045 | [Mi]: An array of points in GG of length n. 1046 | [Zi]: An array of points in GG of length n. 1047 | D: DLEQ proof (c, s). 1048 | 1049 | Output: 1050 | 1051 | True if log_G(Y) == log_(Mi)(Zi) for each i in 1...n, False otherwise. 1052 | 1053 | Steps: 1054 | 1055 | 1. seed <- H_4(G,Y,[Mi,Zi])) 1056 | 2. d1,...dn <- PRNG(seed,salt,info,n) 1057 | 3. c1,...,cn := (int)d1,...,(int)dn 1058 | 4. M := c1M1 + ... + cnMn 1059 | 5. Z := c1Z1 + ... + cnZn 1060 | 6. Output DLEQ_Verify(G,Y,M,Z,D) 1061 | 1062 | 1063 | 1064 | Davidson, et al. Expires September 12, 2019 [Page 19] 1065 | 1066 | Internet-Draft OPRFs March 2019 1067 | 1068 | 1069 | 6.2. Modified protocol execution 1070 | 1071 | The VOPRF protocol from Section Section 4 changes to allow specifying 1072 | multiple blinded PRF inputs [Mi] for i in 1...n. Then P computes the 1073 | array [Zi] and replaces DLEQ_Generate with Batched_DLEQ_Generate over 1074 | these arrays. The same applies to the algorithm VOPRF_Sign. The 1075 | same applies for replacing DLEQ_Verify with Batched_DLEQ_Verify when 1076 | V verifies the response from P and during the algorithm VOPRF_Verify. 1077 | 1078 | 6.3. PRNG and resampling 1079 | 1080 | Any function that satisfies the security properties of a pseudorandom 1081 | number generator can be used for computing the batched DLEQ proof. 1082 | For example, SHAKE-256 [SHAKE] or HKDF-SHA256 [RFC5869] would be 1083 | reasonable choices for groups that have an order of 256 bits. 1084 | 1085 | We note that the PRNG outputs d1,...,dn must be smaller than the 1086 | order of the group/curve that is being used. Resampling can be 1087 | achieved by increasing the value of the iterator that is used in the 1088 | info field of the PRNG input. 1089 | 1090 | 7. Supported ciphersuites 1091 | 1092 | This section specifies supported ECVOPRF group and hash function 1093 | instantiations. We only provide ciphersuites in the EC setting as 1094 | these provide the most efficient way of instantiating the OPRF. Our 1095 | instantiation includes considerations for providing the DLEQ proofs 1096 | that make the instantiation a VOPRF. Supporting OPRF operations 1097 | (ECOPRF) alone can be allowed by simply dropping the relevant 1098 | components. In addition, we currently only support ciphersuites 1099 | demonstrating 128 bits of security. 1100 | 1101 | 7.1. ECVOPRF-P256-HKDF-SHA256-SSWU: 1102 | 1103 | o GG: SECP256K1 curve [SEC2] 1104 | 1105 | o H_1: H2C-P256-SHA256-SSWU- [I-D.irtf-cfrg-hash-to-curve] 1106 | 1107 | * label: voprf_h2c 1108 | 1109 | o H_2: SHA256 1110 | 1111 | o H_3: SHA256 1112 | 1113 | o H_4: SHA256 1114 | 1115 | o PRNG: HKDF-SHA256 1116 | 1117 | 1118 | 1119 | 1120 | Davidson, et al. Expires September 12, 2019 [Page 20] 1121 | 1122 | Internet-Draft OPRFs March 2019 1123 | 1124 | 1125 | 7.2. ECVOPRF-RISTRETTO-HKDF-SHA512-Elligator2: 1126 | 1127 | o GG: Ristretto [RISTRETTO] 1128 | 1129 | o H_1: H2C-Curve25519-SHA512-Elligator2-Clear 1130 | [I-D.irtf-cfrg-hash-to-curve] 1131 | 1132 | * label: voprf_h2c 1133 | 1134 | o H_2: SHA512 1135 | 1136 | o H_3: SHA512 1137 | 1138 | o H_4: SHA512 1139 | 1140 | o PRNG: HKDF-SHA512 1141 | 1142 | In the case of Ristretto, internal point representations are 1143 | represented by Ed25519 [RFC7748] points. As a result, we can use the 1144 | same hash-to-curve encoding as we would use for Ed25519 1145 | [I-D.irtf-cfrg-hash-to-curve]. We remark that the 'label' field is 1146 | necessary for domain separation of the hash-to-curve functionality. 1147 | 1148 | 8. Security Considerations 1149 | 1150 | Security of the protocol depends on P's secrecy of k. Best practices 1151 | recommend P regularly rotate k so as to keep its window of compromise 1152 | small. Moreover, it each key should be generated from a source of 1153 | safe, cryptographic randomness. 1154 | 1155 | Another critical aspect of this protocol is reliance on 1156 | [I-D.irtf-cfrg-hash-to-curve] for mapping arbitrary inputs x to 1157 | points on a curve. Security requires this mapping be pre-image and 1158 | collision resistant. 1159 | 1160 | 8.1. Timing Leaks 1161 | 1162 | To ensure no information is leaked during protocol execution, all 1163 | operations that use secret data MUST be constant time. Operations 1164 | that SHOULD be constant time include: H_1() (hashing arbitrary 1165 | strings to curves) and DLEQ_Generate(). 1166 | [I-D.irtf-cfrg-hash-to-curve] describes various algorithms for 1167 | constant-time implementations of H_1. 1168 | 1169 | 1170 | 1171 | 1172 | 1173 | 1174 | 1175 | 1176 | Davidson, et al. Expires September 12, 2019 [Page 21] 1177 | 1178 | Internet-Draft OPRFs March 2019 1179 | 1180 | 1181 | 8.2. Hashing to curves 1182 | 1183 | We choose different encodings in relation to the elliptic curve that 1184 | is used, all methods are illuminated precisely in 1185 | [I-D.irtf-cfrg-hash-to-curve]. In summary, we use the simplified 1186 | Shallue-Woestijne-Ulas algorithm for hashing binary strings to the 1187 | P-256 curve; the Icart algorithm for hashing binary strings to P384; 1188 | the Elligator2 algorithm for hashing binary strings to CURVE25519 and 1189 | CURVE448. 1190 | 1191 | 8.3. Verifiability (key consistency) 1192 | 1193 | DLEQ proofs are essential to the protocol to allow V to check that 1194 | P's designated private key was used in the computation. A side 1195 | effect of this property is that it prevents P from using a unique key 1196 | for select verifiers as a way of "tagging" them. If all verifiers 1197 | expect use of a certain private key, e.g., by locating P's public key 1198 | published from a trusted registry, then P cannot present unique keys 1199 | to an individual verifier. 1200 | 1201 | For this side effect to hold, P must also be prevented from using 1202 | other techniques to manipulate their public key within the trusted 1203 | registry to reduce client anonymity. For example, if P's public key 1204 | is rotated too frequently then this may stratify the user base into 1205 | small anonymity groups (those with VOPRF_Sign outputs taken from a 1206 | given key epoch). In this case, it may become practical to link 1207 | VOPRF sessions for a given user and thus compromises their privacy. 1208 | 1209 | Similarly, if P can publish N public keys to a trusted registry then 1210 | P may be able to control presentation of these keys in such a way 1211 | that V is retroactively identified by V's key choice across multiple 1212 | requests. 1213 | 1214 | 9. Applications 1215 | 1216 | This section describes various applications of the VOPRF protocol. 1217 | 1218 | 9.1. Privacy Pass 1219 | 1220 | This VOPRF protocol is used by Privacy Pass system to help Tor users 1221 | bypass CAPTCHA challenges. Their system works as follows. Client C 1222 | connects - through Tor - to an edge server E serving content. Upon 1223 | receipt, E serves a CAPTCHA to C, who then solves the CAPTCHA and 1224 | supplies, in response, n blinded points. E verifies the CAPTCHA 1225 | response and, if valid, signs (at most) n blinded points, which are 1226 | then returned to C along with a batched DLEQ proof. C stores the 1227 | tokens if the batched proof verifies correctly. When C attempts to 1228 | connect to E again and is prompted with a CAPTCHA, C uses one of the 1229 | 1230 | 1231 | 1232 | Davidson, et al. Expires September 12, 2019 [Page 22] 1233 | 1234 | Internet-Draft OPRFs March 2019 1235 | 1236 | 1237 | unblinded and signed points, or tokens, to derive a shared symmetric 1238 | key sk used to MAC the CAPTCHA challenge. C sends the CAPTCHA, MAC, 1239 | and token input x to E, who can use x to derive sk and verify the 1240 | CAPTCHA MAC. Thus, each token is used at most once by the system. 1241 | 1242 | The Privacy Pass implementation uses the P-256 instantiation of the 1243 | VOPRF protocol. For more details, see [DGSTV18]. 1244 | 1245 | 9.2. Private Password Checker 1246 | 1247 | In this application, let D be a collection of plaintext passwords 1248 | obtained by prover P. For each password p in D, P computes 1249 | VOPRF_Sign on H_1(p), where H_1 is as described above, and stores the 1250 | result in a separate collection D'. P then publishes D' with Y, its 1251 | public key. If a client C wishes to query D' for a password p', it 1252 | runs the VOPRF protocol using p as input x to obtain output y. By 1253 | construction, y will be the signature of p hashed onto the curve. C 1254 | can then search D' for y to determine if there is a match. 1255 | 1256 | Examples of such password checkers already exist, for example: 1257 | [JKKX16], [JKK14] and [SJKS17]. 1258 | 1259 | 9.2.1. Parameter Commitments 1260 | 1261 | For some applications, it may be desirable for P to bind tokens to 1262 | certain parameters, e.g., protocol versions, ciphersuites, etc. To 1263 | accomplish this, P should use a distinct scalar for each parameter 1264 | combination. Upon redemption of a token T from V, P can later verify 1265 | that T was generated using the scalar associated with the 1266 | corresponding parameters. 1267 | 1268 | 10. Acknowledgements 1269 | 1270 | This document resulted from the work of the Privacy Pass team 1271 | [PrivacyPass]. The authors would also like to acknowledge the 1272 | helpful conversations with Hugo Krawczyk. Eli-Shaoul Khedouri 1273 | provided additional review and comments on key consistency. 1274 | 1275 | 11. Normative References 1276 | 1277 | [ChaumBlindSignature] 1278 | "Blind Signatures for Untraceable Payments", n.d., 1279 | . 1281 | 1282 | [ChaumPedersen] 1283 | "Wallet Databases with Observers", n.d., 1284 | . 1285 | 1286 | 1287 | 1288 | Davidson, et al. Expires September 12, 2019 [Page 23] 1289 | 1290 | Internet-Draft OPRFs March 2019 1291 | 1292 | 1293 | [DECAF] "Decaf, Eliminating cofactors through point compression", 1294 | n.d., . 1295 | 1296 | [DGSTV18] "Privacy Pass, Bypassing Internet Challenges Anonymously", 1297 | n.d., . 1300 | 1301 | [I-D.irtf-cfrg-hash-to-curve] 1302 | Scott, S., Sullivan, N., and C. Wood, "Hashing to Elliptic 1303 | Curves", draft-irtf-cfrg-hash-to-curve-02 (work in 1304 | progress), October 2018. 1305 | 1306 | [JKK14] "Round-Optimal Password-Protected Secret Sharing and 1307 | T-PAKE in the Password-Only model", n.d., 1308 | . 1309 | 1310 | [JKKX16] "Highly-Efficient and Composable Password-Protected Secret 1311 | Sharing (Or, How to Protect Your Bitcoin Wallet Online)", 1312 | n.d., . 1313 | 1314 | [NIST] "Keylength - NIST Report on Cryptographic Key Length and 1315 | Cryptoperiod (2016)", n.d., 1316 | . 1317 | 1318 | [OPAQUE] "The OPAQUE Asymmetric PAKE Protocol", n.d., 1319 | . 1321 | 1322 | [PrivacyPass] 1323 | "Privacy Pass", n.d., 1324 | . 1325 | 1326 | [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 1327 | Requirement Levels", BCP 14, RFC 2119, 1328 | DOI 10.17487/RFC2119, March 1997, 1329 | . 1330 | 1331 | [RFC5869] Krawczyk, H. and P. Eronen, "HMAC-based Extract-and-Expand 1332 | Key Derivation Function (HKDF)", RFC 5869, 1333 | DOI 10.17487/RFC5869, May 2010, 1334 | . 1335 | 1336 | [RFC7748] Langley, A., Hamburg, M., and S. Turner, "Elliptic Curves 1337 | for Security", RFC 7748, DOI 10.17487/RFC7748, January 1338 | 2016, . 1339 | 1340 | 1341 | 1342 | 1343 | 1344 | Davidson, et al. Expires September 12, 2019 [Page 24] 1345 | 1346 | Internet-Draft OPRFs March 2019 1347 | 1348 | 1349 | [RFC8032] Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital 1350 | Signature Algorithm (EdDSA)", RFC 8032, 1351 | DOI 10.17487/RFC8032, January 2017, 1352 | . 1353 | 1354 | [RISTRETTO] 1355 | "The ristretto255 Group", n.d., 1356 | . 1358 | 1359 | [SEC2] Standards for Efficient Cryptography Group (SECG), ., "SEC 1360 | 2: Recommended Elliptic Curve Domain Parameters", n.d., 1361 | . 1362 | 1363 | [SHAKE] "SHA-3 Standard, Permutation-Based Hash and Extendable- 1364 | Output Functions", n.d., 1365 | . 1368 | 1369 | [SJKS17] "SPHINX, A Password Store that Perfectly Hides from 1370 | Itself", n.d., 1371 | . 1372 | 1373 | Appendix A. Test Vectors 1374 | 1375 | This section includes test vectors for the ECVOPRF-P256-HKDF-SHA256 1376 | VOPRF ciphersuite, including batched DLEQ output. 1377 | 1378 | 1379 | 1380 | 1381 | 1382 | 1383 | 1384 | 1385 | 1386 | 1387 | 1388 | 1389 | 1390 | 1391 | 1392 | 1393 | 1394 | 1395 | 1396 | 1397 | 1398 | 1399 | 1400 | Davidson, et al. Expires September 12, 2019 [Page 25] 1401 | 1402 | Internet-Draft OPRFs March 2019 1403 | 1404 | 1405 | P-256 1406 | X: 04b14b08f954f5b6ab1d014b1398f03881d70842acdf06194eb96a6d08186f8cb985c1c5521 \ 1407 | f4ee19e290745331f7eb89a4053de0673dc8ef14cfe9bf8226c6b31 1408 | r: b72265c85b1ba42cfed7caaf00d2ccac0b1a99259ba0dbb5a1fc2941526a6849 1409 | M: 046025a41f81a160c648cfe8fdcaa42e5f7da7a71055f8e23f1dc7e4204ab84b705043ba5c7 \ 1410 | 000123e1fd058150a4d3797008f57a8b2537766d9419c7396ba5279 1411 | k: f84e197c8b712cdf452d2cff52dec1bd96220ed7b9a6f66ed28c67503ae62133 1412 | Z: 043ab5ccb690d844dcb780b2d9e59126d62bc853ba01b2c339ba1c1b78c03e4b6adc5402f77 \ 1413 | 9fc29f639edc138012f0e61960e1784973b37f864e4dc8abbc68e0b 1414 | N: 04e8aa6792d859075821e2fba28500d6974ba776fe230ba47ef7e42be1d967654ce776f889e \ 1415 | e1f374ffa0bce904408aaa4ed8a19c6cc7801022b7848031f4e442a 1416 | D: { s: faddfaf6b5d6b4b6357adf856fc1e0044614ebf9dafdb4c6541c1c9e61243c5b, 1417 | c: 8b403e170b56c915cc18864b3ab3c2502bd8f5ca25301bc03ab5138343040c7b } 1418 | 1419 | P-256 1420 | X: 047e8d567e854e6bdc95727d48b40cbb5569299e0a4e339b6d707b2da3508eb6c238d3d4cb4 \ 1421 | 68afc6ffc82fccbda8051478d1d2c9b21ffdfd628506c873ebb1249 1422 | r: f222dfe530fdbfcb02eb851867bfa8a6da1664dfc7cee4a51eb6ff83c901e15e 1423 | M: 04e2efdc73747e15e38b7a1bb90fe5e4ef964b3b8dccfda428f85a431420c84efca02f0f09c \ 1424 | 83a8241b44572a059ab49c080a39d0bce2d5d0b44ff5d012b5184e7 1425 | k: fb164de0a87e601fd4435c0d7441ff822b5fa5975d0c68035beac05a82c41118 1426 | Z: 049d01e1c555bd3324e8ce93a13946b98bdcc765298e6d60808f93c00bdfba2ebf48eef8f28 \ 1427 | d8c91c903ad6bea3d840f3b9631424a6cc543a0a0e1f2d487192d5b 1428 | N: 04723880e480b60b4415ca627585d1715ab5965570d30c94391a8b023f8854ac26f76c1d6ab \ 1429 | bb38688a5affbcadad50ecbf7c93ef33ddfd735003b5a4b1a21ba14 1430 | D: { s: dfdf6ae40d141b61d5b2d72cf39c4a6c88db6ac5b12044a70c212e2bf80255b4, 1431 | c: 271979a6b51d5f71719127102621fe250e3235867cfcf8dea749c3e253b81997 } 1432 | 1433 | Batched DLEQ (P256) 1434 | M_0: 046025a41f81a160c648cfe8fdcaa42e5f7da7a71055f8e23f1dc7e4204ab84b705043ba5c\ 1435 | 7000123e1fd058150a4d3797008f57a8b2537766d9419c7396ba5279 1436 | M_1: 04e2efdc73747e15e38b7a1bb90fe5e4ef964b3b8dccfda428f85a431420c84efca02f0f09\ 1437 | c83a8241b44572a059ab49c080a39d0bce2d5d0b44ff5d012b5184e7 1438 | Z_0: 043ab5ccb690d844dcb780b2d9e59126d62bc853ba01b2c339ba1c1b78c03e4b6adc5402f7\ 1439 | 79fc29f639edc138012f0e61960e1784973b37f864e4dc8abbc68e0b 1440 | Z_1: 04647e1ab7946b10c1c1c92dd333e2fc9e93e85fdef5939bf2f376ae859248513e0cd91115\ 1441 | e48c6852d8dd173956aec7a81401c3f63a133934898d177f2a237eeb 1442 | k: f84e197c8b712cdf452d2cff52dec1bd96220ed7b9a6f66ed28c67503ae62133 1443 | PRNG: HKDF-SHA256 1444 | salt: "DLEQ_PROOF" 1445 | info: an iterator i for invoking the PRNG on M_i and Z_i 1446 | D: { s: b2123044e633d4721894d573decebc9366869fe3c6b4b79a00311ecfa46c9e34, 1447 | c: 3506df9008e60130fcddf86fdb02cbfe4ceb88ff73f66953b1606f6603309862 } 1448 | 1449 | 1450 | 1451 | 1452 | 1453 | 1454 | 1455 | 1456 | Davidson, et al. Expires September 12, 2019 [Page 26] 1457 | 1458 | Internet-Draft OPRFs March 2019 1459 | 1460 | 1461 | Authors' Addresses 1462 | 1463 | Alex Davidson 1464 | Cloudflare 1465 | County Hall 1466 | London, SE1 7GP 1467 | United Kingdom 1468 | 1469 | Email: adavidson@cloudflare.com 1470 | 1471 | 1472 | Nick Sullivan 1473 | Cloudflare 1474 | 101 Townsend St 1475 | San Francisco 1476 | United States of America 1477 | 1478 | Email: nick@cloudflare.com 1479 | 1480 | 1481 | Christopher A. Wood 1482 | Apple Inc. 1483 | One Apple Park Way 1484 | Cupertino, California 95014 1485 | United States of America 1486 | 1487 | Email: cawood@apple.com 1488 | 1489 | 1490 | 1491 | 1492 | 1493 | 1494 | 1495 | 1496 | 1497 | 1498 | 1499 | 1500 | 1501 | 1502 | 1503 | 1504 | 1505 | 1506 | 1507 | 1508 | 1509 | 1510 | 1511 | 1512 | Davidson, et al. Expires September 12, 2019 [Page 27] 1513 | --------------------------------------------------------------------------------