aboutsummaryrefslogtreecommitdiffstats
path: root/lib/crypto/src
diff options
context:
space:
mode:
authorHans Nilsson <[email protected]>2019-01-31 10:27:23 +0100
committerHans Nilsson <[email protected]>2019-02-25 10:01:24 +0100
commitc7cab680b80ef9220832151ed2c8c23a5d590b8b (patch)
treec197e1682ecdea385f93b5bbeee540e444b84e06 /lib/crypto/src
parentb3dbf45fdbe8c584c89b5e061f6e9a28a5d3ea86 (diff)
downloadotp-c7cab680b80ef9220832151ed2c8c23a5d590b8b.tar.gz
otp-c7cab680b80ef9220832151ed2c8c23a5d590b8b.tar.bz2
otp-c7cab680b80ef9220832151ed2c8c23a5d590b8b.zip
crypto: New experimental api
The new files api_ng.h and api_ng.c implements an api using EVP. The api is not by any mean new, except for the crypto application in Erlang/OTP. The aims at using the block api in a stream manor, that is 1) call crypto_init/4 2..N) call crypto_update/{2,3} The purpose is to simplify and hopefully optimize the SSL and SSH applications. By keeping the crypto state in C in an enif_resource the costful state copying in SSL and SSH is reduced with 1-2 per message sent or received. Changes in other files are for adaptation like FIPS etc since many functions uses the central get_cipher_type() function.
Diffstat (limited to 'lib/crypto/src')
-rw-r--r--lib/crypto/src/crypto.erl366
1 files changed, 288 insertions, 78 deletions
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index de8cfac9a2..fe8390c5b8 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -47,6 +47,19 @@
-export([privkey_to_pubkey/2]).
-export([ec_curve/1, ec_curves/0]).
-export([rand_seed/1]).
+
+%% Experiment
+-export([crypto_init/4,
+ crypto_update/2, crypto_update/3,
+ %% Emulates old api:
+ crypto_stream_init/2, crypto_stream_init/3,
+ crypto_stream_encrypt/2,
+ crypto_stream_decrypt/2,
+ crypto_block_encrypt/3, crypto_block_encrypt/4,
+ crypto_block_decrypt/3, crypto_block_decrypt/4
+ ]).
+
+
%% Engine
-export([
engine_get_all_methods/0,
@@ -89,7 +102,8 @@
-export_type([
stream_state/0,
hmac_state/0,
- hash_state/0
+ hash_state/0,
+ crypto_state/0
]).
%% Private. For tests.
@@ -261,17 +275,36 @@
%%%
-type block_cipher_with_iv() :: cbc_cipher()
| cfb_cipher()
- | aes_cbc128
- | aes_cbc256
| aes_ige256
| blowfish_ofb64
- | des3_cbf % cfb misspelled
- | des_ede3
| rc2_cbc .
--type cbc_cipher() :: des_cbc | des3_cbc | aes_cbc | blowfish_cbc .
--type aead_cipher() :: aes_gcm | aes_ccm | chacha20_poly1305 .
--type cfb_cipher() :: aes_cfb128 | aes_cfb8 | blowfish_cfb64 | des3_cfb | des_cfb .
+-type cbc_cipher() :: des_cbc | des_ede3_cbc
+ | blowfish_cbc
+ | aes_cbc | aes_128_cbc | aes_192_cbc | aes_256_cbc
+ | alias_cbc() .
+-type alias_cbc() :: des3_cbc | des_ede3
+ | aes_cbc128 | aes_cbc256 .
+
+-type aead_cipher() :: aes_gcm
+ | aes_128_gcm
+ | aes_192_gcm
+ | aes_256_gcm
+ | aes_ccm
+ | aes_128_ccm
+ | aes_192_ccm
+ | aes_256_ccm
+ | chacha20_poly1305 .
+
+-type cfb_cipher() :: aes_cfb8
+ | aes_cfb128
+ | blowfish_cfb64
+ | des_cfb
+ | des_ede3_cfb
+ | alias_cfb() .
+-type alias_cfb() :: des_ede3_cbf | des3_cbf
+ | des3_cfb .
+
-type block_cipher_without_iv() :: ecb_cipher() .
-type ecb_cipher() :: des_ecb | blowfish_ecb | aes_ecb .
@@ -342,7 +375,7 @@ stop() ->
supports()->
{Hashs, PubKeys, Ciphers, Macs, Curves, RsaOpts} = algorithms(),
[{hashs, Hashs},
- {ciphers, Ciphers},
+ {ciphers, prepend_cipher_aliases(Ciphers)},
{public_keys, PubKeys},
{macs, Macs},
{curves, Curves},
@@ -473,7 +506,7 @@ hmac_final_n(Context, HashLen) ->
Data :: iodata(),
Mac :: binary().
cmac(Type, Key, Data) ->
- notsup_to_error(cmac_nif(Type, Key, Data)).
+ notsup_to_error(cmac_nif(alias(Type), Key, Data)).
-spec cmac(Type, Key, Data, MacLength) ->
Mac when Type :: ?CMAC_CIPHER_ALGORITHM,
@@ -481,8 +514,9 @@ cmac(Type, Key, Data) ->
Data :: iodata(),
MacLength :: integer(),
Mac :: binary().
+
cmac(Type, Key, Data, MacLength) ->
- erlang:binary_part(cmac(Type, Key, Data), 0, MacLength).
+ erlang:binary_part(cmac(alias(Type), Key, Data), 0, MacLength).
%%%---- POLY1305
@@ -499,91 +533,80 @@ poly1305(Key, Data) ->
%%%---- Block ciphers
+%%%----------------------------------------------------------------
-spec block_encrypt(Type::block_cipher_with_iv(), Key::key()|des3_key(), Ivec::binary(), PlainText::iodata()) -> binary();
(Type::aead_cipher(), Key::iodata(), Ivec::binary(), {AAD::binary(), PlainText::iodata()}) ->
{binary(), binary()};
(aes_gcm | aes_ccm, Key::iodata(), Ivec::binary(), {AAD::binary(), PlainText::iodata(), TagLength::1..16}) ->
{binary(), binary()}.
-block_encrypt(Type, Key, Ivec, PlainText) when Type =:= des_cbc;
- Type =:= des_cfb;
- Type =:= blowfish_cbc;
- Type =:= blowfish_cfb64;
- Type =:= blowfish_ofb64;
- Type =:= aes_cbc128;
- Type =:= aes_cfb8;
- Type =:= aes_cfb128;
- Type =:= aes_cbc256;
- Type =:= aes_cbc;
- Type =:= rc2_cbc ->
- block_crypt_nif(Type, Key, Ivec, PlainText, true);
-block_encrypt(Type, Key0, Ivec, PlainText) when Type =:= des3_cbc;
- Type =:= des_ede3 ->
- Key = check_des3_key(Key0),
- block_crypt_nif(des_ede3_cbc, Key, Ivec, PlainText, true);
-block_encrypt(des3_cbf, Key0, Ivec, PlainText) -> % cfb misspelled
- Key = check_des3_key(Key0),
- block_crypt_nif(des_ede3_cbf, Key, Ivec, PlainText, true);
-block_encrypt(des3_cfb, Key0, Ivec, PlainText) ->
+
+block_encrypt(Type, Key, Ivec, Data) ->
+ do_block_encrypt(alias(Type), Key, Ivec, Data).
+
+do_block_encrypt(Type, Key0, Ivec, Data) when Type =:= des_ede3_cbc;
+ Type =:= des_ede3_cfb ->
Key = check_des3_key(Key0),
- block_crypt_nif(des_ede3_cfb, Key, Ivec, PlainText, true);
-block_encrypt(aes_ige256, Key, Ivec, PlainText) ->
+ block_crypt_nif(Type, Key, Ivec, Data, true);
+
+do_block_encrypt(Type, Key, Ivec, PlainText) when Type =:= aes_ige256 ->
notsup_to_error(aes_ige_crypt_nif(Key, Ivec, PlainText, true));
-block_encrypt(Type, Key, Ivec, {AAD, PlainText}) when Type =:= aes_gcm;
- Type =:= aes_ccm ->
- aead_encrypt(Type, Key, Ivec, AAD, PlainText);
-block_encrypt(Type, Key, Ivec, {AAD, PlainText, TagLength}) when Type =:= aes_gcm;
- Type =:= aes_ccm ->
- aead_encrypt(Type, Key, Ivec, AAD, PlainText, TagLength);
-block_encrypt(chacha20_poly1305=Type, Key, Ivec, {AAD, PlainText}) ->
- aead_encrypt(Type, Key, Ivec, AAD, PlainText, 16).
+do_block_encrypt(Type, Key, Ivec, {AAD, PlainText}) when Type =:= chacha20_poly1305 ->
+ aead_encrypt(Type, Key, Ivec, AAD, PlainText, 16);
+do_block_encrypt(Type, Key, Ivec, Data) when Type =:= aes_gcm;
+ Type =:= aes_ccm ->
+ case Data of
+ {AAD, PlainText} ->
+ aead_encrypt(Type, Key, Ivec, AAD, PlainText);
+ {AAD, PlainText, TagLength} ->
+ aead_encrypt(Type, Key, Ivec, AAD, PlainText, TagLength)
+ end;
+
+do_block_encrypt(Type, Key, Ivec, PlainText) ->
+ block_crypt_nif(Type, Key, Ivec, PlainText, true).
+
+
+
+-spec block_encrypt(Type::block_cipher_without_iv(), Key::key(), PlainText::iodata()) -> binary().
+
+block_encrypt(Type, Key, PlainText) ->
+ block_crypt_nif(alias(Type), Key, PlainText, true).
+
+%%%----------------------------------------------------------------
+%%%----------------------------------------------------------------
-spec block_decrypt(Type::block_cipher_with_iv(), Key::key()|des3_key(), Ivec::binary(), Data::iodata()) -> binary();
(Type::aead_cipher(), Key::iodata(), Ivec::binary(),
{AAD::binary(), Data::iodata(), Tag::binary()}) -> binary() | error.
-block_decrypt(Type, Key, Ivec, Data) when Type =:= des_cbc;
- Type =:= des_cfb;
- Type =:= blowfish_cbc;
- Type =:= blowfish_cfb64;
- Type =:= blowfish_ofb64;
- Type =:= aes_cbc;
- Type =:= aes_cbc128;
- Type =:= aes_cfb8;
- Type =:= aes_cfb128;
- Type =:= aes_cbc256;
- Type =:= rc2_cbc ->
- block_crypt_nif(Type, Key, Ivec, Data, false);
-block_decrypt(Type, Key0, Ivec, Data) when Type =:= des3_cbc;
- Type =:= des_ede3 ->
- Key = check_des3_key(Key0),
- block_crypt_nif(des_ede3_cbc, Key, Ivec, Data, false);
-block_decrypt(des3_cbf, Key0, Ivec, Data) -> % cfb misspelled
- Key = check_des3_key(Key0),
- block_crypt_nif(des_ede3_cbf, Key, Ivec, Data, false);
-block_decrypt(des3_cfb, Key0, Ivec, Data) ->
+
+block_decrypt(Type, Key, Ivec, Data) ->
+ do_block_decrypt(alias(Type), Key, Ivec, Data).
+
+do_block_decrypt(Type, Key0, Ivec, Data) when Type =:= des_ede3_cbc;
+ Type =:= des_ede3_cfb ->
Key = check_des3_key(Key0),
- block_crypt_nif(des_ede3_cfb, Key, Ivec, Data, false);
-block_decrypt(aes_ige256, Key, Ivec, Data) ->
+ block_crypt_nif(Type, Key, Ivec, Data, false);
+
+do_block_decrypt(aes_ige256, Key, Ivec, Data) ->
notsup_to_error(aes_ige_crypt_nif(Key, Ivec, Data, false));
-block_decrypt(Type, Key, Ivec, {AAD, Data, Tag}) when Type =:= aes_gcm;
+
+do_block_decrypt(Type, Key, Ivec, {AAD, Data, Tag}) when Type =:= aes_gcm;
Type =:= aes_ccm;
Type =:= chacha20_poly1305 ->
- aead_decrypt(Type, Key, Ivec, AAD, Data, Tag).
-
+ aead_decrypt(Type, Key, Ivec, AAD, Data, Tag);
--spec block_encrypt(Type::block_cipher_without_iv(), Key::key(), PlainText::iodata()) -> binary().
+do_block_decrypt(Type, Key, Ivec, Data) ->
+ block_crypt_nif(Type, Key, Ivec, Data, false).
-block_encrypt(Type, Key, PlainText) ->
- block_crypt_nif(Type, Key, PlainText, true).
-spec block_decrypt(Type::block_cipher_without_iv(), Key::key(), Data::iodata()) -> binary().
block_decrypt(Type, Key, Data) ->
- block_crypt_nif(Type, Key, Data, false).
-
+ block_crypt_nif(alias(Type), Key, Data, false).
+%%%----------------------------------------------------------------
-spec next_iv(Type:: cbc_cipher(), Data) -> NextIVec when % Type :: cbc_cipher(), %des_cbc | des3_cbc | aes_cbc | aes_ige,
Data :: iodata(),
NextIVec :: binary().
@@ -614,18 +637,30 @@ next_iv(Type, Data, _Ivec) ->
-opaque stream_state() :: {stream_cipher(), reference()}.
--type stream_cipher() :: rc4 | aes_ctr | chacha20 .
+-type stream_cipher() :: stream_cipher_iv() | stream_cipher_no_iv() .
+-type stream_cipher_no_iv() :: rc4 .
+-type stream_cipher_iv() :: aes_ctr
+ | aes_128_ctr
+ | aes_192_ctr
+ | aes_256_ctr
+ | chacha20 .
--spec stream_init(Type, Key, IVec) -> State when Type :: aes_ctr | chacha20,
+-spec stream_init(Type, Key, IVec) -> State when Type :: stream_cipher_iv(),
Key :: iodata(),
IVec :: binary(),
State :: stream_state() .
stream_init(aes_ctr, Key, Ivec) ->
{aes_ctr, aes_ctr_stream_init(Key, Ivec)};
+stream_init(aes_128_ctr, Key, Ivec) ->
+ {aes_ctr, aes_ctr_stream_init(Key, Ivec)};
+stream_init(aes_192_ctr, Key, Ivec) ->
+ {aes_ctr, aes_ctr_stream_init(Key, Ivec)};
+stream_init(aes_256_ctr, Key, Ivec) ->
+ {aes_ctr, aes_ctr_stream_init(Key, Ivec)};
stream_init(chacha20, Key, Ivec) ->
{chacha20, chacha20_stream_init(Key,Ivec)}.
--spec stream_init(Type, Key) -> State when Type :: rc4,
+-spec stream_init(Type, Key) -> State when Type :: stream_cipher_no_iv(),
Key :: iodata(),
State :: stream_state() .
stream_init(rc4, Key) ->
@@ -932,7 +967,7 @@ rand_seed_nif(_Seed) -> ?nif_stub.
DigestType :: rsa_digest_type()
| dss_digest_type()
| ecdsa_digest_type(),
- Msg :: binary() | {digest,binary()},
+ Msg :: iodata() | {digest,iodata()},
Key :: rsa_private()
| dss_private()
| [ecdsa_private() | ecdsa_params()]
@@ -951,7 +986,7 @@ sign(Algorithm, Type, Data, Key) ->
| dss_digest_type()
| ecdsa_digest_type()
| none,
- Msg :: binary() | {digest,binary()},
+ Msg :: iodata() | {digest,iodata()},
Key :: rsa_private()
| dss_private()
| [ecdsa_private() | ecdsa_params()]
@@ -980,7 +1015,7 @@ pkey_sign_nif(_Algorithm, _Type, _Digest, _Key, _Options) -> ?nif_stub.
| dss_digest_type()
| ecdsa_digest_type()
| none,
- Msg :: binary() | {digest,binary()},
+ Msg :: iodata() | {digest,iodata()},
Signature :: binary(),
Key :: rsa_public()
| dss_public()
@@ -998,7 +1033,7 @@ verify(Algorithm, Type, Data, Signature, Key) ->
DigestType :: rsa_digest_type()
| dss_digest_type()
| ecdsa_digest_type(),
- Msg :: binary() | {digest,binary()},
+ Msg :: iodata() | {digest,iodata()},
Signature :: binary(),
Key :: rsa_public()
| dss_public()
@@ -2180,3 +2215,178 @@ check_otp_test_engine(LibDir) ->
{error, notexist}
end
end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%
+%%% Experimental NG
+%%%
+
+%%% -> {ok,State::ref()} | {error,Reason}
+
+-opaque crypto_state() :: reference() | {any(),any(),any(),any()}.
+
+
+%%%----------------------------------------------------------------
+%%%
+%%% Create and initialize a new state for encryption or decryption
+%%%
+
+-spec crypto_init(Cipher, Key, IV, EncryptFlag) -> {ok,State} | {error,term()} | undefined
+ when Cipher :: stream_cipher()
+ | block_cipher_with_iv()
+ | block_cipher_without_iv() ,
+ Key :: iodata(),
+ IV :: binary(),
+ EncryptFlag :: boolean() | undefined,
+ State :: crypto_state() .
+
+crypto_init(Cipher, Key, IV, EncryptFlag) when is_atom(Cipher),
+ is_binary(Key),
+ is_binary(IV),
+ is_atom(EncryptFlag) ->
+ case ng_crypto_init_nif(alias(Cipher), Key, IV, EncryptFlag) of
+ {error,Error} ->
+ {error,Error};
+ undefined -> % For compatibility function crypto_stream_init/3
+ undefined;
+ Ref when is_reference(Ref) ->
+ {ok,Ref};
+ State when is_tuple(State),
+ size(State)==4 ->
+ {ok,State} % compatibility with old cryptolibs < 1.0.1
+ end.
+
+
+%%%----------------------------------------------------------------
+%%%
+%%% Encrypt/decrypt a sequence of bytes. The sum of the sizes
+%%% of all blocks must be an integer multiple of the crypto's
+%%% blocksize.
+%%%
+
+-spec crypto_update(State, Data) -> {ok,Result} | {error,term()}
+ when State :: crypto_state(),
+ Data :: iodata(),
+ Result :: binary() | {crypto_state(),binary()}.
+crypto_update(State, Data) ->
+ mk_ret(ng_crypto_update_nif(State, Data)).
+
+%%%----------------------------------------------------------------
+%%%
+%%% Encrypt/decrypt a sequence of bytes but change the IV first.
+%%% Not applicable for all modes.
+%%%
+
+-spec crypto_update(State, Data, IV) -> {ok,Result} | {error,term()}
+ when State :: crypto_state(),
+ Data :: iodata(),
+ IV :: binary(),
+ Result :: binary() | {crypto_state(),binary()}.
+crypto_update(State, Data, IV) ->
+ mk_ret(ng_crypto_update_nif(State, Data, IV)).
+
+%%%----------------------------------------------------------------
+%%% Helpers
+mk_ret(R) -> mk_ret(R, []).
+
+mk_ret({error,Error}, _) ->
+ {error,Error};
+mk_ret(Bin, Acc) when is_binary(Bin) ->
+ {ok, iolist_to_binary(lists:reverse([Bin|Acc]))};
+mk_ret({State1,Bin}, Acc) when is_tuple(State1),
+ size(State1) == 4,
+ is_binary(Bin) ->
+ %% compatibility with old cryptolibs < 1.0.1
+ {ok, {State1, iolist_to_binary(lists:reverse([Bin|Acc]))}}.
+
+%%%----------------------------------------------------------------
+%%% NIFs
+ng_crypto_init_nif(_Cipher, _Key, _IVec, _EncryptFlg) -> ?nif_stub.
+ng_crypto_update_nif(_State, _Data) -> ?nif_stub.
+ng_crypto_update_nif(_State, _Data, _IV) -> ?nif_stub.
+
+%%%================================================================
+%%% Compatibility functions to be called by "old" api functions.
+
+%%%--------------------------------
+%%%---- block encrypt/decrypt
+crypto_block_encrypt(Cipher, Key, Data) -> crypto_block_encrypt(Cipher, Key, <<>>, Data).
+crypto_block_decrypt(Cipher, Key, Data) -> crypto_block_decrypt(Cipher, Key, <<>>, Data).
+
+crypto_block_encrypt(Cipher, Key, Ivec, Data) -> crypto_block(Cipher, Key, Ivec, Data, true).
+crypto_block_decrypt(Cipher, Key, Ivec, Data) -> crypto_block(Cipher, Key, Ivec, Data, false).
+
+%% AEAD: use old funcs
+
+%%%---- helper
+crypto_block(Cipher, Key, IV, Data, EncryptFlag) ->
+ case crypto_init(Cipher, iolist_to_binary(Key), iolist_to_binary(IV), EncryptFlag) of
+ {ok, Ref} ->
+ case crypto_update(Ref, Data) of
+ {ok, {_,Bin}} when is_binary(Bin) -> Bin;
+ {ok, Bin} when is_binary(Bin) -> Bin;
+ {error,_} -> error(badarg)
+ end;
+
+ {error,_} -> error(badarg)
+ end.
+
+%%%--------------------------------
+%%%---- stream init, encrypt/decrypt
+
+crypto_stream_init(Cipher, Key) ->
+ crypto_stream_init(Cipher, Key, <<>>).
+
+crypto_stream_init(Cipher, Key0, IV0) ->
+ Key = iolist_to_binary(Key0),
+ IV = iolist_to_binary(IV0),
+ %% First check the argumensts:
+ case crypto_init(Cipher, Key, IV, undefined) of
+ undefined ->
+ {Cipher, {Key, IV}};
+ {error,_} ->
+ {error,badarg}
+ end.
+
+crypto_stream_encrypt(State, PlainText) ->
+ crypto_stream_emulate(State, PlainText, true).
+
+crypto_stream_decrypt(State, CryptoText) ->
+ crypto_stream_emulate(State, CryptoText, false).
+
+
+%%%---- helper
+crypto_stream_emulate({Cipher,{Key,IV}}, Data, EncryptFlag) ->
+ case crypto_init(Cipher, Key, IV, EncryptFlag) of
+ {ok,State} ->
+ crypto_stream_emulate({Cipher,State}, Data, EncryptFlag);
+ {error,_} ->
+ error(badarg)
+ end;
+crypto_stream_emulate({Cipher,State}, Data, _) ->
+ case crypto_update(State, Data) of
+ {ok, {State1,Bin}} when is_binary(Bin) -> {{Cipher,State1},Bin};
+ {ok,Bin} when is_binary(Bin) -> {{Cipher,State},Bin};
+ {error,_} -> error(badarg)
+ end.
+
+
+%%%================================================================
+
+prepend_cipher_aliases(L) ->
+ [des3_cbc, des_ede3, des_ede3_cbf, des3_cbf, des3_cfb, aes_cbc128, aes_cbc256 | L].
+
+
+%%%---- des_ede3_cbc
+alias(des3_cbc) -> des_ede3_cbc;
+alias(des_ede3) -> des_ede3_cbc;
+%%%---- des_ede3_cfb
+alias(des_ede3_cbf) -> des_ede3_cfb;
+alias(des3_cbf) -> des_ede3_cfb;
+alias(des3_cfb) -> des_ede3_cfb;
+%%%---- aes_*_cbc
+alias(aes_cbc128) -> aes_128_cbc;
+alias(aes_cbc256) -> aes_256_cbc;
+
+alias(Alg) -> Alg.