This is an automated email from the ASF dual-hosted git repository. rnewson pushed a commit to branch database_encryption in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 0f0e9b11214a7ebb28927e8b08dba3f139f35559 Author: Robert Newson <[email protected]> AuthorDate: Mon Jun 13 22:07:26 2022 +0100 simplify --- rebar.config.script | 1 - rel/overlay/etc/default.ini | 7 - rel/reltool.config | 2 - src/aegis/rebar.config.script | 42 ----- src/aegis/src/aegis.app.src | 28 ---- src/aegis/src/aegis.erl | 60 ------- src/aegis/src/aegis_cmac.erl | 79 ---------- src/aegis/src/aegis_compat.hrl | 42 ----- src/aegis/src/aegis_key_manager.erl | 36 ----- src/aegis/src/aegis_key_manager_config.erl | 64 -------- src/aegis/src/aegis_s2v.erl | 53 ------- src/aegis/src/aegis_siv.erl | 136 ---------------- src/aegis/src/aegis_util.erl | 90 ----------- src/couch/src/couch_bt_engine.erl | 3 +- src/couch/src/couch_bt_engine_compactor.erl | 17 +- src/couch/src/couch_db_updater.erl | 6 +- .../src/couch_encryption_manager.erl} | 21 +-- src/couch/src/couch_file.erl | 173 +++++++++++++-------- src/couch_mrview/src/couch_mrview_compactor.erl | 6 +- src/couch_mrview/src/couch_mrview_index.erl | 6 +- src/couch_mrview/src/couch_mrview_util.erl | 8 +- 21 files changed, 153 insertions(+), 727 deletions(-) diff --git a/rebar.config.script b/rebar.config.script index 0e6af11d3..44c4d61b1 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -113,7 +113,6 @@ os:putenv("COUCHDB_APPS_CONFIG_DIR", filename:join([COUCHDB_ROOT, "rel/apps"])). SubDirs = [ %% must be compiled first as it has a custom behavior "src/couch_epi", - "src/aegis", "src/couch_log", "src/chttpd", "src/couch", diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini index 19b02ed30..162ccb926 100644 --- a/rel/overlay/etc/default.ini +++ b/rel/overlay/etc/default.ini @@ -747,10 +747,3 @@ port = {{prometheus_port}} ; to disable this setting could be if the views need an upgrade but located on ; read-only file system. ;commit_on_header_upgrade = true - -[encryption] -wrapping_key_id = bec46c7438e685d6 - -[encryption_keys] -254abda45788029b = 12b856a100016bbf5e3bab3f7db22cb3c6a7a9442322f16aa998649198df3445 -bec46c7438e685d6 = f9dae865d5396cf146745df47b68f6bb8f3a3118b39758d4cb2f44cf746d3dcb diff --git a/rel/reltool.config b/rel/reltool.config index a7ab87c5f..ab26fb2ed 100644 --- a/rel/reltool.config +++ b/rel/reltool.config @@ -26,7 +26,6 @@ syntax_tools, xmerl, %% couchdb - aegis, b64url, bear, chttpd, @@ -91,7 +90,6 @@ {app, xmerl, [{incl_cond, include}]}, %% couchdb - {app, aegis, [{incl_cond, include}]}, {app, b64url, [{incl_cond, include}]}, {app, bear, [{incl_cond, include}]}, {app, chttpd, [{incl_cond, include}]}, diff --git a/src/aegis/rebar.config.script b/src/aegis/rebar.config.script deleted file mode 100644 index cb334032b..000000000 --- a/src/aegis/rebar.config.script +++ /dev/null @@ -1,42 +0,0 @@ -% Licensed under the Apache License, Version 2.0 (the "License"); you may not -% use this file except in compliance with the License. You may obtain a copy of -% the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -% License for the specific language governing permissions and limitations under -% the License. - - -CoverProps = [ - {cover_enabled, true}, - {cover_print_enabled, true} -], - -CouchConfig = case filelib:is_file(os:getenv("COUCHDB_CONFIG")) of - true -> - {ok, Result} = file:consult(os:getenv("COUCHDB_CONFIG")), - Result; - false -> - [] -end. - -AegisKeyManager = case lists:keyfind(aegis_key_manager, 1, CouchConfig) of - {aegis_key_manager, Module} when Module /= "" -> - list_to_atom(Module); - _ -> - aegis_key_manager_noop -end, - -CurrentOpts = case lists:keyfind(erl_opts, 1, CONFIG) of - {erl_opts, Opts} -> Opts; - false -> [] -end, - -Config = CoverProps ++ CONFIG, - -AegisOpts = {d, 'AEGIS_KEY_MANAGER', AegisKeyManager}, -lists:keystore(erl_opts, 1, Config, {erl_opts, [AegisOpts | CurrentOpts]}). \ No newline at end of file diff --git a/src/aegis/src/aegis.app.src b/src/aegis/src/aegis.app.src deleted file mode 100644 index 9088ec46c..000000000 --- a/src/aegis/src/aegis.app.src +++ /dev/null @@ -1,28 +0,0 @@ -% Licensed under the Apache License, Version 2.0 (the "License"); you may not -% use this file except in compliance with the License. You may obtain a copy of -% the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -% License for the specific language governing permissions and limitations under -% the License. - -{application, aegis, - [{description, "An OTP application"}, - {vsn, git}, - {registered, []}, - {applications, - [kernel, - stdlib, - crypto - ]}, - {env,[]}, - {modules, []}, - - {maintainers, []}, - {licenses, []}, - {links, []} - ]}. diff --git a/src/aegis/src/aegis.erl b/src/aegis/src/aegis.erl deleted file mode 100644 index ac7b49b6d..000000000 --- a/src/aegis/src/aegis.erl +++ /dev/null @@ -1,60 +0,0 @@ -% Licensed under the Apache License, Version 2.0 (the "License"); you may not -% use this file except in compliance with the License. You may obtain a copy of -% the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -% License for the specific language governing permissions and limitations under -% the License. - --module(aegis). - --export([wrap_key/3, unwrap_key/3]). - -wrap_key(KEK, AAD, DEK) when is_binary(KEK), is_list(AAD), is_binary(DEK) -> - ExpandedKey = aegis_util:expand(KEK), - {CipherText, CipherTag} = - aegis_siv:block_encrypt( - ExpandedKey, - AAD, - DEK - ), - <<CipherTag/binary, CipherText/binary>>. - -unwrap_key(KEK, AAD, <<CipherTag:16/binary, CipherText/binary>>) when - is_binary(KEK), is_list(AAD) --> - ExpandedKey = aegis_util:expand(KEK), - aegis_siv:block_decrypt( - ExpandedKey, - AAD, - {CipherText, CipherTag} - ). - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -aegis_test_() -> - [ - ?_assertEqual( - <<91, 78, 2, 43, 95, 157, 34, 252, 93, 35, 150, 141, 155, 139, 247, 136, 154, 203, 16, - 143, 196, 78, 93, 9, 189, 119, 22, 27, 60, 47, 186, 114, 70, 231, 113, 189, 36, 236, - 139, 153, 85, 58, 207, 165, 169, 70, 67, 61>>, - wrap_key(<<0:256>>, [], <<1:256>>) - ), - ?_assertEqual( - <<1:256>>, - unwrap_key( - <<0:256>>, - [], - <<91, 78, 2, 43, 95, 157, 34, 252, 93, 35, 150, 141, 155, 139, 247, 136, 154, 203, - 16, 143, 196, 78, 93, 9, 189, 119, 22, 27, 60, 47, 186, 114, 70, 231, 113, 189, - 36, 236, 139, 153, 85, 58, 207, 165, 169, 70, 67, 61>> - ) - ) - ]. - --endif. diff --git a/src/aegis/src/aegis_cmac.erl b/src/aegis/src/aegis_cmac.erl deleted file mode 100644 index 1ba73415e..000000000 --- a/src/aegis/src/aegis_cmac.erl +++ /dev/null @@ -1,79 +0,0 @@ -% Licensed under the Apache License, Version 2.0 (the "License"); you may not -% use this file except in compliance with the License. You may obtain a copy of -% the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -% License for the specific language governing permissions and limitations under -% the License. - --module(aegis_cmac). - --export([cmac/2]). - --include("aegis_compat.hrl"). - -cmac(Key, Message) -> - cmac(Key, <<0:128>>, Message). - -cmac(Key, X, <<Last:16/binary>>) -> - {K1, _K2} = generate_subkeys(Key), - ?block_encrypt(?ecb(Key), Key, crypto:exor(X, crypto:exor(Last, K1))); -cmac(Key, X, <<Block:16/binary, Rest/binary>>) -> - cmac(Key, ?block_encrypt(?ecb(Key), Key, crypto:exor(X, Block)), Rest); -cmac(Key, X, Last) -> - {_K1, K2} = generate_subkeys(Key), - ?block_encrypt( - ?ecb(Key), - Key, - crypto:exor(X, crypto:exor(aegis_util:pad(Last), K2)) - ). - -generate_subkeys(Key) -> - L = ?block_encrypt(?ecb(Key), Key, <<0:128>>), - K1 = aegis_util:double(L), - K2 = aegis_util:double(K1), - {K1, K2}. - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -cmac_test_() -> - [ - ?_assertEqual( - <<16#bb1d6929e95937287fa37d129b756746:128>>, - cmac( - <<16#2b7e151628aed2a6abf7158809cf4f3c:128>>, - <<>> - ) - ), - - ?_assertEqual( - <<16#070a16b46b4d4144f79bdd9dd04a287c:128>>, - cmac( - <<16#2b7e151628aed2a6abf7158809cf4f3c:128>>, - <<16#6bc1bee22e409f96e93d7e117393172a:128>> - ) - ), - - ?_assertEqual( - <<16#028962f61b7bf89efc6b551f4667d983:128>>, - cmac( - <<16#603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4:256>>, - <<>> - ) - ), - - ?_assertEqual( - <<16#28a7023f452e8f82bd4bf28d8c37c35c:128>>, - cmac( - <<16#603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4:256>>, - <<16#6bc1bee22e409f96e93d7e117393172a:128>> - ) - ) - ]. - --endif. diff --git a/src/aegis/src/aegis_compat.hrl b/src/aegis/src/aegis_compat.hrl deleted file mode 100644 index 9de05eb27..000000000 --- a/src/aegis/src/aegis_compat.hrl +++ /dev/null @@ -1,42 +0,0 @@ -% Licensed under the Apache License, Version 2.0 (the "License"); you may not -% use this file except in compliance with the License. You may obtain a copy of -% the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -% License for the specific language governing permissions and limitations under -% the License. - -%% Assume old crypto api - --define(block_encrypt(Cipher, Key, Data), crypto:block_encrypt(Cipher, Key, Data)). - --define(stream_encrypt(Cipher, Key, IVec, Data), element(2, crypto:stream_encrypt(crypto:stream_init(Cipher, Key, IVec), Data))). - --define(ecb(Key), aes_ecb). - --define(ctr(Key), aes_ctr). - -%% Replace macros if new crypto api is available --ifdef(OTP_RELEASE). --if(?OTP_RELEASE >= 22). - --undef(block_encrypt). --define(block_encrypt(Cipher, Key, Data), crypto:crypto_one_time(Cipher, Key, Data, true)). - --undef(stream_encrypt). --define(stream_encrypt(Cipher, Key, IVec, Data), crypto:crypto_one_time(Cipher, Key, IVec, Data, true)). - --undef(ecb). --define(ecb(Key), case bit_size(Key) of - 128 -> aes_128_ecb; 192 -> aes_192_ecb; 256 -> aes_256_ecb end). - --undef(ctr). --define(ctr(Key), case bit_size(Key) of - 128 -> aes_128_ctr; 192 -> aes_192_ctr; 256 -> aes_256_ctr end). - --endif. --endif. \ No newline at end of file diff --git a/src/aegis/src/aegis_key_manager.erl b/src/aegis/src/aegis_key_manager.erl deleted file mode 100644 index de7bf3808..000000000 --- a/src/aegis/src/aegis_key_manager.erl +++ /dev/null @@ -1,36 +0,0 @@ -% Licensed under the Apache License, Version 2.0 (the "License"); you may not -% use this file except in compliance with the License. You may obtain a copy of -% the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -% License for the specific language governing permissions and limitations under -% the License. - --module(aegis_key_manager). - --export([ - wrap_key/2, - unwrap_key/2 -]). - --type dek() :: binary(). --type wek() :: binary(). - --callback wrap_key(Filepath :: binary(), DataEncryptionKey :: dek()) -> - {ok, WrappedKey :: wek()} - | dont_encrypt - | {error, Reason :: term()}. - --callback unwrap_key(Filepath :: binary(), WrappedKey :: wek()) -> - {ok, DataEncryptionKey :: dek()} - | {error, Reason :: term()}. - -wrap_key(Filepath, DataEncryptionKey) -> - ?AEGIS_KEY_MANAGER:wrap_key(Filepath, DataEncryptionKey). - -unwrap_key(Filepath, WrappedKey) -> - ?AEGIS_KEY_MANAGER:unwrap_key(Filepath, WrappedKey). diff --git a/src/aegis/src/aegis_key_manager_config.erl b/src/aegis/src/aegis_key_manager_config.erl deleted file mode 100644 index a1c8cefbf..000000000 --- a/src/aegis/src/aegis_key_manager_config.erl +++ /dev/null @@ -1,64 +0,0 @@ -% Licensed under the Apache License, Version 2.0 (the "License"); you may not -% use this file except in compliance with the License. You may obtain a copy of -% the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -% License for the specific language governing permissions and limitations under -% the License. - --module(aegis_key_manager_config). --behaviour(aegis_key_manager). - --export([ - wrap_key/2, - unwrap_key/2 -]). - -wrap_key(_Filepath, DataEncryptionKey) when is_binary(DataEncryptionKey) -> - {ok, WrappingKeyId, WrappingKey} = current_wrapping_key(), - WrappedKey = aegis:wrap_key(WrappingKey, [WrappingKeyId], DataEncryptionKey), - {ok, <<(byte_size(WrappingKeyId)):8, WrappingKeyId/binary, WrappedKey/binary>>}. - -unwrap_key( - _Filepath, <<WrappingKeyIdLen:8, WrappingKeyId:WrappingKeyIdLen/binary, WrappedKey/binary>> -) -> - case wrapping_key(WrappingKeyId) of - {ok, WrappingKeyId, WrappingKey} -> - case aegis:unwrap_key(WrappingKey, [WrappingKeyId], WrappedKey) of - fail -> - {error, unwrap_failed}; - Key when is_binary(Key) -> - {ok, Key} - end; - {error, Reason} -> - {error, Reason} - end; -unwrap_key(_, _) -> - {error, invalid_key}. - -current_wrapping_key() -> - wrapping_key(config:get("encryption", "wrapping_key_id")). - -wrapping_key(KeyId) when is_binary(KeyId) -> - wrapping_key(binary_to_list(KeyId)); -wrapping_key(KeyId) when is_list(KeyId), length(KeyId) == 16 -> - case get_config_binary("encryption_keys", KeyId, undefined) of - Hex when is_binary(Hex), byte_size(Hex) == 64 -> - {ok, list_to_binary(KeyId), couch_util:from_hex(Hex)}; - undefined -> - {error, no_key}; - _ -> - {error, invalid_key} - end. - -get_config_binary(Section, Key, Default) -> - case config:get(Section, Key) of - undefined -> - Default; - Value -> - list_to_binary(Value) - end. diff --git a/src/aegis/src/aegis_s2v.erl b/src/aegis/src/aegis_s2v.erl deleted file mode 100644 index 0bc6f49cd..000000000 --- a/src/aegis/src/aegis_s2v.erl +++ /dev/null @@ -1,53 +0,0 @@ -% Licensed under the Apache License, Version 2.0 (the "License"); you may not -% use this file except in compliance with the License. You may obtain a copy of -% the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -% License for the specific language governing permissions and limitations under -% the License. - --module(aegis_s2v). - --export([s2v/3]). - -s2v(Key, [], <<>>) -> - aegis_cmac:cmac(Key, <<1:128>>); -s2v(Key, AAD, PlainText) when length(AAD) < 127 -> - s2v(Key, AAD, PlainText, aegis_cmac:cmac(Key, <<0:128>>)). - -s2v(Key, [], PlainText, Acc) when bit_size(PlainText) >= 128 -> - aegis_cmac:cmac(Key, aegis_util:xorend(PlainText, Acc)); -s2v(Key, [], PlainText, Acc) -> - aegis_cmac:cmac( - Key, - crypto:exor(aegis_util:double(Acc), aegis_util:pad(PlainText)) - ); -s2v(Key, [H | T], PlainText, Acc0) -> - Acc1 = crypto:exor(aegis_util:double(Acc0), aegis_cmac:cmac(Key, H)), - s2v(Key, T, PlainText, Acc1). - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -s2v_0_test() -> - ?assertEqual( - <<16#85632d07c6e8f37f950acd320a2ecc93:128>>, - s2v( - <<16#fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0:128>>, - [<<16#101112131415161718191a1b1c1d1e1f2021222324252627:192>>], - <<16#112233445566778899aabbccddee:112>> - ) - ). - -%% for test coverage only. this value does not come from a test vector. -s2v_1_test() -> - ?assertEqual( - <<106, 56, 130, 35, 180, 192, 121, 7, 97, 30, 181, 248, 111, 114, 85, 151>>, - s2v(<<0:128>>, [], <<>>) - ). - --endif. diff --git a/src/aegis/src/aegis_siv.erl b/src/aegis/src/aegis_siv.erl deleted file mode 100644 index 83d3b69ea..000000000 --- a/src/aegis/src/aegis_siv.erl +++ /dev/null @@ -1,136 +0,0 @@ -% Licensed under the Apache License, Version 2.0 (the "License"); you may not -% use this file except in compliance with the License. You may obtain a copy of -% the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -% License for the specific language governing permissions and limitations under -% the License. - --module(aegis_siv). - --export([block_encrypt/3, block_decrypt/3]). - --include("aegis_compat.hrl"). - --spec block_encrypt(binary(), list(), binary()) -> {binary(), binary()}. -block_encrypt(Key, AAD, PlainText) when - bit_size(Key) == 256; bit_size(Key) == 512 --> - {K1, K2} = split(Key), - <<V:128>> = aegis_s2v:s2v(K1, AAD, PlainText), - Q = V band 16#ffffffffffffffff7fffffff7fffffff, - CipherText = aes_ctr(K2, <<Q:128>>, PlainText), - {CipherText, <<V:128>>}. - -block_decrypt(Key, AAD, {CipherText, <<V:128>>}) when - bit_size(Key) == 256; bit_size(Key) == 512 --> - {K1, K2} = split(Key), - Q = V band 16#ffffffffffffffff7fffffff7fffffff, - PlainText = aes_ctr(K2, <<Q:128>>, CipherText), - <<T:128>> = aegis_s2v:s2v(K1, AAD, PlainText), - case V == T of - true -> - PlainText; - false -> - fail - end. - -split(Key) -> - Half = byte_size(Key) div 2, - <<K1:Half/binary, K2:Half/binary>> = Key, - {K1, K2}. - -aes_ctr(Key, IV, Data) -> - ?stream_encrypt(?ctr(Key), Key, IV, Data). - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -encrypt_test_() -> - [ - ?_assertEqual( - {<<16#40c02b9690c4dc04daef7f6afe5c:112>>, <<16#85632d07c6e8f37f950acd320a2ecc93:128>>}, - block_encrypt( - <<16#fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff:256>>, - [<<16#101112131415161718191a1b1c1d1e1f2021222324252627:192>>], - <<16#112233445566778899aabbccddee:112>> - ) - ), - - ?_assertEqual( - { - << - 16#cb900f2fddbe404326601965c889bf17dba77ceb094fa663b7a3f748ba8af829ea64ad544a272e9c485b62a3fd5c0d:376 - >>, - <<16#7bdb6e3b432667eb06f4d14bff2fbd0f:128>> - }, - block_encrypt( - <<16#7f7e7d7c7b7a79787776757473727170404142434445464748494a4b4c4d4e4f:256>>, - [ - << - 16#00112233445566778899aabbccddeeffdeaddadadeaddadaffeeddccbbaa99887766554433221100:320 - >>, - <<16#102030405060708090a0:80>>, - <<16#09f911029d74e35bd84156c5635688c0:128>> - ], - << - 16#7468697320697320736f6d6520706c61696e7465787420746f20656e6372797074207573696e67205349562d414553:376 - >> - ) - ) - ]. - -decrypt_test_() -> - [ - ?_assertEqual( - <<16#112233445566778899aabbccddee:112>>, - block_decrypt( - <<16#fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff:256>>, - [<<16#101112131415161718191a1b1c1d1e1f2021222324252627:192>>], - {<<16#40c02b9690c4dc04daef7f6afe5c:112>>, << - 16#85632d07c6e8f37f950acd320a2ecc93:128 - >>} - ) - ), - - ?_assertEqual( - fail, - block_decrypt( - <<16#fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff:256>>, - [<<16#101112131415161718191a1b1c1d1e1f2021222324252627:192>>], - {<<16#40c02b9690c4dc04daef7f6afe5c:112>>, << - 16#85632d07c6e8f37f950acd320a2ecc94:128 - >>} - ) - ), - - ?_assertEqual( - << - 16#7468697320697320736f6d6520706c61696e7465787420746f20656e6372797074207573696e67205349562d414553:376 - >>, - - block_decrypt( - <<16#7f7e7d7c7b7a79787776757473727170404142434445464748494a4b4c4d4e4f:256>>, - [ - << - 16#00112233445566778899aabbccddeeffdeaddadadeaddadaffeeddccbbaa99887766554433221100:320 - >>, - <<16#102030405060708090a0:80>>, - <<16#09f911029d74e35bd84156c5635688c0:128>> - ], - { - << - 16#cb900f2fddbe404326601965c889bf17dba77ceb094fa663b7a3f748ba8af829ea64ad544a272e9c485b62a3fd5c0d:376 - >>, - <<16#7bdb6e3b432667eb06f4d14bff2fbd0f:128>> - } - ) - ) - ]. - --endif. diff --git a/src/aegis/src/aegis_util.erl b/src/aegis/src/aegis_util.erl deleted file mode 100644 index 7303b67e4..000000000 --- a/src/aegis/src/aegis_util.erl +++ /dev/null @@ -1,90 +0,0 @@ -% Licensed under the Apache License, Version 2.0 (the "License"); you may not -% use this file except in compliance with the License. You may obtain a copy of -% the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -% License for the specific language governing permissions and limitations under -% the License. - --module(aegis_util). - --export([ - double/1, - expand/1, - pad/1, - xorend/2 -]). - --include("aegis_compat.hrl"). - -%% @doc double -%% is the multiplication of S and 0...010 in the finite field -%% represented using the primitive polynomial -%% x<sup>128</sup> + x<sup>7</sup> + x<sup>2</sup> + x + 1. -%% @end --spec double(Val :: binary()) -> binary(). -double(<<0:1, Lo:127>>) -> - <<(Lo bsl 1):128>>; -double(<<1:1, Lo:127>>) -> - crypto:exor(<<(Lo bsl 1):128>>, <<16#87:128>>). - -%% because SIV only uses half the bits of the input key -%% to encrypt and the other half for the authentication/IV -%% we expand our keys to 512 to ensure an overall security -%% threshold of 256. -expand(Key) when bit_size(Key) == 256 -> - %% expansion technique from Bjoern Tackmann - IBM Zurich - K0 = ?block_encrypt(?ecb(Key), Key, <<0:128>>), - K1 = ?block_encrypt(?ecb(Key), Key, <<1:128>>), - K2 = ?block_encrypt(?ecb(Key), Key, <<2:128>>), - K3 = ?block_encrypt(?ecb(Key), Key, <<3:128>>), - <<K0/binary, K1/binary, K2/binary, K3/binary>>. - -%% @doc pad -%% indicates padding of string X, len(X) < 128, out to 128 bits by -%% the concatenation of a single bit of 1 followed by as many 0 bits -%% as are necessary. -%% @end --spec pad(binary()) -> binary(). -pad(Val) when bit_size(Val) =< 128 -> - Pad = 128 - bit_size(Val) - 1, - <<Val/binary, 1:1, 0:Pad>>. - -%% @doc xorend -%% where len(A) >= len(B), means xoring a string B onto the end of -%% string A -- i.e., leftmost(A, len(A)-len(B)) || (rightmost(A, -%% len(B)) xor B). -%% @end --spec xorend(binary(), binary()) -> binary(). -xorend(A, B) when byte_size(A) >= byte_size(B) -> - Diff = byte_size(A) - byte_size(B), - <<Left:Diff/binary, Right/binary>> = A, - Xor = crypto:exor(Right, B), - <<Left/binary, Xor/binary>>. - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -double_0_test() -> - ?assertEqual( - <<16#1c09bf5f83df7e080280b050b37e0e74:128>>, - double(<<16#0e04dfafc1efbf040140582859bf073a:128>>) - ). - -double_1_test() -> - ?assertEqual( - <<16#dbe13bd0ed8c85dc9af179c99ddbf819:128>>, - double(<<16#edf09de876c642ee4d78bce4ceedfc4f:128>>) - ). - -pad_test() -> - ?assertEqual( - <<16#112233445566778899aabbccddee8000:128>>, - pad(<<16#112233445566778899aabbccddee:112>>) - ). - --endif. diff --git a/src/couch/src/couch_bt_engine.erl b/src/couch/src/couch_bt_engine.erl index 486ed7cb0..d270f8071 100644 --- a/src/couch/src/couch_bt_engine.erl +++ b/src/couch/src/couch_bt_engine.erl @@ -834,7 +834,8 @@ open_db_file(FilePath, Options) -> {error, enoent} -> % Couldn't find file. is there a compact version? This ca % happen (rarely) if we crashed during the file switch. - case couch_file:open(FilePath ++ ".compact", [nologifmissing]) of + Options2 = couch_encryption_manager:encryption_options(Options), + case couch_file:open(FilePath ++ ".compact", [nologifmissing | Options2]) of {ok, Fd} -> Fmt = "Recovering from compaction file: ~s~s", couch_log:info(Fmt, [FilePath, ".compact"]), diff --git a/src/couch/src/couch_bt_engine_compactor.erl b/src/couch/src/couch_bt_engine_compactor.erl index 8ed55b5c3..e39ab76c1 100644 --- a/src/couch/src/couch_bt_engine_compactor.erl +++ b/src/couch/src/couch_bt_engine_compactor.erl @@ -54,7 +54,11 @@ start(#st{} = St, DbName, Options, Parent) -> couch_db_engine:trigger_on_compact(DbName), ?COMP_EVENT(init), - {ok, InitCompSt} = open_compaction_files(DbName, St, Options), + EncryptionOptions = case couch_encryption_manager:get_key(DbName) of + false -> []; + KEK -> [{kek, KEK}] + end, + {ok, InitCompSt} = open_compaction_files(DbName, St, Options ++ EncryptionOptions), ?COMP_EVENT(files_opened), Stages = [ @@ -94,8 +98,9 @@ open_compaction_files(DbName, OldSt, Options) -> } = OldSt, DataFile = DbFilePath ++ ".compact.data", MetaFile = DbFilePath ++ ".compact.meta", - {ok, DataFd, DataHdr} = open_compaction_file(DataFile), - {ok, MetaFd, MetaHdr} = open_compaction_file(MetaFile), + EncryptionOptions = couch_encryption_manager:encryption_options(Options), + {ok, DataFd, DataHdr} = open_compaction_file(DataFile, EncryptionOptions), + {ok, MetaFd, MetaHdr} = open_compaction_file(MetaFile, EncryptionOptions), DataHdrIsDbHdr = couch_bt_engine_header:is_header(DataHdr), CompSt = case {DataHdr, MetaHdr} of @@ -623,15 +628,15 @@ compact_final_sync(#comp_st{new_st = St0} = CompSt) -> new_st = St1 }. -open_compaction_file(FilePath) -> - case couch_file:open(FilePath, [nologifmissing]) of +open_compaction_file(FilePath, FileOpenOptions) -> + case couch_file:open(FilePath, [nologifmissing | FileOpenOptions]) of {ok, Fd} -> case couch_file:read_header(Fd) of {ok, Header} -> {ok, Fd, Header}; no_valid_header -> {ok, Fd, nil} end; {error, enoent} -> - {ok, Fd} = couch_file:open(FilePath, [create]), + {ok, Fd} = couch_file:open(FilePath, [create | FileOpenOptions]), {ok, Fd, nil} end. diff --git a/src/couch/src/couch_db_updater.erl b/src/couch/src/couch_db_updater.erl index 17a1e9160..eca81c7f0 100644 --- a/src/couch/src/couch_db_updater.erl +++ b/src/couch/src/couch_db_updater.erl @@ -37,7 +37,11 @@ init({Engine, DbName, FilePath, Options0}) -> erlang:put(io_priority, {db_update, DbName}), update_idle_limit_from_config(), DefaultSecObj = default_security_object(DbName), - Options = [{default_security_object, DefaultSecObj} | Options0], + Options = [{default_security_object, DefaultSecObj} | Options0] ++ + case couch_encryption_manager:get_key(DbName) of + false -> []; + KEK -> [{kek, KEK}] + end, try {ok, EngineState} = couch_db_engine:init(Engine, FilePath, Options), Db = init_db(DbName, FilePath, EngineState, Options), diff --git a/src/aegis/src/aegis_key_manager_noop.erl b/src/couch/src/couch_encryption_manager.erl similarity index 58% rename from src/aegis/src/aegis_key_manager_noop.erl rename to src/couch/src/couch_encryption_manager.erl index 8e1a3f540..ca736324e 100644 --- a/src/aegis/src/aegis_key_manager_noop.erl +++ b/src/couch/src/couch_encryption_manager.erl @@ -10,16 +10,17 @@ % License for the specific language governing permissions and limitations under % the License. --module(aegis_key_manager_noop). --behaviour(aegis_key_manager). +-module(couch_encryption_manager). --export([ - wrap_key/2, - unwrap_key/2 -]). +-export([get_key/1, encryption_options/1]). -wrap_key(_Filepath, _DataEncryptionKey) -> - dont_encrypt. +-spec get_key(DbName :: binary()) -> KEK :: binary() | false. +get_key(_DbName) -> + <<0:256>>. -unwrap_key(_Filepath, _DataEncryptionKey) -> - {error, encryption_not_supported}. +%% Extract just the encryption related options from an options list. +encryption_options(Options) -> + case lists:keyfind(kek, 1, Options) of + false -> []; + {kek, KEK} -> [{kek, KEK}] + end. \ No newline at end of file diff --git a/src/couch/src/couch_file.erl b/src/couch/src/couch_file.erl index 153dbd422..879070016 100644 --- a/src/couch/src/couch_file.erl +++ b/src/couch/src/couch_file.erl @@ -29,7 +29,7 @@ %% If, at couch_file creation time, encryption is enabled, couch_file %% generates a random 256-bit AES key and a random 128 bit -%% initialisation vector. The currently configured aegis key manager +%% initialisation vector. The currently configured key manager %% module is asked to wrap the key and return it as a %% binary. couch_file stores the IV and the wrapped key in a special %% header block, always block 0, in the first 4 KiB of the file. All @@ -48,20 +48,49 @@ db_monitor, pread_limit = 0, iv, - key + dek, + wek }). --define(encrypt(File, Pos, Data), encrypt_20(File, Pos, Data)). --define(decrypt(File, Pos, Data), decrypt_20(File, Pos, Data)). +-define(encrypt_ctr(File, Pos, Data), + crypto:stream_encrypt( + crypto:stream_init(aes_ctr, File#file.dek, aes_ctr(File#file.iv, Pos)), Data + ) +). +-define(decrypt_ctr(File, Pos, Data), + crypto:stream_decrypt( + crypto:stream_init(aes_ctr, File#file.dek, aes_ctr(File#file.iv, Pos)), Data + ) +). + +-define(aes_gcm_encrypt(Key, IV, AAD, Data), + crypto:block_encrypt(aes_gcm, Key, IV, {AAD, Data, 16})), + +-define(aes_gcm_decrypt(Key, IV, AAD, CipherText, CipherTag), + crypto:block_decrypt(aes_gcm, Key, IV, {AAD, CipherText, CipherTag})). -ifdef(OTP_RELEASE). -if(?OTP_RELEASE >= 22). --undef(encrypt). --define(encrypt(File, Pos, Data), encrypt_22(File, Pos, Data)). +-undef(encrypt_ctr). +-define(encrypt_ctr(File, Pos, Data), + crypto:crypto_one_time(aes_256_ctr, File#file.dek, aes_ctr(File#file.iv, Pos), Data, true) +). + +-undef(decrypt_ctr). +-define(decrypt_ctr(File, Pos, Data), + crypto:crypto_one_time(aes_256_ctr, File#file.dek, aes_ctr(File#file.iv, Pos), Data, false) +). --undef(decrypt). --define(decrypt(File, Pos, Data), decrypt_22(File, Pos, Data)). +-undef(aes_gcm_encrypt). +-define(aes_gcm_encrypt(Key, IV, AAD, Data), + crypto:crypto_one_time_aead(aes_256_gcm, Key, IV, Data, AAD, 16, true) +). + +-undef(aes_gcm_decrypt). +-define(aes_gcm_decrypt(Key, IV, AAD, CipherText, CipherTag), + crypto:crypto_one_time_aead(aes_256_gcm, Key, IV, CipherText, AAD, CipherTag, false) +). -endif. -endif. @@ -469,9 +498,12 @@ init({Filepath, Options, ReturnPid, Ref}) -> ok = file:sync(Fd), maybe_track_open_os_files(Options), erlang:send_after(?INITIAL_WAIT, self(), maybe_close), - init_crypto(Filepath, #file{ - fd = Fd, is_sys = IsSys, pread_limit = Limit - }); + init_crypto( + #file{ + fd = Fd, is_sys = IsSys, pread_limit = Limit + }, + Options + ); false -> ok = file:close(Fd), init_status_error(ReturnPid, Ref, {error, eexist}) @@ -479,9 +511,12 @@ init({Filepath, Options, ReturnPid, Ref}) -> false -> maybe_track_open_os_files(Options), erlang:send_after(?INITIAL_WAIT, self(), maybe_close), - init_crypto(Filepath, #file{ - fd = Fd, is_sys = IsSys, pread_limit = Limit - }) + init_crypto( + #file{ + fd = Fd, is_sys = IsSys, pread_limit = Limit + }, + Options + ) end; Error -> init_status_error(ReturnPid, Ref, Error) @@ -498,9 +533,12 @@ init({Filepath, Options, ReturnPid, Ref}) -> maybe_track_open_os_files(Options), {ok, Eof} = file:position(Fd, eof), erlang:send_after(?INITIAL_WAIT, self(), maybe_close), - init_crypto(Filepath, #file{ - fd = Fd, eof = Eof, is_sys = IsSys, pread_limit = Limit - }); + init_crypto( + #file{ + fd = Fd, eof = Eof, is_sys = IsSys, pread_limit = Limit + }, + Options + ); Error -> init_status_error(ReturnPid, Ref, Error) end; @@ -603,8 +641,7 @@ handle_call({truncate, Pos}, _From, #file{fd = Fd} = File) -> {ok, Pos} = file:position(Fd, Pos), case file:truncate(Fd) of ok -> - {_Fd, Filepath} = get(couch_file_fd), - case init_crypto(Filepath, File#file{eof = Pos}) of + case init_crypto(File#file{eof = Pos}, []) of {ok, File1} -> {reply, ok, File1}; {error, Reason} -> @@ -947,33 +984,49 @@ reset_eof(#file{} = File) -> {ok, Eof} = file:position(File#file.fd, eof), File#file{eof = Eof}. -%% new file or we've wiped all the data, including the wrapped key, so we need a new one. -init_crypto(Filepath, #file{eof = 0} = File0) -> - DataEncryptionKey = crypto:strong_rand_bytes(32), - IV = crypto:strong_rand_bytes(16), - case aegis_key_manager:wrap_key(Filepath, DataEncryptionKey) of - {ok, WrappedKey} -> - case write_encryption_header(File0, WrappedKey, IV) of +%% new file. +init_crypto(#file{eof = 0, dek = undefined, wek = undefined} = File0, Options) -> + case lists:keyfind(kek, 1, Options) of + {kek, KEK} -> + DEK = crypto:strong_rand_bytes(32), + WEK = wrap_key(KEK, DEK), + IV = crypto:strong_rand_bytes(16), + case write_encryption_header(File0, WEK, IV) of {ok, File1} -> ok = file:sync(File1#file.fd), - {ok, init_crypto(File1, DataEncryptionKey, IV)}; + {ok, init_crypto(File1, WEK, DEK, IV)}; {error, Reason} -> {error, Reason} end; - dont_encrypt -> - {ok, File0}; + false -> + {ok, File0} + end; +%% truncated file. +init_crypto(#file{eof = Pos, dek = DEK, wek = WEK} = File0, _Options) when + Pos < ?SIZE_BLOCK, is_binary(DEK), is_binary(WEK) +-> + IV = crypto:strong_rand_bytes(16), + case write_encryption_header(File0, WEK, IV) of + {ok, File1} -> + ok = file:sync(File1#file.fd), + {ok, init_crypto(File1, WEK, DEK, IV)}; {error, Reason} -> {error, Reason} end; %% we're opening an existing file and need to unwrap the key if file is encrypted. -init_crypto(Filepath, #file{eof = Pos, key = undefined} = File) when Pos > 0 -> +init_crypto(#file{eof = Pos, dek = undefined} = File, Options) when Pos >= ?SIZE_BLOCK -> case read_encryption_header(File) of - {ok, WrappedKey, IV} -> - case aegis_key_manager:unwrap_key(Filepath, WrappedKey) of - {ok, DataEncryptionKey} -> - {ok, init_crypto(File, DataEncryptionKey, IV)}; - {error, Reason} -> - {error, Reason} + {ok, WEK, IV} -> + case lists:keyfind(kek, 1, Options) of + {kek, KEK} -> + case unwrap_key(KEK, WEK) of + error -> + {error, <<"failed to unwrap encryption key">>}; + DEK when is_binary(DEK) -> + {ok, init_crypto(File, WEK, DEK, IV)} + end; + false -> + {error, <<"required encryption key not supplied">>} end; not_encrypted -> {ok, File}; @@ -981,19 +1034,27 @@ init_crypto(Filepath, #file{eof = Pos, key = undefined} = File) when Pos > 0 -> {error, Reason} end. -init_crypto(#file{} = File, DataEncryptionKey, IV) when - is_binary(DataEncryptionKey), is_binary(IV) --> - File#file{iv = crypto:bytes_to_integer(IV), key = DataEncryptionKey}. +init_crypto(#file{} = File, WEK, DEK, IV) when is_binary(WEK), is_binary(DEK), is_binary(IV) -> + File#file{iv = crypto:bytes_to_integer(IV), wek = WEK, dek = DEK}. + +wrap_key(KEK, DEK) when is_binary(KEK), is_binary(DEK) -> + IV = crypto:strong_rand_bytes(16), + {<<_:32/binary>> = CipherText, <<_:16/binary>> = CipherTag} = ?aes_gcm_encrypt( + KEK, IV, <<>>, DEK + ), + <<IV:16/binary, CipherText/binary, CipherTag/binary>>. + +unwrap_key(KEK, <<IV:16/binary, CipherText:32/binary, CipherTag:16/binary>>) when is_binary(KEK) -> + ?aes_gcm_decrypt(KEK, IV, <<>>, CipherText, CipherTag). -write_encryption_header(#file{eof = 0} = File, WrappedKey, IV) when - byte_size(WrappedKey) < 1024, bit_size(IV) == 128 +write_encryption_header(#file{eof = Pos} = File, WrappedKey, IV) when + Pos < ?SIZE_BLOCK, byte_size(WrappedKey) < 1024, bit_size(IV) == 128 -> Header = [<<?ENCRYPTED_HEADER>>, IV, <<(byte_size(WrappedKey)):16>>, WrappedKey], PaddedHeader = [Header, <<0:((?SIZE_BLOCK - iolist_size(Header) - 32) * 8)>>], DigestHeader = [PaddedHeader, crypto:hash(sha256, PaddedHeader)], ?SIZE_BLOCK = iolist_size(DigestHeader), - case file:write(File#file.fd, DigestHeader) of + case file:pwrite(File#file.fd, 0, DigestHeader) of ok -> {ok, File#file{eof = ?SIZE_BLOCK}}; {error, Reason} -> @@ -1021,13 +1082,13 @@ read_encryption_header(#file{} = File) -> %% We can encrypt any section of the file but we must make %% sure we align with the key stream. -encrypted_write(#file{key = undefined} = File, Data) -> +encrypted_write(#file{dek = undefined} = File, Data) -> file:write(File#file.fd, Data); encrypted_write(#file{} = File, Data) -> - CipherText = ?encrypt(File, File#file.eof, pad(File#file.eof, Data)), + CipherText = ?encrypt_ctr(File, File#file.eof, pad(File#file.eof, Data)), file:write(File#file.fd, unpad(File#file.eof, CipherText)). -encrypted_pread(#file{key = undefined} = File, LocNums) -> +encrypted_pread(#file{dek = undefined} = File, LocNums) -> file:pread(File#file.fd, LocNums); encrypted_pread(#file{} = File, LocNums) -> case file:pread(File#file.fd, LocNums) of @@ -1035,7 +1096,7 @@ encrypted_pread(#file{} = File, LocNums) -> {ok, lists:zipwith( fun({Pos, _Len}, CipherText) -> - PlainText = ?decrypt(File, Pos, pad(Pos, CipherText)), + PlainText = ?decrypt_ctr(File, Pos, pad(Pos, CipherText)), unpad(Pos, PlainText) end, LocNums, @@ -1045,31 +1106,17 @@ encrypted_pread(#file{} = File, LocNums) -> Else end. -encrypted_pread(#file{key = undefined} = File, Pos, Len) -> +encrypted_pread(#file{dek = undefined} = File, Pos, Len) -> file:pread(File#file.fd, Pos, Len); encrypted_pread(#file{} = File, Pos, Len) -> case file:pread(File#file.fd, Pos, Len) of {ok, CipherText} -> - PlainText = ?decrypt(File, Pos, pad(Pos, CipherText)), + PlainText = ?decrypt_ctr(File, Pos, pad(Pos, CipherText)), {ok, unpad(Pos, PlainText)}; Else -> Else end. -encrypt_20(#file{key = Key, iv = IV}, Pos, Data) -> - State = crypto:stream_init(aes_ctr, Key, aes_ctr(IV, Pos)), - crypto:stream_encrypt(State, Data). - -decrypt_20(#file{key = Key, iv = IV}, Pos, Data) -> - State = crypto:stream_init(aes_ctr, Key, aes_ctr(IV, Pos)), - crypto:stream_decrypt(State, Data). - -encrypt_22(#file{key = Key, iv = IV}, Pos, Data) -> - crypto:crypto_one_time(aes_256_ctr, Key, aes_ctr(IV, Pos), Data, true). - -decrypt_22(#file{key = Key, iv = IV}, Pos, Data) -> - crypto:crypto_one_time(aes_256_ctr, Key, aes_ctr(IV, Pos), Data, false). - aes_ctr(IV, Pos) -> <<(IV + (Pos div 16)):128>>. diff --git a/src/couch_mrview/src/couch_mrview_compactor.erl b/src/couch_mrview/src/couch_mrview_compactor.erl index 28e5a9b3d..be67adbcb 100644 --- a/src/couch_mrview/src/couch_mrview_compactor.erl +++ b/src/couch_mrview/src/couch_mrview_compactor.erl @@ -47,7 +47,11 @@ compact(State) -> {EmptyState, NumDocIds} = couch_util:with_db(DbName, fun(Db) -> CompactFName = couch_mrview_util:compaction_file(DbName, Sig), - {ok, Fd} = couch_mrview_util:open_file(CompactFName), + Options = case couch_encryption_manager:get_key(DbName) of + false -> []; + KEK -> [{kek, KEK}] + end, + {ok, Fd} = couch_mrview_util:open_file(CompactFName, Options), ESt = couch_mrview_util:reset_index(Db, Fd, State), {ok, Count} = couch_db:get_doc_count(Db), diff --git a/src/couch_mrview/src/couch_mrview_index.erl b/src/couch_mrview/src/couch_mrview_index.erl index 1bfdb2818..bcbb19ce5 100644 --- a/src/couch_mrview/src/couch_mrview_index.erl +++ b/src/couch_mrview/src/couch_mrview_index.erl @@ -120,7 +120,11 @@ open(Db, State0) -> OldSig = couch_mrview_util:maybe_update_index_file(State), - case couch_mrview_util:open_file(IndexFName) of + Options = case couch_encryption_manager:get_key(DbName) of + false -> []; + KEK -> [{kek, KEK}] + end, + case couch_mrview_util:open_file(IndexFName, Options) of {ok, Fd} -> case couch_file:read_header(Fd) of % upgrade code for <= 2.x diff --git a/src/couch_mrview/src/couch_mrview_util.erl b/src/couch_mrview/src/couch_mrview_util.erl index 9e3d292ed..f9c533376 100644 --- a/src/couch_mrview/src/couch_mrview_util.erl +++ b/src/couch_mrview/src/couch_mrview_util.erl @@ -17,7 +17,7 @@ -export([verify_view_filename/1, get_signature_from_filename/1]). -export([ddoc_to_mrst/2, init_state/4, reset_index/3]). -export([make_header/1]). --export([index_file/2, compaction_file/2, open_file/1]). +-export([index_file/2, compaction_file/2, open_file/2]). -export([delete_files/2, delete_index_file/2, delete_compaction_file/2]). -export([get_row_count/1, all_docs_reduce_to_count/1, reduce_to_count/1]). -export([all_docs_key_opts/1, all_docs_key_opts/2, key_opts/1, key_opts/2]). @@ -791,10 +791,10 @@ compaction_file(DbName, Sig) -> FileName = couch_index_util:hexsig(Sig) ++ ".compact.view", couch_index_util:index_file(mrview, DbName, FileName). -open_file(FName) -> - case couch_file:open(FName, [nologifmissing]) of +open_file(FName, Options) -> + case couch_file:open(FName, [nologifmissing | Options]) of {ok, Fd} -> {ok, Fd}; - {error, enoent} -> couch_file:open(FName, [create]); + {error, enoent} -> couch_file:open(FName, [create | Options]); Error -> Error end.
