/* This file is part of Pretty Curved Privacy (pcp1). Copyright (C) 2013 T.Linden. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . You can contact me by mail: . */ #include "crypto.h" size_t pcp_sodium_box(unsigned char **cipher, unsigned char *cleartext, size_t clearsize, unsigned char *nonce, unsigned char *secret, unsigned char *pub) { unsigned char *pad_clear; unsigned char *pad_cipher; size_t ciphersize = (clearsize + crypto_box_ZEROBYTES) - crypto_box_BOXZEROBYTES; pad_cipher = ucmalloc(crypto_box_ZEROBYTES + clearsize); pcp_pad_prepend(&pad_clear, cleartext, crypto_box_ZEROBYTES, clearsize); // crypto_box(c,m,mlen,n,pk,sk); crypto_box(pad_cipher, pad_clear, clearsize + crypto_box_ZEROBYTES, nonce, pub, secret); pcp_pad_remove(cipher, pad_cipher, crypto_secretbox_BOXZEROBYTES, ciphersize); free(pad_clear); free(pad_cipher); return ciphersize; } int pcp_sodium_verify_box(unsigned char **cleartext, unsigned char* message, size_t messagesize, unsigned char *nonce, unsigned char *secret, unsigned char *pub) { // verify/decrypt the box unsigned char *pad_cipher; unsigned char *pad_clear; int success = -1; pcp_pad_prepend(&pad_cipher, message, crypto_box_BOXZEROBYTES, messagesize); pad_clear = (unsigned char *)ucmalloc((crypto_box_ZEROBYTES+ messagesize)); // crypto_box_open(m,c,clen,n,pk,sk); if (crypto_box_open(pad_clear, pad_cipher, messagesize + crypto_box_BOXZEROBYTES, nonce, pub, secret) == 0) { success = 0; } pcp_pad_remove(cleartext, pad_clear, crypto_box_ZEROBYTES, messagesize); free(pad_clear); free(pad_cipher); return success; } unsigned char *pcp_box_encrypt(pcp_key_t *secret, pcp_pubkey_t *pub, unsigned char *message, size_t messagesize, size_t *csize) { unsigned char *nonce = pcp_gennonce(); unsigned char *cipher; size_t es = pcp_sodium_box(&cipher, message, messagesize, nonce, secret->secret, pub->pub); if(es <= messagesize) { fatal("failed to encrypt message!\n"); goto errbec; } // scip //fprintf(stderr, "public: "); pcpprint_bin(stderr, pub->pub, 32); fprintf(stderr, "\n"); //fprintf(stderr, "secret: "); pcpprint_bin(stderr, secret->secret, 32); fprintf(stderr, "\n"); //fprintf(stderr, "cipher: "); pcpprint_bin(stderr, cipher, es); fprintf(stderr, "\n"); //fprintf(stderr, " nonce: "); pcpprint_bin(stderr, nonce, crypto_secretbox_NONCEBYTES); fprintf(stderr, "\n"); // put nonce and cipher together unsigned char *combined = ucmalloc(es + crypto_secretbox_NONCEBYTES); memcpy(combined, nonce, crypto_secretbox_NONCEBYTES); memcpy(&combined[crypto_secretbox_NONCEBYTES], cipher, es); free(cipher); free(nonce); *csize = es + crypto_secretbox_NONCEBYTES; return combined; errbec: if(cipher != NULL) free(cipher); free(nonce); return NULL; } unsigned char *pcp_box_decrypt(pcp_key_t *secret, pcp_pubkey_t *pub, unsigned char *cipher, size_t ciphersize, size_t *dsize) { unsigned char *message = NULL; unsigned char *nonce = ucmalloc(crypto_secretbox_NONCEBYTES); unsigned char *cipheronly = ucmalloc(ciphersize - crypto_secretbox_NONCEBYTES); memcpy(nonce, cipher, crypto_secretbox_NONCEBYTES); memcpy(cipheronly, &cipher[crypto_secretbox_NONCEBYTES], ciphersize - crypto_secretbox_NONCEBYTES); if(pcp_sodium_verify_box(&message, cipheronly, ciphersize - crypto_secretbox_NONCEBYTES, nonce, secret->secret, pub->pub) != 0){ fatal("failed to decrypt message!\n"); goto errbed; } free(nonce); free(cipheronly); // resulting size: // ciphersize - crypto_secretbox_ZEROBYTES *dsize = ciphersize - crypto_secretbox_NONCEBYTES - PCP_CRYPTO_ADD; return message; errbed: free(nonce); free(cipheronly); if(message != NULL) free(message); return NULL; } size_t pcp_decrypt_file(FILE *in, FILE* out, pcp_key_t *s, unsigned char *symkey, int verify) { pcp_pubkey_t *cur = NULL; pcp_pubkey_t *sender = NULL; unsigned char *reccipher = NULL; int nrec, recmatch; uint32_t lenrec; byte head[1]; size_t cur_bufsize, rec_size; unsigned char rec_buf[PCP_ASYM_RECIPIENT_SIZE]; #ifdef PCP_ASYM_ADD_SENDER_PUB unsigned char *senderpub; #endif int self = 0; if(ftell(in) == 1) { // header has already been determined outside the lib if(symkey != NULL) self = 1; } else { // step 1, check header cur_bufsize = fread(head, 1, 1, in); if(cur_bufsize != 1 && !feof(in) && !ferror(in)) { if(head[0] == PCP_SYM_CIPHER) { if(symkey != NULL) self = 1; else { fatal("Input is symetrically encrypted but no key have been specified (lib usage failure)\n"); goto errdef1; } } else if(head[0] == PCP_ASYM_CIPHER) { self = 0; } } } if(self) { // just decrypt symetrically and go outa here return pcp_decrypt_file_sym(in, out, symkey, NULL); } #ifdef PCP_ASYM_ADD_SENDER_PUB // step 2, sender's pubkey cur_bufsize = fread(&in_buf, 1, crypto_box_PUBLICKEYBYTES, in); if(cur_bufsize != crypto_box_PUBLICKEYBYTES && !feof(in) && !ferror(in)) { fatal("Error: input file doesn't contain senders public key\n"); goto errdef1; } #endif // step 3, check len recipients cur_bufsize = fread(&lenrec, 1, 4, in); if(cur_bufsize != 4 && !feof(in) && !ferror(in)) { fatal("Error: input file doesn't contain recipient count\n"); goto errdef1; } lenrec = be32toh(lenrec); if(verify) { reccipher = ucmalloc(lenrec * PCP_ASYM_RECIPIENT_SIZE); } // step 4, fetch recipient list and try to decrypt it for us for(nrec=0; nrecpub, crypto_box_PUBLICKEYBYTES, 1, out); //fprintf(stderr, "D: sender pub - %d\n", crypto_box_PUBLICKEYBYTES); if(ferror(out) != 0) goto errec1; #endif // step 3, len recipients, big endian lenrec = recipient_count; lenrec = htobe32(lenrec); fwrite(&lenrec, 4, 1, out); //fprintf(stderr, "D: %d recipients - 4\n", recipient_count); if(ferror(out) != 0) goto errec1; // step 4, recipient list fwrite(recipients_cipher, rec_size * recipient_count, 1, out); //fprintf(stderr, "D: recipients - %ld * %d\n", rec_size, recipient_count); if(ferror(out) != 0) goto errec1; out_size = 5 + (rec_size * recipient_count) + crypto_box_PUBLICKEYBYTES; // step 5, actual encrypted data size_t sym_size = 0; if(sign) { pcp_rec_t *rec = pcp_rec_new(recipients_cipher, rec_size * recipient_count, s, NULL); sym_size = pcp_encrypt_file_sym(in, out, symkey, 1, rec); pcp_rec_free(rec); } else sym_size = pcp_encrypt_file_sym(in, out, symkey, 1, NULL); if(sym_size == 0) goto errec1; return out_size + sym_size; errec1: memset(symkey, 0, crypto_secretbox_KEYBYTES); free(symkey); free(recipients_cipher); if(fileno(in) != 0) fclose(in); if(fileno(out) != 1) fclose(out); return 0; } size_t pcp_encrypt_file_sym(FILE *in, FILE* out, unsigned char *symkey, int havehead, pcp_rec_t *recsign) { /* havehead = 0: write the whole thing from here havehead = 1: no header, being called from asym... */ unsigned char *buf_nonce; unsigned char *buf_cipher; unsigned char in_buf[PCP_BLOCK_SIZE]; size_t cur_bufsize = 0; size_t out_size = 0; size_t es; crypto_generichash_state *st = NULL; unsigned char *hash = NULL; byte head[1]; if(recsign != NULL) { st = ucmalloc(sizeof(crypto_generichash_state)); hash = ucmalloc(crypto_generichash_BYTES_MAX); crypto_generichash_init(st, NULL, 0, 0); } if(havehead == 0) { head[0] = PCP_SYM_CIPHER; es = fwrite(head, 1, 1, out); if(ferror(out) != 0) { fatal("Failed to write encrypted output!\n"); return 0; } } #ifdef PCP_CBC // write the IV, pad it with rubbish, since pcp_decrypt_file_sym // reads in with PCP_BLOCK_SIZE_IN buffersize and uses the last // PCP_BLOCK_SIZE as IV. unsigned char *iv = urmalloc(PCP_BLOCK_SIZE); unsigned char *ivpad = urmalloc(PCP_BLOCK_SIZE_IN - PCP_BLOCK_SIZE); fwrite(ivpad, PCP_BLOCK_SIZE_IN - PCP_BLOCK_SIZE, 1, out); fwrite(iv, PCP_BLOCK_SIZE, 1, out); #endif // 32k-ECB-mode. FIXME: maybe support CBC as well or only use CBC? while(!feof(in)) { cur_bufsize = fread(&in_buf, 1, PCP_BLOCK_SIZE, in); if(cur_bufsize <= 0) break; buf_nonce = pcp_gennonce(); #ifdef PCP_CBC // apply IV to current clear _xorbuf(iv, in_buf, cur_bufsize); #endif es = pcp_sodium_mac(&buf_cipher, in_buf, cur_bufsize, buf_nonce, symkey); fwrite(buf_nonce, crypto_secretbox_NONCEBYTES, 1, out); fwrite(buf_cipher, es, 1, out); free(buf_nonce); free(buf_cipher); out_size += crypto_secretbox_NONCEBYTES + es; if(recsign != NULL) crypto_generichash_update(st, in_buf, cur_bufsize); #ifdef PCP_CBC // make current cipher to next IV, ignore nonce and pad memcpy(iv, &buf_cipher[PCP_CRYPTO_ADD], PCP_BLOCK_SIZE); #endif } if(ferror(out) != 0) { fatal("Failed to write encrypted output!\n"); goto errsym1; } if(recsign != NULL) { /* add encrypted recipient list to the hash */ crypto_generichash_update(st, recsign->cipher, recsign->ciphersize); crypto_generichash_final(st, hash, crypto_generichash_BYTES_MAX); /* generate the actual signature */ unsigned char *signature = pcp_ed_sign(hash, crypto_generichash_BYTES_MAX, recsign->secret); size_t siglen = crypto_sign_BYTES + crypto_generichash_BYTES_MAX; /* encrypt it as well */ buf_nonce = pcp_gennonce(); es = pcp_sodium_mac(&buf_cipher, signature, siglen, buf_nonce, symkey); fwrite(buf_nonce, crypto_secretbox_NONCEBYTES, 1, out); fwrite(buf_cipher, es, 1, out); free(st); free(signature); free(hash); } if(fileno(in) != 0) fclose(in); if(fileno(out) != 1) fclose(out); return out_size; errsym1: if(symkey != NULL) { free(st); free(hash); } return 0; } size_t pcp_decrypt_file_sym(FILE *in, FILE* out, unsigned char *symkey, pcp_rec_t *recverify) { unsigned char *buf_nonce; unsigned char *buf_cipher; unsigned char *buf_clear; size_t out_size, cur_bufsize, es; size_t ciphersize = (PCP_BLOCK_SIZE_IN) - crypto_secretbox_NONCEBYTES; unsigned char in_buf[PCP_BLOCK_SIZE_IN]; buf_nonce = ucmalloc(crypto_secretbox_NONCEBYTES); buf_cipher = ucmalloc(ciphersize); out_size = 0; unsigned char *signature = NULL; unsigned char *signature_cr = NULL; size_t siglen = crypto_sign_BYTES + crypto_generichash_BYTES_MAX; size_t siglen_cr = siglen + PCP_CRYPTO_ADD + crypto_secretbox_NONCEBYTES; crypto_generichash_state *st = NULL; unsigned char *hash = NULL; if(recverify != NULL) { st = ucmalloc(sizeof(crypto_generichash_state)); hash = ucmalloc(crypto_generichash_BYTES_MAX); crypto_generichash_init(st, NULL, 0, 0); signature_cr = ucmalloc(siglen_cr); } #ifdef PCP_CBC unsigned char *iv = NULL; // will be filled during 1st loop #endif while(!feof(in)) { cur_bufsize = fread(&in_buf, 1, PCP_BLOCK_SIZE_IN, in); if(cur_bufsize <= PCP_CRYPTO_ADD) break; // no valid cipher block if(recverify != NULL) { if(cur_bufsize < PCP_BLOCK_SIZE_IN || feof(in)) { // pull out signature memcpy(signature_cr, &in_buf[cur_bufsize - siglen_cr], siglen_cr); cur_bufsize -= siglen_cr; } } #ifdef PCP_CBC if(iv == NULL) { // first block is the IV, don't write it out and skip to the next block iv = ucmalloc(PCP_BLOCK_SIZE); memcpy(iv, &in_buf[PCP_CRYPTO_ADD + crypto_secretbox_NONCEBYTES], PCP_BLOCK_SIZE); continue; } #endif ciphersize = cur_bufsize - crypto_secretbox_NONCEBYTES; memcpy(buf_nonce, in_buf, crypto_secretbox_NONCEBYTES); memcpy(buf_cipher, &in_buf[crypto_secretbox_NONCEBYTES], ciphersize); es = pcp_sodium_verify_mac(&buf_clear, buf_cipher, ciphersize, buf_nonce, symkey); #ifdef PCP_CBC // take last IV and apply it to current clear _xorbuf(iv, buf_clear, cur_bufsize - (PCP_CRYPTO_ADD + crypto_secretbox_NONCEBYTES)); #endif out_size += ciphersize - PCP_CRYPTO_ADD; if(es == 0) { fwrite(buf_clear, ciphersize - PCP_CRYPTO_ADD, 1, out); if(recverify != NULL) crypto_generichash_update(st, buf_clear, ciphersize - PCP_CRYPTO_ADD); free(buf_clear); if(ferror(out) != 0) { fatal("Failed to write decrypted output!\n"); out_size = 0; break; } } else { fatal("Failed to decrypt file content!\n"); free(buf_clear); out_size = 0; break; } #ifdef PCP_CBC // use last cipher as next IV memcpy(iv, &in_buf[PCP_CRYPTO_ADD + crypto_secretbox_NONCEBYTES], PCP_BLOCK_SIZE); #endif } free(buf_nonce); free(buf_cipher); if(recverify != NULL) { /* decrypt the signature */ memcpy(buf_nonce, signature_cr, crypto_secretbox_NONCEBYTES); es = pcp_sodium_verify_mac(&signature, &signature_cr[crypto_secretbox_NONCEBYTES], siglen_cr - crypto_secretbox_NONCEBYTES, buf_nonce, symkey); if(es == 0) { /* add encrypted recipient list to the hash */ crypto_generichash_update(st, recverify->cipher, recverify->ciphersize); crypto_generichash_final(st, hash, crypto_generichash_BYTES_MAX); unsigned char *verifiedhash = NULL; verifiedhash = pcp_ed_verify(signature, siglen, recverify->pub); if(verifiedhash == NULL) out_size = 0; else { if(memcmp(verifiedhash, hash, crypto_generichash_BYTES_MAX) != 0) { // sig verified, but the hash doesn't match fatal("signed hash doesn't match actual hash of signed decrypted file content\n"); out_size = 0; } free(verifiedhash); } } else { fatal("Failed to decrypt signature!\n"); out_size = 0; } free(st); free(hash); free(signature); free(signature_cr); } if(fileno(in) != 0) fclose(in); if(fileno(out) != 1) fclose(out); return out_size; } pcp_rec_t *pcp_rec_new(unsigned char *cipher, size_t clen, pcp_key_t *secret, pcp_pubkey_t *pub) { pcp_rec_t *r = ucmalloc(sizeof(pcp_rec_t)); r->cipher = ucmalloc(clen); memcpy(r->cipher, cipher, clen); r->ciphersize = clen; if(secret != NULL) { r->secret = ucmalloc(sizeof(pcp_key_t)); memcpy(r->secret, secret, sizeof(pcp_key_t)); } else r->secret = NULL; if(pub != NULL) { r->pub = ucmalloc(sizeof(pcp_key_t)); memcpy(r->pub, pub, sizeof(pcp_key_t)); } else r->pub = NULL; return r; } void pcp_rec_free(pcp_rec_t *r) { free(r->cipher); if(r->secret != NULL) { memset(r->secret, 0, sizeof(pcp_key_t)); free(r->secret); } if(r->pub != NULL) { memset(r->pub, 0, sizeof(pcp_pubkey_t)); free(r->pub); } free(r); }