use {c, chacha, constant_time, error};
pub struct SigningContext {
state: State,
}
impl SigningContext {
#[inline]
pub fn from_key(key: Key) -> SigningContext {
let mut ctx = SigningContext {
state: [0u8; STATE_LEN],
};
unsafe { GFp_poly1305_init(&mut ctx.state, &key.bytes); }
ctx
}
#[inline]
pub fn update(&mut self, input: &[u8]) {
unsafe {
GFp_poly1305_update(&mut self.state, input.as_ptr(), input.len());
}
}
#[inline]
pub fn sign(mut self, tag_out: &mut Tag) {
unsafe { GFp_poly1305_finish(&mut self.state, tag_out); }
}
}
pub fn verify(key: Key, msg: &[u8], tag: &Tag)
-> Result<(), error::Unspecified> {
let mut calculated_tag = [0u8; TAG_LEN];
sign(key, msg, &mut calculated_tag);
constant_time::verify_slices_are_equal(&calculated_tag[..], tag)
}
pub fn sign(key: Key, msg: &[u8], tag: &mut Tag) {
let mut ctx = SigningContext::from_key(key);
ctx.update(msg);
ctx.sign(tag)
}
pub struct Key {
bytes: KeyBytes,
}
impl Key {
pub fn derive_using_chacha(chacha20_key: &chacha::Key,
counter: &chacha::Counter) -> Key {
let mut bytes = [0u8; KEY_LEN];
chacha::chacha20_xor_in_place(chacha20_key, counter, &mut bytes);
Key { bytes: bytes }
}
#[cfg(test)]
pub fn from_test_vector(bytes: &[u8; KEY_LEN]) -> Key {
Key { bytes: *bytes }
}
}
type KeyBytes = [u8; KEY_LEN];
pub const KEY_LEN: usize = 32;
pub type Tag = [u8; TAG_LEN];
pub const TAG_LEN: usize = 128 / 8;
type State = [u8; STATE_LEN];
const STATE_LEN: usize = 256;
extern {
fn GFp_poly1305_init(state: &mut State, key: &KeyBytes);
fn GFp_poly1305_finish(state: &mut State, mac: &mut Tag);
fn GFp_poly1305_update(state: &State, in_: *const u8, in_len: c::size_t);
}
#[cfg(test)]
mod tests {
use {c, error, test};
use core;
use super::*;
#[test]
pub fn test_poly1305_state_len() {
assert_eq!((super::STATE_LEN + 255) / 256,
(unsafe { GFp_POLY1305_STATE_LEN } + 255) / 256);
}
#[test]
pub fn test_poly1305() {
test::from_file("src/poly1305_test.txt", |section, test_case| {
assert_eq!(section, "");
let key = test_case.consume_bytes("Key");
let key = slice_as_array_ref!(&key, KEY_LEN).unwrap();
let input = test_case.consume_bytes("Input");
let expected_mac = test_case.consume_bytes("MAC");
let expected_mac =
slice_as_array_ref!(&expected_mac, TAG_LEN).unwrap();
{
let key = Key::from_test_vector(&key);
let mut ctx = SigningContext::from_key(key);
ctx.update(&input);
let mut actual_mac = [0; TAG_LEN];
ctx.sign(&mut actual_mac);
assert_eq!(&expected_mac[..], &actual_mac[..]);
}
{
let key = Key::from_test_vector(&key);
let mut actual_mac = [0; TAG_LEN];
sign(key, &input, &mut actual_mac);
assert_eq!(&expected_mac[..], &actual_mac[..]);
}
{
let key = Key::from_test_vector(&key);
assert_eq!(Ok(()), verify(key, &input, &expected_mac));
}
{
let key = Key::from_test_vector(&key);
let mut ctx = SigningContext::from_key(key);
for chunk in input.chunks(1) {
ctx.update(chunk);
}
let mut actual_mac = [0u8; TAG_LEN];
ctx.sign(&mut actual_mac);
assert_eq!(&expected_mac[..], &actual_mac[..]);
}
try!(test_poly1305_simd(0, key, &input, expected_mac));
try!(test_poly1305_simd(16, key, &input, expected_mac));
try!(test_poly1305_simd(32, key, &input, expected_mac));
try!(test_poly1305_simd(48, key, &input, expected_mac));
Ok(())
})
}
fn test_poly1305_simd(excess: usize, key: &[u8; KEY_LEN], input: &[u8],
expected_mac: &[u8; TAG_LEN])
-> Result<(), error::Unspecified> {
let key = Key::from_test_vector(&key);
let mut ctx = SigningContext::from_key(key);
let init = core::cmp::min(input.len(), 16);
ctx.update(&input[..init]);
let long_chunk_len = 128 + excess;
for chunk in input[init..].chunks(long_chunk_len + excess) {
if chunk.len() > long_chunk_len {
let (long, short) = chunk.split_at(long_chunk_len);
ctx.update(long);
ctx.update(short);
} else {
ctx.update(chunk);
}
}
let mut actual_mac = [0u8; TAG_LEN];
ctx.sign(&mut actual_mac);
assert_eq!(&expected_mac[..], &actual_mac);
Ok(())
}
extern {
static GFp_POLY1305_STATE_LEN: c::size_t;
}
}