mirror of
https://github.com/cesanta/mongoose.git
synced 2024-12-24 00:07:49 +08:00
171 lines
5.1 KiB
C
171 lines
5.1 KiB
C
|
/*
|
||
|
* C CGI Library version 1.2
|
||
|
*
|
||
|
* Author: Stephen C. Losen, University of Virginia
|
||
|
*
|
||
|
* Copyright 2015 Stephen C. Losen. Distributed under the terms
|
||
|
* of the GNU Lesser General Public License (LGPL 2.1)
|
||
|
*
|
||
|
* CGI_encrypt() converts arbitrary input data to a secure,
|
||
|
* encrypted string, using a password provided by the user.
|
||
|
* CGI_decrypt() reverses the process and returns the original
|
||
|
* data. The secure string includes a message digest for
|
||
|
* verifying the data.
|
||
|
*/
|
||
|
|
||
|
#include <sys/time.h>
|
||
|
#include <unistd.h>
|
||
|
#include <string.h>
|
||
|
#include <openssl/rand.h>
|
||
|
#include <openssl/evp.h>
|
||
|
#include <ccgi.h>
|
||
|
|
||
|
#define SALT_SIZE 8
|
||
|
#define DIGEST_SIZE 20
|
||
|
|
||
|
/*
|
||
|
* init_salt() places 8 random bytes into salt with RAND_bytes()
|
||
|
* or else uses SHA1 digest of current time and process ID
|
||
|
*/
|
||
|
|
||
|
static void
|
||
|
init_salt(unsigned char *salt) {
|
||
|
EVP_MD_CTX ctx;
|
||
|
unsigned char md[DIGEST_SIZE];
|
||
|
struct timeval tv;
|
||
|
pid_t pid;
|
||
|
unsigned int rlen;
|
||
|
|
||
|
if (RAND_bytes(salt, SALT_SIZE) == 1) {
|
||
|
return;
|
||
|
}
|
||
|
gettimeofday(&tv, 0);
|
||
|
pid = getpid();
|
||
|
EVP_DigestInit(&ctx, EVP_sha1());
|
||
|
EVP_DigestUpdate(&ctx, &tv, sizeof(tv));
|
||
|
EVP_DigestUpdate(&ctx, &pid, sizeof(pid));
|
||
|
EVP_DigestFinal(&ctx, md, &rlen);
|
||
|
memcpy(salt, md, SALT_SIZE);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* digest() feeds input p of length len, the password and
|
||
|
* the salt to SHA1 and returns the digest in md.
|
||
|
*/
|
||
|
|
||
|
static void
|
||
|
digest(const void *p, int len, const char *password,
|
||
|
const unsigned char *salt, unsigned char *md)
|
||
|
{
|
||
|
EVP_MD_CTX ctx;
|
||
|
unsigned int rlen;
|
||
|
|
||
|
EVP_DigestInit(&ctx, EVP_sha1());
|
||
|
EVP_DigestUpdate(&ctx, salt, SALT_SIZE);
|
||
|
EVP_DigestUpdate(&ctx, password, strlen(password));
|
||
|
EVP_DigestUpdate(&ctx, p, len);
|
||
|
EVP_DigestFinal(&ctx, md, &rlen);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CGI_encrypt() returns an encrypted base64 string using input p
|
||
|
* of length len and the password. We generate a random 8 byte
|
||
|
* salt. We compute a SHA1 message digest using p, the password,
|
||
|
* and the salt. We generate a cipher key using the password and
|
||
|
* salt. We encrypt the digest and input with the AES-256-CBC cipher
|
||
|
* and key. We base64 encode the return string, which consists of
|
||
|
* the unencrypted salt, encrypted digest and encrypted input.
|
||
|
*/
|
||
|
|
||
|
char *
|
||
|
CGI_encrypt(const void *p, int len, const char *password) {
|
||
|
EVP_CIPHER_CTX ctx;
|
||
|
unsigned char md[DIGEST_SIZE];
|
||
|
unsigned char iv[EVP_MAX_IV_LENGTH];
|
||
|
unsigned char key[EVP_MAX_KEY_LENGTH];
|
||
|
unsigned char *out;
|
||
|
char *b64;
|
||
|
int offset, rlen;
|
||
|
|
||
|
if (p == 0 || len <= 0 || password == 0 || *password == 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
out = malloc(SALT_SIZE + DIGEST_SIZE + len + EVP_MAX_BLOCK_LENGTH);
|
||
|
init_salt(out);
|
||
|
digest(p, len, password, out, md);
|
||
|
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), out,
|
||
|
(unsigned char *) password, strlen(password), 1, key, iv);
|
||
|
EVP_EncryptInit(&ctx, EVP_aes_256_cbc(), key, iv);
|
||
|
offset = SALT_SIZE;
|
||
|
EVP_EncryptUpdate(&ctx, out + offset, &rlen, md, DIGEST_SIZE);
|
||
|
offset += rlen;
|
||
|
EVP_EncryptUpdate(&ctx, out + offset, &rlen, p, len);
|
||
|
offset += rlen;
|
||
|
EVP_EncryptFinal(&ctx, out + offset, &rlen);
|
||
|
b64 = CGI_encode_base64(out, offset + rlen);
|
||
|
free(out);
|
||
|
return b64;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CGI_decrypt() returns a pointer to data decrypted from
|
||
|
* base64 string p and the password. p was previously encrypted
|
||
|
* with CGI_encrypt(). We base64 decode p and the first 8 bytes
|
||
|
* is the salt followed by the encrypted message digest and
|
||
|
* encrypted data. We generate the cipher key using the salt
|
||
|
* and the password. We decrypt the message digest and data
|
||
|
* using the AES-256-CBC cipher and key. We re-compute the
|
||
|
* message digest using the decrypted data, password, and salt.
|
||
|
* If the two digests match then we return a pointer to the data
|
||
|
* and place its length in *len. Otherwise we return null.
|
||
|
*/
|
||
|
|
||
|
void *
|
||
|
CGI_decrypt(const char *p, int *len, const char *password) {
|
||
|
EVP_CIPHER_CTX ctx;
|
||
|
unsigned char md[DIGEST_SIZE];
|
||
|
unsigned char iv[EVP_MAX_IV_LENGTH];
|
||
|
unsigned char key[EVP_MAX_KEY_LENGTH];
|
||
|
unsigned char *ret, *out;
|
||
|
int offset, rlen = 0;
|
||
|
|
||
|
if (p == 0 || *p == 0 || password == 0 || *password == 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
ret = CGI_decode_base64(p, &rlen);
|
||
|
if (rlen <= DIGEST_SIZE + SALT_SIZE) {
|
||
|
free(ret);
|
||
|
return 0;
|
||
|
}
|
||
|
out = malloc(rlen + EVP_MAX_BLOCK_LENGTH);
|
||
|
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), ret,
|
||
|
(unsigned char *) password, strlen(password), 1, key, iv);
|
||
|
EVP_DecryptInit(&ctx, EVP_aes_256_cbc(), key, iv);
|
||
|
EVP_DecryptUpdate(&ctx, out, &offset, ret + SALT_SIZE,
|
||
|
rlen - SALT_SIZE);
|
||
|
EVP_DecryptFinal(&ctx, out + offset, &rlen);
|
||
|
rlen += offset - DIGEST_SIZE;
|
||
|
|
||
|
/*
|
||
|
* The salt is in ret, the decrypted digest and decrypted
|
||
|
* data are in out, and the data length is rlen.
|
||
|
*/
|
||
|
|
||
|
if (rlen > 0) {
|
||
|
digest(out + DIGEST_SIZE, rlen, password, ret, md);
|
||
|
}
|
||
|
if (rlen > 0 && memcmp(out, md, DIGEST_SIZE) == 0) {
|
||
|
memcpy(ret, out + DIGEST_SIZE, rlen);
|
||
|
ret[rlen] = 0;
|
||
|
if (len != 0) {
|
||
|
*len = rlen;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
free(ret);
|
||
|
ret = 0;
|
||
|
}
|
||
|
free(out);
|
||
|
return ret;
|
||
|
}
|