-
-
Save Gelbpunkt/39686a0f7cbd8f09a692ee8fe3531ae8 to your computer and use it in GitHub Desktop.
base64 0.21 issue with known lengths (wrong estimate?)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "testing_base64" | |
version = "0.1.0" | |
edition = "2021" | |
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |
[dependencies] | |
base64 = { version = "0.21.0-beta.2" } | |
base64_013 = { package = "base64", version = "0.13" } | |
fastrand = "1" | |
sha1_smol = "1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use base64::{engine::general_purpose, Engine}; | |
use sha1_smol::Sha1; | |
/// The Globally Unique Identifier (GUID) used in the websocket protocol (see [the RFC](https://datatracker.ietf.org/doc/html/rfc6455#section-1.3)). | |
const GUID: &str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | |
fn get_key() -> [u8; 16] { | |
[ | |
fastrand::u8(0..=255), | |
fastrand::u8(0..=255), | |
fastrand::u8(0..=255), | |
fastrand::u8(0..=255), | |
fastrand::u8(0..=255), | |
fastrand::u8(0..=255), | |
fastrand::u8(0..=255), | |
fastrand::u8(0..=255), | |
fastrand::u8(0..=255), | |
fastrand::u8(0..=255), | |
fastrand::u8(0..=255), | |
fastrand::u8(0..=255), | |
fastrand::u8(0..=255), | |
fastrand::u8(0..=255), | |
fastrand::u8(0..=255), | |
fastrand::u8(0..=255), | |
] | |
} | |
fn digest(key: &[u8]) -> [u8; 20] { | |
let mut s = Sha1::new(); | |
s.update(key); | |
s.update(GUID.as_bytes()); | |
s.digest().bytes() | |
} | |
fn main() { | |
let key_bytes = get_key(); | |
let mut key_base64 = [0; 24]; | |
// SAFETY: We know that 16 bytes will be 24 bytes base64-encoded | |
unsafe { | |
general_purpose::STANDARD | |
.encode_slice(key_bytes, &mut key_base64) | |
.unwrap_unchecked() | |
}; | |
// Websocket client sends this in a Sec-WebSocket-Key header | |
// Server reads the header | |
// SHA1 digests are always 20 bytes | |
let ws_accept = digest(&key_base64); | |
// Calculate the Sec-WebSocket-Accept value | |
let ws_accept_base64 = general_purpose::STANDARD.encode(ws_accept); | |
assert_eq!(ws_accept_base64.len(), 28); | |
// Client receives this as the header and validates it | |
// With 0.13, it works | |
let mut ws_accept = [0; 20]; | |
assert!(base64_013::decode_config_slice( | |
&ws_accept_base64, | |
base64_013::STANDARD, | |
&mut ws_accept | |
) | |
.is_ok()); | |
// With 0.21 it does not | |
let mut ws_accept = [0; 20]; | |
// This is why | |
assert_eq!(base64::decoded_len_estimate(ws_accept_base64.len()), 21); | |
assert!(general_purpose::STANDARD | |
.decode_slice(&ws_accept_base64, &mut ws_accept) | |
.is_err()); | |
// Unsafe hack | |
let mut ws_accept = [0; 20]; | |
let mut ws_accept_fake_length = unsafe { | |
let ptr = ws_accept.as_mut_ptr(); | |
std::slice::from_raw_parts_mut(ptr, 21) | |
}; | |
assert!(general_purpose::STANDARD | |
.decode_slice(&ws_accept_base64, &mut ws_accept_fake_length) | |
.is_ok()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment