Line data Source code
1 : /* scram.c
2 : * strophe XMPP client library
3 : *
4 : * SCRAM-SHA1 helper functions according to RFC5802
5 : * HMAC-SHA1 implementation according to RFC2104
6 : *
7 : * Copyright (C) 2013 Dmitry Podgorny <pasis.ua@gmail.com>
8 : *
9 : * This software is provided AS-IS with no warranty, either express
10 : * or implied.
11 : *
12 : * This program is dual licensed under the MIT and GPLv3 licenses.
13 : */
14 :
15 : /** @file
16 : * SCRAM-SHA1 helper functions.
17 : */
18 :
19 : #include <assert.h>
20 : #include <string.h>
21 :
22 : #include "common.h"
23 : #include "sha1.h"
24 : #include "sha256.h"
25 : #include "sha512.h"
26 : #include "ostypes.h"
27 :
28 : #include "scram.h"
29 :
30 : #define HMAC_BLOCK_SIZE_MAX 128
31 :
32 : static const uint8_t ipad = 0x36;
33 : static const uint8_t opad = 0x5C;
34 :
35 : const struct hash_alg scram_sha1 = {
36 : "SCRAM-SHA-1",
37 : SASL_MASK_SCRAMSHA1,
38 : SHA1_DIGEST_SIZE,
39 : (void (*)(const uint8_t *, size_t, uint8_t *))crypto_SHA1,
40 : (void (*)(void *))crypto_SHA1_Init,
41 : (void (*)(void *, const uint8_t *, size_t))crypto_SHA1_Update,
42 : (void (*)(void *, uint8_t *))crypto_SHA1_Final};
43 :
44 : const struct hash_alg scram_sha256 = {
45 : "SCRAM-SHA-256",
46 : SASL_MASK_SCRAMSHA256,
47 : SHA256_DIGEST_SIZE,
48 : (void (*)(const uint8_t *, size_t, uint8_t *))sha256_hash,
49 : (void (*)(void *))sha256_init,
50 : (void (*)(void *, const uint8_t *, size_t))sha256_process,
51 : (void (*)(void *, uint8_t *))sha256_done};
52 :
53 : const struct hash_alg scram_sha512 = {
54 : "SCRAM-SHA-512",
55 : SASL_MASK_SCRAMSHA512,
56 : SHA512_DIGEST_SIZE,
57 : (void (*)(const uint8_t *, size_t, uint8_t *))sha512_hash,
58 : (void (*)(void *))sha512_init,
59 : (void (*)(void *, const uint8_t *, size_t))sha512_process,
60 : (void (*)(void *, uint8_t *))sha512_done};
61 :
62 : union common_hash_ctx {
63 : SHA1_CTX sha1;
64 : sha256_context sha256;
65 : sha512_context sha512;
66 : };
67 :
68 0 : static void crypto_HMAC(const struct hash_alg *alg,
69 : const uint8_t *key,
70 : size_t key_len,
71 : const uint8_t *text,
72 : size_t len,
73 : uint8_t *digest)
74 : {
75 0 : uint8_t key_pad[HMAC_BLOCK_SIZE_MAX];
76 0 : uint8_t key_ipad[HMAC_BLOCK_SIZE_MAX];
77 0 : uint8_t key_opad[HMAC_BLOCK_SIZE_MAX];
78 0 : uint8_t sha_digest[SCRAM_DIGEST_SIZE];
79 0 : size_t blocksize;
80 0 : size_t i;
81 0 : union common_hash_ctx ctx;
82 :
83 0 : assert(alg->digest_size <= HMAC_BLOCK_SIZE_MAX);
84 0 : blocksize = alg->digest_size < 48 ? 64 : 128;
85 :
86 0 : memset(key_pad, 0, blocksize);
87 0 : if (key_len <= blocksize) {
88 0 : memcpy(key_pad, key, key_len);
89 : } else {
90 : /* according to RFC2104 */
91 0 : alg->hash(key, key_len, key_pad);
92 : }
93 :
94 0 : for (i = 0; i < blocksize; i++) {
95 0 : key_ipad[i] = key_pad[i] ^ ipad;
96 0 : key_opad[i] = key_pad[i] ^ opad;
97 : }
98 :
99 0 : alg->init((void *)&ctx);
100 0 : alg->update((void *)&ctx, key_ipad, blocksize);
101 0 : alg->update((void *)&ctx, text, len);
102 0 : alg->final((void *)&ctx, sha_digest);
103 :
104 0 : alg->init((void *)&ctx);
105 0 : alg->update((void *)&ctx, key_opad, blocksize);
106 0 : alg->update((void *)&ctx, sha_digest, alg->digest_size);
107 0 : alg->final((void *)&ctx, digest);
108 0 : }
109 :
110 0 : static void SCRAM_Hi(const struct hash_alg *alg,
111 : const uint8_t *text,
112 : size_t len,
113 : const uint8_t *salt,
114 : size_t salt_len,
115 : uint32_t i,
116 : uint8_t *digest)
117 : {
118 0 : size_t k;
119 0 : uint32_t j;
120 0 : uint8_t tmp[128];
121 :
122 0 : static uint8_t int1[] = {0x0, 0x0, 0x0, 0x1};
123 :
124 : /* assume salt + INT(1) isn't longer than sizeof(tmp) */
125 0 : assert(salt_len <= sizeof(tmp) - sizeof(int1));
126 :
127 0 : memset(digest, 0, alg->digest_size);
128 0 : if (i == 0) {
129 0 : return;
130 : }
131 :
132 0 : memcpy(tmp, salt, salt_len);
133 0 : memcpy(&tmp[salt_len], int1, sizeof(int1));
134 :
135 : /* 'text' for Hi is a 'key' for HMAC */
136 0 : crypto_HMAC(alg, text, len, tmp, salt_len + sizeof(int1), digest);
137 0 : memcpy(tmp, digest, alg->digest_size);
138 :
139 0 : for (j = 1; j < i; j++) {
140 0 : crypto_HMAC(alg, text, len, tmp, alg->digest_size, tmp);
141 0 : for (k = 0; k < alg->digest_size; k++) {
142 0 : digest[k] ^= tmp[k];
143 : }
144 : }
145 : }
146 :
147 0 : void SCRAM_ClientKey(const struct hash_alg *alg,
148 : const uint8_t *password,
149 : size_t len,
150 : const uint8_t *salt,
151 : size_t salt_len,
152 : uint32_t i,
153 : uint8_t *key)
154 : {
155 0 : uint8_t salted[SCRAM_DIGEST_SIZE];
156 :
157 : /* XXX: Normalize(password) is omitted */
158 :
159 0 : SCRAM_Hi(alg, password, len, salt, salt_len, i, salted);
160 0 : crypto_HMAC(alg, salted, alg->digest_size, (uint8_t *)"Client Key",
161 : strlen("Client Key"), key);
162 0 : }
163 :
164 0 : void SCRAM_ClientSignature(const struct hash_alg *alg,
165 : const uint8_t *ClientKey,
166 : const uint8_t *AuthMessage,
167 : size_t len,
168 : uint8_t *sign)
169 : {
170 0 : uint8_t stored[SCRAM_DIGEST_SIZE];
171 :
172 0 : alg->hash(ClientKey, alg->digest_size, stored);
173 0 : crypto_HMAC(alg, stored, alg->digest_size, AuthMessage, len, sign);
174 0 : }
175 :
176 0 : void SCRAM_ClientProof(const struct hash_alg *alg,
177 : const uint8_t *ClientKey,
178 : const uint8_t *ClientSignature,
179 : uint8_t *proof)
180 : {
181 0 : size_t i;
182 0 : for (i = 0; i < alg->digest_size; i++) {
183 0 : proof[i] = ClientKey[i] ^ ClientSignature[i];
184 : }
185 0 : }
|