commit 62c421d5f66fe161b3c6b5b44968f01bf2459829 Author: Elly Jones Date: Thu Dec 15 09:46:13 2011 -0500 Initial commit. diff --git a/README.md b/README.md new file mode 100644 index 00000000..774949f7 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +This package provides Rust bindings for the functionality exposed by OpenSSL's +libcrypto. Currently provided: + +* Hashes (hash.rs) + * MD5 + * SHA-1 + * SHA-2 (224, 256, 384, 512) +* Symmetric crypto (symm.rs) + * AES in ECB or CBC mode, all key lengths +* Keypair generation (pkey.rs) + * RSA, all key lengths +* Asymmetric encryption (pkey.rs) + * RSA with PKCS#1 OAEP padding +* Digital signatures (pkey.rs) + * RSA with whatever your system openssl does (PKCS#1 on my system) and sha256 + +Each module provides two interfaces: a low-level API which wraps the OpenSSL +interfaces as directly as possible and a high-level API which presents the +OpenSSL API as a Rust object and tries to make sensible default choices about +parameters most users won't care about. You probably want to use the high-level +API. For documentation on these, see the individual source files. diff --git a/crypto.rc b/crypto.rc new file mode 100644 index 00000000..cafab3de --- /dev/null +++ b/crypto.rc @@ -0,0 +1,24 @@ +/* + * Copyright 2011 Google Inc. + * + * 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. + */ + +#[link(name = "crypto", + vers = "0.1", + uuid = "38297409-b4c2-4499-8131-a99a7e44dad3")]; +#[crate_type = "lib"]; + +mod hash; +mod pkey; +mod symm; diff --git a/hash.rs b/hash.rs new file mode 100644 index 00000000..5f85c700 --- /dev/null +++ b/hash.rs @@ -0,0 +1,169 @@ +use std; + +import std::ptr; +import std::str; +import std::vec; + +export hasher; +export hashtype; +export mk_hasher; +export hash; +export _native; + +export md5, sha1, sha224, sha256, sha384, sha512; + +type hasher = obj { + /* + Method: init + + Initializes this hasher + */ + fn init(); + + /* + Method: update + + Update this hasher with more input bytes + */ + fn update([u8]); + + /* + Method: final + + Return the digest of all bytes added to this hasher since its last + initialization + */ + fn final() -> [u8]; +}; + +tag hashtype { + md5; + sha1; + sha224; + sha256; + sha384; + sha512; +} + +#[link_name = "crypto"] +#[abi = "cdecl"] +native mod _native { + type EVP_MD_CTX; + type EVP_MD; + + fn EVP_MD_CTX_create() -> EVP_MD_CTX; + + fn EVP_md5() -> EVP_MD; + fn EVP_sha1() -> EVP_MD; + fn EVP_sha224() -> EVP_MD; + fn EVP_sha256() -> EVP_MD; + fn EVP_sha384() -> EVP_MD; + fn EVP_sha512() -> EVP_MD; + + fn EVP_DigestInit(ctx: EVP_MD_CTX, typ: EVP_MD); + fn EVP_DigestUpdate(ctx: EVP_MD_CTX, data: *u8, n: uint); + fn EVP_DigestFinal(ctx: EVP_MD_CTX, res: *u8, n: *u32); +} + +fn evpmd(t: hashtype) -> (_native::EVP_MD, uint) { + alt t { + md5. { (_native::EVP_md5(), 16u) } + sha1. { (_native::EVP_sha1(), 20u) } + sha224. { (_native::EVP_sha224(), 28u) } + sha256. { (_native::EVP_sha256(), 32u) } + sha384. { (_native::EVP_sha384(), 48u) } + sha512. { (_native::EVP_sha512(), 64u) } + } +} + +fn mk_hasher(ht: hashtype) -> hasher { + type hasherstate = { + evp: _native::EVP_MD, + ctx: _native::EVP_MD_CTX, + len: uint + }; + + obj hasher(st: hasherstate) { + fn init() unsafe { + _native::EVP_DigestInit(st.ctx, st.evp); + } + + fn update(data: [u8]) unsafe { + let pdata: *u8 = vec::unsafe::to_ptr::(data); + _native::EVP_DigestUpdate(st.ctx, pdata, vec::len(data)); + } + + fn final() -> [u8] unsafe { + let res: [mutable u8] = vec::init_elt_mut::(0u8, st.len); + let pres: *u8 = vec::unsafe::to_ptr::(res); + _native::EVP_DigestFinal(st.ctx, pres, ptr::null::()); + vec::from_mut::(res) + } + } + + let ctx = _native::EVP_MD_CTX_create(); + let (evp, mdlen) = evpmd(ht); + let st = { evp: evp, ctx: ctx, len: mdlen }; + let h = hasher(st); + h.init(); + ret h; +} + +/* +Function: hash + +Hashes the supplied input data using hash t, returning the resulting hash value +*/ +fn hash(t: hashtype, data: [u8]) -> [u8] unsafe { + let ctx = _native::EVP_MD_CTX_create(); + let (evp, mdlen) = evpmd(t); + let res: [mutable u8] = vec::init_elt_mut::(0u8, mdlen); + let pres: *u8 = vec::unsafe::to_ptr::(res); + let pdata: *u8 = vec::unsafe::to_ptr::(data); + _native::EVP_DigestInit(ctx, evp); + _native::EVP_DigestUpdate(ctx, pdata, vec::len(data)); + _native::EVP_DigestFinal(ctx, pres, ptr::null::()); + ret vec::from_mut::(res); +} + +#[cfg(test)] +mod tests { + // Test vectors from http://www.nsrl.nist.gov/testdata/ + #[test] + fn test_md5() { + let s0 = [0x61u8, 0x62u8, 0x63u8]; + let d0 = + [0x90u8, 0x01u8, 0x50u8, 0x98u8, 0x3cu8, 0xd2u8, 0x4fu8, 0xb0u8, + 0xd6u8, 0x96u8, 0x3fu8, 0x7du8, 0x28u8, 0xe1u8, 0x7fu8, 0x72u8]; + assert(hash(md5, s0) == d0); + } + + #[test] + fn test_sha1() { + let s0 = [0x61u8, 0x62u8, 0x63u8]; + let d0 = + [0xa9u8, 0x99u8, 0x3eu8, 0x36u8, 0x47u8, 0x06u8, 0x81u8, 0x6au8, + 0xbau8, 0x3eu8, 0x25u8, 0x71u8, 0x78u8, 0x50u8, 0xc2u8, 0x6cu8, + 0x9cu8, 0xd0u8, 0xd8u8, 0x9du8]; + assert(hash(sha1, s0) == d0); + } + + #[test] + fn test_sha256() { + let s0 = [0x61u8, 0x62u8, 0x63u8]; + let d0 = + [0xbau8, 0x78u8, 0x16u8, 0xbfu8, 0x8fu8, 0x01u8, 0xcfu8, 0xeau8, + 0x41u8, 0x41u8, 0x40u8, 0xdeu8, 0x5du8, 0xaeu8, 0x22u8, 0x23u8, + 0xb0u8, 0x03u8, 0x61u8, 0xa3u8, 0x96u8, 0x17u8, 0x7au8, 0x9cu8, + 0xb4u8, 0x10u8, 0xffu8, 0x61u8, 0xf2u8, 0x00u8, 0x15u8, 0xadu8]; + assert(hash(sha256, s0) == d0); + } +} + +fn main() { + let h = mk_hasher(sha512); + h.init(); + h.update(str::bytes("")); + log h.final(); + log hash(sha512, str::bytes("")); +} diff --git a/pkey.rs b/pkey.rs new file mode 100644 index 00000000..08d0fcd7 --- /dev/null +++ b/pkey.rs @@ -0,0 +1,351 @@ +use std; + +import std::ptr; +import std::str; +import std::unsafe; +import std::vec; + +export pkeyrole, encrypt, decrypt, sign, verify; +export pkey, mk_pkey; +export _native; + +#[link_name = "crypto"] +#[abi = "cdecl"] +native mod _native { + type EVP_PKEY; + type ANYKEY; + type RSA; + + fn EVP_PKEY_new() -> *EVP_PKEY; + fn EVP_PKEY_free(k: *EVP_PKEY); + fn EVP_PKEY_assign(k: *EVP_PKEY, t: int, inner: *ANYKEY); + fn EVP_PKEY_get0(k: *EVP_PKEY) -> *ANYKEY; + + fn i2d_PublicKey(k: *EVP_PKEY, buf: **u8) -> int; + fn d2i_PublicKey(t: int, k: **EVP_PKEY, buf: **u8, len: uint) -> *EVP_PKEY; + fn i2d_PrivateKey(k: *EVP_PKEY, buf: **u8) -> int; + fn d2i_PrivateKey(t: int, k: **EVP_PKEY, buf: **u8, len: uint) -> *EVP_PKEY; + + fn RSA_generate_key(modsz: uint, e: uint, cb: *u8, cbarg: *u8) -> *RSA; + fn RSA_size(k: *RSA) -> uint; + + fn RSA_public_encrypt(flen: uint, from: *u8, to: *u8, k: *RSA, pad: int) -> int; + fn RSA_private_decrypt(flen: uint, from: *u8, to: *u8, k: *RSA, pad: int) -> int; + fn RSA_sign(t: int, m: *u8, mlen: uint, sig: *u8, siglen: *uint, k: *RSA) -> int; + fn RSA_verify(t: int, m: *u8, mlen: uint, sig: *u8, siglen: uint, k: *RSA) -> int; +} + +tag pkeyparts { + neither; + public; + both; +} + +/* +Tag: pkeyrole + +Represents a role an asymmetric key might be appropriate for. +*/ +tag pkeyrole { + encrypt; + decrypt; + sign; + verify; +} + +/* +Object: pkey + +Represents a public key, optionally with a private key attached. +*/ +type pkey = obj { + /* + Method: save_pub + + Returns a serialized form of the public key, suitable for load_pub(). + */ + fn save_pub() -> [u8]; + + /* + Method: load_pub + + Loads a serialized form of the public key, as produced by save_pub(). + */ + fn load_pub(s: [u8]); + + /* + Method: save_priv + + Returns a serialized form of the public and private keys, suitable for + load_priv(). + */ + fn save_priv() -> [u8]; + + /* + Method: load_priv + + 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. + */ + fn size() -> uint; + + /* + Method: gen() + + Generates a public/private keypair of the specified size. + */ + fn gen(keysz: uint); + + /* + Method: can() + + Returns whether this pkey object can perform the specified role. + */ + fn can(role: pkeyrole) -> bool; + + /* + Method: max_data() + + Returns the maximum amount of data that can be encrypted by an encrypt() + call. + */ + fn max_data() -> uint; + + /* + Method: encrypt() + + 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() + + Decrypts data, expecting OAEP padding, returning the decrypted data. + */ + fn decrypt(s: [u8]) -> [u8]; + + /* + Method: sign() + + 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() + + 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; +}; + +fn rsa_to_any(rsa: *_native::RSA) -> *_native::ANYKEY unsafe { + unsafe::reinterpret_cast::<*_native::RSA, *_native::ANYKEY>(rsa) +} + +fn any_to_rsa(anykey: *_native::ANYKEY) -> *_native::RSA unsafe { + unsafe::reinterpret_cast::<*_native::ANYKEY, *_native::RSA>(anykey) +} + +fn mk_pkey() -> pkey { + type pkeystate = { + mutable evp: *_native::EVP_PKEY, + mutable parts: pkeyparts + }; + + fn _tostr(st: pkeystate, + f: fn@(*_native::EVP_PKEY, **u8) -> int) -> [u8] unsafe { + let len = f(st.evp, ptr::null()); + if len < 0 { ret []; } + let s: [mutable u8] = vec::init_elt_mut::(0u8, len as uint); + let ps: *u8 = vec::unsafe::to_ptr::(s); + let pps: **u8 = ptr::addr_of(ps); + let r = f(st.evp, pps); + let bytes = vec::slice::(s, 0u, r as uint); + ret bytes; + } + + fn _fromstr(st: pkeystate, + f: fn@(int, **_native::EVP_PKEY, **u8, uint) -> *_native::EVP_PKEY, + s: [u8]) unsafe { + let ps: *u8 = vec::unsafe::to_ptr::(s); + let pps: **u8 = ptr::addr_of(ps); + let evp: *_native::EVP_PKEY = ptr::null(); + let pevp: **_native::EVP_PKEY = ptr::addr_of(evp); + f(6, pevp, pps, vec::len(s)); + st.evp = *pevp; + } + + obj pkey(st: pkeystate) { + fn gen(keysz: uint) unsafe { + let rsa = _native::RSA_generate_key(keysz, 65537u, ptr::null(), ptr::null()); + let rsa_ = rsa_to_any(rsa); + // XXX: 6 == NID_rsaEncryption + _native::EVP_PKEY_assign(st.evp, 6, rsa_); + st.parts = both; + } + + fn save_pub() -> [u8] { + // FIXME: https://github.com/graydon/rust/issues/1281 + let f = bind _native::i2d_PublicKey(_, _); + _tostr(st, f) + } + fn load_pub(s: [u8]) { + // FIXME: https://github.com/graydon/rust/issues/1281 + let f = bind _native::d2i_PublicKey(_, _, _, _); + _fromstr(st, f, s); + st.parts = public; + } + fn save_priv() -> [u8] { + // FIXME: https://github.com/graydon/rust/issues/1281 + let f = bind _native::i2d_PrivateKey(_, _); + _tostr(st, f) + } + fn load_priv(s: [u8]) { + // FIXME: https://github.com/graydon/rust/issues/1281 + let f = bind _native::d2i_PrivateKey(_, _, _, _); + _fromstr(st, f, s); + st.parts = both; + } + fn size() -> uint { + _native::RSA_size(any_to_rsa(_native::EVP_PKEY_get0(st.evp))) + } + fn can(r: pkeyrole) -> bool { + alt r { + encrypt. { st.parts != neither } + verify. { st.parts != neither } + decrypt. { st.parts == both } + sign. { st.parts == both } + } + } + fn max_data() -> uint unsafe { + let rsa = any_to_rsa(_native::EVP_PKEY_get0(st.evp)); + let len = _native::RSA_size(rsa); + // 41 comes from RSA_public_encrypt(3) for OAEP + ret len - 41u; + } + fn encrypt(s: [u8]) -> [u8] unsafe { + let rsa = any_to_rsa(_native::EVP_PKEY_get0(st.evp)); + let len = _native::RSA_size(rsa); + // 41 comes from RSA_public_encrypt(3) for OAEP + assert(vec::len(s) < _native::RSA_size(rsa) - 41u); + let r: [mutable u8] = vec::init_elt_mut::(0u8, len + 1u); + let pr: *u8 = vec::unsafe::to_ptr::(r); + let ps: *u8 = vec::unsafe::to_ptr::(s); + // XXX: 4 == RSA_PKCS1_OAEP_PADDING + let rv = _native::RSA_public_encrypt(vec::len(s), ps, pr, rsa, 4); + if rv < 0 { ret []; } + ret vec::slice::(r, 0u, rv as uint); + } + fn decrypt(s: [u8]) -> [u8] unsafe { + let rsa = any_to_rsa(_native::EVP_PKEY_get0(st.evp)); + let len = _native::RSA_size(rsa); + assert(vec::len(s) == _native::RSA_size(rsa)); + let r: [mutable u8] = vec::init_elt_mut::(0u8, len + 1u); + let pr: *u8 = vec::unsafe::to_ptr::(r); + let ps: *u8 = vec::unsafe::to_ptr::(s); + // XXX: 4 == RSA_PKCS1_OAEP_PADDING + let rv = _native::RSA_private_decrypt(vec::len(s), ps, pr, rsa, 4); + if rv < 0 { ret []; } + ret vec::slice::(r, 0u, rv as uint); + } + fn sign(s: [u8]) -> [u8] unsafe { + let rsa = any_to_rsa(_native::EVP_PKEY_get0(st.evp)); + let len = _native::RSA_size(rsa); + let r: [mutable u8] = vec::init_elt_mut::(0u8, len + 1u); + let pr: *u8 = vec::unsafe::to_ptr::(r); + let ps: *u8 = vec::unsafe::to_ptr::(s); + let plen: *uint = ptr::addr_of(len); + // XXX: 672 == NID_sha256 + let rv = _native::RSA_sign(672, ps, vec::len(s), pr, plen, rsa); + if rv < 0 { ret []; } + ret vec::slice::(r, 0u, *plen as uint); + } + fn verify(m: [u8], s: [u8]) -> bool unsafe { + let rsa = any_to_rsa(_native::EVP_PKEY_get0(st.evp)); + let pm: *u8 = vec::unsafe::to_ptr::(m); + let ps: *u8 = vec::unsafe::to_ptr::(s); + // XXX: 672 == NID_sha256 + let rv = _native::RSA_verify(672, pm, vec::len(m), ps, vec::len(s), rsa); + ret rv == 1; + } + } + + let st = { mutable evp: _native::EVP_PKEY_new(), mutable parts: neither }; + let p = pkey(st); + ret p; +} + +#[cfg(test)] +mod tests { + #[test] + fn test_gen_pub() { + let k0 = mk_pkey(); + let k1 = mk_pkey(); + k0.gen(512u); + k1.load_pub(k0.save_pub()); + assert(k0.save_pub() == k1.save_pub()); + assert(k0.size() == k1.size()); + assert(k0.can(encrypt)); + assert(k0.can(decrypt)); + assert(k0.can(verify)); + assert(k0.can(sign)); + assert(k1.can(encrypt)); + assert(!k1.can(decrypt)); + assert(k1.can(verify)); + assert(!k1.can(sign)); + } + + #[test] + fn test_gen_priv() { + let k0 = mk_pkey(); + let k1 = mk_pkey(); + k0.gen(512u); + k1.load_priv(k0.save_priv()); + assert(k0.save_priv() == k1.save_priv()); + assert(k0.size() == k1.size()); + assert(k0.can(encrypt)); + assert(k0.can(decrypt)); + assert(k0.can(verify)); + assert(k0.can(sign)); + assert(k1.can(encrypt)); + assert(k1.can(decrypt)); + assert(k1.can(verify)); + assert(k1.can(sign)); + } + + #[test] + fn test_encrypt() { + let k0 = mk_pkey(); + let k1 = mk_pkey(); + let msg: [u8] = [0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; + k0.gen(512u); + k1.load_pub(k0.save_pub()); + let emsg = k1.encrypt(msg); + let dmsg = k0.decrypt(emsg); + assert(msg == dmsg); + } + + #[test] + fn test_sign() { + let k0 = mk_pkey(); + let k1 = mk_pkey(); + let msg: [u8] = [0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; + k0.gen(512u); + k1.load_pub(k0.save_pub()); + let sig = k0.sign(msg); + let rv = k1.verify(msg, sig); + assert(rv == true); + } +} diff --git a/symm.rs b/symm.rs new file mode 100644 index 00000000..8d286897 --- /dev/null +++ b/symm.rs @@ -0,0 +1,198 @@ +use std; + +import std::ptr; +import std::str; +import std::vec; + +export crypter; +export cryptermode; +export encryptmode, decryptmode; +export cryptertype; +export aes_256_ecb, aes_256_cbc; +export mk_crypter; +export encrypt, decrypt; +export _native; + +#[link_name = "crypto"] +#[abi = "cdecl"] +native mod _native { + type EVP_CIPHER_CTX; + type EVP_CIPHER; + + fn EVP_CIPHER_CTX_new() -> EVP_CIPHER_CTX; + fn EVP_CIPHER_CTX_set_padding(ctx: EVP_CIPHER_CTX, padding: int); + + fn EVP_aes_128_ecb() -> EVP_CIPHER; + fn EVP_aes_128_cbc() -> EVP_CIPHER; + fn EVP_aes_192_ecb() -> EVP_CIPHER; + fn EVP_aes_192_cbc() -> EVP_CIPHER; + fn EVP_aes_256_ecb() -> EVP_CIPHER; + fn EVP_aes_256_cbc() -> EVP_CIPHER; + + fn EVP_CipherInit(ctx: EVP_CIPHER_CTX, evp: EVP_CIPHER, + key: *u8, iv: *u8, mode: int); + fn EVP_CipherUpdate(ctx: EVP_CIPHER_CTX, outbuf: *u8, outlen: *u32, + inbuf: *u8, inlen: u32); + fn EVP_CipherFinal(ctx: EVP_CIPHER_CTX, res: *u8, len: *u32); +} + +/* +Object: crypter + +Represents a symmetric cipher context. +*/ +type crypter = obj { + /* + Method: pad + + 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. + */ + fn init(mode: cryptermode, key: [u8], iv: [u8]); + + /* + Method: update + + Update this crypter with more data to encrypt or decrypt. Returns encrypted + or decrypted bytes. + */ + fn update(data: [u8]) -> [u8]; + + /* + Method: final + + Finish crypting. Returns the remaining partial block of output, if any. + */ + fn final() -> [u8]; +}; + +tag cryptermode { + encryptmode; + decryptmode; +} + +tag cryptertype { + aes_256_ecb; + aes_256_cbc; +} + +fn evpc(t: cryptertype) -> (_native::EVP_CIPHER, uint, uint) { + alt t { + aes_256_ecb. { (_native::EVP_aes_256_ecb(), 32u, 16u) } + aes_256_cbc. { (_native::EVP_aes_256_cbc(), 32u, 16u) } + } +} + +fn mk_crypter(t: cryptertype) -> crypter { + type crypterstate = { + evp: _native::EVP_CIPHER, + ctx: _native::EVP_CIPHER_CTX, + keylen: uint, + blocksize: uint + }; + + obj crypter(st: crypterstate) { + fn pad(padding: bool) { + let v = padding ? 1 : 0; + _native::EVP_CIPHER_CTX_set_padding(st.ctx, v); + } + + fn init (mode: cryptermode, key: [u8], iv: [u8]) unsafe { + let m = alt mode { encryptmode. { 1 } decryptmode. { 0 } }; + assert(vec::len(key) == st.keylen); + let pkey: *u8 = vec::unsafe::to_ptr::(key); + let piv: *u8 = vec::unsafe::to_ptr::(iv); + _native::EVP_CipherInit(st.ctx, st.evp, pkey, piv, m); + } + + fn update(data: [u8]) -> [u8] unsafe { + let pdata: *u8 = vec::unsafe::to_ptr::(data); + let datalen: u32 = vec::len(data) as u32; + let reslen: u32 = datalen + (st.blocksize as u32); + let preslen: *u32 = ptr::addr_of(reslen); + let res: [mutable u8] = vec::init_elt_mut::(0u8, reslen as uint); + let pres: *u8 = vec::unsafe::to_ptr::(res); + _native::EVP_CipherUpdate(st.ctx, pres, preslen, pdata, datalen); + ret vec::slice::(res, 0u, *preslen as uint); + } + + fn final() -> [u8] unsafe { + let reslen: u32 = st.blocksize as u32; + let preslen: *u32 = ptr::addr_of(reslen); + let res: [mutable u8] = vec::init_elt_mut::(0u8, reslen as uint); + let pres: *u8 = vec::unsafe::to_ptr::(res); + _native::EVP_CipherFinal(st.ctx, pres, preslen); + ret vec::slice::(res, 0u, *preslen as uint); + } + } + + let ctx = _native::EVP_CIPHER_CTX_new(); + let (evp, keylen, blocksz) = evpc(t); + let st = { evp: evp, ctx: ctx, keylen: keylen, blocksize: blocksz }; + let h = crypter(st); + ret h; +} + +/* +Function: encrypt + +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); + c.init(encryptmode, key, iv); + let r = c.update(data); + let rest = c.final(); + ret r + rest; +} + +/* +Function: decrypt + +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); + c.init(decryptmode, key, iv); + let r = c.update(data); + let rest = c.final(); + ret r + rest; +} + +#[cfg(test)] +mod tests { + // Test vectors from FIPS-197: + // http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + #[test] + fn test_aes_256_ecb() { + let k0 = + [ 0x00u8, 0x01u8, 0x02u8, 0x03u8, 0x04u8, 0x05u8, 0x06u8, 0x07u8, + 0x08u8, 0x09u8, 0x0au8, 0x0bu8, 0x0cu8, 0x0du8, 0x0eu8, 0x0fu8, + 0x10u8, 0x11u8, 0x12u8, 0x13u8, 0x14u8, 0x15u8, 0x16u8, 0x17u8, + 0x18u8, 0x19u8, 0x1au8, 0x1bu8, 0x1cu8, 0x1du8, 0x1eu8, 0x1fu8 ]; + let p0 = + [ 0x00u8, 0x11u8, 0x22u8, 0x33u8, 0x44u8, 0x55u8, 0x66u8, 0x77u8, + 0x88u8, 0x99u8, 0xaau8, 0xbbu8, 0xccu8, 0xddu8, 0xeeu8, 0xffu8 ]; + 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); + c.init(encryptmode, k0, []); + c.pad(false); + let r0 = c.update(p0) + c.final(); + assert(r0 == c0); + c.init(decryptmode, k0, []); + c.pad(false); + let p1 = c.update(r0) + c.final(); + assert(p1 == p0); + } +}