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 <[email protected]> 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) < 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_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.
