From cd4e72dc98a19cda905c8c40c97b6491e4747b49 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Fri, 20 Apr 2012 09:47:46 -0700 Subject: [PATCH 1/3] Modernize crypto with new rust style and rustdoc support --- hash.rs | 35 +++++------------- pkey.rs | 109 ++++++++++++++++++-------------------------------------- symm.rs | 57 +++++++++-------------------- 3 files changed, 58 insertions(+), 143 deletions(-) diff --git a/hash.rs b/hash.rs index 4ba64e0b..a68347c7 100644 --- a/hash.rs +++ b/hash.rs @@ -1,40 +1,23 @@ -use std; - -import core::ptr; -import core::str; -import core::vec; - import libc::c_uint; export hasher; export hashtype; -export mk_hasher; export hash; export _native; export md5, sha1, sha224, sha256, sha384, sha512; iface hasher { - /* - Method: init - - Initializes this hasher - */ + #[doc = "Initializes this hasher"] fn init(); - /* - Method: update - - Update this hasher with more input bytes - */ + #[doc = "Update this hasher with more input bytes"] fn update([u8]); - /* - Method: final - + #[doc = " Return the digest of all bytes added to this hasher since its last initialization - */ + "] fn final() -> [u8]; } @@ -78,7 +61,7 @@ fn evpmd(t: hashtype) -> (EVP_MD, uint) { } } -fn mk_hasher(ht: hashtype) -> hasher { +fn hasher(ht: hashtype) -> hasher { type hasherstate = { evp: EVP_MD, ctx: EVP_MD_CTX, @@ -111,13 +94,11 @@ fn mk_hasher(ht: hashtype) -> hasher { ret h; } -/* -Function: hash - +#[doc = " Hashes the supplied input data using hash t, returning the resulting hash value -*/ +"] fn hash(t: hashtype, data: [u8]) -> [u8] unsafe { - let h = mk_hasher(t); + let h = hasher(t); h.init(); h.update(data); ret h.final(); diff --git a/pkey.rs b/pkey.rs index 24ed4826..040e4aa4 100644 --- a/pkey.rs +++ b/pkey.rs @@ -1,12 +1,7 @@ -import core::ptr; -import core::str; -import core::unsafe; -import core::vec; - import libc::{c_int, c_uint}; export pkeyrole, encrypt, decrypt, sign, verify; -export pkey, mk_pkey; +export pkey; export _native; type EVP_PKEY = *libc::c_void; @@ -45,11 +40,7 @@ enum pkeyparts { both } -/* -Tag: pkeyrole - -Represents a role an asymmetric key might be appropriate for. -*/ +#[doc = "Represents a role an asymmetric key might be appropriate for."] enum pkeyrole { encrypt, decrypt, @@ -57,100 +48,68 @@ enum pkeyrole { verify } -/* -Object: pkey - -Represents a public key, optionally with a private key attached. -*/ +#[doc = "Represents a public key, optionally with a private key attached."] iface pkey { - /* - Method: save_pub - + #[doc = " Returns a serialized form of the public key, suitable for load_pub(). - */ + "] fn save_pub() -> [u8]; - /* - Method: load_pub - + #[doc = " Loads a serialized form of the public key, as produced by save_pub(). - */ + "] fn load_pub(s: [u8]); - /* - Method: save_priv - + #[doc = " Returns a serialized form of the public and private keys, suitable for load_priv(). - */ + "] fn save_priv() -> [u8]; - /* - Method: load_priv - + #[doc = " Loads a serialized form of the public and private keys, as produced by save_priv(). - */ + "] fn load_priv(s: [u8]); - /* - Method: size() - - Returns the size of the public key modulus. - */ + #[doc = "Returns the size of the public key modulus."] fn size() -> uint; - /* - Method: gen() - - Generates a public/private keypair of the specified size. - */ + #[doc = "Generates a public/private keypair of the specified size."] fn gen(keysz: uint); - /* - Method: can() - + #[doc = " Returns whether this pkey object can perform the specified role. - */ + "] fn can(role: pkeyrole) -> bool; - /* - Method: max_data() - + #[doc = " Returns the maximum amount of data that can be encrypted by an encrypt() call. - */ + "] fn max_data() -> uint; - /* - Method: encrypt() - + #[doc = " Encrypts data using OAEP padding, returning the encrypted data. The supplied data must not be larger than max_data(). - */ + "] fn encrypt(s: [u8]) -> [u8]; - /* - Method: decrypt() - + #[doc = " Decrypts data, expecting OAEP padding, returning the decrypted data. - */ + "] fn decrypt(s: [u8]) -> [u8]; - /* - Method: sign() - + #[doc = " Signs data, using OpenSSL's default scheme and sha256. Unlike encrypt(), can process an arbitrary amount of data; returns the signature. - */ + "] fn sign(s: [u8]) -> [u8]; - /* - Method: verify() - + #[doc = " Verifies a signature s (using OpenSSL's default scheme and sha256) on a message m. Returns true if the signature is valid, and false otherwise. - */ + "] fn verify(m: [u8], s: [u8]) -> bool; } @@ -162,7 +121,7 @@ fn any_to_rsa(anykey: *ANYKEY) -> *RSA unsafe { unsafe::reinterpret_cast::<*ANYKEY, *RSA>(anykey) } -fn mk_pkey() -> pkey { +fn pkey() -> pkey { type pkeystate = { mut evp: *EVP_PKEY, mut parts: pkeyparts @@ -302,8 +261,8 @@ fn mk_pkey() -> pkey { mod tests { #[test] fn test_gen_pub() { - let k0 = mk_pkey(); - let k1 = mk_pkey(); + let k0 = pkey(); + let k1 = pkey(); k0.gen(512u); k1.load_pub(k0.save_pub()); assert(k0.save_pub() == k1.save_pub()); @@ -320,8 +279,8 @@ mod tests { #[test] fn test_gen_priv() { - let k0 = mk_pkey(); - let k1 = mk_pkey(); + let k0 = pkey(); + let k1 = pkey(); k0.gen(512u); k1.load_priv(k0.save_priv()); assert(k0.save_priv() == k1.save_priv()); @@ -338,8 +297,8 @@ mod tests { #[test] fn test_encrypt() { - let k0 = mk_pkey(); - let k1 = mk_pkey(); + let k0 = pkey(); + let k1 = pkey(); let msg: [u8] = [0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; k0.gen(512u); k1.load_pub(k0.save_pub()); @@ -350,8 +309,8 @@ mod tests { #[test] fn test_sign() { - let k0 = mk_pkey(); - let k1 = mk_pkey(); + let k0 = pkey(); + let k1 = pkey(); let msg: [u8] = [0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; k0.gen(512u); k1.load_pub(k0.save_pub()); diff --git a/symm.rs b/symm.rs index 1e0c6cd1..819c8d1f 100644 --- a/symm.rs +++ b/symm.rs @@ -1,9 +1,3 @@ -use std; - -import core::ptr; -import core::str; -import core::vec; - import libc::c_int; export crypter; @@ -11,7 +5,6 @@ export cryptermode; export encryptmode, decryptmode; export cryptertype; export aes_256_ecb, aes_256_cbc; -export mk_crypter; export encrypt, decrypt; export _native; @@ -38,40 +31,26 @@ native mod _native { fn EVP_CipherFinal(ctx: EVP_CIPHER_CTX, res: *u8, len: *u32); } -/* -Object: crypter - -Represents a symmetric cipher context. -*/ +#[doc = "Represents a symmetric cipher context."] iface crypter { - /* - Method: pad - + #[doc = " Enables or disables padding. If padding is disabled, total amount of data encrypted must be a multiple of block size. - */ + "] fn pad(padding: bool); - /* - Method: init - - Initializes this crypter. - */ + #[doc = "Initializes this crypter."] fn init(mode: cryptermode, key: [u8], iv: [u8]); - /* - Method: update - + #[doc = " Update this crypter with more data to encrypt or decrypt. Returns encrypted or decrypted bytes. - */ + "] fn update(data: [u8]) -> [u8]; - /* - Method: final - + #[doc = " Finish crypting. Returns the remaining partial block of output, if any. - */ + "] fn final() -> [u8]; } @@ -92,7 +71,7 @@ fn evpc(t: cryptertype) -> (EVP_CIPHER, uint, uint) { } } -fn mk_crypter(t: cryptertype) -> crypter { +fn crypter(t: cryptertype) -> crypter { type crypterstate = { evp: EVP_CIPHER, ctx: EVP_CIPHER_CTX, @@ -142,28 +121,24 @@ fn mk_crypter(t: cryptertype) -> crypter { ret h; } -/* -Function: encrypt - +#[doc = " Encrypts data, using the specified crypter type in encrypt mode with the specified key and iv; returns the resulting (encrypted) data. -*/ +"] fn encrypt(t: cryptertype, key: [u8], iv: [u8], data: [u8]) -> [u8] { - let c = mk_crypter(t); + let c = crypter(t); c.init(encryptmode, key, iv); let r = c.update(data); let rest = c.final(); ret r + rest; } -/* -Function: decrypt - +#[doc = " Decrypts data, using the specified crypter type in decrypt mode with the specified key and iv; returns the resulting (decrypted) data. -*/ +"] fn decrypt(t: cryptertype, key: [u8], iv: [u8], data: [u8]) -> [u8] { - let c = mk_crypter(t); + let c = crypter(t); c.init(decryptmode, key, iv); let r = c.update(data); let rest = c.final(); @@ -187,7 +162,7 @@ mod tests { let c0 = [ 0x8eu8, 0xa2u8, 0xb7u8, 0xcau8, 0x51u8, 0x67u8, 0x45u8, 0xbfu8, 0xeau8, 0xfcu8, 0x49u8, 0x90u8, 0x4bu8, 0x49u8, 0x60u8, 0x89u8 ]; - let c = mk_crypter(aes_256_ecb); + let c = crypter(aes_256_ecb); c.init(encryptmode, k0, []); c.pad(false); let r0 = c.update(p0) + c.final(); From 6794c17af6815e0c2cd0b88414b1922d4ba04b51 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Fri, 20 Apr 2012 10:04:23 -0700 Subject: [PATCH 2/3] Add support for PKCS5_PBKDF2_HMAC_SHA1. --- crypto.rc | 1 + pkcs5.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 pkcs5.rs diff --git a/crypto.rc b/crypto.rc index b91cb372..cdd25fb8 100644 --- a/crypto.rc +++ b/crypto.rc @@ -24,3 +24,4 @@ use std; // FIXME https://github.com/mozilla/rust/issues/1127 mod hash; mod pkey; mod symm; +mod pkcs5; diff --git a/pkcs5.rs b/pkcs5.rs new file mode 100644 index 00000000..468fe6b0 --- /dev/null +++ b/pkcs5.rs @@ -0,0 +1,89 @@ +import libc::{c_char, c_uchar, c_int}; + +#[link_name = "crypto"] +#[abi = "cdecl"] +native mod _native { + fn PKCS5_PBKDF2_HMAC_SHA1(pass: *c_char, passlen: c_int, + salt: *c_uchar, saltlen: c_int, + iter: c_int, keylen: c_int, + out: *c_uchar) -> c_int; +} + +#[doc = " +Derives a key from a password and salt using the PBKDF2-HMAC-SHA1 algorithm. +"] +fn pbkdf2_hmac_sha1(pass: str, salt: [u8], iter: uint, keylen: uint) -> [u8] { + assert iter >= 1u; + assert keylen >= 1u; + + str::as_c_str(pass) { |pass_buf| + vec::as_buf(salt) { |salt_buf| + let mut out = []; + vec::reserve(out, keylen); + + vec::as_buf(out) { |out_buf| + let r = _native::PKCS5_PBKDF2_HMAC_SHA1( + pass_buf, str::len(pass) as c_int, + salt_buf, vec::len(salt) as c_int, + iter as c_int, keylen as c_int, + out_buf); + + if r != 1 as c_int { fail; } + + unsafe { vec::unsafe::set_len(out, keylen); } + out + } + } + } +} + +#[cfg(test)] +mod tests { + // Test vectors from + // http://tools.ietf.org/html/draft-josefsson-pbkdf2-test-vectors-06 + #[test] + fn test_pbkdf2_hmac_sha1() { + assert pbkdf2_hmac_sha1("password", str::bytes("salt"), 1u, 20u) == [ + 0x0c_u8, 0x60_u8, 0xc8_u8, 0x0f_u8, 0x96_u8, 0x1f_u8, 0x0e_u8, + 0x71_u8, 0xf3_u8, 0xa9_u8, 0xb5_u8, 0x24_u8, 0xaf_u8, 0x60_u8, + 0x12_u8, 0x06_u8, 0x2f_u8, 0xe0_u8, 0x37_u8, 0xa6_u8 + ]; + + assert pbkdf2_hmac_sha1("password", str::bytes("salt"), 2u, 20u) == [ + 0xea_u8, 0x6c_u8, 0x01_u8, 0x4d_u8, 0xc7_u8, 0x2d_u8, 0x6f_u8, + 0x8c_u8, 0xcd_u8, 0x1e_u8, 0xd9_u8, 0x2a_u8, 0xce_u8, 0x1d_u8, + 0x41_u8, 0xf0_u8, 0xd8_u8, 0xde_u8, 0x89_u8, 0x57_u8 + ]; + + assert pbkdf2_hmac_sha1("password", str::bytes("salt"), 4096u, + 20u) == [ + 0x4b_u8, 0x00_u8, 0x79_u8, 0x01_u8, 0xb7_u8, 0x65_u8, 0x48_u8, + 0x9a_u8, 0xbe_u8, 0xad_u8, 0x49_u8, 0xd9_u8, 0x26_u8, 0xf7_u8, + 0x21_u8, 0xd0_u8, 0x65_u8, 0xa4_u8, 0x29_u8, 0xc1_u8 + ]; + + assert pbkdf2_hmac_sha1("password", str::bytes("salt"), 16777216u, + 20u) == [ + 0xee_u8, 0xfe_u8, 0x3d_u8, 0x61_u8, 0xcd_u8, 0x4d_u8, 0xa4_u8, + 0xe4_u8, 0xe9_u8, 0x94_u8, 0x5b_u8, 0x3d_u8, 0x6b_u8, 0xa2_u8, + 0x15_u8, 0x8c_u8, 0x26_u8, 0x34_u8, 0xe9_u8, 0x84_u8 + ]; + + assert pbkdf2_hmac_sha1( + "passwordPASSWORDpassword", + str::bytes("saltSALTsaltSALTsaltSALTsaltSALTsalt"), + 4096u, 25u) == [ + 0x3d_u8, 0x2e_u8, 0xec_u8, 0x4f_u8, 0xe4_u8, 0x1c_u8, 0x84_u8, + 0x9b_u8, 0x80_u8, 0xc8_u8, 0xd8_u8, 0x36_u8, 0x62_u8, 0xc0_u8, + 0xe4_u8, 0x4a_u8, 0x8b_u8, 0x29_u8, 0x1a_u8, 0x96_u8, 0x4c_u8, + 0xf2_u8, 0xf0_u8, 0x70_u8, 0x38_u8 + ]; + + assert pbkdf2_hmac_sha1("pass\x00word", str::bytes("sa\x00lt"), 4096u, + 16u) == [ + 0x56_u8, 0xfa_u8, 0x6a_u8, 0xa7_u8, 0x55_u8, 0x48_u8, 0x09_u8, + 0x9d_u8, 0xcc_u8, 0x37_u8, 0xd7_u8, 0xf0_u8, 0x34_u8, 0x25_u8, + 0xe0_u8, 0xc3_u8 + ]; + } +} From b2f3316758ebbf7da78417675f6b208a5386040f Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sat, 21 Apr 2012 20:03:06 -0700 Subject: [PATCH 3/3] Expose RAND_bytes --- crypto.rc | 1 + rand.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 rand.rs diff --git a/crypto.rc b/crypto.rc index cdd25fb8..4b0d6306 100644 --- a/crypto.rc +++ b/crypto.rc @@ -25,3 +25,4 @@ mod hash; mod pkey; mod symm; mod pkcs5; +mod rand; diff --git a/rand.rs b/rand.rs new file mode 100644 index 00000000..7335b51a --- /dev/null +++ b/rand.rs @@ -0,0 +1,28 @@ +import libc::{c_uchar, c_int}; + +#[link_name = "crypto"] +#[abi = "cdecl"] +native mod _native { + fn RAND_bytes(buf: *c_uchar, num: c_int) -> c_int; +} + +fn rand_bytes(len: uint) -> [u8] { + let mut out = []; + vec::reserve(out, len); + + vec::as_buf(out) { |out_buf| + let r = _native::RAND_bytes(out_buf, len as c_int); + if r != 1 as c_int { fail } + + unsafe { vec::unsafe::set_len(out, len); } + out + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_rand_bytes() { + let _bytes = rand_bytes(5u); + } +}