@@ -95,3 +95,124 @@ docker run -ti --rm -p 6502:6502 \
9595 --mount type=bind,source=./secrets,target=/src/secrets \
9696 gfmodules-prs
9797```
98+
99+
100+
101+ # OPRF Testing
102+
103+ This system uses OPRF for pseudonym generation. To test this, there are some available endpoints:
104+
105+ - '/test/oprf/gen_rsa_key' - Generate a new RSA keypair for OPRF (small 1024 bit for testing)
106+ - '/test/oprf/oprf/client' - Emulates a client that generates OPRF information for a given input
107+ - '/test/oprf/oprf/receiver' - Emulates the receiver of the pseudonym and returns diagnostic information
108+
109+ To use this system:
110+
111+ 1 . First, generate a new keypair that will be used for a test organization with ` /test/oprf/gen_rsa_key ` .
112+ ``` shell
113+ POST /test/oprf/gen_rsa_key
114+
115+ 200 OK
116+ {
117+ " private_key_pem" : " -----BEGIN PRIVATE KEY-----\nMIICdwIBA....neDKJ0DsvA5vfpt0=\n-----END PRIVATE KEY-----\n" ,
118+ " public_key_pem" : " -----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqG....uDQIDAQAB\n-----END PUBLIC KEY-----\n" ,
119+ " public_key_kid" : " ajGAx_LKNrJ8vqdahWlSJvOznBi6lnFfSOw72Z4R4uU"
120+ }
121+ ```
122+
123+ 2 . Create a new organization into the key resolver with a POST to ` /keys ` . Add your organization name like
124+ ` ura:12345678 ` , and you can use scope ` test ` for testing. Add the ** PUBLIC key** to the ` public_key ` field.
125+
126+ ``` shell
127+ POST /keys
128+ {
129+ " organization" : " ura:12345678" ,
130+ " scope" : " test" ,
131+ " public_key" : " -----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqG....uDQIDAQAB\n-----END PUBLIC KEY\n" ,
132+ }
133+
134+ 201 Created
135+ ```
136+
137+ 3. Emulate a client wanting to send a pseudonym over to a receiver by calling ` /test/oprf/client` with a JSON body like:
138+
139+ ` ` ` shell
140+ POST /test/oprf/client
141+ {
142+ " personalId" : " testinput-like-a-bsn-number-or-other-id"
143+ }
144+
145+ 200 OK
146+ {
147+ " blinded_input" : " EJU9qVhKNmw_UhCXDN_aVM4GL1DCmpDs8QD5WOdUBCU=" ,
148+ " blind_factor" : " eNf80WNHbImaUNU-kokBr7ocELBjMtHcy0re_RKBPQ8="
149+ }
150+ ` ` `
151+
152+ This returns the ` blinded_input` that must be sent to the receiver, and the ` blind_factor` that must be send to the
153+ receiver after the server has evaluated the blinded input.
154+
155+ 4. Now we can call the " real" OPRF function `/oprf/eval` with the blinded input, the organization name and scope:
156+
157+ ` ` ` shell
158+ POST /oprf/eval
159+ {
160+ " encryptedPersonalId" : " EJU9qVhKNmw_UhCXDN_aVM4GL1DCmpDs8QD5WOdUBCU=" ,
161+ " recipientOrganization" : " ura:12345678" ,
162+ " recipientScope" : " test"
163+ }
164+
165+ 200 OK
166+ {
167+ " jwe" : " eyJraWQiOi....bJUqbbSUIjqiw"
168+ }
169+ ` ` `
170+
171+ At this point we will get back a JWE that contains the evaluated blinded input and
172+ is encrypted with the public key of the organization. At this point, the client is
173+ not able to decrypt this information. It can only forward this to the receiver.
174+
175+ 5. Now emulate the receiver by calling ` /test/oprf/receiver` with a JSON body like:
176+ ` ` ` shell
177+ POST /test/oprf/receiver
178+ {
179+ " blind_factor" : " eNf80WNHbImaUNU-kokBr7ocELBjMtHcy0re_RKBPQ8=" ,
180+ " jwe" : " eyJraWQiOiA...SzZbJUqbbSUIjqiw" ,
181+ " priv_key_pem" : " -----BEGIN RSA PRIVATE KEY-----\nMIICXAIB...oCfe0=\n-----END RSA PRIVATE KEY-----\n"
182+ }
183+ ` ` `
184+
185+ The blind factor is the one returned by the client, the JWE is the one returned by the prs evaluation, and
186+ the private key is returned by the key generation step.
187+
188+ At this point, it will return any diagnostic information about the OPRF process:
189+
190+ ` ` ` json
191+ {
192+ " jwe_data" : " eyJraWQiOiAi...zZbJUqbbSUIjqiw" ,
193+ " priv_key_pem" : " -----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgH6gmpXpdhtiE...UpWRvoCfe0=\n-----END RSA PRIVATE KEY-----\n" ,
194+ " priv_key_kid" : " rNv1O_mXgxF6QEMfaQGvjev7RbT1FG3sJXxxsu_KHbM" ,
195+ " blind_factor" : " eNf80WNHbImaUNU-kokBr7ocELBjMtHcy0re_RKBPQ8=" ,
196+ " jwe" : {
197+ " headers" : {
198+ " kid" : " rNv1O_mXgxF6QEMfaQGvjev7RbT1FG3sJXxxsu_KHbM" ,
199+ " alg" : " RSA-OAEP-256" ,
200+ " enc" : " A256GCM" ,
201+ " cty" : " application/json"
202+ },
203+ " decrypted" : {
204+ " subject" : " pseudonym:eval:-Jpsoeik2058ip20b9Wd-vlwpjkjxRN4IoBrk8Ym2Bg=" ,
205+ " aud" : " ura:12345678" ,
206+ " scope" : " nvi" ,
207+ " version" : " 1.1" ,
208+ " iat" : 1758616285,
209+ " exp" : 1758616585
210+ }
211+ },
212+ " eval_subject" : " -Jpsoeik2058ip20b9Wd-vlwpjkjxRN4IoBrk8Ym2Bg=" ,
213+ " final_pseudonym" : " fDZYIlajLAV3y8fWl1ObFBDmybUEGrR37pDb-5p5pJJGKvvpDvvMdQmYHKqtiQ8tdF4VL3w8nkbssHtOmkjiOg=="
214+ }
215+ ` ` `
216+
217+ The ` final_pseudonym` is the actual pseudonym that can be stored by the receiver. Note that this pseudonym is deterministic
218+ for the same input, organization and scope. However, it is not possible to reverse this into a BSN.
0 commit comments