Line data Source code
1 : /* SPDX-License-Identifier: MIT OR GPL-3.0-only */
2 : /* scram.c
3 : * strophe XMPP client library
4 : *
5 : * SCRAM-SHA1 helper functions according to RFC5802
6 : * HMAC-SHA1 implementation according to RFC2104
7 : *
8 : * Copyright (C) 2013 Dmitry Podgorny <pasis.ua@gmail.com>
9 : *
10 : * This software is provided AS-IS with no warranty, either express
11 : * or implied.
12 : *
13 : * This program is dual licensed under the MIT or GPLv3 licenses.
14 : */
15 :
16 : /** @file
17 : * SCRAM-SHA1 helper functions.
18 : */
19 :
20 : #include <assert.h>
21 : #include <string.h>
22 :
23 : #include "common.h"
24 : #include "sha1.h"
25 : #include "sha256.h"
26 : #include "sha512.h"
27 : #include "ostypes.h"
28 :
29 : #include "scram.h"
30 :
31 : #define HMAC_BLOCK_SIZE_MAX 128
32 :
33 : static const uint8_t ipad = 0x36;
34 : static const uint8_t opad = 0x5C;
35 :
36 : const struct hash_alg scram_sha1 = {
37 : "SCRAM-SHA-1",
38 : SASL_MASK_SCRAMSHA1,
39 : SHA1_DIGEST_SIZE,
40 : (void (*)(const uint8_t *, size_t, uint8_t *))crypto_SHA1,
41 : (void (*)(void *))crypto_SHA1_Init,
42 : (void (*)(void *, const uint8_t *, size_t))crypto_SHA1_Update,
43 : (void (*)(void *, uint8_t *))crypto_SHA1_Final};
44 :
45 : const struct hash_alg scram_sha1_plus = {
46 : "SCRAM-SHA-1-PLUS",
47 : SASL_MASK_SCRAMSHA1_PLUS,
48 : SHA1_DIGEST_SIZE,
49 : (void (*)(const uint8_t *, size_t, uint8_t *))crypto_SHA1,
50 : (void (*)(void *))crypto_SHA1_Init,
51 : (void (*)(void *, const uint8_t *, size_t))crypto_SHA1_Update,
52 : (void (*)(void *, uint8_t *))crypto_SHA1_Final};
53 :
54 : const struct hash_alg scram_sha256 = {
55 : "SCRAM-SHA-256",
56 : SASL_MASK_SCRAMSHA256,
57 : SHA256_DIGEST_SIZE,
58 : (void (*)(const uint8_t *, size_t, uint8_t *))sha256_hash,
59 : (void (*)(void *))sha256_init,
60 : (void (*)(void *, const uint8_t *, size_t))sha256_process,
61 : (void (*)(void *, uint8_t *))sha256_done};
62 :
63 : const struct hash_alg scram_sha256_plus = {
64 : "SCRAM-SHA-256-PLUS",
65 : SASL_MASK_SCRAMSHA256_PLUS,
66 : SHA256_DIGEST_SIZE,
67 : (void (*)(const uint8_t *, size_t, uint8_t *))sha256_hash,
68 : (void (*)(void *))sha256_init,
69 : (void (*)(void *, const uint8_t *, size_t))sha256_process,
70 : (void (*)(void *, uint8_t *))sha256_done};
71 :
72 : const struct hash_alg scram_sha512 = {
73 : "SCRAM-SHA-512",
74 : SASL_MASK_SCRAMSHA512,
75 : SHA512_DIGEST_SIZE,
76 : (void (*)(const uint8_t *, size_t, uint8_t *))sha512_hash,
77 : (void (*)(void *))sha512_init,
78 : (void (*)(void *, const uint8_t *, size_t))sha512_process,
79 : (void (*)(void *, uint8_t *))sha512_done};
80 :
81 : /* The order of this list defines the order in which the SCRAM algorithms are
82 : * tried if the server supports them.
83 : * Their order is derived from
84 : * https://datatracker.ietf.org/doc/html/draft-ietf-kitten-password-storage
85 : */
86 : const struct hash_alg *scram_algs[] = {
87 : /* *1 */
88 : &scram_sha256_plus,
89 : /* *1 */
90 : &scram_sha1_plus,
91 : /* *1 */
92 : &scram_sha512,
93 : /* *1 */
94 : &scram_sha256,
95 : /* *1 */
96 : &scram_sha1,
97 : };
98 : /* *1 - I want to use clang-format here, but by default it will put multiple
99 : * elements per line if there's no comment. Currently clang-format also doesn't
100 : * have an option to enforce it to behave like that, besides by putting comments
101 : * between each element (or I couldn't find a way to do it). In order to prevent
102 : * clang-format from re-formatting the array I added the comments above and
103 : * wrote this lengthy description.
104 : */
105 : const size_t scram_algs_num = sizeof(scram_algs) / sizeof(scram_algs[0]);
106 :
107 : union common_hash_ctx {
108 : SHA1_CTX sha1;
109 : sha256_context sha256;
110 : sha512_context sha512;
111 : };
112 :
113 0 : static void crypto_HMAC(const struct hash_alg *alg,
114 : const uint8_t *key,
115 : size_t key_len,
116 : const uint8_t *text,
117 : size_t len,
118 : uint8_t *digest)
119 : {
120 0 : uint8_t key_pad[HMAC_BLOCK_SIZE_MAX];
121 0 : uint8_t key_ipad[HMAC_BLOCK_SIZE_MAX];
122 0 : uint8_t key_opad[HMAC_BLOCK_SIZE_MAX];
123 0 : uint8_t sha_digest[SCRAM_DIGEST_SIZE];
124 0 : size_t blocksize;
125 0 : size_t i;
126 0 : union common_hash_ctx ctx;
127 :
128 0 : assert(alg->digest_size <= HMAC_BLOCK_SIZE_MAX);
129 0 : blocksize = alg->digest_size < 48 ? 64 : 128;
130 :
131 0 : memset(key_pad, 0, blocksize);
132 0 : if (key_len <= blocksize) {
133 0 : memcpy(key_pad, key, key_len);
134 : } else {
135 : /* according to RFC2104 */
136 0 : alg->hash(key, key_len, key_pad);
137 : }
138 :
139 0 : for (i = 0; i < blocksize; i++) {
140 0 : key_ipad[i] = key_pad[i] ^ ipad;
141 0 : key_opad[i] = key_pad[i] ^ opad;
142 : }
143 :
144 0 : alg->init((void *)&ctx);
145 0 : alg->update((void *)&ctx, key_ipad, blocksize);
146 0 : alg->update((void *)&ctx, text, len);
147 0 : alg->final((void *)&ctx, sha_digest);
148 :
149 0 : alg->init((void *)&ctx);
150 0 : alg->update((void *)&ctx, key_opad, blocksize);
151 0 : alg->update((void *)&ctx, sha_digest, alg->digest_size);
152 0 : alg->final((void *)&ctx, digest);
153 0 : }
154 :
155 0 : static void SCRAM_Hi(const struct hash_alg *alg,
156 : const uint8_t *text,
157 : size_t len,
158 : const uint8_t *salt,
159 : size_t salt_len,
160 : uint32_t i,
161 : uint8_t *digest)
162 : {
163 0 : size_t k;
164 0 : uint32_t j;
165 0 : uint8_t tmp[128];
166 :
167 0 : static uint8_t int1[] = {0x0, 0x0, 0x0, 0x1};
168 :
169 : /* assume salt + INT(1) isn't longer than sizeof(tmp) */
170 0 : assert(salt_len <= sizeof(tmp) - sizeof(int1));
171 :
172 0 : memset(digest, 0, alg->digest_size);
173 0 : if (i == 0) {
174 0 : return;
175 : }
176 :
177 0 : memcpy(tmp, salt, salt_len);
178 0 : memcpy(&tmp[salt_len], int1, sizeof(int1));
179 :
180 : /* 'text' for Hi is a 'key' for HMAC */
181 0 : crypto_HMAC(alg, text, len, tmp, salt_len + sizeof(int1), digest);
182 0 : memcpy(tmp, digest, alg->digest_size);
183 :
184 0 : for (j = 1; j < i; j++) {
185 0 : crypto_HMAC(alg, text, len, tmp, alg->digest_size, tmp);
186 0 : for (k = 0; k < alg->digest_size; k++) {
187 0 : digest[k] ^= tmp[k];
188 : }
189 : }
190 : }
191 :
192 0 : void SCRAM_ClientKey(const struct hash_alg *alg,
193 : const uint8_t *password,
194 : size_t len,
195 : const uint8_t *salt,
196 : size_t salt_len,
197 : uint32_t i,
198 : uint8_t *key)
199 : {
200 0 : uint8_t salted[SCRAM_DIGEST_SIZE];
201 :
202 : /* XXX: Normalize(password) is omitted */
203 :
204 0 : SCRAM_Hi(alg, password, len, salt, salt_len, i, salted);
205 0 : crypto_HMAC(alg, salted, alg->digest_size, (uint8_t *)"Client Key",
206 : strlen("Client Key"), key);
207 0 : }
208 :
209 0 : void SCRAM_ClientSignature(const struct hash_alg *alg,
210 : const uint8_t *ClientKey,
211 : const uint8_t *AuthMessage,
212 : size_t len,
213 : uint8_t *sign)
214 : {
215 0 : uint8_t stored[SCRAM_DIGEST_SIZE];
216 :
217 0 : alg->hash(ClientKey, alg->digest_size, stored);
218 0 : crypto_HMAC(alg, stored, alg->digest_size, AuthMessage, len, sign);
219 0 : }
220 :
221 0 : void SCRAM_ClientProof(const struct hash_alg *alg,
222 : const uint8_t *ClientKey,
223 : const uint8_t *ClientSignature,
224 : uint8_t *proof)
225 : {
226 0 : size_t i;
227 0 : for (i = 0; i < alg->digest_size; i++) {
228 0 : proof[i] = ClientKey[i] ^ ClientSignature[i];
229 : }
230 0 : }
|