Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,5 +18,6 @@ members = [
"pkcs5",
"pkcs8",
"spki",
"wycheproof2blb",
"x509"
]
77 changes: 5 additions & 72 deletions blobby/examples/convert.rs
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,19 @@
//! Convert utility
use blobby::BlobIterator;
use std::collections::HashMap;
use blobby::{encode_blobs, BlobIterator};
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::{env, error::Error, fs::File};

const NEXT_MASK: u8 = 0b1000_0000;
const VAL_MASK: u8 = 0b0111_1111;

fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

fn encode(reader: impl BufRead, mut writer: impl Write) -> io::Result<usize> {
let mut blobs = Vec::new();
for line in reader.lines() {
let blob = hex::decode(line?.as_str())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
blobs.push(blob);
}

let mut idx_map = HashMap::new();
for blob in blobs.iter().filter(|b| b.len() != 0) {
let v = idx_map.entry(blob.as_slice()).or_insert(0);
*v += 1;
}

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
&[0] => 2,
&[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();

let rev_idx: HashMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

println!("Index len: {:?}", idx.len());
let mut buf = [0u8; 4];
writer.write_all(encode_vlq(idx.len(), &mut buf))?;
for e in idx {
writer.write_all(encode_vlq(e.len(), &mut buf))?;
writer.write_all(e)?;
}

for blob in blobs.iter() {
if let Some(dup_pos) = rev_idx.get(blob.as_slice()) {
let n = (dup_pos << 1) + 1usize;
writer.write_all(encode_vlq(n, &mut buf))?;
} else {
let n = blob.len() << 1;
writer.write_all(encode_vlq(n, &mut buf))?;
writer.write_all(blob)?;
}
}

Ok(blobs.len())
let (data, idx_len) = encode_blobs(&blobs);
let data_len = data.len();
println!("Index len: {:?}", idx_len);
writer.write_all(&data).map(|_| data_len)
}

fn decode<R: BufRead, W: Write>(mut reader: R, mut writer: W) -> io::Result<usize> {
Expand Down
96 changes: 95 additions & 1 deletion blobby/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@
)]
extern crate alloc;

use alloc::{boxed::Box, vec, vec::Vec};
use alloc::{boxed::Box, collections::BTreeMap, vec, vec::Vec};
use core::iter::Iterator;

/// Iterator over binary blobs
Expand DownExpand Up@@ -114,6 +114,100 @@ fn read_vlq(data: &[u8], pos: &mut usize) -> Result<usize, Error> {
Ok(val)
}

/// Write a git-flavoured VLQ value into `buf`.
///
/// Returns the slice within `buf` that holds the value.
fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

/// Encode the given collection of binary blobs in .blb format into `writer`.
/// Returns the encoded data together with a count of the number of blobs included in the index.
///
/// The encoded file format is:
/// - count of index entries=N
/// - N x index entries, each encoded as:
/// - size L of index entry (VLQ)
/// - index blob contents (L bytes)
/// - repeating encoded blobs, each encoded as:
/// - VLQ value that is either:
/// - (J << 1) & 0x01: indicates this blob is index entry J
/// - (L << 1) & 0x00: indicates an explicit blob of len L
/// - (in the latter case) explicit blob contents (L bytes)
pub fn encode_blobs<'a, I, T: 'a>(blobs: &'a I) -> (Vec<u8>, usize)
where
&'a I: IntoIterator<Item = &'a T>,
T: AsRef<[u8]>,
{
let mut idx_map = BTreeMap::new();
blobs
.into_iter()
.map(|v| v.as_ref())
.filter(|blob| !blob.is_empty())
.for_each(|blob| {
let v = idx_map.entry(blob.as_ref()).or_insert(0);
*v += 1;
});

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
[0] => 2,
[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();
let idx_len = idx.len();

let rev_idx: BTreeMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

let mut out_buf = Vec::new();
let mut buf = [0u8; 4];
out_buf.extend_from_slice(encode_vlq(idx.len(), &mut buf));
for e in idx {
out_buf.extend_from_slice(encode_vlq(e.len(), &mut buf));
out_buf.extend_from_slice(e);
}

for blob in blobs.into_iter().map(|v| v.as_ref()) {
if let Some(dup_pos) = rev_idx.get(blob) {
let n = (dup_pos << 1) + 1usize;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
} else {
let n = blob.len() << 1;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
out_buf.extend_from_slice(blob);
}
}

(out_buf, idx_len)
}

impl<'a> BlobIterator<'a> {
/// Create new `BlobIterator` for given `data`.
pub fn new(data: &'a [u8]) -> Result<Self, Error> {
Expand Down
16 changes: 16 additions & 0 deletions wycheproof2blb/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
[package]
name = "wycheproof2blb"
version = "0.1.0"
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
description = "A utility for converting Wycheproof test vectors to the blobby format"
repository = "https://github.com/RustCrypto/utils"
keywords = ["test", "Wycheproof"]
categories = ["cryptography", "no-std"]
edition = "2018"

[dependencies]
blobby = { version = "*", path = "../blobby" }
hex = "*"
serde = { version = "*", features = ["derive"] }
serde_json = "*"
89 changes: 89 additions & 0 deletions wycheproof2blb/src/aead.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
use crate::wycheproof;
use crate::wycheproof::{case_result, description, hex_string};
use crate::TestInfo;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct TestSuite {
#[serde(flatten)]
pub suite: wycheproof::Suite,
#[serde(rename = "testGroups")]
pub test_groups: Vec<TestGroup>,
}

#[derive(Debug, Deserialize)]
pub struct TestGroup {
#[serde(flatten)]
pub group: wycheproof::Group,
#[serde(rename = "ivSize")]
pub iv_size: u32,
#[serde(rename = "keySize")]
pub key_size: u32,
#[serde(rename = "tagSize")]
pub tag_size: u32,
pub tests: Vec<TestCase>,
}

#[derive(Debug, Deserialize)]
pub struct TestCase {
#[serde(flatten)]
pub case: wycheproof::Case,
#[serde(with = "hex_string")]
pub aad: Vec<u8>,
#[serde(with = "hex_string")]
pub ct: Vec<u8>,
#[serde(with = "hex_string")]
pub iv: Vec<u8>,
#[serde(with = "hex_string")]
pub key: Vec<u8>,
#[serde(with = "hex_string")]
pub msg: Vec<u8>,
#[serde(with = "hex_string")]
pub tag: Vec<u8>,
}

pub fn aes_gcm_generator(data: &[u8], algorithm: &str, key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, key_size, 12 * 8)
}

pub fn chacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 12 * 8)
}

pub fn xchacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 24 * 8)
}

