Skip to content

Commit 887b8f6

Browse files
committed
Add chacha20 crypto
1 parent b8a1468 commit 887b8f6

10 files changed

+442
-5
lines changed

Inc/bit_manipulation.h

+11
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,15 @@ bool BitManipulation_clearBit(uint32_t data, uint8_t n, uint32_t* out);
9191
*/
9292
bool BitManipulation_toggleBit(uint32_t data, uint8_t n, uint32_t* out);
9393

94+
95+
/**
96+
* @brief This function rotates the bits of a 32-bit variable to the left.
97+
*
98+
* @param[in] data The input data to rotate.
99+
* @param[in] n_bits The number of bits in the data to be rotated.
100+
*
101+
* @return The rotated data.
102+
*/
103+
uint32_t BitManipulation_rotl32(uint32_t data, uint32_t n_bits);
104+
94105
#endif /* UTILITY_BIT_MANIPULATION_H_ */

Inc/crypto/chacha20.h

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/****************************************************************************
2+
*
3+
* Copyright (c) 2024 IMProject Development Team. All rights reserved.
4+
* Authors: Igor Misic <[email protected]>
5+
*
6+
* Redistribution and use in source and binary forms, with or without
7+
* modification, are permitted provided that the following conditions
8+
* are met:
9+
*
10+
* 1. Redistributions of source code must retain the above copyright
11+
* notice, this list of conditions and the following disclaimer.
12+
* 2. Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in
14+
* the documentation and/or other materials provided with the
15+
* distribution.
16+
* 3. Neither the name IMProject nor the names of its contributors may be
17+
* used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24+
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26+
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27+
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28+
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29+
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30+
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31+
* POSSIBILITY OF SUCH DAMAGE.
32+
*
33+
****************************************************************************/
34+
35+
#ifndef UTILITY_CHACHA20_H_
36+
#define UTILITY_CHACHA20_H_
37+
38+
#include "typedefs.h"
39+
40+
#define CHACHA20_KEY_SIZE 32U
41+
#define CHACHA20_NONCE_SIZE 12U
42+
43+
void chacha20(const byte_t* plaintext,
44+
uint32_t plaintext_len,
45+
const byte_t key[CHACHA20_KEY_SIZE],
46+
const byte_t nonce[CHACHA20_NONCE_SIZE],
47+
uint32_t inc,
48+
byte_t* encrypted_message);
49+
50+
#endif /* UTILITY_CHACHA20_H_ */

Inc/utils.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ void Utils_Serialize24LE(byte_t* buf, uint32_t value);
204204
*
205205
* @note The buffer must be at least 4 bytes long to accommodate the serialized value.
206206
*/
207-
void Utils_Serialize32LE(byte_t* buf, uint32_t value);
207+
void Utils_Serialize32LE(byte_t* const buf, uint32_t value);
208208

209209
/**
210210
* @brief Serialize a blob of data in little-endian format.

Makefile

+6-2
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ IMUTILITY_FILES=\
106106
Src/crc/crc32_variants/crc32_q.c \
107107
Src/crc/crc32_variants/crc32_xfer.c \
108108
Src/crypto/caesar_cipher.c \
109+
Src/crypto/chacha20.c \
109110
Src/sort/bubble_sort.c \
110111
Src/sort/heap_sort.c \
111112
Src/sort/insertion_sort.c \
@@ -126,6 +127,7 @@ SRC_FILES+=$(IMUTILITY_FILES) \
126127
Tests/test_bit_manipulation.c \
127128
Tests/test_bubble_sort.c \
128129
Tests/test_caesar_cipher.c \
130+
Tests/test_chacha20.c \
129131
Tests/test_crc8.c \
130132
Tests/test_crc16.c \
131133
Tests/test_crc32.c \
@@ -138,14 +140,16 @@ SRC_FILES+=$(IMUTILITY_FILES) \
138140
Tests/test_scheduler.c \
139141
Tests/test_selection_sort.c \
140142
Tests/test_utils.c
141-
INC_DIRS_CODE=\
143+
144+
INC_DIRS_CODE= \
142145
-IInc \
143-
-IInc/crypto \
144146
-IInc/crc \
145147
-IInc/crc/crc8_variants \
146148
-IInc/crc/crc16_variants \
147149
-IInc/crc/crc32_variants \
150+
-IInc/crypto \
148151
-IInc/sort
152+
149153
INC_DIRS=$(INC_DIRS_CODE) -I$(UNITY_ROOT)/src -I$(UNITY_ROOT)/extras/fixture/src
150154
SYMBOLS = -DUNITY_FIXTURE_NO_EXTRAS
151155
SYMBOLS += -DUNITY_INCLUDE_DOUBLE

Src/bit_manipulation.c

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/****************************************************************************
22
*
3-
* Copyright (c) 2023 IMProject Development Team. All rights reserved.
3+
* Copyright (c) 2023 - 2024 IMProject Development Team. All rights reserved.
44
* Authors: Juraj Ciberlin <[email protected]>
55
*
66
* Redistribution and use in source and binary forms, with or without
@@ -103,3 +103,10 @@ BitManipulation_toggleBit(uint32_t data, uint8_t n, uint32_t* out) {
103103
}
104104
return status;
105105
}
106+
107+
uint32_t
108+
BitManipulation_rotl32(uint32_t data, uint32_t n_bits) {
109+
110+
/* -E> hide MC3R1.R12.2 1 To optimize efficiency, we do not verify whether n_bits is between 0 and 31. */
111+
return ((data << n_bits) ^ (data >> (32U - n_bits)));
112+
}

Src/crypto/chacha20.c

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/****************************************************************************
2+
*
3+
* Copyright (c) 2024 IMProject Development Team. All rights reserved.
4+
* Authors: Igor Misic <[email protected]>
5+
*
6+
* Redistribution and use in source and binary forms, with or without
7+
* modification, are permitted provided that the following conditions
8+
* are met:
9+
*
10+
* 1. Redistributions of source code must retain the above copyright
11+
* notice, this list of conditions and the following disclaimer.
12+
* 2. Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in
14+
* the documentation and/or other materials provided with the
15+
* distribution.
16+
* 3. Neither the name IMProject nor the names of its contributors may be
17+
* used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24+
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26+
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27+
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28+
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29+
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30+
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31+
* POSSIBILITY OF SUCH DAMAGE.
32+
*
33+
****************************************************************************/
34+
35+
// Chacha20 definition document : https://datatracker.ietf.org/doc/html/rfc8439
36+
37+
#include "chacha20.h"
38+
39+
#include "bit_manipulation.h"
40+
#include "utils.h"
41+
42+
#define KEY_STREAM_SIZE 64U
43+
#define BLOCK_SIZE 64U
44+
45+
// Hex representation of Magic number: "expand 32-byte k"
46+
#define CHACHA_MAGIC_NUMBER_PART_1 (0x61707865U)
47+
#define CHACHA_MAGIC_NUMBER_PART_2 (0x3320646eU)
48+
#define CHACHA_MAGIC_NUMBER_PART_3 (0x79622d32U)
49+
#define CHACHA_MAGIC_NUMBER_PART_4 (0x6b206574U)
50+
51+
#define ARRAY_64_ZERO_VALUES { \
52+
0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, \
53+
0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, \
54+
0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, \
55+
0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, \
56+
0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, \
57+
0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, \
58+
0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, \
59+
0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U \
60+
}
61+
62+
static inline void quarterround(uint32_t* a, uint32_t* b, uint32_t* c, uint32_t* d);
63+
static void chacha20_block(const byte_t key[CHACHA20_KEY_SIZE], const uint32_t counter,
64+
const byte_t nonce[CHACHA20_NONCE_SIZE],
65+
byte_t out[KEY_STREAM_SIZE]);
66+
67+
static inline void
68+
quarterround(uint32_t* a, uint32_t* b, uint32_t* c, uint32_t* d) {
69+
*a += *b;
70+
*d = BitManipulation_rotl32(*d ^ *a, 16);
71+
*c += *d;
72+
*b = BitManipulation_rotl32(*b ^ *c, 12);
73+
*a += *b;
74+
*d = BitManipulation_rotl32(*d ^ *a, 8);
75+
*c += *d;
76+
*b = BitManipulation_rotl32(*b ^ *c, 7);
77+
}
78+
79+
static void
80+
chacha20_block(const byte_t key[CHACHA20_KEY_SIZE], const uint32_t counter, const byte_t nonce[CHACHA20_NONCE_SIZE],
81+
byte_t out[KEY_STREAM_SIZE]) {
82+
83+
uint32_t x0 = CHACHA_MAGIC_NUMBER_PART_1;
84+
uint32_t x1 = CHACHA_MAGIC_NUMBER_PART_2;
85+
uint32_t x2 = CHACHA_MAGIC_NUMBER_PART_3;
86+
uint32_t x3 = CHACHA_MAGIC_NUMBER_PART_4;
87+
uint32_t x4 = Utils_Deserialize32LE(&key[0]);
88+
uint32_t x5 = Utils_Deserialize32LE(&key[4]);
89+
uint32_t x6 = Utils_Deserialize32LE(&key[8]);
90+
uint32_t x7 = Utils_Deserialize32LE(&key[12]);
91+
uint32_t x8 = Utils_Deserialize32LE(&key[16]);
92+
uint32_t x9 = Utils_Deserialize32LE(&key[20]);
93+
uint32_t x10 = Utils_Deserialize32LE(&key[24]);
94+
uint32_t x11 = Utils_Deserialize32LE(&key[28]);
95+
uint32_t x12 = counter;
96+
uint32_t x13 = Utils_Deserialize32LE(&nonce[0]);
97+
uint32_t x14 = Utils_Deserialize32LE(&nonce[4]);
98+
uint32_t x15 = Utils_Deserialize32LE(&nonce[8]);
99+
100+
for (uint8_t i = 0; i < 10U; ++i) {
101+
quarterround(&x0, &x4, &x8, &x12);
102+
quarterround(&x1, &x5, &x9, &x13);
103+
quarterround(&x2, &x6, &x10, &x14);
104+
quarterround(&x3, &x7, &x11, &x15);
105+
quarterround(&x0, &x5, &x10, &x15);
106+
quarterround(&x1, &x6, &x11, &x12);
107+
quarterround(&x2, &x7, &x8, &x13);
108+
quarterround(&x3, &x4, &x9, &x14);
109+
}
110+
111+
x0 += CHACHA_MAGIC_NUMBER_PART_1;
112+
x1 += CHACHA_MAGIC_NUMBER_PART_2;
113+
x2 += CHACHA_MAGIC_NUMBER_PART_3;
114+
x3 += CHACHA_MAGIC_NUMBER_PART_4;
115+
x4 += Utils_Deserialize32LE(&key[0]);
116+
x5 += Utils_Deserialize32LE(&key[4]);
117+
x6 += Utils_Deserialize32LE(&key[8]);
118+
x7 += Utils_Deserialize32LE(&key[12]);
119+
x8 += Utils_Deserialize32LE(&key[16]);
120+
x9 += Utils_Deserialize32LE(&key[20]);
121+
x10 += Utils_Deserialize32LE(&key[24]);
122+
x11 += Utils_Deserialize32LE(&key[28]);
123+
x12 += counter;
124+
x13 += Utils_Deserialize32LE(&nonce[0]);
125+
x14 += Utils_Deserialize32LE(&nonce[4]);
126+
x15 += Utils_Deserialize32LE(&nonce[8]);
127+
128+
/* -E> compliant MC3R1.R18.6 16 automatic storage pointed with "out" is not copied to values from this function */
129+
Utils_Serialize32LE(&out[0], x0);
130+
Utils_Serialize32LE(&out[4], x1);
131+
Utils_Serialize32LE(&out[8], x2);
132+
Utils_Serialize32LE(&out[12], x3);
133+
Utils_Serialize32LE(&out[16], x4);
134+
Utils_Serialize32LE(&out[20], x5);
135+
Utils_Serialize32LE(&out[24], x6);
136+
Utils_Serialize32LE(&out[28], x7);
137+
Utils_Serialize32LE(&out[32], x8);
138+
Utils_Serialize32LE(&out[36], x9);
139+
Utils_Serialize32LE(&out[40], x10);
140+
Utils_Serialize32LE(&out[44], x11);
141+
Utils_Serialize32LE(&out[48], x12);
142+
Utils_Serialize32LE(&out[52], x13);
143+
Utils_Serialize32LE(&out[56], x14);
144+
Utils_Serialize32LE(&out[60], x15);
145+
}
146+
147+
void
148+
chacha20(const byte_t* plaintext,
149+
uint32_t plaintext_len,
150+
const byte_t key[CHACHA20_KEY_SIZE],
151+
const byte_t nonce[CHACHA20_NONCE_SIZE],
152+
uint32_t inc,
153+
byte_t* encrypted_message) {
154+
155+
uint32_t blocks = plaintext_len / BLOCK_SIZE;
156+
uint32_t remaining_bytes = plaintext_len % BLOCK_SIZE;
157+
158+
for (uint32_t j = 0; j < blocks; ++j) {
159+
byte_t key_stream[KEY_STREAM_SIZE] = ARRAY_64_ZERO_VALUES;
160+
chacha20_block(key, j + inc, nonce, key_stream);
161+
162+
for (uint8_t i = 0; i < BLOCK_SIZE; ++i) {
163+
encrypted_message[(j * BLOCK_SIZE) + i] = plaintext[(j * BLOCK_SIZE) + i] ^ key_stream[i];
164+
}
165+
}
166+
167+
if (0U != remaining_bytes) {
168+
byte_t key_stream[KEY_STREAM_SIZE] = ARRAY_64_ZERO_VALUES;
169+
chacha20_block(key, blocks + inc, nonce, key_stream);
170+
171+
for (uint32_t i = 0; i < remaining_bytes; ++i) {
172+
encrypted_message[(blocks * BLOCK_SIZE) + i] = plaintext[(blocks * BLOCK_SIZE) + i] ^ key_stream[i];
173+
}
174+
}
175+
}

Src/utils.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ Utils_Serialize24LE(byte_t* buf, uint32_t value) {
204204
}
205205

206206
void
207-
Utils_Serialize32LE(byte_t* buf, uint32_t value) {
207+
Utils_Serialize32LE(byte_t* const buf, uint32_t value) {
208208
buf[3] = (uint8_t)(value >> 24u) & 0xFFu;
209209
buf[2] = (uint8_t)(value >> 16u) & 0xFFu;
210210
buf[1] = (uint8_t)(value >> 8u) & 0xFFu;

Tests/test_bit_manipulation.c

+21
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ TEST_GROUP_RUNNER(BitManipulation) {
1717
RUN_TEST_CASE(BitManipulation, BitManipulation_setBit);
1818
RUN_TEST_CASE(BitManipulation, BitManipulation_clearBit);
1919
RUN_TEST_CASE(BitManipulation, BitManipulation_toggleBit);
20+
RUN_TEST_CASE(BitManipulation, BitManipulation_rotl32);
2021
}
2122

2223
TEST(BitManipulation, BitManipulation_reflect) {
@@ -83,3 +84,23 @@ TEST(BitManipulation, BitManipulation_toggleBit) {
8384
TEST_ASSERT_TRUE(BitManipulation_toggleBit(data, 4U, &out_data));
8485
TEST_ASSERT_EQUAL_UINT32(0b10100011U, out_data);
8586
}
87+
88+
TEST(BitManipulation, BitManipulation_rotl32) {
89+
const uint32_t data = 0b10110011U;
90+
91+
uint8_t n_bits = 2U;
92+
TEST_ASSERT_BITS(0xFFFFFFFF, 0b00000000000000000000001011001100U, BitManipulation_rotl32(data, n_bits));
93+
94+
n_bits = 3U;
95+
TEST_ASSERT_BITS(0xFFFFFFFF, 0b00000000000000000000010110011000U, BitManipulation_rotl32(data, n_bits));
96+
97+
n_bits = 4U;
98+
TEST_ASSERT_BITS(0xFFFFFFFF, 0b00000000000000000000101100110000U, BitManipulation_rotl32(data, n_bits));
99+
100+
n_bits = 16U;
101+
TEST_ASSERT_BITS(0xFFFFFFFF, 0b00000000101100110000000000000000U, BitManipulation_rotl32(data, n_bits));
102+
103+
n_bits = 32U;
104+
TEST_ASSERT_BITS(0xFFFFFFFF, 0b00000000000000000000000010110011U, BitManipulation_rotl32(data, n_bits));
105+
}
106+

0 commit comments

Comments
 (0)