Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Whitepaper proof read #68

Open
14 of 15 tasks
Tracked by #145 ...
stv0g opened this issue May 20, 2023 · 29 comments · May be fixed by #145
Open
14 of 15 tasks
Tracked by #145 ...

Whitepaper proof read #68

stv0g opened this issue May 20, 2023 · 29 comments · May be fixed by #145

Comments

@stv0g
Copy link
Contributor

stv0g commented May 20, 2023

I've started to work on a Golang implementation of the Rosenpass key exchange1.
While implementing it, I stumbled over some confusing parts in the whitepaper:

Variables / message fields mac, cookie, ini_enc & res_enc

Figure 3 shows the output variables mac and cookie which apparently should be included in a message envelope (Figure 2).
But I find no mention about use of the mac and cookie variable elsewhere in the whitepaper.

Apart from the osk two more output variables are generated after the completion of the handshake: ini_enc & res_enc
What is their purpose?

Message types: Data & CookieReply

Figure 2 shows these two message types. But they are nowhere described or used?

Live session state

Section 2.4.4 covers some variables which are not described and used in the handshake?

  • txkm – Our transmission key
  • txnm – Our transmission nonce
  • sidt – Peer’s session ID (“theirs”)
  • txkt – Peer’s transmission key
  • txnt – Peer’s transmission nonce

Enter live helper

What is the purpose of the enter_live helper function mentioned in section 2.5?
It appears also not to be used?

How are the roles of initiator / responder assigned?

WireGuard has no designated server/client as both sides are treated equal.
How does Rosenpass decide which side initiates the key exchange?

Status

Footnotes

  1. https://github.com/stv0g/go-rosenpass

@koraa koraa changed the title Whitepaper is incomplete? Add whitepaper omissions May 20, 2023
@koraa
Copy link
Member

koraa commented May 22, 2023

The root cause of most of these issues is assuming knowledge of the WireGuard whitepaper1. This is an oversight; if we assume the reader knows the WireGuard whitepaper, we should say so and we shouldn't require users to read the WG whitepaper in the first place.

The other thing to know is that Rosenpass is structured like WireGuard -- it is designed to act like a VPN protocol but in practice we just use it to send a key to WireGuard; this leads to some oddities like the present but barely-used live transport data feature.

  • mac, cookie -- Message integrity checking and proof-of-ip-ownership. See the WireGuard whitepaper section 5.4.4; Rosenpass whitepaper2 figure 3 and Envelope::seal in the code. Rosenpass currently does not implement the proof-of-ip-ownership mechanism and always leaves the cookie-field empty
  • ini_enc, res_enc -- These should be called txki and txkr (transmission key receiver/responder), which is another oversight. They are used to send transport data. The rosenpass application just outputs a key so there is no payload; these are still used to abort retransmissions in case of packages loss.
  • Data & CookieReply -- These would be used if we implemented payload transmission and proof-of-ip-ownership. Leaving them orphaned like this was a bad practice.

Section 2.4.4 covers some variables which are not described and used in the handshake?

That actually was a deliberate decision. These variables are implied by the naming scheme; maybe we should make it more obvious that they are defined but unused.

What is the purpose of the enter_live helper function mentioned in section 2.5?
It appears also not to be used?

On the contrary; it is used to enter the live transport session; Rosenpass does not implement live transport sessions but this is still needed to send the EmptyData packet that terminates the initiators attempts to retransmit the InitConf package in case of package loss.

See Rosenpass Whitepaper Fig. 4 instructions ICI7 and ICR7.

How are the roles of initiator / responder assigned?

They are not assigned; both peers can be initiator and responder at the same time; such a situation results in two handshakes being executed at the same time. This is actually necessary to avoid denial of service attacks on the protocol level since without having executed the handshake neither party can be sure the other is legitimate. This is tricky to get right; the current rust implementation has a race condition due to this; I need to spend some time figuring out how to fix this.

Footnotes

  1. https://www.wireguard.com/papers/wireguard.pdf

  2. https://rosenpass.eu/whitepaper.pdf

@stv0g
Copy link
Contributor Author

stv0g commented May 23, 2023

Hi @koraa, and @Mullana

thanks for the very detailed explanations 😃

I've seen that you already updated the figures of the whitepaper. I have two questions about the changes in Figure 2:

  • Was the removal of the PRF label biscuit_ad intended?
  • Also the PRF label mix has been removed for the hashing of sidr / sidi in the beginning of RespHello / InitConf phases.

Thanks a lot :)

@stv0g
Copy link
Contributor Author

stv0g commented May 23, 2023

ini_enc, res_enc -- These should be called txki and txkr (transmission key receiver/responder), which is another oversight.