fn generator(data: &[u8], algorithm: &str, key_size: u32, iv_size: u32) -> Vec<TestInfo> {
let suite: TestSuite = serde_json::from_slice(data).unwrap();
assert_eq!(algorithm, suite.suite.algorithm);

let mut infos = vec![];
for g in &suite.test_groups {
for tc in &g.tests {
if key_size != 0 && g.key_size != key_size {
continue;
}
if g.iv_size != iv_size {
println!(" skipping tests for iv_size={}", g.iv_size);
continue;
}
let mut combined_ct = Vec::new();
combined_ct.extend_from_slice(&tc.ct);
combined_ct.extend_from_slice(&tc.tag);

infos.push(TestInfo {
data: vec![
tc.key.clone(),
tc.iv.clone(),
tc.aad.clone(),
tc.msg.clone(),
combined_ct,
vec![case_result(&tc.case)],
],
desc: description(&suite.suite, &tc.case),
});
}
}
infos
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,5 +18,6 @@ members = [
"pkcs5",
"pkcs8",
"spki",
"wycheproof2blb",
"x509"
]
77 changes: 5 additions & 72 deletions blobby/examples/convert.rs
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,19 @@
//! Convert utility
use blobby::BlobIterator;
use std::collections::HashMap;
use blobby::{encode_blobs, BlobIterator};
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::{env, error::Error, fs::File};

const NEXT_MASK: u8 = 0b1000_0000;
const VAL_MASK: u8 = 0b0111_1111;

fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

fn encode(reader: impl BufRead, mut writer: impl Write) -> io::Result<usize> {
let mut blobs = Vec::new();
for line in reader.lines() {
let blob = hex::decode(line?.as_str())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
blobs.push(blob);
}

let mut idx_map = HashMap::new();
for blob in blobs.iter().filter(|b| b.len() != 0) {
let v = idx_map.entry(blob.as_slice()).or_insert(0);
*v += 1;
}

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
&[0] => 2,
&[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();

let rev_idx: HashMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

println!("Index len: {:?}", idx.len());
let mut buf = [0u8; 4];
writer.write_all(encode_vlq(idx.len(), &mut buf))?;
for e in idx {
writer.write_all(encode_vlq(e.len(), &mut buf))?;
writer.write_all(e)?;
}

for blob in blobs.iter() {
if let Some(dup_pos) = rev_idx.get(blob.as_slice()) {
let n = (dup_pos << 1) + 1usize;
writer.write_all(encode_vlq(n, &mut buf))?;
} else {
let n = blob.len() << 1;
writer.write_all(encode_vlq(n, &mut buf))?;
writer.write_all(blob)?;
}
}

Ok(blobs.len())
let (data, idx_len) = encode_blobs(&blobs);
let data_len = data.len();
println!("Index len: {:?}", idx_len);
writer.write_all(&data).map(|_| data_len)
}

fn decode<R: BufRead, W: Write>(mut reader: R, mut writer: W) -> io::Result<usize> {
Expand Down
96 changes: 95 additions & 1 deletion blobby/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@
)]
extern crate alloc;

use alloc::{boxed::Box, vec, vec::Vec};
use alloc::{boxed::Box, collections::BTreeMap, vec, vec::Vec};
use core::iter::Iterator;

/// Iterator over binary blobs
Expand DownExpand Up@@ -114,6 +114,100 @@ fn read_vlq(data: &[u8], pos: &mut usize) -> Result<usize, Error> {
Ok(val)
}

/// Write a git-flavoured VLQ value into `buf`.
///
/// Returns the slice within `buf` that holds the value.
fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

/// Encode the given collection of binary blobs in .blb format into `writer`.
/// Returns the encoded data together with a count of the number of blobs included in the index.
///
/// The encoded file format is:
/// - count of index entries=N
/// - N x index entries, each encoded as:
/// - size L of index entry (VLQ)
/// - index blob contents (L bytes)
/// - repeating encoded blobs, each encoded as:
/// - VLQ value that is either:
/// - (J << 1) & 0x01: indicates this blob is index entry J
/// - (L << 1) & 0x00: indicates an explicit blob of len L
/// - (in the latter case) explicit blob contents (L bytes)
pub fn encode_blobs<'a, I, T: 'a>(blobs: &'a I) -> (Vec<u8>, usize)
where
&'a I: IntoIterator<Item = &'a T>,
T: AsRef<[u8]>,
{
let mut idx_map = BTreeMap::new();
blobs
.into_iter()
.map(|v| v.as_ref())
.filter(|blob| !blob.is_empty())
.for_each(|blob| {
let v = idx_map.entry(blob.as_ref()).or_insert(0);
*v += 1;
});

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
[0] => 2,
[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();
let idx_len = idx.len();

let rev_idx: BTreeMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

let mut out_buf = Vec::new();
let mut buf = [0u8; 4];
out_buf.extend_from_slice(encode_vlq(idx.len(), &mut buf));
for e in idx {
out_buf.extend_from_slice(encode_vlq(e.len(), &mut buf));
out_buf.extend_from_slice(e);
}

for blob in blobs.into_iter().map(|v| v.as_ref()) {
if let Some(dup_pos) = rev_idx.get(blob) {
let n = (dup_pos << 1) + 1usize;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
} else {
let n = blob.len() << 1;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
out_buf.extend_from_slice(blob);
}
}

(out_buf, idx_len)
}

impl<'a> BlobIterator<'a> {
/// Create new `BlobIterator` for given `data`.
pub fn new(data: &'a [u8]) -> Result<Self, Error> {
Expand Down
16 changes: 16 additions & 0 deletions wycheproof2blb/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
[package]
name = "wycheproof2blb"
version = "0.1.0"
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
description = "A utility for converting Wycheproof test vectors to the blobby format"
repository = "https://github.com/RustCrypto/utils"
keywords = ["test", "Wycheproof"]
categories = ["cryptography", "no-std"]
edition = "2018"

[dependencies]
blobby = { version = "*", path = "../blobby" }
hex = "*"
serde = { version = "*", features = ["derive"] }
serde_json = "*"
89 changes: 89 additions & 0 deletions wycheproof2blb/src/aead.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
use crate::wycheproof;
use crate::wycheproof::{case_result, description, hex_string};
use crate::TestInfo;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct TestSuite {
#[serde(flatten)]
pub suite: wycheproof::Suite,
#[serde(rename = "testGroups")]
pub test_groups: Vec<TestGroup>,
}

#[derive(Debug, Deserialize)]
pub struct TestGroup {
#[serde(flatten)]
pub group: wycheproof::Group,
#[serde(rename = "ivSize")]
pub iv_size: u32,
#[serde(rename = "keySize")]
pub key_size: u32,
#[serde(rename = "tagSize")]
pub tag_size: u32,
pub tests: Vec<TestCase>,
}

#[derive(Debug, Deserialize)]
pub struct TestCase {
#[serde(flatten)]
pub case: wycheproof::Case,
#[serde(with = "hex_string")]
pub aad: Vec<u8>,
#[serde(with = "hex_string")]
pub ct: Vec<u8>,
#[serde(with = "hex_string")]
pub iv: Vec<u8>,
#[serde(with = "hex_string")]
pub key: Vec<u8>,
#[serde(with = "hex_string")]
pub msg: Vec<u8>,
#[serde(with = "hex_string")]
pub tag: Vec<u8>,
}

pub fn aes_gcm_generator(data: &[u8], algorithm: &str, key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, key_size, 12 * 8)
}

pub fn chacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 12 * 8)
}

pub fn xchacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 24 * 8)
}

