Skip to content

Commit 2d1a1e8

Browse files
authored
Update STRANDLOCK_PROTOCOL.md
1 parent 2bf71e8 commit 2d1a1e8

File tree

1 file changed

+88
-35
lines changed

1 file changed

+88
-35
lines changed

STRANDLOCK_PROTOCOL.md

Lines changed: 88 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,11 @@ Answers don't have to be uncrackable forever, just uncrackable for a reasonable
230230
We highly recommend implementations to only allow user to set a `8+ character` answer, and to check the entropy of provided answer (is all lowercase, is all uppercase, is only digits, etc), and to warn (or prevent) the user from continuing.
231231

232232
Even though the `question` is encrypted, an active *Man-in-the-middle* adversary **can still retrieve it**. The verification would fail, but the adversary would have the `question` plaintext.
233+
233234
This is acceptable, as the purpose of encrypting `SMP` process is to hide *metadata* against **passive** adversaries, not an **active** adversary.
234235

235236
The question **must not** contain any senstive data. And it must not contain any hints to the answer.
237+
236238
Implementations **must** check `answer` and `question` in initation stage, to ensure neither contain the other.
237239

238240

@@ -278,77 +280,128 @@ PFS_PAYLOAD = PFS_TYPE || ALICE_NEW_STRAND_NONCE || PUBLICKEYS_HASHCHAIN_SIGNATU
278280
#### 4.3. PFS Notes:
279281
Even though the use of hash-chains and signatures may appear redundant here, as we already wrap everything in `xChaCha20Poly1305`, and we encrypt its nonce, which serves as a replay protection, and tamper protection, the use of hash-chains here ensures that even if `xChaCha20Poly1305` is broken, PFS keys cannot be replayed, nor tampered with.
280282

283+
The reason we opted for a hash-chain based design, instead of a simple counter, is to ensure metadata of how many key rotations occured never gets leaked, even when `xChaCha20Poly1305` is broken.
284+
Even if `Alice's` or `Bob's` endpoint get compromised, no metadata of how many key rotation occured could be recovered.
285+
286+
### 5. Messaging (`MSGS`)
287+
#### 5.1 OTP Batch Generation (`Alice`)
288+
`Alice` uses `Bob's` `ML-KEM-1024` public-key to generates many shared secrets *in chunks*, concatenating them until their total size reaches (or exceeds) `OTP_PAD_SIZE` size (default `11264 bytes`).
289+
290+
`Alice` does the same thing with `Classic-McEliece-8192128`.
291+
292+
`Alice` generates random bytes of `OTP_PAD_SIZE` size, these are called `xChaCha_shared_secrets`
293+
294+
`Alice` signs all `KEM's` ciphertexts using her signing private key.
295+
296+
`Alice` generates new `ALICE_NEW_STRAND_NONCE` as always, and bundles in `OTP BATCH` type of `0x00`, and constructs the `MSG OTP request`:
297+
```
298+
MSG_REQUEST = MSG_TYPE || 0x00 || ALICE_NEW_STRAND_NONCE || OTP_BATCH_SIGNATURE || ML_KEM_1024_CIPHERTEXT || CLASSIC_MCELIESE_819_CIPHERTEXT || XCHACHA_SHARED_SECRETS
299+
```
300+
301+
`Alice` encrypts the payload using her `ALICE_STRAND_KEY`, and her `ALICE_NEXT_STRAND_NONCE`, and sends payload to Bob.
302+
303+
`Alice` then `XORs` `ML-KEM-1024's` shared secrets with `Classic-McEliece's` shared secrets, then she `XORs` the result with `XChaCha` secrets to produce the final result, the `ALICE_OTP_PADS`.
304+
305+
The first `32 bytes` of pads become the new `ALICE_STRAND_KEY`, and is truncated from `ALICE_OTP_PADS`.
306+
307+
She saves her new `ALICE_STRAND_KEY`, and `ALICE_OTP_PADS`.
308+
309+
These `ALICE_OTP_PADS` will be used to encrypt messages sent from `Alice` to `Bob`.
281310

282-
5. Messaging (MSGS)
283-
5.1 OTP Batch Generation
311+
#### 5.2 New OTP Batch Processing (`Bob`)
312+
`Bob` decrypts the `xChaCha20Poly1305` wrapping, using `ALICE_STRAND_KEY` as key, and `ALICE_NEXT_STRAND_NONCE` as nonce.
313+
`Bob` checks if request type is `MSG_TYPE`, then `Bob` checks the next byte if it's `0x00` (`New OTP Batch`), or `0x01` (New `OTP Message`)
284314

285-
Alice checks if message length + OTP_SIZE_LENGTH ≤ available pad space:
315+
If its a `New OTP Batch`, `Bob` verifies `OTP_BATCH_SIGNATURE` against `ML_KEM_1024_CIPHERTEXT` + `CLASSIC_MCELIESE_819_CIPHERTEXT`
316+
If invalid, abort, by skipping the request. Implementations are recommended to log and display the error to the user, so that they may be notified that someone attempted to MiTM against their conversation.
286317

287-
If not, she generates a new OTP batch.
318+
If valid, `Bob` decapsulate `ML_KEM_1024_CIPHERTEXT` and `CLASSIC_MCELIESE_819_CIPHERTEXT` shared secrets.
319+
`Bob` then follows same exact forumla `Alice` did with her keys, `XOR-ing` `ML-KEM-1024's` shared secrets with `Classic-McEliece's` shared secrets, then she `XORs` the result with `XChaCha` secrets to produce the final result, the `ALICE_OTP_PADS`.
288320

289-
Alice generates ML-KEM-1024 shared secrets in chunks until OTP_PAD_SIZE is reached.
321+
The first `32 bytes` of pads become the new `ALICE_STRAND_KEY`, and are truncated from `ALICE_OTP_PADS`
290322

291-
Alice generates Classic McEliece shared secrets similarly.
323+
`Bob` updates `ALICE_NEXT_STRAND_NONCE` to be `ALICE_NEW_STRAND_NONCE`, then saves.
292324

293-
Alice generates OTP_PAD_SIZE of random bytes for XChaCha shared secrets.
325+
These pads will be used to decrypt messages coming from `Alice`.
294326

295-
Alice generates a new hash chain seed MESSAGE_HASH_CHAIN_LEN (64 bytes).
327+
#### 5.3 Message Sending
328+
`Alice` first `UTF-8` encodes her `message`, then she checks if (`message` size + `OTP_SIZE_LENGTH` size) ≤ available `ALICE_OTP_PADS`.
296329

297-
Alice signs all ciphertexts using her signing key.
330+
If theres not enough pads for the message, she generates and sends a new OTP batch (see section 5.1 & 5.2).
298331

299-
Alice generates ALICE_NEW_STRAND_NONCE and prepares the payload:
332+
`Alice` performs `OTP` encryption on her message, using her pads as key.
333+
`OTP` encryption uses 2 models of padding, depending on the message's size.
334+
- If `message` length < `OTP_MAX_BUCKET` - `OTP_SIZE_LENGTH` (default `2 bytes`), pad to `OTP_MAX_BUCKET` (default `64 bytes`).
300335

301-
MSG_TYPE || 0x00 || ALICE_NEW_STRAND_NONCE || HASH_CHAIN_SEED || OTP_BATCH_SIGNATURE || ML_KEM_1024_CIPHERTEXT || CLASSIC_MCELIESE_819_CIPHERTEXT || XCHACHA_SHARED_SECRETS
336+
- If `message` length > `OTP_MAX_BUCKET`, pad randomly up to `OTP_MAX_RANDOM_PAD` (default `16 bytes`).
302337

338+
**All messages** are prefixed with a padding length field `OTP_SIZE_LENGTH` (`2 bytes` in `big-endian` format).
303339

304-
Alice encrypts using ALICE_STRAND_KEY and sends.
340+
After padding, the new padded message plaintext, is `OTP` encrypted.
341+
342+
After encryption is complete, the `OTP pads` used for encryption **must** be truncated immeditely. Truncate pads *before* sending on wire, and even if request fail, never re-use nor undo truncation.
343+
344+
`Alice`, as always, generates a new `ALICE_NEW_STRAND_NONCE`, and prepares the request data:
345+
```
346+
MSG_DATA = MSG_TYPE || 0x01 || ALICE_NEW_STRAND_NONCE || MESSAGE_ENCRYPTED
347+
```
305348

306-
Alice XORs ML-KEM, McEliece, and XChaCha secrets to produce OTP pads.
349+
`0x01` indicates this is a MSG of type `New OTP Message`.
307350

308-
The first 32 bytes of pads become the new ALICE_STRAND_KEY.
351+
`Alice` then encrypts `MSG_DATA` with `xChaCha20Poly1305` using `ALICE_STRAND_KEY`, and `ALICE_NEXT_STRAND_NONCE` as nonce, then sends it to `Bob`.
309352

310-
5.2 Message Sending
353+
#### 5.3 Receiving Messages (Bob)
311354

312-
Alice UTF-8 encodes the message.
355+
`Bob` decrypts the `xChaCha20Poly1305` wrapping, using `ALICE_STRAND_KEY` as key, and `ALICE_NEXT_STRAND_NONCE` as nonce.
356+
`Bob` checks if request type is `MSG_TYPE`, then `Bob` checks the next byte if it's `0x00` (`New OTP Batch`), or `0x01` (New `OTP Message`)
313357

314-
Alice OTP encrypts the message with generated pads:
358+
if is `New OTP Message`, `Bob` decrypt the encrypted message with `ALICE_OTP_PADS`.
359+
`Bob` then reads the padding prefix of message, and removes the padding, then he removes the padding prefix.
315360

316-
If length < OTP_MAX_BUCKET - OTP_SIZE_LENGTH, pad to OTP_MAX_BUCKET.
361+
`Bob` then `UTF-8` decodes the message, and displays it.
317362

318-
If length > OTP_MAX_BUCKET, pad randomly up to OTP_MAX_RANDOM_PAD.
363+
#### 5.4. MSGs Notes
364+
The reason we don't use a hash-chain like in `PFS`, is because the `xChaCha20Poly1305` wrapping `strand` scheme provides tampering and replay protection. And even if `xChaCha20Poly1305` is broken, messages that get tampered or replayed with, `Bob` would notice as the message content would be inparsable junk (at UTF-8 decoding step).
319365

320-
Prefix message with padding length (OTP_SIZE_LENGTH, 2 bytes, big-endian).
366+
Encrypting with `OTP` ensures that even if `xChaCha20Poly1305` is broken, and even if one `KEM` is broken, messages remain uncompromised.
321367

322-
Advance hash chain: SHA3-512(previous_hash_chain || encrypted_message)
368+
Even if `xChaCha20Poly1305` is broken, and 2 KEMs broken, messages remain uncompromised if the `OTP batch` request was not intercepted.
323369

324-
Generate ALICE_NEW_STRAND_NONCE.
370+
If `OTP batch` request was not intercepted, messages become true OTPs.
325371

326-
Prepare payload:
372+
If `OTP batch` request is intercepted, `OTP` messages inherits the combined security of `xChaCha20Poly1305`, `ML-KEM-1024`, `Classic-McEliece-8192128`, and even the entropy of `SMP answer`.
327373

328-
MSG_TYPE || 0x01 || ALICE_NEW_STRAND_NONCE || HASH_CHAIN || MESSAGE_ENCRYPTED
374+
Additionally, using `OTPs` here provides an odd protection to `xChaCha20Poly1305`, by making "`known plaintext oracles`" attacks impossible, significantly bolstering `xChaCha20Poly1305` security.
329375

376+
Additionally, using `OTPs` makes nonce reuses non-fatal, as we already encrypt nonces, the only possible way for an adversary on wire to know a nonce reuse occured, is if user types same message, with same key, with same nonce.
330377

331-
Encrypt with ALICE_STRAND_KEY using ALICE_NEXT_STRAND_NONCE and send.
378+
Even if a ranodmly generated nonce was repeated, and the user does such unlikely thing, the fact plaintext is OTP encrypted, means the adversary would still see different ciphertexts. Making it impossible for them to know if a nonce reuse occured.
379+
Obviously, this does not mean a nonce reuse wouldn't occur, it just means an adversary wouldn't be able to exploit the fact because to him, is invisible random blobs.
332380

333-
5.3 Receiving Messages (Bob)
381+
However, implementations **MUST** still use cryptographically secure `CSPRNG` for nonce generation nonetheless. This protection property only protects against the off chance a `CSPRNG` generated nonce gets duplicated.
334382

335-
Decrypt payload using ALICE_STRAND_KEY and nonce.
336383

337-
Verify hash chain.
338384

339-
Decrypt message with OTP pads from OTP batch.
385+
### 7. Design choices (Questions & Answers)
386+
**Question**:
387+
Why did you opt for `xChaCha20Poly1305` over `ChaCha20Poly1305` if you're encrypting the nonce ?
340388

341-
Display message.
389+
**Answer**:
390+
Even though we do encrypt the nonce, encrypting the nonce does not prevent nonce-reuse attacks, it only hides the fact they occured.
391+
`xChaCha20Poly1305` nonces are a lot larger than `ChaCha20Poly1305` nonces, which means the probablity of a collision is tiny.
342392

343-
6. Argon2id Parameters for SMP
393+
**Question**
394+
Why did you opt for `OTP` encryption, if you're already using `xChaCha20Poly1305`, why not just use `xChaCha` alone ?
344395

345-
Memory: 3GB (3072 * 1024 KB)
396+
**Answer**
397+
OTP encryption provides unique properties, and when combined with a classical symmetric algorithm, both algorithms benefit each other. On one hand, `xChaCha20Poly1305` encryption of `OTP`-encrypted messages, provides protection against `OTP` implementation errors, on the other hand, using `OTP`-encrypted messages as plaintext to `xChaCha20Poly1305` destroys one of cryptographors favorite oracles `known plaintext oracle`, which removes a whole class of attacks.
346398

347-
Iterations: 50,000
399+
Additionally, if the `OTP Batch` exchange was not intercepted nor logged, OTPs become unbreakable.
348400

349-
Output: 64 bytes
401+
**Question**
402+
Why do you generate random bytes of X size, then hash them with `SHA3_512` and truncate them back to X size ?
350403

351-
7. Design Notes
404+
**Answer**
352405

353406
Nonce hiding: Prevents metadata leakage and hides rare nonce collisions.
354407

0 commit comments

Comments
 (0)