|
|
@@ -20,6 +20,235 @@
|
|
|
|
|
|
#include "fareqr.h"
|
|
|
|
|
|
+// AES CBC, sha256 message digest, with salt.
|
|
|
+// Functions modified from https://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption
|
|
|
+//
|
|
|
+// openssl example commands to encrypt/decrypt:
|
|
|
+// msg=";123456789012345?"
|
|
|
+// key="oovevobie8woid1Ou3iu6aboochei2AeKoceeCh1iePheuRae4Yai1dahtheegi7"
|
|
|
+// openssl enc -aes-256-cbc -in <( echo -n "$msg" ) -out /dev/stdout -pass pass:"$key" -e -base64 -md sha256
|
|
|
+//
|
|
|
+// ---
|
|
|
+//
|
|
|
+// to descypt with openssl
|
|
|
+//
|
|
|
+// openssl aes-256-cbc -d -a -in <( echo "U2FsdGVkX19gpKzk+Y5SGgnQr1uwpUYUpea6nu77wJfG6bD4GZpRneSaxaallz0n" ) -out /dev/stdout -pass pass:"$key" -md sha256
|
|
|
+//
|
|
|
+
|
|
|
+
|
|
|
+static int aes_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *iv, unsigned char *ciphertext) {
|
|
|
+ EVP_CIPHER_CTX *ctx;
|
|
|
+
|
|
|
+ int len;
|
|
|
+
|
|
|
+ int ciphertext_len;
|
|
|
+
|
|
|
+ // Create and initialise the context
|
|
|
+ if(!(ctx = EVP_CIPHER_CTX_new())) { return -1; }
|
|
|
+
|
|
|
+ // Initialise the encryption operation. IMPORTANT - ensure you use a key
|
|
|
+ // and IV size appropriate for your cipher
|
|
|
+ // In this example we are using 256 bit AES (i.e. a 256 bit key). The
|
|
|
+ // IV size for *most* modes is the same as the block size. For AES this
|
|
|
+ // is 128 bits
|
|
|
+ //
|
|
|
+ if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)) { return -2; }
|
|
|
+
|
|
|
+ // Provide the message to be encrypted, and obtain the encrypted output.
|
|
|
+ // EVP_EncryptUpdate can be called multiple times if necessary
|
|
|
+ //
|
|
|
+ if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) { return -3; }
|
|
|
+ ciphertext_len = len;
|
|
|
+
|
|
|
+ // Finalise the encryption. Further ciphertext bytes may be written at
|
|
|
+ // this stage.
|
|
|
+ //
|
|
|
+ if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) { return -4; }
|
|
|
+ ciphertext_len += len;
|
|
|
+
|
|
|
+ // Clean up
|
|
|
+ //
|
|
|
+ EVP_CIPHER_CTX_free(ctx);
|
|
|
+
|
|
|
+ return ciphertext_len;
|
|
|
+}
|
|
|
+
|
|
|
+static int aes_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *iv, unsigned char *plaintext) {
|
|
|
+ EVP_CIPHER_CTX *ctx;
|
|
|
+ int plaintext_len, len;
|
|
|
+
|
|
|
+ // Create and initialise the context
|
|
|
+ //
|
|
|
+ if(!(ctx = EVP_CIPHER_CTX_new())) { return -1; }
|
|
|
+
|
|
|
+ // Initialise the decryption operation. IMPORTANT - ensure you use a key
|
|
|
+ // and IV size appropriate for your cipher
|
|
|
+ // In this example we are using 256 bit AES (i.e. a 256 bit key). The
|
|
|
+ // IV size for *most* modes is the same as the block size. For AES this
|
|
|
+ // is 128 bits
|
|
|
+ //
|
|
|
+ if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)) { return -2; }
|
|
|
+
|
|
|
+ // Provide the message to be decrypted, and obtain the plaintext output.
|
|
|
+ // EVP_DecryptUpdate can be called multiple times if necessary.
|
|
|
+ //
|
|
|
+ if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) { return -3; }
|
|
|
+ plaintext_len = len;
|
|
|
+
|
|
|
+ // Finalise the decryption. Further plaintext bytes may be written at
|
|
|
+ // this stage.
|
|
|
+ //
|
|
|
+ if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) { return -4; }
|
|
|
+ plaintext_len += len;
|
|
|
+
|
|
|
+ // Clean up
|
|
|
+ //
|
|
|
+ EVP_CIPHER_CTX_free(ctx);
|
|
|
+
|
|
|
+ return plaintext_len;
|
|
|
+}
|
|
|
+
|
|
|
+// Helper function to take secret key/pass (pass_key) and the encoded
|
|
|
+// base64 string and decrypt it with AES CBC 256, sha256 message digest.
|
|
|
+//
|
|
|
+// return the number of bytes decoded on success
|
|
|
+// return non-positive number on error
|
|
|
+//
|
|
|
+static int decode_b64(unsigned char *decrypt_text,
|
|
|
+ unsigned char *pass_key,
|
|
|
+ unsigned char *enc_b64) {
|
|
|
+ int i=0, _ret = 0, r=0;
|
|
|
+ unsigned char *enc_b = NULL, salt[8];
|
|
|
+ unsigned char *key = NULL, *iv = NULL, *ciphertext_b = NULL;
|
|
|
+ size_t b64_sz=0, enc_len=0, enc_size=0, ciphertext_len;
|
|
|
+
|
|
|
+ if ((!decrypt_text) || (!pass_key) || (!enc_b64)) { return -9; }
|
|
|
+
|
|
|
+ enc_size = Base64decode_len(enc_b64);
|
|
|
+ if (enc_size < 1) {
|
|
|
+ _ret=-1;
|
|
|
+ goto _decode_b64_cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ enc_b = (unsigned char *)calloc(enc_size, sizeof(char));
|
|
|
+ if (!enc_b) {
|
|
|
+ _ret=-2;
|
|
|
+ goto _decode_b64_cleanup;
|
|
|
+ }
|
|
|
+ Base64decode(enc_b, enc_b64);
|
|
|
+ enc_len = enc_size-1;
|
|
|
+
|
|
|
+ // Check exmpanded string is well formed (has "Salted__" prefix, etc.)
|
|
|
+ // and extract the salt
|
|
|
+ //
|
|
|
+ if (enc_len < 16) {
|
|
|
+ _ret = -3;
|
|
|
+ goto _decode_b64_cleanup;
|
|
|
+ }
|
|
|
+ if (strncmp(enc_b, "Salted__", 8)!=0) {
|
|
|
+ _ret = -4;
|
|
|
+ goto _decode_b64_cleanup;
|
|
|
+ }
|
|
|
+ for (i=0; i<8; i++) { salt[i] = enc_b[i+8]; }
|
|
|
+
|
|
|
+ // Move past the salt prefix 8 bytes) and 8 bytes of salt
|
|
|
+ //
|
|
|
+ ciphertext_b = enc_b + 16;
|
|
|
+ ciphertext_len = enc_len - 16;
|
|
|
+
|
|
|
+ // Allocate iv, key and extract them from the salt and pass phrase
|
|
|
+ //
|
|
|
+ iv = (unsigned char *)calloc(256, sizeof(char));
|
|
|
+ key = (unsigned char *)calloc(256, sizeof(char));
|
|
|
+ r = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha256(), salt, pass_key, strlen(pass_key), 1, key, iv);
|
|
|
+ if (!r) {
|
|
|
+ _ret = -5;
|
|
|
+ goto _decode_b64_cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ r = aes_decrypt(ciphertext_b, ciphertext_len, key, iv, decrypt_text);
|
|
|
+ if (r<=0) {
|
|
|
+ _ret = -6;
|
|
|
+ goto _decode_b64_cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ decrypt_text[r] = '\0';
|
|
|
+ _ret = r;
|
|
|
+
|
|
|
+_decode_b64_cleanup:
|
|
|
+
|
|
|
+ if (enc_b) { free(enc_b); }
|
|
|
+ if (key) { free(key); }
|
|
|
+ if (iv) { free(iv); }
|
|
|
+
|
|
|
+ return _ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int encode_b64(unsigned char *enc_b64,
|
|
|
+ unsigned char *pass_key,
|
|
|
+ unsigned char *msg) {
|
|
|
+ int i=0, _ret = 0, r=0;
|
|
|
+ unsigned char *enc_b = NULL;
|
|
|
+ unsigned char *key = NULL, *iv = NULL, *ciphertext_b = NULL;
|
|
|
+ size_t b64_sz=0, enc_len=0, enc_size=0, ciphertext_len;
|
|
|
+
|
|
|
+ unsigned char salt[8];
|
|
|
+ char _pfx[] = "Salted__";
|
|
|
+
|
|
|
+ if ((!enc_b64) || (!pass_key) || (!msg)) { return -9; }
|
|
|
+
|
|
|
+ for (i=0; i<8; i++) { salt[i] = (unsigned char)(rand() % 256); }
|
|
|
+
|
|
|
+ iv = (unsigned char *)calloc(256, sizeof(char));
|
|
|
+ key = (unsigned char *)calloc(256, sizeof(char));
|
|
|
+ r = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha256(), salt, pass_key, strlen(pass_key), 1, key, iv);
|
|
|
+ if (!r) {
|
|
|
+ _ret = -5;
|
|
|
+ goto _encode_b64_cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ enc_b = (unsigned char *)calloc(2*LINE_BUFFER_SIZE, sizeof(char));
|
|
|
+ if (!enc_b) {
|
|
|
+ _ret = -4;
|
|
|
+ goto _encode_b64_cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i=0; i<8; i++) {
|
|
|
+ enc_b[i] = _pfx[i];
|
|
|
+ enc_b[i+8] = salt[i];
|
|
|
+ }
|
|
|
+
|
|
|
+ enc_len = aes_encrypt(msg, strlen(msg), key, iv, enc_b+16);
|
|
|
+ if (enc_len<=0) {
|
|
|
+ _ret = -6;
|
|
|
+ goto _encode_b64_cleanup;
|
|
|
+ }
|
|
|
+ enc_len += 16;
|
|
|
+
|
|
|
+ b64_sz = Base64encode_len(enc_len);
|
|
|
+ Base64encode(enc_b64, enc_b, enc_len);
|
|
|
+ _ret = b64_sz;
|
|
|
+
|
|
|
+_encode_b64_cleanup:
|
|
|
+
|
|
|
+ if (enc_b) { free(enc_b); }
|
|
|
+ if (key) { free(key); }
|
|
|
+ if (iv) { free(iv); }
|
|
|
+
|
|
|
+ return _ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+// `fqreqr_lookup_decode` uses the public key portion of `fareqr_s`
|
|
|
+// to lookup the private pass/key in the file `seedfn`.
|
|
|
+// If found, it then proceeds to decrypt and store the encrypted
|
|
|
+// credential in `dst_cred`
|
|
|
+//
|
|
|
+// returns 0 on success
|
|
|
+// returns non zero on error
|
|
|
+//
|
|
|
int fareqr_lookup_decode(char *seedfn, char *fareqr_s, char *dst_cred) {
|
|
|
int i, n, r, _ret = 0;;
|
|
|
char *p=NULL, *stop_tok=NULL;
|
|
|
@@ -51,7 +280,8 @@ int fareqr_lookup_decode(char *seedfn, char *fareqr_s, char *dst_cred) {
|
|
|
r = fareqr_lookup_seed_secret(seedfn, pub_key, priv_key);
|
|
|
if (r<0) { _ret = r; }
|
|
|
else {
|
|
|
- r = fareqr_decode(fareqr_s, pub_key, priv_key, dst_cred);
|
|
|
+ //r = fareqr_decode(fareqr_s, pub_key, priv_key, dst_cred);
|
|
|
+ r = decode_b64(dst_cred, priv_key, enc_str);
|
|
|
if (r<0) { _ret = r; }
|
|
|
}
|
|
|
|
|
|
@@ -64,6 +294,8 @@ int fareqr_lookup_decode(char *seedfn, char *fareqr_s, char *dst_cred) {
|
|
|
return _ret;
|
|
|
}
|
|
|
|
|
|
+// depreicated (doing away with one-time pads in favor of AES above)
|
|
|
+//
|
|
|
int fareqr_encode(char *tok_public, char *tok_secret, char *tok_cred, char *fareqr_str) {
|
|
|
int i, r;
|
|
|
uint8_t x,y,z;
|
|
|
@@ -106,6 +338,8 @@ int fareqr_encode(char *tok_public, char *tok_secret, char *tok_cred, char *fare
|
|
|
}
|
|
|
|
|
|
|
|
|
+// depreicated (doing away with one-time pads in favor of AES above)
|
|
|
+//
|
|
|
int fareqr_decode(char *fareqr_s, char *check_pub, char *tok_secret, char *dst_cred) {
|
|
|
int i, n, _ret=0;
|
|
|
char *p, *stop_tok;
|
|
|
@@ -273,11 +507,11 @@ void show_help(FILE *ofp) {
|
|
|
fprintf(ofp, "Here is some example usage:\n");
|
|
|
fprintf(ofp, "\n");
|
|
|
fprintf(ofp, " $ fareqr encode 'wu9XouSh' 'ohNgizahkephain3aosoh2AeH1aethoo4cie6oiSaezimaighai2eiVaefahfien' ';123456789060535?'\n");
|
|
|
- fprintf(ofp, " @wu9XouSh%VFl8VF1PV19TXEBeUVxdBl4=\n");
|
|
|
- fprintf(ofp, " $ fareqr decode 'ohNgizahkephain3aosoh2AeH1aethoo4cie6oiSaezimaighai2eiVaefahfien' '@wu9XouSh%VFl8VF1PV19TXEBeUVxdBl4=$'\n");
|
|
|
+ fprintf(ofp, " @wu9XouSh%%VFl8VF1PV19TXEBeUVxdBl4=\n");
|
|
|
+ fprintf(ofp, " $ fareqr decode 'ohNgizahkephain3aosoh2AeH1aethoo4cie6oiSaezimaighai2eiVaefahfien' '@wu9XouSh%%VFl8VF1PV19TXEBeUVxdBl4=$'\n");
|
|
|
fprintf(ofp, " ;123456789060535?\n");
|
|
|
fprintf(ofp, " $ echo 'wu9XouSh ohNgizahkephain3aosoh2AeH1aethoo4cie6oiSaezimaighai2eiVaefahfien' > ./qr.seed\n");
|
|
|
- fprintf(ofp, " $ fareqr dbdecode ./qr.seed '@wu9XouSh%VFl8VF1PV19TXEBeUVxdBl4=$'\n");
|
|
|
+ fprintf(ofp, " $ fareqr dbdecode ./qr.seed '@wu9XouSh%%VFl8VF1PV19TXEBeUVxdBl4=$'\n");
|
|
|
fprintf(ofp, " ;123456789060535?\n");
|
|
|
fprintf(ofp, "\n");
|
|
|
fprintf(ofp, "Where 'wu9XouSh' is the public key, 'ohNgizahkephain3aosoh2AeH1aethoo4cie6oiSaezimaighai2eiVaefahfien' is the private key and\n");
|
|
|
@@ -386,6 +620,52 @@ int main(int argc, char **argv) {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ // test out our AES encryption
|
|
|
+ //
|
|
|
+ else if (strcmp(argv[1], "aes-decode")==0) {
|
|
|
+ if (argc>2) {
|
|
|
+ tok_secret = strdup(argv[2]);
|
|
|
+ if (argc>3) {
|
|
|
+ fareqr_str = strdup(argv[3]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ tok_cred = (unsigned char *)calloc(LINE_BUFFER_SIZE, sizeof(char));
|
|
|
+ r = decode_b64(tok_cred, tok_secret, fareqr_str);
|
|
|
+ if (r<0) {
|
|
|
+ fprintf(stderr, "could not decode aes base64 string (AES CBC 256, sha256 md) (got %i)\n", r);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ printf("%s\n", tok_cred);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // test out our AES encryption
|
|
|
+ //
|
|
|
+ else if (strcmp(argv[1], "aes-encode")==0) {
|
|
|
+ if (argc>2) {
|
|
|
+ tok_secret = strdup(argv[2]);
|
|
|
+ if (argc>3) {
|
|
|
+ tok_cred = strdup(argv[3]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ printf(">>> '%s' '%s'\n", tok_secret, tok_cred);
|
|
|
+
|
|
|
+
|
|
|
+ fareqr_str = (unsigned char *)calloc(2*LINE_BUFFER_SIZE, sizeof(char));
|
|
|
+ r = encode_b64(fareqr_str, tok_secret, tok_cred);
|
|
|
+ if (r<0) {
|
|
|
+ fprintf(stderr, "could not encode aes base64 string (AES CBC 256, sha256 md) (got %i)\n", r);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ printf("%s\n", fareqr_str);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
else if (strcmp(argv[1], "help")==0) {
|
|
|
show_help(stdout);
|
|
|
exit(0);
|