Skip to content

Refactor [u8] -> [u8; LEN] where appropriate #102

Description

@ounsworth

bc-rust follows the general Rust paradigm of moving as many runtime error conditions as possible to instead be compile-time error conditions.

A prime candidate is core::traits:Hash:

pub trait Hash: Algorithm + Default {
    ...
    fn hash(self, data: &[u8]) -> Vec<u8>;
    fn hash_out(self, data: &[u8], output: &mut [u8]) -> usize;
    ...
}

Since, by definition, a hash function must produce a fixed-size output, this should really be:

pub trait Hash<const OUTPUT_LEN: usize>: Algorithm + Default {
    ...
    fn hash(self, data: &[u8]) -> [u8, OUTPUT_LEN];
    fn hash_out(self, data: &[u8], output: &mut [u8; OUTPUT_LEN]) -> usize;
    ...
}

since that resolves runtime ambiguity about the length of those arrays. Since this is part of the mathematical definition of a hash function, this should still be implementation-agnostic (ie any hash function should be able to implement this).

The task is to make the refactor above, and scan through the rest of the library for any other places where a value with a fixed length is currently being handled as an indefinite-length type (Vec or &[u8]).

This task is related to #49 , but not necessarily a subtask, since if we decide to keep the requirement on a global allocator, then the current Vec<u8> implementation does not technically need to change.

Activity

  1. ounsworth commented on Sep 1, 2026

    @ounsworth
    ContributorAuthor

    It might be that this is not really implementable until the rust compiler generic_const_exprs feature, which is currently only available in nightly, lands in stable.

  2. jjkurczak commented on Sep 1, 2026

    @jjkurczak
    Collaborator

    I recall attempting this some time ago, and there were two difficulties:

    1. The OUTPUT_LEN argument propagates to any other struct that impls Hash (which may be repeating a size that is already an associated constant)
    2. There were some cases that were not possible to handle (possibly in bouncycastle-factory?) because generic_const_exprs was required to satisfy the const generic arg for Hash.

    Another approach would be to make impacted functions generic instead, rather than the trait itself, e.g.:

    fn hash_out<const N: usize>(self, data: &[u8], output: &mut [u8; N]) -> usize;
    
    fn hash_array<const N: usize>(self, data: &[u8]) -> [u8; N]
    where
        Self: Sized,
    {
        let mut output = [0u8; N];
        let _ = self.hash_out(data, &mut output);
        output
    }
  3. simo5 commented on Sep 10, 2026

    @simo5

    since that resolves runtime ambiguity about the length of those arrays. Since this is part of the mathematical definition of a hash function, this should still be implementation-agnostic (ie any hash function should be able to implement this).

    Except for XOF ones, but perhaps you use a different interface for them?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions