1
0
Fork 0
mirror of https://github.com/BLAKE3-team/BLAKE3 synced 2024-05-27 21:06:05 +02:00

Merge branch 'master' into master

This commit is contained in:
Jack O'Connor 2023-04-22 19:59:36 -04:00 committed by GitHub
commit 37f7b54994
Signed by: GitHub
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 775 additions and 486 deletions

View File

@ -9,8 +9,9 @@ import sys
ROOT = Path(__file__).parent.parent.parent
RUST_TARGET = sys.argv[1]
subprocess.run(["cargo", "build", "--target", sys.argv[1], "--release"],
cwd=ROOT / "b3sum")
subprocess.run(
["cargo", "build", "--target", sys.argv[1], "--release"], cwd=ROOT / "b3sum"
)
if platform.system() == "Windows":
original_exe_name = "b3sum.exe"

View File

@ -25,10 +25,20 @@ jobs:
{ "os": "windows-latest", "toolchain": "x86_64-pc-windows-msvc", "name": "Windows MSVC" },
{ "os": "windows-latest", "toolchain": "x86_64-pc-windows-gnu", "name": "Windows GNU" }
]
channel: [stable, beta, nightly]
channel: [
"stable",
"beta",
"nightly",
# The current MSRV. This crate doesn't have an official MSRV policy,
# but in practice we'll probably do what libc does:
# https://github.com/rust-lang/libs-team/issues/72.
# This test target is here so that we notice if we accidentally bump
# the MSRV, but it's not a promise that we won't bump it.
"1.60.0",
]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: ${{ format('{0}-{1}', matrix.channel, matrix.target.toolchain) }}
@ -140,7 +150,7 @@ jobs:
- mips-unknown-linux-gnu
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
@ -170,7 +180,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
# Test the intrinsics-based implementations.
- run: make -f Makefile.testing test
working-directory: ./c
@ -222,7 +232,7 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
@ -234,6 +244,38 @@ jobs:
run: cargo build --target aarch64-apple-darwin
working-directory: ./b3sum
build_tinycc:
name: build with the Tiny C Compiler
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: install TCC
run: sudo apt-get install -y tcc
- name: compile
run: >
tcc -shared -O3 -o libblake3.so \
-DBLAKE3_NO_SSE2 -DBLAKE3_NO_SSE41 -DBLAKE3_NO_AVX2 -DBLAKE3_NO_AVX512 \
blake3.c blake3_dispatch.c blake3_portable.c
working-directory: ./c
# See https://github.com/BLAKE3-team/BLAKE3/issues/271 for why we test this.
# Note that this isn't guaranteed to execute on an AVX-512-supporting server,
# but hopefully at least some of the time it will.
gcc54:
name: "compile and test with GCC 5.4"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: addnab/docker-run-action@v3
with:
image: gcc:5.4
options: -v ${{ github.workspace }}:/work
run: |
cat /proc/cpuinfo
curl https://sh.rustup.rs -sSf | sh -s -- -y --profile minimal
cd /work
~/.cargo/bin/cargo test --features prefer_intrinsics
# CMake build test (Library only), current macOS/Linux only.
cmake_build:
name: CMake ${{ matrix.os }}

View File

@ -23,8 +23,8 @@ jobs:
]
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v1
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.x"
- run: pip install PyGithub

View File

@ -3,6 +3,7 @@
import github
import os
import sys
import time
RETRIES = 10
@ -10,7 +11,7 @@ g = github.Github(os.environ["GITHUB_TOKEN"])
tag_name = os.environ["GITHUB_TAG"]
tag_prefix = "refs/tags/"
if tag_name.startswith(tag_prefix):
tag_name = tag_name[len(tag_prefix):]
tag_name = tag_name[len(tag_prefix) :]
assert len(sys.argv) == 2
asset_path = sys.argv[1]
asset_name = os.path.basename(asset_path)
@ -34,13 +35,20 @@ except github.GithubException as github_error:
else:
raise
releases = list(repo.get_releases())
for release in releases:
if release.tag_name == tag_name:
break
else:
def get_release():
for i in range(RETRIES):
releases = list(repo.get_releases())
for release in releases:
if release.tag_name == tag_name:
return release
print(f"Release for tag {repr(tag_name)} not found. Retrying...")
time.sleep(1)
raise RuntimeError("no release for tag " + repr(tag_name))
release = get_release()
print("Uploading " + repr(asset_path) + "...")
for i in range(RETRIES):
try:

View File

@ -1,13 +1,13 @@
[package]
name = "blake3"
version = "1.3.1"
version = "1.3.3"
authors = ["Jack O'Connor <oconnor663@gmail.com>", "Samuel Neves"]
description = "the BLAKE3 hash function"
repository = "https://github.com/BLAKE3-team/BLAKE3"
license = "CC0-1.0 OR Apache-2.0"
documentation = "https://docs.rs/blake3"
readme = "README.md"
edition = "2018"
edition = "2021"
[features]
default = ["std"]
@ -38,7 +38,9 @@ std = ["digest/std"]
# "signature" crate.)
traits-preview = ["digest"]
# ---------- Features below this line are for internal testing only. ----------
# ---------- Features below this line are undocumented and unstable. ----------
# The following features are mainly intended for testing and benchmarking, and
# they might change or disappear at any time without a major version bump.
# By default on x86_64, this crate uses Samuel Neves' hand-written assembly
# implementations for SSE4.1, AVX2, and AVX512. (These provide both the best
@ -64,7 +66,7 @@ prefer_intrinsics = []
#
# As noted above, these flags are *for testing only* and are not stable. It's
# possible that some users might find that their particular use case performs
# better if e.g. AVX-512 is disabled, because of issues like CPU downlocking.
# better if e.g. AVX-512 is disabled, because of issues like CPU downclocking.
# If that comes up, and if disabling the instruction set here at the feature
# level turns out to be the right approach, then we can design a stable
# feature. Until then, we reserve the right to break these features in a patch
@ -82,14 +84,14 @@ features = ["rayon"]
[dependencies]
arrayref = "0.3.5"
arrayvec = { version = "0.7.0", default-features = false }
constant_time_eq = "0.1.5"
constant_time_eq = "0.2.4"
rayon = { version = "1.2.1", optional = true }
cfg-if = "1.0.0"
digest = { version = "0.10.1", features = [ "mac" ], optional = true }
[dev-dependencies]
hex = "0.4.2"
page_size = "0.4.1"
page_size = "0.5.0"
rand = "0.8.0"
rand_chacha = "0.3.0"
reference_impl = { path = "./reference_impl" }

View File

@ -45,7 +45,7 @@ This repository is the official implementation of BLAKE3. It includes:
* The [C implementation](c), which like the Rust implementation includes
SIMD code and runtime CPU feature detection on x86. Unlike the Rust
implementation, it's not currently multithreaded. See
implementation, it's [not currently multithreaded](c#multithreading). See
[`c/README.md`](c/README.md).
* The [Rust reference implementation](reference_impl/reference_impl.rs),
@ -200,11 +200,12 @@ Alternatively, it is licensed under the Apache License 2.0.
Here's a (non-exhaustive) list of protocols and software that use BLAKE3:
* [Alephium](https://github.com/alephium/alephium/blob/master/crypto/src/main/scala/org/alephium/crypto/Blake3.scala)
* [Chia](https://github.com/Chia-Network/chia-blockchain/blob/main/CHANGELOG.md#10beta8-aka-beta-18---2020-07-16)
* [IPFS](https://github.com/ipfs/go-verifcid/issues/13)
* [LLVM](https://reviews.llvm.org/D121510)
* [Nym](https://github.com/nymtech/nym/blob/59056a22c5e6b01a38da2124662bd1fa3c8abef2/common/nymsphinx/params/src/lib.rs#L5)
* [OpenZFS](https://github.com/openzfs/zfs/pull/11897)
* [OpenZFS](https://github.com/openzfs/zfs/)
* [Redox](https://www.redox-os.org/news/pkgar-introduction/)
* [Skale](https://github.com/skalenetwork/skale-consensus/pull/284)
* [Solana](https://docs.rs/solana-program/1.9.5/solana_program/blake3/index.html)

387
b3sum/Cargo.lock generated
View File

@ -4,9 +4,9 @@ version = 3
[[package]]
name = "anyhow"
version = "1.0.56"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
[[package]]
name = "arrayref"
@ -20,17 +20,6 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -39,7 +28,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "b3sum"
version = "1.3.1"
version = "1.3.3"
dependencies = [
"anyhow",
"blake3",
@ -60,7 +49,7 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "blake3"
version = "1.3.1"
version = "1.3.3"
dependencies = [
"arrayref",
"arrayvec",
@ -73,18 +62,18 @@ dependencies = [
[[package]]
name = "block-buffer"
version = "0.10.2"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
dependencies = [
"generic-array",
]
[[package]]
name = "cc"
version = "1.0.73"
version = "1.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
[[package]]
name = "cfg-if"
@ -94,30 +83,53 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "3.1.6"
version = "4.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123"
checksum = "0acbd8d28a0a60d7108d7ae850af6ba34cf2d1257fc646980e5f97ce14275966"
dependencies = [
"atty",
"bitflags",
"indexmap",
"os_str_bytes",
"clap_derive",
"clap_lex",
"is-terminal",
"once_cell",
"strsim",
"termcolor",
"textwrap",
"terminal_size",
]
[[package]]
name = "clap_derive"
version = "4.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279"
[[package]]
name = "crossbeam-channel"
version = "0.5.4"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
dependencies = [
"cfg-if",
"crossbeam-utils",
@ -125,9 +137,9 @@ dependencies = [
[[package]]
name = "crossbeam-deque"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
dependencies = [
"cfg-if",
"crossbeam-epoch",
@ -136,33 +148,31 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.8"
version = "0.9.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"lazy_static",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.8"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
dependencies = [
"cfg-if",
"lazy_static",
]
[[package]]
name = "crypto-common"
version = "0.1.3"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
@ -170,9 +180,9 @@ dependencies = [
[[package]]
name = "digest"
version = "0.10.3"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
dependencies = [
"block-buffer",
"crypto-common",
@ -193,24 +203,45 @@ dependencies = [
[[package]]
name = "either"
version = "1.6.1"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "fastrand"
version = "1.7.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
dependencies = [
"instant",
]
[[package]]
name = "generic-array"
version = "0.14.5"
version = "0.14.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
dependencies = [
"typenum",
"version_check",
@ -223,10 +254,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "hashbrown"
version = "0.11.2"
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
@ -237,22 +268,21 @@ dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "indexmap"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "instant"
version = "0.1.12"
@ -263,56 +293,84 @@ dependencies = [
]
[[package]]
name = "lazy_static"
version = "1.4.0"
name = "io-lifetimes"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074"
[[package]]
name = "io-lifetimes"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e394faa0efb47f9f227f1cd89978f854542b318a6f64fa695489c9c993056656"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "is-terminal"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae5bc6e2eb41c9def29a3e0f1306382807764b9b53112030eff57435667352d"
dependencies = [
"hermit-abi 0.2.6",
"io-lifetimes 1.0.2",
"rustix 0.36.3",
"windows-sys",
]
[[package]]
name = "libc"
version = "0.2.121"
version = "0.2.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
[[package]]
name = "memchr"
version = "2.4.1"
name = "linux-raw-sys"
version = "0.0.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d"
[[package]]
name = "linux-raw-sys"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
[[package]]
name = "memmap2"
version = "0.5.3"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f"
checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc"
dependencies = [
"libc",
]
[[package]]
name = "memoffset"
version = "0.6.5"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.13.1"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
dependencies = [
"hermit-abi",
"hermit-abi 0.1.19",
"libc",
]
[[package]]
name = "once_cell"
version = "1.10.0"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]]
name = "os_pipe"
@ -326,20 +384,58 @@ dependencies = [
[[package]]
name = "os_str_bytes"
version = "6.0.0"
version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"memchr",
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.5.1"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
dependencies = [
"autocfg",
"crossbeam-deque",
"either",
"rayon-core",
@ -347,22 +443,21 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.9.1"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"lazy_static",
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.2.12"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae183fc1b06c149f0c1793e1eb447c8b04bfe46d48e9e48bfb8d2d7ed64ecf0"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
@ -376,6 +471,34 @@ dependencies = [
"winapi",
]
[[package]]
name = "rustix"
version = "0.35.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9"
dependencies = [
"bitflags",
"errno",
"io-lifetimes 0.7.5",
"libc",
"linux-raw-sys 0.0.46",
"windows-sys",
]
[[package]]
name = "rustix"
version = "0.36.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b1fbb4dfc4eb1d390c02df47760bb19a84bb80b301ecc947ab5406394d8223e"
dependencies = [
"bitflags",
"errno",
"io-lifetimes 1.0.2",
"libc",
"linux-raw-sys 0.1.3",
"windows-sys",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
@ -404,6 +527,17 @@ version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.3.0"
@ -428,10 +562,14 @@ dependencies = [
]
[[package]]
name = "textwrap"
version = "0.15.0"
name = "terminal_size"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
checksum = "40ca90c434fd12083d1a6bdcbe9f92a14f96c8a1ba600ba451734ac334521f7a"
dependencies = [
"rustix 0.35.13",
"windows-sys",
]
[[package]]
name = "typenum"
@ -439,6 +577,12 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unicode-ident"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
[[package]]
name = "version_check"
version = "0.9.4"
@ -447,9 +591,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wild"
version = "2.0.4"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "035793abb854745033f01a07647a79831eba29ec0be377205f2a25b0aa830020"
checksum = "05b116685a6be0c52f5a103334cbff26db643826c7b3735fc0a3ba9871310a74"
dependencies = [
"glob",
]
@ -484,3 +628,60 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
name = "windows_i686_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
name = "windows_i686_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"

View File

@ -1,12 +1,12 @@
[package]
name = "b3sum"
version = "1.3.1"
version = "1.3.3"
authors = ["Jack O'Connor <oconnor663@gmail.com>"]
description = "a command line implementation of the BLAKE3 hash function"
repository = "https://github.com/BLAKE3-team/BLAKE3"
license = "CC0-1.0 OR Apache-2.0"
readme = "README.md"
edition = "2018"
edition = "2021"
[features]
neon = ["blake3/neon"]
@ -16,7 +16,7 @@ pure = ["blake3/pure"]
[dependencies]
anyhow = "1.0.25"
blake3 = { version = "1", path = "..", features = ["rayon"] }
clap = "3.0.5"
clap = { version = "4.0.8", features = ["derive", "wrap_help"] }
hex = "0.4.0"
memmap2 = "0.5.3"
rayon = "1.2.1"

View File

@ -5,37 +5,23 @@ A command line utility for calculating
Coreutils tools like `b2sum` or `md5sum`.
```
b3sum 1.3.1
Usage: b3sum [OPTIONS] [FILE]...
USAGE:
b3sum [OPTIONS] [FILE]...
Arguments:
[FILE]... Files to hash, or checkfiles to check
ARGS:
<FILE>... Files to hash, or checkfiles to check. When no file is given,
or when - is given, read standard input.
OPTIONS:
-c, --check Reads BLAKE3 sums from the [FILE]s and checks them
--derive-key <CONTEXT> Uses the key derivation mode, with the given
context string. Cannot be used with --keyed.
-h, --help Print help information
--keyed Uses the keyed mode. The secret key is read from standard
input, and it must be exactly 32 raw bytes.
-l, --length <LEN> The number of output bytes, prior to hex
encoding (default 32)
--no-mmap Disables memory mapping. Currently this also disables
multithreading.
--no-names Omits filenames in the output
--num-threads <NUM> The maximum number of threads to use. By
default, this is the number of logical cores.
If this flag is omitted, or if its value is 0,
RAYON_NUM_THREADS is also respected.
--quiet Skips printing OK for each successfully verified file.
Must be used with --check.
--raw Writes raw output bytes to stdout, rather than hex.
--no-names is implied. In this case, only a single
input is allowed.
-V, --version Print version information
Options:
-l, --length <LEN> The number of output bytes, before hex encoding [default: 32]
--num-threads <NUM> The maximum number of threads to use
--keyed Use the keyed mode
--derive-key <CONTEXT> Use the key derivation mode, with the given context string
--no-mmap Disable memory mapping
--no-names Omit filenames in the output
--raw Write raw output bytes to stdout, rather than hex
-c, --check Read BLAKE3 sums from the [FILE]s and check them
--quiet Skip printing OK for each successfully verified file
-h, --help Print help information (use `--help` for more detail)
-V, --version Print version information
```
See also [this document about how the `--check` flag

View File

@ -1,7 +1,6 @@
use anyhow::{bail, ensure, Context, Result};
use clap::{Arg, Command};
use anyhow::{bail, ensure, Result};
use clap::Parser;
use std::cmp;
use std::convert::TryInto;
use std::fs::File;
use std::io;
use std::io::prelude::*;
@ -12,124 +11,106 @@ mod unit_tests;
const NAME: &str = "b3sum";
const FILE_ARG: &str = "FILE";
const DERIVE_KEY_ARG: &str = "derive-key";
const DERIVE_KEY_ARG: &str = "derive_key";
const KEYED_ARG: &str = "keyed";
const LENGTH_ARG: &str = "length";
const NO_MMAP_ARG: &str = "no-mmap";
const NO_NAMES_ARG: &str = "no-names";
const NUM_THREADS_ARG: &str = "num-threads";
const NO_NAMES_ARG: &str = "no_names";
const RAW_ARG: &str = "raw";
const CHECK_ARG: &str = "check";
const QUIET_ARG: &str = "quiet";
#[derive(Parser)]
#[command(version, max_term_width(100))]
struct Inner {
/// Files to hash, or checkfiles to check
///
/// When no file is given, or when - is given, read standard input.
file: Vec<PathBuf>,
/// The number of output bytes, before hex encoding
#[arg(
short,
long,
default_value_t = blake3::OUT_LEN as u64,
value_name("LEN")
)]
length: u64,
/// The maximum number of threads to use
///
/// By default, this is the number of logical cores. If this flag is
/// omitted, or if its value is 0, RAYON_NUM_THREADS is also respected.
#[arg(long, value_name("NUM"))]
num_threads: Option<usize>,
/// Use the keyed mode, reading the 32-byte key from stdin
#[arg(long, requires("file"))]
keyed: bool,
/// Use the key derivation mode, with the given context string
///
/// Cannot be used with --keyed.
#[arg(long, value_name("CONTEXT"), conflicts_with(KEYED_ARG))]
derive_key: Option<String>,
/// Disable memory mapping
///
/// Currently this also disables multithreading.
#[arg(long)]
no_mmap: bool,
/// Omit filenames in the output
#[arg(long)]
no_names: bool,
/// Write raw output bytes to stdout, rather than hex
///
/// --no-names is implied. In this case, only a single input is allowed.
#[arg(long)]
raw: bool,
/// Read BLAKE3 sums from the [FILE]s and check them
#[arg(
short,
long,
conflicts_with(DERIVE_KEY_ARG),
conflicts_with(KEYED_ARG),
conflicts_with(LENGTH_ARG),
conflicts_with(RAW_ARG),
conflicts_with(NO_NAMES_ARG)
)]
check: bool,
/// Skip printing OK for each successfully verified file
///
/// Must be used with --check.
#[arg(long, requires(CHECK_ARG))]
quiet: bool,
}
struct Args {
inner: clap::ArgMatches,
inner: Inner,
file_args: Vec<PathBuf>,
base_hasher: blake3::Hasher,
}
impl Args {
fn parse() -> Result<Self> {
let inner = Command::new(NAME)
.version(env!("CARGO_PKG_VERSION"))
.arg(
Arg::new(FILE_ARG)
.multiple_occurrences(true)
.allow_invalid_utf8(true)
.help(
"Files to hash, or checkfiles to check. When no file is given,\n\
or when - is given, read standard input.",
),
)
.arg(
Arg::new(LENGTH_ARG)
.long(LENGTH_ARG)
.short('l')
.takes_value(true)
.value_name("LEN")
.help(
"The number of output bytes, prior to hex\n\
encoding (default 32)",
),
)
.arg(
Arg::new(NUM_THREADS_ARG)
.long(NUM_THREADS_ARG)
.takes_value(true)
.value_name("NUM")
.help(
"The maximum number of threads to use. By\n\
default, this is the number of logical cores.\n\
If this flag is omitted, or if its value is 0,\n\
RAYON_NUM_THREADS is also respected.",
),
)
.arg(Arg::new(KEYED_ARG).long(KEYED_ARG).requires(FILE_ARG).help(
"Uses the keyed mode. The secret key is read from standard\n\
input, and it must be exactly 32 raw bytes.",
))
.arg(
Arg::new(DERIVE_KEY_ARG)
.long(DERIVE_KEY_ARG)
.conflicts_with(KEYED_ARG)
.takes_value(true)
.value_name("CONTEXT")
.help(
"Uses the key derivation mode, with the given\n\
context string. Cannot be used with --keyed.",
),
)
.arg(Arg::new(NO_MMAP_ARG).long(NO_MMAP_ARG).help(
"Disables memory mapping. Currently this also disables\n\
multithreading.",
))
.arg(
Arg::new(NO_NAMES_ARG)
.long(NO_NAMES_ARG)
.help("Omits filenames in the output"),
)
.arg(Arg::new(RAW_ARG).long(RAW_ARG).help(
"Writes raw output bytes to stdout, rather than hex.\n\
--no-names is implied. In this case, only a single\n\
input is allowed.",
))
.arg(
Arg::new(CHECK_ARG)
.long(CHECK_ARG)
.short('c')
.conflicts_with(DERIVE_KEY_ARG)
.conflicts_with(KEYED_ARG)
.conflicts_with(LENGTH_ARG)
.conflicts_with(RAW_ARG)
.conflicts_with(NO_NAMES_ARG)
.help("Reads BLAKE3 sums from the [FILE]s and checks them"),
)
.arg(
Arg::new(QUIET_ARG)
.long(QUIET_ARG)
.requires(CHECK_ARG)
.help(
"Skips printing OK for each successfully verified file.\n\
Must be used with --check.",
),
)
// wild::args_os() is equivalent to std::env::args_os() on Unix,
// but on Windows it adds support for globbing.
.get_matches_from(wild::args_os());
let file_args = if let Some(iter) = inner.values_of_os(FILE_ARG) {
iter.map(|s| s.into()).collect()
// wild::args_os() is equivalent to std::env::args_os() on Unix,
// but on Windows it adds support for globbing.
let inner = Inner::parse_from(wild::args_os());
let file_args = if !inner.file.is_empty() {
inner.file.clone()
} else {
vec!["-".into()]
};
if inner.is_present(RAW_ARG) && file_args.len() > 1 {
if inner.raw && file_args.len() > 1 {
bail!("Only one filename can be provided when using --raw");
}
let base_hasher = if inner.is_present(KEYED_ARG) {
let base_hasher = if inner.keyed {
// In keyed mode, since stdin is used for the key, we can't handle
// `-` arguments. Input::open handles that case below.
blake3::Hasher::new_keyed(&read_key_from_stdin()?)
} else if let Some(context) = inner.value_of(DERIVE_KEY_ARG) {
} else if let Some(ref context) = inner.derive_key {
blake3::Hasher::new_derive_key(context)
} else {
blake3::Hasher::new()
@ -141,48 +122,36 @@ impl Args {
})
}
fn num_threads(&self) -> Result<Option<usize>> {
if let Some(num_threads_str) = self.inner.value_of(NUM_THREADS_ARG) {
Ok(Some(
num_threads_str
.parse()
.context("Failed to parse num threads.")?,
))
} else {
Ok(None)
}
fn num_threads(&self) -> Option<usize> {
self.inner.num_threads
}
fn check(&self) -> bool {
self.inner.is_present(CHECK_ARG)
self.inner.check
}
fn raw(&self) -> bool {
self.inner.is_present(RAW_ARG)
self.inner.raw
}
fn no_mmap(&self) -> bool {
self.inner.is_present(NO_MMAP_ARG)
self.inner.no_mmap
}
fn no_names(&self) -> bool {
self.inner.is_present(NO_NAMES_ARG)
self.inner.no_names
}
fn len(&self) -> Result<u64> {
if let Some(length) = self.inner.value_of(LENGTH_ARG) {
length.parse::<u64>().context("Failed to parse length.")
} else {
Ok(blake3::OUT_LEN as u64)
}
fn len(&self) -> u64 {
self.inner.length
}
fn keyed(&self) -> bool {
self.inner.is_present(KEYED_ARG)
self.inner.keyed
}
fn quiet(&self) -> bool {
self.inner.is_present(QUIET_ARG)
self.inner.quiet
}
}
@ -307,7 +276,7 @@ fn maybe_memmap_file(file: &File) -> Result<Option<memmap2::Mmap>> {
fn write_hex_output(mut output: blake3::OutputReader, args: &Args) -> Result<()> {
// Encoding multiples of the block size is most efficient.
let mut len = args.len()?;
let mut len = args.len();
let mut block = [0; blake3::guts::BLOCK_LEN];
while len > 0 {
output.fill(&mut block);
@ -320,7 +289,7 @@ fn write_hex_output(mut output: blake3::OutputReader, args: &Args) -> Result<()>
}
fn write_raw_output(output: blake3::OutputReader, args: &Args) -> Result<()> {
let mut output = output.take(args.len()?);
let mut output = output.take(args.len());
let stdout = std::io::stdout();
let mut handler = stdout.lock();
std::io::copy(&mut output, &mut handler)?;
@ -334,13 +303,13 @@ fn read_key_from_stdin() -> Result<[u8; blake3::KEY_LEN]> {
.lock()
.take(blake3::KEY_LEN as u64 + 1)
.read_to_end(&mut bytes)?;
if n < 32 {
if n < blake3::KEY_LEN {
bail!(
"expected {} key bytes from stdin, found {}",
blake3::KEY_LEN,
n,
)
} else if n > 32 {
} else if n > blake3::KEY_LEN {
bail!("read more than {} key bytes from stdin", blake3::KEY_LEN)
} else {
Ok(bytes[..blake3::KEY_LEN].try_into().unwrap())
@ -520,8 +489,8 @@ fn hash_one_input(path: &Path, args: &Args) -> Result<()> {
}
// Returns true for success. Having a boolean return value here, instead of
// passing down the some_file_failed reference, makes it less likely that we
// might forget to set it in some error condition.
// passing down the files_failed reference, makes it less likely that we might
// forget to set it in some error condition.
fn check_one_line(line: &str, args: &Args) -> bool {
let parse_result = parse_check_line(&line);
let ParsedCheckLine {
@ -567,7 +536,7 @@ fn check_one_line(line: &str, args: &Args) -> bool {
}
}
fn check_one_checkfile(path: &Path, args: &Args, some_file_failed: &mut bool) -> Result<()> {
fn check_one_checkfile(path: &Path, args: &Args, files_failed: &mut u64) -> Result<()> {
let checkfile_input = Input::open(path, args)?;
let mut bufreader = io::BufReader::new(checkfile_input);
let mut line = String::new();
@ -581,7 +550,9 @@ fn check_one_checkfile(path: &Path, args: &Args, some_file_failed: &mut bool) ->
// return, so it doesn't return a Result.
let success = check_one_line(&line, args);
if !success {
*some_file_failed = true;
// We use `files_failed > 0` to indicate a mismatch, so it's important for correctness
// that it's impossible for this counter to overflow.
*files_failed = files_failed.saturating_add(1);
}
}
}
@ -589,21 +560,16 @@ fn check_one_checkfile(path: &Path, args: &Args, some_file_failed: &mut bool) ->
fn main() -> Result<()> {
let args = Args::parse()?;
let mut thread_pool_builder = rayon::ThreadPoolBuilder::new();
if let Some(num_threads) = args.num_threads()? {
if let Some(num_threads) = args.num_threads() {
thread_pool_builder = thread_pool_builder.num_threads(num_threads);
}
let thread_pool = thread_pool_builder.build()?;
thread_pool.install(|| {
let mut some_file_failed = false;
let mut files_failed = 0u64;
// Note that file_args automatically includes `-` if nothing is given.
for path in &args.file_args {
if args.check() {
// A hash mismatch or a failure to read a hashed file will be
// printed in the checkfile loop, and will not propagate here.
// This is similar to the explicit error handling we do in the
// hashing case immediately below. In these cases,
// some_file_failed will be set to false.
check_one_checkfile(path, &args, &mut some_file_failed)?;
check_one_checkfile(path, &args, &mut files_failed)?;
} else {
// Errors encountered in hashing are tolerated and printed to
// stderr. This allows e.g. `b3sum *` to print errors for
@ -611,11 +577,29 @@ fn main() -> Result<()> {
// errors we'll still return non-zero at the end.
let result = hash_one_input(path, &args);
if let Err(e) = result {
some_file_failed = true;
files_failed = files_failed.saturating_add(1);
eprintln!("{}: {}: {}", NAME, path.to_string_lossy(), e);
}
}
}
std::process::exit(if some_file_failed { 1 } else { 0 });
if args.check() && files_failed > 0 {
eprintln!(
"{}: WARNING: {} computed checksum{} did NOT match",
NAME,
files_failed,
if files_failed == 1 { "" } else { "s" },
);
}
std::process::exit(if files_failed > 0 { 1 } else { 0 });
})
}
#[cfg(test)]
mod test {
use clap::CommandFactory;
#[test]
fn test_args() {
crate::Inner::command().debug_assert();
}
}

View File

@ -113,6 +113,23 @@ fn test_keyed() {
.read()
.unwrap();
assert_eq!(&*expected, &*output);
// Make sure that keys of the wrong length lead to errors.
for bad_length in [0, 1, blake3::KEY_LEN - 1, blake3::KEY_LEN + 1] {
dbg!(bad_length);
let output = cmd!(b3sum_exe(), "--keyed", f.path())
.stdin_bytes(vec![0; bad_length])
.stdout_capture()
.stderr_capture()
.unchecked()
.run()
.unwrap();
assert!(!output.status.success());
assert!(output.stdout.is_empty());
// Make sure the error message is relevant.
let stderr = std::str::from_utf8(&output.stderr).unwrap();
assert!(stderr.contains("key bytes"));
}
}
#[test]
@ -411,7 +428,10 @@ fn test_check() {
c/d: OK\n";
assert!(!output.status.success());
assert_eq!(expected_check_failure, stdout);
assert_eq!("", stderr);
assert_eq!(
"b3sum: WARNING: 1 computed checksum did NOT match\n",
stderr,
);
// Delete one of the files and check again.
fs::remove_file(dir.path().join("b")).unwrap();
@ -433,7 +453,10 @@ fn test_check() {
);
assert!(!output.status.success());
assert_eq!(expected_check_failure, stdout);
assert_eq!("", stderr);
assert_eq!(
"b3sum: WARNING: 1 computed checksum did NOT match\n",
stderr,
);
// Confirm that --quiet suppresses the OKs but not the FAILEDs.
let output = cmd!(b3sum_exe(), "--check", "--quiet", &checkfile_path)
@ -448,7 +471,10 @@ fn test_check() {
let expected_check_failure = format!("b: FAILED ({})\n", open_file_error);
assert!(!output.status.success());
assert_eq!(expected_check_failure, stdout);
assert_eq!("", stderr);
assert_eq!(
"b3sum: WARNING: 1 computed checksum did NOT match\n",
stderr,
);
}
#[test]
@ -463,9 +489,12 @@ fn test_check_invalid_characters() {
.unwrap();
let stdout = std::str::from_utf8(&output.stdout).unwrap();
let stderr = std::str::from_utf8(&output.stderr).unwrap();
let expected_stderr = "\
b3sum: Null character in path\n\
b3sum: WARNING: 1 computed checksum did NOT match\n";
assert!(!output.status.success());
assert_eq!("", stdout);
assert_eq!("b3sum: Null character in path\n", stderr);
assert_eq!(expected_stderr, stderr);
// Check that a Unicode replacement character in the path fails.
let output = cmd!(b3sum_exe(), "--check")
@ -477,9 +506,12 @@ fn test_check_invalid_characters() {
.unwrap();
let stdout = std::str::from_utf8(&output.stdout).unwrap();
let stderr = std::str::from_utf8(&output.stderr).unwrap();
let expected_stderr = "\
b3sum: Unicode replacement character in path\n\
b3sum: WARNING: 1 computed checksum did NOT match\n";
assert!(!output.status.success());
assert_eq!("", stdout);
assert_eq!("b3sum: Unicode replacement character in path\n", stderr);
assert_eq!(expected_stderr, stderr);
// Check that an invalid escape sequence in the path fails.
let output = cmd!(b3sum_exe(), "--check")
@ -491,9 +523,12 @@ fn test_check_invalid_characters() {
.unwrap();
let stdout = std::str::from_utf8(&output.stdout).unwrap();
let stderr = std::str::from_utf8(&output.stderr).unwrap();
let expected_stderr = "\
b3sum: Invalid backslash escape\n\
b3sum: WARNING: 1 computed checksum did NOT match\n";
assert!(!output.status.success());
assert_eq!("", stdout);
assert_eq!("b3sum: Invalid backslash escape\n", stderr);
assert_eq!(expected_stderr, stderr);
// Windows also forbids literal backslashes. Check for that if and only if
// we're on Windows.
@ -507,9 +542,12 @@ fn test_check_invalid_characters() {
.unwrap();
let stdout = std::str::from_utf8(&output.stdout).unwrap();
let stderr = std::str::from_utf8(&output.stderr).unwrap();
let expected_stderr = "\
b3sum: Backslash in path\n\
b3sum: WARNING: 1 computed checksum did NOT match\n";
assert!(!output.status.success());
assert_eq!("", stdout);
assert_eq!("b3sum: Backslash in path\n", stderr);
assert_eq!(expected_stderr, stderr);
}
}

View File

@ -196,14 +196,13 @@ BLAKE3 output is intended to provide N bits of first and second preimage resista
bits of collision resistance, for any N up to 256. Longer outputs don't provide any additional
security.
Avoid relying on the secrecy of the output offset, that is, the number of output bytes read or
the arguments to [`seek`](struct.OutputReader.html#method.seek) or
[`set_position`](struct.OutputReader.html#method.set_position). [_Block-Cipher-Based Tree
Hashing_ by Aldo Gunsing](https://eprint.iacr.org/2022/283) shows that an attacker who knows
both the message and the key (if any) can easily determine the offset of an extended output.
For comparison, AES-CTR has a similar property: if you know the key, you can decrypt a block
from an unknown position in the output stream to recover its block index. Callers with strong
secret keys aren't affected in practice, but secret offsets are a [design
Avoid relying on the secrecy of the output offset, that is, the `seek` argument of
`blake3_hasher_finalize_seek`. [_Block-Cipher-Based Tree Hashing_ by Aldo
Gunsing](https://eprint.iacr.org/2022/283) shows that an attacker who knows both the message
and the key (if any) can easily determine the offset of an extended output. For comparison,
AES-CTR has a similar property: if you know the key, you can decrypt a block from an unknown
position in the output stream to recover its block index. Callers with strong secret keys
aren't affected in practice, but secret offsets are a [design
smell](https://en.wikipedia.org/wiki/Design_smell) in any case.
# Building

View File

@ -246,7 +246,7 @@ INLINE size_t compress_parents_parallel(const uint8_t *child_chaining_values,
// The wide helper function returns (writes out) an array of chaining values
// and returns the length of that array. The number of chaining values returned
// is the dyanmically detected SIMD degree, at most MAX_SIMD_DEGREE. Or fewer,
// is the dynamically detected SIMD degree, at most MAX_SIMD_DEGREE. Or fewer,
// if the input is shorter than that many chunks. The reason for maintaining a
// wide array of chaining values going back up the tree, is to allow the
// implementation to hash as many parents in parallel as possible.

View File

@ -8,7 +8,7 @@
extern "C" {
#endif
#define BLAKE3_VERSION_STRING "1.3.1"
#define BLAKE3_VERSION_STRING "1.3.3"
#define BLAKE3_KEY_LEN 32
#define BLAKE3_OUT_LEN 32
#define BLAKE3_BLOCK_LEN 64

View File

@ -1784,7 +1784,7 @@ blake3_hash_many_avx2:
vmovdqu xmmword ptr [rbx+0x10], xmm1
jmp 4b
.section .rodata
.section .rdata
.p2align 6
ADD0:
.long 0, 1, 2, 3, 4, 5, 6, 7

View File

@ -1047,13 +1047,26 @@ INLINE void transpose_msg_vecs16(const uint8_t *const *inputs,
INLINE void load_counters16(uint64_t counter, bool increment_counter,
__m512i *out_lo, __m512i *out_hi) {
const __m512i mask = _mm512_set1_epi32(-(int32_t)increment_counter);
const __m512i add0 = _mm512_set_epi32(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
const __m512i add1 = _mm512_and_si512(mask, add0);
__m512i l = _mm512_add_epi32(_mm512_set1_epi32((int32_t)counter), add1);
__mmask16 carry = _mm512_cmp_epu32_mask(l, add1, _MM_CMPINT_LT);
__m512i h = _mm512_mask_add_epi32(_mm512_set1_epi32((int32_t)(counter >> 32)), carry, _mm512_set1_epi32((int32_t)(counter >> 32)), _mm512_set1_epi32(1));
*out_lo = l;
*out_hi = h;
const __m512i deltas = _mm512_set_epi32(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
const __m512i masked_deltas = _mm512_and_si512(deltas, mask);
const __m512i low_words = _mm512_add_epi32(
_mm512_set1_epi32((int32_t)counter),
masked_deltas);
// The carry bit is 1 if the high bit of the word was 1 before addition and is
// 0 after.
// NOTE: It would be a bit more natural to use _mm512_cmp_epu32_mask to
// compute the carry bits here, and originally we did, but that intrinsic is
// broken under GCC 5.4. See https://github.com/BLAKE3-team/BLAKE3/issues/271.
const __m512i carries = _mm512_srli_epi32(
_mm512_andnot_si512(
low_words, // 0 after (gets inverted by andnot)
_mm512_set1_epi32((int32_t)counter)), // and 1 before
31);
const __m512i high_words = _mm512_add_epi32(
_mm512_set1_epi32((int32_t)(counter >> 32)),
carries);
*out_lo = low_words;
*out_hi = high_words;
}
static

View File

@ -2587,7 +2587,7 @@ blake3_compress_xof_avx512:
add rsp, 72
ret
.section .rodata
.section .rdata
.p2align 6
INDEX0:
.long 0, 1, 2, 3, 16, 17, 18, 19

View File

@ -7,7 +7,7 @@
name = "blake3_c_rust_bindings"
version = "0.0.0"
description = "TESTING ONLY Rust bindings for the BLAKE3 C implementation"
edition = "2018"
edition = "2021"
[features]
# By default the x86-64 build uses assembly implementations. This feature makes

View File

@ -207,99 +207,107 @@ type HashManyFn = unsafe extern "C" fn(
// A shared helper function for platform-specific tests.
pub fn test_hash_many_fn(hash_many_fn: HashManyFn) {
// 31 (16 + 8 + 4 + 2 + 1) inputs
const NUM_INPUTS: usize = 31;
let mut input_buf = [0; CHUNK_LEN * NUM_INPUTS];
crate::test::paint_test_input(&mut input_buf);
// A counter just prior to u32::MAX.
let counter = (1u64 << 32) - 1;
// Test a few different initial counter values.
// - 0: The base case.
// - u32::MAX: The low word of the counter overflows for all inputs except the first.
// - i32::MAX: *No* overflow. But carry bugs in tricky SIMD code can screw this up, if you XOR
// when you're supposed to ANDNOT...
let initial_counters = [0, u32::MAX as u64, i32::MAX as u64];
for counter in initial_counters {
dbg!(counter);
// First hash chunks.
let mut chunks = ArrayVec::<&[u8; CHUNK_LEN], NUM_INPUTS>::new();
for i in 0..NUM_INPUTS {
chunks.push(array_ref!(input_buf, i * CHUNK_LEN, CHUNK_LEN));
}
let mut portable_chunks_out = [0; NUM_INPUTS * OUT_LEN];
unsafe {
crate::ffi::blake3_hash_many_portable(
chunks.as_ptr() as _,
chunks.len(),
CHUNK_LEN / BLOCK_LEN,
TEST_KEY_WORDS.as_ptr(),
counter,
true,
KEYED_HASH,
CHUNK_START,
CHUNK_END,
portable_chunks_out.as_mut_ptr(),
);
}
// 31 (16 + 8 + 4 + 2 + 1) inputs
const NUM_INPUTS: usize = 31;
let mut input_buf = [0; CHUNK_LEN * NUM_INPUTS];
crate::test::paint_test_input(&mut input_buf);
let mut test_chunks_out = [0; NUM_INPUTS * OUT_LEN];
unsafe {
hash_many_fn(
chunks.as_ptr() as _,
chunks.len(),
CHUNK_LEN / BLOCK_LEN,
TEST_KEY_WORDS.as_ptr(),
counter,
true,
KEYED_HASH,
CHUNK_START,
CHUNK_END,
test_chunks_out.as_mut_ptr(),
);
}
for n in 0..NUM_INPUTS {
dbg!(n);
assert_eq!(
&portable_chunks_out[n * OUT_LEN..][..OUT_LEN],
&test_chunks_out[n * OUT_LEN..][..OUT_LEN]
);
}
// First hash chunks.
let mut chunks = ArrayVec::<&[u8; CHUNK_LEN], NUM_INPUTS>::new();
for i in 0..NUM_INPUTS {
chunks.push(array_ref!(input_buf, i * CHUNK_LEN, CHUNK_LEN));
}
let mut portable_chunks_out = [0; NUM_INPUTS * OUT_LEN];
unsafe {
crate::ffi::blake3_hash_many_portable(
chunks.as_ptr() as _,
chunks.len(),
CHUNK_LEN / BLOCK_LEN,
TEST_KEY_WORDS.as_ptr(),
counter,
true,
KEYED_HASH,
CHUNK_START,
CHUNK_END,
portable_chunks_out.as_mut_ptr(),
);
}
// Then hash parents.
let mut parents = ArrayVec::<&[u8; 2 * OUT_LEN], NUM_INPUTS>::new();
for i in 0..NUM_INPUTS {
parents.push(array_ref!(input_buf, i * 2 * OUT_LEN, 2 * OUT_LEN));
}
let mut portable_parents_out = [0; NUM_INPUTS * OUT_LEN];
unsafe {
crate::ffi::blake3_hash_many_portable(
parents.as_ptr() as _,
parents.len(),
1,
TEST_KEY_WORDS.as_ptr(),
counter,
false,
KEYED_HASH | PARENT,
0,
0,
portable_parents_out.as_mut_ptr(),
);
}
let mut test_chunks_out = [0; NUM_INPUTS * OUT_LEN];
unsafe {
hash_many_fn(
chunks.as_ptr() as _,
chunks.len(),
CHUNK_LEN / BLOCK_LEN,
TEST_KEY_WORDS.as_ptr(),
counter,
true,
KEYED_HASH,
CHUNK_START,
CHUNK_END,
test_chunks_out.as_mut_ptr(),
);
}
for n in 0..NUM_INPUTS {
dbg!(n);
assert_eq!(
&portable_chunks_out[n * OUT_LEN..][..OUT_LEN],
&test_chunks_out[n * OUT_LEN..][..OUT_LEN]
);
}
let mut test_parents_out = [0; NUM_INPUTS * OUT_LEN];
unsafe {
hash_many_fn(
parents.as_ptr() as _,
parents.len(),
1,
TEST_KEY_WORDS.as_ptr(),
counter,
false,
KEYED_HASH | PARENT,
0,
0,
test_parents_out.as_mut_ptr(),
);
}
for n in 0..NUM_INPUTS {
dbg!(n);
assert_eq!(
&portable_parents_out[n * OUT_LEN..][..OUT_LEN],
&test_parents_out[n * OUT_LEN..][..OUT_LEN]
);
// Then hash parents.
let mut parents = ArrayVec::<&[u8; 2 * OUT_LEN], NUM_INPUTS>::new();
for i in 0..NUM_INPUTS {
parents.push(array_ref!(input_buf, i * 2 * OUT_LEN, 2 * OUT_LEN));
}
let mut portable_parents_out = [0; NUM_INPUTS * OUT_LEN];
unsafe {
crate::ffi::blake3_hash_many_portable(
parents.as_ptr() as _,
parents.len(),
1,
TEST_KEY_WORDS.as_ptr(),
counter,
false,
KEYED_HASH | PARENT,
0,
0,
portable_parents_out.as_mut_ptr(),
);
}
let mut test_parents_out = [0; NUM_INPUTS * OUT_LEN];
unsafe {
hash_many_fn(
parents.as_ptr() as _,
parents.len(),
1,
TEST_KEY_WORDS.as_ptr(),
counter,
false,
KEYED_HASH | PARENT,
0,
0,
test_parents_out.as_mut_ptr(),
);
}
for n in 0..NUM_INPUTS {
dbg!(n);
assert_eq!(
&portable_parents_out[n * OUT_LEN..][..OUT_LEN],
&test_parents_out[n * OUT_LEN..][..OUT_LEN]
);
}
}
}

View File

@ -10,7 +10,7 @@
#elif defined(__GNUC__)
#include <immintrin.h>
#else
#error "Unimplemented!"
#undef IS_X86 /* Unimplemented! */
#endif
#endif
@ -101,7 +101,7 @@ static
if (*edx & (1UL << 26))
features |= SSE2;
#endif
if (*ecx & (1UL << 0))
if (*ecx & (1UL << 9))
features |= SSSE3;
if (*ecx & (1UL << 19))
features |= SSE41;

View File

@ -46,7 +46,6 @@ enum blake3_flags {
#if defined(_MSC_VER)
#include <intrin.h>
#endif
#include <immintrin.h>
#endif
#if !defined(BLAKE3_USE_NEON)
@ -88,7 +87,7 @@ static const uint8_t MSG_SCHEDULE[7][16] = {
/* x is assumed to be nonzero. */
static unsigned int highest_one(uint64_t x) {
#if defined(__GNUC__) || defined(__clang__)
return 63 ^ __builtin_clzll(x);
return 63 ^ (unsigned int)__builtin_clzll(x);
#elif defined(_MSC_VER) && defined(IS_X86_64)
unsigned long index;
_BitScanReverse64(&index, x);
@ -118,7 +117,7 @@ static unsigned int highest_one(uint64_t x) {
// Count the number of 1 bits.
INLINE unsigned int popcnt(uint64_t x) {
#if defined(__GNUC__) || defined(__clang__)
return __builtin_popcountll(x);
return (unsigned int)__builtin_popcountll(x);
#else
unsigned int count = 0;
while (x != 0) {

View File

@ -2301,7 +2301,7 @@ blake3_compress_xof_sse2:
ret
.section .rodata
.section .rdata
.p2align 6
BLAKE3_IV:
.long 0x6A09E667, 0xBB67AE85

View File

@ -2042,7 +2042,7 @@ blake3_compress_xof_sse41:
ret
.section .rodata
.section .rdata
.p2align 6
BLAKE3_IV:
.long 0x6A09E667, 0xBB67AE85

View File

@ -1,7 +1,7 @@
[package]
name = "reference_impl"
version = "0.0.0"
edition = "2018"
edition = "2021"
[lib]
name = "reference_impl"

View File

@ -18,7 +18,6 @@
//! ```
use core::cmp::min;
use core::convert::TryInto;
const OUT_LEN: usize = 32;
const KEY_LEN: usize = 32;

View File

@ -21,7 +21,7 @@
//! let mut output = [0; 1000];
//! let mut output_reader = hasher.finalize_xof();
//! output_reader.fill(&mut output);
//! assert_eq!(&output[..32], hash1.as_bytes());
//! assert_eq!(hash1, output[..32]);
//! # }
//!
//! // Print a hash as hex.
@ -659,7 +659,7 @@ fn compress_parents_parallel(
// The wide helper function returns (writes out) an array of chaining values
// and returns the length of that array. The number of chaining values returned
// is the dyanmically detected SIMD degree, at most MAX_SIMD_DEGREE. Or fewer,
// is the dynamically detected SIMD degree, at most MAX_SIMD_DEGREE. Or fewer,
// if the input is shorter than that many chunks. The reason for maintaining a
// wide array of chaining values going back up the tree, is to allow the
// implementation to hash as many parents in parallel as possible.

View File

@ -111,89 +111,98 @@ pub fn test_hash_many_fn(
hash_many_chunks_fn: HashManyFn<[u8; CHUNK_LEN]>,
hash_many_parents_fn: HashManyFn<[u8; 2 * OUT_LEN]>,
) {
// 31 (16 + 8 + 4 + 2 + 1) inputs
const NUM_INPUTS: usize = 31;
let mut input_buf = [0; CHUNK_LEN * NUM_INPUTS];
crate::test::paint_test_input(&mut input_buf);
// A counter just prior to u32::MAX.
let counter = (1u64 << 32) - 1;
// Test a few different initial counter values.
// - 0: The base case.
// - u32::MAX: The low word of the counter overflows for all inputs except the first.
// - i32::MAX: *No* overflow. But carry bugs in tricky SIMD code can screw this up, if you XOR
// when you're supposed to ANDNOT...
let initial_counters = [0, u32::MAX as u64, i32::MAX as u64];
for counter in initial_counters {
#[cfg(feature = "std")]
dbg!(counter);
// First hash chunks.
let mut chunks = ArrayVec::<&[u8; CHUNK_LEN], NUM_INPUTS>::new();
for i in 0..NUM_INPUTS {
chunks.push(array_ref!(input_buf, i * CHUNK_LEN, CHUNK_LEN));
}
let mut portable_chunks_out = [0; NUM_INPUTS * OUT_LEN];
crate::portable::hash_many(
&chunks,
&TEST_KEY_WORDS,
counter,
IncrementCounter::Yes,
crate::KEYED_HASH,
crate::CHUNK_START,
crate::CHUNK_END,
&mut portable_chunks_out,
);
// 31 (16 + 8 + 4 + 2 + 1) inputs
const NUM_INPUTS: usize = 31;
let mut input_buf = [0; CHUNK_LEN * NUM_INPUTS];
crate::test::paint_test_input(&mut input_buf);
let mut test_chunks_out = [0; NUM_INPUTS * OUT_LEN];
unsafe {
hash_many_chunks_fn(
&chunks[..],
// First hash chunks.
let mut chunks = ArrayVec::<&[u8; CHUNK_LEN], NUM_INPUTS>::new();
for i in 0..NUM_INPUTS {
chunks.push(array_ref!(input_buf, i * CHUNK_LEN, CHUNK_LEN));
}
let mut portable_chunks_out = [0; NUM_INPUTS * OUT_LEN];
crate::portable::hash_many(
&chunks,
&TEST_KEY_WORDS,
counter,
IncrementCounter::Yes,
crate::KEYED_HASH,
crate::CHUNK_START,
crate::CHUNK_END,
&mut test_chunks_out,
&mut portable_chunks_out,
);
}
for n in 0..NUM_INPUTS {
#[cfg(feature = "std")]
dbg!(n);
assert_eq!(
&portable_chunks_out[n * OUT_LEN..][..OUT_LEN],
&test_chunks_out[n * OUT_LEN..][..OUT_LEN]
);
}
// Then hash parents.
let mut parents = ArrayVec::<&[u8; 2 * OUT_LEN], NUM_INPUTS>::new();
for i in 0..NUM_INPUTS {
parents.push(array_ref!(input_buf, i * 2 * OUT_LEN, 2 * OUT_LEN));
}
let mut portable_parents_out = [0; NUM_INPUTS * OUT_LEN];
crate::portable::hash_many(
&parents,
&TEST_KEY_WORDS,
counter,
IncrementCounter::No,
crate::KEYED_HASH | crate::PARENT,
0,
0,
&mut portable_parents_out,
);
let mut test_chunks_out = [0; NUM_INPUTS * OUT_LEN];
unsafe {
hash_many_chunks_fn(
&chunks[..],
&TEST_KEY_WORDS,
counter,
IncrementCounter::Yes,
crate::KEYED_HASH,
crate::CHUNK_START,
crate::CHUNK_END,
&mut test_chunks_out,
);
}
for n in 0..NUM_INPUTS {
#[cfg(feature = "std")]
dbg!(n);
assert_eq!(
&portable_chunks_out[n * OUT_LEN..][..OUT_LEN],
&test_chunks_out[n * OUT_LEN..][..OUT_LEN]
);
}
let mut test_parents_out = [0; NUM_INPUTS * OUT_LEN];
unsafe {
hash_many_parents_fn(
&parents[..],
// Then hash parents.
let mut parents = ArrayVec::<&[u8; 2 * OUT_LEN], NUM_INPUTS>::new();
for i in 0..NUM_INPUTS {
parents.push(array_ref!(input_buf, i * 2 * OUT_LEN, 2 * OUT_LEN));
}
let mut portable_parents_out = [0; NUM_INPUTS * OUT_LEN];
crate::portable::hash_many(
&parents,
&TEST_KEY_WORDS,
counter,
IncrementCounter::No,
crate::KEYED_HASH | crate::PARENT,
0,
0,
&mut test_parents_out,
);
}
for n in 0..NUM_INPUTS {
#[cfg(feature = "std")]
dbg!(n);
assert_eq!(
&portable_parents_out[n * OUT_LEN..][..OUT_LEN],
&test_parents_out[n * OUT_LEN..][..OUT_LEN]
&mut portable_parents_out,
);
let mut test_parents_out = [0; NUM_INPUTS * OUT_LEN];
unsafe {
hash_many_parents_fn(
&parents[..],
&TEST_KEY_WORDS,
counter,
IncrementCounter::No,
crate::KEYED_HASH | crate::PARENT,
0,
0,
&mut test_parents_out,
);
}
for n in 0..NUM_INPUTS {
#[cfg(feature = "std")]
dbg!(n);
assert_eq!(
&portable_parents_out[n * OUT_LEN..][..OUT_LEN],
&test_parents_out[n * OUT_LEN..][..OUT_LEN]
);
}
}
}

View File

@ -1,7 +1,7 @@
[package]
name = "test_vectors"
version = "0.0.0"
edition = "2018"
edition = "2021"
[features]
neon = ["blake3/neon"]

View File

@ -142,7 +142,6 @@ pub fn parse_test_cases() -> Cases {
#[cfg(test)]
mod tests {
use super::*;
use std::convert::TryInto;
fn test_reference_impl_all_at_once(
key: &[u8; blake3::KEY_LEN],

View File

@ -1,7 +1,7 @@
[package]
name = "compiler_version"
version = "0.0.0"
edition = "2018"
edition = "2021"
[build-dependencies]
cc = "1.0.50"

View File

@ -1,6 +1,6 @@
[package]
name = "instruction_set_support"
version = "0.0.0"
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -4,7 +4,7 @@
- Bump the version in the root Cargo.toml.
- Bump the version in b3sum/Cargo.toml.
- Delete b3sum/Cargo.lock and recreate it with `cargo build` or similar.
- Update the `--help` output (including the version number) in b3sum/README.md.
- Update the `--help` output in b3sum/README.md if it's changed.
- Bump `BLAKE3_VERSION_STRING` in c/blake3.h.
- Make a version bump commit with change notes.
- `git push` and make sure CI is green.