Skip to content

add adapters for digest trait #678

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

master-hax
Copy link

@master-hax master-hax commented Aug 9, 2025

there is an existing trait digest::Update used in very popular crates like sha2

this trait is essentially equivalent to ErrorType<Error=Infallible> + Write

this PR adds new adapters between the two traits with an optional dependency on digest

example usage:

use embedded_io::Write;
use embedded_io_adapters::digest::FromDigest;
use sha2::{Digest, Sha256};

fn main() {
    let mut hasher = FromDigest::<Sha256>::default();
    hasher.write_all(b"hello world").unwrap();
    println!("{}", hex::encode(hasher.into_inner().finalize()))
}

@master-hax master-hax requested a review from a team as a code owner August 9, 2025 05:43
@@ -13,11 +13,13 @@ categories = [
]

[features]
default = ["digest"]
Copy link
Author

@master-hax master-hax Aug 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be fine to enable by default since digest is also no_std without the default features

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd prefer to make it optional to not bloat up people's dependency trees and also because the 0.10 vs 0.11 thing

fn update(&mut self, data: &[u8]) {
match self.inner.write_all(data) {
Ok(()) => {}
Err(_) => unreachable!(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's better to do Err(e) => match e{} so the compiler checks for you that it's actually unreachable.

@Dirbaio
Copy link
Member

Dirbaio commented Aug 9, 2025

Interesting timing, we were discussing it yesterday in Matrix https://matrix.to/#/!BHcierreUuwCMxVqOf:matrix.org/$e8f4Ojv7DjdxQcGhRarquhTGY88GPgchLVjG93ZPjxE?via=matrix.org&via=catircservices.org&via=mozilla.org

IMO it's a bit niche. For example, the digest crate itself doesn't provide a similar adapter for std::io. What is your use case for this? Do you have something that writes data to a Write and you want to just hash it and not keep the data? (if you keep the data you can just hash the &[u8] afterwards)

Also the rust-crypto ecosystem isn't known for its stability. Latest digest is 0.10, but there's already 0.11 release candidates in the pipeline. If we do this we should name the features digest-010 and digest-011.

@master-hax
Copy link
Author

master-hax commented Aug 10, 2025

thanks for taking a look. i find these traits far superior to those in std::io, so i tend to prefer using these, even for projects that have the standard library available.

long story short, i am currently working on a JSON Web Token library, which often needs to write data after encoding it & authenticating it. it ends up being intuitive to chain APIs that implement embedded_io::Write for this use case. i never care about keeping the data around because i have the original version.

e.g. writing HS256 signed data encoded as base64url becomes incredibly simple (can be done directly to a TCP stream)

fn main() {
    let mut stdout = FromStd::new(std::io::stdout());
    let mut authenticator = AuthenticatedWriter::new(&mut stdout, Sha256::new());
    let mut encoder = Base64BlockEncoder::new(&mut authenticator);
    encoder.write_all(b"hello world").unwrap();
    let (_, _) = encoder.finalize(false).unwrap();
    let hash = authenticator.finalize(b"some secret for HS256");
    stdout.write_all(b"\nsignature: ").unwrap();
    stdout.write_all(hex::encode(hash).as_bytes()).unwrap();
}

output:

aGVsbG8gd29ybGQ

signature: b0d79a7623c6be2bc62685176eefe28b2b8f320ba2fb6093391b66204827f2f1

as for the digest crate not providing an adapter, i think there are 2 reasons:

  1. std::io doesn't provide a way to specify the error type, meaning there's no way to use Infallible as the return type
  2. digest & all the other RustCrypto traits haven't been updated in ages. they have not updated to use const generics because presumably it is a lot of work to migrate everything across multiple crates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants