use crate::{EntityError, ProfileVault};
use ockam_vault::{PublicKey, Secret};
use serde::{Deserialize, Serialize};
use serde_big_array::big_array;
big_array! { BigArray; }
#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct AuthenticationProof {
#[serde(with = "BigArray")]
signature: [u8; 64],
}
impl AuthenticationProof {
pub(crate) fn signature(&self) -> &[u8; 64] {
&self.signature
}
}
impl AuthenticationProof {
pub(crate) fn new(signature: [u8; 64]) -> Self {
AuthenticationProof { signature }
}
}
pub(crate) struct Authentication {}
impl Authentication {
pub(crate) fn generate_proof<V: ProfileVault>(
channel_state: &[u8],
secret: &Secret,
vault: &mut V,
) -> ockam_core::Result<Vec<u8>> {
let signature = vault.sign(secret, channel_state)?;
let proof = AuthenticationProof::new(signature);
serde_bare::to_vec(&proof).map_err(|_| EntityError::BareError.into())
}
pub(crate) fn verify_proof<V: ProfileVault>(
channel_state: &[u8],
responder_public_key: &PublicKey,
proof: &[u8],
vault: &mut V,
) -> ockam_core::Result<bool> {
let proof: AuthenticationProof =
serde_bare::from_slice(proof).map_err(|_| EntityError::BareError)?;
vault.verify(proof.signature(), responder_public_key, channel_state)
}
}
#[cfg(test)]
mod test {
use crate::{Entity, Identity};
use ockam_core::Error;
use ockam_node::Context;
use rand::{thread_rng, RngCore};
fn test_error<S: Into<String>>(error: S) -> ockam_core::Result<()> {
Err(Error::new(0, error))
}
async fn test_auth_use_case(ctx: &Context) -> ockam_core::Result<()> {
let mut alice = Entity::create(&ctx)?;
let mut bob = Entity::create(&ctx)?;
let mut alice_chat = alice.create_profile()?;
let mut bob_chat = bob.create_profile()?;
let alice_contact = alice_chat.as_contact()?;
let bob_contact = bob_chat.as_contact()?;
if !alice_chat.verify_and_add_contact(bob_contact.clone())? {
return test_error("alice failed to add bob");
}
if !bob_chat.verify_and_add_contact(alice_contact.clone())? {
return test_error("bob failed to add alice");
}
let mut state = [0u8; 32];
let mut rng = thread_rng();
rng.fill_bytes(&mut state);
let alice_proof = alice_chat.create_auth_proof(state)?;
let bob_proof = bob_chat.create_auth_proof(state)?;
if !alice_chat.verify_auth_proof(state, bob_contact.identifier(), bob_proof)? {
return test_error("bob's proof was invalid");
}
if !bob_chat.verify_auth_proof(state, alice_contact.identifier(), alice_proof)? {
return test_error("alice's proof was invalid");
}
Ok(())
}
async fn test_key_rotation(ctx: &Context) -> ockam_core::Result<()> {
let mut alice = Entity::create(&ctx)?;
let mut bob = Entity::create(&ctx)?;
let mut alice_chat = alice.create_profile()?;
let mut bob_chat = bob.create_profile()?;
alice_chat.rotate_profile_key()?;
bob_chat.rotate_profile_key()?;
let alice_contact = alice_chat.as_contact()?;
let bob_contact = bob_chat.as_contact()?;
if !alice_chat.verify_and_add_contact(bob_contact.clone())? {
return test_error("alice failed to add bob");
}
if !bob_chat.verify_and_add_contact(alice_contact.clone())? {
return test_error("bob failed to add alice");
}
Ok(())
}
async fn test_update_contact_and_reprove(ctx: &Context) -> ockam_core::Result<()> {
let mut alice = Entity::create(&ctx)?;
let mut bob = Entity::create(&ctx)?;
let mut alice_chat = alice.create_profile()?;
let mut bob_chat = bob.create_profile()?;
let alice_contact = alice_chat.as_contact()?;
let bob_contact = bob_chat.as_contact()?;
if !alice_chat.verify_and_add_contact(bob_contact.clone())? {
return test_error("alice failed to add bob");
}
if !bob_chat.verify_and_add_contact(alice_contact.clone())? {
return test_error("bob failed to add alice");
}
let mut state = [0u8; 32];
let mut rng = thread_rng();
rng.fill_bytes(&mut state);
let alice_proof = alice_chat.create_auth_proof(state)?;
let bob_proof = bob_chat.create_auth_proof(state)?;
if !alice_chat.verify_auth_proof(state, bob_contact.identifier(), bob_proof)? {
return test_error("bob's proof was invalid");
}
if !bob_chat.verify_auth_proof(state, alice_contact.identifier(), alice_proof)? {
return test_error("alice's proof was invalid");
}
alice_chat.rotate_profile_key()?;
bob_chat.rotate_profile_key()?;
let alice_contact = alice_chat.as_contact()?;
let bob_contact = bob_chat.as_contact()?;
let bob_last_event = bob_contact.change_events().last().unwrap().clone();
if !alice_chat.verify_and_update_contact(bob_contact.identifier(), &[bob_last_event])? {
return test_error("alice failed to add bob");
}
let alice_last_event = alice_contact.change_events().last().unwrap().clone();
if !bob_chat.verify_and_update_contact(alice_contact.identifier(), &[alice_last_event])? {
return test_error("bob failed to add alice");
}
let mut state = [0u8; 32];
let mut rng = thread_rng();
rng.fill_bytes(&mut state);
let alice_proof = alice_chat.create_auth_proof(state)?;
let bob_proof = bob_chat.create_auth_proof(state)?;
if !alice_chat.verify_auth_proof(state, bob_contact.identifier(), bob_proof)? {
return test_error("bob's proof was invalid");
}
if !bob_chat.verify_auth_proof(state, alice_contact.identifier(), alice_proof)? {
return test_error("alice's proof was invalid");
}
Ok(())
}
#[test]
fn authentication_tests() {
let (mut ctx, mut exe) = ockam_node::start_node();
exe.execute(async move {
let mut results = Vec::new();
results.push(test_auth_use_case(&ctx).await);
results.push(test_key_rotation(&ctx).await);
results.push(test_update_contact_and_reprove(&ctx).await);
ctx.stop().await.unwrap();
for r in results {
match r {
Err(e) => panic!("test failure: {}", e),
_ => (),
}
}
})
.unwrap();
}
}