You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2022/05/09 18:02:25 UTC

[couchdb] branch aegis_3.x updated (3281159af -> c7b1a14dd)

This is an automated email from the ASF dual-hosted git repository.

rnewson pushed a change to branch aegis_3.x
in repository https://gitbox.apache.org/repos/asf/couchdb.git


 discard 3281159af use AES_SIV (RFC 5297) instead of AES Key Wrap
     new c7b1a14dd use AES_SIV (RFC 5297) instead of AES Key Wrap

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (3281159af)
            \
             N -- N -- N   refs/heads/aegis_3.x (c7b1a14dd)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/couch/src/couch_file.erl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)


[couchdb] 01/01: use AES_SIV (RFC 5297) instead of AES Key Wrap

Posted by rn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rnewson pushed a commit to branch aegis_3.x
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit c7b1a14ddb61dae066f3ec1a748c8626c32ebea3
Author: Robert Newson <rn...@apache.org>
AuthorDate: Mon May 9 14:29:05 2022 +0100

    use AES_SIV (RFC 5297) instead of AES Key Wrap
---
 rebar.config.script             |   1 +
 rel/reltool.config              |   2 +
 src/aegis/src/aegis.app.src     |  28 ++++++++++
 src/aegis/src/aegis.erl         |  51 ++++++++++++++++++
 src/aegis/src/aegis_cmac.erl    |  67 +++++++++++++++++++++++
 src/aegis/src/aegis_s2v.erl     |  51 ++++++++++++++++++
 src/aegis/src/aegis_siv.erl     | 106 ++++++++++++++++++++++++++++++++++++
 src/aegis/src/aegis_util.erl    |  90 +++++++++++++++++++++++++++++++
 src/couch/src/couch_file.erl    |  10 ++--
 src/couch/src/couch_keywrap.erl | 115 ----------------------------------------
 10 files changed, 401 insertions(+), 120 deletions(-)

diff --git a/rebar.config.script b/rebar.config.script
index da3fc58a1..fdee29bc1 100644
--- a/rebar.config.script
+++ b/rebar.config.script
@@ -113,6 +113,7 @@ 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/reltool.config b/rel/reltool.config
index ab26fb2ed..a7ab87c5f 100644
--- a/rel/reltool.config
+++ b/rel/reltool.config
@@ -26,6 +26,7 @@
         syntax_tools,
         xmerl,
         %% couchdb
+        aegis,
         b64url,
         bear,
         chttpd,
@@ -90,6 +91,7 @@
     {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/src/aegis.app.src b/src/aegis/src/aegis.app.src
new file mode 100644
index 000000000..9088ec46c
--- /dev/null
+++ b/src/aegis/src/aegis.app.src
@@ -0,0 +1,28 @@
+% 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
new file mode 100644
index 000000000..90e3f330a
--- /dev/null
+++ b/src/aegis/src/aegis.erl
@@ -0,0 +1,51 @@
+% 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
new file mode 100644
index 000000000..074a77346
--- /dev/null
+++ b/src/aegis/src/aegis_cmac.erl
@@ -0,0 +1,67 @@
+% 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]).
+
+cmac(Key, Message) ->
+    cmac(Key, <<0:128>>, Message).
+
+cmac(Key, X, <<Last:16/binary>>) ->
+    {K1, _K2} = generate_subkeys(Key),
+    crypto:crypto_one_time(cmac_cipher(Key), Key, crypto:exor(X, crypto:exor(Last, K1)), true);
+
+cmac(Key, X, <<Block:16/binary, Rest/binary>>) ->
+    cmac(Key, crypto:crypto_one_time(cmac_cipher(Key), Key, crypto:exor(X, Block), true), Rest);
+
+cmac(Key, X, Last) ->
+    {_K1, K2} = generate_subkeys(Key),
+    crypto:crypto_one_time(cmac_cipher(Key), Key,
+        crypto:exor(X, crypto:exor(aegis_util:pad(Last), K2)), true).
+
+
+generate_subkeys(Key) ->
+    L = crypto:crypto_one_time(cmac_cipher(Key), Key, <<0:128>>, true),
+    K1 = aegis_util:double(L),
+    K2 = aegis_util:double(K1),
+    {K1, K2}.
+
+cmac_cipher(Key) when bit_size(Key) == 128 ->
+    aes_128_ecb;
+cmac_cipher(Key) when bit_size(Key) == 256 ->
+    aes_256_ecb.
+
+
+-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_s2v.erl b/src/aegis/src/aegis_s2v.erl
new file mode 100644
index 000000000..0bae2c403
--- /dev/null
+++ b/src/aegis/src/aegis_s2v.erl
@@ -0,0 +1,51 @@
+% 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
new file mode 100644
index 000000000..4c0542956
--- /dev/null
+++ b/src/aegis/src/aegis_siv.erl
@@ -0,0 +1,106 @@
+% 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]).
+
+-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) ->
+    Cipher = ctr_cipher(Key),
+    crypto:crypto_one_time(Cipher, Key, IV, Data, true).
+
+
+ctr_cipher(Key) when bit_size(Key) == 128 ->
+    aes_128_ctr;
+
+ctr_cipher(Key) when bit_size(Key) == 256 ->
+    aes_256_ctr.
+
+
+-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
new file mode 100644
index 000000000..1705ac222
--- /dev/null
+++ b/src/aegis/src/aegis_util.erl
@@ -0,0 +1,90 @@
+% 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
+]).
+
+%% @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 = crypto:crypto_one_time(aes_256_ecb, Key, <<0:128>>, true),
+    K1 = crypto:crypto_one_time(aes_256_ecb, Key, <<1:128>>, true),
+    K2 = crypto:crypto_one_time(aes_256_ecb, Key, <<2:128>>, true),
+    K3 = crypto:crypto_one_time(aes_256_ecb, Key, <<3:128>>, true),
+    <<K0/binary, K1/binary, K2/binary, K3/binary>>.
+
+
+%% @doc pad
+%% indicates padding of string X, len(X) &lt; 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) &gt;= 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_file.erl b/src/couch/src/couch_file.erl
index f64c36ef2..9b5114168 100644
--- a/src/couch/src/couch_file.erl
+++ b/src/couch/src/couch_file.erl
@@ -23,7 +23,7 @@
 -define(IS_OLD_STATE(S), is_pid(S#file.db_monitor)).
 -define(PREFIX_SIZE, 5).
 -define(DEFAULT_READ_COUNT, 1024).
--define(ENCRYPTED_HEADER, 0,1,2,3,4,5,6,7).
+-define(ENCRYPTED_HEADER, 0, 1, 2, 3, 4, 5, 6, 8, 7, 9, 10, 11, 12, 13, 14, 15).
 
 -type block_id() :: non_neg_integer().
 -type location() :: non_neg_integer().
@@ -931,7 +931,7 @@ reset_eof(#file{} = File) ->
 %% we've wiped all the data, including the wrapped key, so we need a new one.
 init_crypto(#file{eof = 0} = File) ->
     Key = crypto:strong_rand_bytes(32),
-    WrappedKey = couch_keywrap:key_wrap(master_key(), Key),
+    WrappedKey = aegis:wrap_key(master_key(), [], Key),
     Header = <<?ENCRYPTED_HEADER, WrappedKey/binary>>,
     ok = file:write(File#file.fd, Header),
     ok = file:sync(File#file.fd),
@@ -939,9 +939,9 @@ init_crypto(#file{eof = 0} = File) ->
 
 %% we're opening an existing file and need to unwrap the key.
 init_crypto(#file{enc = undefined, dec = undefined} = File) ->
-    case file:pread(File#file.fd, 0, 48) of
-        {ok, <<?ENCRYPTED_HEADER, WrappedKey/binary>>} ->
-            case couch_keywrap:key_unwrap(master_key(), WrappedKey) of
+    case file:pread(File#file.fd, 0, 64) of
+        {ok, <<?ENCRYPTED_HEADER, WrappedKey:48/binary>>} ->
+            case aegis:unwrap_key(master_key(), [], WrappedKey) of
                 fail ->
                     {error, unwrap_failed};
                 Key when is_binary(Key) ->
diff --git a/src/couch/src/couch_keywrap.erl b/src/couch/src/couch_keywrap.erl
deleted file mode 100644
index 2cfa2b104..000000000
--- a/src/couch/src/couch_keywrap.erl
+++ /dev/null
@@ -1,115 +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(couch_keywrap).
-
-%% Implementation of NIST Special Publication 800-38F
-%% For wrapping and unwrapping keys with AES.
-
--export([key_wrap/2, key_unwrap/2]).
-
--define(ICV1, 16#A6A6A6A6A6A6A6A6).
-
--spec key_wrap(WrappingKey :: binary(), KeyToWrap :: binary()) -> binary().
-key_wrap(WrappingKey, KeyToWrap) when
-    is_binary(WrappingKey), bit_size(KeyToWrap) rem 64 == 0
-->
-    N = bit_size(KeyToWrap) div 64,
-    wrap(WrappingKey, <<?ICV1:64>>, KeyToWrap, 1, 6 * N).
-
-wrap(_WrappingKey, A, R, T, End) when T > End ->
-    <<A/binary, R/binary>>;
-wrap(WrappingKey, A, R, T, End) ->
-    <<R1:64, Rest/binary>> = R,
-    <<MSB_B:64, LSB_B:64>> = crypto:crypto_one_time(aes_256_ecb, WrappingKey, <<A/binary, R1:64>>, true),
-    wrap(WrappingKey, <<(MSB_B bxor T):64>>, <<Rest/binary, LSB_B:64>>, T + 1, End).
-
--spec key_unwrap(WrappingKey :: binary(), KeyToUnwrap :: binary()) -> binary() | fail.
-key_unwrap(WrappingKey, KeyToUnwrap) when
-    is_binary(WrappingKey), bit_size(KeyToUnwrap) rem 64 == 0
-->
-    N = (bit_size(KeyToUnwrap) div 64),
-    <<A:64, R/binary>> = KeyToUnwrap,
-    case unwrap(WrappingKey, <<A:64>>, R, 6 * (N - 1)) of
-        <<?ICV1:64, UnwrappedKey/binary>> ->
-            UnwrappedKey;
-        _ ->
-            fail
-    end.
-
-unwrap(_WrappingKey, A, R, 0) ->
-    <<A/binary, R/binary>>;
-unwrap(WrappingKey, <<A:64>>, R, T) ->
-    RestSize = bit_size(R) - 64,
-    <<Rest:RestSize, R2:64>> = R,
-    <<MSB_B:64, LSB_B:64>> = crypto:crypto_one_time(aes_256_ecb, WrappingKey, <<(A bxor T):64, R2:64>>, false),
-    unwrap(WrappingKey, <<MSB_B:64>>, <<LSB_B:64, Rest:RestSize>>, T - 1).
-
--ifdef(TEST).
--include_lib("eunit/include/eunit.hrl").
-
-wrap_test_() ->
-    [
-        %% 128 KEK / 128 DATA
-        test_wrap_unwrap(
-            <<16#000102030405060708090A0B0C0D0E0F:128>>,
-            <<16#00112233445566778899AABBCCDDEEFF:128>>,
-            <<16#1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5:192>>
-        ),
-        %% 192 KEK / 128 DATA
-        test_wrap_unwrap(
-            <<16#000102030405060708090A0B0C0D0E0F1011121314151617:192>>,
-            <<16#00112233445566778899AABBCCDDEEFF:128>>,
-            <<16#96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D:192>>
-        ),
-        %% 256 KEK / 128 DATA
-        test_wrap_unwrap(
-            <<16#000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F:256>>,
-            <<16#00112233445566778899AABBCCDDEEFF:128>>,
-            <<16#64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7:192>>
-        ),
-        %% 192 KEK / 192 DATA
-        test_wrap_unwrap(
-            <<16#000102030405060708090A0B0C0D0E0F1011121314151617:192>>,
-            <<16#00112233445566778899AABBCCDDEEFF0001020304050607:192>>,
-            <<16#031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2:256>>
-        ),
-        %% 256 KEK / 192 DATA
-        test_wrap_unwrap(
-            <<16#000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F:256>>,
-            <<16#00112233445566778899AABBCCDDEEFF0001020304050607:192>>,
-            <<16#A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1:256>>
-        ),
-        %% 256 KEK / 256 DATA
-        test_wrap_unwrap(
-            <<16#000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F:256>>,
-            <<16#00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F:256>>,
-            <<
-                16#28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21:320
-            >>
-        )
-    ].
-
-test_wrap_unwrap(WrappingKey, KeyToWrap, ExpectedWrappedKey) ->
-    [
-        ?_assertEqual(ExpectedWrappedKey, key_wrap(WrappingKey, KeyToWrap)),
-        ?_assertEqual(KeyToWrap, key_unwrap(WrappingKey, key_wrap(WrappingKey, KeyToWrap)))
-    ].
-
-fail_test() ->
-    KEK = <<16#000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F:256>>,
-    CipherText = <<
-        16#28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD20:320
-    >>,
-    ?assertEqual(fail, key_unwrap(KEK, CipherText)).
-
--endif.