PRId
A Pseudonymous Relationship Identifier is represented by the PRId object type.
Serialization
PRId object serialization MUST conform to the following Avro schema:
{
"namespace": "org.dsnp",
"name": "PRId",
"type": "fixed",
"size": 8,
"doc": "Pseudonymous Relationship Identifier"
}
Generation
PRIds are generated cryptographically to represent a relationship from one user to another within a specified context, in a privacy-preserving manner.
Contexts
The following context values are currently defined for PRIds. All other values are reserved for future use.
Context Id | Description | Context string |
---|---|---|
0 | Connection | PRIdCtx0 |
Algorithm
In the following section, the Alice to Bob identifier for context C is called PRIdA→B,C, and the corresponding Bob to Alice identifier is called PRIdB→A,C.
A PRId is derived from Alice and Bob’s keyAgreement
key pairs, using a key exchange protocol as follows. To illustrate the cryptographic operations required, the relevant functions from libsodium are noted. Sodium is a stable, fast, free, and cross-platform cryptography library, and supports all encryption algorithms used in the DSNP specification out of the box.
Definitions:
IdA = DSNP User Id of A (little-endian)
IdB = DSNP User Id of B (little-endian)
Algorithm:
- Both Alice and Bob generate an asymmetric key pair for use with X25519 ECIES.
Each uses the Replace User Data Operation to publish their generated public key in
keyAgreementPublicKeys
.
Libsodium | Algorithm |
---|---|
crypto_box_keypair( &a_public, &a_secret); crypto_box_keypair( &b_public, &b_secret); |
(Apublic, Asecret) ← KGF() (Bpublic, Bsecret) ← KGF() |
- When Alice wants to interact with Bob, she looks up Bob’s public key and performs an X25519 Elliptic-curve Diffie-Hellman key exchange operation using her secret key and Bob’s public key, generating a root shared secret.
Libsodium | Algorithm |
---|---|
crypto_box_beforenm( &root_shared_secret, b_public, a_secret); |
RootSharedSecretAB ← ECDH(Bpublic, Asecret) |
- Alice derives a context-specific subkey
CtxSharedSecretA→B
from the shared secretRootSharedSecretAB
as the master key, Bob’s DSNP User Id as the 64-bit key identifier, and the ASCII encoding of the PRId Context string ("PRIdCtx0"
for connections).
Libsodium | Algorithm |
---|---|
crypto_kdf_derive_from_key( ctx_shared_secret, 32, b_user_id, "PRIdCtx0", root_shared_secret); |
CtxSharedSecretA→B,C ← Blake2b256( key = RootSharedSecretAB, message = {}, salt = IdB || {0}, personal = "PRIdCtx0" || {0}) |
- Alice uses Bob’s DSNP User Id to form an 8-byte little-endian message.
Alice encrypts this message using XSalsa20 with the PRId key
CtxSharedSecretA→B,C
and a nonce of her own User Id (little-endian) followed by 16 zero bytes.
Libsodium | Algorithm |
---|---|
char nonce[24] = {0}; int i; for (i = 0; i < 8; i++) { nonce[i] = (user_id_a >> (i*8)) & 0xff; }
|
PRIdA→B,C ← XSalsa20( message = IdB, key = CtxSharedSecretA→B,C, nonce = Padded24BytesLE(IdA) ) |
- Alice adds the generated PRId to the relevant list of PRIds and publishes an updated copy via the Replace User Data Operation.
Similarly, Bob can calculate the same root shared secret RootSharedSecret
using Alicepublic
and Bobsecret
and derive the same PRIdA→B,C
in order to check if it is in Alice’s published PRIds.
Bob can also derive the PRId subkey for Alice’s DSNP User Id and encrypt Alice’s User Id, using his own as the nonce, to generate the Bob-to-Alice PRId (PRIdB→A,C
), and then publish it to his own list, if desired.
If Alice or Bob wants to prove to a third party that their PRIds are in each other’s PRId list, they can provide the third party with their own subkey CtxSharedSecretA→B,C
or CtxSharedSecretB→A,C
.
The third party can repeat the encryption step using Alice and Bob’s User Ids, and check that the output is present in the published set of PRIds. The root shared secret RootSharedSecret
(used as a master key in this algorithm) should not be divulged.
Test Vector
For the following inputs:
Input | Value |
---|---|
Asecret | 0xc9432ed5c0c5c24e8a4ff190619893918b4d1265a67d123895023fa7324b43e0 |
Apublic | 0x0fea2cafabdc83752be36fa5349640da2c828add0a290df13cd2d8173eb2496f |
Bsecret | 0xdc106e1371293ee9536956e1253f43f8941d4a5c4e40f15968d24b75512b6920 |
Bpublic | 0xd0d4eb21db1df63369c147e63b2573816dd4b3fe513e95bf87f7ed1835407e62 |
IdA | 42 |
IdB | 478 |
Context | PRIdCtx0 |
An implementation of the PRId generation algorithm should produce the following outputs:
Output | Value |
---|---|
PRIdA→B | 0xace4d2995b1a829c |
CtxSharedSecretA→B,C | 0x37cb1a870f0c1dce06f5116faf145ac2cf7a2f7d30136be4eea70c324932e6d2 |
PRIdB→A | 0x1a53b02a26503600 |
CtxSharedSecretB→A,C | 0x32c45c49fcfe12f9db60e74fa66416c5a05832c298814d82032a6783a4b1fca0 |