fn generator(data: &[u8], algorithm: &str, key_size: u32, iv_size: u32) -> Vec<TestInfo> {
let suite: TestSuite = serde_json::from_slice(data).unwrap();
assert_eq!(algorithm, suite.suite.algorithm);

let mut infos = vec![];
for g in &suite.test_groups {
for tc in &g.tests {
if key_size != 0 && g.key_size != key_size {
continue;
}
if g.iv_size != iv_size {
println!(" skipping tests for iv_size={}", g.iv_size);
continue;
}
let mut combined_ct = Vec::new();
combined_ct.extend_from_slice(&tc.ct);
combined_ct.extend_from_slice(&tc.tag);

infos.push(TestInfo {
data: vec![
tc.key.clone(),
tc.iv.clone(),
tc.aad.clone(),
tc.msg.clone(),
combined_ct,
vec![case_result(&tc.case)],
],
desc: description(&suite.suite, &tc.case),
});
}
}
infos
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,5 +18,6 @@ members = [
"pkcs5",
"pkcs8",
"spki",
"wycheproof2blb",
"x509"
]
77 changes: 5 additions & 72 deletions blobby/examples/convert.rs
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,19 @@
//! Convert utility
use blobby::BlobIterator;
use std::collections::HashMap;
use blobby::{encode_blobs, BlobIterator};
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::{env, error::Error, fs::File};

const NEXT_MASK: u8 = 0b1000_0000;
const VAL_MASK: u8 = 0b0111_1111;

fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

fn encode(reader: impl BufRead, mut writer: impl Write) -> io::Result<usize> {
let mut blobs = Vec::new();
for line in reader.lines() {
let blob = hex::decode(line?.as_str())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
blobs.push(blob);
}

let mut idx_map = HashMap::new();
for blob in blobs.iter().filter(|b| b.len() != 0) {
let v = idx_map.entry(blob.as_slice()).or_insert(0);
*v += 1;
}

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
&[0] => 2,
&[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();

let rev_idx: HashMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

println!("Index len: {:?}", idx.len());
let mut buf = [0u8; 4];
writer.write_all(encode_vlq(idx.len(), &mut buf))?;
for e in idx {
writer.write_all(encode_vlq(e.len(), &mut buf))?;
writer.write_all(e)?;
}

for blob in blobs.iter() {
if let Some(dup_pos) = rev_idx.get(blob.as_slice()) {
let n = (dup_pos << 1) + 1usize;
writer.write_all(encode_vlq(n, &mut buf))?;
} else {
let n = blob.len() << 1;
writer.write_all(encode_vlq(n, &mut buf))?;
writer.write_all(blob)?;
}
}

Ok(blobs.len())
let (data, idx_len) = encode_blobs(&blobs);
let data_len = data.len();
println!("Index len: {:?}", idx_len);
writer.write_all(&data).map(|_| data_len)
}

fn decode<R: BufRead, W: Write>(mut reader: R, mut writer: W) -> io::Result<usize> {
Expand Down
96 changes: 95 additions & 1 deletion blobby/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@
)]
extern crate alloc;

use alloc::{boxed::Box, vec, vec::Vec};
use alloc::{boxed::Box, collections::BTreeMap, vec, vec::Vec};
use core::iter::Iterator;

/// Iterator over binary blobs
Expand DownExpand Up@@ -114,6 +114,100 @@ fn read_vlq(data: &[u8], pos: &mut usize) -> Result<usize, Error> {
Ok(val)
}

/// Write a git-flavoured VLQ value into `buf`.
///
/// Returns the slice within `buf` that holds the value.
fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

/// Encode the given collection of binary blobs in .blb format into `writer`.
/// Returns the encoded data together with a count of the number of blobs included in the index.
///
/// The encoded file format is:
/// - count of index entries=N
/// - N x index entries, each encoded as:
/// - size L of index entry (VLQ)
/// - index blob contents (L bytes)
/// - repeating encoded blobs, each encoded as:
/// - VLQ value that is either:
/// - (J << 1) & 0x01: indicates this blob is index entry J
/// - (L << 1) & 0x00: indicates an explicit blob of len L
/// - (in the latter case) explicit blob contents (L bytes)
pub fn encode_blobs<'a, I, T: 'a>(blobs: &'a I) -> (Vec<u8>, usize)
where
&'a I: IntoIterator<Item = &'a T>,
T: AsRef<[u8]>,
{
let mut idx_map = BTreeMap::new();
blobs
.into_iter()
.map(|v| v.as_ref())
.filter(|blob| !blob.is_empty())
.for_each(|blob| {
let v = idx_map.entry(blob.as_ref()).or_insert(0);
*v += 1;
});

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
[0] => 2,
[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();
let idx_len = idx.len();

let rev_idx: BTreeMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

let mut out_buf = Vec::new();
let mut buf = [0u8; 4];
out_buf.extend_from_slice(encode_vlq(idx.len(), &mut buf));
for e in idx {
out_buf.extend_from_slice(encode_vlq(e.len(), &mut buf));
out_buf.extend_from_slice(e);
}

for blob in blobs.into_iter().map(|v| v.as_ref()) {
if let Some(dup_pos) = rev_idx.get(blob) {
let n = (dup_pos << 1) + 1usize;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
} else {
let n = blob.len() << 1;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
out_buf.extend_from_slice(blob);
}
}

(out_buf, idx_len)
}

impl<'a> BlobIterator<'a> {
/// Create new `BlobIterator` for given `data`.
pub fn new(data: &'a [u8]) -> Result<Self, Error> {
Expand Down
16 changes: 16 additions & 0 deletions wycheproof2blb/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
[package]
name = "wycheproof2blb"
version = "0.1.0"
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
description = "A utility for converting Wycheproof test vectors to the blobby format"
repository = "https://github.com/RustCrypto/utils"
keywords = ["test", "Wycheproof"]
categories = ["cryptography", "no-std"]
edition = "2018"

[dependencies]
blobby = { version = "*", path = "../blobby" }
hex = "*"
serde = { version = "*", features = ["derive"] }
serde_json = "*"
89 changes: 89 additions & 0 deletions wycheproof2blb/src/aead.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
use crate::wycheproof;
use crate::wycheproof::{case_result, description, hex_string};
use crate::TestInfo;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct TestSuite {
#[serde(flatten)]
pub suite: wycheproof::Suite,
#[serde(rename = "testGroups")]
pub test_groups: Vec<TestGroup>,
}

#[derive(Debug, Deserialize)]
pub struct TestGroup {
#[serde(flatten)]
pub group: wycheproof::Group,
#[serde(rename = "ivSize")]
pub iv_size: u32,
#[serde(rename = "keySize")]
pub key_size: u32,
#[serde(rename = "tagSize")]
pub tag_size: u32,
pub tests: Vec<TestCase>,
}

#[derive(Debug, Deserialize)]
pub struct TestCase {
#[serde(flatten)]
pub case: wycheproof::Case,
#[serde(with = "hex_string")]
pub aad: Vec<u8>,
#[serde(with = "hex_string")]
pub ct: Vec<u8>,
#[serde(with = "hex_string")]
pub iv: Vec<u8>,
#[serde(with = "hex_string")]
pub key: Vec<u8>,
#[serde(with = "hex_string")]
pub msg: Vec<u8>,
#[serde(with = "hex_string")]
pub tag: Vec<u8>,
}

pub fn aes_gcm_generator(data: &[u8], algorithm: &str, key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, key_size, 12 * 8)
}

pub fn chacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 12 * 8)
}

pub fn xchacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 24 * 8)
}

