/** * Compare one cipher_ctx to another. * * returns 0 if all the parameters (except the derived key data) are the same * returns 1 otherwise */ static int sqlcipher_cipher_ctx_cmp(cipher_ctx *c1, cipher_ctx *c2) { int are_equal = ( c1->iv_sz == c2->iv_sz && c1->kdf_iter == c2->kdf_iter && c1->fast_kdf_iter == c2->fast_kdf_iter && c1->key_sz == c2->key_sz && c1->pass_sz == c2->pass_sz && c1->flags == c2->flags && c1->hmac_sz == c2->hmac_sz && c1->provider->ctx_cmp(c1->provider_ctx, c2->provider_ctx) && ( c1->pass == c2->pass || !sqlcipher_memcmp((const unsigned char*)c1->pass, (const unsigned char*)c2->pass, c1->pass_sz) )); CODEC_TRACE(("sqlcipher_cipher_ctx_cmp: entered \ c1=%p c2=%p \ c1->iv_sz=%d c2->iv_sz=%d \ c1->kdf_iter=%d c2->kdf_iter=%d \ c1->fast_kdf_iter=%d c2->fast_kdf_iter=%d \ c1->key_sz=%d c2->key_sz=%d \ c1->pass_sz=%d c2->pass_sz=%d \ c1->flags=%d c2->flags=%d \ c1->hmac_sz=%d c2->hmac_sz=%d \ c1->provider_ctx=%p c2->provider_ctx=%p \ c1->pass=%p c2->pass=%p \ c1->pass=%s c2->pass=%s \ provider->ctx_cmp=%d \ sqlcipher_memcmp=%d \ are_equal=%d \ \n", c1, c2, c1->iv_sz, c2->iv_sz, c1->kdf_iter, c2->kdf_iter, c1->fast_kdf_iter, c2->fast_kdf_iter, c1->key_sz, c2->key_sz, c1->pass_sz, c2->pass_sz, c1->flags, c2->flags, c1->hmac_sz, c2->hmac_sz, c1->provider_ctx, c2->provider_ctx, c1->pass, c2->pass, c1->pass, c2->pass, c1->provider->ctx_cmp(c1->provider_ctx, c2->provider_ctx), sqlcipher_memcmp((const unsigned char*)c1->pass, (const unsigned char*)c2->pass, c1->pass_sz), are_equal )); return !are_equal; /* return 0 if they are the same, 1 otherwise */ }
/** * Compare one cipher_ctx to another. * * returns 0 if all the parameters (except the derived key data) are the same * returns 1 otherwise */ static int sqlcipher_cipher_ctx_cmp(cipher_ctx *c1, cipher_ctx *c2) { CODEC_TRACE(("sqlcipher_cipher_ctx_cmp: entered c1=%p c2=%p\n", c1, c2)); if( c1->iv_sz == c2->iv_sz && c1->kdf_iter == c2->kdf_iter && c1->fast_kdf_iter == c2->fast_kdf_iter && c1->key_sz == c2->key_sz && c1->pass_sz == c2->pass_sz && c1->flags == c2->flags && c1->hmac_sz == c2->hmac_sz && c1->provider->ctx_cmp(c1->provider_ctx, c2->provider_ctx) && ( c1->pass == c2->pass || !sqlcipher_memcmp((const unsigned char*)c1->pass, (const unsigned char*)c2->pass, c1->pass_sz) ) ) return 0; return 1; }
/* * ctx - codec context * pgno - page number in database * size - size in bytes of input and output buffers * mode - 1 to encrypt, 0 to decrypt * in - pointer to input bytes * out - pouter to output bytes */ int sqlcipher_page_cipher(codec_ctx *ctx, int for_ctx, Pgno pgno, int mode, int page_sz, unsigned char *in, unsigned char *out) { cipher_ctx *c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx; unsigned char *iv_in, *iv_out, *hmac_in, *hmac_out, *out_start; int size; /* calculate some required positions into various buffers */ size = page_sz - c_ctx->reserve_sz; /* adjust size to useable size and memset reserve at end of page */ iv_out = out + size; iv_in = in + size; /* hmac will be written immediately after the initialization vector. the remainder of the page reserve will contain random bytes. note, these pointers are only valid when using hmac */ hmac_in = in + size + c_ctx->iv_sz; hmac_out = out + size + c_ctx->iv_sz; out_start = out; /* note the original position of the output buffer pointer, as out will be rewritten during encryption */ CODEC_TRACE(("codec_cipher:entered pgno=%d, mode=%d, size=%d\n", pgno, mode, size)); CODEC_HEXDUMP("codec_cipher: input page data", in, page_sz); /* the key size should never be zero. If it is, error out. */ if(c_ctx->key_sz == 0) { CODEC_TRACE(("codec_cipher: error possible context corruption, key_sz is zero for pgno=%d\n", pgno)); sqlcipher_memset(out, 0, page_sz); return SQLITE_ERROR; } if(mode == CIPHER_ENCRYPT) { /* start at front of the reserve block, write random data to the end */ if(c_ctx->provider->random(c_ctx->provider_ctx, iv_out, c_ctx->reserve_sz) != SQLITE_OK) return SQLITE_ERROR; } else { /* CIPHER_DECRYPT */ memcpy(iv_out, iv_in, c_ctx->iv_sz); /* copy the iv from the input to output buffer */ } if((c_ctx->flags & CIPHER_FLAG_HMAC) && (mode == CIPHER_DECRYPT) && !ctx->skip_read_hmac) { if(sqlcipher_page_hmac(c_ctx, pgno, in, size + c_ctx->iv_sz, hmac_out) != SQLITE_OK) { sqlcipher_memset(out, 0, page_sz); CODEC_TRACE(("codec_cipher: hmac operations failed for pgno=%d\n", pgno)); return SQLITE_ERROR; } CODEC_TRACE(("codec_cipher: comparing hmac on in=%p out=%p hmac_sz=%d\n", hmac_in, hmac_out, c_ctx->hmac_sz)); if(sqlcipher_memcmp(hmac_in, hmac_out, c_ctx->hmac_sz) != 0) { /* the hmac check failed */ if(sqlcipher_ismemset(in, 0, page_sz) == 0) { /* first check if the entire contents of the page is zeros. If so, this page resulted from a short read (i.e. sqlite attempted to pull a page after the end of the file. these short read failures must be ignored for autovaccum mode to work so wipe the output buffer and return SQLITE_OK to skip the decryption step. */ CODEC_TRACE(("codec_cipher: zeroed page (short read) for pgno %d, encryption but returning SQLITE_OK\n", pgno)); sqlcipher_memset(out, 0, page_sz); return SQLITE_OK; } else { /* if the page memory is not all zeros, it means the there was data and a hmac on the page. since the check failed, the page was either tampered with or corrupted. wipe the output buffer, and return SQLITE_ERROR to the caller */ CODEC_TRACE(("codec_cipher: hmac check failed for pgno=%d returning SQLITE_ERROR\n", pgno)); sqlcipher_memset(out, 0, page_sz); return SQLITE_ERROR; } } } c_ctx->provider->cipher(c_ctx->provider_ctx, mode, c_ctx->key, c_ctx->key_sz, iv_out, in, size, out); if((c_ctx->flags & CIPHER_FLAG_HMAC) && (mode == CIPHER_ENCRYPT)) { sqlcipher_page_hmac(c_ctx, pgno, out_start, size + c_ctx->iv_sz, hmac_out); } CODEC_HEXDUMP("codec_cipher: output page data", out_start, page_sz); return SQLITE_OK; }