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