fn generator(data: &[u8], algorithm: &str, key_size: u32, iv_size: u32) -> Vec<TestInfo> {
let suite: TestSuite = serde_json::from_slice(data).unwrap();
assert_eq!(algorithm, suite.suite.algorithm);

let mut infos = vec![];
for g in &suite.test_groups {
for tc in &g.tests {
if key_size != 0 && g.key_size != key_size {
continue;
}
if g.iv_size != iv_size {
println!(" skipping tests for iv_size={}", g.iv_size);
continue;
}
let mut combined_ct = Vec::new();
combined_ct.extend_from_slice(&tc.ct);
combined_ct.extend_from_slice(&tc.tag);

infos.push(TestInfo {
data: vec![
tc.key.clone(),
tc.iv.clone(),
tc.aad.clone(),
tc.msg.clone(),
combined_ct,
vec![case_result(&tc.case)],
],
desc: description(&suite.suite, &tc.case),
});
}
}
infos
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,5 +18,6 @@ members = [
"pkcs5",
"pkcs8",
"spki",
"wycheproof2blb",
"x509"
]
77 changes: 5 additions & 72 deletions blobby/examples/convert.rs
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,19 @@
//! Convert utility
use blobby::BlobIterator;
use std::collections::HashMap;
use blobby::{encode_blobs, BlobIterator};
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::{env, error::Error, fs::File};

const NEXT_MASK: u8 = 0b1000_0000;
const VAL_MASK: u8 = 0b0111_1111;

fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

fn encode(reader: impl BufRead, mut writer: impl Write) -> io::Result<usize> {
let mut blobs = Vec::new();
for line in reader.lines() {
let blob = hex::decode(line?.as_str())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
blobs.push(blob);
}

let mut idx_map = HashMap::new();
for blob in blobs.iter().filter(|b| b.len() != 0) {
let v = idx_map.entry(blob.as_slice()).or_insert(0);
*v += 1;
}

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
&[0] => 2,
&[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();

let rev_idx: HashMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

println!("Index len: {:?}", idx.len());
let mut buf = [0u8; 4];
writer.write_all(encode_vlq(idx.len(), &mut buf))?;
for e in idx {
writer.write_all(encode_vlq(e.len(), &mut buf))?;
writer.write_all(e)?;
}

for blob in blobs.iter() {
if let Some(dup_pos) = rev_idx.get(blob.as_slice()) {
let n = (dup_pos << 1) + 1usize;
writer.write_all(encode_vlq(n, &mut buf))?;
} else {
let n = blob.len() << 1;
writer.write_all(encode_vlq(n, &mut buf))?;
writer.write_all(blob)?;
}
}

Ok(blobs.len())
let (data, idx_len) = encode_blobs(&blobs);
let data_len = data.len();
println!("Index len: {:?}", idx_len);
writer.write_all(&data).map(|_| data_len)
}

fn decode<R: BufRead, W: Write>(mut reader: R, mut writer: W) -> io::Result<usize> {
Expand Down
96 changes: 95 additions & 1 deletion blobby/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@
)]
extern crate alloc;

use alloc::{boxed::Box, vec, vec::Vec};
use alloc::{boxed::Box, collections::BTreeMap, vec, vec::Vec};
use core::iter::Iterator;

/// Iterator over binary blobs
Expand DownExpand Up@@ -114,6 +114,100 @@ fn read_vlq(data: &[u8], pos: &mut usize) -> Result<usize, Error> {
Ok(val)
}

/// Write a git-flavoured VLQ value into `buf`.
///
/// Returns the slice within `buf` that holds the value.
fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

/// Encode the given collection of binary blobs in .blb format into `writer`.
/// Returns the encoded data together with a count of the number of blobs included in the index.
///
/// The encoded file format is:
/// - count of index entries=N
/// - N x index entries, each encoded as:
/// - size L of index entry (VLQ)
/// - index blob contents (L bytes)
/// - repeating encoded blobs, each encoded as:
/// - VLQ value that is either:
/// - (J << 1) & 0x01: indicates this blob is index entry J
/// - (L << 1) & 0x00: indicates an explicit blob of len L
/// - (in the latter case) explicit blob contents (L bytes)
pub fn encode_blobs<'a, I, T: 'a>(blobs: &'a I) -> (Vec<u8>, usize)
where
&'a I: IntoIterator<Item = &'a T>,
T: AsRef<[u8]>,
{
let mut idx_map = BTreeMap::new();
blobs
.into_iter()
.map(|v| v.as_ref())
.filter(|blob| !blob.is_empty())
.for_each(|blob| {
let v = idx_map.entry(blob.as_ref()).or_insert(0);
*v += 1;
});

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
[0] => 2,
[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();
let idx_len = idx.len();

let rev_idx: BTreeMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

let mut out_buf = Vec::new();
let mut buf = [0u8; 4];
out_buf.extend_from_slice(encode_vlq(idx.len(), &mut buf));
for e in idx {
out_buf.extend_from_slice(encode_vlq(e.len(), &mut buf));
out_buf.extend_from_slice(e);
}

for blob in blobs.into_iter().map(|v| v.as_ref()) {
if let Some(dup_pos) = rev_idx.get(blob) {
let n = (dup_pos << 1) + 1usize;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
} else {
let n = blob.len() << 1;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
out_buf.extend_from_slice(blob);
}
}

(out_buf, idx_len)
}

impl<'a> BlobIterator<'a> {
/// Create new `BlobIterator` for given `data`.
pub fn new(data: &'a [u8]) -> Result<Self, Error> {
Expand Down
16 changes: 16 additions & 0 deletions wycheproof2blb/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
[package]
name = "wycheproof2blb"
version = "0.1.0"
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
description = "A utility for converting Wycheproof test vectors to the blobby format"
repository = "https://github.com/RustCrypto/utils"
keywords = ["test", "Wycheproof"]
categories = ["cryptography", "no-std"]
edition = "2018"

[dependencies]
blobby = { version = "*", path = "../blobby" }
hex = "*"
serde = { version = "*", features = ["derive"] }
serde_json = "*"
89 changes: 89 additions & 0 deletions wycheproof2blb/src/aead.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
use crate::wycheproof;
use crate::wycheproof::{case_result, description, hex_string};
use crate::TestInfo;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct TestSuite {
#[serde(flatten)]
pub suite: wycheproof::Suite,
#[serde(rename = "testGroups")]
pub test_groups: Vec<TestGroup>,
}

#[derive(Debug, Deserialize)]
pub struct TestGroup {
#[serde(flatten)]
pub group: wycheproof::Group,
#[serde(rename = "ivSize")]
pub iv_size: u32,
#[serde(rename = "keySize")]
pub key_size: u32,
#[serde(rename = "tagSize")]
pub tag_size: u32,
pub tests: Vec<TestCase>,
}

#[derive(Debug, Deserialize)]
pub struct TestCase {
#[serde(flatten)]
pub case: wycheproof::Case,
#[serde(with = "hex_string")]
pub aad: Vec<u8>,
#[serde(with = "hex_string")]
pub ct: Vec<u8>,
#[serde(with = "hex_string")]
pub iv: Vec<u8>,
#[serde(with = "hex_string")]
pub key: Vec<u8>,
#[serde(with = "hex_string")]
pub msg: Vec<u8>,
#[serde(with = "hex_string")]
pub tag: Vec<u8>,
}

pub fn aes_gcm_generator(data: &[u8], algorithm: &str, key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, key_size, 12 * 8)
}

pub fn chacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 12 * 8)
}

pub fn xchacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 24 * 8)
}

