|
1 | 1 | from nacl.signing import SigningKey, VerifyKey
|
2 |
| -import nacl.bindings as salt |
| 2 | +import nacl.bindings as sodium |
3 | 3 | from nacl.utils import random
|
4 | 4 | import nacl.hash
|
5 | 5 | from hashlib import blake2b
|
|
16 | 16 |
|
17 | 17 | # This seems a bit weird, but: sodium's sk-to-curve uses the same private key scalar (a) for
|
18 | 18 | # both curves, so getting `a` for the curve25519 implicitly gives us the ed25519 `a` as well.
|
19 |
| - a = salt.crypto_sign_ed25519_sk_to_curve25519(s.encode() + A) |
| 19 | + a = sodium.crypto_sign_ed25519_sk_to_curve25519(s.encode() + A) |
20 | 20 |
|
21 | 21 | A_xpk = s.to_curve25519_private_key().public_key.encode() # session id is 05 + this
|
22 | 22 |
|
23 |
| - assert salt.crypto_scalarmult_ed25519_base_noclamp(a) == A |
24 |
| - assert salt.crypto_scalarmult_base(a) == A_xpk |
25 |
| - assert salt.crypto_core_ed25519_is_valid_point(A) |
| 23 | + assert sodium.crypto_scalarmult_ed25519_base_noclamp(a) == A |
| 24 | + assert sodium.crypto_scalarmult_base(a) == A_xpk |
| 25 | + assert sodium.crypto_core_ed25519_is_valid_point(A) |
26 | 26 |
|
27 | 27 | server_pubkey = random(32)
|
28 |
| - k = salt.crypto_core_ed25519_scalar_reduce(nacl.hash.generichash(server_pubkey, digest_size=64)) |
| 28 | + k = sodium.crypto_core_ed25519_scalar_reduce( |
| 29 | + nacl.hash.generichash(server_pubkey, digest_size=64) |
| 30 | + ) |
29 | 31 |
|
30 |
| - ka = salt.crypto_core_ed25519_scalar_mul(k, a) |
| 32 | + ka = sodium.crypto_core_ed25519_scalar_mul(k, a) |
31 | 33 | # kA will be my blinded pubkey visible from my posts, with '15' prepended, and is an *Ed*
|
32 | 34 | # pubkey, not an X pubkey.
|
33 |
| - kA = salt.crypto_scalarmult_ed25519_noclamp(k, A) |
| 35 | + kA = sodium.crypto_scalarmult_ed25519_noclamp(k, A) |
34 | 36 |
|
35 |
| - assert salt.crypto_scalarmult_ed25519_base_noclamp(ka) == kA |
36 |
| - assert salt.crypto_core_ed25519_is_valid_point(kA) |
| 37 | + assert sodium.crypto_scalarmult_ed25519_base_noclamp(ka) == kA |
| 38 | + assert sodium.crypto_core_ed25519_is_valid_point(kA) |
37 | 39 |
|
38 | 40 | #############################
|
39 | 41 | # Signing (e.g. for X-SOGS-*) with a blinded keypair ka/kA
|
|
45 | 47 | # which gives us a bog standard Ed25519 that can be verified using the kA pubkey with standard
|
46 | 48 | # verification code.
|
47 | 49 | message_to_sign = b'omg happy days'
|
48 |
| - H_rh = salt.crypto_hash_sha512(s.encode())[32:] |
49 |
| - r = salt.crypto_core_ed25519_scalar_reduce(salt.crypto_hash_sha512(H_rh + kA + message_to_sign)) |
50 |
| - sig_R = salt.crypto_scalarmult_ed25519_base_noclamp(r) |
51 |
| - HRAM = salt.crypto_core_ed25519_scalar_reduce( |
52 |
| - salt.crypto_hash_sha512(sig_R + kA + message_to_sign) |
| 50 | + H_rh = sodium.crypto_hash_sha512(s.encode())[32:] |
| 51 | + r = sodium.crypto_core_ed25519_scalar_reduce( |
| 52 | + sodium.crypto_hash_sha512(H_rh + kA + message_to_sign) |
| 53 | + ) |
| 54 | + sig_R = sodium.crypto_scalarmult_ed25519_base_noclamp(r) |
| 55 | + HRAM = sodium.crypto_core_ed25519_scalar_reduce( |
| 56 | + sodium.crypto_hash_sha512(sig_R + kA + message_to_sign) |
| 57 | + ) |
| 58 | + sig_s = sodium.crypto_core_ed25519_scalar_add( |
| 59 | + r, sodium.crypto_core_ed25519_scalar_mul(HRAM, ka) |
53 | 60 | )
|
54 |
| - sig_s = salt.crypto_core_ed25519_scalar_add(r, salt.crypto_core_ed25519_scalar_mul(HRAM, ka)) |
55 | 61 | full_sig = sig_R + sig_s
|
56 | 62 |
|
57 | 63 | assert VerifyKey(kA).verify(message_to_sign, full_sig)
|
|
62 | 68 | # Our user A above wants to send a SOGS DM to another user B:
|
63 | 69 | s2 = SigningKey.generate()
|
64 | 70 | B = s2.verify_key.encode()
|
65 |
| - b = salt.crypto_sign_ed25519_sk_to_curve25519(s2.encode() + B) |
| 71 | + b = sodium.crypto_sign_ed25519_sk_to_curve25519(s2.encode() + B) |
66 | 72 |
|
67 | 73 | # with blinded keys:
|
68 |
| - kb = salt.crypto_core_ed25519_scalar_mul(k, b) |
69 |
| - kB = salt.crypto_scalarmult_ed25519_noclamp(k, B) |
| 74 | + kb = sodium.crypto_core_ed25519_scalar_mul(k, b) |
| 75 | + kB = sodium.crypto_scalarmult_ed25519_noclamp(k, B) |
70 | 76 |
|
71 | 77 | B_xpk = s2.to_curve25519_private_key().public_key.encode()
|
72 | 78 |
|
73 |
| - assert salt.crypto_scalarmult_ed25519_base_noclamp(kb) == kB |
74 |
| - assert salt.crypto_core_ed25519_is_valid_point(kB) |
| 79 | + assert sodium.crypto_scalarmult_ed25519_base_noclamp(kb) == kB |
| 80 | + assert sodium.crypto_core_ed25519_is_valid_point(kB) |
75 | 81 |
|
76 | 82 | #############################
|
77 | 83 | # Finding friends:
|
78 | 84 |
|
79 | 85 | # For example (in reality this would come directly from the known session id):
|
80 |
| - friend_xpk = salt.crypto_sign_ed25519_pk_to_curve25519(A) |
| 86 | + friend_xpk = sodium.crypto_sign_ed25519_pk_to_curve25519(A) |
81 | 87 |
|
82 | 88 | # From the session id (ignoring 05 prefix) we have two possible ed25519 pubkeys; the first is
|
83 | 89 | # the positive (which is what Signal's XEd25519 conversion always uses):
|
84 | 90 | pk1 = xed25519.pubkey(friend_xpk)
|
85 | 91 |
|
86 | 92 | # Blind it:
|
87 |
| - pk1 = salt.crypto_scalarmult_ed25519_noclamp(k, pk1) |
| 93 | + pk1 = sodium.crypto_scalarmult_ed25519_noclamp(k, pk1) |
88 | 94 |
|
89 | 95 | # For the negative, what we're going to get out of the above is simply the negative of pk1, so
|
90 | 96 | # flip the sign bit to get pk2:
|
|
102 | 108 | # around gives the same result as the shortcut:
|
103 | 109 | pk2_alt = xed25519.pubkey(friend_xpk)
|
104 | 110 | pk2_alt = pk2_alt[0:31] + bytes([pk2_alt[31] | 0b1000_0000])
|
105 |
| - pk2_alt = salt.crypto_scalarmult_ed25519_noclamp(k, pk2_alt) |
| 111 | + pk2_alt = sodium.crypto_scalarmult_ed25519_noclamp(k, pk2_alt) |
106 | 112 | assert pk2 == pk2_alt
|
107 | 113 |
|
108 | 114 | # Now, if this is really my friend, his blinded key will equal one of these blinded keys:
|
|
128 | 134 | # BLAKE2b(b kA || kA || kB)
|
129 | 135 | #
|
130 | 136 | enc_key = blake2b(
|
131 |
| - salt.crypto_scalarmult_ed25519_noclamp(a, kB) + kA + kB, digest_size=32 |
| 137 | + sodium.crypto_scalarmult_ed25519_noclamp(a, kB) + kA + kB, digest_size=32 |
132 | 138 | ).digest()
|
133 | 139 |
|
134 | 140 | # Inner data: msg || A (i.e. the sender's ed25519 master pubkey, *not* kA blinded pubkey)
|
135 | 141 | plaintext = msg.encode() + A
|
136 | 142 |
|
137 | 143 | # Encrypt using xchacha20-poly1305
|
138 | 144 | nonce = random(24)
|
139 |
| - ciphertext = salt.crypto_aead_xchacha20poly1305_ietf_encrypt( |
| 145 | + ciphertext = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt( |
140 | 146 | plaintext, aad=None, nonce=nonce, key=enc_key
|
141 | 147 | )
|
142 | 148 |
|
|
151 | 157 |
|
152 | 158 | # Calculate the shared encryption key (see above)
|
153 | 159 | dec_key = blake2b(
|
154 |
| - salt.crypto_scalarmult_ed25519_noclamp(b, kA) + kA + kB, digest_size=32 |
| 160 | + sodium.crypto_scalarmult_ed25519_noclamp(b, kA) + kA + kB, digest_size=32 |
155 | 161 | ).digest()
|
156 | 162 |
|
157 | 163 | assert enc_key == dec_key
|
|
162 | 168 | assert v == 0x00 # Make sure our encryption version is okay
|
163 | 169 |
|
164 | 170 | # Decrypt
|
165 |
| - plaintext = salt.crypto_aead_xchacha20poly1305_ietf_decrypt(ct, aad=None, nonce=nc, key=dec_key) |
| 171 | + plaintext = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt( |
| 172 | + ct, aad=None, nonce=nc, key=dec_key |
| 173 | + ) |
166 | 174 |
|
167 | 175 | assert len(plaintext) > 32
|
168 | 176 |
|
169 | 177 | # Split up: the last 32 bytes are the sender's *unblinded* ed25519 key
|
170 | 178 | message, sender_edpk = plaintext[:-32], plaintext[-32:]
|
171 | 179 |
|
172 | 180 | # Verify that the inner sender_edpk (A) yields the same outer kA we got with the message
|
173 |
| - assert kA == salt.crypto_scalarmult_ed25519_noclamp(k, sender_edpk) |
| 181 | + assert kA == sodium.crypto_scalarmult_ed25519_noclamp(k, sender_edpk) |
174 | 182 |
|
175 | 183 | message = message.decode() # utf-8 bytes back to str
|
176 | 184 |
|
177 |
| - sender_session_id = '05' + salt.crypto_sign_ed25519_pk_to_curve25519(sender_edpk).hex() |
| 185 | + sender_session_id = '05' + sodium.crypto_sign_ed25519_pk_to_curve25519(sender_edpk).hex() |
178 | 186 |
|
179 | 187 | assert message == msg
|
180 | 188 | assert sender_edpk == A
|
|
0 commit comments