Skip to content

Commit c1d1efb

Browse files
authored
x.crypto.sm4: fix cbc API, make iv immutable (fix #25406) (#25416)
1 parent b59a83f commit c1d1efb

2 files changed

Lines changed: 126 additions & 152 deletions

File tree

‎vlib/x/crypto/sm4/sm4.v‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -399,8 +399,11 @@ pub fn (c &SM4Cipher) crypt_ecb(input []u8, mut output []u8) ! {
399399
// `iv` is a 16 bytes Initialization Vector.
400400
// `input` must be padded to multiple of 16 bytes.
401401
// `output` must be exactly the same length as `input`.
402+
// returns:
403+
// Updated IV ([]u8): The last ciphertext block for subsequent operations
404+
// Error: If input length is invalid, IV length incorrect, or buffer mismatch
402405
@[direct_array_access]
403-
pub fn (c &SM4Cipher) crypt_cbc(mut iv []u8, input []u8, mut output []u8) ! {
406+
pub fn (c &SM4Cipher) crypt_cbc(iv []u8, input []u8, mut output []u8) ![]u8 {
404407
mut idx := 0
405408
mut length := input.len
406409
if length & 0x0f != 0 || length == 0 {
@@ -416,17 +419,19 @@ pub fn (c &SM4Cipher) crypt_cbc(mut iv []u8, input []u8, mut output []u8) ! {
416419
// fixed size array, avoid alloc on heap
417420
mut input_16 := [16]u8{}
418421
mut output_16 := [16]u8{}
422+
mut iv_buf := [16]u8{}
423+
unsafe { vmemcpy(&iv_buf[0], &iv[0], 16) }
419424

420425
match c.mode {
421426
.sm4_encrypt {
422427
for length > 0 {
423428
// processing a 128bit block
424429
for i in 0 .. 16 {
425-
input_16[i] = input[idx + i] ^ iv[i] // convert to fixed size array
430+
input_16[i] = input[idx + i] ^ iv_buf[i] // convert to fixed size array
426431
}
427432
// use round keys encrypt the input_16
428433
one_round(c.rk, input_16, mut output_16)
429-
unsafe { vmemcpy(&iv[0], &output_16, 16) } // update iv with output
434+
unsafe { vmemcpy(&iv_buf[0], &output_16, 16) } // update iv_buf with output
430435
unsafe { vmemcpy(&output[idx], &output_16, 16) } // convert from fixed size array
431436
idx += 16
432437
length -= 16
@@ -439,12 +444,13 @@ pub fn (c &SM4Cipher) crypt_cbc(mut iv []u8, input []u8, mut output []u8) ! {
439444
// use round keys decrypt the input_16
440445
one_round(c.rk, input_16, mut output_16)
441446
for i in 0 .. 16 {
442-
output[idx + i] = output_16[i] ^ iv[i] // update output with iv
443-
iv[i] = input_16[i] // update iv with input
447+
output[idx + i] = output_16[i] ^ iv_buf[i] // update output with iv_buf
448+
iv_buf[i] = input_16[i] // update iv_buf with input
444449
}
445450
idx += 16
446451
length -= 16
447452
}
448453
}
449454
}
455+
return iv_buf[..]
450456
}

‎vlib/x/crypto/sm4/sm4_test.v‎

Lines changed: 115 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,152 +1,120 @@
11
// online tester: https://lzltool.cn/SM4
22
import x.crypto.sm4
33

4-
fn test_sm4_ecb() ! {
5-
mut key := [u8(0x01), 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76,
6-
0x54, 0x32, 0x10]
7-
mut input := [u8(0x01), 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76,
8-
0x54, 0x32, 0x10]
9-
mut output := []u8{len: 16}
10-
11-
// key = 0123456789abcdeffedcba9876543210
12-
// plaintext = 0123456789abcdeffedcba9876543210
13-
// ciphertext = 681edf34d206965e86b3e94f536e4246
14-
mut c1 := sm4.new_cipher(.sm4_encrypt, key)!
15-
c1.crypt_ecb(input, mut output)!
16-
assert output.hex() == '681edf34d206965e86b3e94f536e4246'
17-
mut d1 := sm4.new_cipher(.sm4_decrypt, key)!
18-
d1.crypt_ecb(output, mut output)!
19-
assert output.hex() == '0123456789abcdeffedcba9876543210'
20-
21-
// key = 0123456789abcdeffedcba9876543210
22-
// plaintext = 00112233445566778899aabbccddeeff
23-
// ciphertext = 09325c4853832dcb9337a5984f671b9a
24-
key = [u8(0x01), 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54,
25-
0x32, 0x10]
26-
input = [u8(0x00), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
27-
0xdd, 0xee, 0xff]
28-
output = []u8{len: 16}
29-
mut c2 := sm4.new_cipher(.sm4_encrypt, key)!
30-
c2.crypt_ecb(input, mut output)!
31-
assert output.hex() == '09325c4853832dcb9337a5984f671b9a'
32-
mut d2 := sm4.new_cipher(.sm4_decrypt, key)!
33-
d2.crypt_ecb(output, mut output)!
34-
assert output.hex() == '00112233445566778899aabbccddeeff'
35-
36-
// key = 456789abcdeffedcba98765432100123
37-
// plaintext = 2233445566778899aabbccddeeff0011
38-
// ciphertext = 58ab414d84fb3008b0bee987f97021e6
39-
key = [u8(0x45), 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10,
40-
0x01, 0x23]
41-
input = [u8(0x22), 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
42-
0xff, 0x00, 0x11]
43-
output = []u8{len: 16}
44-
mut c3 := sm4.new_cipher(.sm4_encrypt, key)!
45-
c3.crypt_ecb(input, mut output)!
46-
assert output.hex() == '58ab414d84fb3008b0bee987f97021e6'
47-
mut d3 := sm4.new_cipher(.sm4_decrypt, key)!
48-
d3.crypt_ecb(output, mut output)!
49-
assert output.hex() == '2233445566778899aabbccddeeff0011'
50-
51-
// key = 89abcdeffedcba987654321001234567
52-
// plaintext = 445566778899aabbccddeeff00112233445566778899aabbccddeeff00112233
53-
// ciphertext = 5937a929a2d9137216c72a28cd9cf6195937a929a2d9137216c72a28cd9cf619
54-
key = [u8(0x89), 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x01, 0x23,
55-
0x45, 0x67]
56-
input = [u8(0x44), 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00,
57-
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
58-
0x00, 0x11, 0x22, 0x33]
59-
output = []u8{len: 32}
60-
mut c4 := sm4.new_cipher(.sm4_encrypt, key)!
61-
c4.crypt_ecb(input, mut output)!
62-
assert output.hex() == '5937a929a2d9137216c72a28cd9cf6195937a929a2d9137216c72a28cd9cf619'
63-
mut d4 := sm4.new_cipher(.sm4_decrypt, key)!
64-
d4.crypt_ecb(output, mut output)!
65-
assert output.hex() == '445566778899aabbccddeeff00112233445566778899aabbccddeeff00112233'
4+
struct SM4Data {
5+
key string
6+
iv string
7+
input string
8+
output string
669
}
6710

68-
fn test_sm4_cbc() ! {
69-
mut key := [u8(0x01), 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76,
70-
0x54, 0x32, 0x10]
71-
mut iv := [u8(0x01), 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76,
72-
0x54, 0x32, 0x10]
73-
mut orig_iv := iv.clone()
74-
mut input := [u8(0x01), 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76,
75-
0x54, 0x32, 0x10]
76-
mut output := []u8{len: 16}
77-
78-
// key = 0123456789abcdeffedcba9876543210
79-
// iv = 0123456789abcdeffedcba9876543210
80-
// plaintext = 0123456789abcdeffedcba9876543210
81-
// ciphertext = 2677f46b09c122cc975533105bd4a22a
82-
mut c1 := sm4.new_cipher(.sm4_encrypt, key)!
83-
c1.crypt_cbc(mut iv, input, mut output)!
84-
assert output.hex() == '2677f46b09c122cc975533105bd4a22a'
85-
iv = orig_iv.clone()
86-
mut d1 := sm4.new_cipher(.sm4_decrypt, key)!
87-
d1.crypt_cbc(mut iv, output, mut output)!
88-
assert output.hex() == '0123456789abcdeffedcba9876543210'
89-
90-
// key = 0123456789abcdeffedcba9876543210
91-
// iv = 112233445566778899aabbccddeeff00
92-
// plaintext = 00112233445566778899aabbccddeeff
93-
// ciphertext = c3c0fffdcaac88ed63672b2b28a84e80
94-
key = [u8(0x01), 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54,
95-
0x32, 0x10]
96-
iv = [u8(0x11), 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
97-
0xff, 0x00]
98-
orig_iv = iv.clone()
99-
input = [u8(0x00), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
100-
0xdd, 0xee, 0xff]
101-
output = []u8{len: 16}
102-
mut c2 := sm4.new_cipher(.sm4_encrypt, key)!
103-
c2.crypt_cbc(mut iv, input, mut output)!
104-
assert output.hex() == 'c3c0fffdcaac88ed63672b2b28a84e80'
105-
iv = orig_iv.clone()
106-
mut d2 := sm4.new_cipher(.sm4_decrypt, key)!
107-
d2.crypt_cbc(mut iv, output, mut output)!
108-
assert output.hex() == '00112233445566778899aabbccddeeff'
109-
110-
// key = 456789abcdeffedcba98765432100123
111-
// iv = 33445566778899aabbccddeeff001122
112-
// plaintext = 2233445566778899aabbccddeeff0011
113-
// ciphertext = 63dfbfc357c2e040b529c692ec916c6b
114-
key = [u8(0x45), 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10,
115-
0x01, 0x23]
116-
iv = [u8(0x33), 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00,
117-
0x11, 0x22]
118-
orig_iv = iv.clone()
119-
input = [u8(0x22), 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
120-
0xff, 0x00, 0x11]
121-
output = []u8{len: 16}
122-
mut c3 := sm4.new_cipher(.sm4_encrypt, key)!
123-
c3.crypt_cbc(mut iv, input, mut output)!
124-
assert output.hex() == '63dfbfc357c2e040b529c692ec916c6b'
125-
iv = orig_iv.clone()
126-
mut d3 := sm4.new_cipher(.sm4_decrypt, key)!
127-
d3.crypt_cbc(mut iv, output, mut output)!
128-
assert output.hex() == '2233445566778899aabbccddeeff0011'
129-
130-
// key = 89abcdeffedcba987654321001234567
131-
// iv = 48bb57369d2020c66ded73415bfab211
132-
// plaintext = 445566778899aabbccddeeff00112233445566778899aabbccddeeff00112233
133-
// ciphertext = 2aac9d5427ddca8f2ef2eaa175012bcb66a1fd8df190a7db9f053f0ee40d79b5
134-
key = [u8(0x89), 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x01, 0x23,
135-
0x45, 0x67]
136-
iv = [u8(0x48), 0xbb, 0x57, 0x36, 0x9d, 0x20, 0x20, 0xc6, 0x6d, 0xed, 0x73, 0x41, 0x5b, 0xfa,
137-
0xb2, 0x11]
138-
orig_iv = iv.clone()
139-
input = [u8(0x44), 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00,
140-
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
141-
0x00, 0x11, 0x22, 0x33]
142-
output = []u8{len: 32}
143-
mut c4 := sm4.new_cipher(.sm4_encrypt, key)!
144-
c4.crypt_cbc(mut iv, input, mut output)!
145-
assert output.hex() == '2aac9d5427ddca8f2ef2eaa175012bcb66a1fd8df190a7db9f053f0ee40d79b5'
146-
iv = orig_iv.clone()
147-
mut d4 := sm4.new_cipher(.sm4_decrypt, key)!
148-
d4.crypt_cbc(mut iv, output, mut output)!
149-
assert output.hex() == '445566778899aabbccddeeff00112233445566778899aabbccddeeff00112233'
11+
const sm4data_ecb = [
12+
SM4Data{
13+
key: '0123456789abcdeffedcba9876543210'
14+
input: '0123456789abcdeffedcba9876543210'
15+
output: '681edf34d206965e86b3e94f536e4246'
16+
},
17+
SM4Data{
18+
key: '0123456789abcdeffedcba9876543210'
19+
input: '00112233445566778899aabbccddeeff'
20+
output: '09325c4853832dcb9337a5984f671b9a'
21+
},
22+
SM4Data{
23+
key: '456789abcdeffedcba98765432100123'
24+
input: '2233445566778899aabbccddeeff0011'
25+
output: '58ab414d84fb3008b0bee987f97021e6'
26+
},
27+
SM4Data{
28+
key: '89abcdeffedcba987654321001234567'
29+
input: '445566778899aabbccddeeff00112233445566778899aabbccddeeff00112233'
30+
output: '5937a929a2d9137216c72a28cd9cf6195937a929a2d9137216c72a28cd9cf619'
31+
},
32+
]
33+
34+
const sm4data_cbc = [
35+
SM4Data{
36+
key: '0123456789abcdeffedcba9876543210'
37+
iv: '0123456789abcdeffedcba9876543210'
38+
input: '0123456789abcdeffedcba9876543210'
39+
output: '2677f46b09c122cc975533105bd4a22a'
40+
},
41+
SM4Data{
42+
key: '0123456789abcdeffedcba9876543210'
43+
iv: '2677f46b09c122cc975533105bd4a22a'
44+
input: '00112233445566778899aabbccddeeff'
45+
output: '2e7b41173b42b90189bbcf4eeb5b5145'
46+
},
47+
SM4Data{
48+
key: '0123456789abcdeffedcba9876543210'
49+
iv: '2e7b41173b42b90189bbcf4eeb5b5145'
50+
input: '29860013b6b74c7503c524b62ecd52df'
51+
output: 'cd0cd15a934b3f47476292113d7f35bd'
52+
},
53+
]
54+
55+
fn test_sm4_ecb() {
56+
mut key := []u8{}
57+
mut input := []u8{}
58+
mut output := []u8{}
59+
60+
mut cipher := &sm4.SM4Cipher{}
61+
62+
for data in sm4data_ecb {
63+
key = '0x${data.key}'.u8_array()
64+
input = '0x${data.input}'.u8_array()
65+
output = [u8(0)].repeat(input.len) // output must be exactly the same length as input
66+
assert key.len == 16
67+
cipher = sm4.new_cipher(.sm4_encrypt, key)!
68+
cipher.crypt_ecb(input, mut output)!
69+
assert output.hex() == data.output
70+
71+
cipher = sm4.new_cipher(.sm4_decrypt, key)!
72+
cipher.crypt_ecb(output, mut output)!
73+
assert output.hex() == data.input
74+
}
75+
}
76+
77+
fn test_sm4_cbc() {
78+
mut key := []u8{}
79+
mut iv := []u8{}
80+
mut input := []u8{}
81+
mut output := []u8{}
82+
mut iv_next := []u8{}
83+
84+
mut cipher := &sm4.SM4Cipher{}
85+
86+
// CBC encrypt
87+
for i, data in sm4data_cbc {
88+
key = '0x${data.key}'.u8_array()
89+
iv = '0x${data.iv}'.u8_array()
90+
input = '0x${data.input}'.u8_array()
91+
output = [u8(0)].repeat(input.len) // output must be exactly the same length as input
92+
assert key.len == 16
93+
assert iv.len == 16
94+
if i != 0 {
95+
assert iv_next == iv
96+
}
97+
cipher = sm4.new_cipher(.sm4_encrypt, key)!
98+
iv_next = cipher.crypt_cbc(iv, input, mut output)!
99+
dump(iv_next.hex())
100+
assert output.hex() == data.output
101+
}
102+
// CBC decrypt
103+
for i, data in sm4data_cbc {
104+
key = '0x${data.key}'.u8_array()
105+
iv = '0x${data.iv}'.u8_array()
106+
input = '0x${data.output}'.u8_array()
107+
output = [u8(0)].repeat(input.len) // output must be exactly the same length as input
108+
assert key.len == 16
109+
assert iv.len == 16
110+
if i != 0 {
111+
assert iv_next == iv
112+
}
113+
cipher = sm4.new_cipher(.sm4_decrypt, key)!
114+
iv_next = cipher.crypt_cbc(iv, input, mut output)!
115+
dump(iv_next.hex())
116+
assert output.hex() == data.input
117+
}
150118
}
151119

152120
fn test_sm4_wrong_length() ! {
@@ -187,7 +155,7 @@ fn test_sm4_wrong_length() ! {
187155
// wrong iv length test(cbc)
188156
fail_flag = false
189157
mut c3 := sm4.new_cipher(.sm4_encrypt, [u8(0xff)].repeat(16))!
190-
c3.crypt_cbc(mut [u8(0xff)].repeat(22), [u8(0xff)].repeat(16), mut [u8(0xff)].repeat(16)) or {
158+
c3.crypt_cbc([u8(0xff)].repeat(22), [u8(0xff)].repeat(16), mut [u8(0xff)].repeat(16)) or {
191159
fail_flag = true
192160
assert err.msg() == 'iv length must be exactly 16 bytes'
193161
}
@@ -196,7 +164,7 @@ fn test_sm4_wrong_length() ! {
196164
// wrong input length test(cbc)
197165
fail_flag = false
198166
mut c4 := sm4.new_cipher(.sm4_encrypt, [u8(0xff)].repeat(16))!
199-
c4.crypt_cbc(mut [u8(0xff)].repeat(16), [u8(0xff)].repeat(111), mut output) or {
167+
c4.crypt_cbc([u8(0xff)].repeat(16), [u8(0xff)].repeat(111), mut output) or {
200168
fail_flag = true
201169
assert err.msg() == 'input must be padded to multiple of 16 bytes'
202170
}
@@ -205,7 +173,7 @@ fn test_sm4_wrong_length() ! {
205173
// wrong output length test(cbc)
206174
fail_flag = false
207175
mut c5 := sm4.new_cipher(.sm4_encrypt, [u8(0xff)].repeat(16))!
208-
c5.crypt_cbc(mut [u8(0xff)].repeat(16), [u8(0xff)].repeat(16), mut output) or {
176+
c5.crypt_cbc([u8(0xff)].repeat(16), [u8(0xff)].repeat(16), mut output) or {
209177
fail_flag = true
210178
assert err.msg() == 'output must be exactly the same length as input'
211179
}

0 commit comments

Comments
 (0)