// We encrypt like GDBE each sector has a random // nonce prepended to each sector. static int crypto_write(const char *path, const char *buf, size_t size, off_t off, struct fuse_file_info *inf){ size_t written = 0; while(size > 0) { int idx = off / (block_size - crypto_PADDING); // Grab a random nonce unsigned char nonce[crypto_secretbox_NONCEBYTES]; randombytes(nonce, crypto_secretbox_NONCEBYTES); // Set up the necessary buffers size_t to_write = size < block_size - crypto_PADDING ? size + crypto_PADDING : block_size; size_t msize = to_write - crypto_PADDING; size_t fudge = 0; char padding[block_size]; if(off % (block_size - crypto_PADDING) != 0) { // At partial block, have to read the rest of the data // and append the new stuff to our buffer. size_t leftovers = off % (block_size - crypto_PADDING); off_t block_off = idx * (block_size - crypto_PADDING); struct fuse_file_info of = {.flags = O_RDONLY}; int fd = crypto_open(path, &of); if(fd == -1) return -errno; int res = crypto_read(path, padding, leftovers, block_off, &of); if(res < 0) return res; close(fd); if(to_write + res < block_size - crypto_PADDING) to_write += res; fudge = res; } unsigned char mpad[to_write]; unsigned char cpad[to_write]; memset(mpad, 0, to_write); memset(cpad, 0, to_write); memcpy(mpad + crypto_secretbox_ZEROBYTES, padding, fudge); memcpy(mpad + crypto_secretbox_ZEROBYTES + fudge, buf + written, msize); int ohno = crypto_secretbox(cpad, mpad, msize + crypto_secretbox_ZEROBYTES, nonce, key); if(ohno < 0) return -ENXIO; unsigned char block[to_write]; memset(block, 0, to_write); memcpy(block, nonce, crypto_secretbox_NONCEBYTES); memcpy(block + crypto_secretbox_NONCEBYTES, cpad + crypto_secretbox_BOXZEROBYTES, msize + crypto_secretbox_BOXZEROBYTES); int res = pwrite(inf->fh, block, to_write, block_size * idx); if(res == -1) return -errno; res -= crypto_PADDING; written += res - fudge; size -= res - fudge; off += res - fudge; }
int cryptofs_read(Ctx *ctx, char *_file, long long offset, unsigned long count, char *buf) { int result; int fp; gchar *file; file = translate_path(ctx, _file); if ((fp = open(file, 0)) < 0){ g_free(file); return -1; } g_free(file); result = crypto_read(ctx->cryptoctx, fp, buf, count, offset); close(fp); return result; }
static int64_t crypto_seek(URLContext *h, int64_t pos, int whence) { CryptoContext *c = h->priv_data; int64_t block; int64_t newpos; if (c->flags & AVIO_FLAG_WRITE) { av_log(h, AV_LOG_ERROR, "Crypto: seek not supported for write\r\n"); /* seems the most appropriate error to return */ return AVERROR(ESPIPE); } // reset eof, else we won't read it correctly if we already hit eof. c->eof = 0; switch (whence) { case SEEK_SET: break; case SEEK_CUR: pos = pos + c->position; break; case SEEK_END: { int64_t newpos = ffurl_seek( c->hd, pos, AVSEEK_SIZE ); if (newpos < 0) { av_log(h, AV_LOG_ERROR, "Crypto: seek_end - can't get file size (pos=%lld)\r\n", (long long int)pos); return newpos; } pos = newpos - pos; } break; case AVSEEK_SIZE: { int64_t newpos = ffurl_seek( c->hd, pos, AVSEEK_SIZE ); return newpos; } break; default: av_log(h, AV_LOG_ERROR, "Crypto: no support for seek where 'whence' is %d\r\n", whence); return AVERROR(EINVAL); } c->outdata = 0; c->indata = 0; c->indata_used = 0; c->outptr = c->outbuffer; // identify the block containing the IV for the // next block we will decrypt block = pos/BLOCKSIZE; if (block == 0) { // restore the iv to the seed one - this is the iv for the FIRST block memcpy( c->decrypt_iv, c->iv, c->ivlen ); c->position = 0; } else { // else, go back one block - we will get av_cyrpt to read this block // which it will then store use as the iv. // note that the DECRYPTED result will not be correct, // but will be discarded block--; c->position = (block * BLOCKSIZE); } newpos = ffurl_seek( c->hd, c->position, SEEK_SET ); if (newpos < 0) { av_log(h, AV_LOG_ERROR, "Crypto: nested protocol no support for seek or seek failed\n"); return newpos; } // read and discard from here up to required position // (which will set the iv correctly to it). if (pos - c->position) { uint8_t buff[BLOCKSIZE*2]; // maximum size of pos-c->position int len = pos - c->position; int res; while (len > 0) { // note: this may not return all the bytes first time res = crypto_read(h, buff, len); if (res < 0) break; len -= res; } // if we did not get all the bytes if (len != 0) { char errbuf[100] = "unknown error"; av_strerror(res, errbuf, sizeof(errbuf)); av_log(h, AV_LOG_ERROR, "Crypto: discard read did not get all the bytes (%d remain) - read returned (%d)-%s\n", len, res, errbuf); return AVERROR(EINVAL); } } return c->position; }
void functional_crypto(UNUSED(void **state)) { unsigned char nonce[crypto_box_NONCEBYTES]; unsigned char initiatenonce[crypto_box_NONCEBYTES]; unsigned char hellopacket[192] = {0}; unsigned char initiatepacket[256] = {0}; unsigned char messagepacket[120]; unsigned char messagepacketout[120] = {0}; unsigned char allzeroboxed[96] = {0}; unsigned char initiatebox[160] = {0}; unsigned char pubkeybox[96] = {0}; unsigned char lengthbox[40] = {0}; uint64_t plaintextlen; uint64_t readlen; outputstream write; connect_to_db(); wrap_crypto_write = false; assert_int_equal(0, filesystem_load(".keys/server-long-term.pub", serverlongtermpk, sizeof serverlongtermpk)); cc.nonce = (uint64_t) randommod(281474976710656LL); if (!ISODD(cc.nonce)) { cc.nonce++; } cc.receivednonce = 0; cc.state = TUNNEL_INITIAL; memcpy(nonce, "splonebox-client", 16); uint64_pack(nonce + 16, cc.nonce); /* pack hello packet */ memcpy(hellopacket, "oqQN2kaH", 8); /* pack compressed nonce */ memcpy(hellopacket + 104, nonce + 16, 8); /* generate client ephemeral keys */ if (crypto_box_keypair(clientlongtermpk, clientlongtermsk) != 0) return; /* generate client ephemeral keys */ if (crypto_box_keypair(clientshorttermpk, clientshorttermsk) != 0) return; memcpy(hellopacket + 8, clientshorttermpk, 32); assert_int_equal(0, crypto_box(allzeroboxed, allzeroboxed, 96, nonce, serverlongtermpk, clientshorttermsk)); memcpy(hellopacket + 112, allzeroboxed + 16, 80); crypto_init(); /* positiv test */ assert_int_equal(0, crypto_recv_hello_send_cookie(&cc, hellopacket, &write)); /* wrong identifier */ memcpy(hellopacket, "deadbeef", 8); assert_int_not_equal(0, crypto_recv_hello_send_cookie(&cc, hellopacket, &write)); memcpy(hellopacket, "oqQN2kaH", 8); /* wrong nonce */ cc.receivednonce = cc.nonce + 1; assert_int_not_equal(0, crypto_recv_hello_send_cookie(&cc, hellopacket, &write)); cc.receivednonce = 0; /* wrong pubkey */ memset(hellopacket + 8, '0', 32); assert_int_not_equal(0, crypto_recv_hello_send_cookie(&cc, hellopacket, &write)); memcpy(hellopacket + 8, clientshorttermpk, 32); assert_int_equal(0, crypto_recv_hello_send_cookie(&cc, hellopacket, &write)); /* crypto_recv_initiate() test */ /* pack initiate packet */ memcpy(initiatepacket, "oqQN2kaI", 8); memcpy(initiatepacket + 8, cookie, 96); /* pack compressed nonce */ memcpy(initiatepacket + 104, nonce + 16, 8); memcpy(initiatebox + 32, clientlongtermpk, 32); randombytes(initiatebox + 64, 16); memcpy(initiatenonce, "splonePV", 8); memcpy(initiatenonce + 8, initiatebox + 64, 16); memcpy(pubkeybox + 32, clientshorttermpk, 32); memcpy(pubkeybox + 64, servershorttermpk, 32); assert_int_equal(0, crypto_box(pubkeybox, pubkeybox, 96, initiatenonce, serverlongtermpk, clientlongtermsk)); memcpy(initiatebox + 80, pubkeybox + 16, 80); assert_int_equal(0, crypto_box(initiatebox, initiatebox, 160, nonce, servershorttermpk, clientshorttermsk)); memcpy(initiatepacket + 112, initiatebox + 16, 144); /* without valid certificate */ assert_int_not_equal(0, crypto_recv_initiate(&cc, initiatepacket)); /* all plugins are allowed to connect */ db_authorized_set_whitelist_all(); assert_int_equal(0, crypto_recv_initiate(&cc, initiatepacket)); /* crypto_write() test */ assert_int_equal(0, crypto_write(&cc, (char*) allzeroboxed, sizeof(allzeroboxed), &write)); /* crypto_read() test */ /* pack message packet */ memcpy(messagepacket, "oqQN2kaM", 8); /* pack compressed nonce */ memcpy(nonce, "splonebox-client", 16); uint64_pack(nonce + 16, cc.nonce); memcpy(messagepacket + 8, nonce + 16, 8); uint64_pack(lengthbox + 32, 120); assert_int_equal(0, crypto_box(lengthbox, lengthbox, 40, nonce, servershorttermpk, clientshorttermsk)); memcpy(messagepacket + 16, lengthbox + 16, 24); uint64_pack(nonce + 16, cc.nonce + 2); memset(allzeroboxed, 0, 96); assert_int_equal(0, crypto_box_afternm(allzeroboxed, allzeroboxed, 96, nonce, cc.clientshortservershort)); memcpy(messagepacket + 40, allzeroboxed + 16, 80); assert_int_equal(0, crypto_verify_header(&cc, messagepacket, &readlen)); assert_int_equal(0, crypto_read(&cc, messagepacket, (char*)messagepacketout, readlen, &plaintextlen)); db_close(); }