mirror of
https://github.com/saymrwulf/betrusted-curve25519-dalek-source.git
synced 2026-09-04 20:24:07 +00:00
wip: remove betrusted feature
This commit is contained in:
parent
55c5a33411
commit
81f9189d2f
6 changed files with 48 additions and 151 deletions
|
|
@ -57,27 +57,10 @@ serde = { version = "1.0", default-features = false, optional = true, features =
|
||||||
zeroize = { version = "1", default-features = false, optional = true }
|
zeroize = { version = "1", default-features = false, optional = true }
|
||||||
|
|
||||||
# Betrusted/Precursor dependency set
|
# Betrusted/Precursor dependency set
|
||||||
log = { version = "0.4", optional = true }
|
[target.'cfg(curve25519_dalek_backend = "u32e_backend")'.dependencies]
|
||||||
[dependencies.engine-25519]
|
engine25519-as = {git = "https://github.com/betrusted-io/engine25519-as.git", rev = "d249c967556b02ab5439eacb5078fa00c60b93d6", default-features = false, features = [], optional = true}
|
||||||
git = "https://github.com/betrusted-io/xous-engine-25519.git"
|
engine-25519 = { git = "https://github.com/betrusted-io/xous-engine-25519.git", rev = "63d3d1f30736022e791deaacf4dd62c00b42fe2e", optional = true}
|
||||||
rev = "63d3d1f30736022e791deaacf4dd62c00b42fe2e"
|
utralib = {version = "0.1.0", optional = true} # this is bogus -- must be patched in the invoking build environment TODO: if this builds, it seems a crate has been released as version 0.1.23 that could replace this
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.utralib]
|
|
||||||
version = "0.1.0" # this is bogus -- must be patched in the invoking build environment
|
|
||||||
#path="../betrusted-soc/boot/utralib/"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.engine25519-as]
|
|
||||||
git = "https://github.com/betrusted-io/engine25519-as.git"
|
|
||||||
rev = "d249c967556b02ab5439eacb5078fa00c60b93d6"
|
|
||||||
default-features = false
|
|
||||||
features = []
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dev-dependencies.engine25519-as]
|
|
||||||
git = "https://github.com/betrusted-io/engine25519-as.git"
|
|
||||||
rev = "d249c967556b02ab5439eacb5078fa00c60b93d6"
|
|
||||||
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||||
cpufeatures = "0.2.6"
|
cpufeatures = "0.2.6"
|
||||||
|
|
@ -86,9 +69,7 @@ cpufeatures = "0.2.6"
|
||||||
fiat-crypto = { version = "0.2.1", default-features = false }
|
fiat-crypto = { version = "0.2.1", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["alloc", "precomputed-tables", "zeroize", "betrusted", "u32e_backend"]
|
default = ["alloc", "precomputed-tables", "zeroize"]
|
||||||
betrusted = ["engine-25519", "engine25519-as", "log"]
|
|
||||||
u32e_backend = ["engine25519-as", "utralib"]
|
|
||||||
alloc = ["zeroize?/alloc"]
|
alloc = ["zeroize?/alloc"]
|
||||||
precomputed-tables = []
|
precomputed-tables = []
|
||||||
legacy_compatibility = []
|
legacy_compatibility = []
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,8 @@ pub(crate) const SQRT_M1: FieldElement2625 = FieldElement2625::from_limbs([
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/// `APLUS2_OVER_FOUR` is (A+2)/4. (This is used internally within the Montgomery ladder.)
|
/// `APLUS2_OVER_FOUR` is (A+2)/4. (This is used internally within the Montgomery ladder.)
|
||||||
#[cfg(not(feature = "betrusted"))]
|
/// The u32e backend uses hardware acceleration for this.
|
||||||
|
#[cfg(curve_dalek_backend != "u32e_backend")]
|
||||||
pub(crate) const APLUS2_OVER_FOUR: FieldElement2625 =
|
pub(crate) const APLUS2_OVER_FOUR: FieldElement2625 =
|
||||||
FieldElement2625::from_limbs([121666, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
FieldElement2625::from_limbs([121666, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
use utralib::generated::*;
|
|
||||||
pub struct Uart {
|
|
||||||
// pub base: *mut u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Uart {
|
|
||||||
fn put_digit(&mut self, d: u8) {
|
|
||||||
let nyb = d & 0xF;
|
|
||||||
if nyb < 10 {
|
|
||||||
self.putc(nyb + 0x30);
|
|
||||||
} else {
|
|
||||||
self.putc(nyb + 0x61 - 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn put_hex(&mut self, c: u8) {
|
|
||||||
self.put_digit(c >> 4);
|
|
||||||
self.put_digit(c & 0xF);
|
|
||||||
}
|
|
||||||
pub fn newline(&mut self) {
|
|
||||||
self.putc(0xa);
|
|
||||||
self.putc(0xd);
|
|
||||||
}
|
|
||||||
pub fn print_hex_word(&mut self, word: u32) {
|
|
||||||
for &byte in word.to_be_bytes().iter() {
|
|
||||||
self.put_hex(byte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn putc(&self, c: u8) {
|
|
||||||
let base = utra::uart::HW_UART_BASE as *mut u32;
|
|
||||||
let mut uart = CSR::new(base);
|
|
||||||
// Wait until TXFULL is `0`
|
|
||||||
while uart.r(utra::uart::TXFULL) != 0 {}
|
|
||||||
uart.wo(utra::uart::RXTX, c as u32)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getc(&self) -> Option<u8> {
|
|
||||||
let base = utra::uart::HW_UART_BASE as *mut u32;
|
|
||||||
let mut uart = CSR::new(base);
|
|
||||||
match uart.rf(utra::uart::EV_PENDING_RX) {
|
|
||||||
0 => None,
|
|
||||||
ack => {
|
|
||||||
let c = Some(uart.rf(utra::uart::RXTX_RXTX) as u8);
|
|
||||||
uart.wfo(utra::uart::EV_PENDING_RX, ack);
|
|
||||||
c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tiny_write_str(&mut self, s: &str) {
|
|
||||||
for c in s.bytes() {
|
|
||||||
self.putc(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
use core::fmt::{Error, Write};
|
|
||||||
impl Write for Uart {
|
|
||||||
fn write_str(&mut self, s: &str) -> Result<(), Error> {
|
|
||||||
for c in s.bytes() {
|
|
||||||
self.putc(c);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
pub mod debug_print_hardware {
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! print
|
|
||||||
{
|
|
||||||
($($args:tt)+) => ({
|
|
||||||
use core::fmt::Write;
|
|
||||||
let _ = write!(debug::Uart {}, $($args)+);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
#[cfg(test)]
|
|
||||||
mod debug_print_hardware {
|
|
||||||
#[macro_export]
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
macro_rules! print {
|
|
||||||
($($args:tt)+) => ({
|
|
||||||
std::print!($($args)+)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! println
|
|
||||||
{
|
|
||||||
() => ({
|
|
||||||
$crate::print!("\r\n")
|
|
||||||
});
|
|
||||||
($fmt:expr) => ({
|
|
||||||
$crate::print!(concat!($fmt, "\r\n"))
|
|
||||||
});
|
|
||||||
($fmt:expr, $($args:tt)+) => ({
|
|
||||||
$crate::print!(concat!($fmt, "\r\n"), $($args)+)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
unused_qualifications
|
unused_qualifications
|
||||||
)]
|
)]
|
||||||
|
|
||||||
// needed for engine25519-as.
|
// needed for engine25519-as.
|
||||||
#![recursion_limit="512"]
|
#![recursion_limit="512"]
|
||||||
|
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
|
|
@ -60,14 +60,23 @@ pub use digest;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub(crate) mod macros;
|
pub(crate) mod macros;
|
||||||
|
|
||||||
|
//To consider upstreaming, we likely can't do this. Consider the "panic_on_sw_eval" feature
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
#[cfg(any(feature = "betrusted", test, curve25519_dalek_backend = "u32e_backend"))]
|
#[cfg(any(test, curve25519_dalek_backend = "u32e_backend"))]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate engine25519_as;
|
extern crate engine25519_as;
|
||||||
#[cfg(feature = "betrusted")]
|
#[cfg(curve25519_dalek_backend = "u32e_backend")] //this is the binding for betrusted, so it should be gated
|
||||||
|
//with a "betrusted" flag, but we gate it with the backend
|
||||||
|
//flag for now. We'd need to refactor this to be
|
||||||
|
//make it easier to support other platforms,
|
||||||
|
//though there are no other platforms. For
|
||||||
|
//upstreaming this might be diserable, but for
|
||||||
|
//now, we'll leave it as a TODO.
|
||||||
extern crate engine_25519;
|
extern crate engine_25519;
|
||||||
|
|
||||||
#[cfg(curve25519_dalek_backend = "u32e_backend")]
|
#[cfg(curve25519_dalek_backend = "u32e_backend")] //while this is specific to betrusted, any other
|
||||||
|
//use of this hardware would likely also need
|
||||||
|
//utralib, at least that would be easiest.
|
||||||
extern crate utralib;
|
extern crate utralib;
|
||||||
|
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -54,14 +54,13 @@ use core::{
|
||||||
ops::{Mul, MulAssign},
|
ops::{Mul, MulAssign},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "betrusted"))]
|
#[cfg(not(curve25519_dalek_backend = "u32e_backend"))]
|
||||||
use constants::{APLUS2_OVER_FOUR, MONTGOMERY_A, MONTGOMERY_A_NEG};
|
use crate::constants::{APLUS2_OVER_FOUR, MONTGOMERY_A, MONTGOMERY_A_NEG};
|
||||||
#[cfg(feature = "betrusted")]
|
#[cfg(curve25519_dalek_backend = "u32e_backend")]
|
||||||
use constants::{MONTGOMERY_A, MONTGOMERY_A_NEG}; // eliminate constants absorbed into the microcode engine
|
use crate::constants::{MONTGOMERY_A, MONTGOMERY_A_NEG}; // eliminate constants absorbed into the microcode engine
|
||||||
|
|
||||||
use edwards::{CompressedEdwardsY, EdwardsPoint};
|
use crate::edwards::{CompressedEdwardsY, EdwardsPoint};
|
||||||
use field::FieldElement;
|
use crate::field::FieldElement;
|
||||||
use scalar::Scalar;
|
|
||||||
use crate::scalar::{clamp_integer, Scalar};
|
use crate::scalar::{clamp_integer, Scalar};
|
||||||
|
|
||||||
use crate::traits::Identity;
|
use crate::traits::Identity;
|
||||||
|
|
@ -156,6 +155,8 @@ impl MontgomeryPoint {
|
||||||
Self::mul_base(&s)
|
Self::mul_base(&s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: understand _clamped multiplication_ and ensure we are doing it correctly and
|
||||||
|
//compatibily as this has changed since our fork.
|
||||||
/// Given `self` \\( = u\_0(P) \\), and a big-endian bit representation of an integer
|
/// Given `self` \\( = u\_0(P) \\), and a big-endian bit representation of an integer
|
||||||
/// \\(n\\), return \\( u\_0(\[n\]P) \\). This is constant time in the length of `bits`.
|
/// \\(n\\), return \\( u\_0(\[n\]P) \\). This is constant time in the length of `bits`.
|
||||||
///
|
///
|
||||||
|
|
@ -324,15 +325,17 @@ impl ProjectivePoint {
|
||||||
///
|
///
|
||||||
/// * \\( u = U / W \\) if \\( W \neq 0 \\);
|
/// * \\( u = U / W \\) if \\( W \neq 0 \\);
|
||||||
/// * \\( 0 \\) if \\( W \eq 0 \\);
|
/// * \\( 0 \\) if \\( W \eq 0 \\);
|
||||||
#[cfg(not(feature = "betrusted"))]
|
#[cfg(not(curve25519_dalek_backend = "u32e_backend"))]
|
||||||
pub fn as_affine(&self) -> MontgomeryPoint {
|
pub fn as_affine(&self) -> MontgomeryPoint {
|
||||||
#[cfg(all(not(test),feature="betrusted"))] // due to issue https://github.com/rust-lang/rust/issues/59168, you will have to manually comment this out when running a test on the full system and not just this crate.
|
//TODO: consider making this a seperate feature. Something like "panic_on_sw_eval" which would
|
||||||
|
//be ameniable to upstreaming
|
||||||
|
#[cfg(all(not(test),curve25519_dalek_backend = "u32e_backend"))] // due to issue https://github.com/rust-lang/rust/issues/59168, you will have to manually comment this out when running a test on the full system and not just this crate.
|
||||||
log::warn!("sw as_affine being used - check for build config errors!");
|
log::warn!("sw as_affine being used - check for build config errors!");
|
||||||
let u = &self.U * &self.W.invert();
|
let u = &self.U * &self.W.invert();
|
||||||
MontgomeryPoint(u.as_bytes())
|
MontgomeryPoint(u.as_bytes())
|
||||||
}
|
}
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[cfg(feature = "betrusted")]
|
#[cfg(curve25519_dalek_backend = "u32e_backend")]
|
||||||
pub fn as_affine(&self) -> MontgomeryPoint {
|
pub fn as_affine(&self) -> MontgomeryPoint {
|
||||||
let mcode = assemble_engine25519!(
|
let mcode = assemble_engine25519!(
|
||||||
start:
|
start:
|
||||||
|
|
@ -496,7 +499,7 @@ impl ProjectivePoint {
|
||||||
/// $$
|
/// $$
|
||||||
/// (U\_Q : W\_Q) \gets u(P + Q).
|
/// (U\_Q : W\_Q) \gets u(P + Q).
|
||||||
/// $$
|
/// $$
|
||||||
#[cfg(not(feature = "betrusted"))]
|
#[cfg(not(curve25519_dalek_backend = "u32e_backend"))]
|
||||||
#[rustfmt::skip] // keep alignment of explanatory comments
|
#[rustfmt::skip] // keep alignment of explanatory comments
|
||||||
pub(crate) fn differential_add_and_double(
|
pub(crate) fn differential_add_and_double(
|
||||||
P: &mut ProjectivePoint,
|
P: &mut ProjectivePoint,
|
||||||
|
|
@ -538,7 +541,7 @@ pub(crate) fn differential_add_and_double(
|
||||||
Q.W = t17; // W_{Q'} = U_D * 4 (W_P U_Q - U_P W_Q)^2
|
Q.W = t17; // W_{Q'} = U_D * 4 (W_P U_Q - U_P W_Q)^2
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "betrusted")]
|
#[cfg(curve25519_dalek_backend = "u32e_backend")]
|
||||||
fn copy_to_rf(bytes: [u8; 32], register: usize, rf: &mut [u32; engine_25519::RF_SIZE_IN_U32]) {
|
fn copy_to_rf(bytes: [u8; 32], register: usize, rf: &mut [u32; engine_25519::RF_SIZE_IN_U32]) {
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
for (byte, rf_dst) in bytes.chunks_exact(4).zip(rf[register * 8..(register+1)*8].iter_mut()) {
|
for (byte, rf_dst) in bytes.chunks_exact(4).zip(rf[register * 8..(register+1)*8].iter_mut()) {
|
||||||
|
|
@ -546,7 +549,7 @@ fn copy_to_rf(bytes: [u8; 32], register: usize, rf: &mut [u32; engine_25519::RF_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "betrusted")]
|
#[cfg(curve25519_dalek_backend = "u32e_backend")]
|
||||||
fn copy_from_rf(register: usize, rf: &[u32; engine_25519::RF_SIZE_IN_U32]) -> [u8; 32] {
|
fn copy_from_rf(register: usize, rf: &[u32; engine_25519::RF_SIZE_IN_U32]) -> [u8; 32] {
|
||||||
let mut ret: [u8; 32] = [0; 32];
|
let mut ret: [u8; 32] = [0; 32];
|
||||||
|
|
||||||
|
|
@ -560,7 +563,7 @@ fn copy_from_rf(register: usize, rf: &[u32; engine_25519::RF_SIZE_IN_U32]) -> [u
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)] // absorbed into mul, but might be useful later on as a subroutine for something else
|
#[allow(dead_code)] // absorbed into mul, but might be useful later on as a subroutine for something else
|
||||||
#[cfg(feature = "betrusted")]
|
#[cfg(curve25519_dalek_backend = "u32e_backend")]
|
||||||
pub(crate) fn differential_add_and_double_hw(
|
pub(crate) fn differential_add_and_double_hw(
|
||||||
P: &mut ProjectivePoint,
|
P: &mut ProjectivePoint,
|
||||||
Q: &mut ProjectivePoint,
|
Q: &mut ProjectivePoint,
|
||||||
|
|
@ -700,7 +703,7 @@ impl Mul<&Scalar> for &MontgomeryPoint {
|
||||||
type Output = MontgomeryPoint;
|
type Output = MontgomeryPoint;
|
||||||
|
|
||||||
/// Given `self` \\( = u\_0(P) \\), and a `Scalar` \\(n\\), return \\( u\_0([n]P) \\).
|
/// Given `self` \\( = u\_0(P) \\), and a `Scalar` \\(n\\), return \\( u\_0([n]P) \\).
|
||||||
#[cfg(feature = "betrusted")]
|
#[cfg(curve25519_dalek_backend = "u32e_backend")]
|
||||||
fn mul(self, scalar: &Scalar) -> MontgomeryPoint {
|
fn mul(self, scalar: &Scalar) -> MontgomeryPoint {
|
||||||
log::debug!("hw mont");
|
log::debug!("hw mont");
|
||||||
// Algorithm 8 of Costello-Smith 2017
|
// Algorithm 8 of Costello-Smith 2017
|
||||||
|
|
@ -1014,6 +1017,10 @@ impl Mul<&Scalar> for &MontgomeryPoint {
|
||||||
if false { // unmerged affine path
|
if false { // unmerged affine path
|
||||||
x0.U = FieldElement::from_bytes(©_from_rf(25, &result_rf));
|
x0.U = FieldElement::from_bytes(©_from_rf(25, &result_rf));
|
||||||
x0.W = FieldElement::from_bytes(©_from_rf(26, &result_rf));
|
x0.W = FieldElement::from_bytes(©_from_rf(26, &result_rf));
|
||||||
|
|
||||||
|
//Note: is seems this TODO has been handled as ProjectivePoint's as_affine already
|
||||||
|
//has an accelerated version. Should this be removed or is there further
|
||||||
|
//optimization work to be done. If so, what is that work?
|
||||||
|
|
||||||
// TODO: optimize this relatively innocuous looking call.
|
// TODO: optimize this relatively innocuous looking call.
|
||||||
// this consumes about 100ms runtime -- need to implement this using
|
// this consumes about 100ms runtime -- need to implement this using
|
||||||
|
|
@ -1038,9 +1045,10 @@ impl Mul<&Scalar> for &MontgomeryPoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given `self` \\( = u\_0(P) \\), and a `Scalar` \\(n\\), return \\( u\_0(\[n\]P) \\)
|
/// Given `self` \\( = u\_0(P) \\), and a `Scalar` \\(n\\), return \\( u\_0(\[n\]P) \\)
|
||||||
#[cfg(not(feature = "betrusted"))]
|
#[cfg(not(curve25519_dalek_backend = "u32e_backend"))]
|
||||||
fn mul(self, scalar: &Scalar) -> MontgomeryPoint {
|
fn mul(self, scalar: &Scalar) -> MontgomeryPoint {
|
||||||
#[cfg(all(not(test),feature="betrusted"))] // due to issue https://github.com/rust-lang/rust/issues/59168, you will have to manually comment this out when running a test on the full system and not just this crate.
|
// TODO: consider feature "panic_on_sw_eval"
|
||||||
|
#[cfg(all(not(test),curve25519_dalek_backend = "u32e_backend"))] // due to issue https://github.com/rust-lang/rust/issues/59168, you will have to manually comment this out when running a test on the full system and not just this crate.
|
||||||
log::warn!("sw montgomery multiply being used - check for build config errors!");
|
log::warn!("sw montgomery multiply being used - check for build config errors!");
|
||||||
// We multiply by the integer representation of the given Scalar. By scalar invariant #1,
|
// We multiply by the integer representation of the given Scalar. By scalar invariant #1,
|
||||||
// the MSB is 0, so we can skip it.
|
// the MSB is 0, so we can skip it.
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,8 @@ cfg_if! {
|
||||||
///
|
///
|
||||||
/// This is a type alias for one of the scalar types in the `backend`
|
/// This is a type alias for one of the scalar types in the `backend`
|
||||||
/// module.
|
/// module.
|
||||||
|
//TODO: this should be the same as the u32 backend such that we don't even need to have
|
||||||
|
//backend/serial/scalar.rs defined. Double check that to simplify the code.
|
||||||
type UnpackedScalar = backend::serial::u32::scalar::Scalar29;
|
type UnpackedScalar = backend::serial::u32::scalar::Scalar29;
|
||||||
}
|
}
|
||||||
else if #[cfg(curve25519_dalek_backend = "fiat")] {
|
else if #[cfg(curve25519_dalek_backend = "fiat")] {
|
||||||
|
|
@ -846,7 +848,7 @@ impl Scalar {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the bits of the scalar, in little-endian order
|
/// Get the bits of the scalar, in little-endian order
|
||||||
#[cfg(not(feature = "betrusted"))]
|
#[cfg(not(curve25519_dalek_backend = "u32e_backend"))]
|
||||||
pub(crate) fn bits_le(&self) -> impl DoubleEndedIterator<Item = bool> + '_ {
|
pub(crate) fn bits_le(&self) -> impl DoubleEndedIterator<Item = bool> + '_ {
|
||||||
(0..256).map(|i| {
|
(0..256).map(|i| {
|
||||||
// As i runs from 0..256, the bottom 3 bits index the bit, while the upper bits index
|
// As i runs from 0..256, the bottom 3 bits index the bit, while the upper bits index
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue