• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

moonbitlang / x / 301

10 Dec 2024 06:19AM UTC coverage: 85.204% (-2.6%) from 87.841%
301

Pull #78

github

web-flow
Merge b830031f4 into 91f0fdf48
Pull Request #78: feat: new package encoding

105 of 161 new or added lines in 3 files covered. (65.22%)

124 existing lines in 29 files now uncovered.

1169 of 1372 relevant lines covered (85.2%)

434.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.43
/crypto/chacha.mbt
1
// Copyright 2024 International Digital Economy Academy
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
///|
16
fn flipWord(word : UInt) -> UInt {
17
  let b0 = (word & 0xff) << 24
105✔
18
  let b1 = (word & 0xff00) << 8
19
  let b2 = (word & 0xff0000) >> 8
20
  let b3 = (word & 0xff000000) >> 24
21
  b0 | b1 | b2 | b3
22
}
23

24
test "flipWord" {
25
  let word = 0x12345678U
26
  let flipped = flipWord(word)
27
  inspect!(flipped, content="2018915346")
28
}
29

30
///|
31
fn quarterRound(
32
  state : FixedArray[UInt],
33
  w : Int,
34
  x : Int,
35
  y : Int,
36
  z : Int
37
) -> FixedArray[UInt] {
38
  fn fullQuarterRound(
233✔
39
    a : UInt,
40
    b : UInt,
41
    c : UInt,
42
    d : UInt
43
  ) -> (UInt, UInt, UInt, UInt) {
44
    quarterRound4(quarterRound3(quarterRound2(quarterRound1((a, b, c, d)))))
233✔
45
  }
46

47
  let t = fullQuarterRound(state[w], state[x], state[y], state[z])
48
  state[w] = t.0
49
  state[x] = t.1
50
  state[y] = t.2
51
  state[z] = t.3
52
  state
53
}
54

55
test "quarterRound" {
56
  let state = FixedArray::make(16, 0U)
57
  state[0] = 0x879531e0U
58
  state[1] = 0xc5ecf37dU
59
  state[2] = 0x516461b1U
60
  state[3] = 0xc9a62f8aU
61
  state[4] = 0x44c20ef3U
62
  state[5] = 0x3390af7fU
63
  state[6] = 0xd9fc690bU
64
  state[7] = 0x2a5f714cU
65
  state[8] = 0x53372767U
66
  state[9] = 0xb00a5631U
67
  state[10] = 0x974c541aU
68
  state[11] = 0x359e9963U
69
  state[12] = 0x5c971061U
70
  state[13] = 0x3d631689U
71
  state[14] = 0x2098d9d6U
72
  state[15] = 0x91dbd320U
73
  let newState = quarterRound(state, 2, 7, 8, 13)
74
  inspect!(
75
    newState,
76
    content="[2274701792, 3320640381, 3182986972, 3383111562, 1153568499, 865120127, 3657197835, 3484200914, 3832277632, 2953467441, 2538361882, 899586403, 1553404001, 3435166841, 546888150, 2447102752]",
77
  )
78
}
79

80
///|
81
fn chachaBlockRound(state : FixedArray[UInt]) -> FixedArray[UInt] {
82
  let state1 = quarterRound(state, 0, 4, 8, 12)
29✔
83
  let state2 = quarterRound(state1, 1, 5, 9, 13)
84
  let state3 = quarterRound(state2, 2, 6, 10, 14)
85
  let state4 = quarterRound(state3, 3, 7, 11, 15)
86
  let state5 = quarterRound(state4, 0, 5, 10, 15)
87
  let state6 = quarterRound(state5, 1, 6, 11, 12)
88
  let state7 = quarterRound(state6, 2, 7, 8, 13)
89
  let state8 = quarterRound(state7, 3, 4, 9, 14)
90
  state8
91
}
92

93
test "chachaBlockRound" {
94
  let state = FixedArray::make(16, 0U)
95
  state[0] = 0x61707865U
96
  state[1] = 0x3320646eU
97
  state[2] = 0x79622d32U
98
  state[3] = 0x6b206574U
99
  state[4] = 0x03020100U
100
  state[5] = 0x07060504U
101
  state[6] = 0x0b0a0908U
102
  state[7] = 0x0f0e0d0cU
103
  state[8] = 0x13121110U
104
  state[9] = 0x17161514U
105
  state[10] = 0x1b1a1918U
106
  state[11] = 0x1f1e1d1cU
107
  state[12] = 0x00000001U
108
  state[13] = 0x00000000U
109
  state[14] = 0x00000000U
110
  state[15] = 0x00000000U
111
  let newState = chachaBlockRound(state)
112
  inspect!(
113
    newState,
114
    content="[986087425, 3489031050, 2890662805, 2683391196, 1720476390, 1116253759, 2262580386, 3212003942, 2202368212, 756352536, 496298475, 669838588, 567302638, 1860562437, 1434237441, 2097484794]",
115
  )
116
}
117

118
///|
119
fn chachaBlockLoop(state : FixedArray[UInt], n : UInt) -> FixedArray[UInt] {
120
  match n {
33✔
121
    0 => state.copy()
5✔
122
    _ => chachaBlockLoop(chachaBlockRound(state.copy()), n - 1)
28✔
123
  }
124
}
125

126
test "chachaBlockLoop" {
127
  let count = 1U
128
  let key = FixedArray::make(8, 0U)
129
  key[0] = 0x00010203U
130
  key[1] = 0x04050607U
131
  key[2] = 0x08090a0bU
132
  key[3] = 0x0c0d0e0fU
133
  key[4] = 0x10111213U
134
  key[5] = 0x14151617U
135
  key[6] = 0x18191a1bU
136
  key[7] = 0x1c1d1e1fU
137
  let state = FixedArray::make(16, 0U)
138
  state[0] = 0X61707865U
139
  state[1] = 0X3320646eU
140
  state[2] = 0X79622d32U
141
  state[3] = 0X6b206574U
142
  state[4] = flipWord(key[0])
143
  state[5] = flipWord(key[1])
144
  state[6] = flipWord(key[2])
145
  state[7] = flipWord(key[3])
146
  state[8] = flipWord(key[4])
147
  state[9] = flipWord(key[5])
148
  state[10] = flipWord(key[6])
149
  state[11] = flipWord(key[7])
150
  state[12] = count
151
  state[13] = 0
152
  state[14] = 0
153
  state[15] = 0
154
  inspect!(
155
    state,
156
    content="[1634760805, 857760878, 2036477234, 1797285236, 50462976, 117835012, 185207048, 252579084, 319951120, 387323156, 454695192, 522067228, 1, 0, 0, 0]",
157
  )
158
  let newState = chachaBlockLoop(state, 4)
159
  inspect!(
160
    newState,
161
    content="[2919080465, 647515738, 898727107, 777299107, 3407982512, 2489307765, 745530666, 2053399858, 1994399329, 139328223, 3709168053, 3118545354, 4170274417, 867477305, 1393604261, 3769539545]",
162
  )
163
}
164

165
///|
166
fn flipState(state : FixedArray[UInt]) -> FixedArray[UInt] {
167
  state.map(flipWord)
4✔
168
}
169

170
///|
171
fn zipWith(
172
  op : (UInt, UInt) -> UInt,
173
  state1 : FixedArray[UInt],
174
  state2 : FixedArray[UInt]
175
) -> FixedArray[UInt] {
176
  let result = FixedArray::make(state1.length(), 0U)
5✔
177
  for i = 0; i < state1.length(); i = i + 1 {
68✔
178
    result[i] = op(state1[i], state2[i])
68✔
179
  }
180
  result
181
}
182

183
test "zipWith" {
184
  let state1 = FixedArray::make(4, 0U)
185
  state1[0] = 1
186
  state1[1] = 2
187
  state1[2] = 3
188
  state1[3] = 4
189
  let state2 = FixedArray::make(4, 0U)
190
  state2[0] = 5
191
  state2[1] = 6
192
  state2[2] = 7
193
  state2[3] = 8
194
  let result = zipWith(UInt::op_add, state1, state2)
195
  inspect!(result, content="[6, 8, 10, 12]")
196
}
197

198
///| - chacha8: round = 4
199
/// - chacha12: round = 6
200
/// - chacha20: round = 10
201
fn chachaBlock(
202
  key : FixedArray[UInt],
203
  count : UInt,
204
  nonce : UInt,
205
  round : UInt
206
) -> FixedArray[UInt] {
207
  if key.length() != 8 {
4✔
UNCOV
208
    abort("Invalid key length -- key must be 256 bits")
×
209
  }
210
  let state = FixedArray::make(16, 0U)
211
  state[0] = 0X61707865U
212
  state[1] = 0X3320646eU
213
  state[2] = 0X79622d32U
214
  state[3] = 0X6b206574U
215
  state[4] = flipWord(key[0])
216
  state[5] = flipWord(key[1])
217
  state[6] = flipWord(key[2])
218
  state[7] = flipWord(key[3])
219
  state[8] = flipWord(key[4])
220
  state[9] = flipWord(key[5])
221
  state[10] = flipWord(key[6])
222
  state[11] = flipWord(key[7])
223
  state[12] = count
224
  state[13] = nonce
225
  state[14] = nonce
226
  state[15] = nonce
227
  let mixedState = chachaBlockLoop(state.copy(), round)
228
  let combinedState = zipWith(UInt::op_add, state, mixedState)
229
  flipState(combinedState)
230
}
231

232
test "chachaBlock" {
233
  let key = FixedArray::make(8, 0U)
234
  key[0] = 0x00010203U
235
  key[1] = 0x04050607U
236
  key[2] = 0x08090a0bU
237
  key[3] = 0x0c0d0e0fU
238
  key[4] = 0x10111213U
239
  key[5] = 0x14151617U
240
  key[6] = 0x18191a1bU
241
  key[7] = 0x1c1d1e1fU
242
  let keyStream = chachaBlock(key, 1, 0, 4)
243
  inspect!(
244
    keyStream,
245
    content="[1981443599, 3367155801, 4121555886, 386561433, 2964333518, 2044159387, 854489399, 1047687817, 1898967689, 4077872159, 3447861240, 3864461272, 1918276088, 967291955, 2780172371, 3650858720]",
246
  )
247
}
248

249
///|
250
fn stateToBytes(state : FixedArray[UInt]) -> Bytes {
251
  fn from_array(arr : Array[UInt]) -> Bytes {
4✔
252
    let rv = Bytes::new(arr.length())
4✔
253
    for i = 0; i < arr.length(); i = i + 1 {
256✔
254
      rv[i] = arr[i].to_byte()
256✔
255
    }
256
    rv
257
  }
258

259
  let result = Array::make(64, 0U)
260
  for i = 0; i < 16; i = i + 1 {
64✔
261
    let word = state[i]
64✔
262
    result[i * 4 + 3] = word & 0xff
263
    result[i * 4 + 2] = (word & 0xff00) >> 8
264
    result[i * 4 + 1] = (word & 0xff0000) >> 16
265
    result[i * 4 + 0] = (word & 0xff000000) >> 24
266
  }
267
  from_array(result)
268
}
269

270
test "stateToBytes" {
271
  let state = FixedArray::make(16, 0U)
272
  state[0] = 0x879531e0
273
  state[1] = 0xc5ecf37d
274
  state[2] = 0x516461b1
275
  state[3] = 0xc9a62f8a
276
  state[4] = 0x44c20ef3
277
  state[5] = 0x3390af7f
278
  state[6] = 0xd9fc690b
279
  state[7] = 0x2a5f714c
280
  state[8] = 0x53372767
281
  state[9] = 0xb00a5631
282
  state[10] = 0x974c541a
283
  state[11] = 0x359e9963
284
  state[12] = 0x5c971061
285
  state[13] = 0x3d631689
286
  state[14] = 0x2098d9d6
287
  state[15] = 0x91dbd320
288
  let bytes = stateToBytes(state)
289
  inspect!(
290
    bytes_to_hex_string(bytes),
291
    content="879531e0c5ecf37d516461b1c9a62f8a44c20ef33390af7fd9fc690b2a5f714c53372767b00a5631974c541a359e99635c9710613d6316892098d9d691dbd320",
292
  )
293
}
294

295
///| Encrypts a block of data using the ChaCha8 algorithm.
296
/// - [key] must be 8 32-bit unsigned integers.
297
/// - [counter] is the counter value.
298
/// - [block] is the block of data to be encrypted.
299
/// - [nonce] is default to 0
300
/// - Returns the encrypted block of data.
301
pub fn chacha8(
302
  key : FixedArray[UInt],
303
  counter : UInt,
304
  block : Bytes,
305
  nonce~ : UInt = 0
306
) -> Bytes!Error {
307
  if key.length() != 8 {
1✔
UNCOV
308
    fail!("Invalid key length -- key must be 8 32-bit unsigned integers")
×
309
  }
310
  chacha(key, counter, block, 4, nonce)
311
}
312

313
///| Encrypts a block of data using the ChaCha12 algorithm.
314
/// - [key] must be 8 32-bit unsigned integers.
315
/// - [counter] is the counter value.
316
/// - [block] is the block of data to be encrypted.
317
/// - [nonce] is default to 0
318
/// - Returns the encrypted block of data.
319
pub fn chacha12(
320
  key : FixedArray[UInt],
321
  counter : UInt,
322
  block : Bytes,
323
  nonce~ : UInt = 0
324
) -> Bytes!Error {
325
  if key.length() != 8 {
1✔
UNCOV
326
    fail!("Invalid key length -- key must be 8 32-bit unsigned integers")
×
327
  }
328
  chacha(key, counter, block, 6, nonce)
329
}
330

