Skip to content

Commit

Permalink
feat: Add ser/de for HandleLogin state
Browse files Browse the repository at this point in the history
This is necessary to keep a stateless server across
the two request handlers needed to implement login.

This state should be persisted to an ephemeral key/value
store, keyed on the user identifier, and with a short TTL,
to ensure the complete handshake is completed within a
reasonable time. It should also be stored encrypted for
additional security, but this is out of the scope of this feature.

- Add Rust tests
- Reflect change in JS test
- Use OPAQUE latest RFC naming conventions in JS test
  • Loading branch information
franky47 committed Jan 31, 2023
1 parent bc6d35c commit a410378
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 32 deletions.
81 changes: 51 additions & 30 deletions js-test/test.mjs
Original file line number Diff line number Diff line change
@@ -1,57 +1,78 @@
import { Registration, Login, HandleRegistration, HandleLogin, ServerSetup } from 'opaque-wasm';
import {
HandleLogin,
HandleRegistration,
Login,
Registration,
ServerSetup,
set_panic_hook,
} from 'opaque-wasm'

const password = 'test123'
const email = '[email protected]'

async function run() {
console.log('--- STARTING ---')
set_panic_hook()

// Server configuration; this must be saved.
let server_setup = new ServerSetup()

// Save and reload the ServerSetup for demonstration
const server_setup_export = server_setup.serialize();
server_setup = ServerSetup.deserialize(server_setup_export);
const server_setup_export = server_setup.serialize()
server_setup = ServerSetup.deserialize(server_setup_export)

// User registration
const registration = new Registration()
const registration_tx = registration.start(password)
const client_registration = new Registration()
const registration_request = client_registration.start(password)

console.log('--- begin ---', registration_tx)
console.log('--- registration request ---', registration_request)

const serverRegistration = new HandleRegistration(server_setup)
const registration_response = serverRegistration.start(email, registration_tx)
console.log('-- server response --', registration_response)


const registration_final = registration.finish(password, registration_response)
console.log('-- client finish --', registration_final)

const password_file = serverRegistration.finish(registration_final)
const registration_response = serverRegistration.start(
email,
registration_request
)
console.log('-- registration response --', registration_response)

const registration_record = client_registration.finish(
password,
registration_response
)
console.log('-- registration upload --', registration_record)

const password_file = serverRegistration.finish(registration_record)
console.log('-- password_file --', password_file)
const registration_export_key = client_registration.getExportKey()

// User Login

const login = new Login()
const login_tx = login.start(password)
console.log('login_tx', login_tx)

console.log(login)

const serverLogin = new HandleLogin(server_setup)
const login_response = serverLogin.start(password_file, email, login_tx)
const client_login = new Login()
const login_request = client_login.start(password)
console.log('login_request', login_request)

const server_login1 = new HandleLogin(server_setup)
const login_response = server_login1.start(
password_file,
email,
login_request
)
console.log('login_response', login_response)
// Serialize login handler state and persist it outside of the server
// to preserve statlessness across request handlers
const login_state = server_login1.serialize()
server_login1.free()
console.log('login_state', login_state)

const login_final = login.finish(password, login_response)
const login_final = client_login.finish(password, login_response)
console.log('client login final', login_final)
console.log('client session key', client_login.getSessionKey())

console.log(login)

console.log('client session key', login.getSessionKey())

const server_finish = serverLogin.finish(login_final)
const server_login2 = HandleLogin.deserialize(login_state, server_setup)
const server_finish = server_login2.finish(login_final)
console.log('server session key', server_finish)

console.log('registration export key', registration_export_key)
console.log('login export key', client_login.getExportKey())
}

run()
run()
22 changes: 22 additions & 0 deletions src/handle_login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,26 @@ impl HandleLogin {
let result = self.state.unwrap().finish(finish).unwrap();
return Ok(result.session_key.to_vec());
}

pub fn serialize(&self) -> Result<Vec<u8>, JsValue> {
match &self.state {
Some(state) => Ok(state.serialize().to_vec()),
None => Err("Failed to serialize ServerLogin (no state available)".into()),
}
}

pub fn deserialize(
serialized_state: Vec<u8>,
setup: &ServerSetup,
) -> Result<HandleLogin, JsValue> {
let state = match opaque_ke::ServerLogin::<Default>::deserialize(&serialized_state) {
Ok(val) => val,
Err(_) => return Err("Failed to load serialized ServerLogin".into()),
};
Ok(HandleLogin {
setup: setup.clone(),
state: Some(state),
rng: OsRng,
})
}
}
68 changes: 68 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,71 @@ pub use client_registration::Registration;
pub use handle_login::HandleLogin;
pub use handle_registration::HandleRegistration;
pub use server_setup::ServerSetup;

// -----------------------------------------------------------------------------

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn complete_exchange() {
// Server configuration
let server_setup = ServerSetup::new();

// Client configuration
let username = "[email protected]";
let password = "correct horse battery staple";

// Registration
let [password_file, registration_export_key] = {
let mut client_registration = Registration::new();
let registration_request = client_registration.start(&password).unwrap();
let server_registration = HandleRegistration::new(&server_setup);
let registration_response = server_registration
.start(username.into(), registration_request)
.unwrap();
let registration_record = client_registration
.finish(&password, registration_response.clone())
.unwrap();
let password_file = server_registration
.finish(registration_record.clone())
.unwrap();
assert_eq!(password_file, registration_record);
let export_key = client_registration.get_export_key().unwrap();
assert_ne!(export_key, registration_response);
[password_file, export_key]
};

// Login
let login_export_key = {
let mut client_login = Login::new();
let login_request = client_login.start(&password).unwrap();

// Client -> Server - First request handler
let mut server_login1 = HandleLogin::new(&server_setup);
let login_response = server_login1
.start(Some(password_file), username.into(), login_request)
.unwrap();
let serialized_state = server_login1.serialize().unwrap();
// Client <- Server - end of first request handler

let login_record = client_login.finish(&password, login_response).unwrap();
let export_key = client_login.get_export_key().unwrap();
let client_session_key = client_login.get_session_key().unwrap();
assert_ne!(export_key, client_session_key);

// Client -> Server - Second request handler
let server_login2 = HandleLogin::deserialize(serialized_state, &server_setup).unwrap();
let server_session_key = server_login2.finish(login_record).unwrap();
assert_eq!(client_session_key, server_session_key);

export_key
};

assert_eq!(
registration_export_key, login_export_key,
"Export keys differ"
);
}
}
19 changes: 17 additions & 2 deletions src/server_setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use wasm_bindgen::prelude::*;
use rand::rngs::OsRng;

#[wasm_bindgen]
#[derive(Clone)]
#[derive(Clone, PartialEq, Debug)]
pub struct ServerSetup {
internal: opaque_ke::ServerSetup<Default>
}
Expand Down Expand Up @@ -38,4 +38,19 @@ impl ServerSetup {
pub(crate) fn internal<'a>(&'a self) -> &'a opaque_ke::ServerSetup<Default> {
&self.internal
}
}
}

// --

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn server_setup_serde() {
let setup1 = ServerSetup::new();
let serialized = setup1.serialize();
let setup2 = ServerSetup::deserialize(serialized).unwrap();
assert_eq!(setup1, setup2);
}
}

0 comments on commit a410378

Please sign in to comment.