diff options
author | Niclas Eklund <[email protected]> | 2011-04-15 15:49:42 +0200 |
---|---|---|
committer | Niclas Eklund <[email protected]> | 2011-04-15 15:49:42 +0200 |
commit | f505644be64cd4844742b361a8ff411d70f4aa88 (patch) | |
tree | f0b198ec6b9dbb0fdbc211f5ea06f9791ca04bc1 /lib/crypto | |
parent | fb1eab890f6776419b3999285dff45802658e8f0 (diff) | |
download | otp-f505644be64cd4844742b361a8ff411d70f4aa88.tar.gz otp-f505644be64cd4844742b361a8ff411d70f4aa88.tar.bz2 otp-f505644be64cd4844742b361a8ff411d70f4aa88.zip |
Adds NIFs for cryptographically strong random number generation.
Also adds documentation and unit tests.
Thanks to Geoff Cant.
Diffstat (limited to 'lib/crypto')
-rw-r--r-- | lib/crypto/c_src/crypto.c | 60 | ||||
-rw-r--r-- | lib/crypto/doc/src/crypto.xml | 36 | ||||
-rw-r--r-- | lib/crypto/src/crypto.erl | 15 | ||||
-rw-r--r-- | lib/crypto/test/crypto_SUITE.erl | 31 |
4 files changed, 141 insertions, 1 deletions
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index b8786f6f94..d4139f0dfa 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -134,7 +134,9 @@ static ERL_NIF_TERM des_ede3_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_T static ERL_NIF_TERM aes_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM aes_ctr_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM rand_bytes_1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM rand_bytes_3(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM strong_rand_uniform_3(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -204,7 +206,9 @@ static ErlNifFunc nif_funcs[] = { {"aes_ctr_encrypt", 3, aes_ctr_encrypt}, {"aes_ctr_decrypt", 3, aes_ctr_encrypt}, {"rand_bytes", 1, rand_bytes_1}, + {"strong_rand_bytes_nif", 1, strong_rand_bytes_nif}, {"rand_bytes", 3, rand_bytes_3}, + {"strong_rand_uniform", 3, strong_rand_uniform_3}, {"rand_uniform_nif", 2, rand_uniform_nif}, {"mod_exp_nif", 3, mod_exp_nif}, {"dss_verify", 4, dss_verify}, @@ -704,6 +708,22 @@ static ERL_NIF_TERM rand_bytes_1(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar ERL_VALGRIND_MAKE_MEM_DEFINED(data, bytes); return ret; } +static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Bytes) */ + unsigned bytes; + unsigned char* data; + ERL_NIF_TERM ret; + if (!enif_get_uint(env, argv[0], &bytes)) { + return enif_make_badarg(env); + } + data = enif_make_new_binary(env, bytes, &ret); + if ( RAND_bytes(data, bytes) != 1) { + return atom_false; + } + ERL_VALGRIND_MAKE_MEM_DEFINED(data, bytes); + return ret; +} + static ERL_NIF_TERM rand_bytes_3(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Bytes, TopMask, BottomMask) */ unsigned bytes; @@ -724,6 +744,46 @@ static ERL_NIF_TERM rand_bytes_3(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar } return ret; } +static ERL_NIF_TERM strong_rand_uniform_3(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Bytes, TopMask, BottomMask) */ + unsigned bits; + BIGNUM *bn_rand; + int top, bottom; + unsigned char* data; + unsigned dlen; + ERL_NIF_TERM ret; + if (!enif_get_uint(env, argv[0], &bits) + || !enif_get_int(env, argv[1], &top) + || !enif_get_int(env, argv[2], &bottom)) { + return enif_make_badarg(env); + } + if (! (top == -1 || top == 0 || top == 1) ) { + return enif_make_badarg(env); + } + if (! (bottom == 0 || bottom == 1) ) { + return enif_make_badarg(env); + } + + bn_rand = BN_new(); + if (! bn_rand ) { + return enif_make_badarg(env); + } + + /* Get a (bits) bit random number */ + if (! BN_rand(bn_rand, bits, top, bottom) ) { + return enif_make_badarg(env); + } + + /* Copy the bignum into an erlang mpint binary. */ + dlen = BN_num_bytes(bn_rand); + data = enif_make_new_binary(env, dlen+4, &ret); + put_int32(data, dlen); + BN_bn2bin(bn_rand, data+4); + ERL_VALGRIND_MAKE_MEM_DEFINED(data+4, dlen); + BN_free(bn_rand); + + return ret; +} static int get_bn_from_mpint(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp) { diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index c407350c47..087e9ac00c 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -615,6 +615,19 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> </desc> </func> <func> + <name>strong_rand_bytes(N) -> binary()</name> + <fsummary>Generate a binary of random bytes</fsummary> + <type> + <v>N = integer()</v> + </type> + <desc> + <p>Generates N bytes randomly uniform 0..255, and returns the + result in a binary. Uses a cryptographically secure prng seeded and + periodically mixed with operating system provided entropy. By default + this is the <c>RAND_bytes</c> method from OpenSSL.</p> + </desc> + </func> + <func> <name>rand_uniform(Lo, Hi) -> N</name> <fsummary>Generate a random number</fsummary> <type> @@ -629,6 +642,29 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> </desc> </func> <func> + <name>strong_rand_uniform(N, Top, Bottom) -> Mpint</name> + <fsummary>Generate an N bit random number</fsummary> + <type> + <v>N = non_neg_integer()</v> + <v>Top = -1 | 0 | 1</v> + <v>Bottom = 0 | 1</v> + <v>Mpint = binary()</v> + </type> + <desc> + <p>Generate an N bit random number using OpenSSL's + cryptographically strong pseudo random number generator + <c>BN_rand</c>.</p> + <p>The parameter <c>Top</c> places constraints on the most + significant bits of the generated number. If <c>Top</c> is 1, then the + two most significant bits will be set to 1, if <c>Top</c> is 0, the + most significant bit will be 1, and if <c>Top</c> is -1 then no + constraints are applied and thus the generated number may be less than + N bits long.</p> + <p>If <c>Bottom</c> is 1, then the generated number is + constrained to be odd.</p> + </desc> + </func> + <func> <name>mod_exp(N, P, M) -> Result</name> <fsummary>Perform N ^ P mod M</fsummary> <type> diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index d6e2e033c0..153c7833a9 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -46,6 +46,7 @@ -export([rsa_private_encrypt/3, rsa_public_decrypt/3]). -export([dh_generate_key/1, dh_generate_key/2, dh_compute_key/3]). -export([rand_bytes/1, rand_bytes/3, rand_uniform/2]). +-export([strong_rand_bytes/1, strong_rand_uniform/3]). -export([mod_exp/3, mpint/1, erlint/1]). %% -export([idea_cbc_encrypt/3, idea_cbc_decrypt/3]). -export([aes_cbc_128_encrypt/3, aes_cbc_128_decrypt/3]). @@ -68,6 +69,8 @@ des_ede3_cbc_encrypt, des_ede3_cbc_decrypt, aes_cfb_128_encrypt, aes_cfb_128_decrypt, rand_bytes, + strong_rand_bytes, + strong_rand_uniform, rand_uniform, mod_exp, dss_verify,dss_sign, @@ -361,11 +364,23 @@ aes_cfb_128_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub. %% RAND - pseudo random numbers using RN_ functions in crypto lib %% -spec rand_bytes(non_neg_integer()) -> binary(). +-spec strong_rand_bytes(non_neg_integer()) -> binary(). -spec rand_uniform(crypto_integer(), crypto_integer()) -> crypto_integer(). +-spec strong_rand_uniform(Bits::non_neg_integer(), + Top::-1..1, + Bottom::0..1) -> binary(). rand_bytes(_Bytes) -> ?nif_stub. +strong_rand_bytes(Bytes) -> + case strong_rand_bytes_nif(Bytes) of + false -> erlang:error(low_entropy); + Bin -> Bin + end. +strong_rand_bytes_nif(_Bytes) -> ?nif_stub. + rand_bytes(_Bytes, _Topmask, _Bottommask) -> ?nif_stub. +strong_rand_uniform(_Bytes, _Topmask, _Bottommask) -> ?nif_stub. rand_uniform(From,To) when is_binary(From), is_binary(To) -> case rand_uniform_nif(From,To) of diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index fe8f8e69a0..1946d01489 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -46,6 +46,7 @@ aes_ctr/1, mod_exp_test/1, rand_uniform_test/1, + strong_rand_uniform_test/1, rsa_verify_test/1, dsa_verify_test/1, rsa_sign_test/1, @@ -68,7 +69,8 @@ all() -> md5_mac_io, sha, sha_update, %% sha256, sha256_update, sha512,sha512_update, des_cbc, aes_cfb, aes_cbc, - aes_cbc_iter, aes_ctr, des_cbc_iter, des_ecb, rand_uniform_test, + aes_cbc_iter, aes_ctr, des_cbc_iter, des_ecb, + rand_uniform_test, strong_rand_uniform_test, rsa_verify_test, dsa_verify_test, rsa_sign_test, dsa_sign_test, rsa_encrypt_decrypt, dh, exor_test, rc4_test, rc4_stream_test, mod_exp_test, blowfish_cfb64, @@ -710,6 +712,33 @@ rand_uniform_aux_test(N) -> %% %% +strong_rand_uniform_test(doc) -> + "strong_rand_uniform and strong_random_bytes testing"; +strong_rand_uniform_test(suite) -> + []; +strong_rand_uniform_test(Config) when is_list(Config) -> + strong_rand_uniform_aux_test(180), + ?line 10 = byte_size(crypto:strong_rand_bytes(10)). + +strong_rand_uniform_aux_test(0) -> + ?line t(crypto:strong_rand_uniform(0,0,0) =:= <<0,0,0,0>>), + ok; +strong_rand_uniform_aux_test(1) -> + ?line t(crypto:erlint(crypto:strong_rand_uniform(1,0,1)) =:= 1), + ?line rand_uniform_aux_test(0); +strong_rand_uniform_aux_test(N) -> + ?line t(sru_length(crypto:strong_rand_uniform(N,-1,0)) =< N), + ?line t(sru_length(crypto:strong_rand_uniform(N,0,0)) =:= N), + ?line t(crypto:erlint(crypto:strong_rand_uniform(N,0,1)) band 1 =:= 1), + ?line t(crypto:erlint(crypto:strong_rand_uniform(N,1,0)) bsr (N - 2) =:= 2#11), + ?line rand_uniform_aux_test(N-1). + +sru_length(Mpint) -> + I = crypto:erlint(Mpint), + length(erlang:integer_to_list(I, 2)). + +%% +%% %% %% rsa_verify_test(doc) -> |