fn generator(data: &[u8], algorithm: &str, key_size: u32, iv_size: u32) -> Vec<TestInfo> {
let suite: TestSuite = serde_json::from_slice(data).unwrap();
assert_eq!(algorithm, suite.suite.algorithm);

let mut infos = vec![];
for g in &suite.test_groups {
for tc in &g.tests {
if key_size != 0 && g.key_size != key_size {
continue;
}
if g.iv_size != iv_size {
println!(" skipping tests for iv_size={}", g.iv_size);
continue;
}
let mut combined_ct = Vec::new();
combined_ct.extend_from_slice(&tc.ct);
combined_ct.extend_from_slice(&tc.tag);

infos.push(TestInfo {
data: vec![
tc.key.clone(),
tc.iv.clone(),
tc.aad.clone(),
tc.msg.clone(),
combined_ct,
vec![case_result(&tc.case)],
],
desc: description(&suite.suite, &tc.case),
});
}
}
infos
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,5 +18,6 @@ members = [
"pkcs5",
"pkcs8",
"spki",
"wycheproof2blb",
"x509"
]
77 changes: 5 additions & 72 deletions blobby/examples/convert.rs
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,19 @@
//! Convert utility
use blobby::BlobIterator;
use std::collections::HashMap;
use blobby::{encode_blobs, BlobIterator};
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::{env, error::Error, fs::File};

const NEXT_MASK: u8 = 0b1000_0000;
const VAL_MASK: u8 = 0b0111_1111;

fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

fn encode(reader: impl BufRead, mut writer: impl Write) -> io::Result<usize> {
let mut blobs = Vec::new();
for line in reader.lines() {
let blob = hex::decode(line?.as_str())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
blobs.push(blob);
}

let mut idx_map = HashMap::new();
for blob in blobs.iter().filter(|b| b.len() != 0) {
let v = idx_map.entry(blob.as_slice()).or_insert(0);
*v += 1;
}

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
&[0] => 2,
&[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();

let rev_idx: HashMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

println!("Index len: {:?}", idx.len());
let mut buf = [0u8; 4];
writer.write_all(encode_vlq(idx.len(), &mut buf))?;
for e in idx {
writer.write_all(encode_vlq(e.len(), &mut buf))?;
writer.write_all(e)?;
}

for blob in blobs.iter() {
if let Some(dup_pos) = rev_idx.get(blob.as_slice()) {
let n = (dup_pos << 1) + 1usize;
writer.write_all(encode_vlq(n, &mut buf))?;
} else {
let n = blob.len() << 1;
writer.write_all(encode_vlq(n, &mut buf))?;
writer.write_all(blob)?;
}
}

Ok(blobs.len())
let (data, idx_len) = encode_blobs(&blobs);
let data_len = data.len();
println!("Index len: {:?}", idx_len);
writer.write_all(&data).map(|_| data_len)
}

fn decode<R: BufRead, W: Write>(mut reader: R, mut writer: W) -> io::Result<usize> {
Expand Down
96 changes: 95 additions & 1 deletion blobby/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@
)]
extern crate alloc;

use alloc::{boxed::Box, vec, vec::Vec};
use alloc::{boxed::Box, collections::BTreeMap, vec, vec::Vec};
use core::iter::Iterator;

/// Iterator over binary blobs
Expand DownExpand Up@@ -114,6 +114,100 @@ fn read_vlq(data: &[u8], pos: &mut usize) -> Result<usize, Error> {
Ok(val)
}

/// Write a git-flavoured VLQ value into `buf`.
///
/// Returns the slice within `buf` that holds the value.
fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

/// Encode the given collection of binary blobs in .blb format into `writer`.
/// Returns the encoded data together with a count of the number of blobs included in the index.
///
/// The encoded file format is:
/// - count of index entries=N
/// - N x index entries, each encoded as:
/// - size L of index entry (VLQ)
/// - index blob contents (L bytes)
/// - repeating encoded blobs, each encoded as:
/// - VLQ value that is either:
/// - (J << 1) & 0x01: indicates this blob is index entry J
/// - (L << 1) & 0x00: indicates an explicit blob of len L
/// - (in the latter case) explicit blob contents (L bytes)
pub fn encode_blobs<'a, I, T: 'a>(blobs: &'a I) -> (Vec<u8>, usize)
where
&'a I: IntoIterator<Item = &'a T>,
T: AsRef<[u8]>,
{
let mut idx_map = BTreeMap::new();
blobs
.into_iter()
.map(|v| v.as_ref())
.filter(|blob| !blob.is_empty())
.for_each(|blob| {
let v = idx_map.entry(blob.as_ref()).or_insert(0);
*v += 1;
});

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
[0] => 2,
[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();
let idx_len = idx.len();

let rev_idx: BTreeMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

let mut out_buf = Vec::new();
let mut buf = [0u8; 4];
out_buf.extend_from_slice(encode_vlq(idx.len(), &mut buf));
for e in idx {
out_buf.extend_from_slice(encode_vlq(e.len(), &mut buf));
out_buf.extend_from_slice(e);
}

for blob in blobs.into_iter().map(|v| v.as_ref()) {
if let Some(dup_pos) = rev_idx.get(blob) {
let n = (dup_pos << 1) + 1usize;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
} else {
let n = blob.len() << 1;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
out_buf.extend_from_slice(blob);
}
}

(out_buf, idx_len)
}

impl<'a> BlobIterator<'a> {
/// Create new `BlobIterator` for given `data`.
pub fn new(data: &'a [u8]) -> Result<Self, Error> {
Expand Down
16 changes: 16 additions & 0 deletions wycheproof2blb/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
[package]
name = "wycheproof2blb"
version = "0.1.0"
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
description = "A utility for converting Wycheproof test vectors to the blobby format"
repository = "https://github.com/RustCrypto/utils"
keywords = ["test", "Wycheproof"]
categories = ["cryptography", "no-std"]
edition = "2018"

[dependencies]
blobby = { version = "*", path = "../blobby" }
hex = "*"
serde = { version = "*", features = ["derive"] }
serde_json = "*"
89 changes: 89 additions & 0 deletions wycheproof2blb/src/aead.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
use crate::wycheproof;
use crate::wycheproof::{case_result, description, hex_string};
use crate::TestInfo;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct TestSuite {
#[serde(flatten)]
pub suite: wycheproof::Suite,
#[serde(rename = "testGroups")]
pub test_groups: Vec<TestGroup>,
}

#[derive(Debug, Deserialize)]
pub struct TestGroup {
#[serde(flatten)]
pub group: wycheproof::Group,
#[serde(rename = "ivSize")]
pub iv_size: u32,
#[serde(rename = "keySize")]
pub key_size: u32,
#[serde(rename = "tagSize")]
pub tag_size: u32,
pub tests: Vec<TestCase>,
}

#[derive(Debug, Deserialize)]
pub struct TestCase {
#[serde(flatten)]
pub case: wycheproof::Case,
#[serde(with = "hex_string")]
pub aad: Vec<u8>,
#[serde(with = "hex_string")]
pub ct: Vec<u8>,
#[serde(with = "hex_string")]
pub iv: Vec<u8>,
#[serde(with = "hex_string")]
pub key: Vec<u8>,
#[serde(with = "hex_string")]
pub msg: Vec<u8>,
#[serde(with = "hex_string")]
pub tag: Vec<u8>,
}

pub fn aes_gcm_generator(data: &[u8], algorithm: &str, key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, key_size, 12 * 8)
}

pub fn chacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 12 * 8)
}

pub fn xchacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 24 * 8)
}

