nbos

nbos

https://git.tonybtw.com/nbos.git git://git.tonybtw.com/nbos.git
6,510 bytes raw
1
#define _GNU_SOURCE
2
#include "hash.h"
3
4
#include <fcntl.h>
5
#include <stdio.h>
6
#include <string.h>
7
#include <unistd.h>
8
9
static const uint32_t K[64] = {
10
    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
11
    0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
12
    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
13
    0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
14
    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
15
    0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
16
    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
17
    0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
18
    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
19
    0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
20
    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
21
    0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
22
    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
23
    0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
24
    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
25
    0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
26
};
27
28
#define ROTR(x, n)   (((x) >> (n)) | ((x) << (32 - (n))))
29
#define CH(x, y, z)  (((x) & (y)) ^ (~(x) & (z)))
30
#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
31
#define BSIG0(x)     (ROTR(x,  2) ^ ROTR(x, 13) ^ ROTR(x, 22))
32
#define BSIG1(x)     (ROTR(x,  6) ^ ROTR(x, 11) ^ ROTR(x, 25))
33
#define SSIG0(x)     (ROTR(x,  7) ^ ROTR(x, 18) ^ ((x) >>  3))
34
#define SSIG1(x)     (ROTR(x, 17) ^ ROTR(x, 19) ^ ((x) >> 10))
35
36
static void sha256_compress(sha256_ctx *ctx, const uint8_t block[64]) {
37
    uint32_t W[64];
38
    uint32_t a, b, c, d, e, f, g, h;
39
    uint32_t T1, T2;
40
41
    for (int i = 0; i < 16; i++) {
42
        W[i] = ((uint32_t)block[i * 4]     << 24) |
43
               ((uint32_t)block[i * 4 + 1] << 16) |
44
               ((uint32_t)block[i * 4 + 2] <<  8) |
45
               ((uint32_t)block[i * 4 + 3]);
46
    }
47
    for (int i = 16; i < 64; i++) {
48
        W[i] = SSIG1(W[i - 2]) + W[i - 7] + SSIG0(W[i - 15]) + W[i - 16];
49
    }
50
51
    a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3];
52
    e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7];
53
54
    for (int i = 0; i < 64; i++) {
55
        T1 = h + BSIG1(e) + CH(e, f, g) + K[i] + W[i];
56
        T2 = BSIG0(a) + MAJ(a, b, c);
57
        h = g; g = f; f = e; e = d + T1;
58
        d = c; c = b; b = a; a = T1 + T2;
59
    }
60
61
    ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d;
62
    ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h;
63
}
64
65
/**
66
 * sha256_init() - Initialize a SHA-256 hashing context.
67
 * @ctx: Context to initialize.
68
 */
69
void sha256_init(sha256_ctx *ctx) {
70
    ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85;
71
    ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a;
72
    ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c;
73
    ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19;
74
    ctx->bitcount = 0;
75
    ctx->buffer_used = 0;
76
}
77
78
/**
79
 * sha256_update() - Feed data into a SHA-256 context.
80
 * @ctx: Context previously initialized with sha256_init().
81
 * @data: Bytes to hash.
82
 * @len: Length of @data in bytes.
83
 */
84
void sha256_update(sha256_ctx *ctx, const void *data, size_t len) {
85
    const uint8_t *p = data;
86
    while (len > 0) {
87
        size_t n = 64 - ctx->buffer_used;
88
        if (n > len) n = len;
89
        memcpy(ctx->buffer + ctx->buffer_used, p, n);
90
        ctx->buffer_used += n;
91
        p   += n;
92
        len -= n;
93
        if (ctx->buffer_used == 64) {
94
            sha256_compress(ctx, ctx->buffer);
95
            ctx->bitcount += 512;
96
            ctx->buffer_used = 0;
97
        }
98
    }
99
}
100
101
/**
102
 * sha256_final() - Pad and finalize, writing the digest.
103
 * @ctx: Context to finalize. Caller must not reuse without re-init.
104
 * @out: 32-byte buffer to receive the digest.
105
 */
