Last active
February 17, 2017 07:12
-
-
Save jn0/6fced5fb655cd374d58d747bab5b1467 to your computer and use it in GitHub Desktop.
Trying to make mkpasswd(1) SHA-512 compatible JS code...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Stolen from https://www.akkadia.org/drepper/SHA-crypt.txt for references. | |
Unix crypt using SHA-256 and SHA-512 | |
------------------------------------ | |
Author: Ulrich Drepper <drepper@gmail.com> | |
Version: 0.6 2016-8-31 | |
Only editorial changes since 0.4 | |
Discussion Group: | |
Josh Bressers, Red Hat | |
Mark Brown, IBM | |
David Clissold, IBM | |
Don Cragun, Sun | |
Casper Dik, Sun | |
Ulrich Drepper, Red Hat | |
Larry Dwyer, HP | |
Steve Grubb, Red Hat | |
Ravi A Shankar, IBM | |
Borislav Simov, HP | |
Various Unix crypt implementations have been MD5 as an alternative | |
method to the traditional DES encryption for the one-way conversion | |
needed. Both DES and MD5 are deemed insecure for their primary | |
purpose and by association their use in password encryption is put | |
into question. In addition, the produced output for both DES and MD5 | |
has a short length which makes it possible to construct rainbow tables. | |
Requests for a better solution to the problem have been heard for some | |
time. Security departments in companies are trying to phase out all | |
uses of MD5. They demand a method which is officially sanctioned. | |
For US-based users this means tested by the NIST. | |
This rules out the use of another already implemented method with | |
limited spread: the use of the Blowfish encryption method. The choice | |
comes down to tested encryption (3DES, AES) or hash sums (the SHA | |
family). | |
Encryption-based solution do not seem to provide better security (no | |
proof to the contrary) and the higher CPU requirements can be | |
compensated for by adding more complexity to a hash sum-based | |
solution. This is why the decision has been made by a group of Unix | |
and Linux vendors to persue this route. | |
The SHA hash sum functions are well tested. By choosing the SHA-256 | |
and SHA-512 algorithms the produced output is 32 or 64 bytes | |
respectively in size. This fulfills the requirement for a large | |
output set which makes rainbow tables less useful to impossible, at | |
least for the next years. | |
The algorithm used by the MD5-based password hashing is generally | |
deemed safe as well so there is no big problem with using a similar | |
algorithm for the SHA-based password hashing solutions. Parts of the | |
algorithm have been changed and in one instance what is thought to be | |
a mistake in the MD5-based implementation has been fixed. | |
The integration into existing systems is easy if those systems already | |
support the MD5-based solution. Ever since the introduction of the | |
MD5-based method an extended password format is in used: | |
$<ID>$<SALT>$<PWD> | |
If the password is not of this form it is an old-style DES-encrypted | |
password. If the password has this form the ID identifies the method | |
used and this then determines how the rest of the password string is | |
interpreted. So far the following ID values are in use: | |
ID | Method | |
------------------------------- | |
1 | MD5 (Linux, BSD) | |
2a | Blowfish (OpenBSD) | |
md5 | Sun MD5 | |
For the new SHA-256 and SHA-512 methods the following values are | |
selected: | |
ID | Method | |
------------------------------- | |
5 | SHA-256 | |
6 | SHA-512 | |
For the SHA-based methods the SALT string can be a simple string of | |
which up to 16 characters are used. The MD5-based implementation used | |
up to eight characters.. It was decided to allow one extension which | |
follows an invention Sun implemented in their pluggable crypt | |
implementation. If the SALT strings starts with | |
rounds=<N>$ | |
where N is an unsigned decimal number the numeric value of N is used | |
to modify the algorithm used. As will be explained later, the | |
SHA-based algorithm contains a loop which can be run an arbitrary | |
number of times. The more rounds are performed the higher the CPU | |
requirements are. This is a safety mechanism which might help | |
countering brute-force attacks in the face of increasing computing | |
power. | |
The default number of rounds for both algorithms is 5,000. To ensure | |
minimal security and stability on the other hand minimum and maximum | |
values for N are enforced: | |
minimum for N = 1,000 | |
maximum for N = 999,999,999 | |
Any selection of N below the minimum will cause the use of 1,000 | |
rounds and a value of 1 billion and higher will cause 999,999,999 | |
rounds to be used. In these cases the output string produced by the | |
crypt function will not have the same salt as the input salt string | |
since the correct, limited rounds value is used in the output. | |
The PWD part of the password string is the actual computed password. | |
The size of this string is fixed: | |
SHA-256 43 characters | |
SHA-512 86 characters | |
The output consists of the base64-encoded digest. The maximum length | |
of a password string is therefore (excluding final NUL byte in the C | |
representation): | |
SHA-256 80 characters | |
SHA-512 123 characters | |
The input string used for the salt parameter of the crypt function can | |
potentially be much longer. But since the salt string is truncated to | |
at most 16 characters the size of the output string is limited. | |
The algorithm used for the password hashing follows the one used in | |
the Linux/BSD MD5 implementation. The following is a description of | |
the algorithm where the differences are explicitly pointed out. Both, | |
the SHA-256 and the SHA-512 method, use the same algorithm. The only | |
difference, which is also a difference to the MD5 version, are all the | |
cases where an existing digest is used as input for another digest | |
computation. In this case the input size (i.e., the digest size) | |
varies. For MD5 the digest is 16 bytes, for SHA-256 it is 32 bytes, | |
and for SHA-512 it is 64 bytes. The following description will not | |
mention this difference further. | |
The algorithm using three primitives for creating a hash digest: | |
- start a digest. This sets up the data structures and initial state | |
as required for the hash function | |
- add bytes to a digest. This can happen multiple times. Only when | |
the required number of bytes for a round of the hash function is | |
added will anything happen. If the required number of bytes is not | |
yet reached the bytes will simply be queued up. For SHA-256 and | |
SHA-512 the respective sizes are 64 and 128 bytes. | |
- finish the context. This operation causes the currently queued | |
bytes to be padded according to the hash function specification and | |
the result is processed. The final digest is computed and made | |
available to the use. | |
When the algorithm talks about adding the salt string this really | |
means adding the salt string truncated to 16 characters. | |
When the algorithm talks about adding a string the terminating NUL | |
byte of the C presentation of the string in NOT added. | |
Algorithm for crypt using SHA-256/SHA-512: | |
1. start digest A | |
2. the password string is added to digest A | |
3. the salt string is added to digest A. This is just the salt string | |
itself without the enclosing '$', without the magic prefix $5$ and | |
$6$ respectively and without the rounds=<N> specification. | |
NB: the MD5 algorithm did add the $1$ prefix. This is not deemed | |
necessary since it is a constant string and does not add security | |
and /possibly/ allows a plain text attack. Since the rounds=<N> | |
specification should never be added this would also create an | |
inconsistency. | |
4. start digest B | |
5. add the password to digest B | |
6. add the salt string to digest B | |
7. add the password again to digest B | |
8. finish digest B | |
9. For each block of 32 or 64 bytes in the password string (excluding | |
the terminating NUL in the C representation), add digest B to digest A | |
10. For the remaining N bytes of the password string add the first | |
N bytes of digest B to digest A | |
11. For each bit of the binary representation of the length of the | |
password string up to and including the highest 1-digit, starting | |
from to lowest bit position (numeric value 1): | |
a) for a 1-digit add digest B to digest A | |
b) for a 0-digit add the password string | |
NB: this step differs significantly from the MD5 algorithm. It | |
adds more randomness. | |
12. finish digest A | |
13. start digest DP | |
14. for every byte in the password (excluding the terminating NUL byte | |
in the C representation of the string) | |
add the password to digest DP | |
15. finish digest DP | |
16. produce byte sequence P of the same length as the password where | |
a) for each block of 32 or 64 bytes of length of the password string | |
the entire digest DP is used | |
b) for the remaining N (up to 31 or 63) bytes use the first N | |
bytes of digest DP | |
17. start digest DS | |
18. repeat the following 16+A[0] times, where A[0] represents the first | |
byte in digest A interpreted as an 8-bit unsigned value | |
add the salt to digest DS | |
19. finish digest DS | |
20. produce byte sequence S of the same length as the salt string where | |
a) for each block of 32 or 64 bytes of length of the salt string | |
the entire digest DS is used | |
b) for the remaining N (up to 31 or 63) bytes use the first N | |
bytes of digest DS | |
21. repeat a loop according to the number specified in the rounds=<N> | |
specification in the salt (or the default value if none is | |
present). Each round is numbered, starting with 0 and up to N-1. | |
The loop uses a digest as input. In the first round it is the | |
digest produced in step 12. In the latter steps it is the digest | |
produced in step 21.h of the previous round. The following text | |
uses the notation "digest A/C" to describe this behavior. | |
a) start digest C | |
b) for odd round numbers add the byte sequense P to digest C | |
c) for even round numbers add digest A/C | |
d) for all round numbers not divisible by 3 add the byte sequence S | |
e) for all round numbers not divisible by 7 add the byte sequence P | |
f) for odd round numbers add digest A/C | |
g) for even round numbers add the byte sequence P | |
h) finish digest C. | |
22. Produce the output string. This is an ASCII string of the maximum | |
size specified above, consisting of multiple pieces: | |
a) the salt prefix, $5$ or $6$ respectively | |
b) the rounds=<N> specification, if one was present in the input | |
salt string. A trailing '$' is added in this case to separate | |
the rounds specification from the following text. | |
c) the salt string truncated to 16 characters | |
d) a '$' character | |
e) the base-64 encoded final C digest. The encoding used is as | |
follows: | |
111111111122222222223333333333444444444455555555556666 | |
0123456789012345678901234567890123456789012345678901234567890123 | |
---------------------------------------------------------------- | |
./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz | |
Each group of three bytes from the digest produces four | |
characters as output: | |
1. character: the six low bits of the first byte | |
2. character: the two high bits of the first byte and the | |
four low bytes from the second byte | |
3. character: the four high bits from the second byte and | |
the two low bits from the third byte | |
4. character: the six high bits from the third byte | |
The groups of three bytes are as follows (in this sequence). | |
These are the indices into the byte array containing the | |
digest, starting with index 0. For the last group there are | |
not enough bytes left in the digest and the value zero is used | |
in its place. This group also produces only three or two | |
characters as output for SHA-256 and SHA-512 respectively. | |
For SHA-256: | |
#3 #2 #1 <-- byte number in group | |
0 - 10 - 20 | |
21 - 1 - 11 | |
12 - 22 - 2 | |
3 - 13 - 23 | |
24 - 4 - 14 | |
15 - 25 - 5 | |
6 - 16 - 26 | |
27 - 7 - 17 | |
18 - 28 - 8 | |
9 - 19 - 29 | |
* - 31 - 30 | |
For SHA-512: | |
#3 #2 #1 <-- byte number in group | |
0 - 21 - 42 | |
22 - 43 - 1 | |
44 - 2 - 23 | |
3 - 24 - 45 | |
25 - 46 - 4 | |
47 - 5 - 26 | |
6 - 27 - 48 | |
28 - 49 - 7 | |
50 - 8 - 29 | |
9 - 30 - 51 | |
31 - 52 - 10 | |
53 - 11 - 32 | |
12 - 33 - 54 | |
34 - 55 - 13 | |
56 - 14 - 35 | |
15 - 36 - 57 | |
37 - 58 - 16 | |
59 - 17 - 38 | |
18 - 39 - 60 | |
40 - 61 - 19 | |
62 - 20 - 41 | |
* - * - 63 | |
The following are complete implementation of the crypt variants using | |
SHA-256 and SHA-512 respectively. The sources include a self test | |
which can be enabled by defining the macro TEST. | |
-------- sha256crypt.c ------------------------------------------------------ | |
/* SHA256-based Unix crypt implementation. | |
Released into the Public Domain by Ulrich Drepper <drepper@redhat.com>. */ | |
#include <endian.h> | |
#include <errno.h> | |
#include <limits.h> | |
#include <stdint.h> | |
#include <stdbool.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/param.h> | |
#include <sys/types.h> | |
/* Structure to save state of computation between the single steps. */ | |
struct sha256_ctx | |
{ | |
uint32_t H[8]; | |
uint32_t total[2]; | |
uint32_t buflen; | |
char buffer[128]; /* NB: always correctly aligned for uint32_t. */ | |
}; | |
#if __BYTE_ORDER == __LITTLE_ENDIAN | |
# define SWAP(n) \ | |
(((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) | |
#else | |
# define SWAP(n) (n) | |
#endif | |
/* This array contains the bytes used to pad the buffer to the next | |
64-byte boundary. (FIPS 180-2:5.1.1) */ | |
static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; | |
/* Constants for SHA256 from FIPS 180-2:4.2.2. */ | |
static const uint32_t K[64] = | |
{ | |
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, | |
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, | |
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, | |
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, | |
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, | |
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, | |
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, | |
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, | |
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, | |
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, | |
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, | |
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, | |
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, | |
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, | |
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, | |
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 | |
}; | |
/* Process LEN bytes of BUFFER, accumulating context into CTX. | |
It is assumed that LEN % 64 == 0. */ | |
static void | |
sha256_process_block (const void *buffer, size_t len, struct sha256_ctx *ctx) | |
{ | |
const uint32_t *words = buffer; | |
size_t nwords = len / sizeof (uint32_t); | |
uint32_t a = ctx->H[0]; | |
uint32_t b = ctx->H[1]; | |
uint32_t c = ctx->H[2]; | |
uint32_t d = ctx->H[3]; | |
uint32_t e = ctx->H[4]; | |
uint32_t f = ctx->H[5]; | |
uint32_t g = ctx->H[6]; | |
uint32_t h = ctx->H[7]; | |
/* First increment the byte count. FIPS 180-2 specifies the possible | |
length of the file up to 2^64 bits. Here we only compute the | |
number of bytes. Do a double word increment. */ | |
ctx->total[0] += len; | |
if (ctx->total[0] < len) | |
++ctx->total[1]; | |
/* Process all bytes in the buffer with 64 bytes in each round of | |
the loop. */ | |
while (nwords > 0) | |
{ | |
uint32_t W[64]; | |
uint32_t a_save = a; | |
uint32_t b_save = b; | |
uint32_t c_save = c; | |
uint32_t d_save = d; | |
uint32_t e_save = e; | |
uint32_t f_save = f; | |
uint32_t g_save = g; | |
uint32_t h_save = h; | |
/* Operators defined in FIPS 180-2:4.1.2. */ | |
#define Ch(x, y, z) ((x & y) ^ (~x & z)) | |
#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) | |
#define S0(x) (CYCLIC (x, 2) ^ CYCLIC (x, 13) ^ CYCLIC (x, 22)) | |
#define S1(x) (CYCLIC (x, 6) ^ CYCLIC (x, 11) ^ CYCLIC (x, 25)) | |
#define R0(x) (CYCLIC (x, 7) ^ CYCLIC (x, 18) ^ (x >> 3)) | |
#define R1(x) (CYCLIC (x, 17) ^ CYCLIC (x, 19) ^ (x >> 10)) | |
/* It is unfortunate that C does not provide an operator for | |
cyclic rotation. Hope the C compiler is smart enough. */ | |
#define CYCLIC(w, s) ((w >> s) | (w << (32 - s))) | |
/* Compute the message schedule according to FIPS 180-2:6.2.2 step 2. */ | |
for (unsigned int t = 0; t < 16; ++t) | |
{ | |
W[t] = SWAP (*words); | |
++words; | |
} | |
for (unsigned int t = 16; t < 64; ++t) | |
W[t] = R1 (W[t - 2]) + W[t - 7] + R0 (W[t - 15]) + W[t - 16]; | |
/* The actual computation according to FIPS 180-2:6.2.2 step 3. */ | |
for (unsigned int t = 0; t < 64; ++t) | |
{ | |
uint32_t T1 = h + S1 (e) + Ch (e, f, g) + K[t] + W[t]; | |
uint32_t T2 = S0 (a) + Maj (a, b, c); | |
h = g; | |
g = f; | |
f = e; | |
e = d + T1; | |
d = c; | |
c = b; | |
b = a; | |
a = T1 + T2; | |
} | |
/* Add the starting values of the context according to FIPS 180-2:6.2.2 | |
step 4. */ | |
a += a_save; | |
b += b_save; | |
c += c_save; | |
d += d_save; | |
e += e_save; | |
f += f_save; | |
g += g_save; | |
h += h_save; | |
/* Prepare for the next round. */ | |
nwords -= 16; | |
} | |
/* Put checksum in context given as argument. */ | |
ctx->H[0] = a; | |
ctx->H[1] = b; | |
ctx->H[2] = c; | |
ctx->H[3] = d; | |
ctx->H[4] = e; | |
ctx->H[5] = f; | |
ctx->H[6] = g; | |
ctx->H[7] = h; | |
} | |
/* Initialize structure containing state of computation. | |
(FIPS 180-2:5.3.2) */ | |
static void | |
sha256_init_ctx (struct sha256_ctx *ctx) | |
{ | |
ctx->H[0] = 0x6a09e667; | |
ctx->H[1] = 0xbb67ae85; | |
ctx->H[2] = 0x3c6ef372; | |
ctx->H[3] = 0xa54ff53a; | |
ctx->H[4] = 0x510e527f; | |
ctx->H[5] = 0x9b05688c; | |
ctx->H[6] = 0x1f83d9ab; | |
ctx->H[7] = 0x5be0cd19; | |
ctx->total[0] = ctx->total[1] = 0; | |
ctx->buflen = 0; | |
} | |
/* Process the remaining bytes in the internal buffer and the usual | |
prolog according to the standard and write the result to RESBUF. | |
IMPORTANT: On some systems it is required that RESBUF is correctly | |
aligned for a 32 bits value. */ | |
static void * | |
sha256_finish_ctx (struct sha256_ctx *ctx, void *resbuf) | |
{ | |
/* Take yet unprocessed bytes into account. */ | |
uint32_t bytes = ctx->buflen; | |
size_t pad; | |
/* Now count remaining bytes. */ | |
ctx->total[0] += bytes; | |
if (ctx->total[0] < bytes) | |
++ctx->total[1]; | |
pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; | |
memcpy (&ctx->buffer[bytes], fillbuf, pad); | |
/* Put the 64-bit file length in *bits* at the end of the buffer. */ | |
*(uint32_t *) &ctx->buffer[bytes + pad + 4] = SWAP (ctx->total[0] << 3); | |
*(uint32_t *) &ctx->buffer[bytes + pad] = SWAP ((ctx->total[1] << 3) | | |
(ctx->total[0] >> 29)); | |
/* Process last bytes. */ | |
sha256_process_block (ctx->buffer, bytes + pad + 8, ctx); | |
/* Put result from CTX in first 32 bytes following RESBUF. */ | |
for (unsigned int i = 0; i < 8; ++i) | |
((uint32_t *) resbuf)[i] = SWAP (ctx->H[i]); | |
return resbuf; | |
} | |
static void | |
sha256_process_bytes (const void *buffer, size_t len, struct sha256_ctx *ctx) | |
{ | |
/* When we already have some bits in our internal buffer concatenate | |
both inputs first. */ | |
if (ctx->buflen != 0) | |
{ | |
size_t left_over = ctx->buflen; | |
size_t add = 128 - left_over > len ? len : 128 - left_over; | |
memcpy (&ctx->buffer[left_over], buffer, add); | |
ctx->buflen += add; | |
if (ctx->buflen > 64) | |
{ | |
sha256_process_block (ctx->buffer, ctx->buflen & ~63, ctx); | |
ctx->buflen &= 63; | |
/* The regions in the following copy operation cannot overlap. */ | |
memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], | |
ctx->buflen); | |
} | |
buffer = (const char *) buffer + add; | |
len -= add; | |
} | |
/* Process available complete blocks. */ | |
if (len >= 64) | |
{ | |
/* To check alignment gcc has an appropriate operator. Other | |
compilers don't. */ | |
#if __GNUC__ >= 2 | |
# define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__ (uint32_t) != 0) | |
#else | |
# define UNALIGNED_P(p) (((uintptr_t) p) % sizeof (uint32_t) != 0) | |
#endif | |
if (UNALIGNED_P (buffer)) | |
while (len > 64) | |
{ | |
sha256_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); | |
buffer = (const char *) buffer + 64; | |
len -= 64; | |
} | |
else | |
{ | |
sha256_process_block (buffer, len & ~63, ctx); | |
buffer = (const char *) buffer + (len & ~63); | |
len &= 63; | |
} | |
} | |
/* Move remaining bytes into internal buffer. */ | |
if (len > 0) | |
{ | |
size_t left_over = ctx->buflen; | |
memcpy (&ctx->buffer[left_over], buffer, len); | |
left_over += len; | |
if (left_over >= 64) | |
{ | |
sha256_process_block (ctx->buffer, 64, ctx); | |
left_over -= 64; | |
memcpy (ctx->buffer, &ctx->buffer[64], left_over); | |
} | |
ctx->buflen = left_over; | |
} | |
} | |
/* Define our magic string to mark salt for SHA256 "encryption" | |
replacement. */ | |
static const char sha256_salt_prefix[] = "$5$"; | |
/* Prefix for optional rounds specification. */ | |
static const char sha256_rounds_prefix[] = "rounds="; | |
/* Maximum salt string length. */ | |
#define SALT_LEN_MAX 16 | |
/* Default number of rounds if not explicitly specified. */ | |
#define ROUNDS_DEFAULT 5000 | |
/* Minimum number of rounds. */ | |
#define ROUNDS_MIN 1000 | |
/* Maximum number of rounds. */ | |
#define ROUNDS_MAX 999999999 | |
/* Table with characters for base64 transformation. */ | |
static const char b64t[64] = | |
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; | |
static char * | |
sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen) | |
{ | |
unsigned char alt_result[32] | |
__attribute__ ((__aligned__ (__alignof__ (uint32_t)))); | |
unsigned char temp_result[32] | |
__attribute__ ((__aligned__ (__alignof__ (uint32_t)))); | |
struct sha256_ctx ctx; | |
struct sha256_ctx alt_ctx; | |
size_t salt_len; | |
size_t key_len; | |
size_t cnt; | |
char *cp; | |
char *copied_key = NULL; | |
char *copied_salt = NULL; | |
char *p_bytes; | |
char *s_bytes; | |
/* Default number of rounds. */ | |
size_t rounds = ROUNDS_DEFAULT; | |
bool rounds_custom = false; | |
/* Find beginning of salt string. The prefix should normally always | |
be present. Just in case it is not. */ | |
if (strncmp (sha256_salt_prefix, salt, sizeof (sha256_salt_prefix) - 1) == 0) | |
/* Skip salt prefix. */ | |
salt += sizeof (sha256_salt_prefix) - 1; | |
if (strncmp (salt, sha256_rounds_prefix, sizeof (sha256_rounds_prefix) - 1) | |
== 0) | |
{ | |
const char *num = salt + sizeof (sha256_rounds_prefix) - 1; | |
char *endp; | |
unsigned long int srounds = strtoul (num, &endp, 10); | |
if (*endp == '$') | |
{ | |
salt = endp + 1; | |
rounds = MAX (ROUNDS_MIN, MIN (srounds, ROUNDS_MAX)); | |
rounds_custom = true; | |
} | |
} | |
salt_len = MIN (strcspn (salt, "$"), SALT_LEN_MAX); | |
key_len = strlen (key); | |
if ((key - (char *) 0) % __alignof__ (uint32_t) != 0) | |
{ | |
char *tmp = (char *) alloca (key_len + __alignof__ (uint32_t)); | |
key = copied_key = | |
memcpy (tmp + __alignof__ (uint32_t) | |
- (tmp - (char *) 0) % __alignof__ (uint32_t), | |
key, key_len); | |
} | |
if ((salt - (char *) 0) % __alignof__ (uint32_t) != 0) | |
{ | |
char *tmp = (char *) alloca (salt_len + __alignof__ (uint32_t)); | |
salt = copied_salt = | |
memcpy (tmp + __alignof__ (uint32_t) | |
- (tmp - (char *) 0) % __alignof__ (uint32_t), | |
salt, salt_len); | |
} | |
/* Prepare for the real work. */ | |
sha256_init_ctx (&ctx); | |
/* Add the key string. */ | |
sha256_process_bytes (key, key_len, &ctx); | |
/* The last part is the salt string. This must be at most 16 | |
characters and it ends at the first `$' character (for | |
compatibility with existing implementations). */ | |
sha256_process_bytes (salt, salt_len, &ctx); | |
/* Compute alternate SHA256 sum with input KEY, SALT, and KEY. The | |
final result will be added to the first context. */ | |
sha256_init_ctx (&alt_ctx); | |
/* Add key. */ | |
sha256_process_bytes (key, key_len, &alt_ctx); | |
/* Add salt. */ | |
sha256_process_bytes (salt, salt_len, &alt_ctx); | |
/* Add key again. */ | |
sha256_process_bytes (key, key_len, &alt_ctx); | |
/* Now get result of this (32 bytes) and add it to the other | |
context. */ | |
sha256_finish_ctx (&alt_ctx, alt_result); | |
/* Add for any character in the key one byte of the alternate sum. */ | |
for (cnt = key_len; cnt > 32; cnt -= 32) | |
sha256_process_bytes (alt_result, 32, &ctx); | |
sha256_process_bytes (alt_result, cnt, &ctx); | |
/* Take the binary representation of the length of the key and for every | |
1 add the alternate sum, for every 0 the key. */ | |
for (cnt = key_len; cnt > 0; cnt >>= 1) | |
if ((cnt & 1) != 0) | |
sha256_process_bytes (alt_result, 32, &ctx); | |
else | |
sha256_process_bytes (key, key_len, &ctx); | |
/* Create intermediate result. */ | |
sha256_finish_ctx (&ctx, alt_result); | |
/* Start computation of P byte sequence. */ | |
sha256_init_ctx (&alt_ctx); | |
/* For every character in the password add the entire password. */ | |
for (cnt = 0; cnt < key_len; ++cnt) | |
sha256_process_bytes (key, key_len, &alt_ctx); | |
/* Finish the digest. */ | |
sha256_finish_ctx (&alt_ctx, temp_result); | |
/* Create byte sequence P. */ | |
cp = p_bytes = alloca (key_len); | |
for (cnt = key_len; cnt >= 32; cnt -= 32) | |
cp = mempcpy (cp, temp_result, 32); | |
memcpy (cp, temp_result, cnt); | |
/* Start computation of S byte sequence. */ | |
sha256_init_ctx (&alt_ctx); | |
/* For every character in the password add the entire password. */ | |
for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) | |
sha256_process_bytes (salt, salt_len, &alt_ctx); | |
/* Finish the digest. */ | |
sha256_finish_ctx (&alt_ctx, temp_result); | |
/* Create byte sequence S. */ | |
cp = s_bytes = alloca (salt_len); | |
for (cnt = salt_len; cnt >= 32; cnt -= 32) | |
cp = mempcpy (cp, temp_result, 32); | |
memcpy (cp, temp_result, cnt); | |
/* Repeatedly run the collected hash value through SHA256 to burn | |
CPU cycles. */ | |
for (cnt = 0; cnt < rounds; ++cnt) | |
{ | |
/* New context. */ | |
sha256_init_ctx (&ctx); | |
/* Add key or last result. */ | |
if ((cnt & 1) != 0) | |
sha256_process_bytes (p_bytes, key_len, &ctx); | |
else | |
sha256_process_bytes (alt_result, 32, &ctx); | |
/* Add salt for numbers not divisible by 3. */ | |
if (cnt % 3 != 0) | |
sha256_process_bytes (s_bytes, salt_len, &ctx); | |
/* Add key for numbers not divisible by 7. */ | |
if (cnt % 7 != 0) | |
sha256_process_bytes (p_bytes, key_len, &ctx); | |
/* Add key or last result. */ | |
if ((cnt & 1) != 0) | |
sha256_process_bytes (alt_result, 32, &ctx); | |
else | |
sha256_process_bytes (p_bytes, key_len, &ctx); | |
/* Create intermediate result. */ | |
sha256_finish_ctx (&ctx, alt_result); | |
} | |
/* Now we can construct the result string. It consists of three | |
parts. */ | |
cp = stpncpy (buffer, sha256_salt_prefix, MAX (0, buflen)); | |
buflen -= sizeof (sha256_salt_prefix) - 1; | |
if (rounds_custom) | |
{ | |
int n = snprintf (cp, MAX (0, buflen), "%s%zu$", | |
sha256_rounds_prefix, rounds); | |
cp += n; | |
buflen -= n; | |
} | |
cp = stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len)); | |
buflen -= MIN ((size_t) MAX (0, buflen), salt_len); | |
if (buflen > 0) | |
{ | |
*cp++ = '$'; | |
--buflen; | |
} | |
#define b64_from_24bit(B2, B1, B0, N) \ | |
do { \ | |
unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ | |
int n = (N); \ | |
while (n-- > 0 && buflen > 0) \ | |
{ \ | |
*cp++ = b64t[w & 0x3f]; \ | |
--buflen; \ | |
w >>= 6; \ | |
} \ | |
} while (0) | |
b64_from_24bit (alt_result[0], alt_result[10], alt_result[20], 4); | |
b64_from_24bit (alt_result[21], alt_result[1], alt_result[11], 4); | |
b64_from_24bit (alt_result[12], alt_result[22], alt_result[2], 4); | |
b64_from_24bit (alt_result[3], alt_result[13], alt_result[23], 4); | |
b64_from_24bit (alt_result[24], alt_result[4], alt_result[14], 4); | |
b64_from_24bit (alt_result[15], alt_result[25], alt_result[5], 4); | |
b64_from_24bit (alt_result[6], alt_result[16], alt_result[26], 4); | |
b64_from_24bit (alt_result[27], alt_result[7], alt_result[17], 4); | |
b64_from_24bit (alt_result[18], alt_result[28], alt_result[8], 4); | |
b64_from_24bit (alt_result[9], alt_result[19], alt_result[29], 4); | |
b64_from_24bit (0, alt_result[31], alt_result[30], 3); | |
if (buflen <= 0) | |
{ | |
errno = ERANGE; | |
buffer = NULL; | |
} | |
else | |
*cp = '\0'; /* Terminate the string. */ | |
/* Clear the buffer for the intermediate result so that people | |
attaching to processes or reading core dumps cannot get any | |
information. We do it in this way to clear correct_words[] | |
inside the SHA256 implementation as well. */ | |
sha256_init_ctx (&ctx); | |
sha256_finish_ctx (&ctx, alt_result); | |
memset (temp_result, '\0', sizeof (temp_result)); | |
memset (p_bytes, '\0', key_len); | |
memset (s_bytes, '\0', salt_len); | |
memset (&ctx, '\0', sizeof (ctx)); | |
memset (&alt_ctx, '\0', sizeof (alt_ctx)); | |
if (copied_key != NULL) | |
memset (copied_key, '\0', key_len); | |
if (copied_salt != NULL) | |
memset (copied_salt, '\0', salt_len); | |
return buffer; | |
} | |
/* This entry point is equivalent to the `crypt' function in Unix | |
libcs. */ | |
char * | |
sha256_crypt (const char *key, const char *salt) | |
{ | |
/* We don't want to have an arbitrary limit in the size of the | |
password. We can compute an upper bound for the size of the | |
result in advance and so we can prepare the buffer we pass to | |
`sha256_crypt_r'. */ | |
static char *buffer; | |
static int buflen; | |
int needed = (sizeof (sha256_salt_prefix) - 1 | |
+ sizeof (sha256_rounds_prefix) + 9 + 1 | |
+ strlen (salt) + 1 + 43 + 1); | |
if (buflen < needed) | |
{ | |
char *new_buffer = (char *) realloc (buffer, needed); | |
if (new_buffer == NULL) | |
return NULL; | |
buffer = new_buffer; | |
buflen = needed; | |
} | |
return sha256_crypt_r (key, salt, buffer, buflen); | |
} | |
#ifdef TEST | |
static const struct | |
{ | |
const char *input; | |
const char result[32]; | |
} tests[] = | |
{ | |
/* Test vectors from FIPS 180-2: appendix B.1. */ | |
{ "abc", | |
"\xba\x78\x16\xbf\x8f\x01\xcf\xea\x41\x41\x40\xde\x5d\xae\x22\x23" | |
"\xb0\x03\x61\xa3\x96\x17\x7a\x9c\xb4\x10\xff\x61\xf2\x00\x15\xad" }, | |
/* Test vectors from FIPS 180-2: appendix B.2. */ | |
{ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", | |
"\x24\x8d\x6a\x61\xd2\x06\x38\xb8\xe5\xc0\x26\x93\x0c\x3e\x60\x39" | |
"\xa3\x3c\xe4\x59\x64\xff\x21\x67\xf6\xec\xed\xd4\x19\xdb\x06\xc1" }, | |
/* Test vectors from the NESSIE project. */ | |
{ "", | |
"\xe3\xb0\xc4\x42\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24" | |
"\x27\xae\x41\xe4\x64\x9b\x93\x4c\xa4\x95\x99\x1b\x78\x52\xb8\x55" }, | |
{ "a", | |
"\xca\x97\x81\x12\xca\x1b\xbd\xca\xfa\xc2\x31\xb3\x9a\x23\xdc\x4d" | |
"\xa7\x86\xef\xf8\x14\x7c\x4e\x72\xb9\x80\x77\x85\xaf\xee\x48\xbb" }, | |
{ "message digest", | |
"\xf7\x84\x6f\x55\xcf\x23\xe1\x4e\xeb\xea\xb5\xb4\xe1\x55\x0c\xad" | |
"\x5b\x50\x9e\x33\x48\xfb\xc4\xef\xa3\xa1\x41\x3d\x39\x3c\xb6\x50" }, | |
{ "abcdefghijklmnopqrstuvwxyz", | |
"\x71\xc4\x80\xdf\x93\xd6\xae\x2f\x1e\xfa\xd1\x44\x7c\x66\xc9\x52" | |
"\x5e\x31\x62\x18\xcf\x51\xfc\x8d\x9e\xd8\x32\xf2\xda\xf1\x8b\x73" }, | |
{ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", | |
"\x24\x8d\x6a\x61\xd2\x06\x38\xb8\xe5\xc0\x26\x93\x0c\x3e\x60\x39" | |
"\xa3\x3c\xe4\x59\x64\xff\x21\x67\xf6\xec\xed\xd4\x19\xdb\x06\xc1" }, | |
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", | |
"\xdb\x4b\xfc\xbd\x4d\xa0\xcd\x85\xa6\x0c\x3c\x37\xd3\xfb\xd8\x80" | |
"\x5c\x77\xf1\x5f\xc6\xb1\xfd\xfe\x61\x4e\xe0\xa7\xc8\xfd\xb4\xc0" }, | |
{ "123456789012345678901234567890123456789012345678901234567890" | |
"12345678901234567890", | |
"\xf3\x71\xbc\x4a\x31\x1f\x2b\x00\x9e\xef\x95\x2d\xd8\x3c\xa8\x0e" | |
"\x2b\x60\x02\x6c\x8e\x93\x55\x92\xd0\xf9\xc3\x08\x45\x3c\x81\x3e" } | |
}; | |
#define ntests (sizeof (tests) / sizeof (tests[0])) | |
static const struct | |
{ | |
const char *salt; | |
const char *input; | |
const char *expected; | |
} tests2[] = | |
{ | |
{ "$5$saltstring", "Hello world!", | |
"$5$saltstring$5B8vYYiY.CVt1RlTTf8KbXBH3hsxY/GNooZaBBGWEc5" }, | |
{ "$5$rounds=10000$saltstringsaltstring", "Hello world!", | |
"$5$rounds=10000$saltstringsaltst$3xv.VbSHBb41AL9AvLeujZkZRBAwqFMz2." | |
"opqey6IcA" }, | |
{ "$5$rounds=5000$toolongsaltstring", "This is just a test", | |
"$5$rounds=5000$toolongsaltstrin$Un/5jzAHMgOGZ5.mWJpuVolil07guHPvOW8" | |
"mGRcvxa5" }, | |
{ "$5$rounds=1400$anotherlongsaltstring", | |
"a very much longer text to encrypt. This one even stretches over more" | |
"than one line.", | |
"$5$rounds=1400$anotherlongsalts$Rx.j8H.h8HjEDGomFU8bDkXm3XIUnzyxf12" | |
"oP84Bnq1" }, | |
{ "$5$rounds=77777$short", | |
"we have a short salt string but not a short password", | |
"$5$rounds=77777$short$JiO1O3ZpDAxGJeaDIuqCoEFysAe1mZNJRs3pw0KQRd/" }, | |
{ "$5$rounds=123456$asaltof16chars..", "a short string", | |
"$5$rounds=123456$asaltof16chars..$gP3VQ/6X7UUEW3HkBn2w1/Ptq2jxPyzV/" | |
"cZKmF/wJvD" }, | |
{ "$5$rounds=10$roundstoolow", "the minimum number is still observed", | |
"$5$rounds=1000$roundstoolow$yfvwcWrQ8l/K0DAWyuPMDNHpIVlTQebY9l/gL97" | |
"2bIC" }, | |
}; | |
#define ntests2 (sizeof (tests2) / sizeof (tests2[0])) | |
int | |
main (void) | |
{ | |
struct sha256_ctx ctx; | |
char sum[32]; | |
int result = 0; | |
int cnt; | |
for (cnt = 0; cnt < (int) ntests; ++cnt) | |
{ | |
sha256_init_ctx (&ctx); | |
sha256_process_bytes (tests[cnt].input, strlen (tests[cnt].input), &ctx); | |
sha256_finish_ctx (&ctx, sum); | |
if (memcmp (tests[cnt].result, sum, 32) != 0) | |
{ | |
printf ("test %d run %d failed\n", cnt, 1); | |
result = 1; | |
} | |
sha256_init_ctx (&ctx); | |
for (int i = 0; tests[cnt].input[i] != '\0'; ++i) | |
sha256_process_bytes (&tests[cnt].input[i], 1, &ctx); | |
sha256_finish_ctx (&ctx, sum); | |
if (memcmp (tests[cnt].result, sum, 32) != 0) | |
{ | |
printf ("test %d run %d failed\n", cnt, 2); | |
result = 1; | |
} | |
} | |
/* Test vector from FIPS 180-2: appendix B.3. */ | |
char buf[1000]; | |
memset (buf, 'a', sizeof (buf)); | |
sha256_init_ctx (&ctx); | |
for (int i = 0; i < 1000; ++i) | |
sha256_process_bytes (buf, sizeof (buf), &ctx); | |
sha256_finish_ctx (&ctx, sum); | |
static const char expected[32] = | |
"\xcd\xc7\x6e\x5c\x99\x14\xfb\x92\x81\xa1\xc7\xe2\x84\xd7\x3e\x67" | |
"\xf1\x80\x9a\x48\xa4\x97\x20\x0e\x04\x6d\x39\xcc\xc7\x11\x2c\xd0"; | |
if (memcmp (expected, sum, 32) != 0) | |
{ | |
printf ("test %d failed\n", cnt); | |
result = 1; | |
} | |
for (cnt = 0; cnt < ntests2; ++cnt) | |
{ | |
char *cp = sha256_crypt (tests2[cnt].input, tests2[cnt].salt); | |
if (strcmp (cp, tests2[cnt].expected) != 0) | |
{ | |
printf ("test %d: expected \"%s\", got \"%s\"\n", | |
cnt, tests2[cnt].expected, cp); | |
result = 1; | |
} | |
} | |
if (result == 0) | |
puts ("all tests OK"); | |
return result; | |
} | |
#endif | |
----------------------------------------------------------------------------- | |
-------- sha512crypt.c ------------------------------------------------------ | |
/* SHA512-based Unix crypt implementation. | |
Released into the Public Domain by Ulrich Drepper <drepper@redhat.com>. */ | |
#include <endian.h> | |
#include <errno.h> | |
#include <limits.h> | |
#include <stdbool.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/param.h> | |
#include <sys/types.h> | |
/* Structure to save state of computation between the single steps. */ | |
struct sha512_ctx | |
{ | |
uint64_t H[8]; | |
uint64_t total[2]; | |
uint64_t buflen; | |
char buffer[256]; /* NB: always correctly aligned for uint64_t. */ | |
}; | |
#if __BYTE_ORDER == __LITTLE_ENDIAN | |
# define SWAP(n) \ | |
(((n) << 56) \ | |
| (((n) & 0xff00) << 40) \ | |
| (((n) & 0xff0000) << 24) \ | |
| (((n) & 0xff000000) << 8) \ | |
| (((n) >> 8) & 0xff000000) \ | |
| (((n) >> 24) & 0xff0000) \ | |
| (((n) >> 40) & 0xff00) \ | |
| ((n) >> 56)) | |
#else | |
# define SWAP(n) (n) | |
#endif | |
/* This array contains the bytes used to pad the buffer to the next | |
64-byte boundary. (FIPS 180-2:5.1.2) */ | |
static const unsigned char fillbuf[128] = { 0x80, 0 /* , 0, 0, ... */ }; | |
/* Constants for SHA512 from FIPS 180-2:4.2.3. */ | |
static const uint64_t K[80] = | |
{ | |
UINT64_C (0x428a2f98d728ae22), UINT64_C (0x7137449123ef65cd), | |
UINT64_C (0xb5c0fbcfec4d3b2f), UINT64_C (0xe9b5dba58189dbbc), | |
UINT64_C (0x3956c25bf348b538), UINT64_C (0x59f111f1b605d019), | |
UINT64_C (0x923f82a4af194f9b), UINT64_C (0xab1c5ed5da6d8118), | |
UINT64_C (0xd807aa98a3030242), UINT64_C (0x12835b0145706fbe), | |
UINT64_C (0x243185be4ee4b28c), UINT64_C (0x550c7dc3d5ffb4e2), | |
UINT64_C (0x72be5d74f27b896f), UINT64_C (0x80deb1fe3b1696b1), | |
UINT64_C (0x9bdc06a725c71235), UINT64_C (0xc19bf174cf692694), | |
UINT64_C (0xe49b69c19ef14ad2), UINT64_C (0xefbe4786384f25e3), | |
UINT64_C (0x0fc19dc68b8cd5b5), UINT64_C (0x240ca1cc77ac9c65), | |
UINT64_C (0x2de92c6f592b0275), UINT64_C (0x4a7484aa6ea6e483), | |
UINT64_C (0x5cb0a9dcbd41fbd4), UINT64_C (0x76f988da831153b5), | |
UINT64_C (0x983e5152ee66dfab), UINT64_C (0xa831c66d2db43210), | |
UINT64_C (0xb00327c898fb213f), UINT64_C (0xbf597fc7beef0ee4), | |
UINT64_C (0xc6e00bf33da88fc2), UINT64_C (0xd5a79147930aa725), | |
UINT64_C (0x06ca6351e003826f), UINT64_C (0x142929670a0e6e70), | |
UINT64_C (0x27b70a8546d22ffc), UINT64_C (0x2e1b21385c26c926), | |
UINT64_C (0x4d2c6dfc5ac42aed), UINT64_C (0x53380d139d95b3df), | |
UINT64_C (0x650a73548baf63de), UINT64_C (0x766a0abb3c77b2a8), | |
UINT64_C (0x81c2c92e47edaee6), UINT64_C (0x92722c851482353b), | |
UINT64_C (0xa2bfe8a14cf10364), UINT64_C (0xa81a664bbc423001), | |
UINT64_C (0xc24b8b70d0f89791), UINT64_C (0xc76c51a30654be30), | |
UINT64_C (0xd192e819d6ef5218), UINT64_C (0xd69906245565a910), | |
UINT64_C (0xf40e35855771202a), UINT64_C (0x106aa07032bbd1b8), | |
UINT64_C (0x19a4c116b8d2d0c8), UINT64_C (0x1e376c085141ab53), | |
UINT64_C (0x2748774cdf8eeb99), UINT64_C (0x34b0bcb5e19b48a8), | |
UINT64_C (0x391c0cb3c5c95a63), UINT64_C (0x4ed8aa4ae3418acb), | |
UINT64_C (0x5b9cca4f7763e373), UINT64_C (0x682e6ff3d6b2b8a3), | |
UINT64_C (0x748f82ee5defb2fc), UINT64_C (0x78a5636f43172f60), | |
UINT64_C (0x84c87814a1f0ab72), UINT64_C (0x8cc702081a6439ec), | |
UINT64_C (0x90befffa23631e28), UINT64_C (0xa4506cebde82bde9), | |
UINT64_C (0xbef9a3f7b2c67915), UINT64_C (0xc67178f2e372532b), | |
UINT64_C (0xca273eceea26619c), UINT64_C (0xd186b8c721c0c207), | |
UINT64_C (0xeada7dd6cde0eb1e), UINT64_C (0xf57d4f7fee6ed178), | |
UINT64_C (0x06f067aa72176fba), UINT64_C (0x0a637dc5a2c898a6), | |
UINT64_C (0x113f9804bef90dae), UINT64_C (0x1b710b35131c471b), | |
UINT64_C (0x28db77f523047d84), UINT64_C (0x32caab7b40c72493), | |
UINT64_C (0x3c9ebe0a15c9bebc), UINT64_C (0x431d67c49c100d4c), | |
UINT64_C (0x4cc5d4becb3e42b6), UINT64_C (0x597f299cfc657e2a), | |
UINT64_C (0x5fcb6fab3ad6faec), UINT64_C (0x6c44198c4a475817) | |
}; | |
/* Process LEN bytes of BUFFER, accumulating context into CTX. | |
It is assumed that LEN % 128 == 0. */ | |
static void | |
sha512_process_block (const void *buffer, size_t len, struct sha512_ctx *ctx) | |
{ | |
const uint64_t *words = buffer; | |
size_t nwords = len / sizeof (uint64_t); | |
uint64_t a = ctx->H[0]; | |
uint64_t b = ctx->H[1]; | |
uint64_t c = ctx->H[2]; | |
uint64_t d = ctx->H[3]; | |
uint64_t e = ctx->H[4]; | |
uint64_t f = ctx->H[5]; | |
uint64_t g = ctx->H[6]; | |
uint64_t h = ctx->H[7]; | |
/* First increment the byte count. FIPS 180-2 specifies the possible | |
length of the file up to 2^128 bits. Here we only compute the | |
number of bytes. Do a double word increment. */ | |
ctx->total[0] += len; | |
if (ctx->total[0] < len) | |
++ctx->total[1]; | |
/* Process all bytes in the buffer with 128 bytes in each round of | |
the loop. */ | |
while (nwords > 0) | |
{ | |
uint64_t W[80]; | |
uint64_t a_save = a; | |
uint64_t b_save = b; | |
uint64_t c_save = c; | |
uint64_t d_save = d; | |
uint64_t e_save = e; | |
uint64_t f_save = f; | |
uint64_t g_save = g; | |
uint64_t h_save = h; | |
/* Operators defined in FIPS 180-2:4.1.2. */ | |
#define Ch(x, y, z) ((x & y) ^ (~x & z)) | |
#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) | |
#define S0(x) (CYCLIC (x, 28) ^ CYCLIC (x, 34) ^ CYCLIC (x, 39)) | |
#define S1(x) (CYCLIC (x, 14) ^ CYCLIC (x, 18) ^ CYCLIC (x, 41)) | |
#define R0(x) (CYCLIC (x, 1) ^ CYCLIC (x, 8) ^ (x >> 7)) | |
#define R1(x) (CYCLIC (x, 19) ^ CYCLIC (x, 61) ^ (x >> 6)) | |
/* It is unfortunate that C does not provide an operator for | |
cyclic rotation. Hope the C compiler is smart enough. */ | |
#define CYCLIC(w, s) ((w >> s) | (w << (64 - s))) | |
/* Compute the message schedule according to FIPS 180-2:6.3.2 step 2. */ | |
for (unsigned int t = 0; t < 16; ++t) | |
{ | |
W[t] = SWAP (*words); | |
++words; | |
} | |
for (unsigned int t = 16; t < 80; ++t) | |
W[t] = R1 (W[t - 2]) + W[t - 7] + R0 (W[t - 15]) + W[t - 16]; | |
/* The actual computation according to FIPS 180-2:6.3.2 step 3. */ | |
for (unsigned int t = 0; t < 80; ++t) | |
{ | |
uint64_t T1 = h + S1 (e) + Ch (e, f, g) + K[t] + W[t]; | |
uint64_t T2 = S0 (a) + Maj (a, b, c); | |
h = g; | |
g = f; | |
f = e; | |
e = d + T1; | |
d = c; | |
c = b; | |
b = a; | |
a = T1 + T2; | |
} | |
/* Add the starting values of the context according to FIPS 180-2:6.3.2 | |
step 4. */ | |
a += a_save; | |
b += b_save; | |
c += c_save; | |
d += d_save; | |
e += e_save; | |
f += f_save; | |
g += g_save; | |
h += h_save; | |
/* Prepare for the next round. */ | |
nwords -= 16; | |
} | |
/* Put checksum in context given as argument. */ | |
ctx->H[0] = a; | |
ctx->H[1] = b; | |
ctx->H[2] = c; | |
ctx->H[3] = d; | |
ctx->H[4] = e; | |
ctx->H[5] = f; | |
ctx->H[6] = g; | |
ctx->H[7] = h; | |
} | |
/* Initialize structure containing state of computation. | |
(FIPS 180-2:5.3.3) */ | |
static void | |
sha512_init_ctx (struct sha512_ctx *ctx) | |
{ | |
ctx->H[0] = UINT64_C (0x6a09e667f3bcc908); | |
ctx->H[1] = UINT64_C (0xbb67ae8584caa73b); | |
ctx->H[2] = UINT64_C (0x3c6ef372fe94f82b); | |
ctx->H[3] = UINT64_C (0xa54ff53a5f1d36f1); | |
ctx->H[4] = UINT64_C (0x510e527fade682d1); | |
ctx->H[5] = UINT64_C (0x9b05688c2b3e6c1f); | |
ctx->H[6] = UINT64_C (0x1f83d9abfb41bd6b); | |
ctx->H[7] = UINT64_C (0x5be0cd19137e2179); | |
ctx->total[0] = ctx->total[1] = 0; | |
ctx->buflen = 0; | |
} | |
/* Process the remaining bytes in the internal buffer and the usual | |
prolog according to the standard and write the result to RESBUF. | |
IMPORTANT: On some systems it is required that RESBUF is correctly | |
aligned for a 32 bits value. */ | |
static void * | |
sha512_finish_ctx (struct sha512_ctx *ctx, void *resbuf) | |
{ | |
/* Take yet unprocessed bytes into account. */ | |
uint64_t bytes = ctx->buflen; | |
size_t pad; | |
/* Now count remaining bytes. */ | |
ctx->total[0] += bytes; | |
if (ctx->total[0] < bytes) | |
++ctx->total[1]; | |
pad = bytes >= 112 ? 128 + 112 - bytes : 112 - bytes; | |
memcpy (&ctx->buffer[bytes], fillbuf, pad); | |
/* Put the 128-bit file length in *bits* at the end of the buffer. */ | |
*(uint64_t *) &ctx->buffer[bytes + pad + 8] = SWAP (ctx->total[0] << 3); | |
*(uint64_t *) &ctx->buffer[bytes + pad] = SWAP ((ctx->total[1] << 3) | | |
(ctx->total[0] >> 61)); | |
/* Process last bytes. */ | |
sha512_process_block (ctx->buffer, bytes + pad + 16, ctx); | |
/* Put result from CTX in first 64 bytes following RESBUF. */ | |
for (unsigned int i = 0; i < 8; ++i) | |
((uint64_t *) resbuf)[i] = SWAP (ctx->H[i]); | |
return resbuf; | |
} | |
static void | |
sha512_process_bytes (const void *buffer, size_t len, struct sha512_ctx *ctx) | |
{ | |
/* When we already have some bits in our internal buffer concatenate | |
both inputs first. */ | |
if (ctx->buflen != 0) | |
{ | |
size_t left_over = ctx->buflen; | |
size_t add = 256 - left_over > len ? len : 256 - left_over; | |
memcpy (&ctx->buffer[left_over], buffer, add); | |
ctx->buflen += add; | |
if (ctx->buflen > 128) | |
{ | |
sha512_process_block (ctx->buffer, ctx->buflen & ~127, ctx); | |
ctx->buflen &= 127; | |
/* The regions in the following copy operation cannot overlap. */ | |
memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~127], | |
ctx->buflen); | |
} | |
buffer = (const char *) buffer + add; | |
len -= add; | |
} | |
/* Process available complete blocks. */ | |
if (len >= 128) | |
{ | |
#if !_STRING_ARCH_unaligned | |
/* To check alignment gcc has an appropriate operator. Other | |
compilers don't. */ | |
# if __GNUC__ >= 2 | |
# define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__ (uint64_t) != 0) | |
# else | |
# define UNALIGNED_P(p) (((uintptr_t) p) % sizeof (uint64_t) != 0) | |
# endif | |
if (UNALIGNED_P (buffer)) | |
while (len > 128) | |
{ | |
sha512_process_block (memcpy (ctx->buffer, buffer, 128), 128, | |
ctx); | |
buffer = (const char *) buffer + 128; | |
len -= 128; | |
} | |
else | |
#endif | |
{ | |
sha512_process_block (buffer, len & ~127, ctx); | |
buffer = (const char *) buffer + (len & ~127); | |
len &= 127; | |
} | |
} | |
/* Move remaining bytes into internal buffer. */ | |
if (len > 0) | |
{ | |
size_t left_over = ctx->buflen; | |
memcpy (&ctx->buffer[left_over], buffer, len); | |
left_over += len; | |
if (left_over >= 128) | |
{ | |
sha512_process_block (ctx->buffer, 128, ctx); | |
left_over -= 128; | |
memcpy (ctx->buffer, &ctx->buffer[128], left_over); | |
} | |
ctx->buflen = left_over; | |
} | |
} | |
/* Define our magic string to mark salt for SHA512 "encryption" | |
replacement. */ | |
static const char sha512_salt_prefix[] = "$6$"; | |
/* Prefix for optional rounds specification. */ | |
static const char sha512_rounds_prefix[] = "rounds="; | |
/* Maximum salt string length. */ | |
#define SALT_LEN_MAX 16 | |
/* Default number of rounds if not explicitly specified. */ | |
#define ROUNDS_DEFAULT 5000 | |
/* Minimum number of rounds. */ | |
#define ROUNDS_MIN 1000 | |
/* Maximum number of rounds. */ | |
#define ROUNDS_MAX 999999999 | |
/* Table with characters for base64 transformation. */ | |
static const char b64t[64] = | |
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; | |
static char * | |
sha512_crypt_r (const char *key, const char *salt, char *buffer, int buflen) | |
{ | |
unsigned char alt_result[64] | |
__attribute__ ((__aligned__ (__alignof__ (uint64_t)))); | |
unsigned char temp_result[64] | |
__attribute__ ((__aligned__ (__alignof__ (uint64_t)))); | |
struct sha512_ctx ctx; | |
struct sha512_ctx alt_ctx; | |
size_t salt_len; | |
size_t key_len; | |
size_t cnt; | |
char *cp; | |
char *copied_key = NULL; | |
char *copied_salt = NULL; | |
char *p_bytes; | |
char *s_bytes; | |
/* Default number of rounds. */ | |
size_t rounds = ROUNDS_DEFAULT; | |
bool rounds_custom = false; | |
/* Find beginning of salt string. The prefix should normally always | |
be present. Just in case it is not. */ | |
if (strncmp (sha512_salt_prefix, salt, sizeof (sha512_salt_prefix) - 1) == 0) | |
/* Skip salt prefix. */ | |
salt += sizeof (sha512_salt_prefix) - 1; | |
if (strncmp (salt, sha512_rounds_prefix, sizeof (sha512_rounds_prefix) - 1) | |
== 0) | |
{ | |
const char *num = salt + sizeof (sha512_rounds_prefix) - 1; | |
char *endp; | |
unsigned long int srounds = strtoul (num, &endp, 10); | |
if (*endp == '$') | |
{ | |
salt = endp + 1; | |
rounds = MAX (ROUNDS_MIN, MIN (srounds, ROUNDS_MAX)); | |
rounds_custom = true; | |
} | |
} | |
salt_len = MIN (strcspn (salt, "$"), SALT_LEN_MAX); | |
key_len = strlen (key); | |
if ((key - (char *) 0) % __alignof__ (uint64_t) != 0) | |
{ | |
char *tmp = (char *) alloca (key_len + __alignof__ (uint64_t)); | |
key = copied_key = | |
memcpy (tmp + __alignof__ (uint64_t) | |
- (tmp - (char *) 0) % __alignof__ (uint64_t), | |
key, key_len); | |
} | |
if ((salt - (char *) 0) % __alignof__ (uint64_t) != 0) | |
{ | |
char *tmp = (char *) alloca (salt_len + __alignof__ (uint64_t)); | |
salt = copied_salt = | |
memcpy (tmp + __alignof__ (uint64_t) | |
- (tmp - (char *) 0) % __alignof__ (uint64_t), | |
salt, salt_len); | |
} | |
/* Prepare for the real work. */ | |
sha512_init_ctx (&ctx); | |
/* Add the key string. */ | |
sha512_process_bytes (key, key_len, &ctx); | |
/* The last part is the salt string. This must be at most 16 | |
characters and it ends at the first `$' character (for | |
compatibility with existing implementations). */ | |
sha512_process_bytes (salt, salt_len, &ctx); | |
/* Compute alternate SHA512 sum with input KEY, SALT, and KEY. The | |
final result will be added to the first context. */ | |
sha512_init_ctx (&alt_ctx); | |
/* Add key. */ | |
sha512_process_bytes (key, key_len, &alt_ctx); | |
/* Add salt. */ | |
sha512_process_bytes (salt, salt_len, &alt_ctx); | |
/* Add key again. */ | |
sha512_process_bytes (key, key_len, &alt_ctx); | |
/* Now get result of this (64 bytes) and add it to the other | |
context. */ | |
sha512_finish_ctx (&alt_ctx, alt_result); | |
/* Add for any character in the key one byte of the alternate sum. */ | |
for (cnt = key_len; cnt > 64; cnt -= 64) | |
sha512_process_bytes (alt_result, 64, &ctx); | |
sha512_process_bytes (alt_result, cnt, &ctx); | |
/* Take the binary representation of the length of the key and for every | |
1 add the alternate sum, for every 0 the key. */ | |
for (cnt = key_len; cnt > 0; cnt >>= 1) | |
if ((cnt & 1) != 0) | |
sha512_process_bytes (alt_result, 64, &ctx); | |
else | |
sha512_process_bytes (key, key_len, &ctx); | |
/* Create intermediate result. */ | |
sha512_finish_ctx (&ctx, alt_result); | |
/* Start computation of P byte sequence. */ | |
sha512_init_ctx (&alt_ctx); | |
/* For every character in the password add the entire password. */ | |
for (cnt = 0; cnt < key_len; ++cnt) | |
sha512_process_bytes (key, key_len, &alt_ctx); | |
/* Finish the digest. */ | |
sha512_finish_ctx (&alt_ctx, temp_result); | |
/* Create byte sequence P. */ | |
cp = p_bytes = alloca (key_len); | |
for (cnt = key_len; cnt >= 64; cnt -= 64) | |
cp = mempcpy (cp, temp_result, 64); | |
memcpy (cp, temp_result, cnt); | |
/* Start computation of S byte sequence. */ | |
sha512_init_ctx (&alt_ctx); | |
/* For every character in the password add the entire password. */ | |
for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) | |
sha512_process_bytes (salt, salt_len, &alt_ctx); | |
/* Finish the digest. */ | |
sha512_finish_ctx (&alt_ctx, temp_result); | |
/* Create byte sequence S. */ | |
cp = s_bytes = alloca (salt_len); | |
for (cnt = salt_len; cnt >= 64; cnt -= 64) | |
cp = mempcpy (cp, temp_result, 64); | |
memcpy (cp, temp_result, cnt); | |
/* Repeatedly run the collected hash value through SHA512 to burn | |
CPU cycles. */ | |
for (cnt = 0; cnt < rounds; ++cnt) | |
{ | |
/* New context. */ | |
sha512_init_ctx (&ctx); | |
/* Add key or last result. */ | |
if ((cnt & 1) != 0) | |
sha512_process_bytes (p_bytes, key_len, &ctx); | |
else | |
sha512_process_bytes (alt_result, 64, &ctx); | |
/* Add salt for numbers not divisible by 3. */ | |
if (cnt % 3 != 0) | |
sha512_process_bytes (s_bytes, salt_len, &ctx); | |
/* Add key for numbers not divisible by 7. */ | |
if (cnt % 7 != 0) | |
sha512_process_bytes (p_bytes, key_len, &ctx); | |
/* Add key or last result. */ | |
if ((cnt & 1) != 0) | |
sha512_process_bytes (alt_result, 64, &ctx); | |
else | |
sha512_process_bytes (p_bytes, key_len, &ctx); | |
/* Create intermediate result. */ | |
sha512_finish_ctx (&ctx, alt_result); | |
} | |
/* Now we can construct the result string. It consists of three | |
parts. */ | |
cp = __stpncpy (buffer, sha512_salt_prefix, MAX (0, buflen)); | |
buflen -= sizeof (sha512_salt_prefix) - 1; | |
if (rounds_custom) | |
{ | |
int n = snprintf (cp, MAX (0, buflen), "%s%zu$", | |
sha512_rounds_prefix, rounds); | |
cp += n; | |
buflen -= n; | |
} | |
cp = __stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len)); | |
buflen -= MIN ((size_t) MAX (0, buflen), salt_len); | |
if (buflen > 0) | |
{ | |
*cp++ = '$'; | |
--buflen; | |
} | |
#define b64_from_24bit(B2, B1, B0, N) \ | |
do { \ | |
unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ | |
int n = (N); \ | |
while (n-- > 0 && buflen > 0) \ | |
{ \ | |
*cp++ = b64t[w & 0x3f]; \ | |
--buflen; \ | |
w >>= 6; \ | |
} \ | |
} while (0) | |
b64_from_24bit (alt_result[0], alt_result[21], alt_result[42], 4); | |
b64_from_24bit (alt_result[22], alt_result[43], alt_result[1], 4); | |
b64_from_24bit (alt_result[44], alt_result[2], alt_result[23], 4); | |
b64_from_24bit (alt_result[3], alt_result[24], alt_result[45], 4); | |
b64_from_24bit (alt_result[25], alt_result[46], alt_result[4], 4); | |
b64_from_24bit (alt_result[47], alt_result[5], alt_result[26], 4); | |
b64_from_24bit (alt_result[6], alt_result[27], alt_result[48], 4); | |
b64_from_24bit (alt_result[28], alt_result[49], alt_result[7], 4); | |
b64_from_24bit (alt_result[50], alt_result[8], alt_result[29], 4); | |
b64_from_24bit (alt_result[9], alt_result[30], alt_result[51], 4); | |
b64_from_24bit (alt_result[31], alt_result[52], alt_result[10], 4); | |
b64_from_24bit (alt_result[53], alt_result[11], alt_result[32], 4); | |
b64_from_24bit (alt_result[12], alt_result[33], alt_result[54], 4); | |
b64_from_24bit (alt_result[34], alt_result[55], alt_result[13], 4); | |
b64_from_24bit (alt_result[56], alt_result[14], alt_result[35], 4); | |
b64_from_24bit (alt_result[15], alt_result[36], alt_result[57], 4); | |
b64_from_24bit (alt_result[37], alt_result[58], alt_result[16], 4); | |
b64_from_24bit (alt_result[59], alt_result[17], alt_result[38], 4); | |
b64_from_24bit (alt_result[18], alt_result[39], alt_result[60], 4); | |
b64_from_24bit (alt_result[40], alt_result[61], alt_result[19], 4); | |
b64_from_24bit (alt_result[62], alt_result[20], alt_result[41], 4); | |
b64_from_24bit (0, 0, alt_result[63], 2); | |
if (buflen <= 0) | |
{ | |
errno = ERANGE; | |
buffer = NULL; | |
} | |
else | |
*cp = '\0'; /* Terminate the string. */ | |
/* Clear the buffer for the intermediate result so that people | |
attaching to processes or reading core dumps cannot get any | |
information. We do it in this way to clear correct_words[] | |
inside the SHA512 implementation as well. */ | |
sha512_init_ctx (&ctx); | |
sha512_finish_ctx (&ctx, alt_result); | |
memset (temp_result, '\0', sizeof (temp_result)); | |
memset (p_bytes, '\0', key_len); | |
memset (s_bytes, '\0', salt_len); | |
memset (&ctx, '\0', sizeof (ctx)); | |
memset (&alt_ctx, '\0', sizeof (alt_ctx)); | |
if (copied_key != NULL) | |
memset (copied_key, '\0', key_len); | |
if (copied_salt != NULL) | |
memset (copied_salt, '\0', salt_len); | |
return buffer; | |
} | |
/* This entry point is equivalent to the `crypt' function in Unix | |
libcs. */ | |
char * | |
sha512_crypt (const char *key, const char *salt) | |
{ | |
/* We don't want to have an arbitrary limit in the size of the | |
password. We can compute an upper bound for the size of the | |
result in advance and so we can prepare the buffer we pass to | |
`sha512_crypt_r'. */ | |
static char *buffer; | |
static int buflen; | |
int needed = (sizeof (sha512_salt_prefix) - 1 | |
+ sizeof (sha512_rounds_prefix) + 9 + 1 | |
+ strlen (salt) + 1 + 86 + 1); | |
if (buflen < needed) | |
{ | |
char *new_buffer = (char *) realloc (buffer, needed); | |
if (new_buffer == NULL) | |
return NULL; | |
buffer = new_buffer; | |
buflen = needed; | |
} | |
return sha512_crypt_r (key, salt, buffer, buflen); | |
} | |
#ifdef TEST | |
static const struct | |
{ | |
const char *input; | |
const char result[64]; | |
} tests[] = | |
{ | |
/* Test vectors from FIPS 180-2: appendix C.1. */ | |
{ "abc", | |
"\xdd\xaf\x35\xa1\x93\x61\x7a\xba\xcc\x41\x73\x49\xae\x20\x41\x31" | |
"\x12\xe6\xfa\x4e\x89\xa9\x7e\xa2\x0a\x9e\xee\xe6\x4b\x55\xd3\x9a" | |
"\x21\x92\x99\x2a\x27\x4f\xc1\xa8\x36\xba\x3c\x23\xa3\xfe\xeb\xbd" | |
"\x45\x4d\x44\x23\x64\x3c\xe8\x0e\x2a\x9a\xc9\x4f\xa5\x4c\xa4\x9f" }, | |
/* Test vectors from FIPS 180-2: appendix C.2. */ | |
{ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" | |
"hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", | |
"\x8e\x95\x9b\x75\xda\xe3\x13\xda\x8c\xf4\xf7\x28\x14\xfc\x14\x3f" | |
"\x8f\x77\x79\xc6\xeb\x9f\x7f\xa1\x72\x99\xae\xad\xb6\x88\x90\x18" | |
"\x50\x1d\x28\x9e\x49\x00\xf7\xe4\x33\x1b\x99\xde\xc4\xb5\x43\x3a" | |
"\xc7\xd3\x29\xee\xb6\xdd\x26\x54\x5e\x96\xe5\x5b\x87\x4b\xe9\x09" }, | |
/* Test vectors from the NESSIE project. */ | |
{ "", | |
"\xcf\x83\xe1\x35\x7e\xef\xb8\xbd\xf1\x54\x28\x50\xd6\x6d\x80\x07" | |
"\xd6\x20\xe4\x05\x0b\x57\x15\xdc\x83\xf4\xa9\x21\xd3\x6c\xe9\xce" | |
"\x47\xd0\xd1\x3c\x5d\x85\xf2\xb0\xff\x83\x18\xd2\x87\x7e\xec\x2f" | |
"\x63\xb9\x31\xbd\x47\x41\x7a\x81\xa5\x38\x32\x7a\xf9\x27\xda\x3e" }, | |
{ "a", | |
"\x1f\x40\xfc\x92\xda\x24\x16\x94\x75\x09\x79\xee\x6c\xf5\x82\xf2" | |
"\xd5\xd7\xd2\x8e\x18\x33\x5d\xe0\x5a\xbc\x54\xd0\x56\x0e\x0f\x53" | |
"\x02\x86\x0c\x65\x2b\xf0\x8d\x56\x02\x52\xaa\x5e\x74\x21\x05\x46" | |
"\xf3\x69\xfb\xbb\xce\x8c\x12\xcf\xc7\x95\x7b\x26\x52\xfe\x9a\x75" }, | |
{ "message digest", | |
"\x10\x7d\xbf\x38\x9d\x9e\x9f\x71\xa3\xa9\x5f\x6c\x05\x5b\x92\x51" | |
"\xbc\x52\x68\xc2\xbe\x16\xd6\xc1\x34\x92\xea\x45\xb0\x19\x9f\x33" | |
"\x09\xe1\x64\x55\xab\x1e\x96\x11\x8e\x8a\x90\x5d\x55\x97\xb7\x20" | |
"\x38\xdd\xb3\x72\xa8\x98\x26\x04\x6d\xe6\x66\x87\xbb\x42\x0e\x7c" }, | |
{ "abcdefghijklmnopqrstuvwxyz", | |
"\x4d\xbf\xf8\x6c\xc2\xca\x1b\xae\x1e\x16\x46\x8a\x05\xcb\x98\x81" | |
"\xc9\x7f\x17\x53\xbc\xe3\x61\x90\x34\x89\x8f\xaa\x1a\xab\xe4\x29" | |
"\x95\x5a\x1b\xf8\xec\x48\x3d\x74\x21\xfe\x3c\x16\x46\x61\x3a\x59" | |
"\xed\x54\x41\xfb\x0f\x32\x13\x89\xf7\x7f\x48\xa8\x79\xc7\xb1\xf1" }, | |
{ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", | |
"\x20\x4a\x8f\xc6\xdd\xa8\x2f\x0a\x0c\xed\x7b\xeb\x8e\x08\xa4\x16" | |
"\x57\xc1\x6e\xf4\x68\xb2\x28\xa8\x27\x9b\xe3\x31\xa7\x03\xc3\x35" | |
"\x96\xfd\x15\xc1\x3b\x1b\x07\xf9\xaa\x1d\x3b\xea\x57\x78\x9c\xa0" | |
"\x31\xad\x85\xc7\xa7\x1d\xd7\x03\x54\xec\x63\x12\x38\xca\x34\x45" }, | |
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", | |
"\x1e\x07\xbe\x23\xc2\x6a\x86\xea\x37\xea\x81\x0c\x8e\xc7\x80\x93" | |
"\x52\x51\x5a\x97\x0e\x92\x53\xc2\x6f\x53\x6c\xfc\x7a\x99\x96\xc4" | |
"\x5c\x83\x70\x58\x3e\x0a\x78\xfa\x4a\x90\x04\x1d\x71\xa4\xce\xab" | |
"\x74\x23\xf1\x9c\x71\xb9\xd5\xa3\xe0\x12\x49\xf0\xbe\xbd\x58\x94" }, | |
{ "123456789012345678901234567890123456789012345678901234567890" | |
"12345678901234567890", | |
"\x72\xec\x1e\xf1\x12\x4a\x45\xb0\x47\xe8\xb7\xc7\x5a\x93\x21\x95" | |
"\x13\x5b\xb6\x1d\xe2\x4e\xc0\xd1\x91\x40\x42\x24\x6e\x0a\xec\x3a" | |
"\x23\x54\xe0\x93\xd7\x6f\x30\x48\xb4\x56\x76\x43\x46\x90\x0c\xb1" | |
"\x30\xd2\xa4\xfd\x5d\xd1\x6a\xbb\x5e\x30\xbc\xb8\x50\xde\xe8\x43" } | |
}; | |
#define ntests (sizeof (tests) / sizeof (tests[0])) | |
static const struct | |
{ | |
const char *salt; | |
const char *input; | |
const char *expected; | |
} tests2[] = | |
{ | |
{ "$6$saltstring", "Hello world!", | |
"$6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJu" | |
"esI68u4OTLiBFdcbYEdFCoEOfaS35inz1" }, | |
{ "$6$rounds=10000$saltstringsaltstring", "Hello world!", | |
"$6$rounds=10000$saltstringsaltst$OW1/O6BYHV6BcXZu8QVeXbDWra3Oeqh0sb" | |
"HbbMCVNSnCM/UrjmM0Dp8vOuZeHBy/YTBmSK6H9qs/y3RnOaw5v." }, | |
{ "$6$rounds=5000$toolongsaltstring", "This is just a test", | |
"$6$rounds=5000$toolongsaltstrin$lQ8jolhgVRVhY4b5pZKaysCLi0QBxGoNeKQ" | |
"zQ3glMhwllF7oGDZxUhx1yxdYcz/e1JSbq3y6JMxxl8audkUEm0" }, | |
{ "$6$rounds=1400$anotherlongsaltstring", | |
"a very much longer text to encrypt. This one even stretches over more" | |
"than one line.", | |
"$6$rounds=1400$anotherlongsalts$POfYwTEok97VWcjxIiSOjiykti.o/pQs.wP" | |
"vMxQ6Fm7I6IoYN3CmLs66x9t0oSwbtEW7o7UmJEiDwGqd8p4ur1" }, | |
{ "$6$rounds=77777$short", | |
"we have a short salt string but not a short password", | |
"$6$rounds=77777$short$WuQyW2YR.hBNpjjRhpYD/ifIw05xdfeEyQoMxIXbkvr0g" | |
"ge1a1x3yRULJ5CCaUeOxFmtlcGZelFl5CxtgfiAc0" }, | |
{ "$6$rounds=123456$asaltof16chars..", "a short string", | |
"$6$rounds=123456$asaltof16chars..$BtCwjqMJGx5hrJhZywWvt0RLE8uZ4oPwc" | |
"elCjmw2kSYu.Ec6ycULevoBK25fs2xXgMNrCzIMVcgEJAstJeonj1" }, | |
{ "$6$rounds=10$roundstoolow", "the minimum number is still observed", | |
"$6$rounds=1000$roundstoolow$kUMsbe306n21p9R.FRkW3IGn.S9NPN0x50YhH1x" | |
"hLsPuWGsUSklZt58jaTfF4ZEQpyUNGc0dqbpBYYBaHHrsX." }, | |
}; | |
#define ntests2 (sizeof (tests2) / sizeof (tests2[0])) | |
int | |
main (void) | |
{ | |
struct sha512_ctx ctx; | |
char sum[64]; | |
int result = 0; | |
int cnt; | |
for (cnt = 0; cnt < (int) ntests; ++cnt) | |
{ | |
sha512_init_ctx (&ctx); | |
sha512_process_bytes (tests[cnt].input, strlen (tests[cnt].input), &ctx); | |
sha512_finish_ctx (&ctx, sum); | |
if (memcmp (tests[cnt].result, sum, 64) != 0) | |
{ | |
printf ("test %d run %d failed\n", cnt, 1); | |
result = 1; | |
} | |
sha512_init_ctx (&ctx); | |
for (int i = 0; tests[cnt].input[i] != '\0'; ++i) | |
sha512_process_bytes (&tests[cnt].input[i], 1, &ctx); | |
sha512_finish_ctx (&ctx, sum); | |
if (memcmp (tests[cnt].result, sum, 64) != 0) | |
{ | |
printf ("test %d run %d failed\n", cnt, 2); | |
result = 1; | |
} | |
} | |
/* Test vector from FIPS 180-2: appendix C.3. */ | |
char buf[1000]; | |
memset (buf, 'a', sizeof (buf)); | |
sha512_init_ctx (&ctx); | |
for (int i = 0; i < 1000; ++i) | |
sha512_process_bytes (buf, sizeof (buf), &ctx); | |
sha512_finish_ctx (&ctx, sum); | |
static const char expected[64] = | |
"\xe7\x18\x48\x3d\x0c\xe7\x69\x64\x4e\x2e\x42\xc7\xbc\x15\xb4\x63" | |
"\x8e\x1f\x98\xb1\x3b\x20\x44\x28\x56\x32\xa8\x03\xaf\xa9\x73\xeb" | |
"\xde\x0f\xf2\x44\x87\x7e\xa6\x0a\x4c\xb0\x43\x2c\xe5\x77\xc3\x1b" | |
"\xeb\x00\x9c\x5c\x2c\x49\xaa\x2e\x4e\xad\xb2\x17\xad\x8c\xc0\x9b"; | |
if (memcmp (expected, sum, 64) != 0) | |
{ | |
printf ("test %d failed\n", cnt); | |
result = 1; | |
} | |
for (cnt = 0; cnt < ntests2; ++cnt) | |
{ | |
char *cp = sha512_crypt (tests2[cnt].input, tests2[cnt].salt); | |
if (strcmp (cp, tests2[cnt].expected) != 0) | |
{ | |
printf ("test %d: expected \"%s\", got \"%s\"\n", | |
cnt, tests2[cnt].expected, cp); | |
result = 1; | |
} | |
} | |
if (result == 0) | |
puts ("all tests OK"); | |
return result; | |
} | |
#endif | |
----------------------------------------------------------------------------- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* The idea was stolen here: | |
* http://stackoverflow.com/questions/34463134/ | |
* (sha-512-crypt-output-written-with-python-code-is-different-from-mkpasswd) | |
* | |
* Actual code was rewritten using specification at http://www.akkadia.org/drepper/SHA-crypt.txt | |
* | |
* Crypto primitives for sha512_crypt() were taken here | |
* https://github.com/tomyun/crypto-js/blob/xeit-3.1.2/src/sha512.js | |
* | |
* sha512_crypt_r() was written from scratch entierly. | |
* It's accompanied with u64() "type" implementation to handle 64-bit unsigned integers. | |
* There are methods with "64" suffix (like .add64()) which are "optimized" versions. | |
* That optimization was enforced by JS timeouts when the code was ran in FireFox... | |
* The implementation of CYCLIC(value, bits) was moved into u64 as .rcyclic() method. | |
* The code is supposed to be endianness-aware... I tried to. | |
* | |
* Optimized: context.total became just "numeric" limiting total size of the data to be hashed. | |
* | |
* L<XXX> refs are for URLs like this: | |
* https://gist.github.com/jn0/6fced5fb655cd374d58d747bab5b1467#file-sha-crypt-txt-L<XXX> | |
* | |
* The test case. | |
* The hash (real world one, taken from /etc/shadow): | |
* '$6$TkGdx9fY$'+ | |
* 'OP22sJrVgigytriACCj0eK596d/5FJknPVPPzct/jfdhse3XErIxoaOLWN9pxpqGHy9Kn6yavRBz5GG1bTvSV0' | |
* Its binary value should be (single line): | |
* 38fdb6b09ad5822832b6b8800828f478ae7de9dff91499273d53cfcdcb7f8df7 | |
* 61b1edd712b231a1a38b58df69c69a861f2f4a9fac9abd1073e461b56d3bd257 | |
* | |
* Anyway, it doesn't work :( | |
*/ | |
'use strict'; | |
var str2hex = function(s) { // any string to string of hex | |
return s.split('') | |
.map(function(b){return ('0' + b.charCodeAt(0).toString(16)).slice(-2)}) | |
.join('') | |
} | |
var _BigEndian = undefined; | |
function BigEndian() | |
{ | |
if (_BigEndian === undefined) { | |
var a = new Uint32Array([0x12345678]); | |
var b = new Uint8Array(a.buffer, a.byteOffset, a.byteLength); | |
_BigEndian = (b[0] == 0x12); | |
} | |
return _BigEndian; | |
} | |
// console.log('BigEndian: ' + BigEndian()); | |
var U64_MAX_HI_OUTPUT_MASK = parseInt('1'.repeat(64-53), 2) | |
function u64(hi, lo) | |
{ | |
this.ALL_32_1 = 4294967295; // 0xffffffff | |
if ((hi !== undefined) && (lo === undefined)) { lo = hi; hi = 0; } | |
if (hi === undefined) hi = 0; | |
if (lo === undefined) lo = 0; | |
if (hi > this.ALL_32_1) this.hi = hi & this.ALL_32_1; else this.hi = hi; | |
if (lo > this.ALL_32_1) this.lo = lo & this.ALL_32_1; else this.lo = lo; | |
this.norm = function() { | |
this.hi &= this.ALL_32_1; | |
this.lo &= this.ALL_32_1; | |
return this; | |
} | |
this.lshift = function(bits) { | |
// X << bits | |
bits = parseInt(bits); | |
if (bits < 0) { | |
return this.rshift(0 - bits); | |
} | |
var u64r = this.copy(); | |
if (bits >= 64) { | |
u64r.hi = 0; | |
u64r.lo = 0; | |
return u64r; | |
} | |
if (bits >= 32) { | |
bits -= 32; | |
u64r.hi = bits ? ((this.lo << bits) & this.ALL_32_1) : this.lo; | |
u64r.lo = 0; | |
return u64r; | |
} | |
var off = u64r.lo >>> (32 - bits); | |
u64r.lo <<= bits; | |
u64r.hi = (u64r.hi << (32 - bits)) | off; | |
return u64r.norm(); | |
} | |
this.rshift = function(bits) { | |
// X >> bits | |
bits = parseInt(bits); | |
if (bits < 0) { | |
return this.lshift(0 - bits); | |
} | |
var u64r = this.copy(); | |
if (bits >= 64) { | |
u64r.hi = 0; | |
u64r.lo = 0; | |
return u64r; | |
} | |
if (bits >= 32) { | |
bits -= 32; | |
u64r.lo = (bits ? (u64r.hi >>> bits) : u64r.hi) & u64r.ALL_32_1; | |
u64r.hi = 0; | |
return u64r; | |
} | |
var off = u64r.hi & ((-1) >>> (32 - bits)); | |
u64r.hi = u64r.hi >>> bits; | |
u64r.lo = (off << (32 - bits)) | (u64r.lo >>> bits); | |
return u64r.norm(); | |
} | |
this.rcyclic = function(bits) { | |
// X >> bits >> X | |
bits = parseInt(bits) % 64; | |
if (bits < 0) { | |
throw new Error('not implemented'); | |
} | |
var u64r = this.copy(); | |
if (bits >= 32) { | |
var t = u64r.lo; | |
u64r.lo = u64r.hi; | |
u64r.hi = t; | |
bits -= 32; | |
} | |
var move = u64r.hi & ((-1) >>> (32 - bits)); | |
var lost = u64r.lo & ((-1) >>> (32 - bits)); | |
u64r.hi = (lost << (32 - bits)) | (u64r.hi >>> bits); | |
u64r.lo = (move << (32 - bits)) | (u64r.lo >>> bits); | |
return u64r; | |
} | |
this.not = function() { | |
return new u64(~this.hi, ~this.lo); | |
} | |
this.and64 = function(v) { | |
return new u64(this.hi & v.hi, this.lo & v.lo); | |
} | |
this.and = function(v) { | |
if (v instanceof u64) | |
return this.and64(v); | |
if ((typeof v) == 'number') | |
return new u64(this.hi, this.lo & v); | |
throw new Error('Wrong type: '+(typeof u64v)); | |
} | |
this.or64 = function(v) { | |
return new u64(this.hi | v.hi, this.lo | v.lo); | |
} | |
this.or = function(v) { | |
if (v instanceof u64) | |
return this.or64(v); | |
if ((typeof v) == 'number') | |
return new u64(this.hi, this.lo | v); | |
throw new Error('Wrong type: '+(typeof u64v)); | |
} | |
this.xor64 = function(v) { | |
return new u64(this.hi ^ v.hi, this.lo ^ v.lo); | |
} | |
this.xor = function(v) { | |
if (v instanceof u64) | |
return this.xor64(v); | |
if ((typeof v) == 'number') | |
return new u64(this.hi, this.lo ^ v); | |
throw new Error('Wrong type: '+(typeof u64v)); | |
} | |
this._carry_add = function(errorOnOverflow) { | |
var overflow = (errorOnOverflow === undefined) ? false : errorOnOverflow; | |
if (this.lo > this.ALL_32_1) { | |
this.hi += 1; | |
} | |
if (this.hi > this.ALL_32_1) { | |
if (overflow) throw new Error('u64 overflow'); | |
} | |
return this.norm(); | |
} | |
this.inc64 = function(v) { | |
// this += v // nocheks | |
this.hi += v.hi; | |
this.lo += v.lo; | |
return this._carry_add(); | |
} | |
this.inc = function(v) { | |
// this += v | |
if (v === undefined) { | |
return this.inc(1); | |
} | |
if (v instanceof u64) { | |
return this.inc64(v); | |
} | |
if ((typeof v) == 'number') { | |
this.lo += v; | |
return this._carry_add(); | |
} | |
throw new Error('Wrong type: '+(typeof u64v)); | |
} | |
this.dec = function(v) { | |
// this -= v | |
if (v === undefined) { | |
return this.dec(1); | |
} | |
if ((typeof v) == 'number') { | |
// I don't want even to think it twice! | |
return this.dec(U64(0, v)); | |
} | |
if (v instanceof u64) { | |
// Base-32 arithmetics... | |
var base = this.ALL_32_1 + 1; // i.e. 10 in 32-ry | |
var tmp = this.lo - v.lo; | |
if (tmp < 0) { | |
// borrow from .hi | |
this.lo = base + tmp; | |
this.hi += ((this.hi >= -tmp) ? 0 : base) + tmp; | |
} else { | |
this.lo = tmp; | |
} | |
tmp = this.hi - v.hi; | |
if (tmp < 0) { | |
// borrow from nowhere | |
this.hi = base + tmp; | |
} else { | |
this.hi = tmp; | |
} | |
return this.norm(); | |
} | |
throw new Error('Wrong type: '+(typeof u64v)); | |
} | |
this.add64 = function(v) { | |
// this + v // speedup | |
var r = new u64(0, 0); | |
r.hi = this.hi + v.hi; | |
r.lo = this.lo + v.lo; | |
return r._carry_add(); | |
} | |
this.add = function(v) { | |
// this + v | |
var r = new u64(this); | |
r.inc(v); | |
return r; | |
} | |
this.subtract = function(v) { | |
// this - v | |
return this.get().dec(v); | |
} | |
this.equal = function(v) { | |
// this == v | |
if (v instanceof u64) { | |
return (this.hi == v.hi) && (this.hi == v.hi); | |
} | |
if ((typeof v) == 'number') { | |
return (this.hi == 0) && (this.lo == v); | |
} | |
throw new Error('Wrong type: '+(typeof u64v)); | |
} | |
this.lt = this.less = function(v) { | |
// this < v | |
if (v instanceof u64) { | |
if (this.hi < v.hi) return true; | |
if (this.hi > v.hi) return false; | |
return this.lo < v.lo; | |
} | |
if ((typeof v) == 'number') { | |
return (this.hi == 0) && (this.lo < v); | |
} | |
throw new Error('Wrong type: '+(typeof u64v)); | |
} | |
this.ge = this.greater_or_equal = function(v) { | |
// this < v | |
if (v instanceof u64) { | |
if (this.hi > v.hi) return true; | |
if (this.hi < v.hi) return false; | |
return this.lo >= v.lo; | |
} | |
if ((typeof v) == 'number') { | |
return (this.hi == 0) && (this.lo >= v); | |
} | |
throw new Error('Wrong type: '+(typeof u64v)); | |
} | |
this.set64 = function(v) { | |
// speed hack: no checks, etc. | |
this.hi = v.hi; | |
this.lo = v.lo; | |
return this; | |
} | |
this.set = function(v, lv) { | |
// this := v | |
if (lv !== undefined) { | |
if (((typeof v) == 'number') && ((typeof lv) == 'number')) { | |
return this.set(U64(v, lv)); | |
} | |
throw new Error('Cannot u64.set(' + v.toSource() + ', ' + lv.toSource() + ')'); | |
} | |
if (v instanceof u64) { | |
this.hi = v.hi; | |
this.lo = v.lo; | |
return this.norm(); | |
} | |
if ((typeof v) == 'number') { | |
if (v <= this.ALL_32_1) { | |
this.hi = 0; | |
this.lo = v; | |
} else { | |
this.lo = v % Math.pow(2, 32); | |
this.hi = parseInt(v / Math.pow(2, 32)); | |
} | |
return this.norm(); | |
} | |
throw new Error('Wrong type: '+(typeof u64v)); | |
} | |
this.get = this.copy = function() { | |
return new u64(this.hi, this.lo); | |
} | |
this.toString = this.toSource = function(base) { | |
var b = (base === undefined) ? 16 : base; | |
var p = ''; | |
switch(b) { | |
case 16 : p = '0x'; break; | |
case 10 : p = ''; break; | |
case 8 : p = '0'; break; | |
case 2 : p = '0b'; break; | |
default : p = '?'; break; | |
} | |
return 'u64(' + p + this.hi.toString(b) + ',' + p + this.lo.toString(b) + ')'; | |
} | |
this.bytes = function() { | |
var bytes = []; | |
var add_bytes = BigEndian() ? function(v) { | |
for (var i = 0; i < 4; i++) { | |
bytes.unshift((v >>> (i * 8)) & 255); | |
} | |
} : function(v) { | |
for (var i = 0; i < 4; i++) { | |
bytes.push((v >>> (i * 8)) & 255); | |
} | |
} | |
if (BigEndian()) { | |
add_bytes(this.hi); | |
add_bytes(this.lo); | |
} else { | |
add_bytes(this.lo); | |
add_bytes(this.hi); | |
} | |
return bytes; | |
} | |
this.chars = function() { | |
return this.bytes().map(function(n){return String.fromCharCode(n)}).join(''); | |
} | |
this.is_zero = function() { | |
return (this.hi == 0) && (this.lo == 0); | |
} | |
this.set_bits = function(v) { | |
var res = this.get(); | |
if (v instanceof u64) { | |
res.hi |= v.hi; | |
res.lo |= v.lo; | |
} | |
if ((typeof v) == 'number') { | |
res.lo |= v; | |
} | |
return res; | |
} | |
this.clear_bits = function(v) { | |
var res = this.get(); | |
if (v instanceof u64) { | |
res.hi &= ~(v.hi); | |
res.lo &= ~(v.lo); | |
} | |
if ((typeof v) == 'number') { | |
res.lo &= ~v; | |
} | |
return res; | |
} | |
this.swap = function() { | |
// 0x123456789abcdef0 --> 0xf0debc9a78563412, i.e. | |
// u64(0x12345678,0x9abcdef0).swap() --> u64(0xf0debc9a,0x78563412) | |
var w = new Uint32Array([1,2]); | |
var b = new Uint8Array(w.buffer, w.byteOffset, w.byteLength); | |
b.set(this.bytes().reverse()); | |
return new u64(w[1], w[0]); | |
} | |
this.integer = function() { | |
if (this.hi & U64_MAX_HI_OUTPUT_MASK) { | |
throw new Error('Integer overflow'); | |
} | |
return this.hi * Math.pow(2, 32) + this.lo; | |
} | |
return this; | |
} | |
function U64(h, l) { return new u64(h, l) } | |
function u64_parse(bytes) | |
{ | |
if (bytes.length < 8) { | |
throw new Error('Wrong byte array size: ' + bytes.length); | |
} | |
var w = new Uint32Array([0, 0]); | |
var b = new Uint8Array(w.buffer, w.byteOffset, w.byteLength); | |
for (var i = 0; i < 8; i++) { | |
b[i] = bytes.charCodeAt(i); | |
} | |
return BigEndian() ? (new u64(w[0], w[1])) : (new u64(w[1], w[0])); | |
/* | |
var b = bytes.slice(0, 8).split('').map(function(c){return c.charCodeAt(0)}); | |
var res = U64(0); | |
if (BigEndian()) { | |
var b2w = function(x) { return (x[0] << 24) | (x[1] << 16) | (x[2] << 8) | (x[3]) } | |
res.set(b2w(b.slice(0, 4)), b2w(b.slice(4, 8))); | |
} else { | |
var b2w = function(x) { return (x[3] << 24) | (x[2] << 16) | (x[1] << 8) | (x[0]) } | |
res.set(b2w(b.slice(4, 8)), b2w(b.slice(0, 4))); | |
} | |
return res; | |
*/ | |
} | |
function SWAP(n) // L1111 | |
{ | |
if (!(n instanceof u64)) throw new Error('Wrong type: '+(typeof n)); | |
return BigEndian() ? n : n.swap(); | |
} | |
var Z128bits = '\0'.repeat(16); | |
function SWAP_int_as_string_128(n) | |
{ | |
if (n == 0) { | |
return Z128bits; | |
} | |
var hi = n >>> 32; | |
var lo = n & 4294967295; | |
var w; | |
if (BigEndian()) { | |
w = new Uint32Array([0, 0, hi, lo]); | |
} else { | |
w = new Uint32Array([lo, hi, 0, 0]); | |
} | |
var b = new Uint8Array(w.buffer, w.byteOffset, w.byteLength); | |
var r = []; | |
for (var i = 0; i < b.byteLength; i++) { | |
r.unshift(b[i]); | |
} | |
return r.join(''); | |
} | |
/* | |
var nn = new u64(0x12345678, 0x9abcdef0); | |
console.log('nn=' + nn + '; bytes=' + nn.bytes() + '; swap=' + nn.swap()); | |
var xx = SWAP(nn); | |
console.log('xx=' + xx); | |
*/ | |
var sha512_K; // L1131 | |
var sha512_fillbuf; | |
function sha512_ctx() | |
{ | |
this.H = [ // 8 x u64 | |
U64(0, 0), U64(0, 0), | |
U64(0, 0), U64(0, 0), | |
U64(0, 0), U64(0, 0), | |
U64(0, 0), U64(0, 0) | |
]; | |
this.total = 0; // was: [ U64(0, 0), U64(0, 0) ]; // 2 x u64 | |
this.buflen = 0; | |
this.buffer = '\0'.repeat(256); // 256 x u8 | |
if (sha512_fillbuf === undefined) { | |
/* This array contains the bytes used to pad the buffer to the next | |
64-byte boundary. (FIPS 180-2:5.1.2) * / | |
static const unsigned char fillbuf[128] = { 0x80, 0 / * , 0, 0, ... * / }; */ | |
sha512_fillbuf = '\x80' + '\0'.repeat(127); | |
} | |
if (sha512_K === undefined) { | |
sha512_K = new Array( // L1131 | |
U64(0x428a2f98, 0xd728ae22), U64(0x71374491, 0x23ef65cd), | |
U64(0xb5c0fbcf, 0xec4d3b2f), U64(0xe9b5dba5, 0x8189dbbc), | |
U64(0x3956c25b, 0xf348b538), U64(0x59f111f1, 0xb605d019), | |
U64(0x923f82a4, 0xaf194f9b), U64(0xab1c5ed5, 0xda6d8118), | |
U64(0xd807aa98, 0xa3030242), U64(0x12835b01, 0x45706fbe), | |
U64(0x243185be, 0x4ee4b28c), U64(0x550c7dc3, 0xd5ffb4e2), | |
U64(0x72be5d74, 0xf27b896f), U64(0x80deb1fe, 0x3b1696b1), | |
U64(0x9bdc06a7, 0x25c71235), U64(0xc19bf174, 0xcf692694), | |
U64(0xe49b69c1, 0x9ef14ad2), U64(0xefbe4786, 0x384f25e3), | |
U64(0x0fc19dc6, 0x8b8cd5b5), U64(0x240ca1cc, 0x77ac9c65), | |
U64(0x2de92c6f, 0x592b0275), U64(0x4a7484aa, 0x6ea6e483), | |
U64(0x5cb0a9dc, 0xbd41fbd4), U64(0x76f988da, 0x831153b5), | |
U64(0x983e5152, 0xee66dfab), U64(0xa831c66d, 0x2db43210), | |
U64(0xb00327c8, 0x98fb213f), U64(0xbf597fc7, 0xbeef0ee4), | |
U64(0xc6e00bf3, 0x3da88fc2), U64(0xd5a79147, 0x930aa725), | |
U64(0x06ca6351, 0xe003826f), U64(0x14292967, 0x0a0e6e70), | |
U64(0x27b70a85, 0x46d22ffc), U64(0x2e1b2138, 0x5c26c926), | |
U64(0x4d2c6dfc, 0x5ac42aed), U64(0x53380d13, 0x9d95b3df), | |
U64(0x650a7354, 0x8baf63de), U64(0x766a0abb, 0x3c77b2a8), | |
U64(0x81c2c92e, 0x47edaee6), U64(0x92722c85, 0x1482353b), | |
U64(0xa2bfe8a1, 0x4cf10364), U64(0xa81a664b, 0xbc423001), | |
U64(0xc24b8b70, 0xd0f89791), U64(0xc76c51a3, 0x0654be30), | |
U64(0xd192e819, 0xd6ef5218), U64(0xd6990624, 0x5565a910), | |
U64(0xf40e3585, 0x5771202a), U64(0x106aa070, 0x32bbd1b8), | |
U64(0x19a4c116, 0xb8d2d0c8), U64(0x1e376c08, 0x5141ab53), | |
U64(0x2748774c, 0xdf8eeb99), U64(0x34b0bcb5, 0xe19b48a8), | |
U64(0x391c0cb3, 0xc5c95a63), U64(0x4ed8aa4a, 0xe3418acb), | |
U64(0x5b9cca4f, 0x7763e373), U64(0x682e6ff3, 0xd6b2b8a3), | |
U64(0x748f82ee, 0x5defb2fc), U64(0x78a5636f, 0x43172f60), | |
U64(0x84c87814, 0xa1f0ab72), U64(0x8cc70208, 0x1a6439ec), | |
U64(0x90befffa, 0x23631e28), U64(0xa4506ceb, 0xde82bde9), | |
U64(0xbef9a3f7, 0xb2c67915), U64(0xc67178f2, 0xe372532b), | |
U64(0xca273ece, 0xea26619c), U64(0xd186b8c7, 0x21c0c207), | |
U64(0xeada7dd6, 0xcde0eb1e), U64(0xf57d4f7f, 0xee6ed178), | |
U64(0x06f067aa, 0x72176fba), U64(0x0a637dc5, 0xa2c898a6), | |
U64(0x113f9804, 0xbef90dae), U64(0x1b710b35, 0x131c471b), | |
U64(0x28db77f5, 0x23047d84), U64(0x32caab7b, 0x40c72493), | |
U64(0x3c9ebe0a, 0x15c9bebc), U64(0x431d67c4, 0x9c100d4c), | |
U64(0x4cc5d4be, 0xcb3e42b6), U64(0x597f299c, 0xfc657e2a), | |
U64(0x5fcb6fab, 0x3ad6faec), U64(0x6c44198c, 0x4a475817) | |
); | |
} | |
/* Operators defined in FIPS 180-2:4.1.2. */ // L1214 | |
this.Ch = function(x, y, z) { // L1215 | |
return (x.and64(y)) | |
.xor64((x.not()) | |
.and64(z)); | |
} | |
this.Maj = function(x, y, z) { // L1216 | |
return (x.and64(y)) | |
.xor64(x.and64(z)) | |
.xor64(y.and64(z)); | |
} | |
this.S0 = function(x) { // L1217 | |
//return this.CYCLIC(x, 28) | |
// .xor64(this.CYCLIC(x, 34)) | |
// .xor64(this.CYCLIC(x, 39)); | |
return (x.rcyclic(28)) | |
.xor64(x.rcyclic(34)) | |
.xor64(x.rcyclic(39)); | |
} | |
this.S1 = function(x) { // L1218 | |
//return this.CYCLIC(x, 14) | |
// .xor64(this.CYCLIC(x, 18)) | |
// .xor64(this.CYCLIC (x, 41)); | |
return (x.rcyclic(14)) | |
.xor64(x.rcyclic(18)) | |
.xor64(x.rcyclic(41)); | |
} | |
this.R0 = function(x) { // L1219 | |
//return this.CYCLIC(x, 1) | |
// .xor64(this.CYCLIC(x, 8)) | |
// .xor64(x.rshift(7)); | |
return (x.rcyclic(1)) | |
.xor64(x.rcyclic(8)) | |
.xor64(x.rshift(7)); | |
} | |
this.R1 = function(x) { // L1220 | |
//return this.CYCLIC(x, 19) | |
// .xor64(this.CYCLIC(x, 61)) | |
// .xor64(x.rshift(6)); | |
return (x.rcyclic(19)) | |
.xor64(x.rcyclic(61)) | |
.xor64(x.rshift(6)); | |
} | |
/* It is unfortunate that C does not provide an operator for | |
cyclic rotation. Hope the C compiler is smart enough. */ | |
this.CYCLIC = function(w, s) { // L1224 | |
return (w.rshift(s)) | |
.or64(w.lshift(64 - s)); | |
} | |
/* Process LEN bytes of BUFFER, accumulating context into CTX. | |
It is assumed that LEN % 128 == 0. */ | |
this.process_block = function(len) { // L1177 | |
var words = []; // = buffer // const uint64_t * | |
// the "buffer" is not "this.buffer" only in case of aligned handling at L1381 | |
var nwords = parseInt(len / 8); | |
var tmp = this.buffer.slice(); | |
for (var t = 0; t < nwords; t++) { | |
words.push(u64_parse(tmp)); | |
tmp = tmp.slice(8); | |
} | |
var a = this.H[0], | |
b = this.H[1], | |
c = this.H[2], | |
d = this.H[3], | |
e = this.H[4], | |
f = this.H[5], | |
g = this.H[6], | |
h = this.H[7]; | |
var T1 = new u64(0); | |
var T2 = new u64(0); | |
var t = 0; | |
/* First increment the byte count. FIPS 180-2 specifies the possible | |
length of the file up to 2^128 bits. Here we only compute the | |
number of bytes. Do a double word increment. * / | |
this.total[0].inc(len); // L1196 | |
if (this.total[0].less(len)) { // L1197 | |
this.total[1].inc(1); // L1198 | |
} | |
* | |
I don't need passwords of 4e37 bytes long. | |
Hence, only simple 53-bit "numbers" here. | |
*/ | |
this.total += len; | |
/* Process all bytes in the buffer with 128 bytes in each round of | |
the loop. */ | |
while (nwords) { // L1202 | |
var W = [ /* | |
U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), // 8 | |
U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), // 16 | |
U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), // 24 | |
U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), // 32 | |
U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), // 40 | |
U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), // 48 | |
U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), // 56 | |
U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), // 64 | |
U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), // 72 | |
U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0), U64(0) // 80 | |
*/ ]; | |
var a_save = a.get(); | |
var b_save = b.get(); | |
var c_save = c.get(); | |
var d_save = d.get(); | |
var e_save = e.get(); | |
var f_save = f.get(); | |
var g_save = g.get(); | |
var h_save = h.get(); | |
/* Compute the message schedule according to FIPS 180-2:6.3.2 step 2. */ | |
for (t = 0; t < 16; ++t) { // L1227 | |
// W[t] = SWAP(words[t]); | |
// W.push(SWAP(words[t])); | |
W.push(words[t].swap()); | |
} | |
for (t = 16; t < 80; ++t) { // L1232 | |
// W[t] = R1 (W[t - 2]) + W[t - 7] + R0 (W[t - 15]) + W[t - 16]; | |
W.push(this.R1(W[t - 2]) | |
.inc64(W[t - 7]) | |
.inc64(this.R0(W[t - 15])) | |
.inc64(W[t - 16]) | |
); | |
} | |
/* The actual computation according to FIPS 180-2:6.3.2 step 3. */ | |
for (t = 0; t < 80; ++t) { // L1236 | |
// T1 = h + S1 (e) + Ch (e, f, g) + K[t] + W[t]; | |
T1.set64(h) | |
.inc64(this.S1(e)) | |
.inc64(this.Ch(e, f, g)) | |
.inc64(sha512_K[t]) | |
.inc64(W[t]); | |
// T2 = S0 (a) + Maj (a, b, c); | |
T2.set64(this.S0(a)) | |
.inc64(this.Maj(a, b, c)); | |
h.set64(g); | |
g.set64(f); | |
f.set64(e); | |
e.set64(d.inc64(T1)); | |
d.set64(c); | |
c.set64(b); | |
b.set64(a); | |
a.set64(T1.inc64(T2)); | |
} | |
/* Add the starting values of the context according to FIPS 180-2:6.3.2 | |
step 4. */ | |
a.inc64(a_save); // L1252 | |
b.inc64(b_save); | |
c.inc64(c_save); | |
d.inc64(d_save); | |
e.inc64(e_save); | |
f.inc64(f_save); | |
g.inc64(g_save); | |
h.inc64(h_save); | |
/* Prepare for the next round. */ | |
nwords -= 16; // L1262 | |
} // L1263 | |
/* Put checksum in context given as argument. */ // L1265 | |
this.H[0].set64(a); | |
this.H[1].set64(b); | |
this.H[2].set64(c); | |
this.H[3].set64(d); | |
this.H[4].set64(e); | |
this.H[5].set64(f); | |
this.H[6].set64(g); | |
this.H[7].set64(h); | |
} // end of this.process_block // L1274 | |
/* Initialize structure containing state of computation. | |
(FIPS 180-2:5.3.3) */ | |
this.init = function() { // L1280 | |
this.H[0].set(0x6a09e667, 0xf3bcc908); | |
this.H[1].set(0xbb67ae85, 0x84caa73b); | |
this.H[2].set(0x3c6ef372, 0xfe94f82b); | |
this.H[3].set(0xa54ff53a, 0x5f1d36f1); | |
this.H[4].set(0x510e527f, 0xade682d1); | |
this.H[5].set(0x9b05688c, 0x2b3e6c1f); | |
this.H[6].set(0x1f83d9ab, 0xfb41bd6b); | |
this.H[7].set(0x5be0cd19, 0x137e2179); | |
// this.total[0].set(0); | |
// this.total[1].set(0); | |
this.total = 0; | |
this.buflen = 0; | |
this.buffer = ''; | |
} | |
/* Process the remaining bytes in the internal buffer and the usual | |
prolog according to the standard and write the result to RESBUF. | |
IMPORTANT: On some systems it is required that RESBUF is correctly | |
aligned for a 32 bits value. */ | |
this.finish = function() { // L1302 | |
/* Take yet unprocessed bytes into account. */ | |
var bytes = this.buflen; | |
var pad = (bytes >= 112) ? (128 + 112 - bytes) : (112 - bytes); // L1313 | |
/* Now count remaining bytes. * / | |
this.total[0].inc(bytes); // L1309 | |
if (this.total[0].less(bytes)) { | |
this.total[1].inc(1); | |
} | |
*/ | |
this.total += bytes; | |
/* memcpy (&ctx->buffer[bytes], fillbuf, pad); */ | |
if (this.buffer.length != bytes) debugger; | |
this.buffer += sha512_fillbuf.slice(0, pad); // L1314 | |
/* Put the 128-bit file length in *bits* at the end of the buffer. * / | |
*(uint64_t *) &ctx->buffer[bytes + pad + 8] = SWAP(ctx->total[0] << 3); | |
*(uint64_t *) &ctx->buffer[bytes + pad] = SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 61)); | |
* / | |
this.buffer += SWAP(this.total[1].lshift(3).or(this.total[0].rshift(61))).chars(); // L1318 | |
this.buffer += SWAP(this.total[0].lshift(3)).chars(); // L1317 | |
*/ | |
// SWAP...(this.total >>> 61); we have 53 bits only | |
this.buffer += SWAP_int_as_string_128(this.total * 8); // + 16 bytes, in bits, no <<3 here! | |
if ((bytes + pad + 16) % 64) debugger; | |
/* Process last bytes. */ | |
this.process_block(bytes + pad + 16); // L1322 | |
/* Put result from CTX in first 64 bytes following RESBUF. */ | |
var res = ''; | |
for (var i = 0; i < 8; i++) { | |
res += SWAP(this.H[i]).chars(); | |
} | |
if (res.length != 64) debugger; | |
return res; | |
} | |
this.update = this.process_bytes = function(buffer, len) { // L1333 | |
var left_over = this.buflen; // L1339 | |
var tmp = ''; | |
/* When we already have some bits in our internal buffer concatenate | |
both inputs first. */ | |
if (left_over != 0) { | |
var add = ((256 - left_over) > len) ? len : (256 - left_over); | |
// memcpy (&ctx->buffer[left_over], buffer, add); | |
tmp = buffer.slice(0, add); | |
this.buffer += tmp; | |
this.buflen += add; | |
if (this.buflen > 128) { // L1345 | |
this.process_block(this.buflen & ~127); | |
this.buflen &= 127; | |
/* The regions in the following copy operation cannot overlap. * | |
memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~127], ctx->buflen); */ | |
var idx1 = (left_over + add) & ~127; | |
var idx2 = idx1 + this.buflen; | |
// buffer = 'xxxxxxxx<idx1>yyy<idx2>zzzz' | |
// > 'yyy<buflen>xxxxxyyyzzzz' | |
// but buflen makes it 'yyy<buflen>' | |
// tmp = this.buffer.slice(idx2); | |
// this.buffer = this.buffer.slice(idx1, idx2) + tmp; | |
tmp = this.buffer.slice(idx1, idx2); | |
this.buffer = tmp; | |
} | |
buffer = buffer.slice(add); | |
len -= add; | |
} | |
/* Process available complete blocks. */ | |
while (len > 128) { // L1360, L1371 | |
/* ??? ignore alignment ??? (L1370) */ | |
this.buffer = buffer.slice(0, 128) + this.buffer(128); | |
this.process_block(128); | |
buffer = buffer.slice(128); | |
len -= 128; | |
} | |
/* Move remaining bytes into internal buffer. */ | |
if (len > 0) { // L1388 | |
left_over = this.buflen; | |
// memcpy (&ctx->buffer[left_over], buffer, len); | |
tmp = this.buffer.slice(0, left_over); | |
this.buffer = tmp + buffer.slice(0, len); | |
left_over += len; | |
if (left_over >= 128) { | |
this.process_block(128); | |
left_over -= 128; | |
this.buffer = buffer.slice(128, 128 + left_over); // L1398 | |
} | |
this.buflen = left_over; | |
} | |
} | |
return this; | |
} // end of function sha512_ctx | |
function sha512_crypt_r(key, salt, rounds_in) | |
{ | |
var rounds = (rounds_in !== undefined) ? rounds_in : 5000; // L1444 | |
salt = salt.slice(0, 16); | |
var salt_len = salt.length; | |
var key_len = key.length; | |
var ctx = new sha512_ctx(); | |
var alt_ctx = new sha512_ctx(); | |
var cnt = 0; | |
var alt_result = ''; | |
var temp_result = ''; | |
/* Prepare for the real work. */ | |
ctx.init(); // L1489 | |
/* Add the key string. */ | |
ctx.process_bytes(key, key_len); | |
/* The last part is the salt string. This must be at most 16 | |
characters and it ends at the first `$' character (for | |
compatibility with existing implementations). */ | |
ctx.process_bytes(salt, salt_len); | |
/* Compute alternate SHA512 sum with input KEY, SALT, and KEY. The | |
final result will be added to the first context. */ | |
alt_ctx.init(); | |
/* Add key. */ | |
alt_ctx.process_bytes (key, key_len); | |
/* Add salt. */ | |
alt_ctx.process_bytes (salt, salt_len); | |
/* Add key again. */ | |
alt_ctx.process_bytes (key, key_len); | |
/* Now get result of this (64 bytes) and add it to the other | |
context. */ | |
alt_result = alt_ctx.finish(); | |
/* Add for any character in the key one byte of the alternate sum. */ | |
for (cnt = key_len; cnt > 64; cnt -= 64) { | |
ctx.process_bytes (alt_result, 64); | |
} | |
ctx.process_bytes (alt_result, cnt); | |
/* Take the binary representation of the length of the key and for every | |
1 add the alternate sum, for every 0 the key. */ | |
for (cnt = key_len; cnt > 0; cnt >>>= 1) { | |
if ((cnt & 1) != 0) { | |
ctx.process_bytes (alt_result, 64); | |
} else { | |
ctx.process_bytes (key, key_len); | |
} | |
} | |
/* Create intermediate result. */ | |
alt_result = ctx.finish(); | |
/* Start computation of P byte sequence. */ | |
alt_ctx.init(); | |
/* For every character in the password add the entire password. */ | |
for (cnt = 0; cnt < key_len; ++cnt) { | |
alt_ctx.process_bytes (key, key_len); | |
} | |
/* Finish the digest. */ | |
temp_result = alt_ctx.finish(); | |
/* Create byte sequence P. */ | |
var cp = ''; | |
for (cnt = key_len; cnt >= 64; cnt -= 64) { | |
cp += temp_result.slice(0, 64); | |
} | |
cp += temp_result.slice(0, cnt); | |
var p_bytes = cp.slice(); | |
/* Start computation of S byte sequence. */ | |
alt_ctx.init(); | |
/* For every character in the password add the entire password. */ | |
for (cnt = 0; cnt < 16 + alt_result.charCodeAt(0); ++cnt) { | |
alt_ctx.process_bytes (salt, salt_len); | |
} | |
/* Finish the digest. */ | |
temp_result = alt_ctx.finish(); | |
/* Create byte sequence S. */ | |
cp = ''; | |
for (cnt = salt_len; cnt >= 64; cnt -= 64) { | |
cp += temp_result.slice(0, 64); | |
} | |
cp += temp_result.slice(0, cnt); | |
var s_bytes = cp.slice(); | |
/* Repeatedly run the collected hash value through SHA512 to burn | |
CPU cycles. */ | |
for (cnt = 0; cnt < rounds; ++cnt) { | |
/* New context. */ | |
ctx.init(); | |
/* Add key or last result. */ | |
if ((cnt & 1) != 0) { | |
ctx.process_bytes (p_bytes, key_len); | |
} else { | |
ctx.process_bytes (alt_result, 64); | |
} | |
/* Add salt for numbers not divisible by 3. */ | |
if (cnt % 3 != 0) { | |
ctx.process_bytes (s_bytes, salt_len); | |
} | |
/* Add key for numbers not divisible by 7. */ | |
if (cnt % 7 != 0) { | |
ctx.process_bytes (p_bytes, key_len); | |
} | |
/* Add key or last result. */ | |
if ((cnt & 1) != 0) { | |
ctx.process_bytes (alt_result, 64); | |
} else { | |
ctx.process_bytes (p_bytes, key_len); | |
} | |
/* Create intermediate result. */ | |
alt_result = ctx.finish(); | |
} | |
return alt_result; | |
/* | |
* It's supposed to continue handling to produce that base64-encoded | |
* '$6$'-prefixed salt-included character string... | |
*/ | |
} // end of function sha512_crypt_r | |
// ============================================================================ | |
// This part relays on the CryptoJS library: | |
// include crypto-js/core.js | |
// include crypto-js/sha512.js | |
// Fetch data from hash object and return its byte-string value | |
function hash_bytes(obj) | |
{ | |
var digest = obj.finalize(); | |
var hexstring = digest.toString(CryptoJS.enc.Hex); | |
var bytes = []; | |
/* optimized out | |
if (hexstring.length & 1) { | |
console.warn('hashObjToBytes('+obj+'): odd size! '+hexstring.length); | |
hexstring = '0' + hexstring; // make it even sized | |
} | |
*/ | |
for (var i = 0; i < hexstring.length; i += 2) { | |
bytes.push(parseInt('0x' + hexstring.slice(i, i + 2))); | |
} | |
// return bytes.map(String.fromCharCode).join(''); << This wont work AS EXPECTED! | |
return bytes.map(function(x){return String.fromCharCode(x)}).join(''); | |
} | |
// From https://gist.github.com/jn0/6fced5fb655cd374d58d747bab5b1467#file-sha-crypt-txt-L170 | |
function sha512_crypt(password, salt, rounds_in) | |
{ | |
var rounds = (rounds_in !== undefined) ? rounds_in : 5000; // L106 | |
var hasher = CryptoJS.algo.SHA512; | |
if ((rounds < 1000) || (rounds > 999999999)) { // L110 | |
console.error('bad rounds ' + rounds); | |
rounds = 5000; | |
} | |
salt = salt.slice(0, 16); // L1413 | |
var A = hasher.create(); // SECTION 1. L1489 | |
A.update(password); // SECTION 2. L1492 | |
A.update(salt); // SECTION 3. L1497 | |
var B = hasher.create(); // SECTION 4. L1502 | |
B.update(password); // SECTION 5. L1505 | |
B.update(salt); // SECTION 6. L1508 | |
B.update(password); // SECTION 7. L1511 | |
var digestB = hash_bytes(B); // SECTION 8. L1515 | |
var i = password.length; // L1518 | |
while (i > 64) { // | |
A.update(digestB); // SECTION 9. L1519 | |
i -= 64; // | |
} | |
A.update(digestB.slice(0, i)); // SECTION 10. L1520 | |
for (i = password.length; i > 0; i = i >>> 1) { // | |
A.update((i & 1) ? digestB : password); // SECTION 11 | |
} | |
var digestA = hash_bytes(A); // SECTION 12. | |
var DP = hasher.create() // SECTION 13. | |
for (i = 0; i < password.length; i++) { // | |
DP.update(password); // SECTION 14. | |
} | |
var digestDP = hash_bytes(DP); // SECTION 15. | |
var P = digestDP.repeat(parseInt(password.length / 64)); // 16. produce byte sequence P of | |
// the same length as the password | |
P += digestDP.slice(0, password.length % 64); // SECTION 16.b) | |
var DS = hasher.create(); // SECTION 17. | |
var A0 = digestA.charCodeAt(0) & 0x0ff; // SECTION 18. | |
for (i = 0; i < (16 + A0); i++) { // | |
DS.update(salt); // | |
} | |
var digestDS = hash_bytes(DS); // SECTION 19. | |
var S = digestDS.repeat(parseInt(salt.length / 64)); // 20. produce byte sequence S of | |
// the same length as the salt string | |
S += digestDS.slice(0, salt.length % 64); // SECTION 20.b) | |
// SECTION 21. (https://gist.github.com/jn0/6fced5fb655cd374d58d747bab5b1467#file-sha-crypt-txt-L249) | |
var digest_AC = digestA.slice(); | |
for (i = 0; i < rounds; i++) { // | |
var C = hasher.create(); // SECTION 21.a) | |
C.update((i & 1) ? P : digest_AC); // SECTION 21.b) & 21.c) | |
if (i % 3) { C.update(S); } // SECTION 21.d) | |
if (i % 7) { C.update(P); } // SECTION 21.e) | |
C.update((i & 1) ? digest_AC : P); // SECTION 21.f) & 21.g) | |
var digestC = hash_bytes(C); // SECTION 21.h) | |
digest_AC = digestC.slice(); | |
} | |
/* digest_AC has the hash we need, binary */ | |
// SECTION 22 (https://gist.github.com/jn0/6fced5fb655cd374d58d747bab5b1467#file-sha-crypt-txt-L275) | |
var out = '$6$'; // SECTION 22.a) & L87 | |
if (rounds_in !== undefined) { // SECTION 22.b) | |
out += 'rounds=' + rounds_in + '$'; // L96 | |
} | |
out += salt.slice(0, 16); // SECTION 22.c) | |
out += '$'; // SECTION 22.d) | |
// SECTION 22.e) | |
var b64t = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; // L1422 | |
var b64_from_24bit = function(digest, BL, N) { // L1618 | |
var w = 0, woff = 0, bidx = 0; | |
while (BL.length) { // 3 or 1 actually | |
bidx = BL.pop(); // last-to-first | |
/* optimized out | |
if (digest.charCodeAt(bidx) > 0x0ff) { | |
console.error('At '+bidx+' value '+digest.charCodeAt(bidx)); | |
} | |
*/ | |
w |= digest.charCodeAt(bidx) << woff; | |
woff += 8; | |
} | |
var r = ''; | |
while (N-- > 0) { | |
r += b64t[w & 0x3f]; | |
w = w >>> 6; | |
} | |
return r; | |
} | |
var b64 = ''; | |
b64 += b64_from_24bit(digest_AC, [ 0, 21, 42], 4); // L1630 | |
b64 += b64_from_24bit(digest_AC, [22, 43, 1], 4); | |
b64 += b64_from_24bit(digest_AC, [44, 2, 23], 4); | |
b64 += b64_from_24bit(digest_AC, [ 3, 24, 45], 4); | |
b64 += b64_from_24bit(digest_AC, [25, 46, 4], 4); | |
b64 += b64_from_24bit(digest_AC, [47, 5, 26], 4); | |
b64 += b64_from_24bit(digest_AC, [ 6, 27, 48], 4); | |
b64 += b64_from_24bit(digest_AC, [28, 49, 7], 4); | |
b64 += b64_from_24bit(digest_AC, [50, 8, 29], 4); | |
b64 += b64_from_24bit(digest_AC, [ 9, 30, 51], 4); | |
b64 += b64_from_24bit(digest_AC, [31, 52, 10], 4); | |
b64 += b64_from_24bit(digest_AC, [53, 11, 32], 4); | |
b64 += b64_from_24bit(digest_AC, [12, 33, 54], 4); | |
b64 += b64_from_24bit(digest_AC, [34, 55, 13], 4); | |
b64 += b64_from_24bit(digest_AC, [56, 14, 35], 4); | |
b64 += b64_from_24bit(digest_AC, [15, 36, 57], 4); | |
b64 += b64_from_24bit(digest_AC, [37, 58, 16], 4); | |
b64 += b64_from_24bit(digest_AC, [59, 17, 38], 4); | |
b64 += b64_from_24bit(digest_AC, [18, 39, 60], 4); | |
b64 += b64_from_24bit(digest_AC, [40, 61, 19], 4); | |
b64 += b64_from_24bit(digest_AC, [62, 20, 41], 4); | |
b64 += b64_from_24bit(digest_AC, [ 63], 2); | |
out += b64; | |
if (b64.length != 86) { // L123 | |
console.error('PWD size: ' + b64.length + ' (expected 86)'); | |
return null; | |
} | |
if (out.length > 123) { // L130 | |
console.error('Max size: 123, current: ' + out.length); | |
return null; | |
} | |
return out; | |
} | |
// end of function sha512_crypt() | |
/* | |
actual = sha512_crypt('password', 'salt1234') | |
expected = '$6$salt1234$Zr07alHmuONZlfKILiGKKULQZaBG6Qmf5smHCNH35KnciTapZ7dItwaCv5SKZ1xH9ydG59SCgkdtsTqVWGhk81' | |
console.log('expected=' + expected); | |
console.log('actual=' + actual); | |
console.log('actual == expected: ' + (actual == expected)); | |
*/ | |
var my1 = '$6$TkGdx9fY$OP22sJrVgigytriACCj0eK596d/5FJknPVPPzct/jfdhse3XErIxoaOLWN9pxpqGHy9Kn6yavRBz5GG1bTvSV0'; | |
var xxx, yyy; | |
console.log('Expect: ' + my1); | |
var pwd = my1.split('$').pop(); | |
console.log('AA=[' + str2hex(window.atob(pwd)) + ']'); | |
var password = 'demo', salt = 'TkGdx9fY'; | |
xxx = sha512_crypt_r(password, salt); | |
console.log('xx=[' + str2hex(xxx) + ']'); | |
// EOF // |
sha512_context.total became generic "numeral"...
Bug fix + context move
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
u64() reviewed and fixed.