fn generator(data: &[u8], algorithm: &str, key_size: u32, iv_size: u32) -> Vec<TestInfo> {
let suite: TestSuite = serde_json::from_slice(data).unwrap();
assert_eq!(algorithm, suite.suite.algorithm);

let mut infos = vec![];
for g in &suite.test_groups {
for tc in &g.tests {
if key_size != 0 && g.key_size != key_size {
continue;
}
if g.iv_size != iv_size {
println!(" skipping tests for iv_size={}", g.iv_size);
continue;
}
let mut combined_ct = Vec::new();
combined_ct.extend_from_slice(&tc.ct);
combined_ct.extend_from_slice(&tc.tag);

infos.push(TestInfo {
data: vec![
tc.key.clone(),
tc.iv.clone(),
tc.aad.clone(),
tc.msg.clone(),
combined_ct,
vec![case_result(&tc.case)],
],
desc: description(&suite.suite, &tc.case),
});
}
}
infos
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,5 +18,6 @@ members = [
"pkcs5",
"pkcs8",
"spki",
"wycheproof2blb",
"x509"
]
77 changes: 5 additions & 72 deletions blobby/examples/convert.rs
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,19 @@
//! Convert utility
use blobby::BlobIterator;
use std::collections::HashMap;
use blobby::{encode_blobs, BlobIterator};
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::{env, error::Error, fs::File};

const NEXT_MASK: u8 = 0b1000_0000;
const VAL_MASK: u8 = 0b0111_1111;

fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

fn encode(reader: impl BufRead, mut writer: impl Write) -> io::Result<usize> {
let mut blobs = Vec::new();
for line in reader.lines() {
let blob = hex::decode(line?.as_str())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
blobs.push(blob);
}

let mut idx_map = HashMap::new();
for blob in blobs.iter().filter(|b| b.len() != 0) {
let v = idx_map.entry(blob.as_slice()).or_insert(0);
*v += 1;
}

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
&[0] => 2,
&[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();

let rev_idx: HashMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

println!("Index len: {:?}", idx.len());
let mut buf = [0u8; 4];
writer.write_all(encode_vlq(idx.len(), &mut buf))?;
for e in idx {
writer.write_all(encode_vlq(e.len(), &mut buf))?;
writer.write_all(e)?;
}

for blob in blobs.iter() {
if let Some(dup_pos) = rev_idx.get(blob.as_slice()) {
let n = (dup_pos << 1) + 1usize;
writer.write_all(encode_vlq(n, &mut buf))?;
} else {
let n = blob.len() << 1;
writer.write_all(encode_vlq(n, &mut buf))?;
writer.write_all(blob)?;
}
}

Ok(blobs.len())
let (data, idx_len) = encode_blobs(&blobs);
let data_len = data.len();
println!("Index len: {:?}", idx_len);
writer.write_all(&data).map(|_| data_len)
}

fn decode<R: BufRead, W: Write>(mut reader: R, mut writer: W) -> io::Result<usize> {
Expand Down
96 changes: 95 additions & 1 deletion blobby/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@
)]
extern crate alloc;

use alloc::{boxed::Box, vec, vec::Vec};
use alloc::{boxed::Box, collections::BTreeMap, vec, vec::Vec};
use core::iter::Iterator;

/// Iterator over binary blobs
Expand DownExpand Up@@ -114,6 +114,100 @@ fn read_vlq(data: &[u8], pos: &mut usize) -> Result<usize, Error> {
Ok(val)
}

/// Write a git-flavoured VLQ value into `buf`.
///
/// Returns the slice within `buf` that holds the value.
fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

/// Encode the given collection of binary blobs in .blb format into `writer`.
/// Returns the encoded data together with a count of the number of blobs included in the index.
///
/// The encoded file format is:
/// - count of index entries=N
/// - N x index entries, each encoded as:
/// - size L of index entry (VLQ)
/// - index blob contents (L bytes)
/// - repeating encoded blobs, each encoded as:
/// - VLQ value that is either:
/// - (J << 1) & 0x01: indicates this blob is index entry J
/// - (L << 1) & 0x00: indicates an explicit blob of len L
/// - (in the latter case) explicit blob contents (L bytes)
pub fn encode_blobs<'a, I, T: 'a>(blobs: &'a I) -> (Vec<u8>, usize)
where
&'a I: IntoIterator<Item = &'a T>,
T: AsRef<[u8]>,
{
let mut idx_map = BTreeMap::new();
blobs
.into_iter()
.map(|v| v.as_ref())
.filter(|blob| !blob.is_empty())
.for_each(|blob| {
let v = idx_map.entry(blob.as_ref()).or_insert(0);
*v += 1;
});

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
[0] => 2,
[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();
let idx_len = idx.len();

let rev_idx: BTreeMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

let mut out_buf = Vec::new();
let mut buf = [0u8; 4];
out_buf.extend_from_slice(encode_vlq(idx.len(), &mut buf));
for e in idx {
out_buf.extend_from_slice(encode_vlq(e.len(), &mut buf));
out_buf.extend_from_slice(e);
}

for blob in blobs.into_iter().map(|v| v.as_ref()) {
if let Some(dup_pos) = rev_idx.get(blob) {
let n = (dup_pos << 1) + 1usize;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
} else {
let n = blob.len() << 1;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
out_buf.extend_from_slice(blob);
}
}

(out_buf, idx_len)
}

impl<'a> BlobIterator<'a> {
/// Create new `BlobIterator` for given `data`.
pub fn new(data: &'a [u8]) -> Result<Self, Error> {
Expand Down
16 changes: 16 additions & 0 deletions wycheproof2blb/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
[package]
name = "wycheproof2blb"
version = "0.1.0"
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
description = "A utility for converting Wycheproof test vectors to the blobby format"
repository = "https://github.com/RustCrypto/utils"
keywords = ["test", "Wycheproof"]
categories = ["cryptography", "no-std"]
edition = "2018"

[dependencies]
blobby = { version = "*", path = "../blobby" }
hex = "*"
serde = { version = "*", features = ["derive"] }
serde_json = "*"
89 changes: 89 additions & 0 deletions wycheproof2blb/src/aead.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
use crate::wycheproof;
use crate::wycheproof::{case_result, description, hex_string};
use crate::TestInfo;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct TestSuite {
#[serde(flatten)]
pub suite: wycheproof::Suite,
#[serde(rename = "testGroups")]
pub test_groups: Vec<TestGroup>,
}

#[derive(Debug, Deserialize)]
pub struct TestGroup {
#[serde(flatten)]
pub group: wycheproof::Group,
#[serde(rename = "ivSize")]
pub iv_size: u32,
#[serde(rename = "keySize")]
pub key_size: u32,
#[serde(rename = "tagSize")]
pub tag_size: u32,
pub tests: Vec<TestCase>,
}

#[derive(Debug, Deserialize)]
pub struct TestCase {
#[serde(flatten)]
pub case: wycheproof::Case,
#[serde(with = "hex_string")]
pub aad: Vec<u8>,
#[serde(with = "hex_string")]
pub ct: Vec<u8>,
#[serde(with = "hex_string")]
pub iv: Vec<u8>,
#[serde(with = "hex_string")]
pub key: Vec<u8>,
#[serde(with = "hex_string")]
pub msg: Vec<u8>,
#[serde(with = "hex_string")]
pub tag: Vec<u8>,
}

pub fn aes_gcm_generator(data: &[u8], algorithm: &str, key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, key_size, 12 * 8)
}

pub fn chacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 12 * 8)
}

pub fn xchacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 24 * 8)
}

