diff --git a/blockset-lib/src/app/get.rs b/blockset-lib/src/app/get.rs index a0472268..7d10a7e2 100644 --- a/blockset-lib/src/app/get.rs +++ b/blockset-lib/src/app/get.rs @@ -13,7 +13,7 @@ use crate::{ cdt::node_type::NodeType, common::status_line::{mb, StatusLine}, forest::{file::FileForest, node_id::ForestNodeId, Forest}, - uint::u224::U224, + uint::u224x::U224, }; use super::{ diff --git a/blockset-lib/src/app/mod.rs b/blockset-lib/src/app/mod.rs index 474f9346..90946fb1 100644 --- a/blockset-lib/src/app/mod.rs +++ b/blockset-lib/src/app/mod.rs @@ -24,7 +24,7 @@ use crate::{ }, forest::{file::FileForest, tree_add::ForestTreeAdd}, info::calculate_total, - uint::u224::U224, + uint::u224x::U224, }; fn set_progress( @@ -170,7 +170,7 @@ mod test { use std::io::Write; use wasm_bindgen_test::wasm_bindgen_test; - use crate::{cdt::node_id::root, common::base32::ToBase32, run, uint::u256::U256}; + use crate::{cdt::node_id::root, common::base32::ToBase32, run, uint::u256x::U256}; #[wasm_bindgen_test] #[test] diff --git a/blockset-lib/src/asn1.rs b/blockset-lib/src/asn1.rs new file mode 100644 index 00000000..75c80aee --- /dev/null +++ b/blockset-lib/src/asn1.rs @@ -0,0 +1,387 @@ +use core::iter::once; + +use nanvm_lib::common::{cast::Cast, default::default}; + +type OctetString = Vec; + +#[derive(PartialEq, Debug)] +struct ObjectIdentifier { + a0: u8, + a1: u8, + a2: Vec, +} + +#[derive(PartialEq, Debug)] +struct BitString { + padding: u8, + value: Vec, +} + +type Sequence = Vec; + +#[derive(PartialEq, Debug)] +enum Any { + Bool(bool), + Integer(i128), + OctetString(Vec), + ObjectIdentifier(ObjectIdentifier), + BitString(BitString), + Sequence(Sequence), +} + +fn d(a: &mut dyn Iterator) -> T { + let len = if let Some(len) = a.next() { + if len < 0x80 { + len as usize + } else { + read_u128(&mut a.take(len as usize & 0x78)).0 as usize + } + } else { + 0 + }; + T::deserialize(&mut a.take(len)) +} + +impl Any { + fn serialize(self) -> Vec { + fn f(v: T) -> Vec { + let mut v = v.serialize(); + let len = v.len() as i128; + let mut result = [T::TAG].cast(); + if len < 0x80 { + result.push(len as u8); + } else { + let mut int = write_i128(len, false); + result.push(int.len() as u8 | 0x80); + result.append(&mut int); + } + result.append(&mut v); + result + } + match self { + Any::Bool(v) => f(v), + Any::Integer(v) => f(v), + Any::OctetString(v) => f(v), + Any::BitString(v) => f(v), + Any::ObjectIdentifier(v) => f(v), + Any::Sequence(v) => f(v), + } + } + fn deserialize(a: &mut dyn Iterator) -> Option { + if let Some(tag) = a.next() { + match tag { + bool::TAG => Some(Any::Bool(d(a))), + i128::TAG => Some(Any::Integer(d(a))), + OctetString::TAG => Some(Any::OctetString(d(a))), + BitString::TAG => Some(Any::BitString(d(a))), + ObjectIdentifier::TAG => Some(Any::ObjectIdentifier(d(a))), + Sequence::TAG => Some(Any::Sequence(d(a))), + _ => None, + } + } else { + None + } + } +} + +trait Serialize: Sized { + const TAG: u8; + fn serialize(self) -> Vec; + fn deserialize(a: &mut impl Iterator) -> Self; +} + +impl Serialize for bool { + const TAG: u8 = 1; + fn serialize(self) -> Vec { + [if self { 0xFF } else { 0 }].cast() + } + fn deserialize(a: &mut impl Iterator) -> Self { + if let Some(v) = a.next() { + v != 0 + } else { + false + } + } +} + +fn write_i128(n: i128, signed: bool) -> Vec { + let leading = if n.is_negative() { + n.leading_ones() + } else { + n.leading_zeros() + } - signed as u32; + let len = 16 - (leading >> 3); + let mut result = Vec::with_capacity(len as usize); + let mut i = len - 1; + loop { + result.push((n >> (i << 3)) as u8); + if i == 0 { + break; + } + i -= 1; + } + result +} + +fn read_u128(i: &mut impl Iterator) -> (u128, i32) { + let mut result = 0; + let mut bits = 0; + for v in i { + result = (result << 8) | (v as u128); + bits += 8; + } + (result, bits) +} + +impl Serialize for i128 { + const TAG: u8 = 2; + fn serialize(self) -> Vec { + write_i128(self, true) + } + fn deserialize(i: &mut impl Iterator) -> Self { + let (mut result, c) = read_u128(i); + if result >> (c - 1) == 1 && c < 128 { + result |= u128::MAX << c + } + result as i128 + } +} + +impl Serialize for BitString { + const TAG: u8 = 3; + fn serialize(self) -> Vec { + once(self.padding).chain(self.value).collect() + } + fn deserialize(a: &mut impl Iterator) -> Self { + let padding = a.next().unwrap_or_default(); + Self { + padding, + value: a.collect(), + } + } +} + +impl Serialize for OctetString { + const TAG: u8 = 4; + fn serialize(self) -> Vec { + self + } + fn deserialize(a: &mut impl Iterator) -> Self { + a.collect() + } +} + +impl Serialize for Sequence { + const TAG: u8 = 0x30; + fn serialize(self) -> Vec { + let mut result = Vec::default(); + for a in self { + result.append(&mut a.serialize()) + } + result + } + fn deserialize(a: &mut impl Iterator) -> Self { + let mut result: Self = default(); + while let Some(v) = Any::deserialize(a) { result.push(v) } + result + } +} + +const OI: u8 = 40; + +impl Serialize for ObjectIdentifier { + const TAG: u8 = 6; + fn serialize(self) -> Vec { + let mut result = [self.a0 * OI + self.a1].cast(); + for a in self.a2 { + let mut len = 1.max(((128 + 6) - a.leading_zeros()) / 7) - 1; + loop { + let f = len > 0; + result.push(((a >> (len * 7)) as u8 & 0x7F) | ((f as u8) << 7)); + if !f { + break; + } + len -= 1; + } + } + result + } + fn deserialize(a: &mut impl Iterator) -> Self { + let a01 = a.next().unwrap_or_default(); + let mut a2: Vec = default(); + while let Some(mut v) = a.next() { + let mut x = 0; + loop { + x |= (v & 0x7F) as u128; + if v >> 7 == 0 { + break; + } + x <<= 7; + v = a.next().unwrap_or_default(); + } + a2.push(x); + } + Self { + a0: a01 / OI, + a1: a01 % OI, + a2, + } + } +} + +#[cfg(test)] +mod test { + use core::fmt; + + use nanvm_lib::common::{cast::Cast, default::default}; + use wasm_bindgen_test::wasm_bindgen_test; + + use super::{Any, BitString, ObjectIdentifier, Serialize}; + + #[wasm_bindgen_test] + #[test] + fn i128_test() { + assert_eq!((-1i128).leading_ones(), 128); + assert_eq!(-1i128 as u128, u128::MAX); + } + + fn f(v: T, a: &[u8]) { + let i = &mut a.into_iter().map(|x| *x); + let m = T::deserialize(i); + assert_eq!(i.next(), None); + assert_eq!(m, v); + assert_eq!(v.serialize(), a); + } + + #[wasm_bindgen_test] + #[test] + fn bool_test() { + f(true, &[0xFF]); + f(false, &[0]); + } + + #[wasm_bindgen_test] + #[test] + fn integer_test() { + // len: 1 + f(0, &[0]); + f(1, &[1]); + f(-1, &[0xFF]); + f(0x7F, &[0x7F]); + f(-0x80, &[0x80]); + // len: 2 + f(0x80, &[0, 0x80]); + f(-0x81, &[0xFF, 0x7F]); + f(0x7FFF, &[0x7F, 0xFF]); + f(-0x8000, &[0x80, 0x00]); + // len: 16 + f( + i128::MAX, + &[ + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, + ], + ); + f( + i128::MIN, + &[0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ); + } + + fn any_f(v: Any, a: &[u8]) { + let mut i = a.into_iter().map(|x| *x); + assert_eq!(Any::deserialize(&mut i).unwrap(), v); + assert_eq!(i.next(), None); + assert_eq!(v.serialize(), a); + } + + #[wasm_bindgen_test] + #[test] + fn any_test() { + any_f(Any::Bool(true), &[1, 1, 0xFF]); + any_f(Any::Bool(false), &[1, 1, 0]); + any_f( + Any::Integer(i128::MIN), + &[2, 16, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ); + any_f( + Any::Integer(i128::MIN + 1), + &[2, 16, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + ); + any_f( + Any::ObjectIdentifier(ObjectIdentifier { + a0: 2, + a1: 0, + a2: [1, 235].cast(), + }), + &[6, 4, 80, 1, 0x81, 0x6b], + ); + any_f( + Any::OctetString([123, 45, 67, 89].cast()), + &[4, 4, 123, 45, 67, 89], + ); + any_f( + Any::BitString(BitString { + padding: 3, + value: [98, 76, 54, 32, 10].cast(), + }), + &[3, 6, 3, 98, 76, 54, 32, 10], + ); + any_f( + Any::Sequence([].cast()), + &[0x30, 0] + ); + any_f( + Any::Sequence([Any::Bool(false)].cast()), + &[0x30, 3, 1, 1, 0] + ); + any_f( + Any::Sequence([Any::Integer(126), Any::Bool(true)].cast()), + &[ + // + 0x30, 6, + // Integer(126) + 2, 1, 126, + // Bool(true) + 1, 1, 255 + ] + ); + } + + #[wasm_bindgen_test] + #[test] + fn oi_test() { + f( + ObjectIdentifier { + a0: 0, + a1: 3, + a2: default(), + }, + &[3], + ); + f( + ObjectIdentifier { + a0: 1, + a1: 2, + a2: default(), + }, + &[42], + ); + f( + ObjectIdentifier { + a0: 2, + a1: 17, + a2: [127, 5, 0x89].cast(), + }, + &[97, 127, 5, 0x81, 9], + ); + f( + ObjectIdentifier { + a0: 3, + a1: 39, + a2: [0x82, 0x4345, 0x26789A].cast(), + }, + &[159, 0x81, 2, 0x81, 0x86, 0x45, 0x81, 0x99, 0xF1, 0x1A], + ); + } +} diff --git a/blockset-lib/src/cdt/main_tree.rs b/blockset-lib/src/cdt/main_tree.rs index ba1b5f61..0ac6fc7f 100644 --- a/blockset-lib/src/cdt/main_tree.rs +++ b/blockset-lib/src/cdt/main_tree.rs @@ -2,7 +2,7 @@ use std::io; use nanvm_lib::common::default::default; -use crate::uint::u224::U224; +use crate::uint::u224x::U224; use super::{ node_id::{root, to_node_id}, @@ -65,7 +65,7 @@ mod test { use crate::{ cdt::node_id::{merge, root, to_node_id}, - uint::{u224::U224, u256::U256}, + uint::{u224x::U224, u256x::U256}, }; use super::{super::tree_add::TreeAdd, MainTreeAdd}; diff --git a/blockset-lib/src/cdt/node_id.rs b/blockset-lib/src/cdt/node_id.rs index 5bbefa44..9299ba2d 100644 --- a/blockset-lib/src/cdt/node_id.rs +++ b/blockset-lib/src/cdt/node_id.rs @@ -1,9 +1,9 @@ use crate::{ sha2::{compress::compress, sha224::SHA224}, uint::{ - u128::to_u32x4, - u224::U224, - u256::{bitor, shl, U256}, + u128x::to_u32x4, + u224x::U224, + u256x::{bitor, shl, U256}, }, }; @@ -39,7 +39,10 @@ pub const fn merge(a: &U256, b: &U256) -> U256 { } let len = a_len + b_len; if len <= LEN_MAX { - set_len(&bitor(&remove_len(a), &shl(&remove_len(b), a_len)), len) + set_len( + &bitor(&remove_len(a), &shl(&remove_len(b), a_len as i32)), + len, + ) } else { let mut x = compress(SHA224, [*a, *b]); x[1] |= 0xFFFF_FFFF << 96; @@ -65,7 +68,7 @@ mod test { use crate::{ cdt::node_id::{len, merge, remove_len, to_node_id, LEN_HI_POS}, - uint::u256::{shl, U256}, + uint::u256x::{shl, U256}, }; #[wasm_bindgen_test] @@ -81,7 +84,7 @@ mod test { assert_eq!(a, [0x12, 0]); b = remove_len(&b); assert_eq!(b, [0x34, 0]); - b = shl(&b, a_len); + b = shl(&b, a_len as i32); assert_eq!(b, [0x3400, 0]); } diff --git a/blockset-lib/src/cdt/subtree.rs b/blockset-lib/src/cdt/subtree.rs index 449219ce..e4ca3a6e 100644 --- a/blockset-lib/src/cdt/subtree.rs +++ b/blockset-lib/src/cdt/subtree.rs @@ -2,7 +2,7 @@ use nanvm_lib::common::cast::Cast; use crate::{ cdt::node_id::merge, - uint::u256::{great, U256}, + uint::u256x::{great, U256}, }; // It should work faster than (a ^ b).leading_zeros(). @@ -88,7 +88,7 @@ mod test { node_id::{merge, to_node_id}, subtree::Node, }, - uint::u256::U256, + uint::u256x::U256, }; use super::{height, SubTree}; diff --git a/blockset-lib/src/cdt/tree_add.rs b/blockset-lib/src/cdt/tree_add.rs index 0c68a79f..71a5536f 100644 --- a/blockset-lib/src/cdt/tree_add.rs +++ b/blockset-lib/src/cdt/tree_add.rs @@ -1,6 +1,6 @@ use std::io; -use crate::uint::{u224::U224, u256::U256}; +use crate::uint::{u224x::U224, u256x::U256}; pub trait TreeAdd { fn push(&mut self, node_id: &U256, main_height: usize) -> io::Result; diff --git a/blockset-lib/src/common/status_line.rs b/blockset-lib/src/common/status_line.rs index 3f7dee06..6130d29d 100644 --- a/blockset-lib/src/common/status_line.rs +++ b/blockset-lib/src/common/status_line.rs @@ -2,7 +2,7 @@ use std::io::{self, Write}; use io_trait::Io; -use crate::uint::u64::div_rem; +use crate::uint::u64x::div_rem; pub struct StatusLine<'a, T: Io> { io: &'a T, diff --git a/blockset-lib/src/elliptic_curve/mod.rs b/blockset-lib/src/elliptic_curve/mod.rs new file mode 100644 index 00000000..878c932f --- /dev/null +++ b/blockset-lib/src/elliptic_curve/mod.rs @@ -0,0 +1,14 @@ +pub mod order; +pub mod order_tag; +pub mod point; +pub mod scalar; + +use crate::{prime_field::prime::Prime, uint::u256x::U256}; + +pub trait EllipticCurve: Prime { + const GX: U256; + const GY: U256; + const A: U256; + const B: U256; + const N: U256; +} diff --git a/blockset-lib/src/elliptic_curve/order.rs b/blockset-lib/src/elliptic_curve/order.rs new file mode 100644 index 00000000..82458db6 --- /dev/null +++ b/blockset-lib/src/elliptic_curve/order.rs @@ -0,0 +1,5 @@ +use crate::prime_field::scalar::Scalar; + +use super::order_tag::OrderTag; + +pub type Order = Scalar>; diff --git a/blockset-lib/src/elliptic_curve/order_tag.rs b/blockset-lib/src/elliptic_curve/order_tag.rs new file mode 100644 index 00000000..7579649f --- /dev/null +++ b/blockset-lib/src/elliptic_curve/order_tag.rs @@ -0,0 +1,11 @@ +use std::marker::PhantomData; + +use crate::{prime_field::prime::Prime, uint::u256x::U256}; + +use super::EllipticCurve; + +pub struct OrderTag(PhantomData); + +impl Prime for OrderTag { + const P: U256 = C::N; +} diff --git a/blockset-lib/src/elliptic_curve/point.rs b/blockset-lib/src/elliptic_curve/point.rs new file mode 100644 index 00000000..522f0215 --- /dev/null +++ b/blockset-lib/src/elliptic_curve/point.rs @@ -0,0 +1,77 @@ +use crate::{prime_field::scalar::Scalar, uint::u256x}; + +use super::{order::Order, EllipticCurve}; + +pub type Point = [Scalar; 2]; + +const X: usize = 0; +const Y: usize = 1; + +const fn eq(a: &Point, b: &Point) -> bool { + a[X].eq(&b[X]) && a[Y].eq(&b[Y]) +} + +const fn from_m([x, y]: Point, pqx: Scalar, m: Scalar) -> Point { + let m2 = m.mul(m); + let rx = m2.sub(pqx); + let ry = m.mul(rx.sub(x)).add(y); + let r = [rx, ry.neg()]; + r +} + +pub const fn neg([x, y]: Point) -> Point { + [x, y.neg()] +} + +pub const fn double(p: Point) -> Point { + let [x, y] = p; + // if y = 0, it means either the point is `O` or `m` is not defined. + if y.eq(&Scalar::_0) { + return Scalar::O; + } + from_m( + p, + x.mul(Scalar::_2), + x.mul(x) + .mul(Scalar::_3) + .add(Scalar::A) + .div(y.mul(Scalar::_2)), + ) +} + +pub const fn from_x(x: Scalar) -> Point { + if let Some(y) = x.y() { + return [x, y]; + } + panic!(); +} + +pub const fn add(p: Point, q: Point) -> Point { + let [px, py] = p; + let [qx, qy] = q; + if px.eq(&qx) { + return if py.eq(&qy) { double(p) } else { Scalar::O }; + } + if eq(&p, &Scalar::O) { + return q; + } + if eq(&q, &Scalar::O) { + return p; + } + from_m(p, px.add(qx), py.sub(qy).div(px.sub(qx))) +} + +pub const fn mul(mut p: Point, mut n: Order) -> Point { + let mut r = Scalar::O; + loop { + if n.0[0] & 1 != 0 { + r = add(r, p); + } + n.0 = u256x::shr(&n.0, 1); + if u256x::eq(&n.0, &u256x::_0) { + break; + } + p = double(p); + } + r +} diff --git a/blockset-lib/src/elliptic_curve/scalar.rs b/blockset-lib/src/elliptic_curve/scalar.rs new file mode 100644 index 00000000..91492cfa --- /dev/null +++ b/blockset-lib/src/elliptic_curve/scalar.rs @@ -0,0 +1,22 @@ +use crate::prime_field::scalar::Scalar; + +use super::EllipticCurve; + +impl Scalar

{ + pub const _2: Self = Self::n(2); + pub const _3: Self = Self::n(3); + pub const A: Self = Self::new(P::A); + pub const B: Self = Self::new(P::B); + // Points: + pub const G: [Self; 2] = [Self::new(P::GX), Self::new(P::GY)]; + // Note: [0, 0] should not be on a curve so we can use it as an infinity point. + // `b != 0`. + pub const O: [Self; 2] = [Self::_0, Self::_0]; + // + pub const fn y2(self) -> Self { + self.pow(Self::_3).add(self.mul(Self::A)).add(Self::B) + } + pub const fn y(self) -> Option { + self.y2().sqrt() + } +} diff --git a/blockset-lib/src/forest/mem.rs b/blockset-lib/src/forest/mem.rs index ccf70bde..2db5c9d9 100644 --- a/blockset-lib/src/forest/mem.rs +++ b/blockset-lib/src/forest/mem.rs @@ -1,7 +1,7 @@ #![cfg(test)] use std::{collections::BTreeMap, io}; -use crate::uint::u224::U224; +use crate::uint::u224x::U224; use super::{node_id::ForestNodeId, Forest}; diff --git a/blockset-lib/src/forest/mod.rs b/blockset-lib/src/forest/mod.rs index 9756d820..7985bc70 100644 --- a/blockset-lib/src/forest/mod.rs +++ b/blockset-lib/src/forest/mod.rs @@ -2,7 +2,7 @@ use std::io::{self, Write}; use crate::{ cdt::{node_id::root, node_type::NodeType}, - uint::{u224::U224, u32::from_u8x4}, + uint::{u224x::U224, u32x::from_u8x4}, }; use self::node_id::ForestNodeId; diff --git a/blockset-lib/src/forest/node_id.rs b/blockset-lib/src/forest/node_id.rs index e3e0bdb7..e99e7b25 100644 --- a/blockset-lib/src/forest/node_id.rs +++ b/blockset-lib/src/forest/node_id.rs @@ -1,4 +1,4 @@ -use crate::{cdt::node_type::NodeType, uint::u224::U224}; +use crate::{cdt::node_type::NodeType, uint::u224x::U224}; pub struct ForestNodeId { pub node_type: NodeType, diff --git a/blockset-lib/src/forest/tree_add.rs b/blockset-lib/src/forest/tree_add.rs index a78a47e0..e55a18d6 100644 --- a/blockset-lib/src/forest/tree_add.rs +++ b/blockset-lib/src/forest/tree_add.rs @@ -11,9 +11,9 @@ use crate::{ tree_add::TreeAdd, }, uint::{ - u224::U224, - u256::{to_u224, U256}, - u32::to_u8x4, + u224x::U224, + u256x::{to_u224, U256}, + u32x::to_u8x4, }, }; @@ -143,7 +143,7 @@ mod test { cdt::{main_tree::MainTreeAdd, node_type::NodeType, tree_add::TreeAdd}, common::status_line::{mb, StatusLine}, forest::{mem::MemForest, node_id::ForestNodeId, Forest}, - uint::u224::U224, + uint::u224x::U224, }; use super::ForestTreeAdd; diff --git a/blockset-lib/src/hmac.rs b/blockset-lib/src/hmac.rs new file mode 100644 index 00000000..3ac14db5 --- /dev/null +++ b/blockset-lib/src/hmac.rs @@ -0,0 +1,107 @@ +use crate::{ + sha2::{self, be_chunk::BeChunk, hash_state::HashState, sha256::SHA256}, + uint::{ + u256x::{self, U256}, + u512x::{self, U512}, + }, +}; + +const fn repeat(v: u128) -> U512 { + let x = [v, v]; + [x, x] +} + +const I_PAD: U512 = repeat(0x36363636_36363636_36363636_36363636); +const O_PAD: U512 = repeat(0x5C5C5C5C_5C5C5C5C_5C5C5C5C_5C5C5C5C); + +// https://cryptii.com/pipes/hmac +pub struct HmacSha256 { + state: sha2::state::State, + key: U512, +} + +impl HmacSha256 { + const fn hash_state(key: U512, pad: U512) -> HashState { + HashState::new(SHA256).push(u512x::bitxor(&key, &pad)) + } + pub const fn new(key: U512) -> Self { + Self { + state: Self::hash_state(key, I_PAD).state(), + key, + } + } + pub const fn push(mut self, rest: &BeChunk) -> Self { + self.state = self.state.push(rest); + self + } + #[inline(always)] + const fn push_array(mut self, a: &[u8]) -> Self { + self.state = self.state.push_array(a); + self + } + pub const fn end(self) -> U256 { + Self::hash_state(self.key, O_PAD).end(BeChunk::new([u256x::_0, self.state.end()], 0x100)) + } +} + +#[cfg(test)] +mod tests { + use wasm_bindgen_test::wasm_bindgen_test; + + use crate::uint::u512x; + + use super::HmacSha256; + + #[test] + #[wasm_bindgen_test] + fn test() { + assert_eq!( + HmacSha256::new(u512x::_0).end(), + [ + 0xff1697c4_93715653_c6c71214_4292c5ad, + 0xb613679a_0814d9ec_772f95d7_78c35fc5, + ] + ); + assert_eq!( + HmacSha256::new(u512x::be( + 0x1000_0000_0000_0000_0000_0000_0000_0000, + 0, + 0, + 0 + )) + .end(), + [ + 0x720f729a_884cf655_581a0f6b_83e05d01, + 0xe5ed5d2c_d3d2da2c_8a23322c_a509cc41 + ] + ); + assert_eq!( + HmacSha256::new(u512x::be( + 0x1000_0000_0000_0000_0000_0000_0000_0000, + 0, + 0, + 0 + )) + .push_array(b"The quick brown fox jumps over the lazy dog") + .end(), + [ + 0x3f900df9_52fcd88a_d4dc6134_a2b7af12, + 0x0b8e8977_aa8b1ad0_5691c746_04ed9cf6 + ] + ); + assert_eq!( + HmacSha256::new(u512x::be( + 0xFFFF_FFFF_0000_0000_0000_0000_0000_0000, + 0, + 0, + 0 + )) + .push_array(b"The quick brown fox jumps over the lazy dog") + .end(), + [ + 0xdcf8a948_20e1e8eb_0010e0b2_14b89fa8, + 0x8d0e6c9d_de99251d_18203a1a_c3288a93, + ] + ) + } +} diff --git a/blockset-lib/src/lib.rs b/blockset-lib/src/lib.rs index 9977430c..f9eedb84 100644 --- a/blockset-lib/src/lib.rs +++ b/blockset-lib/src/lib.rs @@ -1,8 +1,14 @@ mod app; +mod asn1; mod cdt; mod common; +mod elliptic_curve; mod forest; +mod hmac; mod info; +mod nonce; +mod prime_field; +mod sec; mod sha2; mod uint; diff --git a/blockset-lib/src/nonce.rs b/blockset-lib/src/nonce.rs new file mode 100644 index 00000000..ec0f6205 --- /dev/null +++ b/blockset-lib/src/nonce.rs @@ -0,0 +1,99 @@ +// RFC6979 https://www.rfc-editor.org/rfc/rfc6979 + +use crate::{ + hmac::HmacSha256, + prime_field::{prime::Prime, scalar::Scalar}, + sha2::be_chunk::BeChunk, + uint::u256x::{self, U256}, +}; + +struct VK { + v: U256, + k: U256, +} + +pub const fn nonce(pk: &Scalar

, m: &Scalar

) -> Scalar

{ + let p = Scalar::

::P; + let offset = Scalar::

::OFFSET as i32; + const fn c(v: &Scalar

) -> BeChunk { + let offset8 = Scalar::

::OFFSET8; + BeChunk::new( + [u256x::_0, u256x::shl(&v.0, offset8 as i32)], + 256 - offset8 as u16, + ) + } + let mut vk = VK { + v: [ + 0x01010101_01010101_01010101_01010101, + 0x01010101_01010101_01010101_01010101, + ], + k: u256x::_0, + }; + const fn h(&VK { v, k }: &VK) -> HmacSha256 { + HmacSha256::new([u256x::_0, k]).push(&BeChunk::u256(v)) + } + const fn g(vk: &VK) -> U256 { + h(vk).end() + } + const fn s(vk: &VK, b: u8) -> HmacSha256 { + h(vk).push(&BeChunk::u8(b)) + } + const fn f(pk: &BeChunk, m: &BeChunk, mut vk: VK, b: u8) -> VK { + vk.k = s(&vk, b).push(pk).push(m).end(); + vk.v = g(&vk); + vk + } + let pkc = c::

(pk); + let mc = c::

(m); + vk = f(&pkc, &mc, vk, 0x00); + vk = f(&pkc, &mc, vk, 0x01); + loop { + vk.v = g(&vk); + let k = u256x::shr(&vk.v, offset); + if u256x::less(&k, &p) { + return Scalar::

::new(k); + } + vk.k = s(&vk, 0x00).end(); + vk.v = g(&vk); + } +} + +#[cfg(test)] +mod tests { + use wasm_bindgen_test::wasm_bindgen_test; + + use crate::{ + nonce::nonce, + prime_field::{prime::Prime, scalar::Scalar}, + sha2::{sha256::SHA256, state::State}, + uint::u256x::{self, U256}, + }; + + struct Sect163k1(); + + impl Prime for Sect163k1 { + const P: U256 = u256x::be(0x04_00000000, 0x00000000_00020108_A2E0CC0D_99F8A5EF); + } + + #[test] + #[wasm_bindgen_test] + fn test() { + const Q: U256 = u256x::be(0x04_00000000, 0x00000000_00020108_A2E0CC0D_99F8A5EF); + const X: U256 = u256x::be(0x00_9A4D6792, 0x295A7F73_0FC3F2B4_9CBC0F62_E862272F); + const UX: U256 = u256x::be(0x07_9AEE090D, 0xB05EC252_D5CB4452_F356BE19_8A4FF96F); + const UY: U256 = u256x::be(0x07_82E29634, 0xDDC9A31E_F40386E8_96BAA18B_53AFA5A3); + let h1 = State::new(SHA256).push_array(b"sample").end(); + assert_eq!( + h1, + u256x::be( + 0xAF2BDBE1_AA9B6EC1_E2ADE1D6_94F41FC7, + 0x1A831D02_68E98915_62113D8A_62ADD1BF + ) + ); + let n = nonce::(&Scalar::new(X), &Scalar::from_be(h1)); + assert_eq!( + n.0, + u256x::be(0x02_3AF4074C, 0x90A02B3F_E61D286D_5C87F425_E6BDD81B) + ); + } +} diff --git a/blockset-lib/src/prime_field/mod.rs b/blockset-lib/src/prime_field/mod.rs new file mode 100644 index 00000000..d04398a0 --- /dev/null +++ b/blockset-lib/src/prime_field/mod.rs @@ -0,0 +1,3 @@ +pub mod prime; +pub mod scalar; +pub mod vec2; diff --git a/blockset-lib/src/prime_field/prime.rs b/blockset-lib/src/prime_field/prime.rs new file mode 100644 index 00000000..e81f7dbe --- /dev/null +++ b/blockset-lib/src/prime_field/prime.rs @@ -0,0 +1,5 @@ +use crate::uint::u256x::U256; + +pub trait Prime { + const P: U256; +} diff --git a/blockset-lib/src/prime_field/scalar.rs b/blockset-lib/src/prime_field/scalar.rs new file mode 100644 index 00000000..f35927e4 --- /dev/null +++ b/blockset-lib/src/prime_field/scalar.rs @@ -0,0 +1,165 @@ +use core::fmt; +use std::marker::PhantomData; + +use crate::{ + prime_field::vec2, + uint::{ + u256x::{self, U256}, + u512x, + }, +}; + +use super::{prime::Prime, vec2::Vec2}; + +pub struct Scalar(pub U256, PhantomData

); + +impl Clone for Scalar

{ + fn clone(&self) -> Self { + Self::unchecked_new(self.0) + } +} + +impl PartialEq for Scalar

{ + fn eq(&self, other: &Self) -> bool { + self.eq(other) + } +} + +impl fmt::Debug for Scalar

{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Copy for Scalar

{} + +impl Scalar

{ + pub const P: U256 = P::P; + pub const OFFSET: u32 = u256x::leading_zeros(Self::P); + pub const OFFSET8: u32 = Self::OFFSET >> 3 << 3; + pub const _0: Self = Self::n(0); + pub const _1: Self = Self::n(1); + pub const MAX: Self = Self::new(u256x::wsub(Self::P, [1, 0])); + pub const MIDDLE: Self = Self::new(u256x::shr(&Self::P, 1)); + // (P+1)/4 + const SQRT_K: Self = Self::new(u256x::shr(&u256x::wadd(Self::P, [1, 0]), 2)); + const fn is_valid(key: U256) -> bool { + u256x::less(&key, &Self::P) + } + // + #[inline(always)] + pub const fn unchecked_new(num: U256) -> Self { + Self(num, PhantomData) + } + #[inline(always)] + pub const fn new(mut num: U256) -> Self { + num = if u256x::less(&num, &Self::P) { + num + } else { + u256x::wsub(num, Self::P) + }; + Self::unchecked_new(num) + } + pub const fn from_be(num: U256) -> Self { + Self::new(u256x::shr(&num, Self::OFFSET as i32)) + } + #[inline(always)] + pub const fn n(num: u128) -> Self { + Self::new([num, 0]) + } + #[inline(always)] + pub const fn eq(&self, b: &Self) -> bool { + u256x::eq(&self.0, &b.0) + } + // + pub const fn sub(self, b: Self) -> Self { + let (mut result, b) = u256x::osub(self.0, b.0); + if b { + result = u256x::wadd(result, Self::P) + } + Self::unchecked_new(result) + } + #[inline(always)] + pub const fn neg(self) -> Self { + Self::_0.sub(self) + } + pub const fn add(self, b: Self) -> Self { + self.sub(b.neg()) + } + #[inline(always)] + const fn is_neg(&self) -> bool { + u256x::less(&Self::MIDDLE.0, &self.0) + } + #[inline(always)] + pub const fn abs(self) -> Self { + if self.is_neg() { + self.neg() + } else { + self + } + } + // + pub const fn mul(self, b: Self) -> Self { + Self::unchecked_new(u512x::div_rem(u256x::mul(self.0, b.0), [Self::P, u256x::_0])[1][0]) + } + pub const fn reciprocal2(mut self) -> Vec2

{ + assert!(!Self::_0.eq(&self)); + let mut a0 = Self::P; + let mut f0 = [Self::_1, Self::_0]; + let mut f1 = [Self::_0, Self::_1]; + loop { + if Self::_1.eq(&self) { + return f1; + } + let [q, a2] = u256x::div_rem(a0, self.0); + a0 = self.0; + self = Self::unchecked_new(a2); + let f2 = vec2::sub(f0, vec2::mul(f1, Self::unchecked_new(q))); + f0 = f1; + f1 = f2; + } + } + pub const fn reciprocal(mut self) -> Self { + assert!(!Self::_0.eq(&self)); + let mut a0 = Self::P; + let mut f0 = Self::_0; + let mut f1 = Self::_1; + loop { + if Self::_1.eq(&self) { + return f1; + } + let [q, a2] = u256x::div_rem(a0, self.0); + a0 = self.0; + self = Self::unchecked_new(a2); + let f2 = f0.sub(f1.mul(Self::unchecked_new(q))); + f0 = f1; + f1 = f2; + } + } + pub const fn div(self, b: Self) -> Self { + self.mul(b.reciprocal()) + } + pub const fn pow(mut self, mut n: Self) -> Self { + let mut result = Self::_1; + loop { + if n.0[0] & 1 == 1 { + result = result.mul(self); + } + n.0 = u256x::shr(&n.0, 1); + if Self::_0.eq(&n) { + break; + } + self = self.mul(self); + } + result + } + pub const fn sqrt(self) -> Option { + assert!(Self::P[0] & 3 == 3); + let result = self.pow(Self::SQRT_K); + if result.mul(result).eq(&self) { + Some(result) + } else { + None + } + } +} diff --git a/blockset-lib/src/prime_field/vec2.rs b/blockset-lib/src/prime_field/vec2.rs new file mode 100644 index 00000000..6c3256ca --- /dev/null +++ b/blockset-lib/src/prime_field/vec2.rs @@ -0,0 +1,11 @@ +use super::{prime::Prime, scalar::Scalar}; + +pub type Vec2

= [Scalar

; 2]; + +pub const fn mul([x, y]: Vec2

, a: Scalar

) -> Vec2

{ + [x.mul(a), y.mul(a)] +} + +pub const fn sub([x0, y0]: Vec2

, [x1, y1]: Vec2

) -> Vec2

{ + [x0.sub(x1), y0.sub(y1)] +} diff --git a/blockset-lib/src/sec/mod.rs b/blockset-lib/src/sec/mod.rs new file mode 100644 index 00000000..be9b3fdc --- /dev/null +++ b/blockset-lib/src/sec/mod.rs @@ -0,0 +1,228 @@ +use crate::{ + elliptic_curve::{ + order::Order, + point::{self, Point}, + EllipticCurve, + }, + nonce::nonce, + prime_field::scalar::Scalar, +}; + +mod p192r1; +mod p224r1; +mod p256k1; +mod p256r1; +mod test; + +type Signature = [Order; 2]; + +impl Order { + pub const fn public_key(self) -> Point { + point::mul(Scalar::G, self) + } + pub const fn sign(self, z: Self) -> Signature { + let k = nonce(&self, &z); + let r = Self::new(point::mul(Scalar::G, k)[0].0); + let s = z.add(r.mul(self)).div(k); + [r, s] + } +} + +pub const fn verify( + pub_key: Point, + z: Order, + [r, s]: Signature, +) -> bool { + let si = s.reciprocal(); + let u1 = z.mul(si); + let u2 = r.mul(si); + let p = Order::new(point::add(point::mul(Scalar::G, u1), point::mul(pub_key, u2))[0].0); + p.eq(&r) +} + +#[cfg(test)] +mod tests { + use wasm_bindgen_test::wasm_bindgen_test; + + use crate::{ + elliptic_curve::{order::Order, EllipticCurve}, + nonce::nonce, + prime_field::scalar::Scalar, + sec::{p224r1::P224r1, p256k1::P256k1, p256r1::P256r1, verify}, + sha2::{sha256::SHA256, state::State}, + uint::u256x::{self, U256}, + }; + + use super::p192r1::P192r1; + + #[test] + #[wasm_bindgen_test] + fn test_192() { + fn g( + xe: U256, + px: U256, + py: U256, + k0: U256, + r0: U256, + s0: U256, + k1: U256, + r1: U256, + s1: U256, + ) { + let x = Order::::new(xe); + let public_key = x.public_key(); + assert_eq!(public_key, [Scalar::new(px), Scalar::new(py)]); + let f = |a, ke, re, se| { + let h1 = Scalar::from_be(State::new(SHA256).push_array(a).end()); + let k = nonce(&x, &h1); + assert_eq!(k.0, ke); + let rs = x.sign(h1); + let [r, s] = rs; + assert_eq!(r.0, re); + assert_eq!(s.0, se); + assert!(verify(public_key, h1, rs)); + }; + f(b"sample", k0, r0, s0); + f(b"test", k1, r1, s1); + } + g::( + u256x::be(0x6FAB0349_34E4C0FC, 0x9AE67F5B_5659A9D7_D1FEFD18_7EE09FD4), + u256x::be(0xAC2C77F5_29F91689, 0xFEA0EA5E_FEC7F210_D8EEA0B9_E047ED56), + u256x::be(0x3BC723E5_7670BD48, 0x87EBC732_C523063D_0A7C957B_C97C1C43), + u256x::be(0x32B1B6D7_D42A05CB, 0x44906572_7A84804F_B1A3E34D_8F261496), + u256x::be(0x4B0B8CE9_8A92866A, 0x2820E20A_A6B75B56_382E0F9B_FD5ECB55), + u256x::be(0xCCDB0069_26EA9565, 0xCBADC840_829D8C38_4E06DE1F_1E381B85), + u256x::be(0x5C4CE89C_F56D9E7C, 0x77C85853_39B006B9_7B5F0680_B4306C6C), + u256x::be(0x3A718BD8_B4926C3B, 0x52EE6BBE_67EF79B1_8CB6EB62_B1AD97AE), + u256x::be(0x5662E684_8A4A19B1, 0xF1AE2F72_ACD4B8BB_E50F1EAC_65D9124F), + ); + g::( + u256x::be( + 0xF220266E_1105BFE3_083E03EC, + 0x7A3A6546_51F45E37_167E8860_0BF257C1, + ), + u256x::be( + 0x00CF08DA_5AD719E4_2707FA43, + 0x1292DEA1_1244D64F_C51610D9_4B130D6C, + ), + u256x::be( + 0xEEAB6F3D_EBE455E3_DBF85416, + 0xF7030CBD_94F34F2D_6F232C69_F3C1385A, + ), + u256x::be( + 0xAD3029E0_278F8064_3DE33917, + 0xCE6908C7_0A8FF50A_411F06E4_1DEDFCDC, + ), + u256x::be( + 0x61AA3DA0_10E8E840_6C656BC4, + 0x77A7A718_9895E7E8_40CDFE8F_F42307BA, + ), + u256x::be( + 0xBC814050_DAB5D237_70879494, + 0xF9E0A680_DC1AF716_1991BDE6_92B10101, + ), + u256x::be( + 0xFF86F579_24DA248D_6E44E815, + 0x4EB69F0A_E2AEBAEE_9931D0B5_A969F904, + ), + u256x::be( + 0xAD04DDE8_7B84747A_243A631E, + 0xA47A1BA6_D1FAA059_149AD244_0DE6FBA6, + ), + u256x::be( + 0x178D49B1_AE90E3D8_B629BE3D, + 0xB5683915_F4E8C99F_DF6E666C_F37ADCFD, + ), + ); + g::( + u256x::be( + 0xC9AFA9D8_45BA7516_6B5C2157_67B1D693, + 0x4E50C3DB_36E89B12_7B8A622B_120F6721, + ), + u256x::be( + 0x60FED4BA_255A9D31_C961EB74_C6356D68, + 0xC049B892_3B61FA6C_E669622E_60F29FB6, + ), + u256x::be( + 0x7903FE10_08B8BC99_A41AE9E9_5628BC64, + 0xF2F1B20C_2D7E9F51_77A3C294_D4462299, + ), + u256x::be( + 0xA6E3C57D_D01ABE90_08653839_8355DD4C, + 0x3B17AA87_3382B0F2_4D612949_3D8AAD60, + ), + u256x::be( + 0xEFD48B2A_ACB6A8FD_1140DD9C_D45E81D6, + 0x9D2C877B_56AAF991_C34D0EA8_4EAF3716, + ), + u256x::be( + 0xF7CB1C94_2D657C41_D436C7A1_B6E29F65, + 0xF3E900DB_B9AFF406_4DC4AB2F_843ACDA8, + ), + u256x::be( + 0xD16B6AE8_27F17175_E040871A_1C7EC350, + 0x0192C4C9_2677336E_C2537ACA_EE0008E0, + ), + u256x::be( + 0xF1ABB023_518351CD_71D88156_7B1EA663, + 0xED3EFCF6_C5132B35_4F28D3B0_B7D38367, + ), + u256x::be( + 0x019F4113_742A2B14_BD25926B_49C64915, + 0x5F267E60_D3814B4C_0CC84250_E46F0083, + ), + ); + } + + #[test] + #[wasm_bindgen_test] + fn test() { + let f = |p, h| { + // Alice + let private_key = Order::::new(p); + let public_key = private_key.public_key(); + let hash = Order::new(h); + let signature = private_key.sign(hash); + // Bob + let result = verify(public_key, hash, signature); + assert!(result); + // Enemy + let w_private_key = private_key.add(Order::_1); + let w_signature = w_private_key.sign(hash); + // Bob + let w_result = verify(public_key, hash, w_signature); + assert!(!w_result); + }; + f( + [ + 1234567890_1234567890_1234567890_123456789, + 234567890_1234567890_1234567890_1234567890, + ], + [ + 34567890_1234567890_1234567890_1234567890, + 4567890_1234567890_1234567890_1234567890_1, + ], + ); + f( + [ + 7890_1234567890_1234567890_1234567890_1234, + 890_1234567890_1234567890_1234567890_12345, + ], + [ + 90_1234567890_1234567890_1234567890_123456, + 1234567890_1234567890_1234567890_123456790, + ], + ); + f( + [ + 1111111111_2222222222_3333333333_444444444, + 4444444444_5555555555_6666666666_77777777, + ], + [ + 8888888888_9999999999_0000000000_11111111, + 2222222222_3333333333_4444444444_555555555, + ], + ); + f([u128::MAX, u128::MAX], [u128::MAX, u128::MAX]); + } +} diff --git a/blockset-lib/src/sec/p192r1.rs b/blockset-lib/src/sec/p192r1.rs new file mode 100644 index 00000000..eeff96f7 --- /dev/null +++ b/blockset-lib/src/sec/p192r1.rs @@ -0,0 +1,61 @@ +use crate::{ + elliptic_curve::EllipticCurve, + prime_field::prime::Prime, + uint::u256x::{self, U256}, +}; + +// https://neuromancer.sk/std/secg/secp192r1 +pub struct P192r1(); + +impl Prime for P192r1 { + const P: U256 = u256x::be( + 0x00000000_00000000_ffffffff_ffffffff, + 0xffffffff_fffffffe_ffffffff_ffffffff, + ); +} + +impl EllipticCurve for P192r1 { + const GX: U256 = u256x::be( + 0x00000000_00000000_188da80e_b03090f6, + 0x7cbf20eb_43a18800_f4ff0afd_82ff1012, + ); + const GY: U256 = u256x::be( + 0x00000000_00000000_07192b95_ffc8da78, + 0x631011ed_6b24cdd5_73f977a1_1e794811, + ); + const A: U256 = u256x::be( + 0x00000000_00000000_ffffffff_ffffffff, + 0xffffffff_fffffffe_ffffffff_fffffffc, + ); + const B: U256 = u256x::be( + 0x00000000_00000000_64210519_e59c80e7, + 0x0fa7e9ab_72243049_feb8deec_c146b9b1, + ); + const N: U256 = u256x::be( + 0x00000000_00000000_ffffffff_ffffffff, + 0xffffffff_99def836_146bc9b1_b4d22831, + ); +} + +#[cfg(test)] +mod test { + use wasm_bindgen_test::wasm_bindgen_test; + + use crate::{prime_field::scalar::Scalar, sec::test::gen_test}; + + use super::P192r1; + + #[test] + #[wasm_bindgen_test] + fn test() { + gen_test::(); + } + + #[test] + #[wasm_bindgen_test] + fn test_b() { + let r0 = Scalar::::B.sqrt().unwrap(); + let r1 = Scalar::::_0.y().unwrap(); + assert_eq!(r0.abs(), r1.abs()); + } +} diff --git a/blockset-lib/src/sec/p224r1.rs b/blockset-lib/src/sec/p224r1.rs new file mode 100644 index 00000000..a4be46e5 --- /dev/null +++ b/blockset-lib/src/sec/p224r1.rs @@ -0,0 +1,54 @@ +// https://neuromancer.sk/std/secg/secp224r1 + +use crate::{ + elliptic_curve::EllipticCurve, + prime_field::prime::Prime, + uint::u256x::{self, U256}, +}; + +pub struct P224r1(); + +impl Prime for P224r1 { + const P: U256 = u256x::be( + 0x00000000_fffffff_ffffffff_fffffffff, + 0xffffffff_00000000_00000000_00000001, + ); +} + +impl EllipticCurve for P224r1 { + const GX: U256 = u256x::be( + 0x00000000_b70e0cbd_6bb4bf7f_321390b9, + 0x4a03c1d3_56c21122_343280d6_115c1d21, + ); + const GY: U256 = u256x::be( + 0x00000000_bd376388_b5f723fb_4c22dfe6, + 0xcd4375a0_5a074764_44d58199_85007e34, + ); + const A: U256 = u256x::be( + 0x00000000_ffffffff_ffffffff_ffffffff, + 0xfffffffe_ffffffff_ffffffff_fffffffe, + ); + const B: U256 = u256x::be( + 0x00000000_b4050a85_0c04b3ab_f5413256, + 0x5044b0b7_d7bfd8ba_270b3943_2355ffb4, + ); + const N: U256 = u256x::be( + 0x00000000_ffffffff_ffffffff_ffffffff, + 0xffff16a2_e0b8f03e_13dd2945_5c5c2a3d, + ); +} + +#[cfg(test)] +mod test { + use wasm_bindgen_test::wasm_bindgen_test; + + use crate::sec::test::gen_test1; + + use super::P224r1; + + #[test] + #[wasm_bindgen_test] + fn test() { + gen_test1::(); + } +} diff --git a/blockset-lib/src/sec/p256k1.rs b/blockset-lib/src/sec/p256k1.rs new file mode 100644 index 00000000..11479610 --- /dev/null +++ b/blockset-lib/src/sec/p256k1.rs @@ -0,0 +1,334 @@ +use crate::{ + elliptic_curve::EllipticCurve, + prime_field::prime::Prime, + uint::u256x::{self, U256}, +}; + +// https://en.bitcoin.it/wiki/Secp256k1 +// https://neuromancer.sk/std/secg/secp256k1 +pub struct P256k1(); + +impl Prime for P256k1 { + const P: U256 = u256x::be( + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F, + ); +} + +impl EllipticCurve for P256k1 { + const GX: U256 = u256x::be( + 0x79BE667E_F9DCBBAC_55A06295_CE870B07, + 0x029BFCDB_2DCE28D9_59F2815B_16F81798, + ); + const GY: U256 = u256x::be( + 0x483ADA77_26A3C465_5DA4FBFC_0E1108A8, + 0xFD17B448_A6855419_9C47D08F_FB10D4B8, + ); + const A: U256 = u256x::_0; + const B: U256 = u256x::from_u128(7); + const N: U256 = u256x::be( + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE, + 0xBAAEDCE6_AF48A03B_BFD25E8C_D0364141, + ); +} + +#[cfg(test)] +mod test { + use wasm_bindgen_test::wasm_bindgen_test; + + use crate::{ + elliptic_curve::{order::Order, point::from_x}, + prime_field, + sec::{ + p256k1::P256k1, + test::{gen_test, gen_test_double, test_point_mul}, + }, + uint::u256x::{self, U256}, + }; + + type Scalar = prime_field::scalar::Scalar; + + const fn is_valid(key: U256) -> bool { + u256x::less(&key, &Scalar::P) + } + + const fn is_valid_private_key(key: U256) -> bool { + u256x::less(&u256x::_0, &key) && is_valid(key) + } + + const Q2: Scalar = Scalar::new([ + 25454351255596125846892804522787951607, + 43929286026618122883815740552890121610, + ]); + + #[test] + #[wasm_bindgen_test] + fn test_y() { + gen_test::(); + fn check(lo: u128, hi: u128, some: bool) { + assert_eq!(Scalar::new([lo, hi]).y().is_some(), some); + } + // some random numbers + check( + 0x9fd69639_62398010_7c2f54a3_5a168569, + 0xc288ac7d_64d0e032_1978d304_cce41ac9, + true, + ); + check( + 0xdee33137_a71b5674_78700202_824cc0b4, + 0x0c7e1456_b02e4892_ae84b0d8_fbc104f6, + true, + ); + check( + 0xf8278695_19ceeb05_02738f13_0ee52287, + 0x604c5652_7ee62bb6_925b7286_69e67659, + false, + ); + // + assert_eq!(Scalar::_1.y().unwrap(), Q2.mul(Scalar::n(2))); + } + + #[test] + #[wasm_bindgen_test] + fn test_sqrt() { + assert_eq!(Scalar::n(2).sqrt(), Some(Q2)); + assert_eq!(Scalar::n(3).sqrt(), None); + assert_eq!(Scalar::n(5).sqrt(), None); + assert_eq!(Scalar::n(6).sqrt(), None); + // So $y^2 = x^3 + 7$ is not defined when $x = 0$. + assert_eq!(Scalar::n(7).sqrt(), None); + assert_eq!(Scalar::new([8, 0]).sqrt(), Some(Q2.mul(Scalar::n(2)))); + } + + #[test] + #[wasm_bindgen_test] + fn test_pow() { + assert_eq!( + Scalar::_2.pow(Scalar::new([255, 0])), + Scalar::new([0, 0x8000_0000_0000_0000_0000_0000_0000_0000]) + ); + assert_eq!( + Scalar::_3.pow(Scalar::new([122, 0])), + Scalar::new(u256x::be( + 0x2_9396f76b_67b7c403, + 0xd73a1059_b8013933_6878e449_38606769 + )) + ); + } + + #[test] + #[wasm_bindgen_test] + fn test() { + // + assert!(!is_valid_private_key([0, 0])); + assert!(is_valid_private_key([1, 0])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E, + 0 + ])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F, + 0 + ])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF, + 0 + ])); + // + assert!(is_valid_private_key([0, 1])); + assert!(is_valid_private_key([1, 1])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E, + 1 + ])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F, + 1 + ])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF, + 1 + ])); + // + assert!(is_valid_private_key([ + 0, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E + ])); + assert!(is_valid_private_key([ + 1, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E + ])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E + ])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E + ])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E + ])); + // + assert!(is_valid_private_key([ + 0, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F + ])); + assert!(is_valid_private_key([ + 1, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F + ])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F + ])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F + ])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F + ])); + // + assert!(is_valid_private_key([ + 0, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE + ])); + assert!(is_valid_private_key([ + 1, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE + ])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE + ])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE + ])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE + ])); + // + assert!(is_valid_private_key([ + 0, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ])); + assert!(is_valid_private_key([ + 1, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ])); + assert!(is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ])); + assert!(!is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ])); + assert!(!is_valid_private_key([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ])); + } + + #[test] + #[wasm_bindgen_test] + fn test_add() { + assert_eq!( + Scalar::new([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ]) + .add(Scalar::new([1, 0])), + Scalar::new([0, 0]) + ); + assert_eq!( + Scalar::new([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ]) + .add(Scalar::new([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ])), + Scalar::new([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2D, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ]) + ); + } + #[test] + #[wasm_bindgen_test] + fn test_sub() { + assert_eq!( + Scalar::new([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ]) + .sub(Scalar::new([1, 0])), + Scalar::new([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2D, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ]) + ); + assert_eq!( + Scalar::new([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ]) + .sub(Scalar::new([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2D, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ])), + Scalar::new([1, 0]) + ); + assert_eq!( + Scalar::new([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2D, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ]) + .sub(Scalar::new([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ])), + Scalar::new([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ]) + ); + assert_eq!( + Scalar::new([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ]) + .sub(Scalar::new([ + 0xFFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2E, + 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF + ])), + Scalar::_0 + ); + } + + const N: Order = Order::unchecked_new(Order::::P); + + #[test] + #[wasm_bindgen_test] + fn test_double() { + gen_test_double(Scalar::_1); + } + + #[test] + #[wasm_bindgen_test] + fn test_mul_1() { + let s = |x| test_point_mul(from_x(x)); + s(Scalar::_1); + s(Scalar::_2); + s(Scalar::_3); + s(Scalar::n(4)); + s(Scalar::n(6)); + } +} diff --git a/blockset-lib/src/sec/p256r1.rs b/blockset-lib/src/sec/p256r1.rs new file mode 100644 index 00000000..22c17a44 --- /dev/null +++ b/blockset-lib/src/sec/p256r1.rs @@ -0,0 +1,54 @@ +// https://neuromancer.sk/std/secg/secp256r1 + +use crate::{ + elliptic_curve::EllipticCurve, + prime_field::prime::Prime, + uint::u256x::{self, U256}, +}; + +pub struct P256r1(); + +impl Prime for P256r1 { + const P: U256 = u256x::be( + 0xffffffff_00000001_00000000_00000000, + 0x00000000_ffffffff_ffffffff_ffffffff, + ); +} + +impl EllipticCurve for P256r1 { + const GX: U256 = u256x::be( + 0x6b17d1f2_e12c4247_f8bce6e5_63a440f2, + 0x77037d81_2deb33a0_f4a13945_d898c296, + ); + const GY: U256 = u256x::be( + 0x4fe342e2_fe1a7f9b_8ee7eb4a_7c0f9e16, + 0x2bce3357_6b315ece_cbb64068_37bf51f5, + ); + const A: U256 = u256x::be( + 0xffffffff_00000001_00000000_00000000, + 0x00000000_ffffffff_ffffffff_fffffffc, + ); + const B: U256 = u256x::be( + 0x5ac635d8_aa3a93e7_b3ebbd55_769886bc, + 0x651d06b0_cc53b0f6_3bce3c3e_27d2604b, + ); + const N: U256 = u256x::be( + 0xffffffff_00000000_ffffffff_ffffffff, + 0xbce6faad_a7179e84_f3b9cac2_fc632551, + ); +} + +#[cfg(test)] +mod test { + use wasm_bindgen_test::wasm_bindgen_test; + + use crate::sec::test::gen_test; + + use super::P256r1; + + #[test] + #[wasm_bindgen_test] + fn test() { + gen_test::(); + } +} diff --git a/blockset-lib/src/sec/test.rs b/blockset-lib/src/sec/test.rs new file mode 100644 index 00000000..9ed5cf86 --- /dev/null +++ b/blockset-lib/src/sec/test.rs @@ -0,0 +1,232 @@ +#![cfg(test)] + +use crate::{ + elliptic_curve::{ + order::Order, + point::{double, from_x, mul, neg, Point}, + EllipticCurve, + }, + prime_field::{scalar::Scalar, vec2::Vec2}, + uint::u256x, +}; + +fn sqrt_test(c: Scalar) { + let c2 = c.mul(c); + let s = c2.sqrt().unwrap(); + assert_eq!(c, s.abs()); +} + +fn pow_common(s: Scalar) { + assert_eq!(s.pow(Scalar::_0), Scalar::_1); + assert_eq!(s.pow(Scalar::_1), s); + // https://en.wikipedia.org/wiki/Fermat%27s_little_theorem + // a^(p-1) % p = 1 + assert_eq!(s.pow(Scalar::MIDDLE).abs(), Scalar::_1); + assert_eq!(s.pow(Scalar::MAX.sub(Scalar::_1)), s.reciprocal()); + assert_eq!(s.pow(Scalar::MAX), Scalar::_1); +} + +fn pow_test() { + let s2 = Scalar::::new([2, 0]); + let s3 = Scalar::::n(3); + let s4 = Scalar::::new([4, 0]); + let s8 = Scalar::::new([8, 0]); + let s9 = Scalar::::new([9, 0]); + let s27 = Scalar::::new([27, 0]); + // 0 + assert_eq!(Scalar::::_0.pow(Scalar::_0), Scalar::_1); + assert_eq!(Scalar::::_0.pow(Scalar::MAX), Scalar::_0); + // 1 + pow_common(Scalar::::_1); + // 2 + pow_common(s2); + assert_eq!(s2.pow(s2), s4); + assert_eq!(s2.pow(s3), s8); + assert_eq!(s2.pow(Scalar::new([128, 0])), Scalar::new([0, 1])); + assert_eq!( + s2.pow(Scalar::new([191, 0])), + Scalar::new([0, 0x0000_0000_0000_0000_8000_0000_0000_0000]) + ); + // 3 + pow_common(s3); + assert_eq!(s3.pow(s2), s9); + assert_eq!(s3.pow(Scalar::n(3)), s27); + assert_eq!( + s3.pow(Scalar::new([100, 0])), + Scalar::new(u256x::be(0x5a4653ca, 0x67376856_5b41f775_d6947d55_cf3813d1)) + ); + assert_eq!( + s3.pow(Scalar::new([110, 0])), + Scalar::new(u256x::be( + 0x5156_c7b52959, + 0xeb5176ff_82e03b94_10a1d5d2_2dd2daf9 + )) + ); + assert_eq!( + s3.pow(Scalar::new([120, 0])), + Scalar::new(u256x::be( + 0x4949a9b6_99bf15c7, + 0x89b11e42_db8e5bb0_60f0fceb_b0ee4461 + )) + ); + assert_eq!( + s3.pow(Scalar::new([121, 0])), + Scalar::new(u256x::be( + 0xdbdcfd23_cd3d4156, + 0x9d135ac8_92ab1311_22d2f6c3_12cacd23 + )) + ); + // Gx + pow_common(Scalar::::G[0]); + // MIDDLE + pow_common(Scalar::::MIDDLE); + // MAX-1 + pow_common(Scalar::::MAX.sub(Scalar::_1)); + // MAX + pow_common(Scalar::::MAX); +} + +fn test_mul() { + assert_eq!(Scalar::::_0.mul(Scalar::MAX), Scalar::_0); + assert_eq!(Scalar::::_1.mul(Scalar::_1), Scalar::_1); + assert_eq!( + Scalar::::new([2, 0]).mul(Scalar::new([2, 0])), + Scalar::::new([4, 0]) + ); + assert_eq!(Scalar::::MAX.mul(Scalar::MAX), Scalar::_1); +} + +fn test_reciprocal() { + let x = |s: Scalar| { + let v = s.reciprocal(); + assert_eq!(v.mul(s), Scalar::_1); + }; + let f = |s: Scalar, v: Scalar| { + assert_eq!(s.reciprocal(), v); + assert_eq!(v.mul(s), Scalar::_1); + }; + f(Scalar::_1, Scalar::_1); + f(Scalar::MAX, Scalar::MAX); + x(Scalar::::new([2, 0])); + x(Scalar::new([3, 0])); + x(Scalar::new([4, 0])); + x(Scalar::new([u128::MAX, 0])); + x(Scalar::new([5, 1])); + x(Scalar::new([u128::MAX, 1])); + x(Scalar::new([6, 2])); + x(Scalar::new([7, 3])); + x(Scalar::new([8, u128::MAX])); + x(Scalar::new([Scalar::::P[0] - 9, u128::MAX])); +} + +fn test_reciprocal2() { + let x = |s: Scalar| { + let v = s.reciprocal2(); + assert_eq!(v[1].mul(s), Scalar::_1); + }; + let f = |s: Scalar, v: Vec2| { + assert_eq!(s.reciprocal2(), v); + assert_eq!(v[1].mul(s), Scalar::_1); + }; + f(Scalar::_1, [Scalar::_0, Scalar::_1]); + f(Scalar::MAX, [Scalar::_1, Scalar::MAX]); + x(Scalar::new([2, 0])); + x(Scalar::new([3, 0])); + x(Scalar::new([4, 0])); + x(Scalar::new([u128::MAX, 0])); + x(Scalar::new([5, 1])); + x(Scalar::new([u128::MAX, 1])); + x(Scalar::new([6, 2])); + x(Scalar::new([7, 3])); + x(Scalar::new([8, u128::MAX])); + x(Scalar::new([Scalar::::P[0] - 9, u128::MAX])); +} + +fn n() -> Order { + Order::unchecked_new(Order::::P) +} + +fn test_mul_o() { + assert_eq!(mul(Scalar::::O, Order::_0), Scalar::O); + assert_eq!(mul(Scalar::::O, Order::_1), Scalar::O); + assert_eq!(mul(Scalar::::O, Order::n(2)), Scalar::O); + assert_eq!(mul(Scalar::::O, Order::new([0, 1])), Scalar::O); + assert_eq!(mul(Scalar::::O, n()), Scalar::O); +} + +pub fn gen_test_double2(p: Point) { + let p1 = double(p); + let p2 = double(p1); + let p3 = double(p2); +} + +pub fn gen_test_double(x: Scalar) { + gen_test_double2(from_x(x)); +} + +pub fn point_check([x, y]: Point

) { + assert_eq!(x.y2(), y.mul(y)); +} + +pub fn test_point_mul(p: Point) { + let pn = neg(p); + assert_eq!(mul(p, Order::_0), Scalar::O); + assert_eq!(mul(p, Order::_1), p); + assert_eq!(mul(p, n()), Scalar::O); + assert_eq!(mul(p, n().sub(Order::_1)), pn); + // + let f = |s| { + let r = mul(p, s); + point_check(r); + let rn = mul(pn, s); + point_check(rn); + assert_ne!(r, Scalar::O); + assert_ne!(r, p); + assert_ne!(r, pn); + assert_ne!(rn, Scalar::O); + assert_ne!(rn, p); + assert_ne!(rn, pn); + assert_ne!(r, rn); + assert_eq!(r, neg(rn)); + }; + f(Order::n(2)); + f(Order::new([3, 0])); + f(Order::new([0, 1])); + f(Order::new([1, 1])); + f(Order::new([0, 2])); + f(Order::new([2, 2])); + f(Order::new([0, 3])); + f(Order::new([3, 3])); +} + +fn gen_test3() { + assert_eq!(Scalar::::G[0].y().unwrap(), Scalar::G[1]); + // SQRT + for i in 1..1000 { + sqrt_test(Scalar::::new([i, 0])); + } + sqrt_test(Scalar::::G[0]); + sqrt_test(Scalar::::MIDDLE); + gen_test_double::(Scalar::::G[0]); +} + +pub fn gen_test1() { + assert_eq!(Scalar::::G[0].y2(), Scalar::G[1].mul(Scalar::G[1])); + // pow + pow_test::(); + // mul + test_mul::(); + // reciprocal + test_reciprocal::(); + test_reciprocal2::(); + // point + test_mul_o::(); + gen_test_double2::(Scalar::::G); + test_point_mul(Scalar::::G); + test_point_mul(neg(Scalar::::G)); +} + +pub fn gen_test() { + gen_test1::(); + gen_test3::(); +} diff --git a/blockset-lib/src/sha2/be_chunk.rs b/blockset-lib/src/sha2/be_chunk.rs new file mode 100644 index 00000000..feed6633 --- /dev/null +++ b/blockset-lib/src/sha2/be_chunk.rs @@ -0,0 +1,39 @@ +use crate::uint::{ + u256x::{self, U256}, + u512x::{self, U512}, +}; + +#[derive(Default)] +pub struct BeChunk { + pub data: U512, + pub len: u16, +} + +impl BeChunk { + pub const fn new(data: U512, len: u16) -> Self { + Self { data, len } + } + pub const fn default() -> Self { + Self::new(u512x::_0, 0) + } + pub const fn chain(mut self, &BeChunk { data, len }: &BeChunk) -> (Option, Self) { + let d = self.len as i32; + self.data = u512x::bitor(&self.data, &u512x::shl(&data, -d)); + self.len += len; + let r0 = if self.len >= 0x200 { + let r = self.data; + self.len -= 0x200; + self.data = u512x::shl(&data, 0x200 - d); + Some(r) + } else { + None + }; + (r0, self) + } + pub const fn u256(v: U256) -> Self { + BeChunk::new([u256x::_0, v], 0x100) + } + pub const fn u8(v: u8) -> Self { + BeChunk::new(u512x::be((v as u128) << 0x78, 0, 0, 0), 8) + } +} diff --git a/blockset-lib/src/sha2/compress.rs b/blockset-lib/src/sha2/compress.rs index ca8ac27f..fae5566f 100644 --- a/blockset-lib/src/sha2/compress.rs +++ b/blockset-lib/src/sha2/compress.rs @@ -1,6 +1,6 @@ use crate::uint::{ - u256::{u32x8_add, U256}, - u512::U512, + u256x::{u32x8_wadd, U256}, + u512x::U512, }; use super::{round::round16, w_round::w_round16}; @@ -14,5 +14,5 @@ pub const fn compress(init: U256, mut w: U512) -> U256 { x = round16(x, &w, 2); w = w_round16(w); x = round16(x, &w, 3); - u32x8_add(&x, &init) + u32x8_wadd(&x, &init) } diff --git a/blockset-lib/src/sha2/hash_state.rs b/blockset-lib/src/sha2/hash_state.rs new file mode 100644 index 00000000..b616ff00 --- /dev/null +++ b/blockset-lib/src/sha2/hash_state.rs @@ -0,0 +1,509 @@ +use crate::uint::{ + u256x::{self, U256}, + u512x::{self, U512}, +}; + +use super::{be_chunk::BeChunk, compress::compress, state::State}; + +pub struct HashState { + hash: U256, + len: u64, +} + +impl HashState { + pub const fn new(hash: U256) -> Self { + Self { hash, len: 0 } + } + pub const fn state(self) -> State { + State::from_hash_state(self) + } + const fn swap_compress(mut self, data: U512) -> Self { + self.hash = compress(self.hash, u512x::swap32(data)); + self + } + pub const fn push(mut self, data: U512) -> Self { + self = self.swap_compress(data); + self.len += 0x200; + self + } + pub const fn end(mut self, BeChunk { mut data, mut len }: BeChunk) -> U256 { + assert!(len <= 0x200); + if len == 0x200 { + self = self.push(data); + data = u512x::_0; + len = 0; + } + data = u512x::set_bit(data, 0x1FF - len as u32); + self.len += len as u64; + let data00 = self.len as u128; + if len < 0x1FF - 0x40 { + data[0][0] |= data00; + self = self.swap_compress(data); + } else { + self = self.swap_compress(data); + self = self.swap_compress([[data00, 0], u256x::_0]); + } + u256x::swap32(self.hash) + } +} + +#[cfg(test)] +mod tests { + use wasm_bindgen_test::wasm_bindgen_test; + + use crate::{ + sha2::{hash_state::BeChunk, sha224::SHA224, sha256::SHA256}, + uint::{u256x, u512x}, + }; + + use super::HashState; + + #[test] + #[wasm_bindgen_test] + fn test() { + let f = |init, k, len| HashState::new(init).end(BeChunk::new(k, len)); + // d14a028c_2a3a2bc9_476102bb_288234c4 + // 15a2b01f_828ea62a_c5b3e42f + { + let mut h = f(SHA224, u512x::_0, 0); + h[0] |= 0xFFFF_FFFF; + assert_eq!( + h, + u256x::be( + 0xd14a028c_2a3a2bc9_476102bb_288234c4, + 0x15a2b01f_828ea62a_c5b3e42f_FFFFFFFF, + ) + ); + } + // e3b0c442_98fc1c14_9afbf4c8_996fb924 + // 27ae41e4_649b934c_a495991b_7852b855 + assert_eq!( + f(SHA256, u512x::_0, 0), + u256x::be( + 0xe3b0c442_98fc1c14_9afbf4c8_996fb924, + 0x27ae41e4_649b934c_a495991b_7852b855 + ), + ); + // "0" + // 5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9 + assert_eq!( + f( + SHA256, + u512x::be(0x30000000_00000000_00000000_00000000, 0, 0, 0), + 8 + ), + u256x::be( + 0x5feceb6_6ffc86f38_d952786c_6d696c79, + 0xc2dbc23_9dd4e91b4_6729d73a_27fb57e9, + ) + ); + // "01" + // 938db8c9f82c8cb58d3f3ef4fd250036a48d26a712753d2fde5abd03a85cabf4 + assert_eq!( + f( + SHA256, + u512x::be(0x30310000_00000000_00000000_00000000, 0, 0, 0), + 16 + ), + u256x::be( + 0x938db8c_9f82c8cb5_8d3f3ef4_fd250036, + 0xa48d26a_712753d2f_de5abd03_a85cabf4, + ) + ); + // "012" + // bf6aaaab7c143ca12ae448c69fb72bb4cf1b29154b9086a927a0a91ae334cdf7 + assert_eq!( + f( + SHA256, + [[0, 0], [0, 0x30313200_00000000_00000000_00000000]], + 24 + ), + u256x::be( + 0xbf6aaaa_b7c143ca1_2ae448c6_9fb72bb4, + 0xcf1b291_54b9086a9_27a0a91a_e334cdf7, + ) + ); + // "0123" + // 1be2e452b46d7a0d9656bbb1f768e8248eba1b75baed65f5d99eafa948899a6a + assert_eq!( + f( + SHA256, + [[0, 0], [0, 0x30313233_00000000_00000000_00000000]], + 32 + ), + u256x::be( + 0x1be2e45_2b46d7a0d_9656bbb1_f768e824, + 0x8eba1b7_5baed65f5_d99eafa9_48899a6a, + ) + ); + // "01234" + // c565fe03ca9b6242e01dfddefe9bba3d98b270e19cd02fd85ceaf75e2b25bf12 + assert_eq!( + f( + SHA256, + [[0, 0], [0, 0x30313233_34000000_00000000_00000000]], + 40 + ), + u256x::be( + 0xc565fe0_3ca9b6242_e01dfdde_fe9bba3d, + 0x98b270e_19cd02fd8_5ceaf75e_2b25bf12, + ) + ); + // "01234567" + // 924592b9b103f14f833faafb67f480691f01988aa457c0061769f58cd47311bc + assert_eq!( + f( + SHA256, + [[0, 0], [0, 0x30313233_34353637_00000000_00000000]], + 64 + ), + u256x::be( + 0x924592b_9b103f14f_833faafb_67f48069, + 0x1f01988_aa457c006_1769f58c_d47311bc, + ) + ); + // "0123456789ABCDEF" + // 2125b2c332b1113aae9bfc5e9f7e3b4c91d828cb942c2df1eeb02502eccae9e9 + assert_eq!( + f( + SHA256, + [[0, 0], [0, 0x30313233_34353637_38394142_43444546]], + 128 + ), + u256x::be( + 0x2125b2c_332b1113a_ae9bfc5e_9f7e3b4c, + 0x91d828c_b942c2df1_eeb02502_eccae9e9, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF" + // cd6c1f7d1dc6717d6371d2647910ca71ba3bf0b611083d322466b8843b4285b6 + assert_eq!( + f( + SHA256, + [ + [0, 0], + [ + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546 + ] + ], + 256 + ), + u256x::be( + 0xcd6c1f7_d1dc6717d_6371d264_7910ca71, + 0xba3bf0b_611083d32_2466b884_3b4285b6, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" + // a2d094d2605d33b19a0c75f3aa4b5dc1eeacba0068799289f2a0960e755e5cd2 + assert_eq!( + f( + SHA256, + [ + [0, 0x30313233_34353637_38394142_43444546], + [ + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546 + ], + ], + 384 + ), + u256x::be( + 0xa2d094d_2605d33b1_9a0c75f3_aa4b5dc1, + 0xeeacba0_068799289_f2a0960e_755e5cd2, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFa" + // 4ae493e89db8ecc2b52f49cd0c0bb6f3d68793733e84347005ba8fb59fc653bf + assert_eq!( + f( + SHA256, + [ + [ + 0x61000000_00000000_00000000_00000000, + 0x30313233_34353637_38394142_43444546, + ], + [ + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546 + ] + ], + 392 + ), + u256x::be( + 0x4ae493e_89db8ecc2_b52f49cd_0c0bb6f3, + 0xd687937_33e843470_05ba8fb5_9fc653bf, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFab" + // 3cba8dc04c46e175ade60333067a631cf5d5804610e8679800014ffbbb00b877 + assert_eq!( + f( + SHA256, + u512x::be( + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x61620000_00000000_00000000_00000000, + ), + 400 + ), + u256x::be( + 0x3cba8dc_04c46e175_ade60333_067a631c, + 0xf5d5804_610e86798_00014ffb_bb00b877, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFabc" + // eaaf843057d4b3f741b4a19262164fb61adb0daf2c8196981696b414c7ad09fe + assert_eq!( + f( + SHA256, + u512x::be( + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x61626300_00000000_00000000_00000000, + ), + 408 + ), + u256x::be( + 0xeaaf843_057d4b3f7_41b4a192_62164fb6, + 0x1adb0da_f2c819698_1696b414_c7ad09fe, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFabcd" + // 5622518d2df953c3e8506bd6d5c3a20f10d409afbb005ebec1b7ab15280dcfd6 + assert_eq!( + f( + SHA256, + u512x::be( + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x61626364_00000000_00000000_00000000, + ), + 416 + ), + u256x::be( + 0x5622518_d2df953c3_e8506bd6_d5c3a20f, + 0x10d409a_fbb005ebe_c1b7ab15_280dcfd6, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFabcde" + // e8c72b140c1b515b08e76dab90cd7b9483760a93767e30028a9fe94011d34c55 + assert_eq!( + f( + SHA256, + u512x::be( + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x61626364_65000000_00000000_00000000, + ), + 424 + ), + u256x::be( + 0xe8c72b1_40c1b515b_08e76dab_90cd7b94, + 0x83760a9_3767e3002_8a9fe940_11d34c55, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFabcdef" + // 1979ef41c2bd877b3150fe6aba05372d9e1de8cd06a45918d4a75604b66026f3 + assert_eq!( + f( + SHA256, + u512x::be( + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x61626364_65660000_00000000_00000000, + ), + 432 + ), + u256x::be( + 0x1979ef4_1c2bd877b_3150fe6a_ba05372d, + 0x9e1de8c_d06a45918_d4a75604_b66026f3, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFabcdefg" + // c74c91051470cf0f398242e4832498da50b6fa22a9786a2924fe732c865616cc + assert_eq!( + f( + SHA256, + u512x::be( + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x61626364_65666700_00000000_00000000, + ), + 440 + ), + u256x::be( + 0xc74c910_51470cf0f_398242e4_832498da, + 0x50b6fa2_2a9786a29_24fe732c_865616cc, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFabcdefgh" + // a32254a85e25153b03f9cd3ec2cfd74af080b3f5dd8bc2e73bbf9702923f5b5e + assert_eq!( + f( + SHA256, + u512x::be( + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x61626364_65666768_00000000_00000000, + ), + 448 + ), + u256x::be( + 0xa32254a_85e25153b_03f9cd3e_c2cfd74a, + 0xf080b3f_5dd8bc2e7_3bbf9702_923f5b5e, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFabcdefghi" + // d4f8ba39f2bbf210e284c3df1af0f4a842d56f8d59a13f9ccbc762d97487ff0a + assert_eq!( + f( + SHA256, + u512x::be( + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x61626364_65666768_69000000_00000000, + ), + 456 + ), + u256x::be( + 0xd4f8ba3_9f2bbf210_e284c3df_1af0f4a8, + 0x42d56f8_d59a13f9c_cbc762d9_7487ff0a, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFabcdefghij" + // 87c074cbd39fe6f70f6cdee1652a0b5c87d443838c3110907c8fddb9ea45aa30 + assert_eq!( + f( + SHA256, + u512x::be( + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x61626364_65666768_696A0000_00000000, + ), + 464 + ), + u256x::be( + 0x87c074c_bd39fe6f7_0f6cdee1_652a0b5c, + 0x87d4438_38c311090_7c8fddb9_ea45aa30, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFabcdefghijk" + // af2bd64ee47c437502fee60861488b70de1fb8a7f614c0c496974e2308703058 + assert_eq!( + f( + SHA256, + u512x::be( + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x61626364_65666768_696A6B00_00000000, + ), + 472 + ), + u256x::be( + 0xaf2bd64_ee47c4375_02fee608_61488b70, + 0xde1fb8a_7f614c0c4_96974e23_08703058, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFabcdefghijkl" + // d470d6fbea20d21cecc15d3818442654885027e12f40568377524f512144c539 + assert_eq!( + f( + SHA256, + u512x::be( + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x61626364_65666768_696A6B6C_00000000, + ), + 480 + ), + u256x::be( + 0xd470d6f_bea20d21c_ecc15d38_18442654, + 0x885027e_12f405683_77524f51_2144c539, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFabcdefghijklm" + // be0cdfaca8524e0de0725dcca4b0c78785bf82c7861903cb5e006128e4408265 + assert_eq!( + f( + SHA256, + u512x::be( + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x61626364_65666768_696A6B6C_6D000000, + ), + 488 + ), + u256x::be( + 0xbe0cdfa_ca8524e0d_e0725dcc_a4b0c787, + 0x85bf82c_7861903cb_5e006128_e4408265, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFabcdefghijklmn" + // 245f1842136a9c656b54104352e734206d59546227dc233cdecb8ad70c2a944d + assert_eq!( + f( + SHA256, + u512x::be( + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x61626364_65666768_696A6B6C_6D6E0000, + ), + 496 + ), + u256x::be( + 0x245f184_2136a9c65_6b541043_52e73420, + 0x6d59546_227dc233c_decb8ad7_0c2a944d, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFabcdefghijklmno" + // 02198db64650f032738690585554acd9e9030a85b55d0ec46be30cb2ac05992c + assert_eq!( + f( + SHA256, + u512x::be( + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x61626364_65666768_696A6B6C_6D6E6F00, + ), + 504 + ), + u256x::be( + 0x02198db_64650f032_73869058_5554acd9, + 0xe9030a8_5b55d0ec4_6be30cb2_ac05992c, + ) + ); + // "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEFabcdefghijklmnop" + // ef8e2b127f816dee68cd063810d0976ade5e30b2ea59c47de2ac2c3a7b8f9471 + assert_eq!( + f( + SHA256, + u512x::be( + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x30313233_34353637_38394142_43444546, + 0x61626364_65666768_696A6B6C_6D6E6F70, + ), + 512 + ), + u256x::be( + 0xef8e2b1_27f816dee_68cd0638_10d0976a, + 0xde5e30b_2ea59c47d_e2ac2c3a_7b8f9471, + ) + ); + } +} diff --git a/blockset-lib/src/sha2/mod.rs b/blockset-lib/src/sha2/mod.rs index 69c78ee3..ee622b80 100644 --- a/blockset-lib/src/sha2/mod.rs +++ b/blockset-lib/src/sha2/mod.rs @@ -3,5 +3,9 @@ mod sigma32; mod w_round; // pub +pub mod be_chunk; pub mod compress; +pub mod hash_state; pub mod sha224; +pub mod sha256; +pub mod state; diff --git a/blockset-lib/src/sha2/round.rs b/blockset-lib/src/sha2/round.rs index 5af51e89..fd95254c 100644 --- a/blockset-lib/src/sha2/round.rs +++ b/blockset-lib/src/sha2/round.rs @@ -1,8 +1,8 @@ use crate::uint::{ - u128::{get_u32, to_u32x4}, - u256::U256, - u32::{add, add5}, - u512::{get_u128, new, U512}, + u128x::{get_u32, to_u32x4}, + u256x::U256, + u32x::{wadd, wadd5}, + u512x::{get_u128, le, U512}, }; use super::sigma32::{BIG0, BIG1}; @@ -11,7 +11,7 @@ const fn round([s0, s1]: U256, i: usize, w: u128, k: u128) -> U256 { let (a, e) = { let t1 = { let [e, f, g, h] = to_u32x4(s1); - add5( + wadd5( h, BIG1.get(e), (e & f) ^ (!e & g), @@ -20,8 +20,8 @@ const fn round([s0, s1]: U256, i: usize, w: u128, k: u128) -> U256 { ) }; let [a, b, c, d] = to_u32x4(s0); - let t2 = add(BIG0.get(a), (a & b) ^ (a & c) ^ (b & c)); - (add(t1, t2), add(d, t1)) + let t2 = wadd(BIG0.get(a), (a & b) ^ (a & c) ^ (b & c)); + (wadd(t1, t2), wadd(d, t1)) }; [a as u128 | (s0 << 32), e as u128 | (s1 << 32)] } @@ -36,25 +36,25 @@ const fn round4(mut x: U256, i: usize, w: &U512, k: &U512) -> U256 { } pub const K: [U512; 4] = [ - new( + le( 0xe9b5dba5_b5c0fbcf_71374491_428a2f98, 0xab1c5ed5_923f82a4_59f111f1_3956c25b, 0x550c7dc3_243185be_12835b01_d807aa98, 0xc19bf174_9bdc06a7_80deb1fe_72be5d74, ), - new( + le( 0x240ca1cc_0fc19dc6_efbe4786_e49b69c1, 0x76f988da_5cb0a9dc_4a7484aa_2de92c6f, 0xbf597fc7_b00327c8_a831c66d_983e5152, 0x14292967_06ca6351_d5a79147_c6e00bf3, ), - new( + le( 0x53380d13_4d2c6dfc_2e1b2138_27b70a85, 0x92722c85_81c2c92e_766a0abb_650a7354, 0xc76c51a3_c24b8b70_a81a664b_a2bfe8a1, 0x106aa070_f40e3585_d6990624_d192e819, ), - new( + le( 0x34b0bcb5_2748774c_1e376c08_19a4c116, 0x682e6ff3_5b9cca4f_4ed8aa4a_391c0cb3, 0x8cc70208_84c87814_78a5636f_748f82ee, diff --git a/blockset-lib/src/sha2/sha224.rs b/blockset-lib/src/sha2/sha224.rs index 24a1532a..eca36ab8 100644 --- a/blockset-lib/src/sha2/sha224.rs +++ b/blockset-lib/src/sha2/sha224.rs @@ -1,4 +1,4 @@ -use crate::uint::u256::U256; +use crate::uint::u256x::U256; pub const SHA224: U256 = [ 0xf70e5939_3070dd17_367cd507_c1059ed8, @@ -9,7 +9,7 @@ pub const SHA224: U256 = [ mod test { use wasm_bindgen_test::wasm_bindgen_test; - use crate::{sha2::compress::compress, uint::u256::U256}; + use crate::{sha2::compress::compress, uint::u256x::U256}; use super::SHA224; diff --git a/blockset-lib/src/sha2/sha256.rs b/blockset-lib/src/sha2/sha256.rs new file mode 100644 index 00000000..63e2f7aa --- /dev/null +++ b/blockset-lib/src/sha2/sha256.rs @@ -0,0 +1,26 @@ +use crate::uint::u256x::U256; + +pub const SHA256: U256 = [ + 0xa54ff53a_3c6ef372_bb67ae85_6a09e667, + 0x5be0cd19_1f83d9ab_9b05688c_510e527f, +]; + +#[cfg(test)] +mod tests { + use wasm_bindgen_test::wasm_bindgen_test; + + use crate::sha2::{compress::compress, sha256::SHA256}; + + #[wasm_bindgen_test] + #[test] + fn runtime_test() { + let x = compress(SHA256, [[0x8000_0000, 0], [0, 0]]); + assert_eq!( + x, + [ + 0x996fb924_9afbf4c8_98fc1c14_e3b0c442, + 0x7852b855_a495991b_649b934c_27ae41e4, + ] + ); + } +} diff --git a/blockset-lib/src/sha2/state.rs b/blockset-lib/src/sha2/state.rs new file mode 100644 index 00000000..560cf906 --- /dev/null +++ b/blockset-lib/src/sha2/state.rs @@ -0,0 +1,75 @@ +use crate::uint::u256x::U256; + +use super::{be_chunk::BeChunk, hash_state::HashState}; + +pub struct State { + state: HashState, + rest: BeChunk, +} + +impl State { + pub const fn from_hash_state(state: HashState) -> Self { + Self { + state, + rest: BeChunk::default(), + } + } + pub const fn new(hash: U256) -> Self { + Self::from_hash_state(HashState::new(hash)) + } + pub const fn end(self) -> U256 { + self.state.end(self.rest) + } + pub const fn push(mut self, rest: &BeChunk) -> Self { + let (v, rest) = self.rest.chain(rest); + if let Some(v) = v { + self.state = self.state.push(v); + } + self.rest = rest; + self + } + pub const fn push_array(mut self, v: &[u8]) -> Self { + let len = v.len(); + let mut i = 0; + loop { + if i == len { + return self; + } + self = self.push(&BeChunk::u8(v[i])); + i += 1; + } + } +} + +#[cfg(test)] +mod tests { + use wasm_bindgen_test::wasm_bindgen_test; + + use crate::sha2::sha256::SHA256; + + use super::State; + + #[test] + #[wasm_bindgen_test] + fn test() { + let f = |v, a1, a0| { + let h = State::new(SHA256).push_array(v).end(); + assert_eq!(h, [a0, a1]); + }; + f( + b"", + 0xe3b0c442_98fc1c14_9afbf4c8_996fb924, + 0x27ae41e4_649b934c_a495991b_7852b855, + ); + f( + b"0", + 0x5feceb6_6ffc86f38_d952786c_6d696c79, + 0xc2dbc23_9dd4e91b4_6729d73a_27fb57e9, + ); + f( + b"The quick brown fox jumps over the lazy dog", + 0xd7a8fbb3_07d78094_69ca9abc_b0082e4f, + 0x8d5651e4_6d3cdb76_2d02d0bf_37c9e592, + ); + } +} diff --git a/blockset-lib/src/sha2/w_round.rs b/blockset-lib/src/sha2/w_round.rs index f29ccea3..4f5f7474 100644 --- a/blockset-lib/src/sha2/w_round.rs +++ b/blockset-lib/src/sha2/w_round.rs @@ -1,14 +1,14 @@ use crate::uint::{ - u128::{from_u32x4, to_u32x4}, - u32::add4, - u512::{get_u128, U512}, + u128x::{from_u32x4, to_u32x4}, + u32x::wadd4, + u512x::{get_u128, U512}, }; use super::sigma32::{SMALL0, SMALL1}; #[inline(always)] const fn w_round(w0: u32, w1: u32, w9: u32, we: u32) -> u32 { - add4(SMALL1.get(we), w9, SMALL0.get(w1), w0) + wadd4(SMALL1.get(we), w9, SMALL0.get(w1), w0) } #[inline(always)] diff --git a/blockset-lib/src/uint/mod.rs b/blockset-lib/src/uint/mod.rs index be045ec8..ad28d8da 100644 --- a/blockset-lib/src/uint/mod.rs +++ b/blockset-lib/src/uint/mod.rs @@ -1,6 +1,6 @@ -pub mod u128; -pub mod u224; -pub mod u256; -pub mod u32; -pub mod u512; -pub mod u64; +pub mod u128x; +pub mod u224x; +pub mod u256x; +pub mod u32x; +pub mod u512x; +pub mod u64x; diff --git a/blockset-lib/src/uint/u128.rs b/blockset-lib/src/uint/u128x.rs similarity index 50% rename from blockset-lib/src/uint/u128.rs rename to blockset-lib/src/uint/u128x.rs index a12fccd9..26d11e6a 100644 --- a/blockset-lib/src/uint/u128.rs +++ b/blockset-lib/src/uint/u128x.rs @@ -1,4 +1,6 @@ -use crate::uint::u32::add; +use crate::uint::u32x::wadd; + +use super::u256x::{self, U256}; /// Converts a 128-bit unsigned integer (`u128`) into a vector of four 32-bit unsigned integers (`[u32; 4]`). /// This function essentially 'splits' the 128-bit value into a vector of four components, each representing a @@ -30,10 +32,21 @@ pub const fn get_u32(v: u128, i: usize) -> u32 { /// Performs element-wise addition of two 128-bit vectors (`u128`), represented as arrays of 32-bit components. /// Each component of the vectors is added using `add`, which handles overflow by wrapping around. #[inline(always)] -pub const fn u32x4_add(a: u128, b: u128) -> u128 { +pub const fn u32x4_wadd(a: u128, b: u128) -> u128 { let [a0, a1, a2, a3] = to_u32x4(a); let [b0, b1, b2, b3] = to_u32x4(b); - from_u32x4([add(a0, b0), add(a1, b1), add(a2, b2), add(a3, b3)]) + from_u32x4([wadd(a0, b0), wadd(a1, b1), wadd(a2, b2), wadd(a3, b3)]) +} + +#[inline(always)] +pub const fn swap64(a: u128) -> u128 { + (a >> 64) | (a << 64) +} + +#[inline(always)] +pub const fn swap32(a: u128) -> u128 { + const MASK: u128 = 0x0000_0000_FFFF_FFFF_0000_0000_FFFF_FFFF; + swap64(((a >> 32) & MASK) | ((a & MASK) << 32)) } #[inline(always)] @@ -45,11 +58,62 @@ pub const fn shl(u: u128, i: i32) -> u128 { } } +#[inline(always)] +const fn lo_hi(a: u128) -> [u128; 2] { + [a as u64 as u128, a >> 64] +} + +/// Multiplication with overflow. +/// a0 * b0 + (a1 * b0 + a0 * b1) << 64 + (a1 * b1) << 128 +pub const fn mul(a: u128, b: u128) -> U256 { + let [a0, a1] = lo_hi(a); + let [b0, b1] = lo_hi(b); + let r0 = [a0 * b0, 0]; + let r1 = { + let (x, o) = (a1 * b0).overflowing_add(a0 * b1); + [x << 64, (x >> 64) | ((o as u128) << 64)] + }; + let r2 = [0, a1 * b1]; + u256x::wadd(u256x::wadd(r0, r1), r2) +} + +pub const fn set_bit(a: u128, i: u32) -> u128 { + a | (1 << i) +} + #[cfg(test)] mod test { use wasm_bindgen_test::wasm_bindgen_test; - use crate::uint::u128::shl; + use crate::uint::u128x::{mul, shl}; + + #[wasm_bindgen_test] + #[test] + fn test_mul() { + assert_eq!(mul(0, 0), [0, 0]); + assert_eq!(mul(0, 1), [0, 0]); + assert_eq!(mul(1, 1), [1, 0]); + assert_eq!(mul(2, 3), [6, 0]); + assert_eq!(mul(6, 7), [42, 0]); + assert_eq!(mul(42, 43), [1_806, 0]); + assert_eq!(mul(1_806, 1_807), [3_263_442, 0]); + assert_eq!(mul(3_263_442, 3_263_443), [10_650_056_950_806, 0]); + assert_eq!( + mul(10_650_056_950_806, 10_650_056_950_807), + [113_423_713_055_421_844_361_000_442, 0] + ); + assert_eq!( + mul( + 113_423_713_055_421_844_361_000_442, + 113_423_713_055_421_844_361_000_443 + ), + [ + 337_284_947_070_536_250_008_747_159_125_413_113_374, + 37_806_656_864_672 + ] + ); + assert_eq!(mul(u128::MAX, u128::MAX), [1, u128::MAX - 1]); + } fn check_shl(a: u128, b: i32, expected: u128, f: fn(u128, i32) -> u128) { assert_eq!(f(a, b), expected); diff --git a/blockset-lib/src/uint/u224.rs b/blockset-lib/src/uint/u224x.rs similarity index 100% rename from blockset-lib/src/uint/u224.rs rename to blockset-lib/src/uint/u224x.rs diff --git a/blockset-lib/src/uint/u256.rs b/blockset-lib/src/uint/u256.rs deleted file mode 100644 index de0dc1c8..00000000 --- a/blockset-lib/src/uint/u256.rs +++ /dev/null @@ -1,105 +0,0 @@ -use crate::uint::u128::{shl as shl128, to_u32x4, u32x4_add}; - -pub type U256 = [u128; 2]; - -#[inline(always)] -pub const fn u32x8_add(&[a0, a1]: &U256, &[b0, b1]: &U256) -> U256 { - [u32x4_add(a0, b0), u32x4_add(a1, b1)] -} - -#[inline(always)] -pub const fn shl(&[lo, hi]: &U256, i: usize) -> U256 { - [ - shl128(lo, i as i32), - shl128(hi, i as i32) | shl128(lo, i as i32 - 128), - ] -} - -#[inline(always)] -pub const fn bitor(&[a0, a1]: &U256, &[b0, b1]: &U256) -> U256 { - [a0 | b0, a1 | b1] -} - -// Don't use `<` for `U256` because it's not LE comparison. -#[inline(always)] -pub const fn less(&[a0, a1]: &U256, &[b0, b1]: &U256) -> bool { - if a1 == b1 { - a0 < b0 - } else { - a1 < b1 - } -} - -#[inline(always)] -pub const fn great(a: &U256, b: &U256) -> bool { - less(b, a) -} - -pub const fn to_u224(&[a0, a1]: &U256) -> Option<[u32; 7]> { - let [a10, a11, a12, a13] = to_u32x4(a1); - if a13 != 0xFFFF_FFFF { - return None; - } - let [a00, a01, a02, a03] = to_u32x4(a0); - Some([a00, a01, a02, a03, a10, a11, a12]) -} - -#[cfg(test)] -mod test { - use wasm_bindgen_test::wasm_bindgen_test; - - use super::{shl, U256}; - - const X: U256 = [ - 0x100F_0E0D_0C0B_0A09_0807_0605_0403_0201, - 0x201F_1E1D_1C1B_1A19_1817_1615_1413_1211, - ]; - - #[wasm_bindgen_test] - #[test] - fn shl_test() { - assert_eq!(shl(&X, 0), X); - assert_eq!( - shl(&X, 1), - // 123456789ABCDEF - // 000000011111111 - // 2468ACE02468ACE - [ - 0x201E_1C1A_1816_1412_100E_0C0A_0806_0402, - 0x403E_3C3A_3836_3432_302E_2C2A_2826_2422, - ] - ); - assert_eq!( - shl(&X, 4), - [ - 0x00F_0E0D_0C0B_0A09_0807_0605_0403_0201_0, - 0x01F_1E1D_1C1B_1A19_1817_1615_1413_1211_1, - ] - ); - assert_eq!( - shl(&X, 124), - [ - 0x1000_0000_0000_0000_0000_0000_0000_0000, - 0x1100F_0E0D_0C0B_0A09_0807_0605_0403_020, - ] - ); - assert_eq!( - shl(&X, 127), - [ - 0x8000_0000_0000_0000_0000_0000_0000_0000, - 0x8807_8706_8605_8504_8403_8302_8201_8100, - ] - ); - } - - #[wasm_bindgen_test] - #[test] - fn shl_test2() { - assert_eq!(shl(&X, 128), [0, 0x100F_0E0D_0C0B_0A09_0807_0605_0403_0201]); - assert_eq!(shl(&X, 129), [0, 0x201E_1C1A_1816_1412_100E_0C0A_0806_0402]); - assert_eq!(shl(&X, 136), [0, 0x0F_0E0D_0C0B_0A09_0807_0605_0403_020100]); - assert_eq!(shl(&X, 248), [0, 0x0100_0000_0000_0000_0000_0000_0000_0000]); - assert_eq!(shl(&X, 255), [0, 0x8000_0000_0000_0000_0000_0000_0000_0000]); - assert_eq!(shl(&X, 256), [0; 2]); - } -} diff --git a/blockset-lib/src/uint/u256x.rs b/blockset-lib/src/uint/u256x.rs new file mode 100644 index 00000000..2cbe48b1 --- /dev/null +++ b/blockset-lib/src/uint/u256x.rs @@ -0,0 +1,404 @@ +use crate::uint::u128x::{to_u32x4, u32x4_wadd}; + +use super::{ + u128x, + u512x::{self, U512}, +}; + +pub type U256 = [u128; 2]; + +#[inline(always)] +pub const fn be(a1: u128, a0: u128) -> U256 { + [a0, a1] +} + +#[inline(always)] +pub const fn u32x8_wadd(&[a0, a1]: &U256, &[b0, b1]: &U256) -> U256 { + [u32x4_wadd(a0, b0), u32x4_wadd(a1, b1)] +} + +pub const fn shl(&[lo, hi]: &U256, i: i32) -> U256 { + let loi = u128x::shl(lo, i); + let hii = u128x::shl(hi, i); + if i < 0 { + [loi | u128x::shl(hi, i + 128), hii] + } else { + [loi, hii | u128x::shl(lo, i - 128)] + } +} + +#[inline(always)] +pub const fn shr(a: &U256, i: i32) -> U256 { + shl(a, -i) +} + +#[inline(always)] +pub const fn bitor(&[a0, a1]: &U256, &[b0, b1]: &U256) -> U256 { + [a0 | b0, a1 | b1] +} + +#[inline(always)] +pub const fn bitxor(&[a0, a1]: &U256, &[b0, b1]: &U256) -> U256 { + [a0 ^ b0, a1 ^ b1] +} + +#[inline(always)] +pub const fn bitand(&[a0, a1]: &U256, &[b0, b1]: &U256) -> U256 { + [a0 & b0, a1 & b1] +} + +#[inline(always)] +pub const fn eq(&[a0, a1]: &U256, &[b0, b1]: &U256) -> bool { + a0 == b0 && a1 == b1 +} + +// Don't use `<` for `U256` because it's not LE comparison. +#[inline(always)] +pub const fn less(&[a0, a1]: &U256, &[b0, b1]: &U256) -> bool { + if a1 == b1 { + a0 < b0 + } else { + a1 < b1 + } +} + +#[inline(always)] +pub const fn great(a: &U256, b: &U256) -> bool { + less(b, a) +} + +pub const fn to_u224(&[a0, a1]: &U256) -> Option<[u32; 7]> { + let [a10, a11, a12, a13] = to_u32x4(a1); + if a13 != 0xFFFF_FFFF { + return None; + } + let [a00, a01, a02, a03] = to_u32x4(a0); + Some([a00, a01, a02, a03, a10, a11, a12]) +} + +pub const fn oadd([a0, a1]: U256, [b0, b1]: U256) -> (U256, bool) { + let (r0, c) = a0.overflowing_add(b0); + let (a1c, c0) = a1.overflowing_add(c as u128); + let (r1, c1) = a1c.overflowing_add(b1); + assert!(!(c0 & c1)); + ([r0, r1], c0 | c1) +} + +#[inline(always)] +pub const fn wadd(a: U256, b: U256) -> U256 { + oadd(a, b).0 +} + +pub const fn osub([a0, a1]: U256, [b0, b1]: U256) -> (U256, bool) { + let (r0, c) = a0.overflowing_sub(b0); + let (a1c, c0) = a1.overflowing_sub(c as u128); + let (r1, c1) = a1c.overflowing_sub(b1); + assert!(!(c0 & c1)); + ([r0, r1], c0 | c1) +} + +#[inline(always)] +pub const fn wsub(a: U256, b: U256) -> U256 { + osub(a, b).0 +} + +#[inline(always)] +pub const fn from_u128(a: u128) -> U256 { + [a, 0] +} + +#[inline(always)] +pub const fn from_bool(a: bool) -> U256 { + from_u128(a as u128) +} + +pub const _0: U256 = from_u128(0); +pub const _1: U256 = from_u128(1); +pub const MAX: U256 = [u128::MAX, u128::MAX]; + +pub const fn leading_zeros([a0, a1]: U256) -> u32 { + match a1.leading_zeros() { + 128 => 128 + a0.leading_zeros(), + x => x, + } +} + +pub const fn mul([a0, a1]: U256, [b0, b1]: U256) -> U512 { + let r0 = [u128x::mul(a0, b0), _0]; + let r1 = { + let ([x0, x1], c) = oadd(u128x::mul(a1, b0), u128x::mul(a0, b1)); + [[0, x0], [x1, c as u128]] + }; + let r2 = [_0, u128x::mul(a1, b1)]; + u512x::wadd(u512x::wadd(r0, r1), r2) +} + +pub const fn div_rem(mut a: U256, b: U256) -> [U256; 2] { + assert!(!eq(&b, &_0)); + let b_offset = leading_zeros(b); + let mut q = _0; + loop { + let a_offset = leading_zeros(a); + if a_offset > b_offset { + break; + } + let mut offset = b_offset - a_offset; + let mut bx = shl(&b, offset as i32); + if less(&a, &bx) { + if offset == 0 { + break; + } + offset -= 1; + bx = shl(&b, offset as i32); + } + a = wsub(a, bx); + q = set_bit(q, offset); + } + [q, a] +} + +pub const fn set_bit([a0, a1]: U256, i: u32) -> U256 { + if i < 128 { + [u128x::set_bit(a0, i), a1] + } else { + [a0, u128x::set_bit(a1, i - 128)] + } +} + +pub const fn get_bit([a0, a1]: U256, i: u32) -> bool { + let x = if i < 128 { a0 >> i } else { a1 >> (i - 128) }; + (x & 1) != 0 +} + +pub const fn swap32([a0, a1]: U256) -> U256 { + [u128x::swap32(a1), u128x::swap32(a0)] +} + +#[cfg(test)] +mod test { + use wasm_bindgen_test::wasm_bindgen_test; + + use crate::uint::u256x::{div_rem, from_u128, leading_zeros, mul, osub, wadd, _0}; + + use super::{shl, U256}; + + #[test] + #[wasm_bindgen_test] + fn test_literal() { + const fn be(a: &[u8; 32]) -> U256 { + [0, 0] + } + be(b"01234567890123456789012345678901"); + } + + #[test] + #[wasm_bindgen_test] + fn test_leading_zeros() { + assert_eq!(leading_zeros(_0), 256); + assert_eq!(leading_zeros(from_u128(1)), 255); + assert_eq!(leading_zeros([0, 1]), 127); + assert_eq!(leading_zeros([0, 1 << 127]), 0); + } + + #[test] + #[wasm_bindgen_test] + fn test_mul() { + assert_eq!(mul([1, 2], [3, 4]), [[3, 10], [8, 0]]); + assert_eq!(mul([3, 10], [8, 1]), [[24, 83], [10, 0]]); + assert_eq!(mul([24, 83], [10, 1]), [[240, 854], [83, 0]]); + assert_eq!(mul([240, 854], [83, 1]), [[19_920, 71_122], [854, 0]]); + assert_eq!( + mul([19_920, 71_122], [854, 1]), + [[17_011_680, 60_758_108], [71_122, 0]] + ); + assert_eq!( + mul([17_011_680, 60_758_108], [71_122, 1]), + [[1_209_904_704_960, 4_321_255_168_856], [60_758_108, 0]] + ); + assert_eq!( + mul([1_209_904_704_960, 4_321_255_168_856], [60_758_108, 1]), + [ + [73_511_520_733_667_815_680, 262_551_289_454_815_789_408,], + [4_321_255_168_856, 0] + ] + ); + assert_eq!( + mul( + [73_511_520_733_667_815_680, 262_551_289_454_815_789_408,], + [4_321_255_168_856, 1] + ), + [ + [ + 317_662_038_940_827_061_850_491_084_462_080, + 1_134_551_116_646_504_047_761_375_644_092_928 + ], + [262_551_289_454_815_789_408, 0] + ] + ); + assert_eq!( + mul( + [ + 317_662_038_940_827_061_850_491_084_462_080, + 1_134_551_116_646_504_047_761_375_644_092_928 + ], + [262_551_289_454_815_789_408, 1] + ), + [ + [ + 5_371_062_831_933_040_625_063_535_520_489_472_000, + 301_007_701_468_498_206_271_604_815_495_196_034_759 + ], + [1_134_551_116_646_504_048_636_759_994_223_388, 0] + ] + ); + assert_eq!( + mul( + [ + 5_371_062_831_933_040_625_063_535_520_489_472_000, + 301_007_701_468_498_206_271_604_815_495_196_034_759 + ], + [1_134_551_116_646_504_048_636_759_994_223_388, 1] + ), + [ + [ + 60_254_530_117_125_738_360_543_440_126_070_226_944, + 59_522_230_753_612_122_465_001_774_279_148_829_737 + ], + [301_008_705_072_143_350_860_077_419_384_325_574_160, 0] + ] + ); + assert_eq!( + mul( + [ + 60_254_530_117_125_738_360_543_440_126_070_226_944, + 59_522_230_753_612_122_465_001_774_279_148_829_737 + ], + [301_008_705_072_143_350_860_077_419_384_325_574_160, 1] + ), + [ + [ + 9_558_019_270_435_171_971_601_763_919_821_012_992, + 95_671_844_947_069_765_262_691_166_418_826_886_560 + ], + [112_174_708_060_239_291_868_245_242_763_017_670_789, 0] + ] + ); + assert_eq!( + mul( + [ + 9_558_019_270_435_171_971_601_763_919_821_012_992, + 95_671_844_947_069_765_262_691_166_418_826_886_560 + ], + [ + 112_174_708_060_239_291_868_245_242_763_017_670_789, + 301_008_705_072_143_350_860_077_419_384_325_574_160 + ] + ), + [ + [ + 124_520_065_329_156_727_566_853_617_294_340_259_840, + 85_178_092_293_149_724_732_751_559_228_462_861_597 + ], + [ + 143_515_348_539_978_426_307_971_476_180_421_425_837, + 84_629_886_702_508_214_136_893_118_870_499_585_684 + ] + ] + ); + assert_eq!( + mul([u128::MAX, u128::MAX], [u128::MAX, u128::MAX]), + [[1, 0], [u128::MAX - 1, u128::MAX]] + ); + } + + #[test] + #[wasm_bindgen_test] + fn test_add() { + assert_eq!(wadd([0, 0], [0, 0]), [0, 0]); + assert_eq!(wadd([0, 1], [0, 2]), [0, 3]); + assert_eq!(wadd([1, 2], [3, 4]), [4, 6]); + assert_eq!(wadd([u128::MAX, 3], [4, 5]), [3, 9]); + } + + #[test] + #[wasm_bindgen_test] + fn test_osub() { + assert_eq!(osub([0, 0], [0, 0]), ([0, 0], false)); + assert_eq!(osub([0, 1], [0, 2]), ([0, u128::MAX], true)); + assert_eq!(osub([0, 2], [0, 1]), ([0, 1], false)); + assert_eq!(osub([1, 2], [3, 4]), ([u128::MAX - 1, u128::MAX - 2], true)); + assert_eq!(osub([3, 4], [1, 2]), ([2, 2], false)); + assert_eq!( + osub([u128::MAX, 3], [4, 5]), + ([u128::MAX - 4, u128::MAX - 1], true) + ); + assert_eq!(osub([4, 5], [u128::MAX, 3]), ([5, 1], false)); + } + + const X: U256 = [ + 0x100F_0E0D_0C0B_0A09_0807_0605_0403_0201, + 0x201F_1E1D_1C1B_1A19_1817_1615_1413_1211, + ]; + + #[wasm_bindgen_test] + #[test] + fn shl_test() { + assert_eq!(shl(&X, 0), X); + assert_eq!( + shl(&X, 1), + // 123456789ABCDEF + // 000000011111111 + // 2468ACE02468ACE + [ + 0x201E_1C1A_1816_1412_100E_0C0A_0806_0402, + 0x403E_3C3A_3836_3432_302E_2C2A_2826_2422, + ] + ); + assert_eq!( + shl(&X, 4), + [ + 0x00F_0E0D_0C0B_0A09_0807_0605_0403_0201_0, + 0x01F_1E1D_1C1B_1A19_1817_1615_1413_1211_1, + ] + ); + assert_eq!( + shl(&X, 124), + [ + 0x1000_0000_0000_0000_0000_0000_0000_0000, + 0x1100F_0E0D_0C0B_0A09_0807_0605_0403_020, + ] + ); + assert_eq!( + shl(&X, 127), + [ + 0x8000_0000_0000_0000_0000_0000_0000_0000, + 0x8807_8706_8605_8504_8403_8302_8201_8100, + ] + ); + } + + #[wasm_bindgen_test] + #[test] + fn shl_test2() { + assert_eq!(shl(&X, 128), [0, 0x100F_0E0D_0C0B_0A09_0807_0605_0403_0201]); + assert_eq!(shl(&X, 129), [0, 0x201E_1C1A_1816_1412_100E_0C0A_0806_0402]); + assert_eq!(shl(&X, 136), [0, 0x0F_0E0D_0C0B_0A09_0807_0605_0403_020100]); + assert_eq!(shl(&X, 248), [0, 0x0100_0000_0000_0000_0000_0000_0000_0000]); + assert_eq!(shl(&X, 255), [0, 0x8000_0000_0000_0000_0000_0000_0000_0000]); + assert_eq!(shl(&X, 256), [0; 2]); + } + + #[test] + #[wasm_bindgen_test] + fn test_div_rem() { + // assert_eq!(div_rem([[0, 0], [0, 0]], [[0, 0], [0, 0]]), [[[0, 0], [0, 0]], [[0, 0], [0, 0]]]); + assert_eq!(div_rem([0, 0], [1, 0]), [[0, 0], [0, 0]]); + assert_eq!(div_rem([1, 2], [1, 0]), [[1, 2], [0, 0]]); + assert_eq!(div_rem([1, 2], [2, 0]), [[0, 1], [1, 0]]); + assert_eq!( + div_rem([1, 2], [3, 0]), + [[226854911280625642308916404954512140971, 0], [0, 0]] + ); + assert_eq!(div_rem([1, 2], [3, 4]), [[0, 0], [1, 2]]); + } +} diff --git a/blockset-lib/src/uint/u32.rs b/blockset-lib/src/uint/u32x.rs similarity index 56% rename from blockset-lib/src/uint/u32.rs rename to blockset-lib/src/uint/u32x.rs index fb0916e1..f9448f05 100644 --- a/blockset-lib/src/uint/u32.rs +++ b/blockset-lib/src/uint/u32x.rs @@ -2,32 +2,32 @@ /// /// This function safely handles integer overflow without panicking. #[inline(always)] -pub const fn add(a: u32, b: u32) -> u32 { - a.overflowing_add(b).0 +pub const fn wadd(a: u32, b: u32) -> u32 { + a.wrapping_add(b) } /// Adds three `u32` integers and returns the sum. /// /// This function safely handles integer overflow without panicking. #[inline(always)] -pub const fn add3(a: u32, b: u32, c: u32) -> u32 { - add(add(a, b), c) +pub const fn wadd3(a: u32, b: u32, c: u32) -> u32 { + wadd(wadd(a, b), c) } /// Adds four `u32` integers and returns the sum. /// /// This function safely handles integer overflow without panicking. #[inline(always)] -pub const fn add4(a: u32, b: u32, c: u32, d: u32) -> u32 { - add(add(a, b), add(c, d)) +pub const fn wadd4(a: u32, b: u32, c: u32, d: u32) -> u32 { + wadd(wadd(a, b), wadd(c, d)) } /// Adds five `u32` integers and returns the sum. /// /// This function safely handles integer overflow without panicking. #[inline(always)] -pub const fn add5(a: u32, b: u32, c: u32, d: u32, e: u32) -> u32 { - add3(add3(a, b, c), d, e) +pub const fn wadd5(a: u32, b: u32, c: u32, d: u32, e: u32) -> u32 { + wadd3(wadd3(a, b, c), d, e) } /// Converts a `u32` integer into an array of four `u8` bytes (little-endian). @@ -40,20 +40,27 @@ pub const fn to_u8x4(a: u32) -> [u8; 4] { /// Constructs a `u32` integer from an array of four `u8` bytes (little-endian). #[inline(always)] pub const fn from_u8x4(a: &[u8; 4]) -> u32 { - a[0] as u32 | ((a[1] as u32) << 8) | ((a[2] as u32) << 16) | ((a[3] as u32) << 24) + // a[0] as u32 | ((a[1] as u32) << 8) | ((a[2] as u32) << 16) | ((a[3] as u32) << 24) + u32::from_le_bytes(*a) +} + +#[inline(always)] +pub const fn div_rem(a: u32, b: u32) -> [u32; 2] { + [a / b, a % b] } #[cfg(test)] mod test { + use super::{to_u8x4, wadd, wadd3, wadd4, wadd5}; use wasm_bindgen_test::wasm_bindgen_test; #[wasm_bindgen_test] #[test] fn test() { - assert_eq!(super::add(1, 2), 3); - assert_eq!(super::add3(1, 2, 3), 6); - assert_eq!(super::add4(1, 2, 3, 4), 10); - assert_eq!(super::add5(1, 2, 3, 4, 5), 15); - assert_eq!(super::to_u8x4(0x12345678), [0x78, 0x56, 0x34, 0x12]); + assert_eq!(wadd(1, 2), 3); + assert_eq!(wadd3(1, 2, 3), 6); + assert_eq!(wadd4(1, 2, 3, 4), 10); + assert_eq!(wadd5(1, 2, 3, 4, 5), 15); + assert_eq!(to_u8x4(0x12345678), [0x78, 0x56, 0x34, 0x12]); } } diff --git a/blockset-lib/src/uint/u512.rs b/blockset-lib/src/uint/u512.rs deleted file mode 100644 index 2cb83d69..00000000 --- a/blockset-lib/src/uint/u512.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::uint::u256::U256; - -pub type U512 = [U256; 2]; - -pub const fn new(a: u128, b: u128, c: u128, d: u128) -> U512 { - [[a, b], [c, d]] -} - -pub const fn get_u128(a: &U512, i: usize) -> u128 { - a[(i >> 1) & 1][i & 1] -} - -#[cfg(test)] -mod test { - use wasm_bindgen_test::wasm_bindgen_test; - - use super::{new, U512}; - - //#[inline(never)] - fn create2( - a: u128, - b: u128, - c: u128, - d: u128, - i: u128, - f: fn(u128, u128, u128, u128) -> U512, - ) -> U512 { - f(a * i, b + i, c / (i + 1), d - 1) - } - - //#[inline(never)] - fn create(a: u128, b: u128, c: u128, d: u128) { - for i in 0..10 { - let x = create2(a, b, c, d, i, new); - assert_eq!(x[0][0], a * i); - assert_eq!(x[0][1], b + i); - assert_eq!(x[1][0], c / (i + 1)); - assert_eq!(x[1][1], d - 1); - let xa = new(a, b + i, c / (i + 1), d - 1); - assert_eq!(xa[0][0], a); - assert_eq!(xa[0][1], b + i); - assert_eq!(xa[1][0], c / (i + 1)); - assert_eq!(xa[1][1], d - 1); - } - } - - #[wasm_bindgen_test] - #[test] - fn test() { - create(1, 2, 3, 4); - } -} diff --git a/blockset-lib/src/uint/u512x.rs b/blockset-lib/src/uint/u512x.rs new file mode 100644 index 00000000..e01af821 --- /dev/null +++ b/blockset-lib/src/uint/u512x.rs @@ -0,0 +1,363 @@ +use crate::uint::u256x::U256; + +use super::u256x; + +pub type U512 = [U256; 2]; + +#[inline(always)] +pub const fn be(a3: u128, a2: u128, a1: u128, a0: u128) -> U512 { + [[a0, a1], [a2, a3]] +} + +#[inline(always)] +pub const fn le(a: u128, b: u128, c: u128, d: u128) -> U512 { + [[a, b], [c, d]] +} + +pub const fn get_u128(a: &U512, i: usize) -> u128 { + a[(i >> 1) & 1][i & 1] +} + +pub const fn wadd([a0, a1]: U512, [b0, b1]: U512) -> U512 { + let (r0, c) = u256x::oadd(a0, b0); + [r0, u256x::wadd(u256x::wadd(a1, b1), u256x::from_bool(c))] +} + +pub const fn wsub([a0, a1]: U512, [b0, b1]: U512) -> U512 { + let (r0, c) = u256x::osub(a0, b0); + [r0, u256x::wsub(u256x::wsub(a1, b1), u256x::from_bool(c))] +} + +pub const fn less([a0, a1]: U512, [b0, b1]: U512) -> bool { + if u256x::eq(&a1, &b1) { + u256x::less(&a0, &b0) + } else { + u256x::less(&a1, &b1) + } +} + +pub const _0: U512 = [u256x::_0, u256x::_0]; + +pub const fn leading_zeros([a0, a1]: U512) -> u32 { + match u256x::leading_zeros(a1) { + 256 => 256 + u256x::leading_zeros(a0), + x => x, + } +} + +pub const fn shl([lo, hi]: &U512, i: i32) -> U512 { + let loi = u256x::shl(lo, i); + let hii = u256x::shl(hi, i); + if i < 0 { + [u256x::bitor(&loi, &u256x::shl(hi, i + 256)), hii] + } else { + [loi, u256x::bitor(&hii, &u256x::shl(lo, i - 256))] + } +} + +pub const fn set_bit([a0, a1]: U512, i: u32) -> U512 { + if i < 256 { + [u256x::set_bit(a0, i), a1] + } else { + [a0, u256x::set_bit(a1, i - 256)] + } +} + +pub const fn eq([a0, a1]: &U512, [b0, b1]: &U512) -> bool { + u256x::eq(a0, b0) && u256x::eq(a1, b1) +} + +pub const fn div_rem(mut a: U512, b: U512) -> [U512; 2] { + assert!(!eq(&b, &_0)); + let b_offset = leading_zeros(b); + let mut q = _0; + loop { + let a_offset = leading_zeros(a); + if a_offset > b_offset { + break; + } + let mut offset = b_offset - a_offset; + let mut bx = shl(&b, offset as i32); + if less(a, bx) { + if offset == 0 { + break; + } + offset -= 1; + bx = shl(&b, offset as i32); + } + a = wsub(a, bx); + q = set_bit(q, offset); + } + [q, a] +} + +pub const fn swap32([a0, a1]: U512) -> U512 { + [u256x::swap32(a1), u256x::swap32(a0)] +} + +#[inline(always)] +pub const fn bitor(&[a0, a1]: &U512, &[b0, b1]: &U512) -> U512 { + [u256x::bitor(&a0, &b0), u256x::bitor(&a1, &b1)] +} + +#[inline(always)] +pub const fn bitxor(&[a0, a1]: &U512, &[b0, b1]: &U512) -> U512 { + [u256x::bitxor(&a0, &b0), u256x::bitxor(&a1, &b1)] +} + +#[cfg(test)] +mod test { + use wasm_bindgen_test::wasm_bindgen_test; + + use crate::uint::u512x::{div_rem, le, wadd}; + + use super::U512; + + #[test] + #[wasm_bindgen_test] + fn test_div_rem() { + // assert_eq!(div_rem([[0, 0], [0, 0]], [[0, 0], [0, 0]]), [[[0, 0], [0, 0]], [[0, 0], [0, 0]]]); + assert_eq!( + div_rem([[0, 0], [0, 0]], [[1, 0], [0, 0]]), + [[[0, 0], [0, 0]], [[0, 0], [0, 0]]] + ); + assert_eq!( + div_rem([[1, 2], [3, 4]], [[1, 0], [0, 0]]), + [[[1, 2], [3, 4]], [[0, 0], [0, 0]]] + ); + assert_eq!( + div_rem([[1, 2], [3, 4]], [[2, 0], [0, 0]]), + [ + [[0, 170141183460469231731687303715884105729], [1, 2]], + [[1, 0], [0, 0]] + ] + ); + assert_eq!( + div_rem([[1, 2], [3, 4]], [[3, 0], [0, 0]]), + [ + [ + [0, 113427455640312821154458202477256070486], + [113427455640312821154458202477256070486, 1] + ], + [[1, 0], [0, 0]] + ] + ); + assert_eq!( + div_rem([[1, 2], [3, 4]], [[3, 5], [0, 0]]), + [ + [ + [ + 313059777567263386386304638837226754539, + 272225893536750770770699685945414569164 + ], + [0, 0] + ], + [[81667768061025231231209905783624370752, 4], [0, 0]] + ] + ); + assert_eq!( + div_rem([[1, 2], [3, 4]], [[3, 5], [7, 0]]), + [ + [[194447066811964836264785489961010406546, 0], [0, 0]], + [ + [ + 97223533405982418132392744980505203275, + 48611766702991209066196372490252601638 + ], + [2, 0] + ] + ] + ); + assert_eq!( + div_rem([[1, 2], [3, 4]], [[3, 5], [7, 1]]), + [ + [[3, 0], [0, 0]], + [ + [ + 340282366920938463463374607431768211448, + 340282366920938463463374607431768211442, + ], + [340282366920938463463374607431768211437, 0] + ] + ] + ); + assert_eq!( + div_rem([[1, 2], [3, 4]], [[3, 5], [7, 11]]), + [[[0, 0], [0, 0]], [[1, 2,], [3, 4]]] + ); + } + + #[test] + #[wasm_bindgen_test] + fn test_add() { + assert_eq!(wadd([[0, 0], [0, 0]], [[0, 0], [0, 0]]), [[0, 0], [0, 0]]); + assert_eq!(wadd([[1, 2], [3, 4]], [[5, 6], [7, 8]]), [[6, 8], [10, 12]]); + assert_eq!( + wadd([[11, 22], [33, 44]], [[55, 66], [77, 88]]), + [[66, 88], [110, 132]] + ); + assert_eq!( + wadd([[1111, 2222], [3333, 4444]], [[5555, 6666], [7777, 8888]]), + [[6666, 8888], [11110, 13332]] + ); + assert_eq!( + wadd( + [[1111_1111, 2222_2222], [3333_3333, 4444_4444]], + [[5555_5555, 6666_6666], [7777_7777, 8888_8888]] + ), + [[6666_6666, 8888_8888], [1_1111_1110, 1_3333_3332]] + ); + assert_eq!( + wadd( + [ + [1111_1111_1111_1111, 2222_2222_2222_2222], + [3333_3333_3333_3333, 4444_4444_4444_4444] + ], + [ + [5555_5555_5555_5555, 6666_6666_6666_6666], + [7777_7777_7777_7777, 8888_8888_8888_8888] + ] + ), + [ + [6666_6666_6666_6666, 8888_8888_8888_8888], + [1_1111_1111_1111_1110, 1_3333_3333_3333_3332] + ] + ); + assert_eq!( + wadd( + [ + [ + 1111_1111_1111_1111_1111_1111_1111_1111, + 2222_2222_2222_2222_2222_2222_2222_2222 + ], + [ + 3333_3333_3333_3333_3333_3333_3333_3333, + 4444_4444_4444_4444_4444_4444_4444_4444 + ] + ], + [ + [ + 5555_5555_5555_5555_5555_5555_5555_5555, + 6666_6666_6666_6666_6666_6666_6666_6666 + ], + [ + 7777_7777_7777_7777_7777_7777_7777_7777, + 8888_8888_8888_8888_8888_8888_8888_8888 + ] + ] + ), + [ + [ + 6666_6666_6666_6666_6666_6666_6666_6666, + 8888_8888_8888_8888_8888_8888_8888_8888 + ], + [ + 1_1111_1111_1111_1111_1111_1111_1111_1110, + 1_3333_3333_3333_3333_3333_3333_3333_3332 + ] + ] + ); + assert_eq!( + wadd( + [ + [ + 11_1111_1111_1111_1111_1111_1111_1111_1111_1111, + 22_2222_2222_2222_2222_2222_2222_2222_2222_2222, + ], + [ + 33_3333_3333_3333_3333_3333_3333_3333_3333_3333, + 44_4444_4444_4444_4444_4444_4444_4444_4444_4444, + ] + ], + [ + [ + 55_5555_5555_5555_5555_5555_5555_5555_5555_5555, + 66_6666_6666_6666_6666_6666_6666_6666_6666_6666, + ], + [ + 77_7777_7777_7777_7777_7777_7777_7777_7777_7777, + 88_8888_8888_8888_8888_8888_8888_8888_8888_8888, + ] + ] + ), + [ + [ + 66_6666_6666_6666_6666_6666_6666_6666_6666_6666, + 88_8888_8888_8888_8888_8888_8888_8888_8888_8888 + ], + [ + 111_1111_1111_1111_1111_1111_1111_1111_1111_1110, + 133_3333_3333_3333_3333_3333_3333_3333_3333_3332 + ] + ] + ); + assert_eq!( + wadd( + [ + [ + 111_1111_1111_1111_1111_1111_1111_1111_1111_1111, + 222_2222_2222_2222_2222_2222_2222_2222_2222_2222, + ], + [ + 333_3333_3333_3333_3333_3333_3333_3333_3333_3333, + 244_4444_4444_4444_4444_4444_4444_4444_4444_4444, + ] + ], + [ + [ + 255_5555_5555_5555_5555_5555_5555_5555_5555_5555, + 266_6666_6666_6666_6666_6666_6666_6666_6666_6666, + ], + [ + 277_7777_7777_7777_7777_7777_7777_7777_7777_7777, + 288_8888_8888_8888_8888_8888_8888_8888_8888_8888, + ] + ] + ), + [ + [ + 26_3842_9974_5728_2032_0329_2059_2348_9845_5210, + 148_6065_2196_7950_4254_2551_4281_4571_2067_7433, + ], + [ + 270_8287_4419_0172_6476_4773_6503_6793_4289_9655, + 193_0509_6641_2394_8698_6995_8725_9015_6512_1877, + ] + ] + ); + } + + //#[inline(never)] + fn create2( + a: u128, + b: u128, + c: u128, + d: u128, + i: u128, + f: fn(u128, u128, u128, u128) -> U512, + ) -> U512 { + f(a * i, b + i, c / (i + 1), d - 1) + } + + //#[inline(never)] + fn create(a: u128, b: u128, c: u128, d: u128) { + for i in 0..10 { + let x = create2(a, b, c, d, i, le); + assert_eq!(x[0][0], a * i); + assert_eq!(x[0][1], b + i); + assert_eq!(x[1][0], c / (i + 1)); + assert_eq!(x[1][1], d - 1); + let xa = le(a, b + i, c / (i + 1), d - 1); + assert_eq!(xa[0][0], a); + assert_eq!(xa[0][1], b + i); + assert_eq!(xa[1][0], c / (i + 1)); + assert_eq!(xa[1][1], d - 1); + } + } + + #[wasm_bindgen_test] + #[test] + fn test() { + create(1, 2, 3, 4); + } +} diff --git a/blockset-lib/src/uint/u64.rs b/blockset-lib/src/uint/u64.rs deleted file mode 100644 index 23c959d7..00000000 --- a/blockset-lib/src/uint/u64.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[inline(always)] -pub const fn div_rem(a: u64, b: u64) -> (u64, u64) { - (a / b, a % b) -} diff --git a/blockset-lib/src/uint/u64x.rs b/blockset-lib/src/uint/u64x.rs new file mode 100644 index 00000000..2d4f60d1 --- /dev/null +++ b/blockset-lib/src/uint/u64x.rs @@ -0,0 +1,28 @@ +#[inline(always)] +pub const fn div_rem(a: u64, b: u64) -> (u64, u64) { + (a / b, a % b) +} + +#[inline(always)] +pub const fn swap32(a: u64) -> u64 { + (a >> 32) | (a << 32) +} + +#[cfg(test)] +mod test { + use wasm_bindgen_test::wasm_bindgen_test; + + use super::swap32; + + const fn byte_swap(mut a: u64) -> u64 { + a = swap32(a); + a = ((a >> 16) & 0x0000FFFF_0000FFFF) | ((a & 0x0000FFFF_0000FFFF) << 16); + ((a >> 8) & 0x00FF00FF_00FF00FF) | ((a & 0x00FF00FF_00FF00FF) << 8) + } + + #[test] + #[wasm_bindgen_test] + fn test() { + assert_eq!(byte_swap(0x01234567_89ABCDEF), 0xEFCDAB89_67452301); + } +} diff --git a/notes/block-types.md b/notes/block-types.md index 4557640a..1fc66f25 100644 --- a/notes/block-types.md +++ b/notes/block-types.md @@ -13,10 +13,10 @@ type SignatureTag = { } type Signature = { publicKey: string - dataAddress: Address - timeStamp?: Time - signature: string + message: Address | AddressAndTimeStamp + signature: string } +type AddressAndTimeStamp = string ``` ## Revision (Version) @@ -38,7 +38,7 @@ type DirectoryTag = { directory: Directory } type Directory = { - [path in Path]: DataAddress + [path in Path]: DataAddress } // a path using `/` as a separator. type Path = string diff --git a/tests/u512x/div_rem.js b/tests/u512x/div_rem.js new file mode 100644 index 00000000..f6993074 --- /dev/null +++ b/tests/u512x/div_rem.js @@ -0,0 +1,20 @@ +const merge = ([a0, a1, a2, a3]) => a0 | (a1 << 128n) | (a2 << 256n) | (a3 << 384n) + +const mask = (1n << 128n) - 1n + +const split = a => [a & mask, (a >> 128n) & mask, (a >> 256n) & mask, (a >> 384n) & mask] + +const div_rem = (a, b) => { + const an = merge(a) + const bn = merge(b) + console.log('div_rem: ', split(an / bn), ', ', split(an % bn)) +} + +div_rem([0n, 0n, 0n, 0n], [1n, 0n, 0n, 0n]) +div_rem([1n, 2n, 3n, 4n], [1n, 0n, 0n, 0n]) +div_rem([1n, 2n, 3n, 4n], [2n, 0n, 0n, 0n]) +div_rem([1n, 2n, 3n, 4n], [3n, 0n, 0n, 0n]) +div_rem([1n, 2n, 3n, 4n], [3n, 5n, 0n, 0n]) +div_rem([1n, 2n, 3n, 4n], [3n, 5n, 7n, 0n]) +div_rem([1n, 2n, 3n, 4n], [3n, 5n, 7n, 1n]) +div_rem([1n, 2n, 3n, 4n], [3n, 5n, 7n, 11n])