106
void sha256_final(sha256_ctx *ctx, uint8_t out[32]) {
107
    ctx->bitcount += (uint64_t)ctx->buffer_used * 8;
108
    ctx->buffer[ctx->buffer_used++] = 0x80;
109
    if (ctx->buffer_used > 56) {
110
        while (ctx->buffer_used < 64) ctx->buffer[ctx->buffer_used++] = 0;
111
        sha256_compress(ctx, ctx->buffer);
112
        ctx->buffer_used = 0;
113
    }
114
    while (ctx->buffer_used < 56) ctx->buffer[ctx->buffer_used++] = 0;
115
    for (int i = 7; i >= 0; i--) {
116
        ctx->buffer[ctx->buffer_used++] = (uint8_t)(ctx->bitcount >> (i * 8));
117
    }
118
    sha256_compress(ctx, ctx->buffer);
119
120
    for (int i = 0; i < 8; i++) {
121
        out[i * 4]     = (uint8_t)(ctx->state[i] >> 24);
122
        out[i * 4 + 1] = (uint8_t)(ctx->state[i] >> 16);
123
        out[i * 4 + 2] = (uint8_t)(ctx->state[i] >>  8);
124
        out[i * 4 + 3] = (uint8_t)(ctx->state[i]);
125
    }
126
}
127
128
/**
129
 * sha256_hash() - One-shot SHA-256 over a single buffer.
130
 * @data: Bytes to hash.
131
 * @len: Length of @data in bytes.
132
 * @out: 32-byte buffer to receive the digest.
133
 */
134
void sha256_hash(const void *data, size_t len, uint8_t out[32]) {
135
    sha256_ctx ctx;
136
    sha256_init(&ctx);
137
    sha256_update(&ctx, data, len);
138
    sha256_final(&ctx, out);
139
}
140
141
/**
142
 * sha256_hex() - Hex-encode a SHA-256 digest.
143
 * @digest: 32-byte digest.
144
 * @out: 65-byte buffer to receive the lowercase hex string and NUL.
145
 */
146
void sha256_hex(const uint8_t digest[32], char out[65]) {
147
    static const char hex[] = "0123456789abcdef";
148
    for (int i = 0; i < 32; i++) {
149
        out[i * 2]     = hex[digest[i] >> 4];
150
        out[i * 2 + 1] = hex[digest[i] & 0xf];
151
    }
152
    out[64] = '\0';
153
}
154
155
/**
156
 * sha256_verify_file() - Compare a file's SHA-256 against an expected hex.
157
 * @path: Filesystem path to read.
158
 * @expected_hex: Expected lowercase hex digest, or NULL to skip comparison.
159
 * @actual_hex: Optional 65-byte buffer to receive the computed hex digest.
160
 *
161
 * Return: true if the file hashes to @expected_hex (or @expected_hex is NULL
162
 * and the file was readable); false on mismatch or I/O error.
163
 */
164
bool sha256_verify_file(
165
    const char *path,
166
    const char *expected_hex,
167
    char        actual_hex[65])
168
{
169
    int fd = open(path, O_RDONLY | O_CLOEXEC);
170
    if (fd < 0) return false;
171
172
    sha256_ctx ctx;
173
    sha256_init(&ctx);
174
175
    uint8_t buf[8192];
176
    for (;;) {
177
        ssize_t n = read(fd, buf, sizeof(buf));
178
        if (n < 0) {
179
            close(fd);
180
            return false;
181
        }
182
        if (n == 0) break;
183
        sha256_update(&ctx, buf, (size_t)n);
184
    }
185
    close(fd);
186
187
    uint8_t digest[32];
188
    sha256_final(&ctx, digest);
189
190
    char hex[65];
191
    sha256_hex(digest, hex);
192
    if (actual_hex != nullptr) memcpy(actual_hex, hex, 65);
193
194
    if (expected_hex == nullptr) return true;
195
    return strcmp(hex, expected_hex) == 0;
196
}