fn generator(data: &[u8], algorithm: &str, key_size: u32, iv_size: u32) -> Vec<TestInfo> {
let suite: TestSuite = serde_json::from_slice(data).unwrap();
assert_eq!(algorithm, suite.suite.algorithm);

let mut infos = vec![];
for g in &suite.test_groups {
for tc in &g.tests {
if key_size != 0 && g.key_size != key_size {
continue;
}
if g.iv_size != iv_size {
println!(" skipping tests for iv_size={}", g.iv_size);
continue;
}
let mut combined_ct = Vec::new();
combined_ct.extend_from_slice(&tc.ct);
combined_ct.extend_from_slice(&tc.tag);

infos.push(TestInfo {
data: vec![
tc.key.clone(),
tc.iv.clone(),
tc.aad.clone(),
tc.msg.clone(),
combined_ct,
vec![case_result(&tc.case)],
],
desc: description(&suite.suite, &tc.case),
});
}
}
infos
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,5 +18,6 @@ members = [
"pkcs5",
"pkcs8",
"spki",
"wycheproof2blb",
"x509"
]
77 changes: 5 additions & 72 deletions blobby/examples/convert.rs
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,19 @@
//! Convert utility
use blobby::BlobIterator;
use std::collections::HashMap;
use blobby::{encode_blobs, BlobIterator};
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::{env, error::Error, fs::File};

const NEXT_MASK: u8 = 0b1000_0000;
const VAL_MASK: u8 = 0b0111_1111;

fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

fn encode(reader: impl BufRead, mut writer: impl Write) -> io::Result<usize> {
let mut blobs = Vec::new();
for line in reader.lines() {
let blob = hex::decode(line?.as_str())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
blobs.push(blob);
}

let mut idx_map = HashMap::new();
for blob in blobs.iter().filter(|b| b.len() != 0) {
let v = idx_map.entry(blob.as_slice()).or_insert(0);
*v += 1;
}

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
&[0] => 2,
&[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();

let rev_idx: HashMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

println!("Index len: {:?}", idx.len());
let mut buf = [0u8; 4];
writer.write_all(encode_vlq(idx.len(), &mut buf))?;
for e in idx {
writer.write_all(encode_vlq(e.len(), &mut buf))?;
writer.write_all(e)?;
}

for blob in blobs.iter() {
if let Some(dup_pos) = rev_idx.get(blob.as_slice()) {
let n = (dup_pos << 1) + 1usize;
writer.write_all(encode_vlq(n, &mut buf))?;
} else {
let n = blob.len() << 1;
writer.write_all(encode_vlq(n, &mut buf))?;
writer.write_all(blob)?;
}
}

Ok(blobs.len())
let (data, idx_len) = encode_blobs(&blobs);
let data_len = data.len();
println!("Index len: {:?}", idx_len);
writer.write_all(&data).map(|_| data_len)
}

fn decode<R: BufRead, W: Write>(mut reader: R, mut writer: W) -> io::Result<usize> {
Expand Down
96 changes: 95 additions & 1 deletion blobby/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@
)]
extern crate alloc;

use alloc::{boxed::Box, vec, vec::Vec};
use alloc::{boxed::Box, collections::BTreeMap, vec, vec::Vec};
use core::iter::Iterator;

/// Iterator over binary blobs
Expand DownExpand Up@@ -114,6 +114,100 @@ fn read_vlq(data: &[u8], pos: &mut usize) -> Result<usize, Error> {
Ok(val)
}

/// Write a git-flavoured VLQ value into `buf`.
///
/// Returns the slice within `buf` that holds the value.
fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

/// Encode the given collection of binary blobs in .blb format into `writer`.
/// Returns the encoded data together with a count of the number of blobs included in the index.
///
/// The encoded file format is:
/// - count of index entries=N
/// - N x index entries, each encoded as:
/// - size L of index entry (VLQ)
/// - index blob contents (L bytes)
/// - repeating encoded blobs, each encoded as:
/// - VLQ value that is either:
/// - (J << 1) & 0x01: indicates this blob is index entry J
/// - (L << 1) & 0x00: indicates an explicit blob of len L
/// - (in the latter case) explicit blob contents (L bytes)
pub fn encode_blobs<'a, I, T: 'a>(blobs: &'a I) -> (Vec<u8>, usize)
where
&'a I: IntoIterator<Item = &'a T>,
T: AsRef<[u8]>,
{
let mut idx_map = BTreeMap::new();
blobs
.into_iter()
.map(|v| v.as_ref())
.filter(|blob| !blob.is_empty())
.for_each(|blob| {
let v = idx_map.entry(blob.as_ref()).or_insert(0);
*v += 1;
});

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
[0] => 2,
[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();
let idx_len = idx.len();

let rev_idx: BTreeMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

let mut out_buf = Vec::new();
let mut buf = [0u8; 4];
out_buf.extend_from_slice(encode_vlq(idx.len(), &mut buf));
for e in idx {
out_buf.extend_from_slice(encode_vlq(e.len(), &mut buf));
out_buf.extend_from_slice(e);
}

for blob in blobs.into_iter().map(|v| v.as_ref()) {
if let Some(dup_pos) = rev_idx.get(blob) {
let n = (dup_pos << 1) + 1usize;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
} else {
let n = blob.len() << 1;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
out_buf.extend_from_slice(blob);
}
}

(out_buf, idx_len)
}

impl<'a> BlobIterator<'a> {
/// Create new `BlobIterator` for given `data`.
pub fn new(data: &'a [u8]) -> Result<Self, Error> {
Expand Down
16 changes: 16 additions & 0 deletions wycheproof2blb/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
[package]
name = "wycheproof2blb"
version = "0.1.0"
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
description = "A utility for converting Wycheproof test vectors to the blobby format"
repository = "https://github.com/RustCrypto/utils"
keywords = ["test", "Wycheproof"]
categories = ["cryptography", "no-std"]
edition = "2018"

[dependencies]
blobby = { version = "*", path = "../blobby" }
hex = "*"
serde = { version = "*", features = ["derive"] }
serde_json = "*"
89 changes: 89 additions & 0 deletions wycheproof2blb/src/aead.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
use crate::wycheproof;
use crate::wycheproof::{case_result, description, hex_string};
use crate::TestInfo;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct TestSuite {
#[serde(flatten)]
pub suite: wycheproof::Suite,
#[serde(rename = "testGroups")]
pub test_groups: Vec<TestGroup>,
}

#[derive(Debug, Deserialize)]
pub struct TestGroup {
#[serde(flatten)]
pub group: wycheproof::Group,
#[serde(rename = "ivSize")]
pub iv_size: u32,
#[serde(rename = "keySize")]
pub key_size: u32,
#[serde(rename = "tagSize")]
pub tag_size: u32,
pub tests: Vec<TestCase>,
}

#[derive(Debug, Deserialize)]
pub struct TestCase {
#[serde(flatten)]
pub case: wycheproof::Case,
#[serde(with = "hex_string")]
pub aad: Vec<u8>,
#[serde(with = "hex_string")]
pub ct: Vec<u8>,
#[serde(with = "hex_string")]
pub iv: Vec<u8>,
#[serde(with = "hex_string")]
pub key: Vec<u8>,
#[serde(with = "hex_string")]
pub msg: Vec<u8>,
#[serde(with = "hex_string")]
pub tag: Vec<u8>,
}

pub fn aes_gcm_generator(data: &[u8], algorithm: &str, key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, key_size, 12 * 8)
}

pub fn chacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 12 * 8)
}

pub fn xchacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 24 * 8)
}

