Article WX7V CodeSOD: Elliptical Curveball

CodeSOD: Elliptical Curveball

by
Phil Pennock
from The Daily WTF on (#WX7V)

Why is it that you hear people saying, "don't roll your own crypto"? It can't be that bad, right? I mean, if the code gives the correct outputs when given the correct inputs?

Everything in cryptography depends upon "high quality" random numbers, and lots of them. People get into semi-informed flamewars about what "entropy" means, government agencies sneak backdoors into algorithms, performance matters, secrecy matters, and unpredictability matters. The standard which defines four randomness generators is NIST Special Publication 800-90. One of the four raised suspicions because it (Dual_EC_DRBG) was three times slower than any of the others.

Joe" well, Joe sends us his own code, which "fixed" this. He made one of the others slower. Using MySQL stored procedures. Just bear in mind, with the below, that it's still cleaner, more comprehensible, and generally saner than OpenSSL.

---- prng_nistctr_sp-- Implements the NIST SP 800-90 CTR_DRBG cryptographic random number generator standard-- with MySQL AES_ENCRYPT() as the block cipher (256 bit seed, 128 bit output)--DELIMITER //DROP FUNCTION prng.make_128bit_block//CREATE DEFINER = 'prng'@'localhost'FUNCTION prng.make_128bit_block (p_h BIGINT UNSIGNED, p_l BIGINT UNSIGNED)RETURNS TEXTLANGUAGE SQLCONTAINS SQLDETERMINISTICSQL SECURITY INVOKERBEGIN RETURN UNHEX( CONCAT(LPAD(HEX(p_h),16,'0'),LPAD(HEX(p_l),16,'0')) );END;//DROP PROCEDURE prng.inc_128bit_value//CREATE DEFINER = 'prng'@'localhost'PROCEDURE prng.inc_128bit_value (INOUT p_h BIGINT UNSIGNED, INOUT p_l BIGINT UNSIGNED)LANGUAGE SQLCONTAINS SQLDETERMINISTICSQL SECURITY INVOKERBEGIN IF p_l < 18446744073709551615 THEN SET p_l = p_l + 1; ELSE SET p_l = 0; IF p_h < 18446744073709551615 THEN SET p_h = p_h + 1; ELSE SET p_h = 0; END IF; END IF;END;//DROP PROCEDURE prng.nistctr_block_encrypt//CREATE DEFINER = 'prng'@'localhost'PROCEDURE prng.nistctr_block_encrypt (p_vh BIGINT UNSIGNED, p_vl BIGINT UNSIGNED, p_kh BIGINT UNSIGNED, p_kl BIGINT UNSIGNED, OUT o_h BIGINT UNSIGNED, OUT o_l BIGINT UNSIGNED)LANGUAGE SQLCONTAINS SQLDETERMINISTICSQL SECURITY INVOKERBEGIN DECLARE hexblock TEXT; SET hexblock = HEX( AES_ENCRYPT(prng.make_128bit_block(p_vh, p_vl), prng.make_128bit_block(p_kh, p_kl)) ); SET o_h = CONV( SUBSTR(hexblock, 1, 16), 16, 10); SET o_l = CONV( SUBSTR(hexblock, 17, 16), 16, 10);END;//DROP PROCEDURE prng.nistctr_update//CREATE DEFINER = 'prng'@'localhost'PROCEDURE prng.nistctr_update (INOUT p_vh BIGINT UNSIGNED, INOUT p_vl BIGINT UNSIGNED, INOUT p_kh BIGINT UNSIGNED, INOUT p_kl BIGINT UNSIGNED, IN p_dah BIGINT UNSIGNED, IN p_dal BIGINT UNSIGNED, IN p_dbh BIGINT UNSIGNED, IN p_dbl BIGINT UNSIGNED)LANGUAGE SQLCONTAINS SQLNOT DETERMINISTICSQL SECURITY INVOKERBEGIN DECLARE tah,tal,tbh,tbl BIGINT UNSIGNED DEFAULT 0; CALL prng.inc_128bit_value(p_vh, p_vl); CALL prng.nistctr_block_encrypt(p_vh, p_vl, p_kh, p_kl, tah, tal); CALL prng.inc_128bit_value(p_vh, p_vl); CALL prng.nistctr_block_encrypt(p_vh, p_vl, p_kh, p_kl, tbh, tbl); SET tah = tah ^ p_dah; SET tal = tal ^ p_dal; SET tbh = tbh ^ p_dbh; SET tbl = tbl ^ p_dbl; SET p_kh = tah; SET p_kl = tal; SET p_vh = tbh; SET p_vl = tbl;END;//DROP PROCEDURE prng.nistctr_instantiate//CREATE DEFINER = 'prng'@'localhost'PROCEDURE prng.nistctr_instantiate (INOUT p_vh BIGINT UNSIGNED, INOUT p_vl BIGINT UNSIGNED, INOUT p_kh BIGINT UNSIGNED, INOUT p_kl BIGINT UNSIGNED, IN p_dah BIGINT UNSIGNED, IN p_dal BIGINT UNSIGNED, IN p_dbh BIGINT UNSIGNED, IN p_dbl BIGINT UNSIGNED)LANGUAGE SQLMODIFIES SQL DATANOT DETERMINISTICSQL SECURITY INVOKERBEGIN SET p_kh = 0; SET p_kl = 0; SET p_vh = 0; SET p_vl = 0; CALL prng.nistctr_update(p_vh, p_vl, p_kh, p_kl, p_dah, p_dal, p_dbh, p_dbl);END;//DROP PROCEDURE prng.nistctr_reseed//CREATE DEFINER = 'prng'@'localhost'PROCEDURE prng.nistctr_reseed (INOUT p_vh BIGINT UNSIGNED, INOUT p_vl BIGINT UNSIGNED, INOUT p_kh BIGINT UNSIGNED, INOUT p_kl BIGINT UNSIGNED, IN p_dah BIGINT UNSIGNED, IN p_dal BIGINT UNSIGNED, IN p_dbh BIGINT UNSIGNED, IN p_dbl BIGINT UNSIGNED)LANGUAGE SQLMODIFIES SQL DATANOT DETERMINISTICSQL SECURITY INVOKERBEGIN CALL prng.nistctr_update(p_vh, p_vl, p_kh, p_kl, p_dah, p_dal, p_dbh, p_dbl);END;//DROP PROCEDURE prng.nistctr_generate//CREATE DEFINER = 'prng'@'localhost'PROCEDURE prng.nistctr_generate (INOUT p_vh BIGINT UNSIGNED, INOUT p_vl BIGINT UNSIGNED, INOUT p_kh BIGINT UNSIGNED, INOUT p_kl BIGINT UNSIGNED, OUT o_h BIGINT UNSIGNED, OUT o_l BIGINT UNSIGNED, IN p_dah BIGINT UNSIGNED, IN p_dal BIGINT UNSIGNED, IN p_dbh BIGINT UNSIGNED, IN p_dbl BIGINT UNSIGNED)LANGUAGE SQLMODIFIES SQL DATANOT DETERMINISTICSQL SECURITY INVOKERBEGIN SET o_h = NULL; SET o_l = NULL; IF p_dal IS NULL THEN SET p_dal = 0; END IF; IF p_dbh IS NULL THEN SET p_dbh = 0; END IF; IF p_dbl IS NULL THEN SET p_dbl = 0; END IF; IF p_dah IS NULL THEN SET p_dah = 0; ELSE CALL prng.nistctr_update(p_vh, p_vl, p_kh, p_kl, p_dah, p_dal, p_dbh, p_dbl); END IF; CALL prng.inc_128bit_value(p_vh, p_vl); CALL prng.nistctr_block_encrypt(p_vh, p_vl, p_kh, p_kl, o_h, o_l); CALL prng.nistctr_update(p_vh, p_vl, p_kh, p_kl, p_dah, p_dal, p_dbh, p_dbl);END;//DROP FUNCTION arosystem.hex_prng_generate_block//CREATE DEFINER = 'prng'@'localhost'FUNCTION arosystem.hex_prng_generate_block ()RETURNS TEXTMODIFIES SQL DATANOT DETERMINISTICSQL SECURITY DEFINERBEGIN DECLARE nvh,nvl,nkh,nkl BIGINT UNSIGNED DEFAULT 0; DECLARE oh,ol BIGINT UNSIGNED DEFAULT NULL; SELECT vh,vl,kh,kl INTO nvh,nvl,nkh,nkl FROM prng.prng_nistctr_state WHERE usable = TRUE LIMIT 1; CALL prng.nistctr_generate(nvh,nvl,nkh,nkl,oh,ol,NULL,NULL,NULL,NULL); UPDATE prng.prng_nistctr_state SET vh=nvh,vl=nvl,kh=nkh,kl=nkl, reseed_counter = reseed_counter + 1; RETURN HEX( prng.make_128bit_block(oh,ol) );END;//DROP PROCEDURE prng.prng_init_hex//CREATE DEFINER = 'prng'@'localhost'PROCEDURE prng.prng_init_hex (hexseed TEXT)MODIFIES SQL DATANOT DETERMINISTICSQL SECURITY INVOKERBEGIN DECLARE nvh,nvl,nkh,nkl BIGINT UNSIGNED DEFAULT 0; DECLARE dah,dal,dbh,dbl BIGINT UNSIGNED DEFAULT 0; SET hexseed = LPAD(hexseed, 64, '0'); SET dah = CONV( SUBSTR(hexseed, 1, 16), 16, 10); SET dal = CONV( SUBSTR(hexseed, 17, 16), 16, 10); SET dbh = CONV( SUBSTR(hexseed, 33, 16), 16, 10); SET dbl = CONV( SUBSTR(hexseed, 49, 16), 16, 10); CALL prng.nistctr_instantiate(nvh,nvl,nkh,nkl,dah,dal,dbh,dbl); UPDATE prng.prng_nistctr_state SET vh=nvh,vl=nvl,kh=nkh,kl=nkl, usable = TRUE, reseed_counter = 1;END;//

Joe at least imbued a certain something into the dance of bouncing in and out of hex representations; safely passing around the strings, and splitting the 128-bit values into 64-bit chunks, then recombining later. Even the modulo arithmetic, implemented by comparison to a hard-coded representation of 2^64-1, before either adding 1 or setting it to 0.

Yet, in the razzle-dazzle of the dance, certain teensy things may have slipped from Joe's attention. Is that seed the required minimum size? What happens in the presence of concurrency? Oops- looks like two concurrent callerts to hex_prng_generate_block can get the same entropy, making the same updates to the state. Better hope this isn't being used for keying material to protect against eavesdropping, or an attacker can get the same keys by simply talking at the same time. And how is this initialized for the very first time?

Just because it looks like it works, returning good values, in good conditions, isn't enough for crypto. Enjoy Joe's "masterpiece". It might wake you up better than that cup of coffee as you follow along, but please, don't try this at home.

inedo50.png[Advertisement] Use NuGet or npm? Check out ProGet, the easy-to-use package repository that lets you host and manage your own personal or enterprise-wide NuGet feeds and npm repositories. It's got an impressively-featured free edition, too! TheDailyWtf?d=yIl2AUoC8zAf3Ty5UbFNr4
External Content
Source RSS or Atom Feed
Feed Location http://syndication.thedailywtf.com/TheDailyWtf
Feed Title The Daily WTF
Feed Link http://thedailywtf.com/
Reply 0 comments