This is an automated email from the ASF dual-hosted git repository. eiri pushed a commit to branch prototype/fdb-encryption in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 8f9a988c875973a530806be492cb731b55bf7d12 Author: Eric Avdey <[email protected]> AuthorDate: Thu Mar 19 21:30:12 2020 -0300 Switch from rev to update counter for key derivation --- src/fabric/src/fabric2_encryption.erl | 46 +++++++++++++++++++---------------- src/fabric/src/fabric2_fdb.erl | 18 ++++++++------ 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/fabric/src/fabric2_encryption.erl b/src/fabric/src/fabric2_encryption.erl index 6a9e82e..b589f6e 100644 --- a/src/fabric/src/fabric2_encryption.erl +++ b/src/fabric/src/fabric2_encryption.erl @@ -55,22 +55,24 @@ get_wrapped_kek(DbName) when is_binary(DbName) -> gen_server:call(?MODULE, {get_wrapped_kek, DbName}). -encode(WrappedKEK, DbName, DocId, DocRev, DocBody) +encode(WrappedKEK, DbName, DocId, UpdateCounter, DocBody) when is_binary(WrappedKEK), is_binary(DbName), is_binary(DocId), - is_binary(DocRev), + is_integer(UpdateCounter), UpdateCounter > 0, is_binary(DocBody) -> - gen_server:call(?MODULE, {encode, WrappedKEK, DbName, DocId, DocRev, DocBody}). + gen_server:call(?MODULE, + {encode, WrappedKEK, DbName, DocId, UpdateCounter, DocBody}). -decode(WrappedKEK, DbName, DocId, DocRev, DocBody) +decode(WrappedKEK, DbName, DocId, UpdateCounter, DocBody) when is_binary(WrappedKEK), is_binary(DbName), is_binary(DocId), - is_binary(DocRev), + is_integer(UpdateCounter), UpdateCounter > 0, is_binary(DocBody) -> - gen_server:call(?MODULE, {decode, WrappedKEK, DbName, DocId, DocRev, DocBody}). + gen_server:call(?MODULE, + {decode, WrappedKEK, DbName, DocId, UpdateCounter, DocBody}). @@ -96,7 +98,8 @@ handle_call({get_wrapped_kek, _DbName}, _From, #{cache := Cache} = St) -> true = ets:insert(Cache, {WrappedKEK, KEK}), {reply, {ok, WrappedKEK}, St}; -handle_call({encode, WrappedKEK, DbName, DocId, DocRev, DocBody}, From, St) -> +handle_call({encode, WrappedKEK, DbName, DocId, UpdateCounter, DocBody}, + From, St) -> #{ iid := InstanceId, cache := Cache, @@ -105,14 +108,15 @@ handle_call({encode, WrappedKEK, DbName, DocId, DocRev, DocBody}, From, St) -> {ok, KEK} = unwrap_kek(Cache, WrappedKEK), {Pid, _Ref} = erlang:spawn_monitor(?MODULE, - do_encode, [KEK, InstanceId, DbName, DocId, DocRev, DocBody]), + do_encode, [KEK, InstanceId, DbName, DocId, UpdateCounter, DocBody]), NewSt = St#{ waiters := dict:store(Pid, From, Waiters) }, {noreply, NewSt}; -handle_call({decode, WrappedKEK, DbName, DocId, DocRev, Encoded}, From, St) -> +handle_call({decode, WrappedKEK, DbName, DocId, UpdateCounter, Encoded}, + From, St) -> #{ iid := InstanceId, cache := Cache, @@ -121,7 +125,7 @@ handle_call({decode, WrappedKEK, DbName, DocId, DocRev, Encoded}, From, St) -> {ok, KEK} = unwrap_kek(Cache, WrappedKEK), {Pid, _Ref} = erlang:spawn_monitor(?MODULE, - do_decode, [KEK, InstanceId, DbName, DocId, DocRev, Encoded]), + do_decode, [KEK, InstanceId, DbName, DocId, UpdateCounter, Encoded]), NewSt = St#{ waiters := dict:store(Pid, From, Waiters) @@ -164,10 +168,10 @@ init_st() -> }}. -do_encode(KEK, InstanceId, DbName, DocId, DocRev, DocBody) -> +do_encode(KEK, InstanceId, DbName, DocId, UpdateCounter, DocBody) -> try {ok, AAD} = get_aad(InstanceId, DbName), - {ok, DEK} = get_dek(KEK, DocId, DocRev), + {ok, DEK} = get_dek(KEK, DocId, UpdateCounter), {CipherText, CipherTag} = crypto:block_encrypt( aes_gcm, DEK, <<0:96>>, {AAD, DocBody, 16}), <<CipherTag/binary, CipherText/binary>> @@ -180,11 +184,11 @@ do_encode(KEK, InstanceId, DbName, DocId, DocRev, DocBody) -> end. -do_decode(KEK, InstanceId, DbName, DocId, DocRev, Encoded) -> +do_decode(KEK, InstanceId, DbName, DocId, UpdateCounter, Encoded) -> try <<CipherTag:16/binary, CipherText/binary>> = Encoded, {ok, AAD} = get_aad(InstanceId, DbName), - {ok, DEK} = get_dek(KEK, DocId, DocRev), + {ok, DEK} = get_dek(KEK, DocId, UpdateCounter), crypto:block_decrypt( aes_gcm, DEK, <<0:96>>, {AAD, CipherText, CipherTag}) of @@ -200,8 +204,8 @@ get_aad(InstanceId, DbName) when is_binary(InstanceId), is_binary(DbName) -> {ok, <<InstanceId/binary, 0:8, DbName/binary>>}. -get_dek(KEK, DocId, DocRev) when bit_size(KEK) == 256 -> - Context = <<DocId/binary, 0:8, DocRev/binary>>, +get_dek(KEK, DocId, UpdateCounter) when bit_size(KEK) == 256 -> + Context = <<DocId/binary, 0:8, (integer_to_binary(UpdateCounter))/binary>>, PlainText = <<1:16, ?LABEL, 0:8, Context/binary, 256:16>>, <<_:256>> = DEK = crypto:hmac(sha256, KEK, PlainText), {ok, DEK}. @@ -243,24 +247,24 @@ get_unwrap_kek_test() -> get_dek_test() -> KEK = crypto:strong_rand_bytes(32), - {ok, DEK} = get_dek(KEK, <<"0001">>, <<"1-abcdefgh">>), + {ok, DEK} = get_dek(KEK, <<"0001">>, 1), ?assertNotEqual(KEK, DEK), ?assertEqual(32, byte_size(DEK)). encode_decode_test() -> KEK = crypto:strong_rand_bytes(32), - {IId, DbName, DocId, DocRev, DocBody} - = {<<"dev">>, <<"db">>, <<"0001">>, <<"1-abcdefgh">>, <<"[ohai]">>}, + {IId, DbName, DocId, UpdateCounter, DocBody} + = {<<"dev">>, <<"db">>, <<"0001">>, 1, <<"[ohai]">>}, {ok, EncResult} = try - do_encode(KEK, IId, DbName, DocId, DocRev, DocBody) + do_encode(KEK, IId, DbName, DocId, UpdateCounter, DocBody) catch exit:ER -> ER end, ?assertNotEqual(DocBody, EncResult), {ok, DecResult} = try - do_decode(KEK, IId, DbName, DocId, DocRev, EncResult) + do_decode(KEK, IId, DbName, DocId, UpdateCounter, EncResult) catch exit:DR -> DR end, diff --git a/src/fabric/src/fabric2_fdb.erl b/src/fabric/src/fabric2_fdb.erl index d68985e..203a5b7 100644 --- a/src/fabric/src/fabric2_fdb.erl +++ b/src/fabric/src/fabric2_fdb.erl @@ -205,6 +205,7 @@ create(#{} = Db0, Options) -> {?DB_STATS, <<"doc_del_count">>, ?uint2bin(0)}, {?DB_STATS, <<"doc_design_count">>, ?uint2bin(0)}, {?DB_STATS, <<"doc_local_count">>, ?uint2bin(0)}, + {?DB_STATS, <<"update_count">>, ?uint2bin(1)}, {?DB_STATS, <<"sizes">>, <<"external">>, ?uint2bin(2)}, {?DB_STATS, <<"sizes">>, <<"views">>, ?uint2bin(0)} ], @@ -813,10 +814,11 @@ write_doc(#{} = Db0, Doc, NewWinner0, OldWinner, ToUpdate, ToRemove) -> ok end, - % Update database size + % Update database size and db's update counter AddSize = sum_add_rev_sizes([NewWinner | ToUpdate]), RemSize = sum_rem_rev_sizes(ToRemove), incr_stat(Db, <<"sizes">>, <<"external">>, AddSize - RemSize), + incr_stat(Db, <<"update_count">>, 1), ok. @@ -1335,12 +1337,13 @@ doc_to_fdb(Db, #doc{} = Doc) -> DiskAtts = lists:map(fun couch_att:to_disk_term/1, Atts), - BinRev = couch_doc:rev_to_str({Start, Rev}), + UpdateCounter = get_stat(Db, <<"update_count">>), BinBody = term_to_binary(Body, [{compressed, 0}, {minor_version, 1}]), {ok, Encoded} = fabric2_encryption:encode( - WrappedKEK, DbName, Id, BinRev, BinBody), + WrappedKEK, DbName, Id, UpdateCounter, BinBody), - Value = term_to_binary({Encoded, DiskAtts, Deleted}, [{minor_version, 1}]), + Value = term_to_binary({UpdateCounter, Encoded, DiskAtts, Deleted}, + [{minor_version, 1}]), Chunks = chunkify_binary(Value), {Rows, _} = lists:mapfoldl(fun(Chunk, ChunkId) -> @@ -1354,18 +1357,17 @@ doc_to_fdb(Db, #doc{} = Doc) -> fdb_to_doc(_Db, _DocId, _Pos, _Path, []) -> {not_found, missing}; -fdb_to_doc(Db, DocId, Pos, [Rev | _] = Path, BinRows) when is_list(BinRows) -> +fdb_to_doc(Db, DocId, Pos, Path, BinRows) when is_list(BinRows) -> #{ name := DbName, wrapped_kek := WrappedKEK } = Db, Bin = iolist_to_binary(BinRows), - {Encoded, DiskAtts, Deleted} = binary_to_term(Bin, [safe]), + {UpdateCounter, Encoded, DiskAtts, Deleted} = binary_to_term(Bin, [safe]), - BinRev = couch_doc:rev_to_str({Pos, Rev}), {ok, BinBody} = fabric2_encryption:decode( - WrappedKEK, DbName, DocId, BinRev, Encoded), + WrappedKEK, DbName, DocId, UpdateCounter, Encoded), Body = binary_to_term(BinBody, [safe]), Atts = lists:map(fun(Att) ->