fn generator(data: &[u8], algorithm: &str, key_size: u32, iv_size: u32) -> Vec<TestInfo> {
let suite: TestSuite = serde_json::from_slice(data).unwrap();
assert_eq!(algorithm, suite.suite.algorithm);

let mut infos = vec![];
for g in &suite.test_groups {
for tc in &g.tests {
if key_size != 0 && g.key_size != key_size {
continue;
}
if g.iv_size != iv_size {
println!(" skipping tests for iv_size={}", g.iv_size);
continue;
}
let mut combined_ct = Vec::new();
combined_ct.extend_from_slice(&tc.ct);
combined_ct.extend_from_slice(&tc.tag);

infos.push(TestInfo {
data: vec![
tc.key.clone(),
tc.iv.clone(),
tc.aad.clone(),
tc.msg.clone(),
combined_ct,
vec![case_result(&tc.case)],
],
desc: description(&suite.suite, &tc.case),
});
}
}
infos
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,5 +18,6 @@ members = [
"pkcs5",
"pkcs8",
"spki",
"wycheproof2blb",
"x509"
]
77 changes: 5 additions & 72 deletions blobby/examples/convert.rs
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,19 @@
//! Convert utility
use blobby::BlobIterator;
use std::collections::HashMap;
use blobby::{encode_blobs, BlobIterator};
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::{env, error::Error, fs::File};

const NEXT_MASK: u8 = 0b1000_0000;
const VAL_MASK: u8 = 0b0111_1111;

fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

fn encode(reader: impl BufRead, mut writer: impl Write) -> io::Result<usize> {
let mut blobs = Vec::new();
for line in reader.lines() {
let blob = hex::decode(line?.as_str())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
blobs.push(blob);
}

let mut idx_map = HashMap::new();
for blob in blobs.iter().filter(|b| b.len() != 0) {
let v = idx_map.entry(blob.as_slice()).or_insert(0);
*v += 1;
}

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
&[0] => 2,
&[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();

let rev_idx: HashMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

println!("Index len: {:?}", idx.len());
let mut buf = [0u8; 4];
writer.write_all(encode_vlq(idx.len(), &mut buf))?;
for e in idx {
writer.write_all(encode_vlq(e.len(), &mut buf))?;
writer.write_all(e)?;
}

for blob in blobs.iter() {
if let Some(dup_pos) = rev_idx.get(blob.as_slice()) {
let n = (dup_pos << 1) + 1usize;
writer.write_all(encode_vlq(n, &mut buf))?;
} else {
let n = blob.len() << 1;
writer.write_all(encode_vlq(n, &mut buf))?;
writer.write_all(blob)?;
}
}

Ok(blobs.len())
let (data, idx_len) = encode_blobs(&blobs);
let data_len = data.len();
println!("Index len: {:?}", idx_len);
writer.write_all(&data).map(|_| data_len)
}

fn decode<R: BufRead, W: Write>(mut reader: R, mut writer: W) -> io::Result<usize> {
Expand Down
96 changes: 95 additions & 1 deletion blobby/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@
)]
extern crate alloc;

use alloc::{boxed::Box, vec, vec::Vec};
use alloc::{boxed::Box, collections::BTreeMap, vec, vec::Vec};
use core::iter::Iterator;

/// Iterator over binary blobs
Expand DownExpand Up@@ -114,6 +114,100 @@ fn read_vlq(data: &[u8], pos: &mut usize) -> Result<usize, Error> {
Ok(val)
}

/// Write a git-flavoured VLQ value into `buf`.
///
/// Returns the slice within `buf` that holds the value.
fn encode_vlq(mut val: usize, buf: &mut [u8; 4]) -> &[u8] {
macro_rules! step {
($n:expr) => {
buf[$n] = if $n == 3 {
(val & (VAL_MASK as usize)) as u8
} else {
val -= 1;
NEXT_MASK | (val & (VAL_MASK as usize)) as u8
};
val >>= 7;
if val == 0 {
return &buf[$n..];
}
};
}

step!(3);
step!(2);
step!(1);
step!(0);
panic!("integer is too big")
}

/// Encode the given collection of binary blobs in .blb format into `writer`.
/// Returns the encoded data together with a count of the number of blobs included in the index.
///
/// The encoded file format is:
/// - count of index entries=N
/// - N x index entries, each encoded as:
/// - size L of index entry (VLQ)
/// - index blob contents (L bytes)
/// - repeating encoded blobs, each encoded as:
/// - VLQ value that is either:
/// - (J << 1) & 0x01: indicates this blob is index entry J
/// - (L << 1) & 0x00: indicates an explicit blob of len L
/// - (in the latter case) explicit blob contents (L bytes)
pub fn encode_blobs<'a, I, T: 'a>(blobs: &'a I) -> (Vec<u8>, usize)
where
&'a I: IntoIterator<Item = &'a T>,
T: AsRef<[u8]>,
{
let mut idx_map = BTreeMap::new();
blobs
.into_iter()
.map(|v| v.as_ref())
.filter(|blob| !blob.is_empty())
.for_each(|blob| {
let v = idx_map.entry(blob.as_ref()).or_insert(0);
*v += 1;
});

let mut idx: Vec<&[u8]> = idx_map
.iter()
.filter(|(_, &v)| v > 1)
.map(|(&k, _)| k)
.collect();
idx.sort_by_key(|e| {
let k = match e {
[0] => 2,
[1] => 1,
_ => 0,
};
(k, idx_map.get(e).unwrap())
});
idx.reverse();
let idx_len = idx.len();

let rev_idx: BTreeMap<&[u8], usize> = idx.iter().enumerate().map(|(i, &e)| (e, i)).collect();

let mut out_buf = Vec::new();
let mut buf = [0u8; 4];
out_buf.extend_from_slice(encode_vlq(idx.len(), &mut buf));
for e in idx {
out_buf.extend_from_slice(encode_vlq(e.len(), &mut buf));
out_buf.extend_from_slice(e);
}

for blob in blobs.into_iter().map(|v| v.as_ref()) {
if let Some(dup_pos) = rev_idx.get(blob) {
let n = (dup_pos << 1) + 1usize;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
} else {
let n = blob.len() << 1;
out_buf.extend_from_slice(encode_vlq(n, &mut buf));
out_buf.extend_from_slice(blob);
}
}

(out_buf, idx_len)
}

impl<'a> BlobIterator<'a> {
/// Create new `BlobIterator` for given `data`.
pub fn new(data: &'a [u8]) -> Result<Self, Error> {
Expand Down
16 changes: 16 additions & 0 deletions wycheproof2blb/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
[package]
name = "wycheproof2blb"
version = "0.1.0"
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
description = "A utility for converting Wycheproof test vectors to the blobby format"
repository = "https://github.com/RustCrypto/utils"
keywords = ["test", "Wycheproof"]
categories = ["cryptography", "no-std"]
edition = "2018"

[dependencies]
blobby = { version = "*", path = "../blobby" }
hex = "*"
serde = { version = "*", features = ["derive"] }
serde_json = "*"
89 changes: 89 additions & 0 deletions wycheproof2blb/src/aead.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
use crate::wycheproof;
use crate::wycheproof::{case_result, description, hex_string};
use crate::TestInfo;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct TestSuite {
#[serde(flatten)]
pub suite: wycheproof::Suite,
#[serde(rename = "testGroups")]
pub test_groups: Vec<TestGroup>,
}

#[derive(Debug, Deserialize)]
pub struct TestGroup {
#[serde(flatten)]
pub group: wycheproof::Group,
#[serde(rename = "ivSize")]
pub iv_size: u32,
#[serde(rename = "keySize")]
pub key_size: u32,
#[serde(rename = "tagSize")]
pub tag_size: u32,
pub tests: Vec<TestCase>,
}

#[derive(Debug, Deserialize)]
pub struct TestCase {
#[serde(flatten)]
pub case: wycheproof::Case,
#[serde(with = "hex_string")]
pub aad: Vec<u8>,
#[serde(with = "hex_string")]
pub ct: Vec<u8>,
#[serde(with = "hex_string")]
pub iv: Vec<u8>,
#[serde(with = "hex_string")]
pub key: Vec<u8>,
#[serde(with = "hex_string")]
pub msg: Vec<u8>,
#[serde(with = "hex_string")]
pub tag: Vec<u8>,
}

pub fn aes_gcm_generator(data: &[u8], algorithm: &str, key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, key_size, 12 * 8)
}

pub fn chacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 12 * 8)
}

pub fn xchacha20_poly1305(data: &[u8], algorithm: &str, _key_size: u32) -> Vec<TestInfo> {
generator(data, algorithm, 256, 24 * 8)
}

fn generator(data: &[u8], algorithm: &str, key_size: u32, iv_size: u32) -> Vec<TestInfo> {
let suite: TestSuite = serde_json::from_slice(data).unwrap();
assert_eq!(algorithm, suite.suite.algorithm);

let mut infos = vec![];
for g in &suite.test_groups {
for tc in &g.tests {
if key_size != 0 && g.key_size != key_size {
continue;
}
if g.iv_size != iv_size {
println!(" skipping tests for iv_size={}", g.iv_size);
continue;
}
let mut combined_ct = Vec::new();
combined_ct.extend_from_slice(&tc.ct);
combined_ct.extend_from_slice(&tc.tag);

infos.push(TestInfo {
data: vec![
tc.key.clone(),
tc.iv.clone(),
tc.aad.clone(),
tc.msg.clone(),
combined_ct,
vec![case_result(&tc.case)],
],
desc: description(&suite.suite, &tc.case),
});
}
}
infos
}
Loading