├── .gitmodules ├── LICENSE.md ├── .note.xml ├── .gitignore ├── Makefile ├── poc ├── .gitignore ├── Makefile ├── groups.sage ├── test_oprf.sage ├── ristretto_decaf.sage ├── oprf.sage └── vectors │ └── allVectors.json ├── .github └── workflows │ ├── archive.yml │ ├── publish.yml │ └── ghpages.yml ├── .travis.yml ├── CONTRIBUTING.md ├── README.md └── releases ├── draft-sullivan-cfrg-voprf-00.txt └── draft-sullivan-cfrg-voprf-03.txt /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "poc/h2c"] 2 | path = poc/h2c 3 | url = git@github.com:cfrg/draft-irtf-cfrg-hash-to-curve.git 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.note.xml: -------------------------------------------------------------------------------- 1 | 2 | Source for this draft and an issue tracker can be found at 3 | . 4 | 5 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /.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/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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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-07/poc) | Sage/Python | draft-07 | 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-08 | Base | 27 | | [voprf](https://github.com/bytemare/voprf) | Go | draft-08 | All | 28 | | [CIRCL](https://github.com/cloudflare/circl) | Go | draft-08 | 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 | | [voprf-poc-go](https://github.com/alxdavids/voprf-poc/tree/master/go) | Go | draft-03 | All | 32 | | [voprf-poc-rust](https://github.com/alxdavids/voprf-poc/tree/master/rust) | Rust | draft-03 | All | 33 | | [ecc](https://github.com/aldenml/ecc) | C | draft-08 | All | 34 | 35 | ### Other Integrations 36 | 37 | | Implementation | Language | Version | Modes | Notes | 38 | | ------------------------------------------------------------------------- |:---------|:---------|:-------|:------| 39 | | [opaque-ke](https://github.com/novifinancial/opaque-ke/) | Rust | draft-06 | Base | As a component for OPAQUE | 40 | | [opaque](https://github.com/bytemare/opaque) | Go | draft-08 | Base | As a component for OPAQUE | 41 | | [libopaque](https://github.com/stef/libopaque) | C | draft-09 | Base | As a component for OPAQUE | 42 | 43 | Submit a PR if you have a compliant implementation! 44 | 45 | ## Contributing 46 | 47 | See the 48 | [guidelines for contributions](https://github.com/cfrg/draft-irtf-cfrg-voprf/blob/master/CONTRIBUTING.md). 49 | -------------------------------------------------------------------------------- /poc/groups.sage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sage 2 | # vim: syntax=python 3 | 4 | import sys 5 | import json 6 | import random 7 | import hashlib 8 | import binascii 9 | import struct 10 | 11 | from hash_to_field import I2OSP, OS2IP, expand_message_xmd, expand_message_xof, XMDExpander, hash_to_field 12 | 13 | try: 14 | from sagelib.suite_p256 import p256_sswu_ro, p256_order, p256_p, p256_F, p256_A, p256_B 15 | from sagelib.suite_p384 import p384_sswu_ro, p384_order, p384_p, p384_F, p384_A, p384_B 16 | from sagelib.suite_p521 import p521_sswu_ro, p521_order, p521_p, p521_F, p521_A, p521_B 17 | from sagelib.common import sgn0 18 | from sagelib.ristretto_decaf import Ed25519Point, Ed448GoldilocksPoint 19 | except ImportError as e: 20 | sys.exit("Error loading preprocessed sage files. Try running `make setup && make clean pyfiles`. Full error: " + e) 21 | 22 | if sys.version_info[0] == 3: 23 | xrange = range 24 | _as_bytes = lambda x: x if isinstance(x, bytes) else bytes(x, "utf-8") 25 | _strxor = lambda str1, str2: bytes( s1 ^ s2 for (s1, s2) in zip(str1, str2) ) 26 | else: 27 | _as_bytes = lambda x: x 28 | _strxor = lambda str1, str2: ''.join( chr(ord(s1) ^ ord(s2)) for (s1, s2) in zip(str1, str2) ) 29 | 30 | # Fix a seed so all test vectors are deterministic 31 | FIXED_SEED = "oprf".encode('utf-8') 32 | random.seed(int.from_bytes(hashlib.sha256(FIXED_SEED).digest(), 'big')) 33 | 34 | # little-endian version of I2OSP 35 | def I2OSP_le(val, length): 36 | val = int(val) 37 | if val < 0 or val >= (1 << (8 * length)): 38 | raise ValueError("bad I2OSP call: val=%d length=%d" % (val, length)) 39 | ret = [0] * length 40 | val_ = val 41 | for idx in range(0, length): 42 | ret[idx] = val_ & 0xff 43 | val_ = val_ >> 8 44 | ret = struct.pack("=" + "B" * length, *ret) 45 | assert OS2IP_le(ret, True) == val 46 | return ret 47 | 48 | # little-endian version of OS2IP 49 | def OS2IP_le(octets, skip_assert=False): 50 | ret = 0 51 | for octet in reversed(struct.unpack("=" + "B" * len(octets), octets)): 52 | ret = ret << 8 53 | ret += octet 54 | if not skip_assert: 55 | assert octets == I2OSP_le(ret, len(octets)) 56 | return ret 57 | 58 | class Group(object): 59 | def __init__(self, name): 60 | self.name = name 61 | 62 | def generator(self): 63 | return None 64 | 65 | def identity(self): 66 | return 0 67 | 68 | def order(self): 69 | return 0 70 | 71 | def serialize(self, element): 72 | return None 73 | 74 | def deserialize(self, encoded): 75 | return None 76 | 77 | def serialize_scalar(self, scalar): 78 | pass 79 | 80 | def element_byte_length(self): 81 | pass 82 | 83 | def scalar_byte_length(self): 84 | pass 85 | 86 | def hash_to_group(self, x): 87 | return None 88 | 89 | def hash_to_scalar(self, x): 90 | return None 91 | 92 | def random_scalar(self): 93 | return random.randint(1, self.order() - 1) 94 | 95 | def key_gen(self): 96 | skS = ZZ(self.random_scalar()) 97 | pkS = self.generator() * skS 98 | return skS, pkS 99 | 100 | def __str__(self): 101 | return self.name 102 | 103 | class GroupNISTCurve(Group): 104 | def __init__(self, name, suite, F, A, B, p, order, gx, gy, L, H, expander, k): 105 | Group.__init__(self, name) 106 | self.F = F 107 | EC = EllipticCurve(F, [F(A), F(B)]) 108 | self.curve = EC 109 | self.gx = gx 110 | self.gy = gy 111 | self.p = p 112 | self.a = A 113 | self.b = B 114 | self.group_order = order 115 | self.h2c_suite = suite 116 | self.G = EC(F(gx), F(gy)) 117 | self.m = F.degree() 118 | self.L = L 119 | self.k = k 120 | self.H = H 121 | self.expander = expander 122 | self.field_bytes_length = int(ceil(len(self.p.bits()) / 8)) 123 | 124 | def generator(self): 125 | return self.G 126 | 127 | def order(self): 128 | return self.group_order 129 | 130 | def identity(self): 131 | return self.curve(0) 132 | 133 | def serialize(self, element): 134 | x, y = element[0], element[1] 135 | sgn = sgn0(y) 136 | byte = 2 if sgn == 0 else 3 137 | return I2OSP(byte, 1) + I2OSP(x, self.field_bytes_length) 138 | 139 | # this is using point compression 140 | def deserialize(self, encoded): 141 | # 0x02 | 0x03 || x 142 | pve = encoded[0] == 0x02 143 | nve = encoded[0] == 0x03 144 | assert(pve or nve) 145 | assert(len(encoded) % 2 != 0) 146 | element_length = (len(encoded) - 1) / 2 147 | x = OS2IP(encoded[1:]) 148 | y2 = x^3 + self.a*x + self.b 149 | y = y2.sqrt() 150 | parity = 0 if pve else 1 151 | if sgn0(y) != parity: 152 | y = -y 153 | return self.curve(self.F(x), self.F(y)) 154 | 155 | def serialize_scalar(self, scalar): 156 | return I2OSP(scalar % self.order(), self.scalar_byte_length()) 157 | 158 | def element_byte_length(self): 159 | return int(1 + self.field_bytes_length) 160 | 161 | def scalar_byte_length(self): 162 | return int(self.field_bytes_length) 163 | 164 | def hash_to_group(self, msg, dst): 165 | self.h2c_suite.expand._dst = dst 166 | return self.h2c_suite(msg) 167 | 168 | def hash_to_scalar(self, msg, dst): 169 | expander = self.expander(dst, self.H, self.k) 170 | return hash_to_field(msg, 1, self.order(), self.m, self.L, expander)[0][0] 171 | 172 | class GroupP256(GroupNISTCurve): 173 | def __init__(self): 174 | # See FIPS 186-3, section D.2.3 175 | gx = 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296 176 | gy = 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5 177 | 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) 178 | 179 | class GroupP384(GroupNISTCurve): 180 | def __init__(self): 181 | # See FIPS 186-3, section D.2.4 182 | gx = 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7 183 | gy = 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f 184 | 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) 185 | 186 | class GroupP521(GroupNISTCurve): 187 | def __init__(self): 188 | # See FIPS 186-3, section D.2.5 189 | gx = 0xc6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66 190 | gy = 0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650 191 | 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) 192 | 193 | class GroupRistretto255(Group): 194 | def __init__(self): 195 | Group.__init__(self, "ristretto255") 196 | self.k = 128 197 | self.L = 48 198 | self.field_bytes_length = 32 199 | 200 | def generator(self): 201 | return Ed25519Point().base() 202 | 203 | def order(self): 204 | return Ed25519Point().order 205 | 206 | def identity(self): 207 | return Ed25519Point().identity() 208 | 209 | def serialize(self, element): 210 | return element.encode() 211 | 212 | def deserialize(self, encoded): 213 | return Ed25519Point().decode(encoded) 214 | 215 | def serialize_scalar(self, scalar): 216 | return I2OSP(scalar % self.order(), self.scalar_byte_length())[::-1] 217 | 218 | def element_byte_length(self): 219 | return self.field_bytes_length 220 | 221 | def scalar_byte_length(self): 222 | return self.field_bytes_length 223 | 224 | def hash_to_group(self, msg, dst): 225 | return Ed25519Point().hash_to_group(msg, dst) 226 | 227 | def hash_to_scalar(self, msg, dst): 228 | uniform_bytes = expand_message_xmd(msg, dst, 64, hashlib.sha512, self.k) 229 | return OS2IP_le(uniform_bytes) % self.order() 230 | 231 | class GroupDecaf448(Group): 232 | def __init__(self): 233 | Group.__init__(self, "decaf448") 234 | self.k = 224 235 | self.L = 84 236 | self.field_bytes_length = 56 237 | 238 | def generator(self): 239 | return Ed448GoldilocksPoint().base() 240 | 241 | def order(self): 242 | return Ed448GoldilocksPoint().order 243 | 244 | def identity(self): 245 | return Ed448GoldilocksPoint().identity() 246 | 247 | def serialize(self, element): 248 | return element.encode() 249 | 250 | def deserialize(self, encoded): 251 | return Ed448GoldilocksPoint().decode(encoded) 252 | 253 | def serialize_scalar(self, scalar): 254 | return I2OSP(scalar % self.order(), self.scalar_byte_length())[::-1] 255 | 256 | def element_byte_length(self): 257 | return self.field_bytes_length 258 | 259 | def scalar_byte_length(self): 260 | return self.field_bytes_length 261 | 262 | def hash_to_group(self, msg, dst): 263 | return Ed448GoldilocksPoint().hash_to_group(msg, dst) 264 | 265 | def hash_to_scalar(self, msg, dst): 266 | uniform_bytes = expand_message_xof(msg, dst, int(64), hashlib.shake_256, self.k) 267 | return OS2IP_le(uniform_bytes) % self.order() 268 | -------------------------------------------------------------------------------- /poc/test_oprf.sage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sage 2 | # vim: syntax=python 3 | 4 | import sys 5 | import json 6 | import binascii 7 | 8 | try: 9 | from sagelib.oprf \ 10 | import DeriveKeyPair, \ 11 | SetupOPRFServer, SetupOPRFClient, MODE_OPRF, \ 12 | SetupVOPRFServer, SetupVOPRFClient, MODE_VOPRF, \ 13 | SetupPOPRFServer, SetupPOPRFClient, MODE_POPRF, \ 14 | oprf_ciphersuites, _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, suite, mode, info): 44 | self.inputs = [b'\x00', b'\x5A'*17] 45 | self.suite = suite 46 | self.mode = mode 47 | self.info = info 48 | self.key_info = _as_bytes("test key") 49 | 50 | self.seed = b'\xA3' * suite.group.scalar_byte_length() 51 | skS, pkS = DeriveKeyPair(self.mode, self.suite, self.seed, self.key_info) 52 | if mode == MODE_OPRF: 53 | self.server = SetupOPRFServer(suite, skS) 54 | self.client = SetupOPRFClient(suite) 55 | elif mode == MODE_VOPRF: 56 | self.server = SetupVOPRFServer(suite, skS, pkS) 57 | self.client = SetupVOPRFClient(suite, pkS) 58 | elif mode == MODE_POPRF: 59 | self.server = SetupPOPRFServer(suite, skS, pkS) 60 | self.client = SetupPOPRFClient(suite, pkS) 61 | else: 62 | raise Exception("bad mode") 63 | 64 | def run(self): 65 | group = self.client.suite.group 66 | client = self.client 67 | server = self.server 68 | 69 | def create_test_vector_for_input(x, info): 70 | if self.mode == MODE_POPRF: 71 | blind, blinded_element, tweaked_key = client.blind(x, info) 72 | evaluated_element, proof, proof_randomness = server.evaluate(blinded_element, info) 73 | output = client.finalize(x, blind, evaluated_element, blinded_element, proof, info, tweaked_key) 74 | else: 75 | blind, blinded_element = client.blind(x) 76 | evaluated_element, proof, proof_randomness = server.evaluate(blinded_element, info) 77 | output = client.finalize(x, blind, evaluated_element, blinded_element, proof, info) 78 | 79 | assert(server.verify_finalize(x, output, info)) 80 | 81 | vector = {} 82 | vector["Blind"] = to_hex(group.serialize_scalar(blind)) 83 | vector["BlindedElement"] = to_hex(group.serialize(blinded_element)) 84 | vector["EvaluationElement"] = to_hex(group.serialize(evaluated_element)) 85 | 86 | if self.mode == MODE_VOPRF or self.mode == MODE_POPRF: 87 | vector["Proof"] = { 88 | "proof": to_hex(group.serialize_scalar(proof[0]) + group.serialize_scalar(proof[1])), 89 | "r": to_hex(group.serialize_scalar(proof_randomness)), 90 | } 91 | 92 | vector["Input"] = to_hex(x) 93 | if self.mode == MODE_POPRF: 94 | vector["Info"] = to_hex(info) 95 | vector["Output"] = to_hex(output) 96 | vector["Batch"] = int(1) 97 | 98 | return vector 99 | 100 | def create_batched_test_vector_for_inputs(xs, info): 101 | blinds = [] 102 | blinded_elements = [] 103 | tweaked_key = None 104 | for x in xs: 105 | if self.mode == MODE_POPRF: 106 | blind, blinded_element, tweaked_key = client.blind(x, info) 107 | blinds.append(blind) 108 | blinded_elements.append(blinded_element) 109 | else: 110 | blind, blinded_element = client.blind(x) 111 | blinds.append(blind) 112 | blinded_elements.append(blinded_element) 113 | 114 | evaluated_elements, proof, proof_randomness = server.evaluate_batch(blinded_elements, info) 115 | 116 | if self.mode == MODE_POPRF: 117 | outputs = client.finalize_batch(xs, blinds, evaluated_elements, blinded_elements, proof, info, tweaked_key) 118 | else: 119 | outputs = client.finalize_batch(xs, blinds, evaluated_elements, blinded_elements, proof, info) 120 | 121 | for i, output in enumerate(outputs): 122 | assert(server.verify_finalize(xs[i], output, info)) 123 | 124 | vector = {} 125 | vector["Blind"] = ",".join([to_hex(group.serialize_scalar(blind)) for blind in blinds]) 126 | vector["BlindedElement"] = to_hex(list(map(lambda e : group.serialize(e), blinded_elements))) 127 | vector["EvaluationElement"] = to_hex(list(map(lambda e : group.serialize(e), evaluated_elements))) 128 | 129 | if self.mode == MODE_VOPRF or self.mode == MODE_POPRF: 130 | vector["Proof"] = { 131 | "proof": to_hex(group.serialize_scalar(proof[0]) + group.serialize_scalar(proof[1])), 132 | "r": to_hex(group.serialize_scalar(proof_randomness)), 133 | } 134 | 135 | vector["Input"] = to_hex(xs) 136 | if self.mode == MODE_POPRF: 137 | vector["Info"] = to_hex(info) 138 | vector["Output"] = to_hex(outputs) 139 | vector["Batch"] = int(len(xs)) 140 | 141 | return vector 142 | 143 | vectors = [create_test_vector_for_input(x, self.info) for x in self.inputs] 144 | if self.mode == MODE_VOPRF or self.mode == MODE_POPRF: 145 | vectors.append(create_batched_test_vector_for_inputs(self.inputs, self.info)) 146 | 147 | vecSuite = {} 148 | vecSuite["suiteName"] = self.suite.name 149 | vecSuite["suiteID"] = int(self.suite.identifier) 150 | vecSuite["mode"] = int(self.mode) 151 | vecSuite["hash"] = self.suite.H().name.upper() 152 | vecSuite["keyInfo"] = to_hex(self.key_info) 153 | vecSuite["seed"] = to_hex(self.seed) 154 | vecSuite["skSm"] = to_hex(group.serialize_scalar(server.skS)) 155 | vecSuite["groupDST"] = to_hex(client.group_domain_separation_tag()) 156 | if self.mode == MODE_VOPRF or self.mode == MODE_POPRF: 157 | vecSuite["pkSm"] = to_hex(group.serialize(server.pkS)) 158 | vecSuite["vectors"] = vectors 159 | 160 | return vecSuite 161 | 162 | def wrap_write(fh, arg, *args): 163 | line_length = 68 164 | string = " ".join( [arg] + list(args)) 165 | for hunk in (string[0+i:line_length+i] for i in range(0, len(string), line_length)): 166 | if hunk and len(hunk.strip()) > 0: 167 | fh.write(hunk + "\n") 168 | 169 | def write_blob(fh, name, blob): 170 | wrap_write(fh, name + ' = ' + to_hex(blob)) 171 | 172 | def write_value(fh, name, value): 173 | wrap_write(fh, name + ' = ' + value) 174 | 175 | def write_oprf_vector(fh, vector): 176 | fh.write("~~~\n") 177 | write_value(fh, "Seed", vector["seed"]) 178 | write_value(fh, "KeyInfo", vector["keyInfo"]) 179 | write_value(fh, "skSm", vector["skSm"]) 180 | fh.write("~~~\n") 181 | fh.write("\n") 182 | for i, v in enumerate(vector["vectors"]): 183 | fh.write("#### Test Vector " + str(i+1) + ", Batch Size " + str(v["Batch"]) + "\n") 184 | fh.write("\n") 185 | fh.write("~~~\n") 186 | write_value(fh, "Input", v["Input"]) 187 | write_value(fh, "Blind", v["Blind"]) 188 | write_value(fh, "BlindedElement", v["BlindedElement"]) 189 | write_value(fh, "EvaluationElement", v["EvaluationElement"]) 190 | write_value(fh, "Output", v["Output"]) 191 | fh.write("~~~\n") 192 | fh.write("\n") 193 | 194 | def write_voprf_vector(fh, vector): 195 | fh.write("~~~\n") 196 | write_value(fh, "Seed", vector["seed"]) 197 | write_value(fh, "KeyInfo", vector["keyInfo"]) 198 | write_value(fh, "skSm", vector["skSm"]) 199 | write_value(fh, "pkSm", vector["pkSm"]) 200 | fh.write("~~~\n") 201 | fh.write("\n") 202 | for i, v in enumerate(vector["vectors"]): 203 | fh.write("#### Test Vector " + str(i+1) + ", Batch Size " + str(v["Batch"]) + "\n") 204 | fh.write("\n") 205 | fh.write("~~~\n") 206 | write_value(fh, "Input", v["Input"]) 207 | write_value(fh, "Blind", v["Blind"]) 208 | write_value(fh, "BlindedElement", v["BlindedElement"]) 209 | write_value(fh, "EvaluationElement", v["EvaluationElement"]) 210 | write_value(fh, "Proof", v["Proof"]["proof"]) 211 | write_value(fh, "ProofRandomScalar", v["Proof"]["r"]) 212 | write_value(fh, "Output", v["Output"]) 213 | fh.write("~~~\n") 214 | fh.write("\n") 215 | 216 | def write_poprf_vector(fh, vector): 217 | fh.write("~~~\n") 218 | write_value(fh, "Seed", vector["seed"]) 219 | write_value(fh, "KeyInfo", vector["keyInfo"]) 220 | write_value(fh, "skSm", vector["skSm"]) 221 | write_value(fh, "pkSm", vector["pkSm"]) 222 | fh.write("~~~\n") 223 | fh.write("\n") 224 | for i, v in enumerate(vector["vectors"]): 225 | fh.write("#### Test Vector " + str(i+1) + ", Batch Size " + str(v["Batch"]) + "\n") 226 | fh.write("\n") 227 | fh.write("~~~\n") 228 | write_value(fh, "Input", v["Input"]) 229 | write_value(fh, "Info", v["Info"]) 230 | write_value(fh, "Blind", v["Blind"]) 231 | write_value(fh, "BlindedElement", v["BlindedElement"]) 232 | write_value(fh, "EvaluationElement", v["EvaluationElement"]) 233 | write_value(fh, "Proof", v["Proof"]["proof"]) 234 | write_value(fh, "ProofRandomScalar", v["Proof"]["r"]) 235 | write_value(fh, "Output", v["Output"]) 236 | fh.write("~~~\n") 237 | fh.write("\n") 238 | 239 | mode_map = { 240 | MODE_OPRF: "OPRF", 241 | MODE_VOPRF: "VOPRF", 242 | MODE_POPRF: "POPRF", 243 | } 244 | 245 | def main(path="vectors"): 246 | allVectors = {} 247 | for suite_id in test_suites: 248 | suite = oprf_ciphersuites[suite_id] 249 | suiteVectors = {} 250 | for mode in [MODE_OPRF, MODE_VOPRF, MODE_POPRF]: 251 | protocol = Protocol(suite, mode, _as_bytes("test info")) 252 | suiteVectors[str(mode)] = protocol.run() 253 | allVectors[suite.name] = suiteVectors 254 | 255 | flatVectors = [] 256 | for suite in allVectors: 257 | for mode in allVectors[suite]: 258 | flatVectors.append(allVectors[suite][mode]) 259 | with open(path + "/allVectors.json", 'wt') as f: 260 | json.dump(flatVectors, f, sort_keys=True, indent=2) 261 | f.write("\n") 262 | 263 | with open(path + "/allVectors.txt", 'wt') as f: 264 | for suite in allVectors: 265 | f.write("## " + suite + "\n") 266 | f.write("\n") 267 | for mode in allVectors[suite]: 268 | if mode == str(MODE_OPRF): 269 | f.write("### OPRF Mode\n") 270 | f.write("\n") 271 | write_oprf_vector(f, allVectors[suite][mode]) 272 | elif mode == str(MODE_VOPRF): 273 | f.write("### VOPRF Mode\n") 274 | f.write("\n") 275 | write_voprf_vector(f, allVectors[suite][mode]) 276 | else: 277 | f.write("### POPRF Mode\n") 278 | f.write("\n") 279 | write_poprf_vector(f, allVectors[suite][mode]) 280 | 281 | if __name__ == "__main__": 282 | main() 283 | -------------------------------------------------------------------------------- /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/oprf.sage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sage 2 | # vim: syntax=python 3 | 4 | import sys 5 | import json 6 | import hashlib 7 | import binascii 8 | 9 | from collections import namedtuple 10 | 11 | from hash_to_field import I2OSP 12 | 13 | try: 14 | from sagelib.groups import GroupP256, GroupP384, GroupP521, GroupRistretto255, GroupDecaf448 15 | from sagelib.suite_p256 import p256_sswu_ro, p256_order, p256_p, p256_F, p256_A, p256_B 16 | from sagelib.suite_p384 import p384_sswu_ro, p384_order, p384_p, p384_F, p384_A, p384_B 17 | from sagelib.suite_p521 import p521_sswu_ro, p521_order, p521_p, p521_F, p521_A, p521_B 18 | from sagelib.common import sgn0 19 | from sagelib.ristretto_decaf import Ed25519Point, Ed448GoldilocksPoint 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 | if sys.version_info[0] == 3: 24 | xrange = range 25 | _as_bytes = lambda x: x if isinstance(x, bytes) else bytes(x, "utf-8") 26 | _strxor = lambda str1, str2: bytes( s1 ^ s2 for (s1, s2) in zip(str1, str2) ) 27 | else: 28 | _as_bytes = lambda x: x 29 | _strxor = lambda str1, str2: ''.join( chr(ord(s1) ^ ord(s2)) for (s1, s2) in zip(str1, str2) ) 30 | 31 | class Context(object): 32 | def __init__(self, version, mode, suite): 33 | self.mode = mode 34 | self.suite = suite 35 | self.context_string = _as_bytes(version) + I2OSP(self.mode, 1) + I2OSP(self.suite.identifier, 2) 36 | 37 | def group_domain_separation_tag(self): 38 | return _as_bytes("HashToGroup-") + self.context_string 39 | 40 | def scalar_domain_separation_tag(self): 41 | return _as_bytes("HashToScalar-") + self.context_string 42 | 43 | def domain_separation_tag(self, prefix): 44 | return _as_bytes(prefix) + self.context_string 45 | 46 | class Evaluation(object): 47 | def __init__(self, evaluated_element, proof): 48 | self.evaluated_element = evaluated_element 49 | self.proof = proof 50 | 51 | class OPRFClientContext(Context): 52 | def __init__(self, version, mode, suite): 53 | Context.__init__(self, version, mode, suite) 54 | 55 | def identifier(self): 56 | return self.identifier 57 | 58 | def blind(self, x): 59 | blind = ZZ(self.suite.group.random_scalar()) 60 | P = self.suite.group.hash_to_group(x, self.group_domain_separation_tag()) 61 | if P == self.suite.group.identity(): 62 | raise Exception("InvalidInputError") 63 | blinded_element = blind * P 64 | return blind, blinded_element 65 | 66 | def unblind(self, blind, evaluated_element, blinded_element, proof): 67 | blind_inv = inverse_mod(blind, self.suite.group.order()) 68 | N = blind_inv * evaluated_element 69 | unblinded_element = self.suite.group.serialize(N) 70 | return unblinded_element 71 | 72 | def finalize(self, x, blind, evaluated_element, blinded_element, proof, info): 73 | unblinded_element = self.unblind(blind, evaluated_element, blinded_element, proof) 74 | finalize_input = I2OSP(len(x), 2) + x \ 75 | + I2OSP(len(unblinded_element), 2) + unblinded_element \ 76 | + _as_bytes("Finalize") 77 | 78 | return self.suite.hash(finalize_input) 79 | 80 | class OPRFServerContext(Context): 81 | def __init__(self, version, mode, suite, skS, pkS): 82 | Context.__init__(self, version, mode, suite) 83 | self.skS = skS 84 | self.pkS = pkS 85 | 86 | def evaluate(self, blinded_element, info): 87 | evaluated_element = self.skS * blinded_element 88 | return evaluated_element, None, None 89 | 90 | def verify_finalize(self, x, expected_digest, info): 91 | P = self.suite.group.hash_to_group(x, self.group_domain_separation_tag()) 92 | evaluated_element, _, _ = self.evaluate(P, info) # Ignore proof output 93 | issued_element = self.suite.group.serialize(evaluated_element) 94 | 95 | finalize_input = I2OSP(len(x), 2) + x \ 96 | + I2OSP(len(issued_element), 2) + issued_element \ 97 | + _as_bytes("Finalize") 98 | 99 | digest = self.suite.hash(finalize_input) 100 | 101 | return (digest == expected_digest) 102 | 103 | class Verifiable(object): 104 | def compute_composites_inner(self, k, B, Cs, Ds): 105 | assert(len(Cs) == len(Ds)) 106 | 107 | seedDST = _as_bytes("Seed-") + self.context_string 108 | Bm = self.suite.group.serialize(B) 109 | 110 | h1_input = I2OSP(len(Bm), 2) + Bm \ 111 | + I2OSP(len(seedDST), 2) + seedDST 112 | seed = self.suite.hash(h1_input) 113 | 114 | M = self.suite.group.identity() 115 | Z = self.suite.group.identity() 116 | 117 | for i in range(len(Cs)): 118 | Ci = self.suite.group.serialize(Cs[i]) 119 | Di = self.suite.group.serialize(Ds[i]) 120 | h2_input = I2OSP(len(seed), 2) + seed \ 121 | + I2OSP(i, 2) \ 122 | + I2OSP(len(Ci), 2) + Ci \ 123 | + I2OSP(len(Di), 2) + Di \ 124 | + _as_bytes("Composite") 125 | 126 | di = self.suite.group.hash_to_scalar(h2_input, self.scalar_domain_separation_tag()) 127 | M = (di * Cs[i]) + M 128 | 129 | if k == None: 130 | Z = (di * Ds[i]) + Z 131 | 132 | if k != None: 133 | Z = k * M 134 | 135 | return [M, Z] 136 | 137 | def compute_composites_fast(self, k, B, Cs, Ds): 138 | return self.compute_composites_inner(k, B, Cs, Ds) 139 | 140 | def compute_composites(self, B, Cs, Ds): 141 | return self.compute_composites_inner(None, B, Cs, Ds) 142 | 143 | class VOPRFClientContext(OPRFClientContext,Verifiable): 144 | def __init__(self, version, mode, suite, pkS): 145 | OPRFClientContext.__init__(self, version, mode, suite) 146 | self.pkS = pkS 147 | 148 | def verify_proof(self, A, B, Cs, Ds, proof): 149 | a = self.compute_composites(B, Cs, Ds) 150 | 151 | M = a[0] 152 | Z = a[1] 153 | t2 = (proof[1] * A) + (proof[0] * B) 154 | t3 = (proof[1] * M) + (proof[0] * Z) 155 | 156 | Bm = self.suite.group.serialize(B) 157 | a0 = self.suite.group.serialize(M) 158 | a1 = self.suite.group.serialize(Z) 159 | a2 = self.suite.group.serialize(t2) 160 | a3 = self.suite.group.serialize(t3) 161 | 162 | h2s_input = I2OSP(len(Bm), 2) + Bm \ 163 | + I2OSP(len(a0), 2) + a0 \ 164 | + I2OSP(len(a1), 2) + a1 \ 165 | + I2OSP(len(a2), 2) + a2 \ 166 | + I2OSP(len(a3), 2) + a3 \ 167 | + _as_bytes("Challenge") 168 | 169 | c = self.suite.group.hash_to_scalar(h2s_input, self.scalar_domain_separation_tag()) 170 | 171 | assert(c == proof[0]) 172 | return c == proof[0] 173 | 174 | def unblind(self, blind, evaluated_element, blinded_element, proof): 175 | G = self.suite.group.generator() 176 | if not self.verify_proof(G, self.pkS, [blinded_element], [evaluated_element], proof): 177 | raise Exception("VerifyError") 178 | 179 | blind_inv = inverse_mod(blind, self.suite.group.order()) 180 | N = blind_inv * evaluated_element 181 | unblinded_element = self.suite.group.serialize(N) 182 | return unblinded_element 183 | 184 | def unblind_batch(self, blinds, evaluated_elements, blinded_elements, proof): 185 | assert(len(blinds) == len(evaluated_elements)) 186 | assert(len(evaluated_elements) == len(blinded_elements)) 187 | 188 | G = self.suite.group.generator() 189 | if not self.verify_proof(G, self.pkS, blinded_elements, evaluated_elements, proof): 190 | raise Exception("VerifyError") 191 | 192 | unblinded_elements = [] 193 | for i, evaluated_element in enumerate(evaluated_elements): 194 | blind_inv = inverse_mod(blinds[i], self.suite.group.order()) 195 | N = blind_inv * evaluated_element 196 | unblinded_element = self.suite.group.serialize(N) 197 | unblinded_elements.append(unblinded_element) 198 | 199 | return unblinded_elements 200 | 201 | def finalize(self, x, blind, evaluated_element, blinded_element, proof, info): 202 | unblinded_element = self.unblind(blind, evaluated_element, blinded_element, proof) 203 | finalize_input = I2OSP(len(x), 2) + x \ 204 | + I2OSP(len(unblinded_element), 2) + unblinded_element \ 205 | + _as_bytes("Finalize") 206 | 207 | return self.suite.hash(finalize_input) 208 | 209 | def finalize_batch(self, xs, blinds, evaluated_elements, blinded_elements, proof, info): 210 | assert(len(blinds) == len(evaluated_elements)) 211 | assert(len(evaluated_elements) == len(blinded_elements)) 212 | 213 | unblinded_elements = self.unblind_batch(blinds, evaluated_elements, blinded_elements, proof) 214 | 215 | outputs = [] 216 | for i, unblinded_element in enumerate(unblinded_elements): 217 | finalize_input = I2OSP(len(xs[i]), 2) + xs[i] \ 218 | + I2OSP(len(unblinded_element), 2) + unblinded_element \ 219 | + _as_bytes("Finalize") 220 | 221 | digest = self.suite.hash(finalize_input) 222 | outputs.append(digest) 223 | 224 | return outputs 225 | 226 | class VOPRFServerContext(OPRFServerContext,Verifiable): 227 | def __init__(self, version, mode, suite, skS, pkS): 228 | OPRFServerContext.__init__(self, version, mode, suite, skS, pkS) 229 | 230 | def generate_proof(self, k, A, B, Cs, Ds): 231 | a = self.compute_composites_fast(k, B, Cs, Ds) 232 | 233 | r = ZZ(self.suite.group.random_scalar()) 234 | M = a[0] 235 | Z = a[1] 236 | t2 = r * A 237 | t3 = r * M 238 | 239 | Bm = self.suite.group.serialize(B) 240 | a0 = self.suite.group.serialize(M) 241 | a1 = self.suite.group.serialize(Z) 242 | a2 = self.suite.group.serialize(t2) 243 | a3 = self.suite.group.serialize(t3) 244 | 245 | h2s_input = I2OSP(len(Bm), 2) + Bm \ 246 | + I2OSP(len(a0), 2) + a0 \ 247 | + I2OSP(len(a1), 2) + a1 \ 248 | + I2OSP(len(a2), 2) + a2 \ 249 | + I2OSP(len(a3), 2) + a3 \ 250 | + _as_bytes("Challenge") 251 | 252 | c = self.suite.group.hash_to_scalar(h2s_input, self.scalar_domain_separation_tag()) 253 | s = (r - c * k) % self.suite.group.order() 254 | 255 | return [c, s], r 256 | 257 | def evaluate(self, blinded_element, info): 258 | evaluated_element = self.skS * blinded_element 259 | proof, r = self.generate_proof(self.skS, self.suite.group.generator(), self.pkS, [blinded_element], [evaluated_element]) 260 | return evaluated_element, proof, r 261 | 262 | def evaluate_batch(self, blinded_elements, info): 263 | evaluated_elements = [] 264 | for blinded_element in blinded_elements: 265 | evaluated_element = self.skS * blinded_element 266 | evaluated_elements.append(evaluated_element) 267 | 268 | proof, r = self.generate_proof(self.skS, self.suite.group.generator(), self.pkS, blinded_elements, evaluated_elements) 269 | return evaluated_elements, proof, r 270 | 271 | class POPRFClientContext(VOPRFClientContext): 272 | def __init__(self, version, mode, suite, pkS): 273 | VOPRFClientContext.__init__(self, version, mode, suite, pkS) 274 | self.pkS = pkS 275 | 276 | def blind(self, x, info): 277 | context = _as_bytes("Info") + I2OSP(len(info), 2) + info 278 | t = self.suite.group.hash_to_scalar(context, self.scalar_domain_separation_tag()) 279 | G = self.suite.group.generator() 280 | tweaked_key = (G * t) + self.pkS 281 | if tweaked_key == self.suite.group.identity(): 282 | raise Exception("InvalidInputError") 283 | 284 | blind = ZZ(self.suite.group.random_scalar()) 285 | P = self.suite.group.hash_to_group(x, self.group_domain_separation_tag()) 286 | if P == self.suite.group.identity(): 287 | raise Exception("InvalidInputError") 288 | 289 | blinded_element = blind * P 290 | return blind, blinded_element, tweaked_key 291 | 292 | def unblind(self, blind, evaluated_element, blinded_element, proof, tweaked_key): 293 | G = self.suite.group.generator() 294 | if not self.verify_proof(G, tweaked_key, [evaluated_element], [blinded_element], proof): 295 | raise Exception("Proof verification failed") 296 | 297 | blind_inv = inverse_mod(blind, self.suite.group.order()) 298 | N = blind_inv * evaluated_element 299 | unblinded_element = self.suite.group.serialize(N) 300 | return unblinded_element 301 | 302 | def unblind_batch(self, blinds, evaluated_elements, blinded_elements, proof, tweaked_key): 303 | assert(len(blinds) == len(evaluated_elements)) 304 | assert(len(evaluated_elements) == len(blinded_elements)) 305 | 306 | G = self.suite.group.generator() 307 | if not self.verify_proof(G, tweaked_key, evaluated_elements, blinded_elements, proof): 308 | raise Exception("Proof verification failed") 309 | 310 | unblinded_elements = [] 311 | for i, evaluated_element in enumerate(evaluated_elements): 312 | blind_inv = inverse_mod(blinds[i], self.suite.group.order()) 313 | N = blind_inv * evaluated_element 314 | unblinded_element = self.suite.group.serialize(N) 315 | unblinded_elements.append(unblinded_element) 316 | 317 | return unblinded_elements 318 | 319 | def finalize(self, x, blind, evaluated_element, blinded_element, proof, info, tweaked_key): 320 | unblinded_element = self.unblind(blind, evaluated_element, blinded_element, proof, tweaked_key) 321 | finalize_input = I2OSP(len(x), 2) + x \ 322 | + I2OSP(len(info), 2) + info \ 323 | + I2OSP(len(unblinded_element), 2) + unblinded_element \ 324 | + _as_bytes("Finalize") 325 | 326 | return self.suite.hash(finalize_input) 327 | 328 | def finalize_batch(self, xs, blinds, evaluated_elements, blinded_elements, proof, info, tweaked_key): 329 | assert(len(blinds) == len(evaluated_elements)) 330 | assert(len(evaluated_elements) == len(blinded_elements)) 331 | 332 | unblinded_elements = self.unblind_batch(blinds, evaluated_elements, blinded_elements, proof, tweaked_key) 333 | 334 | outputs = [] 335 | for i, unblinded_element in enumerate(unblinded_elements): 336 | finalize_input = I2OSP(len(xs[i]), 2) + xs[i] \ 337 | + I2OSP(len(info), 2) + info \ 338 | + I2OSP(len(unblinded_element), 2) + unblinded_element \ 339 | + _as_bytes("Finalize") 340 | 341 | digest = self.suite.hash(finalize_input) 342 | outputs.append(digest) 343 | 344 | return outputs 345 | 346 | class POPRFServerContext(VOPRFServerContext): 347 | def __init__(self, version, mode, suite, skS, pkS): 348 | VOPRFServerContext.__init__(self, version, mode, suite, skS, pkS) 349 | 350 | def evaluate(self, blinded_element, info): 351 | context = _as_bytes("Info") + I2OSP(len(info), 2) + info 352 | t = self.suite.group.hash_to_scalar(context, self.scalar_domain_separation_tag()) 353 | k = self.skS + t 354 | if int(k) == 0: 355 | raise Exception("InverseError") 356 | k_inv = inverse_mod(k, self.suite.group.order()) 357 | evaluated_element = k_inv * blinded_element 358 | 359 | G = self.suite.group.generator() 360 | U = k * G 361 | proof, r = self.generate_proof(k, G, U, [evaluated_element], [blinded_element]) 362 | return evaluated_element, proof, r 363 | 364 | def evaluate_batch(self, blinded_elements, info): 365 | context = _as_bytes("Info") + I2OSP(len(info), 2) + info 366 | t = self.suite.group.hash_to_scalar(context, self.scalar_domain_separation_tag()) 367 | 368 | evaluated_elements = [] 369 | for blinded_element in blinded_elements: 370 | k = self.skS + t 371 | if int(k) == 0: 372 | raise Exception("InverseError") 373 | k_inv = inverse_mod(k, self.suite.group.order()) 374 | evaluated_element = k_inv * blinded_element 375 | evaluated_elements.append(evaluated_element) 376 | 377 | G = self.suite.group.generator() 378 | U = k * G 379 | proof, r = self.generate_proof(k, G, U, evaluated_elements, blinded_elements) 380 | return evaluated_elements, proof, r 381 | 382 | def verify_finalize(self, x, expected_digest, info): 383 | P = self.suite.group.hash_to_group(x, self.group_domain_separation_tag()) 384 | evaluated_element, _, _ = self.evaluate(P, info) # Ignore proof output 385 | issued_element = self.suite.group.serialize(evaluated_element) 386 | 387 | finalize_input = I2OSP(len(x), 2) + x \ 388 | + I2OSP(len(info), 2) + info \ 389 | + I2OSP(len(issued_element), 2) + issued_element \ 390 | + _as_bytes("Finalize") 391 | 392 | digest = self.suite.hash(finalize_input) 393 | 394 | return (digest == expected_digest) 395 | 396 | MODE_OPRF = 0x00 397 | MODE_VOPRF = 0x01 398 | MODE_POPRF = 0x02 399 | 400 | VERSION = "VOPRF09-" 401 | 402 | def DeriveKeyPair(mode, suite, seed, info): 403 | ctx = Context(VERSION, mode, suite) 404 | deriveInput = seed + I2OSP(len(info), 2) + info 405 | counter = 0 406 | skS = ZZ(0) 407 | while ZZ(skS) == ZZ(0): 408 | if counter > 255: 409 | raise Exception("DeriveKeyPairError") 410 | hashInput = deriveInput + I2OSP(counter, 1) 411 | skS = suite.group.hash_to_scalar(hashInput, ctx.domain_separation_tag("DeriveKeyPair")) 412 | counter = counter + 1 413 | pkS = skS * suite.group.generator() 414 | return skS, pkS 415 | 416 | def SetupOPRFServer(suite, skS): 417 | return OPRFServerContext(VERSION, MODE_OPRF, suite, skS, None) 418 | 419 | def SetupOPRFClient(suite): 420 | return OPRFClientContext(VERSION, MODE_OPRF, suite) 421 | 422 | def SetupVOPRFServer(suite, skS, pkS): 423 | return VOPRFServerContext(VERSION, MODE_VOPRF, suite, skS, pkS) 424 | 425 | def SetupVOPRFClient(suite, pkS): 426 | return VOPRFClientContext(VERSION, MODE_VOPRF, suite, pkS) 427 | 428 | def SetupPOPRFServer(suite, skS, pkS): 429 | return POPRFServerContext(VERSION, MODE_POPRF, suite, skS, pkS) 430 | 431 | def SetupPOPRFClient(suite, pkS): 432 | return POPRFClientContext(VERSION, MODE_POPRF, suite, pkS) 433 | 434 | Ciphersuite = namedtuple("Ciphersuite", ["name", "identifier", "group", "H", "hash"]) 435 | 436 | ciphersuite_ristretto255_sha512 = 0x0001 437 | ciphersuite_decaf448_shake256 = 0x0002 438 | ciphersuite_p256_sha256 = 0x0003 439 | ciphersuite_p384_sha384 = 0x0004 440 | ciphersuite_p521_sha512 = 0x0005 441 | 442 | oprf_ciphersuites = { 443 | ciphersuite_ristretto255_sha512: Ciphersuite("OPRF(ristretto255, SHA-512)", ciphersuite_ristretto255_sha512, GroupRistretto255(), hashlib.sha512, lambda x : hashlib.sha512(x).digest()), 444 | ciphersuite_decaf448_shake256: Ciphersuite("OPRF(decaf448, SHAKE-256)", ciphersuite_decaf448_shake256, GroupDecaf448(), hashlib.shake_256, lambda x : hashlib.shake_256(x).digest(int(64))), 445 | ciphersuite_p256_sha256: Ciphersuite("OPRF(P-256, SHA-256)", ciphersuite_p256_sha256, GroupP256(), hashlib.sha256, lambda x : hashlib.sha256(x).digest()), 446 | ciphersuite_p384_sha384: Ciphersuite("OPRF(P-384, SHA-384)", ciphersuite_p384_sha384, GroupP384(), hashlib.sha384, lambda x : hashlib.sha384(x).digest()), 447 | ciphersuite_p521_sha512: Ciphersuite("OPRF(P-521, SHA-512)", ciphersuite_p521_sha512, GroupP521(), hashlib.sha512, lambda x : hashlib.sha512(x).digest()), 448 | } 449 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /poc/vectors/allVectors.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "groupDST": "48617368546f47726f75702d564f50524630392d000001", 4 | "hash": "SHA512", 5 | "keyInfo": "74657374206b6579", 6 | "mode": 0, 7 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 8 | "skSm": "8ce0798c296bdeb665d52312d81a596dbb4ef0d25adb10c7f2b58c72dd2e540a", 9 | "suiteID": 1, 10 | "suiteName": "OPRF(ristretto255, SHA-512)", 11 | "vectors": [ 12 | { 13 | "Batch": 1, 14 | "Blind": "c604c785ada70d77a5256ae21767de8c3304115237d262134f5e46e512cf8e03", 15 | "BlindedElement": "8453ce4f98478a73faf24dd0c2e81d9a5e399171d2687cc258b9e593623bde4d", 16 | "EvaluationElement": "22bcfc0930ecddf4ada3f0cb421c8d6669576fc4fbbe24e18c94d0f36e767466", 17 | "Input": "00", 18 | "Output": "2765a7f9fa7e9d5440bbf1262dc1041277bed5f27fd27ee89662192a408508bb8711559d5a5390560065b83b946ed7b433d0c1df09bd23871804ae78e4a4d215" 19 | }, 20 | { 21 | "Batch": 1, 22 | "Blind": "5ed895206bfc53316d307b23e46ecc6623afb3086da74189a416012be037e50b", 23 | "BlindedElement": "86ef8baa01dd6cc34a067d2fc56cde51498a54cb0c30f63f08353d912164d711", 24 | "EvaluationElement": "a27d5e498927ca96e493373a04e263115c31b918411df0ced382db4e66388766", 25 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 26 | "Output": "3d6c9ec7dd6f51b987b46b79128d98323accd7c1561faa50d287c5285ecda1e660f3ee2929aebd431a7a7d511767cbd1054735a6e19aee1b9423a1e6f479535e" 27 | } 28 | ] 29 | }, 30 | { 31 | "groupDST": "48617368546f47726f75702d564f50524630392d010001", 32 | "hash": "SHA512", 33 | "keyInfo": "74657374206b6579", 34 | "mode": 1, 35 | "pkSm": "46919d396c12fbb7a02f4ce8f51a9941ddc1c4335682e1b03b0ca5b3524c6619", 36 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 37 | "skSm": "24d1aca670a768b99e888c19f9ee3252d17e32689507b2b9b803ae23b1997f07", 38 | "suiteID": 1, 39 | "suiteName": "OPRF(ristretto255, SHA-512)", 40 | "vectors": [ 41 | { 42 | "Batch": 1, 43 | "Blind": "ed8366feb6b1d05d1f46acb727061e43aadfafe9c10e5a64e7518d63e3263503", 44 | "BlindedElement": "444550ea064013c569fe63567eb93e7a9496902a573ea1e665476fd39d5edc40", 45 | "EvaluationElement": "7af7a45e4f1e0c6d410d41704e16d980ebff051fd0975fcecd17f79a6b57a473", 46 | "Input": "00", 47 | "Output": "453a358544b4e92bbc4625d08ffdde64c0dbc4f9b1501d548e3a6d8094ba70a993c13a6e65a46880bbd65272ba54cf199577760815098e5e10cb951b1fc5b027", 48 | "Proof": { 49 | "proof": "26982a26b2aa20f1e449be5a858c59d7992f7f4a13b007e3980f5c36e8aea7014268883db3094e08e3f493b3d23bae87ac098a33e775172c1027f1b5d025ca08", 50 | "r": "019cbd1d7420292528f8cdd62f339fdabb602f04a95dac9dbcec831b8c681a09" 51 | } 52 | }, 53 | { 54 | "Batch": 1, 55 | "Blind": "e6d0f1d89ad552e383d6c6f4e8598cc3037d6e274d22da3089e7afbd4171ea02", 56 | "BlindedElement": "82d9fc20daf67106ae2d2c584c615d103dafda653ac5b2b2c6faafc06e3f1c0a", 57 | "EvaluationElement": "60c468920f4f949be9aaaf9b4fb27dc7bc89daca4a3aaa31e96efae56c02ac75", 58 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 59 | "Output": "a2bacfc82a4cac041edab1e1c0d0dc63f46631fb4886f8c395f0b184a9b7cbbef2eee05bbd3f085552d8c80e77711b2ad9ba2b7574e2531591380e717d29c6f5", 60 | "Proof": { 61 | "proof": "4a7490fd0a9e13cc66bcdeded002899a3e206364d9bdbaf9998a73dd728c8602a6967f81a4948e6de797d638ee02ca44d933d05f2715fa1618b6a3324f3b2608", 62 | "r": "74ae06fd50d5f26c2519bd7b184f45dd3ef2cb50197d42df9d013f7d6c312a0b" 63 | } 64 | }, 65 | { 66 | "Batch": 2, 67 | "Blind": "80513e77795feeec6d2c450589b0e1b178febd5c193a9fcba0d27f0a06e0d50f,533c2e6d91c934f919ac218973be55ba0d7b234160a0d4cf3bddafbda99e2e0c", 68 | "BlindedElement": "70a6ac589da4cfff4a1135c21e438a50935317ad6900810a59e76c2c28d8e562,5ed4710468c94e6c0181aef8276204ec6aef509f5cf1d7d61846931481d23d76", 69 | "EvaluationElement": "5ef7bc4c54aa5fccb4328fd725d3c20130ebe3ced54f28b6e6c4591815158059,0ce1a236be8dba445cf57ddddec8f1c2d9be2c164add431fc18e3279be968c2d", 70 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 71 | "Output": "453a358544b4e92bbc4625d08ffdde64c0dbc4f9b1501d548e3a6d8094ba70a993c13a6e65a46880bbd65272ba54cf199577760815098e5e10cb951b1fc5b027,a2bacfc82a4cac041edab1e1c0d0dc63f46631fb4886f8c395f0b184a9b7cbbef2eee05bbd3f085552d8c80e77711b2ad9ba2b7574e2531591380e717d29c6f5", 72 | "Proof": { 73 | "proof": "53afba40c6c27636a0694def258728f192d25ec5f97ee1e87a408fd206156107d3b82b618242f10ff459d7d30d0a68d9e381254d2e5f6bc82671f093f47c0e01", 74 | "r": "3af5aec325791592eee4a8860522f8444c8e71ac33af5186a9706137886dce08" 75 | } 76 | } 77 | ] 78 | }, 79 | { 80 | "groupDST": "48617368546f47726f75702d564f50524630392d020001", 81 | "hash": "SHA512", 82 | "keyInfo": "74657374206b6579", 83 | "mode": 2, 84 | "pkSm": "1a2fac6b919790c613e0fbed070471778fbb1c6950d40a4e059acb652dc57161", 85 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 86 | "skSm": "02c1b949c3d65f83b18890aa8d099f6063fa6a72add8d776bc15a291fd08ff04", 87 | "suiteID": 1, 88 | "suiteName": "OPRF(ristretto255, SHA-512)", 89 | "vectors": [ 90 | { 91 | "Batch": 1, 92 | "Blind": "7e5bcbf82a46109ee0d24e9bcab41fc830a6ce8b82fc1e9213a043b743b95800", 93 | "BlindedElement": "da01485047605a666542d0599ef2fbeed0c2e45a97c6e3d420f832918e09f535", 94 | "EvaluationElement": "3015fc16fe179bdb9054da5297c77d1f249dabf32e4fdcc4937d6ba5e99d7b53", 95 | "Info": "7465737420696e666f", 96 | "Input": "00", 97 | "Output": "4d04eccb77a29bd8a00fb1e3f391e0601340c3dc874fc7bb16cfd92d961532d18b4edfffaec94457cb19111bca1ecd19e46124c6a5d29703d09df5e5ab521b28", 98 | "Proof": { 99 | "proof": "f10470180fc884a2f51472eddde9ad9a4080b00e13f63c130cece83b93ca500f956b08e35ed2670ca504c704e0b74687451f5985627c93e2290a5da0dffc1d0b", 100 | "r": "080d0a4d352de92672ab709b1ae1888cb48dfabc2d6ca5b914b335512fe70508" 101 | } 102 | }, 103 | { 104 | "Batch": 1, 105 | "Blind": "de2e98f422bf7b99be19f7da7cac62f1599d35a225ec6340149a0aaff3102003", 106 | "BlindedElement": "909f8d2d517fa2235f8b35f91220636732541d9f3e309c6988d6d8c987e5a357", 107 | "EvaluationElement": "241786b8f9da3e8c28d75dc23b5f8b251ec150ccb453efa712f6e9b72e763a0a", 108 | "Info": "7465737420696e666f", 109 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 110 | "Output": "a88ab2bceba2c9c5a0ee0ee45636e65042b5f274af864f8c1560d32ecee4373c31907f237609d3f164beec32e3270588961c1d19cee467d2a3b0445ebdea2159", 111 | "Proof": { 112 | "proof": "a3748b980aec81add561bcd7ac4fe2b09a93bd8a127991788fd618bf7fb793034a6f7f59cdcab538ed3e50d74b31f82dff14e3c8d3a081f744a6bdf93526ed0e", 113 | "r": "c4d002aa4cfcf281657cf36fe562bc60d9133e0e72a74432f685b2b6a4b42a0c" 114 | } 115 | }, 116 | { 117 | "Batch": 2, 118 | "Blind": "e79a642b20f4c9118febffaf6b6a31471fe7794aa77ced123f07e56cb8cf7c01,0bb106c0e1aac79e92dd2d051e90efe4e2e093bc1e82b80e8cce6afa4f519802", 119 | "BlindedElement": "3206271954cce85425971fddfebe14acad819b9753ffc1718157e54a5e56542f,847b21e32855892256a3eee10ea5c512d362b34de1ab278573cf91edfcb14a03", 120 | "EvaluationElement": "76d3282ac9aabc9b0133df89e680ab0d43f2946c224db25e798abdf0ed1d255a,e8483fbacb3e62787a803dd6d688e4db26be5392f529f1dd6a7f06e2b28dc52c", 121 | "Info": "7465737420696e666f", 122 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 123 | "Output": "4d04eccb77a29bd8a00fb1e3f391e0601340c3dc874fc7bb16cfd92d961532d18b4edfffaec94457cb19111bca1ecd19e46124c6a5d29703d09df5e5ab521b28,a88ab2bceba2c9c5a0ee0ee45636e65042b5f274af864f8c1560d32ecee4373c31907f237609d3f164beec32e3270588961c1d19cee467d2a3b0445ebdea2159", 124 | "Proof": { 125 | "proof": "f5b8d39897f12dd1f8fc927e2f7f563629b7b45f1e6b5eeb469c043d21437907a0e9236beec240a04e0fb906a7d126a8cb40e22730106446c1fa3a40a5283406", 126 | "r": "668b3aab5207735beb86c5379228da260159dc24f7c5c2483a81aff8fbffcc0d" 127 | } 128 | } 129 | ] 130 | }, 131 | { 132 | "groupDST": "48617368546f47726f75702d564f50524630392d000002", 133 | "hash": "SHAKE_256", 134 | "keyInfo": "74657374206b6579", 135 | "mode": 0, 136 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 137 | "skSm": "416f3b0f4d13ac19e6aacade4ecf8b7e9c55d808311be2bea0dae4f4c56d073e7229b8b72a8c7eb68bd2e98336baaec1ac47c82cf2c5e33b", 138 | "suiteID": 2, 139 | "suiteName": "OPRF(decaf448, SHAKE-256)", 140 | "vectors": [ 141 | { 142 | "Batch": 1, 143 | "Blind": "d62851d4bc07947c858dc735e9e22aaf0576f161ab555182908dbd7947b1c988956fa73b17b373b72fd4e3c0264a26aa4cab20fd6193b933", 144 | "BlindedElement": "d078a185d2d8a54b68d6df4e83640192d3659e18fec68d43e4802998d3c9fd819b32070caa78083c909d68daeb7fd420a73f931452a2b70d", 145 | "EvaluationElement": "3452e46b6277b032627a7e5d22aa1b25459f8de90dda31379ed490bb0078eeec05fc4265fafbb5252d4228f9f1f5453bbd391d6b8589f232", 146 | "Input": "00", 147 | "Output": "b93d3ed18489c1236cc965d202254de35767ea673560d6c225cec0b30fe3adc88fee63f8a78d127cd64c7077e1d3ac4a7cc761335c0bcdc12d6981ad87302858" 148 | }, 149 | { 150 | "Batch": 1, 151 | "Blind": "ac345e8d755997956ddd1f267a2d86175aeae5e1168932285a6f602b4b20a570a697452b3ddbb7d0e29363adebbcb5673294396b82931f37", 152 | "BlindedElement": "283f0fab2be6ac3a3c8eacfd504f3ef63f518892f7b000f1dcc1ca2e773aba0fbee48b100886b90d5a08377cbf5ccf69801ae2c23e1adbf2", 153 | "EvaluationElement": "ae30bab51a34c45a76d00034b29e1c5346fbe3718c3863028e47226456880a85a2e5118f274a8c260dae62fcec3cde8624405fc7cddbc867", 154 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 155 | "Output": "aaf99e5a044bbce915bf3ba381e25da62e4b2cea4cee2f47f3662940284579c0f8e1e011062ba010ca4f2c67a8157481c9ae7a458ea035a89e1948bfc5b8323b" 156 | } 157 | ] 158 | }, 159 | { 160 | "groupDST": "48617368546f47726f75702d564f50524630392d010002", 161 | "hash": "SHAKE_256", 162 | "keyInfo": "74657374206b6579", 163 | "mode": 1, 164 | "pkSm": "f27f3a898855240ef102d7bd6795aab2fa3972db3d47005cbd33e721cbed5a3fd37508d093ecc645fa80a7f928c4313cfbd4654e8ea7de8f", 165 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 166 | "skSm": "167e36766c08353fbc8fc46d89619f0306603a8ed33f7eafcca998ed2e890d15f64a22b0196aaa298e4a502275d4ced5c6131de64597c500", 167 | "suiteID": 2, 168 | "suiteName": "OPRF(decaf448, SHAKE-256)", 169 | "vectors": [ 170 | { 171 | "Batch": 1, 172 | "Blind": "4bdfc97a75132d92a1da241baff84fada3e7b12d5b712efcac9ba734d54c2b24bff0ef6310404b5c05d60d7c258cea6500229ee015149f0f", 173 | "BlindedElement": "1ceb0a3432ac6b583c31fa70b7c17ac86e0aa425e0593d04b58021670f725eee6664e6cd2041d90f157bc213a2aa4ed7929630b2d9898a76", 174 | "EvaluationElement": "3afaa02425294a4810766c68e9e4c3c507b109b9064ed56a148a419371d5fb158f6ab5f0da62a6ba915bbe431097f5c71854821c1f10889d", 175 | "Input": "00", 176 | "Output": "b558e37f6435a12fefded196936a4c1d0882bf4a115002920744ecb312843678f396f7d36711cf551750388ddf7a53a3aea7fd0ac60568cd2d4ead16a1ee106f", 177 | "Proof": { 178 | "proof": "f02f7ab2722508e343b5692078556e7ca9b2d63bf83dff902150b867775bf375693cc6a0adf33178ba7e72d6179b36ed051065c93619752958746f0d52e2e3a989d86df15f458847abdcc23976147b7b10c96452332aa03bfce1b89b7aeed080869d7ce8c7acb7414e7dbfcda298b532", 179 | "r": "54534ad9db9f6df6ce515d1b8017923b65cada199e936a623c8eb3bd08e9b3f6584a85e4ff26e9f869d30b6c7c6cc56fd94e306974fbcc3b" 180 | } 181 | }, 182 | { 183 | "Batch": 1, 184 | "Blind": "beda1edc786e5fd0033feac1992c53a607d516a46251614940e76a2763b80683e5b789398710bdbc774d9221dd33c509b4805fc26f0c8d0b", 185 | "BlindedElement": "2e04b6883057a5b5ba020d077ae36dee76a07c2f3eb8cc55bafdfb3da9c7405ffe50802f646ca3c3ef39d195c2d88ee56e73825c7cd2319a", 186 | "EvaluationElement": "6270b2f73738aa846dca34d7b30b7c4f943e31d4d4fb35c598f5d608cf25648b44553d43b158dc2707eda170dc439740c10d7b4355bf0f83", 187 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 188 | "Output": "eb14608be2f14c25b2c9fdd23690d293d0c6aaac501a3405b626b8699cf34bb9dd4c2d7987b6391519b9480da453611509ba98098b3e79a35acd00f5e9d8abce", 189 | "Proof": { 190 | "proof": "f731f60aa18d508f07dd3b7851fe9f8cfe6f02c4ea2814cfe8af3203e49344041e6acf0f09fdffdc02d22728544b9bda8d0604e727f27a1efa16526f169191dedb35a1338bf399d8737d6d1638f6d4b895c0869b4194e66fb0dbb4b3e0437a2af0d76dd8cfb0bf38c9de605dc5749603", 191 | "r": "00cc800042a0cff31f865698f8858efa75a1f0faef934317dd6a10bfbbb39f9f2d97dcd5ff4eae02980b08fc68da7b71d39399dc4eb0400a" 192 | } 193 | }, 194 | { 195 | "Batch": 2, 196 | "Blind": "89ae863bc6f3e8b59bbd1354548220e81cd0ffb6f9e4ec2173870ae684f86b1c06e41ecdb9ef83429e58098b8f30a6b49d414ad5f941cf05,7b1f0b697d9efa2b60c984df38632f1096f2bf292478118d78e9edabbe9ad22900ad84b1a2cdcb869c70c83260691e69ea7f473c3b478707", 197 | "BlindedElement": "c86795bcec21f2b337865406ecbb9495dcacfb0b0d7d2a857dd31f0f70619a403d42bb57fc53c9182878baa7be06e337c885ba0023190d63,7eba6e7672c0a7cb7c725ac98adf2b081e05fce49bd5cbc6c0b687aceac45ee0aab63cb13d0f0493a265996a7aa94c9b30f4fc0c385a36af", 198 | "EvaluationElement": "14553405aed5bd3b2672fc74f52aa1d9efb9cdd5ec668476d74c60eca8930994aeaf61eba482173e5953988d702ce5175ab10c1585cbc4a2,7275d01889988341f8c9c8d0adaedb54af2d166112ac01f2c053fc772cb09d69a33ee0ffc6a92ca0d752e35f4a33ba0677c37a3618ae07dd", 199 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 200 | "Output": "b558e37f6435a12fefded196936a4c1d0882bf4a115002920744ecb312843678f396f7d36711cf551750388ddf7a53a3aea7fd0ac60568cd2d4ead16a1ee106f,eb14608be2f14c25b2c9fdd23690d293d0c6aaac501a3405b626b8699cf34bb9dd4c2d7987b6391519b9480da453611509ba98098b3e79a35acd00f5e9d8abce", 201 | "Proof": { 202 | "proof": "820c0da6f0ceb390355da6fb002549f37031e92337bde432d3518541d2f3e6e4f86fbaa2aa0aa53f15db278a0aa2d305226911e408c25f2bcb3a6774089d075d3a92e273fbe5359a9c81f9e83082a2e8b02f34d248789f8da583296e7c531e9d870790042248e589809a40631feaa914", 203 | "r": "7baaffc0af7cf69078ce1702514d93f32828684a1796b559988623c12413cf511d13cb07ecb6d54be4962fe28eed7d4386c156301dc2db01" 204 | } 205 | } 206 | ] 207 | }, 208 | { 209 | "groupDST": "48617368546f47726f75702d564f50524630392d020002", 210 | "hash": "SHAKE_256", 211 | "keyInfo": "74657374206b6579", 212 | "mode": 2, 213 | "pkSm": "2a742a63231b139ce19eab43e7a855f32e5dcbd16ef52a7f968456a8141045d49e3e28a995cfaa22ee104e22f2239f624b3fa7d41bf15186", 214 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 215 | "skSm": "f68691e40ba92bfdf37acfff161f5404f9ae0e53c7cedb0a790ab17c4c0a74a314c24974057464185e2d2e648f74ee6663443646db2c111a", 216 | "suiteID": 2, 217 | "suiteName": "OPRF(decaf448, SHAKE-256)", 218 | "vectors": [ 219 | { 220 | "Batch": 1, 221 | "Blind": "ee671e4c9b6783bd5e4a55d2e8474fe0ec811b4cca7c0e51a886c4343d83c4e5228b87399f1dbf033ee131fe52bae62a0cb27eb7abfcab24", 222 | "BlindedElement": "4c371528ab436b8a6a5bea333cc5702c70cdddb80d12dc2eafa06b87c15bba8b0b5451bc09f3d07e57c12af4c0398b09ae91b678fdeaf2aa", 223 | "EvaluationElement": "d27f65d6c41880303989752e40748e940add1ad32e7f76ccbb873b7fff424d348ec8e43c11402e02934c1fcdadeacbca2d2e5171daaeef90", 224 | "Info": "7465737420696e666f", 225 | "Input": "00", 226 | "Output": "1ffbf9591b674e6a089279a8319c75e949cc277d7b5c75736141218030790755e90af009768e1b9240c9734d8886c6121123384140b26c38c7a6c4217a1b3d94", 227 | "Proof": { 228 | "proof": "bf2f61413c56c0351151c1995007ceb2e197c987056f20a54f0027e544a0b20a7891b9aa882203f2e09e1a0ca9464e3cdf130eea9e1123023460d3f280dac87d23b8d2258666d002f57810d8847832b775984819e457c7bbe703947e7aeccdf59d3e520437edefc26b814f9fa7fa9917", 229 | "r": "c4b297c662a87631531aade91c0558d87224d92247bdfa419a53af4cbdb352b0a2016e5e5f6c0bee4a642526ef9910289315b71fdee5df1e" 230 | } 231 | }, 232 | { 233 | "Batch": 1, 234 | "Blind": "1abe4937f28f531b14ac96b844320e7a66810c2d9391cbb877348301ab59a3a91b4a2129672886ae5da7839f2ac8cf1c5fa92703f5b3fd06", 235 | "BlindedElement": "dea615b00285247715173fc6db40cab1436607bc0eaed3d7a1a1467b70c7ff2f2ce91c05bcaeda2b01952926f254f13e1a763a174caa693a", 236 | "EvaluationElement": "a692017b9e91efbe6641c3ef0cd3b352022ed08bb5ed0c1da0838589bebe53c2d2959818359cb0213b94cafa672673608b9a2280671d6c75", 237 | "Info": "7465737420696e666f", 238 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 239 | "Output": "daeb206a0e1fc120ebe4ad885f851f456f7d8908166839b7dc541f712514203d9a3589025b4bfad6a79c6d40bfbf217f44a9aa17874a1ec271b23cced72a44ef", 240 | "Proof": { 241 | "proof": "ede122b8ce87d22fa1bb9dd38dc76da1a9ff812a8d2cbf3d2a6e86a10331a849d203bd925d6f130d80f333aa0443488731769e975b4c900d923d740fec13a61d3175a0daf9a88d8f66704b36ca2b1b7fefd6cab4ffb50fe998e53ce4743ee9466a56886f79fd6d5b924553f64130c60a", 242 | "r": "a3e896e126d371f6380ca41757f6458b93b049e1b0d73ab5b8d914b08dff3e52e62ea8898d35b2862d28ff4c5f89353d25d6b5a8dc014d3b" 243 | } 244 | }, 245 | { 246 | "Batch": 2, 247 | "Blind": "255d8adc40b8f39f14cd8bd4ade8abbb95166afdc9e922203abe7a8539854c64b943b0b46e1e1b47cfb52e9a0867c8cde22bcbdd724d9f09,71bd897c56c86b31b096103b7e2d26d0f4d66be95299379b41668dbbc5ece26cc212d9f2cbfaf479efa17b7f6b056dfcfbee5bd7365cea26", 248 | "BlindedElement": "361b80bba04ff4b211e38e636a8530531213a44f44738992b18eaf0d9759eedcb7e4034e9bda6f8829250aff72343b0d2d1e23d612d94674,c68d79d1a614b90e6ab1dc14a982f9fdd423edc94a10d87d45e32935e363079967ad2894828b1764cca8dec5e9f919def474b1d03b6c069d", 249 | "EvaluationElement": "32629ccfd36787d8d80756f025f6c23c21145dd22c28d974f34098e166a300731b691e1faa7e3959c1bb38312c43d1d693cbab4b90fe7d2e,eec655a1a869b3f0f470f7a0f2cef69eb6539c6c1b9e49d9b380bccc7b510d466f45d88fa690b687a8507d1e0b275028d095292fc4aadd2d", 250 | "Info": "7465737420696e666f", 251 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 252 | "Output": "1ffbf9591b674e6a089279a8319c75e949cc277d7b5c75736141218030790755e90af009768e1b9240c9734d8886c6121123384140b26c38c7a6c4217a1b3d94,daeb206a0e1fc120ebe4ad885f851f456f7d8908166839b7dc541f712514203d9a3589025b4bfad6a79c6d40bfbf217f44a9aa17874a1ec271b23cced72a44ef", 253 | "Proof": { 254 | "proof": "2f3501925c81837232ae34e5351518ad35e24f1d32f7459da3c19cae774695e7dc1eca32133dd57cd0e2eb67c75c9edd9cd3ff9c5e1759314ea99a4eca322f6e56f4b80795f67d1bf747834d2d7b3049351979ca876ecf28f87b81fba243269e3c09ea1889abd968af67c7ca511d0c3d", 255 | "r": "bbbf1ebe98b192e93cedceb9c0164e95b891bd8bc81721b8ea31835d6f9687a36c94592ab76579f42ce1be6961f0700496e71df8c17ab50c" 256 | } 257 | } 258 | ] 259 | }, 260 | { 261 | "groupDST": "48617368546f47726f75702d564f50524630392d000003", 262 | "hash": "SHA256", 263 | "keyInfo": "74657374206b6579", 264 | "mode": 0, 265 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 266 | "skSm": "88a91851d93ab3e4f2636babc60d6ce9d1aee2b86dece13fa8590d955a08d987", 267 | "suiteID": 3, 268 | "suiteName": "OPRF(P-256, SHA-256)", 269 | "vectors": [ 270 | { 271 | "Batch": 1, 272 | "Blind": "f70cf205f782fa11a0d61b2f5a8a2a1143368327f3077c68a1545e9aafbba6aa", 273 | "BlindedElement": "0372ffe1ebd9273f17b09916d31e7884707e8902f7e3af2a1b3ae1dfbfae9b5126", 274 | "EvaluationElement": "02aa5b346b0375cd734014ffa9ed2135a1b07565c44fe64d5accfe6ab6d8c37f77", 275 | "Input": "00", 276 | "Output": "413c5d45657ce515914232ef0bafdbc1bfa5c272d4b403f2cea0ccf7ca18f9be" 277 | }, 278 | { 279 | "Batch": 1, 280 | "Blind": "482562df55c99bf9591cb0eab2a72d044c05ca2cc2ef9b609a38546f74b6d689", 281 | "BlindedElement": "02fefe6e044601a158175fb4bf90c06841ca7211dde4e56e5cac6dd45728cfa04a", 282 | "EvaluationElement": "03167ed445f79ffa867268e30c0aa240ad1a8635690164066d833e350802e57273", 283 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 284 | "Output": "2a44e98a9df03b79dc27c178d96cfa69ba995159fe6a7b6013c7205f9ba57038" 285 | } 286 | ] 287 | }, 288 | { 289 | "groupDST": "48617368546f47726f75702d564f50524630392d010003", 290 | "hash": "SHA256", 291 | "keyInfo": "74657374206b6579", 292 | "mode": 1, 293 | "pkSm": "0201d3da874a209120ac442081e9ef9ed8ee76fda919d0f386cb5a0143755b10df", 294 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 295 | "skSm": "c8a626b52be02b06e9cdb1a05490392938642a30b1451b0cd1be1d3612b336b5", 296 | "suiteID": 3, 297 | "suiteName": "OPRF(P-256, SHA-256)", 298 | "vectors": [ 299 | { 300 | "Batch": 1, 301 | "Blind": "e74c5078a81806f74dd65065273c5bd886c7f87ff8c5f39f90320718eff747e3", 302 | "BlindedElement": "029d750421c5c726658902c47d3675ebba01ba25d0bd127bf6e338b801b166f1d2", 303 | "EvaluationElement": "0291e9890c7418a2fc1ac635d2650bae3f1a25a9ffcd0bc01b3c39fcee4b095dca", 304 | "Input": "00", 305 | "Output": "a906579bce2c9123e5a105d4bdbcafb513d7d764e4f0937bee95b36252778424", 306 | "Proof": { 307 | "proof": "54ec2d8558f5c72ff32489556c3ba1f3087810c5f51cc025f07adc034df2dcd6d706e7bdae3119b70748cbf76b66d520de87bf90287a091cf6f8d2a465cf2200", 308 | "r": "dfc19eb96faba6382ec845097904db87240b9dd47b1e487ec625f11a7ba2cc3e" 309 | } 310 | }, 311 | { 312 | "Batch": 1, 313 | "Blind": "dfe89ac0cdd6b74684580de0f95f8e06aa6f6663d48b1a4b998a539380ed73cb", 314 | "BlindedElement": "02e6085d4017ae0bede4b261977b588349d323414eb5c409e552e2bd4c82df498b", 315 | "EvaluationElement": "03a649c5ac48f33a6c6cd82120145e673e17395ca94ea824c7d2dda7203ba4159a", 316 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 317 | "Output": "d13c62d285a71acb534dcebdf312bfec0e2a3fcb79f4ac32d2dfb0bc9aae3cc7", 318 | "Proof": { 319 | "proof": "1a79f6a52579f7acb0100c916390989a1dca3c1b3078402e102b8dd037f0b34d929d38239b34175f1328708ec197bfc532ef31dafdf1ee85db4ccf8769844fdb", 320 | "r": "4f9a70536c175f11a827452672b60d4e9f89eba281046e2839dd2c7a98309b07" 321 | } 322 | }, 323 | { 324 | "Batch": 2, 325 | "Blind": "9e68a597db2c14728fade716a6a82d600444b26de335ba38cf092d80c7cf2cb6,d3d6e1e1006dc8984ad28a4c93ecfc36fc2171046b3c4284855cfa2434ed98dc", 326 | "BlindedElement": "03b2332e9dc41bd9b7997df58c1f432d13c4f018cb2095ef8eb14ef3b323aceb86,03eea6961f7f16deaa8deb6f68a865ae04d8be760626cad589b22cb90262e30b0b", 327 | "EvaluationElement": "021f7b60c7c53fd3a6867cb38bb7f6febdbd802a78d1011100a779b67a801c3bbe,0266e83c8c525cd612669737496f0a736feb7d4209a520d2cdc204971215db0262", 328 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 329 | "Output": "a906579bce2c9123e5a105d4bdbcafb513d7d764e4f0937bee95b36252778424,d13c62d285a71acb534dcebdf312bfec0e2a3fcb79f4ac32d2dfb0bc9aae3cc7", 330 | "Proof": { 331 | "proof": "92c5c32bc18f3f5dbdd51473f4e3ecc9b07797c63d679be5399b223ae801ddd1e469df512f907d317a0930dd0e644b26c96edc87d2f8e0a09e66bc73db8647c5", 332 | "r": "6e953a630772f68b53baade9962d164565d8c0e3a1ba1a337759061965a423da" 333 | } 334 | } 335 | ] 336 | }, 337 | { 338 | "groupDST": "48617368546f47726f75702d564f50524630392d020003", 339 | "hash": "SHA256", 340 | "keyInfo": "74657374206b6579", 341 | "mode": 2, 342 | "pkSm": "02eca084e8d6ac9ed1c5962e004e95e7c68a81e04be93ceabf79c619de2bcc3eb9", 343 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 344 | "skSm": "b75567bfc40aaaf7735c35c6ad5d55a725c9d42ac66df2e1dbd2027bde289264", 345 | "suiteID": 3, 346 | "suiteName": "OPRF(P-256, SHA-256)", 347 | "vectors": [ 348 | { 349 | "Batch": 1, 350 | "Blind": "4238835743037876080d2e3e27bc3ce7b5fb6a1107ffedeaedb371767432b68c", 351 | "BlindedElement": "02cb57f07ba100b93ce1bf8176963c8c7f73a76827f1c1401a923d7ca4083e15aa", 352 | "EvaluationElement": "03059d58ec9a801e33f57525c03241d8ffb61b67a18edd35222d864ffbb42b5d2f", 353 | "Info": "7465737420696e666f", 354 | "Input": "00", 355 | "Output": "15fce9922a2307349aac2eccc41941283e3c5e938aaf2506f99a6d8b6ee34ef8", 356 | "Proof": { 357 | "proof": "13889d6849850ccd0119981fc053a38a30a57d275091df2887943d1332f738204f8a6cf2fb6e57c9b118ec82b9b012f8864561e4cd8866245f9c762b9d45dbf9", 358 | "r": "3d5c65b55a1b8960563b3420d7764097502850c445ccd86e2d20d7e4ec77617b" 359 | } 360 | }, 361 | { 362 | "Batch": 1, 363 | "Blind": "c262bf51dc970d63acb5ab74318e54223c759e9747f59c0d4ecbc087302667fb", 364 | "BlindedElement": "02208809631cc08f553d7843db566c55746e760a77c63513d2b22096f98452cf9d", 365 | "EvaluationElement": "02301ee1cf1d01276649ac0f718ebbfa1c0d6a1b3e7ea82b3085e9173910fcb0ef", 366 | "Info": "7465737420696e666f", 367 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 368 | "Output": "a06ed7380210856caaba173bcad06266186c6638d86e372c3c96b9bd2f353543", 369 | "Proof": { 370 | "proof": "cb69a1ec76643a2100cc9bfe6cf1ed1fa5ba3612ed3e3211036b5ed835a138be3eb92126694e3e925ab138d4df885be18ed80371847f80baab82ce70588eebaf", 371 | "r": "6c6990f0fcd9a655f77ff0b2ebcfe21e1a1ca4a84361e9f1b18e24c9a40ed5ef" 372 | } 373 | }, 374 | { 375 | "Batch": 2, 376 | "Blind": "3f95c9b1334d8af16ae1e69f5adc24e5aa89ebb63637c835fd39b17a1a4453ec,9801a9d83b5d1c0fc0812c10e18f146b14d7eb94755a918bac1ef8d69d21a7c2", 377 | "BlindedElement": "0277b1e97f06cb0976bf196a30dc7b3f635a40ec1337d4bda00f809eaf244f7133,0306823c1610cc81b08db444d7c23cd368e8b6fc1a7fa3d727f28bcc7d3afa9c41", 378 | "EvaluationElement": "030f395a0e0328018c78de95ff498a0afb54fbcf3419722649e211940852ad0171,0221335509649461a4d201d2887af62313466af660559c348e8ac326fbc1c147af", 379 | "Info": "7465737420696e666f", 380 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 381 | "Output": "15fce9922a2307349aac2eccc41941283e3c5e938aaf2506f99a6d8b6ee34ef8,a06ed7380210856caaba173bcad06266186c6638d86e372c3c96b9bd2f353543", 382 | "Proof": { 383 | "proof": "187aaa12108c49c1395001ccaa677519572ac4680b0f41b346b9879d3ea6fdb9fced5c7f20b351d03786d031de79cd3c03723ce48053a13b640fe6051ee3584e", 384 | "r": "fa0ea4754fb56527be010296ea880e1c6a4dbbc9ede543a2ad0f83fd60fdacb6" 385 | } 386 | } 387 | ] 388 | }, 389 | { 390 | "groupDST": "48617368546f47726f75702d564f50524630392d000004", 391 | "hash": "SHA384", 392 | "keyInfo": "74657374206b6579", 393 | "mode": 0, 394 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 395 | "skSm": "8b0972b97a0339dbcdb993113426ce1fe1b11efefe53e010bc0ea279dda2e37ac7a5599acec1a77f43a3ac7a8252782f", 396 | "suiteID": 4, 397 | "suiteName": "OPRF(P-384, SHA-384)", 398 | "vectors": [ 399 | { 400 | "Batch": 1, 401 | "Blind": "cda63dff3137c959747ec1d27852fce42d79fc710159f349e7da18455479e27473269d2926fec54d4567adabd7951ad6", 402 | "BlindedElement": "020db1b05b22ddd8a851792dfb5b10b4f237d69522097cbb0127ae537e3256f86e35a72554a6ebdb26c28342fee16473dd", 403 | "EvaluationElement": "031e6e8c82d3284727a724a5854b3e2bf9958b4e5470601f4ca37d33d26879eca817796cb7e98bbbb1d1739eeafb33c027", 404 | "Input": "00", 405 | "Output": "b2e380ca96ea80f7550a6b663e5f7752d7d7772c46169d72308a84259031e804ba577ac34e632f535a9519a692734016" 406 | }, 407 | { 408 | "Batch": 1, 409 | "Blind": "f9e066cf04a050c4fd762bff10c1b9bd5d37afc6f3644f8545b9a09a6d7a3073b3c9b3d78588213957ea3a5dfd0f1fe4", 410 | "BlindedElement": "023c36bf6352c93d27b118972d1040cf22f99d5a1c8134afb898d30b319f70a096973db23410881f84eea599c0c73220bd", 411 | "EvaluationElement": "0240b6a002d0190793ea62a7499244027753d63b0a57cea198c8c6dc883cdeb273ab385699bb414f1040bb6819313cd675", 412 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 413 | "Output": "1d155a7ba2ea75c4f1e76fb0a37231e9b0776eed3f24a6541a01907ca8afb984a74408e6d2de8e481cae5dd03bdae3ce" 414 | } 415 | ] 416 | }, 417 | { 418 | "groupDST": "48617368546f47726f75702d564f50524630392d010004", 419 | "hash": "SHA384", 420 | "keyInfo": "74657374206b6579", 421 | "mode": 1, 422 | "pkSm": "02d7bdae4b97ecf0fbb8c00cff3a3a9b6d0fb0cc34f8490a98a74dbb59a85f43bda8ca7b3c0b05164f38d8efdef2c3426a", 423 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 424 | "skSm": "70855cb96c961b39ea3ea5776d89c8b7623f5891a26e8437f86e2c713bdb0da23415590a28184dc22088a215ebc7fe45", 425 | "suiteID": 4, 426 | "suiteName": "OPRF(P-384, SHA-384)", 427 | "vectors": [ 428 | { 429 | "Batch": 1, 430 | "Blind": "61247a74d0c62c98ddff1365bb9b82b279e775b7220c673c782e351691bea8206a6b6856c044df390ab5683964fc7aac", 431 | "BlindedElement": "026601d99c313b827a09aad832fcc814ac5257a57bb49d65c05e247df9518315a66557fc8af56b4521c51900aaab1a2ea9", 432 | "EvaluationElement": "020b478b9c9b1a5935e07fb532eac2e596b78170a0e755ecc71829419e63a2119eae23be281e109de205cd85af7e42228a", 433 | "Input": "00", 434 | "Output": "f18884ace2e342f849cea7f2f17de902b9884574fdaa8f507356f482c6b67013f329e8c899b3c2c154af1defaa11d656", 435 | "Proof": { 436 | "proof": "02d0946f1795048bc803171aea5b4a9a5f256bd5fa9414e5fa76dd17a4aaa94307814d57c2cca239485e29bb76d4ac1b4d3d62dfbb8e43c7135b2ebe50fe923e30bd99e1e6ec961db18fa6e67c63dd6652284c15860156c08d64d838efbeeb68", 437 | "r": "f5685928c72d9dab8ddfe45de734ce0d4ff5823d2e40c4fcf880e9a8272b46eea593b1095e7d38ba6ff37c42b3c48598" 438 | } 439 | }, 440 | { 441 | "Batch": 1, 442 | "Blind": "ef54a703503046d8272eaea47cfa963b696f07af04cbc6545ca16de56540574e2bc92534ac475d6a3649f3e9cdf20a7f", 443 | "BlindedElement": "0279e61686e698fdbac5cf484f54846db8cdf6f403fa88209c34c56c584fe4ca600ac81b61aad11c5e639ff1add3b30de4", 444 | "EvaluationElement": "03900e8e3f5b8bf698e7aa0aacb8dbdfaedf80220b1f640de2049615985b19b913569cc2feb90725a3661146fb88ef3755", 445 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 446 | "Output": "f91d172cdecdea4f8299c8b39426db4c47428b82f8872b8539ad9b019deb48b8d3c928c572ed988d5591a4442c060438", 447 | "Proof": { 448 | "proof": "343536346a1145b81336eafc239f225dc6a154752492707c0465f029aa9af0fe2bf0428285e43b596db633b50f0801b62b0e9c64c62f329b8a84324a415e4a586cbf9477b1285c9b74f614c352e06658a8997486b8177006491e84aa96a3de09", 449 | "r": "0cdd9475ad6d9e630235ff21b634bc650bf837aaa273530dc66aa53bb9adb4f0ed499871eb81ae8c1af769a56d4fc42b" 450 | } 451 | }, 452 | { 453 | "Batch": 2, 454 | "Blind": "485cccf5018abbf875b8e81c5ade0def4fe6fa8dfc15388367a60f23616cd1468dae601875f7dd570624d0ae9d7be2e7,b0d53a6f8da29c3cf4f8695135d645424c747bec642bc91375ff142da4687426b0b4f35c14eb2477c52e1ffe177f193b", 455 | "BlindedElement": "03dbcb21b211e7b5d2cf0c36d782308af28458539423f67a29336355e55035137eb768b1935b5a825c589a2913f0c2894e,036c8b2fa4dd9cd057561d377b4686cddc82317ad3e5eda08bece2a8616ca724937ff933e340a47fc09bfe9b0fc1ef9ab6", 456 | "EvaluationElement": "03a1cba477a408162aacdca43e059309fd61cc14687a107bd492a1ec688a010ff49c60684e0f973412a7da2e627b1553a5,036fe6df8a99bb7b2c4a5020ab4c6d7e71b5abca2d5d5a418f2314b614deb40c7b3acad982951b5f524e56f0e9ac7d8e95", 457 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 458 | "Output": "f18884ace2e342f849cea7f2f17de902b9884574fdaa8f507356f482c6b67013f329e8c899b3c2c154af1defaa11d656,f91d172cdecdea4f8299c8b39426db4c47428b82f8872b8539ad9b019deb48b8d3c928c572ed988d5591a4442c060438", 459 | "Proof": { 460 | "proof": "1e26bf1210717b88dfae585008100e9ccaebe93b8605ca168a608cbf1855697b7b87d0b9c6bdca85e43143b3630e87f2fe9ce519dba3d477d2a869bcad0db9dc6239cd11938213f9bfd63d39de090a6fc90cd1f33f164b2c54c38bc31ad98ddf", 461 | "r": "b36f4c2a140b7a3c53dd8efb6171d3bb4d73591be8483a1a38e40c13a04b0f2180dda3c36e3d43c3a8f127158d010945" 462 | } 463 | } 464 | ] 465 | }, 466 | { 467 | "groupDST": "48617368546f47726f75702d564f50524630392d020004", 468 | "hash": "SHA384", 469 | "keyInfo": "74657374206b6579", 470 | "mode": 2, 471 | "pkSm": "0286f37b6295bba7ebf35d2bfbb944d441fc416e51eb5ceeb63ac98afa6a627ccafe20bd600c728bc5b1300148ef2ba6e6", 472 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 473 | "skSm": "2b65a799c107905abcc4f94bdc4756e8641112ae1fc21708cd9d62a95262938ded6834e46bad252b4e533ee7eec7e26e", 474 | "suiteID": 4, 475 | "suiteName": "OPRF(P-384, SHA-384)", 476 | "vectors": [ 477 | { 478 | "Batch": 1, 479 | "Blind": "9572d3a8a106f875023c9722b2de94efaa02c8e46a9e48f3e2ee00241f9a75f3f7493200a8a605644334de4987fb60da", 480 | "BlindedElement": "0252f98f04a956afa469c62ca2850f751b112dc019d4e713c662fc0735ef8573f1497cea55b750f27f0efc8330e394a3ab", 481 | "EvaluationElement": "03fb20c33a7f6f01f2bb388318a6db84f7183bc3bd5e5840302fe38b6b313649b523238b4c4c625614440dd6ddbbcc7272", 482 | "Info": "7465737420696e666f", 483 | "Input": "00", 484 | "Output": "af52cf184180177970be0770e1c7920aa307b767556a13de38a64723d8dcc7b344af9b6dd8f117ac2cef249ee3acc8fb", 485 | "Proof": { 486 | "proof": "d33c83c1840a48759659a4d417769ae3bb1adb86326a36fa1ff24f70066b75d0200e5c1e7d9847e91f7d3d6843efc62101c401a7c952cde32ada6fec848450d8564e2c778af47ece4f50a88c6d2281bdd858b90fdfad8b093c986bc1e59aaa2e", 487 | "r": "7e82569cb56d97e9c20e59311bac3a50735d573abb787b251879b77de4df554c91e25e117919a9db2af19b32ce0d501d" 488 | } 489 | }, 490 | { 491 | "Batch": 1, 492 | "Blind": "01e6e57b7ec6752a45c74f3ed36a3eb8ad0dafb634f668e415357a04fab501c0f6764e854701129e38071b008286c5fc", 493 | "BlindedElement": "0257c264a1016e7a1a8236e46cb3bc11a0f13178b03262e01531da14a05e75a811ba4669fc41cc9453298f71c23834f91c", 494 | "EvaluationElement": "0295529cd99f4255be59966e430bef38c93a5261b0624612091327c9aedaaaa40d22b03280ec15620bc91d48970f18c68f", 495 | "Info": "7465737420696e666f", 496 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 497 | "Output": "8bc546462de3087cddafcf81435d5802c0c31f557c791b115a092d5b71ea2b6e20986bb624ead85c7a63c976c05dcddd", 498 | "Proof": { 499 | "proof": "b5dfd5fc5ddc61ad8234c544aacbf280193da985d9204d5a30ef9d1a5964c1e70ffc3d9c986c93f561ab6f91f012c8ef9b9b6f2d1c178f01fe172c37c98fd4ef05e5b15c15e810241ed6dda051500165d9f79d4e83580ea4c810a95dddcb593c", 500 | "r": "6b61028c0ce57aa6729d935ef02e2dd607cb7efcf4ae3bbac5ec43774e65a9980f648a5af772f5e7337fbeefbee276ca" 501 | } 502 | }, 503 | { 504 | "Batch": 2, 505 | "Blind": "ebd2fec41edafcba833ccaac567c14d2fa01f55b33a2fbbb37118f2f5603b1298346e02cbdf55c95ef9b1aadda5ef281,be210603388cbcabb8cb630aa1ad04d73e349009a438ce248380bd4b7e6758211fe9692922fb61f00f1a39bc735cefce", 506 | "BlindedElement": "02b1938a7613e9567a67aac83c50529238e3323c212dd4074911980c4d998479174ddb9c925d1b761b33da2ea0bd0ea057,031b9a47b7caf732ff32db035d1d073fd925c17dbb6c83e00a49af674166bf264bdb00c303edb26af96fed6fe9ce44dc36", 507 | "EvaluationElement": "02c75f2d383c18692e0e11b08e9187c4c047d28116977c8e5e1e872f1cf5eab457c04fd50274cd5cc4b1996a607470694e,02e72fbfff4c7047923b967ee9a6b37d902b49a465242c12b2b910daa5f30c3f947899283ed0a6c75834855a1ac0c64065", 508 | "Info": "7465737420696e666f", 509 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 510 | "Output": "af52cf184180177970be0770e1c7920aa307b767556a13de38a64723d8dcc7b344af9b6dd8f117ac2cef249ee3acc8fb,8bc546462de3087cddafcf81435d5802c0c31f557c791b115a092d5b71ea2b6e20986bb624ead85c7a63c976c05dcddd", 511 | "Proof": { 512 | "proof": "bded438b699d3bb8bab26954f9a7fb5bb402f043c3364dc4f2b68976748a77868dec1fa2d0774d306043ee8abbad5ef8ceee6a331be2906124f53f37c96d7f5a4aa543053ccca87b577a32d803c3ca4841e37b3c4b5cf20aad11c59dec72a350", 513 | "r": "c7a86f11c143a291e349b70b34e67b38fe9dc6f90b47375087d72e891df74070810500dfd391282c15d87bacdc9867a5" 514 | } 515 | } 516 | ] 517 | }, 518 | { 519 | "groupDST": "48617368546f47726f75702d564f50524630392d000005", 520 | "hash": "SHA512", 521 | "keyInfo": "74657374206b6579", 522 | "mode": 0, 523 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 524 | "skSm": "00dc7a8db919a1076810a0c1503716d91668fa9edc60952317f26d47a090b70dcfd3f530d07f48675cf8236d1daa81f3ff0f289942632e5cefd27a2190f0cefdc302", 525 | "suiteID": 5, 526 | "suiteName": "OPRF(P-521, SHA-512)", 527 | "vectors": [ 528 | { 529 | "Batch": 1, 530 | "Blind": "00b638b3000884019316267eae9b424f812592e4dc9cd7f7aebfb1d3d2b8c7fa7904503aef20c694a01d3e1154fe98e7232be9eaec5789a012a559367b1f99654ddf", 531 | "BlindedElement": "02016f3fc7b3c84f673c75b3bb3e00ddd81e734cc84fe3bd4a7671e0a971879b7678c048f40aae87179614abc2261522303257a92127a195298744c54094b7b87499c0", 532 | "EvaluationElement": "0301ddff1ac88acd812a2917cee4917f8a692eaabf9fd0529981441b83e368175b566657729a8be5ba2573e33e7734c146ef4c8b7d41f450384280797318ff3a62d79c", 533 | "Input": "00", 534 | "Output": "383e3098d74b43f75d2e1136d7e7c08702d992e6f5f24f2bd438f98b86d9d143ce87281b2daf7d67c94370903ba81495655d6e9626443a895b37bb74c0276f2a" 535 | }, 536 | { 537 | "Batch": 1, 538 | "Blind": "00219598d5f1544830f9d667b683234c68ef3db95227fe3ebdfd963d03070055fef107bfeb3c79c86b934061f894227b23a69eb0b53f168a4a2230ef6a7d703ac4ce", 539 | "BlindedElement": "0201e75b88d5de2b839f356a05c359ed610601450d83dcc9def649fbd00a9790161e9333cb07978d1567ecab037c498ae2b00d9181abfd7bcee3feedc11de88c54190f", 540 | "EvaluationElement": "0200b99d9694bbf9b9ea783e18e5fd049fb2fdf169cf386abe304ccf8cdd633f7a1e25083ec6a6ca3a6e82367b38ee3c991e024097cf6fad928b023817cdc5dea21751", 541 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 542 | "Output": "5100f12a88477ba993cfe8eb5a82a835892b7fa3bdb47dc1db19725e4c1138798e0f965df4f649e3a159aaca1fdd07034f7b91c0c9ac3d064b50953bb5c867c3" 543 | } 544 | ] 545 | }, 546 | { 547 | "groupDST": "48617368546f47726f75702d564f50524630392d010005", 548 | "hash": "SHA512", 549 | "keyInfo": "74657374206b6579", 550 | "mode": 1, 551 | "pkSm": "020006090cc9a6f2eaef7e12759a8b5362e9972b4f36c4b3a3d71c4b67469638593ed8f46291542e0f04fd462a8e8ab96047be087a9d3fb182f4c138c6fc956593b205", 552 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 553 | "skSm": "00ef23ce13076b43a5a33e8fb8f94c940bbeabb762e5380d69cc9fa82c22a8de39431c99e8b9d9fb75ea90446f895db04fe402b8be2c9df839f7d10ea6a23e7e0eb4", 554 | "suiteID": 5, 555 | "suiteName": "OPRF(P-521, SHA-512)", 556 | "vectors": [ 557 | { 558 | "Batch": 1, 559 | "Blind": "01dd6b45efbc57c5f087181c9f03d5b5e51b3a90cc9da17604b2e59a93759eb0985d2259c20e3783be009527adf47f8fb5b1437cba7731c34ac70bc6ab1b8c14ff42", 560 | "BlindedElement": "0201b93fc5997dc0e8acd5b3ffa3a6f1be1522986c17ed60e5bad7b057136b3b7e31b5a7073a744a1304bf9bce4a27b02d77f1caf73a5f72686fa9e83dbe9f730b4304", 561 | "EvaluationElement": "0301142205a8fb983efb6d76111db30e2a6e7c54724fe0ee54f842a477cbf03adcb2cca8df2f165a65694e7a056948f8afd651b32ea8153cc26f819cc5b1243f383910", 562 | "Input": "00", 563 | "Output": "b3e837431aaafdfa8efbf486d70ca2d4364ef86afc7a8941d9bf1a6adb7bfd8c5302f91ee5796d956b5d3ea95fd0138d55d3059b1f4febf8cfd552e31fa2cf97", 564 | "Proof": { 565 | "proof": "00fd70ea3e4b7b008de749adb7403ca66f7aa56ae06a587d7d74a06daf6d5c4dcce8a81acdbee1fcd21ce55db4440383b9fcfc1d584db6fa022a5fd62595c7d939820116ad656f5ad470b5bdd208248a0a0b064960c80e180239691c5eb77b4a760eeda8c27cf3cddc37d6329ad4997a37ffe9aba6f96d45e3e67fad86ec7b1d3a47487f", 566 | "r": "01ce330164821b9b2a108e3ef8964622075015ac9ea0f8380dcce04b4c70b85f82bd8d1806c3f85daa0e690689a7ed6faa65712283a076c4eaee988dcf39d6775f40" 567 | } 568 | }, 569 | { 570 | "Batch": 1, 571 | "Blind": "001745a97be4680b39889979a8b4b4322450628389ff2d90c0799597e99c926ae54b2fce5ca13daa8cabbd4da53324fbd20554f2c56460442edb7d6ee76b64ab68d1", 572 | "BlindedElement": "0300c2eed082810750dc327122ac1d9de647d1943f1767bd54656835ef71dd68347436c121df49c997b0adb11cc421ec8abc5611b6d9e86468e4666001e3db386ace9f", 573 | "EvaluationElement": "02002ddf4ceaed6eefea8f12ca765c6d800e3f514f4fc75f52f55a555cbecfb724f2bde39d5890f4b0dfcd3ea9029f663c7babddb30dd0692bfe0f76190b7ee30bf68a", 574 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 575 | "Output": "e8f92bac6c7ae89918d724697d8c45da339f55b61d527c50104e66582803a8e6dcceae31b0d499e471aca460194a011d6b8b94fe2886b8b5a0c242079bfbf09c", 576 | "Proof": { 577 | "proof": "018d1fbf1bc79cb4636d905f03bba48e0d872ac89c4c7e8bcc6884dc796d22ceb648dd373b196d23335052a8d8013b154b38d233d68612213e3cea8f9024a1d01e160004438ef7171638b7799a987739064e2313f9a902413d1f11f0eb5c7e8ca00d9c83d03aa12bef8059144bc169bec7f4258858438daaa3ebdacad658e5f3eb7c6d5b", 578 | "r": "0013559a0599ff077b4ebcbe7f73e9fc1bc25fff3fc5fd6c8bc664e27822fdece106def4a69460e9777347a314fbbe5035803d3aa65819e81997c4d89909e25ce20e" 579 | } 580 | }, 581 | { 582 | "Batch": 2, 583 | "Blind": "017b1679ed98960e4cee27f330d5d3dccebf40596dc7e8b057938841423f8b336f12c6c4dfa3a822d8f670e5aa46e733baaec9f93d5e14ad9ab99dfcbcb2ad157a8b,00010fddc6356f1aa3fb05702631e213b4bbbe8fe5176fff25526ed5b1772ba6164952c3c2da8017fdf337f81f5cbd0ec805923a335fa1bde3dbb840b3924c5ceba6", 584 | "BlindedElement": "03001658908fb353f15fd637ec5b9703cc1dfe5a8aab4f5fd519c0e41f69300769918d28963c07e5678ecb98c235c406f29dd1cd1dadadb4e23b98a1cb290992b9aa46,030123068d8554e65999a6df1fde0bf10550ec5e223ef1d30c073dee509933502a59106aae80e67d33f93400e2c32b5d9bd49f4e8cb97f08f4181998d330013b9e07a3", 585 | "EvaluationElement": "03012d790d77da1675a8e4ca5d9ce622046e31ea4af9511fa32ef80fbe60e9e04866feeb7f31818c1ff0ed263cecf07f787129450f9f7322fa9b593edc5cb626d1abb8,020198f45b9988bf0ab56c37bbaed0df4099c9b7b7a89b5c7030a451687cd9f8fd777f587dd7dcd717f529149d21c0763da4a03441d96e6fa245888a352d08ddda70db", 586 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 587 | "Output": "b3e837431aaafdfa8efbf486d70ca2d4364ef86afc7a8941d9bf1a6adb7bfd8c5302f91ee5796d956b5d3ea95fd0138d55d3059b1f4febf8cfd552e31fa2cf97,e8f92bac6c7ae89918d724697d8c45da339f55b61d527c50104e66582803a8e6dcceae31b0d499e471aca460194a011d6b8b94fe2886b8b5a0c242079bfbf09c", 588 | "Proof": { 589 | "proof": "01d6f0f91660fc391573f819587ad310bf3da80a2dd4cda113c1fbbec9d1cd88a0e4d783833c4c64bbcb6414688d925af2a4a5845b2e3b6634df5e5d901047d5148f01a1ec61207132162fc851126b07397beeeae6814e8e135e4a03ca8bd346568d23c85e0a64b9b83de207ff8d17674863bde02eaeb0fa16d05a9b44cb01654bd0806d", 590 | "r": "001caeef2365ebf9c1edbdb24825e5735614aaf644f03458a1f30c90229f8068bec0ae930eef110e98ea1cbc6d849b4c9ca5b7a970d0320ba5f4f95f5cd4f501d720" 591 | } 592 | } 593 | ] 594 | }, 595 | { 596 | "groupDST": "48617368546f47726f75702d564f50524630392d020005", 597 | "hash": "SHA512", 598 | "keyInfo": "74657374206b6579", 599 | "mode": 2, 600 | "pkSm": "03013db33ba3e475e5696be39d99fd9ffd96452c4fe78df4eef57230979431f734aceefad464c4885b99313a775f5f4524db3c8404400169fd139ca053b75c6d7e848e", 601 | "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", 602 | "skSm": "01f8c6fb8fb3265b70e97870f942f7de645132f694c139446ab758871bdf0058a2a83b4679fc9fc1c6a0936b2f2e8e079a75b20688d4fe828e74d16bfc625528992e", 603 | "suiteID": 5, 604 | "suiteName": "OPRF(P-521, SHA-512)", 605 | "vectors": [ 606 | { 607 | "Batch": 1, 608 | "Blind": "00dc9f04fb076cffe7d179d692a05b0c2210b6c008c1062c1e54514ef654eefc0519dd1867571c9d518e305fdf463231b6ec8b7498e2122a7a6033b6261a1696a773", 609 | "BlindedElement": "03009a6b363627cbc6ba5f241493a724a69ca7a85f203fb5100bde9f36ee57e3fe75a5b41d10c6d9a2799fcee9cd1f4bcd730cb8d9be7aa5e8a7a488b6ae3004afd2a8", 610 | "EvaluationElement": "03009ae81470679a5c5733401488cc6648a522a208e698e9879307e794158ce508e08a50556ec66a055f05f5d5276231258d95d004a49a3080372f3e9d2075753c010f", 611 | "Info": "7465737420696e666f", 612 | "Input": "00", 613 | "Output": "70ad5e29de9f6e35f16afab3b97c1b26fdf6be0da60aff48a99980ddb8d7c2d728a8a5d2837179bfddd612712e014c0c9b9596cbb5a6ee6761c564dbb8921b4e", 614 | "Proof": { 615 | "proof": "0122e18e5c3e2242617098cf1d6b5868d66fb4f4816ddd3769e5b7f326f0ea3d79cd8b8b87be31c1acb9559a2ffdd13f4af7ee143e5081a2db996f3a7d2da83973e100f559c9dbb7b16df3d5f609d2f8f2184e9e204e6444db72608e4816beee31c859dfabfe137bc3bae06947d767cd8cb6ad634134cf6faec24bc8341d51b584872ae1", 616 | "r": "00c07a53a1c70f44466b3861be4f8ef48c2bb1aec2e478e341c467fd4a2638aeca63ed6c4bc48d008bca3f36f043e0eb73a44aba77e5e37d5ab1389e09b80a34cfaa" 617 | } 618 | }, 619 | { 620 | "Batch": 1, 621 | "Blind": "0085ad3fc8c91caec3bd7699591b10d6da93877a470e128f38030627dffcbbf1f576b38677841fc47af778f9d85ac9bce6279388ddf4607e295e64cea6f4f95078b8", 622 | "BlindedElement": "0201eeaedeb3692cc0ecfeacdf9cab61947eb0d23bbfe2e1fbf8de0907f9410b6089d060d3af63411fd81b9d588fa2c48bf8ec63ec66c14b86d237124042ca83fc99e1", 623 | "EvaluationElement": "0300886138e19945036ebe6f4195cf9f688d9e5a7c89597dfeea6e0e5fcf4b53a9dfa280c8409b6abe8051e3394279d0b669440af8a27aad169de10446eb88e09d6801", 624 | "Info": "7465737420696e666f", 625 | "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 626 | "Output": "ee2d8e42030da6283ab59a11f41a171c65e208306e00c6f965a56c10f33bf0942bb38b7e1a33c70bc3542d27220379cbcef8b91898c720be948e9db214a14bb9", 627 | "Proof": { 628 | "proof": "01cb4d8a14eeb472ee3e2fbfe3f6d49f3654cfe6238254bea17ce30848ca934e20e82c2a33d140de55b24fab047811e20b46f6dcaf3c0945c802e8489131618617ef001e233aa2c3d674bce7465278faab6300d4f6b5463e1597d74e2a69865bb0681604f9210edbf50bf341d836dc09af85e603b4b2b8b55c90c2efd979a4e312b653e1", 629 | "r": "003a09eed29f2e7f8950d766270d390db7a53b8080b89cb9e024e1e008d83bd90e94f501281b6b49c351c959348b3a65f24c6f74e77a62905a6d3e4b0b10600a7cbc" 630 | } 631 | }, 632 | { 633 | "Batch": 2, 634 | "Blind": "019dd87ebabccec2627d4006b698d9ba57f6e207c989448d39fe0431e60c9a9a4110596d5a16fa6cdf3f66467524f295b5dc8f3492c6da02dd7387bd1dc40065b232,00adaeeed48a6f9a8fb57640c3bff88d3ab3cc52ef969f02beaba2c6e32c2f37baaf4ee9c691833dc081e2a0fb6ff636525457a21c1fc56bf3514635ac7fb8618f73", 635 | "BlindedElement": "030170a994d40e8517aed3a7efae01b650dc131c1ae07158f02ad70d211348a4b328add9d17e93d2e747dd8bc6960a5ab3bec6a9a29f793bf5663d0ab108b5f84fa751,03011045c5ea9567626cf6d2baa158830b035e66c249df4967bbbf917e64bf27e4ec49623704a7c621b32f05e1c7bf1b89960c82d4203c4efa6a1056e083be789d017f", 636 | "EvaluationElement": "0300fadd09cc84c7e91c2173be0e65ee3c1b6ef98cf0cdd757ec432f12f13b5d457edc0311d61ddd831f8becd5231bdc492e92c9d0c103e55ea516ee00fe64c10d0e8e,03005941fb7eabc6353a5c2e4a6b284b7b8ee8da6c4435af6c4c472195bb0deb44e7dc215299c7fe38feafa2b0a1a1db7dd3090c5ce8171247f647da20e04acef8164d", 637 | "Info": "7465737420696e666f", 638 | "Input": "00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", 639 | "Output": "70ad5e29de9f6e35f16afab3b97c1b26fdf6be0da60aff48a99980ddb8d7c2d728a8a5d2837179bfddd612712e014c0c9b9596cbb5a6ee6761c564dbb8921b4e,ee2d8e42030da6283ab59a11f41a171c65e208306e00c6f965a56c10f33bf0942bb38b7e1a33c70bc3542d27220379cbcef8b91898c720be948e9db214a14bb9", 640 | "Proof": { 641 | "proof": "0186deebd9e2db71ca43bcb57311371390c2d04ac9c3189e155f10c9c548f6f22c051d38203493176e8392ef405c783759c735cb6f7219636c140cb2dc070a1158c001bfdb12f34e00a582e22e1eb2fbdebcffe4b6e0818de5c50451617213e1ab20fa5392eb3b535206140a2619732c012d5f331a615755f5397feb9e1fb16d1320d20d", 642 | "r": "010a82559ee5e4ba79c390c4033405e3f792bc49daa905c694707e7e0191104b34d68c7cc81c2e392da60b838eadf434b693d9b4f7c7beb31e37008156656c19382b" 643 | } 644 | } 645 | ] 646 | } 647 | ] 648 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------