@@ -1254,6 +1254,87 @@ static int sqlcipher_check_connection(const char *filename, char *key, int key_s
12541254 return rc ;
12551255}
12561256
1257+ int sqlcipher_codec_ctx_integrity_check (codec_ctx * ctx , Parse * pParse , char * column ) {
1258+ Pgno page = 1 ;
1259+ int i , trans_rc , rc = 0 ;
1260+ char * result ;
1261+ unsigned char * hmac_out = NULL ;
1262+ sqlite3_file * fd = sqlite3PagerFile (ctx -> pBt -> pBt -> pPager );
1263+ i64 file_sz ;
1264+
1265+ Vdbe * v = sqlite3GetVdbe (pParse );
1266+ sqlite3VdbeSetNumCols (v , 1 );
1267+ sqlite3VdbeSetColName (v , 0 , COLNAME_NAME , column , SQLITE_STATIC );
1268+
1269+ if (fd == NULL || fd -> pMethods == 0 ) {
1270+ sqlite3VdbeAddOp4 (v , OP_String8 , 0 , 1 , 0 , "database file is undefined" , P4_TRANSIENT );
1271+ sqlite3VdbeAddOp2 (v , OP_ResultRow , 1 , 1 );
1272+ goto cleanup ;
1273+ }
1274+
1275+ if (!(ctx -> flags & CIPHER_FLAG_HMAC )) {
1276+ sqlite3VdbeAddOp4 (v , OP_String8 , 0 , 1 , 0 , "HMAC is not enabled, unable to integrity check" , P4_TRANSIENT );
1277+ sqlite3VdbeAddOp2 (v , OP_ResultRow , 1 , 1 );
1278+ goto cleanup ;
1279+ }
1280+
1281+ if ((rc = sqlcipher_codec_key_derive (ctx )) != SQLITE_OK ) {
1282+ sqlite3VdbeAddOp4 (v , OP_String8 , 0 , 1 , 0 , "unable to derive keys" , P4_TRANSIENT );
1283+ sqlite3VdbeAddOp2 (v , OP_ResultRow , 1 , 1 );
1284+ goto cleanup ;
1285+ }
1286+
1287+ /* establish an exclusive lock on the database */
1288+ if ((trans_rc = sqlite3BtreeBeginTrans (ctx -> pBt , 2 , 0 )) != SQLITE_OK ) {
1289+ sqlite3VdbeAddOp4 (v , OP_String8 , 0 , 1 , 0 , "unable to lock database" , P4_TRANSIENT );
1290+ sqlite3VdbeAddOp2 (v , OP_ResultRow , 1 , 1 );
1291+ goto cleanup ;
1292+ }
1293+
1294+ sqlite3OsFileSize (fd , & file_sz );
1295+ hmac_out = sqlcipher_malloc (ctx -> hmac_sz );
1296+
1297+ for (page = 1 ; page <= file_sz / ctx -> page_sz ; page ++ ) {
1298+ int offset = (page - 1 ) * ctx -> page_sz ;
1299+ int payload_sz = ctx -> page_sz - ctx -> reserve_sz + ctx -> iv_sz ;
1300+ int read_sz = ctx -> page_sz ;
1301+
1302+ if (page == 1 ) {
1303+ int page1_offset = ctx -> plaintext_header_sz ? ctx -> plaintext_header_sz : FILE_HEADER_SZ ;
1304+ read_sz = read_sz - page1_offset ;
1305+ payload_sz = payload_sz - page1_offset ;
1306+ offset += page1_offset ;
1307+ }
1308+
1309+ sqlcipher_memset (ctx -> buffer , 0 , ctx -> page_sz );
1310+ sqlcipher_memset (hmac_out , 0 , ctx -> hmac_sz );
1311+ if (sqlite3OsRead (fd , ctx -> buffer , read_sz , offset ) != SQLITE_OK ) {
1312+ result = sqlite3_mprintf ("error reading %d bytes from file page %d at offset %d\n" , read_sz , page , offset );
1313+ sqlite3VdbeAddOp4 (v , OP_String8 , 0 , 1 , 0 , result , P4_DYNAMIC );
1314+ sqlite3VdbeAddOp2 (v , OP_ResultRow , 1 , 1 );
1315+ } else if (sqlcipher_page_hmac (ctx , ctx -> read_ctx , page , ctx -> buffer , payload_sz , hmac_out ) != SQLITE_OK ) {
1316+ result = sqlite3_mprintf ("HMAC operation failed for page %d" , page );
1317+ sqlite3VdbeAddOp4 (v , OP_String8 , 0 , 1 , 0 , result , P4_DYNAMIC );
1318+ sqlite3VdbeAddOp2 (v , OP_ResultRow , 1 , 1 );
1319+ } else if (sqlcipher_memcmp (ctx -> buffer + payload_sz , hmac_out , ctx -> hmac_sz ) != 0 ) {
1320+ result = sqlite3_mprintf ("HMAC verification failed for page %d" , page );
1321+ sqlite3VdbeAddOp4 (v , OP_String8 , 0 , 1 , 0 , result , P4_DYNAMIC );
1322+ sqlite3VdbeAddOp2 (v , OP_ResultRow , 1 , 1 );
1323+ }
1324+ }
1325+
1326+ if (file_sz % ctx -> page_sz != 0 ) {
1327+ result = sqlite3_mprintf ("page %d has an invalid size of %d bytes" , page , file_sz - ((file_sz / ctx -> page_sz ) * ctx -> page_sz ));
1328+ sqlite3VdbeAddOp4 (v , OP_String8 , 0 , 1 , 0 , result , P4_DYNAMIC );
1329+ sqlite3VdbeAddOp2 (v , OP_ResultRow , 1 , 1 );
1330+ }
1331+
1332+ cleanup :
1333+ if (trans_rc == SQLITE_OK ) sqlite3BtreeRollback (ctx -> pBt , SQLITE_OK , 0 );
1334+ if (hmac_out != NULL ) sqlcipher_free (hmac_out , ctx -> hmac_sz );
1335+ return SQLITE_OK ;
1336+ }
1337+
12571338int sqlcipher_codec_ctx_migrate (codec_ctx * ctx ) {
12581339 int i , pass_sz , keyspec_sz , nRes , user_version , rc , oflags ;
12591340 Db * pDb = 0 ;
0 commit comments