ОБНОВЛЕНИЕ: ПРОБЛЕМА РЕШЕНА
Как прокомментировали ниже несколько щедрых людей, проблема была связана со строковыми функциями, используемыми при работе с необработанными байтами. Основной проблемой было использование Sprintf внутри цикла, который теперь я изменил на memmove. Обновленный код следует:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
/*
Don't forget the compile switches:
gcc sha.c -lssl -lcrypto -lm -o sha
*/
typedef struct {
unsigned char raw_hash[EVP_MAX_MD_SIZE];
size_t hash_len;
} HashResult;/*
adapted from https://gist.github.com/barrysteyn/4409525
*/
int Base64Encode(const char* message, unsigned int message_size, char** buffer) { //Encodes a string to base64
BIO *bio, *b64;
FILE* stream;
int encodedSize = 4*ceil((double)message_size/3);
*buffer = (char *)malloc(encodedSize+1);
stream = fmemopen(*buffer, encodedSize+1, "w");
b64 = BIO_new(BIO_f_base64());
bio = BIO_new_fp(stream, BIO_NOCLOSE);
bio = BIO_push(b64, bio);
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Ignore newlines - write everything in one line
BIO_write(bio, message, message_size);
BIO_flush(bio);
BIO_free_all(bio);
fclose(stream);
return (0); //success
}HashResult HashThis(char message[], int m_len, EVP_MD_CTX mdctx) {
unsigned char md_value[EVP_MAX_MD_SIZE];
int md_len;
EVP_DigestInit_ex(&mdctx, EVP_sha512(), NULL);
EVP_DigestUpdate(&mdctx, message, m_len);
EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
HashResult hash;
memcpy(hash.raw_hash, md_value, md_len);
hash.hash_len = md_len;
return hash;
}main(int argc, char *argv[])
{
int i, l;
char salt[256] = "SALT";
char pass[256] = "PASSWORD";
char salted[512];
char digest[512];
char* base64EncodeOutput;
sprintf(salted, "%s{%s}", pass, salt);
printf("C: Salted is: >>>>>>%s<<<<<<\n", salted);
EVP_MD_CTX mdctx;
OpenSSL_add_all_digests();
EVP_MD_CTX_init(&mdctx);
int s_len = strlen(salted);
HashResult h_result = HashThis(salted, s_len, mdctx);
memcpy(digest, h_result.raw_hash, h_result.hash_len);
for(i = 1; i < 20; i++){
memmove(digest+h_result.hash_len, salted, s_len);
l = h_result.hash_len;
h_result.hash_len = 0;
memset(h_result.raw_hash, 0, l);
h_result = HashThis(digest, (int)l+s_len, mdctx);
memset(digest, 0, 512);
memcpy(digest, h_result.raw_hash, h_result.hash_len);
memset(base64EncodeOutput, 0, strlen(base64EncodeOutput));
Base64Encode(digest, l, &base64EncodeOutput);
printf("%d => %s\n", i, base64EncodeOutput);
}
EVP_MD_CTX_cleanup(&mdctx);
}
ОРИГИНАЛЬНАЯ ПОЧТА:
Я пытаюсь реализовать в C то же растяжение ключа, что и в PHP с помощью стандартного кодировщика паролей Symfony:
<?php
$password = "PASSWORD";
$salt = "SALT";
$salted = $password."{".$salt."}";
$digest = hash("sha512", $salted, true);
echo "PHP: Salted is: >>>>>>$salted<<<<<<\n";
for ($i = 1; $i < 20; ++$i) {
$digest = hash("sha512", $digest.$salted, true);
$encoded_password = base64_encode($digest);
echo "$i => $encoded_password\n";
}
?>
…пробую это:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
/*
Don't forget the compile switches:
gcc sha.c -lssl -lcrypto -lm -o sha
*/
typedef struct {
unsigned char raw_hash[EVP_MAX_MD_SIZE];
size_t hash_len;
} HashResult;/*
adapted from https://gist.github.com/barrysteyn/4409525
*/
int Base64Encode(const char* message, unsigned int message_size, char** buffer) { //Encodes a string to base64
BIO *bio, *b64;
FILE* stream;
int encodedSize = 4*ceil((double)message_size/3);
*buffer = (char *)malloc(encodedSize+1);
stream = fmemopen(*buffer, encodedSize+1, "w");
b64 = BIO_new(BIO_f_base64());
bio = BIO_new_fp(stream, BIO_NOCLOSE);
bio = BIO_push(b64, bio);
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Ignore newlines - write everything in one line
BIO_write(bio, message, message_size);
BIO_flush(bio);
BIO_free_all(bio);
fclose(stream);
return (0); //success
}HashResult HashThis(char message[], EVP_MD_CTX mdctx) {
unsigned char md_value[EVP_MAX_MD_SIZE];
int md_len;
EVP_DigestInit_ex(&mdctx, EVP_sha512(), NULL);
EVP_DigestUpdate(&mdctx, message, strlen(message));
EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
HashResult hash;
memcpy(hash.raw_hash, md_value, md_len);
hash.hash_len = md_len;
return hash;
}main(int argc, char *argv[])
{
int i, l;
char salt[256] = "SALT";
char pass[256] = "PASSWORD";
char salted[512];
char digest[512];
char* base64EncodeOutput;
sprintf(salted, "%s{%s}", pass, salt);
printf("C: Salted is: >>>>>>%s<<<<<<\n", salted);
EVP_MD_CTX mdctx;
OpenSSL_add_all_digests();
EVP_MD_CTX_init(&mdctx);
HashResult h_result = HashThis(salted, mdctx);
memcpy(digest, h_result.raw_hash, h_result.hash_len);
for(i = 1; i < 20; i++){
sprintf(digest, "%s%s", digest, salted);
l = h_result.hash_len;
h_result.hash_len = 0;
memset(h_result.raw_hash, 0, l);
h_result = HashThis(digest, mdctx);
memset(digest, 0, 512);
memcpy(digest, h_result.raw_hash, sizeof(h_result.raw_hash));
memset(base64EncodeOutput, 0, strlen(base64EncodeOutput));
Base64Encode(digest, l, &base64EncodeOutput);
printf("%d => %s\n", i, base64EncodeOutput);
}
EVP_MD_CTX_cleanup(&mdctx);
}
…дает следующие результаты:
C: Salted is: >>>>>>PASSWORD{SALT}<<<<<<
1 => 2yqDGoZ6NrdAKeS/yf7fCT/Zbv0/1Wa7cGwiy1pOvTpFE/I2Xfpajh8ukRhkM6j/8rv90C02ISeaz8K9awJLzw==
2 => QVn8YydCA/9J5j8YTnUlnDXtqq0yNRPVz2m1QFkZ7fwQBWY4rgjP/JUCIjQbL4JRyIZATf3Bsnh0OfhaT52f/A==
3 => ixpdJ4TaAY5POys95nhtU0Ghbu+yDgch3PL0UsPktxVUa5igEEdRmzMH5JZH7SrTkBaHUCa9ThKiSfA/TdbPog==
4 => Am1dkcqXZkXOfDuwsj/VaZy3j3CQnzDzbj1B4g4/dkCJ5g/0nXAti7PKdo5oxm8GIv3AoHHW6eldTbwGj3IgzA==
5 => mrfwPBiT3+TDl41xcf34p0A+eNHP18qWzm0uwOpzAarkfemzV5xsnz/x2QxuE4V7vNgYB8pVV6mYqRQkqitQRA==
6 => JIGX+XD6XVaPwP3LuJO/OToef2jBiCq70mKyg6PmvUARigS5VkZMrcbnI/PXdHXrZ081fVMYavJRsXJiXRhiaQ==
7 => oqU+PpM+HqJmUKcmOUMtIxvPgsCNyJ4zo+986ksr+Wvjp6ejDezFS49NuUXxi03GIj0ueLJxdPlIABvEBpd6RQ==
8 => lg8/4/xhjb0IIBndLnH00UM6Umyq5NrtwrwXcHCFgoRJK54m9sIUgtsPlJd7N3F1RDCoRhQBmXzrTz1vPXkQYQ==
9 => 2VAoIXuK960520NAL8iQUIbsTT4LRoEl5drxTXDfBEAPxCX+cA9hRKo2VTollak8x9VKTuzmR8c4zEKZhFlYAA==
10 => TxFIQe3ynULLQFyePIJq7v3dybsMMp9krpISG5UsR3qhXkcC50RwAH90n2LA7isxS9Cn2wiHbgKi3v4mLaWnWA==
11 => yhsHdTR7tlxZpoG2LsJWcG9UrNNsCgFf8cfLd/duwRNeD08aKuChv9hSpQAaXAKkkGlqqApmUGDu0G+gRVbLhg==
12 => SqJfMgE5WJoc5ImGoDaXhiYCoXmxVnBA3rDcGHC975Q8mtJMxpU4nxSe67dq+pSH5bpR02Se5QXypHeI/PwGLg==
13 => kbU6SUbAQhcysnw6O3RsjeX1xwKwd9j7L1ejJhhu1APa4ZzjFQmyFX3pHG7tnBitPPXRKxbgu49dRUZOtrtHgA==
14 => K4r4AxmWTZIT7YA+BaGrVWCrdjFk2EiIK+s352FZeAmzDUpiJe8wC6DuVYwfmY8z3krmYlw9k+cHvfAlDFVwZg==
15 => h7qPJz5mhzWPfeiQ+/quMWPmmb8yVjBc8KNvRN95h7Ycw7/nAj9+E19M2q1OaaJnT8xJ/ZzYUM4BV/vSol0Wpg==
16 => WHTn5zz5JY/x0iQf3J0rceaaa5d0J3kPhChrQZsI7t/OU1RSSuumsiEzrPs4m/p/RsCTbI1XBsydoHzOr7Xf8A==
17 => 5BTyu5Iujc6g2j7OhGAQJWqMyqC75lbEXCxUnixB1tse7YuhcrSD4Q977ijC8WGjt+dTxI4XiNWx8pR7p4ixoA==
18 => 0j+GqFg75KPJgeg0f4Tvhr95qNWgyQT8eiK+kRGatXvnO0Guq0LdJlw6LwA2Ymfhr+GBy1HVmahCuzWTjxKTNg==
19 => OGFBWgdqCjkkLSSbxywIekHqVXaXBHbm3NrxVYb+xPELr1efA/0L6S0xjynHH+NxIQ1cyQYlGeMAh+Ks3Rd08Q==PHP: Salted is: >>>>>>PASSWORD{SALT}<<<<<<
1 => 2yqDGoZ6NrdAKeS/yf7fCT/Zbv0/1Wa7cGwiy1pOvTpFE/I2Xfpajh8ukRhkM6j/8rv90C02ISeaz8K9awJLzw==
2 => QVn8YydCA/9J5j8YTnUlnDXtqq0yNRPVz2m1QFkZ7fwQBWY4rgjP/JUCIjQbL4JRyIZATf3Bsnh0OfhaT52f/A==
3 => ixpdJ4TaAY5POys95nhtU0Ghbu+yDgch3PL0UsPktxVUa5igEEdRmzMH5JZH7SrTkBaHUCa9ThKiSfA/TdbPog==
4 => Am1dkcqXZkXOfDuwsj/VaZy3j3CQnzDzbj1B4g4/dkCJ5g/0nXAti7PKdo5oxm8GIv3AoHHW6eldTbwGj3IgzA==
5 => mrfwPBiT3+TDl41xcf34p0A+eNHP18qWzm0uwOpzAarkfemzV5xsnz/x2QxuE4V7vNgYB8pVV6mYqRQkqitQRA==
6 => JIGX+XD6XVaPwP3LuJO/OToef2jBiCq70mKyg6PmvUARigS5VkZMrcbnI/PXdHXrZ081fVMYavJRsXJiXRhiaQ==
7 => oqU+PpM+HqJmUKcmOUMtIxvPgsCNyJ4zo+986ksr+Wvjp6ejDezFS49NuUXxi03GIj0ueLJxdPlIABvEBpd6RQ==
8 => nf9QhPhXnG7Fnh0t7YRXnfq/JlBUUogt9A8U+7aTA2A834c8SKiGqMgXqm7K7LUPHw3F8jwYxeMyXktPvUmMQQ==
9 => QvttW+hXvBL2DLA5qCfq2205C/6A+YhYRA4YY63Fb7kqGYq3vrQIiSr/xsl/o8HCZr1KZ+lvNw4+ds/r/yqmBg==
10 => tP0kRYQ1k3VAWXGqkHVh+i00e8WMODwmSh9UHtIcJm97sKVpum6iUzfKSEEHWbfjyUGG2P/+jgZLOe4LV1Rmig==
11 => Wxmdu8t1VCMf/6Inax8jCzCSOUEwiDDugoQafE4lYN8k5NkCXUyIvXINcg0Di5ayW0NnxNuOEmmXR6rdTopyrA==
12 => cNUPNmfJyCBZ6zKVaB2UCiiKyKzNKREPv7cBFCJFdrCB4t1Vqaw3TmldrjOiclJ3+w3tx4rTn2P1K1nP2SMcKg==
13 => AT1LD3sETQe50HmVcHmqgY6emY+sT0OZPSIRffKjHfV1xktejbQnGE1evfrls5MpacULmzgNJccjsbWnDomsVA==
14 => Um02XaFEiRm7oxQAQ7pUsxXxnhI9M6xSymapkKPrHBmhjrgcSPimMQ9tUi9Vc7H5OJlAvW0svM2e45pwZfxh+w==
15 => 68vct/q024/3EppVZo4fw4vWI2IYN/99RsjI7ebvrv6GZL0xwqV3ERXynGuLdTlILwQyovM9QA7tvRNduu3qqQ==
16 => PRfC+T2nLT9NRk/k+/XZ/UoNHYWGJR7naRLSX16+++rzjFVDMOnrQYFqEHWf+qTxqPMrS2NmyoD4P9pNr5d16A==
17 => N3l3MBljhgzNsd7K3x6dU9btwAlTaJMk/8S7Jp9ICrOss6FVc+hMKYkUuiuE1vT+P3DK6s+NeArWS8/DtBGyug==
18 => ihidFTx2BPBhHvq0WZ9yVEPoYciKApNsm9mSvSArZsf75rWrJRFA1fluusKllwNXPvbZinLczd8d8EQLNnsG5A==
19 => EDo6YqOQaUmd0Vp8cGZPe5G4Dta2j/JFtc4W4aYQLo5+OvKdolzLu6YLK3GjKHCJdpbj1fUsH7sKxH98UyFhxA==
Вывод сырых дайджестов покажет, что к 8-й итерации дайджест начинается и заканчивается так же, как в PHP, но в середине есть несколько байтов, отличающихся.
Есть идеи, почему это происходит?
Как кто-нибудь испытывал подобные результаты?
Мне нужно сделать это, чтобы я мог хэшировать пароли в устаревшей системе.
Любая помощь будет оценена.
Не используйте строковые функции для двоичных данных, такие как:
strlen(message)
или ваш код не удастся.
Других решений пока нет …