331
///| Encrypts a block of data using the ChaCha20 algorithm.
332
/// - [key] must be 8 32-bit unsigned integers.
333
/// - [counter] is the counter value.
334
/// - [block] is the block of data to be encrypted.
335
/// - [nonce] is default to 0
336
/// - Returns the encrypted block of data.
337
pub fn chacha20(
338
  key : FixedArray[UInt],
339
  counter : UInt,
340
  block : Bytes,
341
  nonce~ : UInt = 0
342
) -> Bytes!Error {
343
  if key.length() != 8 {
1✔
UNCOV
344
    fail!("Invalid key length -- key must be 8 32-bit unsigned integers")
×
345
  }
346
  chacha(key, counter, block, 10, nonce)
347
}
348

349
///|
350
fn chacha(
351
  key : FixedArray[UInt],
352
  counter : UInt,
353
  block : Bytes,
354
  round : UInt,
355
  nonce : UInt
356
) -> Bytes {
357
  if block.length() == 0 {
6✔
358
    return Bytes::new(0)
3✔
359
  }
360
  fn takeToFixedArray(b : Bytes, len : Int) -> FixedArray[Byte] {
361
    let result = FixedArray::make(len, b'0')
6✔
362
    for i = 0; i < len; i = i + 1 {
36✔
363
      result[i] = b[i]
36✔
364
    }
365
    result
366
  }
367

368
  fn zipWith(
369
    op : (Byte, Byte) -> Byte,
370
    state1 : FixedArray[Byte],
371
    state2 : FixedArray[Byte]
372
  ) -> Bytes {
373
    let result = Array::make(state1.length(), b'0')
3✔
374
    for i = 0; i < state1.length(); i = i + 1 {
18✔
375
      result[i] = op(state1[i], state2[i])
18✔
376
    }
377
    Bytes::from_array(result)
378
  }
379

380
  fn dropBytes(b : Bytes, len : Int) -> Bytes {
381
    let result = Bytes::new(b.length() - len)
3✔
UNCOV
382
    for i = 0; i < b.length() - len; i = i + 1 {
×
UNCOV
383
      result[i] = b[len + i]
×
384
    }
385
    result
386
  }
387

388
  fn concatBytes(b1 : Bytes, b2 : Bytes) -> Bytes {
389
    let result = Bytes::new(b1.length() + b2.length())
3✔
390
    for i = 0; i < b1.length(); i = i + 1 {
18✔
391
      result[i] = b1[i]
18✔
392
    }
393
    for i = 0; i < b2.length(); i = i + 1 {
×
UNCOV
394
      result[b1.length() + i] = b2[i]
×
395
    }
396
    result
397
  }
398

399
  let keyStream = chachaBlock(key, counter, nonce, round)
400
  let pad = stateToBytes(keyStream)
UNCOV
401
  let len = if block.length() < 64 { block.length() } else { 64 }
×
402
  let maskedBlock = zipWith(
403
    Byte::lxor,
404
    takeToFixedArray(pad, len),
405
    takeToFixedArray(block, len),
406
  )
407
  let res = concatBytes(
408
    maskedBlock,
409
    chacha(key, counter + 1, dropBytes(block, len), round, nonce),
410
  )
411
  res
412
}
413

414
///|
415
fn quarterRound1(t : (UInt, UInt, UInt, UInt)) -> (UInt, UInt, UInt, UInt) {
416
  let aprime = t.0 + t.1
233✔
417
  let dprime = t.3 ^ aprime
418
  let dout = rotate_left_u(dprime, 16)
419
  (aprime, t.1, t.2, dout)
420
}
421

422
///|
423
fn quarterRound2(t : (UInt, UInt, UInt, UInt)) -> (UInt, UInt, UInt, UInt) {
424
  let cprime = t.2 + t.3
233✔
425
  let bprime = t.1 ^ cprime
426
  let bout = rotate_left_u(bprime, 12)
427
  (t.0, bout, cprime, t.3)
428
}
429

430
///|
431
fn quarterRound3(t : (UInt, UInt, UInt, UInt)) -> (UInt, UInt, UInt, UInt) {
432
  let aprime = t.0 + t.1
233✔
433
  let dprime = t.3 ^ aprime
434
  let dout = rotate_left_u(dprime, 8)
435
  (aprime, t.1, t.2, dout)
436
}
437

438
///|
439
fn quarterRound4(t : (UInt, UInt, UInt, UInt)) -> (UInt, UInt, UInt, UInt) {
440
  let cprime = t.2 + t.3
233✔
441
  let bprime = t.1 ^ cprime
442
  let bout = rotate_left_u(bprime, 7)
443
  (t.0, bout, cprime, t.3)
444
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc