Skip to content

Commit 9570700

Browse files
authored
Add SHA256 utility implementation (ros2#408)
* Add sha256 utility implementation. Signed-off-by: Emerson Knapp <emerson.b.knapp@gmail.com>
1 parent 725b6f1 commit 9570700

4 files changed

Lines changed: 376 additions & 0 deletions

File tree

CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ set(rcutils_sources
6363
src/process.c
6464
src/qsort.c
6565
src/repl_str.c
66+
src/sha256.c
6667
src/shared_library.c
6768
src/snprintf.c
6869
src/split.c
@@ -362,6 +363,13 @@ if(BUILD_TESTING)
362363
target_link_libraries(test_repl_str ${PROJECT_NAME})
363364
endif()
364365

366+
ament_add_gtest(test_sha256
367+
test/test_sha256.cpp
368+
)
369+
if(TARGET test_sha256)
370+
target_link_libraries(test_sha256 ${PROJECT_NAME})
371+
endif()
372+
365373
macro(add_dummy_shared_library target)
366374
add_library(${target} test/dummy_shared_library/dummy_shared_library.c)
367375
if(WIN32)

include/rcutils/sha256.h

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright 2023 Open Source Robotics Foundation, Inc.
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+
/// \file Provides a simple SHA256 algorithm for hashing.
16+
/// This implementation makes no security guarantees, its use case
17+
/// is for non-sensitive comparison of message digests
18+
/// Implementation originally copied from Brad Conte
19+
/// https://github.com/B-Con/crypto-algorithms/blob/master/sha256.c
20+
21+
/** \file sha256.h
22+
* \brief SHA256 implementation
23+
*
24+
* This contains an implementation of the SHA256 algorithm
25+
* It was originally copied from Brad Conte
26+
* https://github.com/B-Con/crypto-algorithms/blob/master/sha256.c
27+
* and modified to meet ros2 code formatting and compiler warning requirements.
28+
* Algorithm specification can be found here:
29+
* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
30+
* This implementation uses little endian byte order.
31+
*/
32+
33+
#ifndef RCUTILS__SHA256_H_
34+
#define RCUTILS__SHA256_H_
35+
36+
#ifdef __cplusplus
37+
extern "C"
38+
{
39+
#endif
40+
41+
#include <stdint.h>
42+
43+
#include "rcutils/visibility_control.h"
44+
45+
#define RCUTILS_SHA256_BLOCK_SIZE 32
46+
47+
typedef struct RCUTILS_PUBLIC_TYPE rcutils_sha256_ctx_s
48+
{
49+
uint8_t data[64];
50+
size_t datalen;
51+
uint64_t bitlen;
52+
uint32_t state[8];
53+
} rcutils_sha256_ctx_t;
54+
55+
/// Initialize the sha256 algorithm context with starting state.
56+
/**
57+
* Call this on any new context before starting to input data.
58+
*
59+
* \param[inout] ctx
60+
* \return void
61+
*/
62+
RCUTILS_PUBLIC
63+
void rcutils_sha256_init(rcutils_sha256_ctx_t * ctx);
64+
65+
/// Add data to the sha256 algorithm
66+
/**
67+
* This may be called repeatedly on an initialized context.
68+
*
69+
* \param[inout] ctx Initialized sha256 context struct
70+
* \param[in] data Data to add to the total message being hashed
71+
* \param[in] data_len Size of the input data.
72+
* \return void
73+
*/
74+
RCUTILS_PUBLIC
75+
void rcutils_sha256_update(rcutils_sha256_ctx_t * ctx, const uint8_t * data, size_t data_len);
76+
77+
/// Finalize and output sha256 hash for all data added.
78+
/**
79+
* Call only once on a context that has been initialized, and optionally updated with data.
80+
*
81+
* \param[inout] ctx Initialized sha256 context struct
82+
* \param[out] output_hash Calculated sha256 message digest to be filled
83+
* \return void
84+
*/
85+
RCUTILS_PUBLIC
86+
void rcutils_sha256_final(
87+
rcutils_sha256_ctx_t * ctx,
88+
uint8_t output_hash[RCUTILS_SHA256_BLOCK_SIZE]);
89+
90+
#ifdef __cplusplus
91+
}
92+
#endif
93+
94+
#endif // RCUTILS__SHA256_H_

src/sha256.c

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// Copyright 2023 Open Source Robotics Foundation, Inc.
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+
#include <assert.h>
16+
#include <string.h>
17+
18+
#include "rcutils/sha256.h"
19+
20+
static inline size_t min(size_t a, size_t b)
21+
{
22+
return a < b ? a : b;
23+
}
24+
25+
static inline size_t max(size_t a, size_t b)
26+
{
27+
return a > b ? a : b;
28+
}
29+
30+
static inline uint32_t rotright(uint32_t a, const uint8_t b)
31+
{
32+
assert(b < 32);
33+
return (a >> b) | (a << (32 - b));
34+
}
35+
36+
static inline uint32_t ch(uint32_t x, uint32_t y, uint32_t z)
37+
{
38+
return (x & y) ^ (~x & z);
39+
}
40+
41+
static inline uint32_t maj(uint32_t x, uint32_t y, uint32_t z)
42+
{
43+
return (x & y) ^ (x & z) ^ (y & z);
44+
}
45+
46+
static inline uint32_t ep0(uint32_t x)
47+
{
48+
return rotright(x, 2) ^ rotright(x, 13) ^ rotright(x, 22);
49+
}
50+
51+
static inline uint32_t ep1(uint32_t x)
52+
{
53+
return rotright(x, 6) ^ rotright(x, 11) ^ rotright(x, 25);
54+
}
55+
56+
static inline uint32_t sig0(uint32_t x)
57+
{
58+
return rotright(x, 7) ^ rotright(x, 18) ^ (x >> 3);
59+
}
60+
61+
static inline uint32_t sig1(uint32_t x)
62+
{
63+
return rotright(x, 17) ^ rotright(x, 19) ^ (x >> 10);
64+
}
65+
66+
static const uint32_t k[64] = {
67+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
68+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
69+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
70+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
71+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
72+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
73+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
74+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
75+
};
76+
77+
static void sha256_transform(rcutils_sha256_ctx_t * ctx)
78+
{
79+
uint32_t a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
80+
uint8_t * data = ctx->data;
81+
82+
for (i = 0, j = 0; i < 16; ++i, j += 4) {
83+
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
84+
}
85+
for ( ; i < 64; ++i) {
86+
m[i] = sig1(m[i - 2]) + m[i - 7] + sig0(m[i - 15]) + m[i - 16];
87+
}
88+
89+
a = ctx->state[0];
90+
b = ctx->state[1];
91+
c = ctx->state[2];
92+
d = ctx->state[3];
93+
e = ctx->state[4];
94+
f = ctx->state[5];
95+
g = ctx->state[6];
96+
h = ctx->state[7];
97+
98+
for (i = 0; i < 64; ++i) {
99+
t1 = h + ep1(e) + ch(e, f, g) + k[i] + m[i];
100+
t2 = ep0(a) + maj(a, b, c);
101+
h = g;
102+
g = f;
103+
f = e;
104+
e = d + t1;
105+
d = c;
106+
c = b;
107+
b = a;
108+
a = t1 + t2;
109+
}
110+
111+
ctx->state[0] += a;
112+
ctx->state[1] += b;
113+
ctx->state[2] += c;
114+
ctx->state[3] += d;
115+
ctx->state[4] += e;
116+
ctx->state[5] += f;
117+
ctx->state[6] += g;
118+
ctx->state[7] += h;
119+
}
120+
121+
void rcutils_sha256_init(rcutils_sha256_ctx_t * ctx)
122+
{
123+
ctx->datalen = 0;
124+
ctx->bitlen = 0;
125+
ctx->state[0] = 0x6a09e667;
126+
ctx->state[1] = 0xbb67ae85;
127+
ctx->state[2] = 0x3c6ef372;
128+
ctx->state[3] = 0xa54ff53a;
129+
ctx->state[4] = 0x510e527f;
130+
ctx->state[5] = 0x9b05688c;
131+
ctx->state[6] = 0x1f83d9ab;
132+
ctx->state[7] = 0x5be0cd19;
133+
}
134+
135+
void rcutils_sha256_update(rcutils_sha256_ctx_t * ctx, const uint8_t * data, size_t len)
136+
{
137+
size_t i, data_remaining, block_remaining, copy_len;
138+
i = 0;
139+
140+
while (i < len) {
141+
data_remaining = len - i;
142+
block_remaining = 64 - ctx->datalen;
143+
copy_len = min(min(block_remaining, data_remaining), 64);
144+
145+
memcpy(ctx->data + ctx->datalen, data + i, copy_len);
146+
ctx->datalen += copy_len;
147+
i += copy_len;
148+
149+
if (ctx->datalen >= 64) {
150+
sha256_transform(ctx);
151+
ctx->bitlen += 512;
152+
ctx->datalen = 0;
153+
}
154+
}
155+
}
156+
157+
void rcutils_sha256_final(
158+
rcutils_sha256_ctx_t * ctx, uint8_t output_hash[RCUTILS_SHA256_BLOCK_SIZE])
159+
{
160+
size_t i = ctx->datalen;
161+
162+
// Pad whatever data is left in the buffer.
163+
if (ctx->datalen < 56) {
164+
ctx->data[i++] = 0x80;
165+
memset(ctx->data + i, 0x00, 56 - i);
166+
} else {
167+
ctx->data[i++] = 0x80;
168+
if (i < 64) {
169+
memset(ctx->data + i, 0x00, 64 - i);
170+
}
171+
sha256_transform(ctx);
172+
memset(ctx->data, 0, 56);
173+
}
174+
175+
// Append to the padding the total message's length in bits and transform.
176+
ctx->bitlen += ctx->datalen * 8;
177+
ctx->data[63] = (uint8_t)(ctx->bitlen >> 0);
178+
ctx->data[62] = (uint8_t)(ctx->bitlen >> 8);
179+
ctx->data[61] = (uint8_t)(ctx->bitlen >> 16);
180+
ctx->data[60] = (uint8_t)(ctx->bitlen >> 24);
181+
ctx->data[59] = (uint8_t)(ctx->bitlen >> 32);
182+
ctx->data[58] = (uint8_t)(ctx->bitlen >> 40);
183+
ctx->data[57] = (uint8_t)(ctx->bitlen >> 48);
184+
ctx->data[56] = (uint8_t)(ctx->bitlen >> 56);
185+
sha256_transform(ctx);
186+
187+
// Since this implementation uses little endian byte ordering and SHA uses big endian,
188+
// reverse all the bytes when copying the final state to the output hash.
189+
for (i = 0; i < 4; ++i) {
190+
output_hash[i + 0] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
191+
output_hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
192+
output_hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
193+
output_hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
194+
output_hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
195+
output_hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
196+
output_hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
197+
output_hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
198+
}
199+
}

test/test_sha256.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright 2023 Open Source Robotics Foundation, Inc.
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+
#include <gtest/gtest.h>
16+
17+
#include "rcutils/sha256.h"
18+
19+
TEST(TestSHA256, test_text1) {
20+
uint8_t text1[] = {"abc"};
21+
size_t text1_len = sizeof(text1) - 1;
22+
uint8_t expected_hash1[RCUTILS_SHA256_BLOCK_SIZE] = {
23+
0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
24+
0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
25+
0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
26+
0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad};
27+
uint8_t buf[RCUTILS_SHA256_BLOCK_SIZE];
28+
29+
30+
rcutils_sha256_ctx_t ctx;
31+
rcutils_sha256_init(&ctx);
32+
rcutils_sha256_update(&ctx, text1, text1_len);
33+
rcutils_sha256_final(&ctx, buf);
34+
35+
ASSERT_EQ(0, memcmp(expected_hash1, buf, RCUTILS_SHA256_BLOCK_SIZE));
36+
}
37+
38+
TEST(TestSHA256, test_text2) {
39+
uint8_t text2[] = {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"};
40+
size_t text2_len = sizeof(text2) - 1;
41+
uint8_t expected_hash2[RCUTILS_SHA256_BLOCK_SIZE] = {
42+
0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
43+
0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
44+
0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
45+
0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1};
46+
uint8_t buf[RCUTILS_SHA256_BLOCK_SIZE];
47+
48+
rcutils_sha256_ctx_t ctx;
49+
rcutils_sha256_init(&ctx);
50+
rcutils_sha256_update(&ctx, text2, text2_len);
51+
rcutils_sha256_final(&ctx, buf);
52+
53+
ASSERT_EQ(0, memcmp(expected_hash2, buf, RCUTILS_SHA256_BLOCK_SIZE));
54+
}
55+
56+
TEST(TestSHA256, test_multi_update) {
57+
uint8_t text[] = {"aaaaaaaaaa"};
58+
size_t text_len = sizeof(text) - 1;
59+
60+
uint8_t expected_hash[RCUTILS_SHA256_BLOCK_SIZE] = {
61+
0x28, 0x16, 0x59, 0x78, 0x88, 0xe4, 0xa0, 0xd3,
62+
0xa3, 0x6b, 0x82, 0xb8, 0x33, 0x16, 0xab, 0x32,
63+
0x68, 0x0e, 0xb8, 0xf0, 0x0f, 0x8c, 0xd3, 0xb9,
64+
0x04, 0xd6, 0x81, 0x24, 0x6d, 0x28, 0x5a, 0x0e};
65+
uint8_t buf[RCUTILS_SHA256_BLOCK_SIZE];
66+
67+
rcutils_sha256_ctx_t ctx;
68+
rcutils_sha256_init(&ctx);
69+
for (int i = 0; i < 10; i++) {
70+
rcutils_sha256_update(&ctx, text, text_len);
71+
}
72+
rcutils_sha256_final(&ctx, buf);
73+
74+
ASSERT_EQ(0, memcmp(expected_hash, buf, RCUTILS_SHA256_BLOCK_SIZE));
75+
}

0 commit comments

Comments
 (0)