Skip to content
This repository was archived by the owner on Mar 10, 2026. It is now read-only.

Commit 3f196be

Browse files
authored
Update PROTOCOL.md
1 parent 01c5fc9 commit 3f196be

File tree

1 file changed

+53
-3
lines changed

1 file changed

+53
-3
lines changed

PROTOCOL.md

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ Perfect Forward Secrecy (PFS) ensure that if a ML-KEM-1024 keypair was compromis
236236
### 5.1. Assumptions
237237
`Alice` wants to generate / rotate ephemeral `ML-KEM-1024` (`Kyber1024`) keys with `Bob`.
238238
`Alice` and `Bob` have verified each other's `per-contact` keys using `SMP`
239+
`Alice` is the initiator
239240

240241
### 5.2. PFS Exchange
241242
`Alice` generates new ephemeral `ML-KEM-1024` keypair and signs them with her `per-contact` keys for `Bob`
@@ -280,12 +281,61 @@ Then `Bob` does the same as `Alice` did, generating his own `hash chain` seed if
280281

281282
Now `Alice` and `Bob` both have each other ephemeral public keys, Have successfully rotated their ephemeral keys.
282283

283-
### 5.3. Security notes
284-
We use `hash chain`s for replay protection.
285-
We also use `per-contact` keys for tampering and spoofing protection.
284+
### 5.3. PFS rotation counters
285+
`Alice` stores a `rotate_at` and `rotation_counter` variables alongside her ephemeral keys, locally.
286+
287+
Those counters will be used in `6. Messages` to determine when it is time to rotate ephemeral keys.
288+
289+
### 5.4. Security notes
290+
We use `hash chain`s for replay protection.
291+
292+
The reason we opted for a `hash chain` instead of a simple `replay_counter`, is to hide the crucial metadata of how many key rotations happened and in which order. This help us later on build plausible deniability
293+
294+
We also use `per-contact` keys (`ML-DSA-87`) for tampering and spoofing detection & protection.
286295

287296

288297
## 6. Messages
298+
Coldwire uses One-Time-Pads (OTP) for encrypting message content.
299+
Pads are shared using `PFS` ephemeral `Kyber1024` keys
300+
301+
### 6.1. Assumptions
302+
`Alice` wants to send a
303+
```python
304+
"Hello, World!"
305+
```
306+
message to `Bob`
307+
308+
`Alice` and `Bob` are already `SMP` verified, and have exchanged their ephemeral keys
309+
310+
### 6.2. Message prepartions
311+
Before `Alice` sends her message to `Bob`, she checks if `rotate_at` equals the `rotation_counter`, if positive, she first rotates her ephemeral keys with `Bob` (see `5. Perfect Forward Secrecy` for more details).
312+
313+
If negative, she calculates if she has enough pads for the `message`, the `64 byte` `hash-chain`, `padding`, and `padding_length` headers, this is calculated like:
314+
```python
315+
OTP_PADDING_LIMIT = 1024
316+
OTP_PADDING_LENGTH = 2
317+
318+
message = "Hello, World!".encode("utf-8")
319+
320+
next_hash_chain = sha3_512(last_hash_chain + message)
321+
322+
message = next_hash_chain + message
323+
324+
message_otp_padding_length = max(0, OTP_PADDING_LIMIT - OTP_PADDING_LENGTH - len(message))
325+
alice_pads = b"" # Empty
326+
327+
# If length of our message, UTF-8 encoded, with OTP padding length is greater than our available pads
328+
if len(message) + OTP_PADDING_LENGTH + message_otp_padding_length > len(alice_pads):
329+
generate_pads()
330+
331+
```
332+
333+
`OTP_PADDING_LENGTH` is `2 bytes`, which can hold up to `65535 bytes` of `padding`.
334+
If `message` length is greater than `OTP_PADDING_LIMIT`, the message is not padded.
335+
336+
Unlike in `5. Perfect Forward Secrecy`, our `hash_chain` here provides both replay protection *and* tampering protection
337+
338+
289339

290340

291341
## WORK-IN-PROGRESS

0 commit comments

Comments
 (0)