Skip to content

Commit

Permalink
add SM3 digest support
Browse files Browse the repository at this point in the history
  • Loading branch information
notch1p committed Jul 30, 2024
1 parent 3f9222e commit f45a732
Show file tree
Hide file tree
Showing 4 changed files with 336 additions and 34 deletions.
16 changes: 3 additions & 13 deletions crypto/md5.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fn MD5Context::compute(self : MD5Context) -> Bytes {
md5_update(self, bytes_slice)
let mut j = 0
for i = 0; i < 14; i = i + 1 {
input[i] = u8_to_u32(self.buffer, j)
input[i] = u8_to_u32le(self.buffer, i=j)
j += 4
}
md5_transform(self.state, input)
Expand Down Expand Up @@ -89,12 +89,6 @@ fn MD5Context::i(x : UInt, y : UInt, z : UInt) -> UInt {
y ^ (x | z.lnot())
}

fn u8_to_u32(x : Bytes, i : Int) -> UInt {
x[i].to_int().to_uint() | x[i + 1].to_int().to_uint().lsl(8) | x[i + 2].to_int().to_uint().lsl(
16,
) | x[i + 3].to_int().to_uint().lsl(24)
}

fn md5_update(ctx : MD5Context, data : Bytes) -> Unit {
let input = FixedArray::make(16, 0U)
let mut idx = (ctx.count[0].lsr(3) & 0x3f).to_int()
Expand All @@ -111,7 +105,7 @@ fn md5_update(ctx : MD5Context, data : Bytes) -> Unit {
if idx == 0x40 {
let mut j = 0
for k = 0; k < 16; k = k + 1 {
input[k] = u8_to_u32(ctx.buffer, j)
input[k] = u8_to_u32le(ctx.buffer, i=j)
j += 4
}
md5_transform(ctx.state, input)
Expand All @@ -120,10 +114,6 @@ fn md5_update(ctx : MD5Context, data : Bytes) -> Unit {
}
}

fn rotate_left_u(x : UInt, n : Int) -> UInt {
x.lsl(n).lor(x.lsr(32 - n))
}

fn md5_transform(state : FixedArray[UInt], input : FixedArray[UInt]) -> Unit {
let a = Ref::new(state[0])
let b = Ref::new(state[1])
Expand All @@ -139,7 +129,7 @@ fn md5_transform(state : FixedArray[UInt], input : FixedArray[UInt]) -> Unit {
s : Int, // calculate w/ floor(2^32 * |sin(i + 1)|) for i in 0..63
ac : UInt
) -> Unit {
state_a.val = state_b.val + rotate_left_u(
state_a.val = state_b.val + urotate_left(
state_a.val + op(state_b.val, state_c.val, state_d.val) + input[k] + ac,
s,
)
Expand Down
216 changes: 216 additions & 0 deletions crypto/sm3.mbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
// Copyright 2024 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// A SM3 implementation based on
// - [GM/T 0004-2012] https://www.oscca.gov.cn/sca/xxgk/2010-12/17/1002389/files/302a3ada057c4a73830536d03e683110.pdf
// SM3 is as secure as SHA256, providing similar performance. https://doi.org/10.3390/electronics8091033

priv struct SM3Context {
reg : FixedArray[UInt] // register A B C D E F G H. i.e. digest
mut len : Int64
mut msg : Array[Byte]
}

fn SM3Context::make() -> SM3Context {
{
reg: [
0x7380166F, 0x4914B2B9, 0x172442D7, 0xDA8A0600, 0xA96F30BC, 0x163138AA, 0xE38DEE4D,
0xB0FB0E4E,
],
len: 0,
msg: [],
}
}

let t : FixedArray[UInt] = [ // pre calculated
0x79cc4519, 0xf3988a32, 0xe7311465, 0xce6228cb, 0x9cc45197, 0x3988a32f, 0x7311465e,
0xe6228cbc, 0xcc451979, 0x988a32f3, 0x311465e7, 0x6228cbce, 0xc451979c, 0x88a32f39,
0x11465e73, 0x228cbce6, 0x9d8a7a87, 0x3b14f50f, 0x7629ea1e, 0xec53d43c, 0xd8a7a879,
0xb14f50f3, 0x629ea1e7, 0xc53d43ce, 0x8a7a879d, 0x14f50f3b, 0x29ea1e76, 0x53d43cec,
0xa7a879d8, 0x4f50f3b1, 0x9ea1e762, 0x3d43cec5, 0x7a879d8a, 0xf50f3b14, 0xea1e7629,
0xd43cec53, 0xa879d8a7, 0x50f3b14f, 0xa1e7629e, 0x43cec53d, 0x879d8a7a, 0x0f3b14f5,
0x1e7629ea, 0x3cec53d4, 0x79d8a7a8, 0xf3b14f50, 0xe7629ea1, 0xcec53d43, 0x9d8a7a87,
0x3b14f50f, 0x7629ea1e, 0xec53d43c, 0xd8a7a879, 0xb14f50f3, 0x629ea1e7, 0xc53d43ce,
0x8a7a879d, 0x14f50f3b, 0x29ea1e76, 0x53d43cec, 0xa7a879d8, 0x4f50f3b1, 0x9ea1e762,
0x3d43cec5,
]

// auxiliary functions
// FF_j = | X xor Y xor Z where 0 <= j <= 15
// | (X and Y) or (X and Z) or (Y and Z) where 16 <= j <=63
fn SM3Context::ff_0(x : UInt, y : UInt, z : UInt) -> UInt {
x ^ y ^ z
}

fn SM3Context::ff_1(x : UInt, y : UInt, z : UInt) -> UInt {
(x & y) | (x & z) | (y & z)
}

// GG_j = | X xor Y xor Z where 0 <= j <= 15
// | (X and Y) or (~X and Z) where 16 <= j <= 63
fn SM3Context::gg_0(x : UInt, y : UInt, z : UInt) -> UInt {
x ^ y ^ z
}

fn SM3Context::gg_1(x : UInt, y : UInt, z : UInt) -> UInt {
((y ^ z) & x) ^ z // equivalent of (x & y) | (x.lnot() & z), but faster
}

// P_0 = X xor (X <<< 9) xor (X <<< 17)
// P_1 = X xor (X <<< 15) xor (X <<< 23)
fn SM3Context::p_0(x : UInt) -> UInt {
x ^ urotate_left(x, 9) ^ urotate_left(x, 17)
}

fn SM3Context::p_1(x : UInt) -> UInt {
x ^ urotate_left(x, 15) ^ urotate_left(x, 23)
}

fn pad(self : SM3Context) -> Array[Byte] {
self.msg.push(b'\x80')
while self.msg.length() % 64 != 56 {
self.msg.push(b'\x00')
}
self.msg.push(self.len.lsr(56).to_byte())
self.msg.push(self.len.lsr(48).to_byte())
self.msg.push(self.len.lsr(40).to_byte())
self.msg.push(self.len.lsr(32).to_byte())
self.msg.push(self.len.lsr(24).to_byte())
self.msg.push(self.len.lsr(16).to_byte())
self.msg.push(self.len.lsr(8).to_byte())
self.msg.push(self.len.lsr(0).to_byte())
return self.msg
}

fn update(self : SM3Context, data : Ref[Array[Byte]]) -> Array[UInt] {
let w_0 = FixedArray::make(68, 0U)
let w_1 = FixedArray::make(64, 0U)
let mut a = self.reg[0]
let mut b = self.reg[1]
let mut c = self.reg[2]
let mut d = self.reg[3]
let mut e = self.reg[4]
let mut f = self.reg[5]
let mut g = self.reg[6]
let mut h = self.reg[7]
while data.val.length() >= 64 {
for index = 0; index < 16; index = index + 1 {
w_0[index] = arr_u8_to_u32be(data.val, i=4 * index)
}
for index = 16; index < 68; index = index + 1 {
w_0[index] = p_1(
w_0[index - 16] ^ w_0[index - 9] ^ urotate_left(w_0[index - 3], 15),
) ^ urotate_left(w_0[index - 13], 7) ^ w_0[index - 6]
}
for index = 0; index < 64; index = index + 1 {
w_1[index] = w_0[index] ^ w_0[index + 4]
}
let mut a1 = a
let mut b1 = b
let mut c1 = c
let mut d1 = d
let mut e1 = e
let mut f1 = f
let mut g1 = g
let mut h1 = h
for index = 0; index < 16; index = index + 1 {
let ss_1 = urotate_left(urotate_left(a1, 12) + e1 + t[index], 7)
let ss_2 = ss_1 ^ urotate_left(a1, 12)
let tt_1 = ff_0(a1, b1, c1) + d1 + ss_2 + w_1[index]
let tt_2 = gg_0(e1, f1, g1) + h1 + ss_1 + w_0[index]
d1 = c1
c1 = urotate_left(b1, 9)
b1 = a1
a1 = tt_1
h1 = g1
g1 = urotate_left(f1, 19)
f1 = e1
e1 = p_0(tt_2)
}
for index = 16; index < 64; index = index + 1 {
let ss_1 = urotate_left(urotate_left(a1, 12) + e1 + t[index], 7)
let ss_2 = ss_1 ^ urotate_left(a1, 12)
let tt_1 = ff_1(a1, b1, c1) + d1 + ss_2 + w_1[index]
let tt_2 = gg_1(e1, f1, g1) + h1 + ss_1 + w_0[index]
d1 = c1
c1 = urotate_left(b1, 9)
b1 = a1
a1 = tt_1
h1 = g1
g1 = urotate_left(f1, 19)
f1 = e1
e1 = p_0(tt_2)
}
a = a ^ a1
b = b ^ b1
c = c ^ c1
d = d ^ d1
e = e ^ e1
f = f ^ f1
g = g ^ g1
h = h ^ h1
let t_arr = Array::make(data.val.length() - 64, b'\x00')
data.val.blit_to(t_arr, len=data.val.length() - 64, src_offset=64)
data.val = t_arr
}
let t_arr : Array[UInt] = [a, b, c, d, e, f, g, h]
for index = 0; index < 8; index = index + 1 {
self.reg[index] = t_arr[index]
}
return t_arr
}

fn compute(self : SM3Context, data : Array[Byte]) -> Unit {
self.len += data.length().to_int64() * 8L
let msg = self.msg + data
let block_num = msg.length() / 64
let len = msg.length() - block_num * 64
let _ = self.update(Ref::new(msg))
let new_msg = Array::make(len, b'\x00')
msg.blit_to(new_msg, ~len, src_offset=block_num * 64)
self.msg = new_msg
}

fn sum(self : SM3Context, ~data : Array[Byte] = []) -> Array[UInt] {
self.compute(data)
let msg = self.pad()
self.update(Ref::new(msg))
}

pub fn sm3(data : Bytes) -> Array[UInt] {
let bytes_data = bytes_to_byte_array(data)
let ctx = SM3Context::make()
let _ = ctx.compute(bytes_data)
ctx.sum()
}

test {
inspect!(
uints_to_hex_string(
sm3(
b"\x61\x62\x63", // abc in utf-8
),
),
content="66C7F0F462EEEDD9D1F2D46BDC10E4E24167C4875CF2F7A2297DA02B8F4BA8E0".to_lower(),
)
inspect!(
uints_to_hex_string(
sm3(
// abcd * 16 in utf-8
b"\x61\x62\x63\x64\x61\x62\x63\x64\x61\x62\x63\x64\x61\x62\x63\x64\x61\x62\x63\x64\x61\x62\x63\x64\x61\x62\x63\x64\x61\x62\x63\x64\x61\x62\x63\x64\x61\x62\x63\x64\x61\x62\x63\x64\x61\x62\x63\x64\x61\x62\x63\x64\x61\x62\x63\x64\x61\x62\x63\x64\x61\x62\x63\x64",
),
),
content="debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732",
)
}
53 changes: 53 additions & 0 deletions crypto/sm3_test.mbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2024 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

fn sm3test(s : String) -> String {
@crypto.uints_to_hex_string(@crypto.sm3(s.to_bytes()))
}

// testcases from GM/T 0004-2012
test "sm3_default" {
inspect!(
sm3test(""),
content="1AB21D8355CFA17F8E61194831E81A8F22BEC8C728FEFB747ED035EB5082AA2B".to_lower(),
)
inspect!(
sm3test("a"),
content="F4CCF00EF22555DD42706FD542022232A726A16062134C3C0FFE8FC7FA6CFE83".to_lower(),
)
inspect!(
sm3test("message digest"),
content="2F2FBA46CD6817494D1451BB2A9E9A82D8C60AA355E8E52889B5F0DD32492F7F".to_lower(),
)
inspect!(
sm3test("abcdefghijklmnopqrstuvwxyz"),
content="0470A95F1D8C774444B3D25B23FC064AC928909DDC1DD4766CA9D5E0C6210BA3".to_lower(),
)
inspect!(
sm3test("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"),
content="829B71880A21666E5A415D21F7F9FA10B5A8E7AD31F9E013FAC1B849820C538B".to_lower(),
)
inspect!(
sm3test(
"12345678901234567890123456789012345678901234567890123456789012345678901234567890",
),
content="E9CCF13E608C6A60E2677B932E3713E6BB146B3096AA20A3542EE694E7052474".to_lower(),
)
inspect!(
sm3test(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
),
content="2BC9534464A997501E6E319279DF0801EE45D9C53C02C7315DF19A4C3AF59AC4".to_lower(),
)
}
Loading

0 comments on commit f45a732

Please sign in to comment.