Should we maybe rename those as well in the figure?

@Mullana
Copy link
Contributor

Mullana commented May 23, 2023

Was the removal of the PRF label biscuit_ad intended?
Also the PRF label mix has been removed for the hashing of sidr / sidi in the beginning of RespHello / InitConf phases.

Hi @stv0g
That was my mistake. I accidentally used an old version. But it's already fixed!

@stv0g
Copy link
Contributor Author

stv0g commented May 23, 2023

Oh you are right. Sorry, I was confused by GitHubs image-diffing feature..

@koraa
Copy link
Member

koraa commented May 23, 2023

ini_enc, res_enc -- These should be called txki and txkr (transmission key receiver/responder), which is another oversight.

Should we maybe rename those as well in the figure?

I think we should!

@Mullana Its not urgent at all :)

@stv0g
Copy link
Contributor Author

stv0g commented May 23, 2023

Hi @koraa,

I added a little status section to the description of this ticket so we can track the progress.

There is another thing, which should probably be added to the whitepaper:

Endianess of biscuit counter

It is currently not defined but is essential for a working implementation.
Unfortunately, I am lacking the Rust skills to figure out myself the endianess.

@koraa
Copy link
Member

koraa commented May 23, 2023

@stv0g Thank you!

Everything is currently in little endian. And wonderful idea to add the status section.

@stv0g
Copy link
Contributor Author

stv0g commented May 23, 2023

Figure 4: Wrong cipher-text variable

In Figure 4, Step IHR5 it should probably say decaps_and_mix<SKEM>(sskr, spkr, sctr) instead of decaps_and_mix<SKEM>(sskr, spkr, ct1)

@stv0g
Copy link
Contributor Author

stv0g commented May 24, 2023

@koraa Sorry for all the spam here. These are just peer-review comments about the whitepaper. Nothing critical or urgent. I just want to keep track of my comments so we can improve the next version of the white paper 😃

Section 2.4.1: Better naming for session/index table

Section 2.4.1 mentions a global table for keeping track of ongoing session:

index – A lookup table mapping the session ID to the ongoing initiator handshake
or live session

I am wondering where this naming comes from. I think something like sessions would be more appropriate here.

@koraa

This comment was marked as off-topic.

@stv0g
Copy link
Contributor Author

stv0g commented May 24, 2023

load_biscuit: Inverted assertions for replay detection

The pseudo code for load_biscuit() includes the following assert statement:

assert(pt.biscuit_no ≤ peer.biscuit_used);

Shouldnt it be the opposite? E.g.:

assert(pt.biscuit_no > peer.biscuit_used);

The number in the biscuit must be larger than any previously seen number.

Step ICR5 of Figure 4 actually makes the correct assertion.
Maybe I am missing a point here. But why are the assertion made twice?


Edit: Looking at the implementation, the assertions seem to be correct. It works with them as in the whitepaper. However, I will need to put in some more effort to understand this. I guess its related to the replay vs. retransmission detection.

@stv0g
Copy link
Contributor Author

stv0g commented May 24, 2023

Figure 3: Wrong PRF labels for session encryption keys

The whitepaper uses the following PRF labels for deriving the session encryption keys:

  • ini_enc: initiator session encryption
  • res_enc: responder session encryption

However, the Rust implementation uses:

  • ini_enc: initiator handshake encryption
  • res_enc: responder handshake encryption

See:

prflabel_leaf!(_ckextract, ini_enc, "initiator handshake encryption");

@stv0g
Copy link
Contributor Author

stv0g commented May 24, 2023

Section 2.3: Wrong protocol identifier

The whitepaper uses the following string as the protocol identifier

PROTOCOL = "rosenpass 1 rosenpass.eu aead=chachapoly1305 hash=blake2s
↪ ekem=kyber512 skem=mceliece460896 xaead=xchachapoly1305"

However, the Rust implementation uses:

PrfTree::zero().mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 BLAKE2s".as_bytes())

@koraa

This comment was marked as off-topic.

@stv0g

This comment was marked as off-topic.

@stv0g
Copy link
Contributor Author

stv0g commented May 25, 2023

Section 2.1.1: Wrong hash function?

Section 2.1.1 says:

As keyed hash function we use the HMAC construction [12] with BLAKE2s [14] as the inner hash function.

However the Rust implementation used BLAKE2b:

blake2b_flexible(out, &NOTHING, data)

@stv0g
Copy link
Contributor Author

stv0g commented May 25, 2023

Section 2.1.1: Describe non-standard HMAC-Blake2 variant

I fallen into the trap to assume that the key of the inner Blake2 hash-function for the HMAC calculation will be set to zero like Wireguard is doing it:

https://github.com/WireGuard/wireguard-go/blob/052af4a8072bbbd3bfe7edf46fe3c1b350f71f08/device/noise-helpers.go#L24-L31

However, Rosenpass seems to take another approach and uses the ipad/opad xor-ed key (temp_key in the code) as the key for the inner hash functions:

rosenpass/src/sodium.rs

Lines 251 to 266 in b29720b

pub fn hmac_into(out: &mut [u8], key: &[u8], data: &[u8]) -> Result<()> {
// Not bothering with padding; the implementation
// uses appropriately sized keys.
ensure!(key.len() == KEY_SIZE);
const IPAD: [u8; KEY_SIZE] = [0x36u8; KEY_SIZE];
let mut temp_key = [0u8; KEY_SIZE];
temp_key.copy_from_slice(key);
xor_into(&mut temp_key, &IPAD);
let outer_data = mac(&temp_key, data)?;
const OPAD: [u8; KEY_SIZE] = [0x5Cu8; KEY_SIZE];
temp_key.copy_from_slice(key);
xor_into(&mut temp_key, &OPAD);
mac_into(out, &temp_key, &outer_data)
}

The white paper does not motivate why this is done. I would argue that the HMAC implementation of Rosenpass is not following RFC 2104 as referenced in 1.

Interestingly, also Jason Donenfeld is arguing that HMAC-Blake2s is not that useful?

https://lore.kernel.org/lkml/20220111181037.632969-2-Jason@zx2c4.com/

Footnotes

  1. Dr. Hugo Krawczyk, Mihir Bellare, and Ran Canetti. HMAC: Keyed-Hashing for
    Message Authentication. RFC 2104. Feb. 1997. DOI: 10.17487/RFC2104. https:
    //www.rfc-editor.org/info/rfc2104 (cit. on p. 4).

@stv0g
Copy link
Contributor Author

stv0g commented May 25, 2023

Figure 3 / Section 2.5: Wrong order of mixing in en/decaps_and_mix()

The white paper mixes in the following order:

  1. pk
  2. ct
  3. shk

The Rust implementation mixes in the following:

  1. pk
  2. shk
  3. ct

See:

rosenpass/src/protocol.rs

Lines 1251 to 1252 in b29720b

self.mix(pk)?.mix(shk.secret())?.mix(ct)
}

@stv0g
Copy link
Contributor Author

stv0g commented May 26, 2023

Figure 2: Wrong field ordering in RespHello message

The white paper defines the order as follows:

  1. sidr
  2. sidi
  3. ecti
  4. scti
  5. biscuit
  6. auth

The Rust code implements it as

  1. sidr
  2. sidi
  3. ecti
  4. scti
  5. biscuit
  6. auth

Note that 5. and 6. are swapped:

rosenpass/src/msgs.rs

Lines 255 to 268 in b29720b

data_lense! { RespHello :=
/// Randomly generated connection id
sidr: 4,
/// Copied from InitHello
sidi: 4,
/// Kyber 512 Ephemeral Ciphertext
ecti: EphemeralKEM::CT_LEN,
/// Classic McEliece Ciphertext
scti: StaticKEM::CT_LEN,
/// Empty encrypted message (just an auth tag)
auth: sodium::AEAD_TAG_LEN,
/// Responders handshake state in encrypted form
biscuit: BISCUIT_CT_LEN
}

@koraa
Copy link
Member

koraa commented May 26, 2023

Section 2.1.1: Describe non-standard HMAC-Blake2 variant

I fallen into the trap to assume that the key of the inner Blake2 hash-function for the HMAC calculation will be set to zero like Wireguard is doing it:

https://github.com/WireGuard/wireguard-go/blob/052af4a8072bbbd3bfe7edf46fe3c1b350f71f08/device/noise-helpers.go#L24-L31

However, Rosenpass seems to take another approach and uses the ipad/opad xor-ed key (temp_key in the code) as the key for the inner hash functions:

rosenpass/src/sodium.rs

Lines 251 to 266 in b29720b

pub fn hmac_into(out: &mut [u8], key: &[u8], data: &[u8]) -> Result<()> {
// Not bothering with padding; the implementation
// uses appropriately sized keys.
ensure!(key.len() == KEY_SIZE);
const IPAD: [u8; KEY_SIZE] = [0x36u8; KEY_SIZE];
let mut temp_key = [0u8; KEY_SIZE];
temp_key.copy_from_slice(key);
xor_into(&mut temp_key, &IPAD);
let outer_data = mac(&temp_key, data)?;
const OPAD: [u8; KEY_SIZE] = [0x5Cu8; KEY_SIZE];
temp_key.copy_from_slice(key);
xor_into(&mut temp_key, &OPAD);
mac_into(out, &temp_key, &outer_data)
}

The white paper does not motivate why this is done. I would argue that the HMAC implementation of Rosenpass is not following RFC 2104 as referenced in 1.

Interestingly, also Jason Donenfeld is arguing that HMAC-Blake2s is not that useful?

https://lore.kernel.org/lkml/20220111181037.632969-2-Jason@zx2c4.com/

Footnotes

1. Dr. Hugo Krawczyk, Mihir Bellare, and Ran Canetti. HMAC: Keyed-Hashing for
   Message Authentication. RFC 2104. Feb. 1997. DOI: 10.17487/RFC2104. https:
   //www.rfc-editor.org/info/rfc2104 (cit. on p. 4). [leftwards_arrow_with_hook](#user-content-fnref-12-fb4d902f8b48d5eac583aa77e22018e2)

The entire situation surrounding HMAC is a mess…I'll tell you some time in detail; it's better as an anecdote than as a technical comment. Right now the assumption is that rosenpass does the same thing WireGuard does; although I haven't consulted the WireGuard source code myself. I'll do a deep dive when I apply your comments to check this.

Good catch!

@stv0g
Copy link
Contributor Author

stv0g commented May 26, 2023

Hi @koraa,

do you mind if I split up this issue into multipLe issues for addressing, discussing each individual inconsistency?
Or do you prefer to have it all in one?

@koraa
Copy link
Member

koraa commented May 26, 2023

Hi @koraa,

do you mind if I split up this issue into multipLe issues for addressing, discussing each individual inconsistency? Or do you prefer to have it all in one?

I kind of like having it in one issue, I'd wait until this commit settles and then address all issues in one go xD.
We could also convert this into a project though.

@stv0g
Copy link
Contributor Author

stv0g commented May 31, 2023

Section 2.4.3: Mismatching biscuit key epoch

The white paper says:

The biscuit_key used to encrypt biscuits should be rotated every two minutes.

The Rust implementation uses an epoch of 5 minutes:

pub const BISCUIT_EPOCH: Timing = 300.0;

@koraa koraa changed the title Add whitepaper omissions Whitepaper proof read Jun 2, 2023
@stv0g
Copy link
Contributor Author

stv0g commented Aug 1, 2023

Section 2.1.4 / 2.1.5: Add references to exact versions of KEM specifications which are used by Rosenpass

Currently, the whitepaper does not say which version of Classic McEliece nor Kyber is used.

In the case of Classic McElience, the Rust implementation of Rosenpass currently uses liboqs which implements and older version of the KEM (Round 3) while the latest version of the KEM is specified by the NIST PQC round 4 submission.

I assume that there are also different version of Kyber in existance. So we should be more precise in saying which version of the KEMs must be used.

@stv0g
Copy link
Contributor Author

stv0g commented Aug 17, 2023

Figure 3: Indicate when responder/initiator are authenticated

I think Figure 3 would be a great place to show from which step onwards responder/initiators are authenticated. This is currently only visualized in Figure 1 in which we are lacking the implementation details.

@stv0g
Copy link
Contributor Author

stv0g commented Sep 13, 2023

Some nits from @wucke13 and @emilengler

From #108

@wucke13 and I just went through the whitepaper together and found some slight issues, which are as follows:

  • s/private/secret/g
  • s/server/responder/g
  • Renaming pidiC to pidict to imply its ciphertext nature
  • What do the results in the Global Domain diagram mean? Are they hash functions, are they hash inputs?
    • Consider expanding the legend; explain what > and >> mean a bit further

@koraa
Copy link
Member

koraa commented Oct 2, 2023

@Mullana I am adding some TODOs for you in the issue description; please give me some time to finalize the changes I am making before actually starting on any of these TODOs

@koraa koraa mentioned this issue Oct 2, 2023
23 tasks
koraa added a commit that referenced this issue Oct 3, 2023
Issue: #68 (#68)

initiator handshake encryption -> initiator session encryption
responder handshake encryption -> responder session encryption
koraa added a commit that referenced this issue Oct 3, 2023
koraa added a commit that referenced this issue Oct 3, 2023
@koraa
Copy link
Member

koraa commented Oct 3, 2023

@stv0g I applied most of your suggestions; I decided not to apply the rest of the suggestions or to do something else entirely (such as migrating to SHA3).

Further work is tracked in #136

@koraa koraa linked a pull request Nov 5, 2023 that will close this issue
23 tasks
koraa added a commit that referenced this issue Nov 16, 2023
Issue: #68 (#68)

initiator handshake encryption -> initiator session encryption
responder handshake encryption -> responder session encryption
koraa added a commit that referenced this issue Nov 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants