Initial Commit with Primitives Identity and Transcript
This commit is contained in:
commit
ed506c3f4a
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavaScriptSettings">
|
||||||
|
<option name="languageLevel" value="ES6" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/tapir-rs.iml" filepath="$PROJECT_DIR$/.idea/tapir-rs.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "tapir-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Sarah Jamie Lewis <sarah@openprivacy.ca>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
torut = "0.1.6"
|
||||||
|
rand = "0.7.3"
|
||||||
|
curve25519-dalek = "3.0.0"
|
||||||
|
x25519-dalek = "1.1"
|
||||||
|
ed25519-dalek = "1.0.1"
|
||||||
|
merlin = "2.0.0"
|
||||||
|
hex = "0.4.2"
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Tapir-rs: Tiny Anonymous Peer (in Rust)
|
||||||
|
|
||||||
|
A prototype for the inevitable transition of Cwtch and other Open Privacy projects from Golang to Rust!
|
||||||
|
|
||||||
|
Very WIP:
|
||||||
|
|
||||||
|
## Primitives
|
||||||
|
|
||||||
|
* Identity - Done!
|
||||||
|
* Transcript - Done!
|
||||||
|
|
||||||
|
## Applications
|
||||||
|
* TranscriptApp
|
||||||
|
* AuthenticationApp
|
||||||
|
|
||||||
|
## Networks
|
||||||
|
* Base Onion Service
|
|
@ -0,0 +1,10 @@
|
||||||
|
use crate::primitives::transcript::Transcript;
|
||||||
|
|
||||||
|
pub mod transcript_app;
|
||||||
|
|
||||||
|
pub trait Application {
|
||||||
|
fn new_instance(&self) -> Box<dyn Application>;
|
||||||
|
fn init(&mut self);
|
||||||
|
fn transcript(&mut self) -> Option<&mut Transcript>;
|
||||||
|
fn propagate_transcript(&mut self, transcript: &Transcript);
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
use crate::primitives::transcript::Transcript;
|
||||||
|
use crate::applications::Application;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TranscriptApp {
|
||||||
|
transcript: Option<Transcript>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Application for TranscriptApp {
|
||||||
|
fn new_instance(&self) -> Box<dyn Application> {
|
||||||
|
Box::new(TranscriptApp {
|
||||||
|
transcript: None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self) {
|
||||||
|
self.transcript = Some(Transcript::new_transcript("primitives-transcript".as_ref()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transcript(&mut self) -> Option<&mut Transcript> {
|
||||||
|
return self.transcript.as_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn propagate_transcript(&mut self, transcript: &Transcript) {
|
||||||
|
match self.transcript {
|
||||||
|
None => self.transcript = Some(transcript.clone()),
|
||||||
|
_ => panic!("this is a bug...we should find a nicer rust way of doing this")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transcript_app() {
|
||||||
|
//let mut transcript_app = TranscriptApp::NewInstance();
|
||||||
|
// transcript_app.Init();
|
||||||
|
// transcript_app.Transcript();
|
||||||
|
//println!("Transcript App Initial Commit: {:x}", transcript_app.Transcript().challenge_bytes())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#![feature(array_methods)]
|
||||||
|
|
||||||
|
pub mod applications;
|
||||||
|
pub mod networks;
|
||||||
|
pub mod primitives;
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
assert_eq!(2 + 2, 4);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod tor;
|
|
@ -0,0 +1,31 @@
|
||||||
|
use std::net::TcpStream;
|
||||||
|
use std::io::Error;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TorProcess {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TorProcess {
|
||||||
|
pub fn connect() -> Result<TorProcess, Error> {
|
||||||
|
match TcpStream::connect(&format!("127.0.0.1:{}", 9051)) {
|
||||||
|
Ok(_conn) => {
|
||||||
|
Ok(TorProcess{})
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
return Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::networks::tor::TorProcess;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn connect_tor() {
|
||||||
|
println!("{:?}", TorProcess::connect());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
use ed25519_dalek::{Keypair, PublicKey, ExpandedSecretKey};
|
||||||
|
use x25519_dalek::{StaticSecret, SharedSecret};
|
||||||
|
use x25519_dalek::PublicKey as X25519PublicKey ;
|
||||||
|
use rand::rngs::OsRng;
|
||||||
|
use std::intrinsics::transmute;
|
||||||
|
|
||||||
|
/// Identity - An ed25519 keypair, required for established a Tor v3 onion service and used to
|
||||||
|
/// maintain a consistent cryptographic identity for a peer.
|
||||||
|
pub struct Identity {
|
||||||
|
keypair: Keypair
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Identity {
|
||||||
|
|
||||||
|
/// Initialize a persistent identity
|
||||||
|
pub fn initialize(keypair: Keypair) ->Identity {
|
||||||
|
Identity {
|
||||||
|
keypair
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize an ephemeral identity - used for both ephemeral diffie hellman key exchanges
|
||||||
|
/// in addition to anonymous primitives connections to various onion services.
|
||||||
|
pub fn initialize_ephemeral_identity() -> Identity {
|
||||||
|
let mut csprng = OsRng{};
|
||||||
|
let keypair = ed25519_dalek::Keypair::generate(&mut csprng);
|
||||||
|
Identity {
|
||||||
|
keypair
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a diffie hellman exchange between `self` and the `remote_pub_key`
|
||||||
|
/// Implementation: this method converts the secret key of `self` into a `StaticSecret`
|
||||||
|
/// and the `remote_pub_key` into a `x25519_dalek::PublicKey` such that they can be used
|
||||||
|
/// as part of the exchange.
|
||||||
|
/// This is used as part of the ephemeral diffie hellman 3-way exchange.
|
||||||
|
pub fn edh(&self, remote_pub_key: PublicKey) -> SharedSecret {
|
||||||
|
let esk = ExpandedSecretKey::from(&self.keypair.secret).to_bytes();
|
||||||
|
let hsk : *const [u8;32] = unsafe{transmute(esk.as_ptr())};
|
||||||
|
let private_x25519 = StaticSecret::from(unsafe{**&hsk});
|
||||||
|
let public_x25519: X25519PublicKey = Identity::to_x25519(remote_pub_key);
|
||||||
|
return private_x25519.diffie_hellman(&public_x25519);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// helper function that converts an Edwards PublicKey into a Montgomery PublicKey
|
||||||
|
/// i.e. ed25519 to x25519
|
||||||
|
fn to_x25519(edwards_pub_key: PublicKey) -> X25519PublicKey {
|
||||||
|
let edwards = curve25519_dalek::edwards::CompressedEdwardsY::from_slice(edwards_pub_key.to_bytes().as_slice());
|
||||||
|
X25519PublicKey::from(edwards.decompress().unwrap().to_montgomery().to_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::primitives::identity::Identity;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_identity() {
|
||||||
|
let alice = Identity::initialize_ephemeral_identity();
|
||||||
|
let bob = Identity::initialize_ephemeral_identity();
|
||||||
|
|
||||||
|
assert_eq!(alice.edh(bob.keypair.public).to_bytes(), bob.edh(alice.keypair.public).to_bytes());
|
||||||
|
println!("Alice Shared Secret: {:?}", alice.edh(bob.keypair.public).to_bytes());
|
||||||
|
println!("Bob Shared Secret: {:?}", bob.edh(alice.keypair.public).to_bytes());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod identity;
|
||||||
|
pub mod transcript;
|
|
@ -0,0 +1,63 @@
|
||||||
|
/// Transcript is a wrapper around Merlin Transcripts to provide some structure for
|
||||||
|
/// more complex protocol-driven applications as seen in Tapir
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Transcript {
|
||||||
|
mtranscript: merlin::Transcript,
|
||||||
|
transcript: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transcript {
|
||||||
|
pub fn new_transcript(label: &'static str) -> Transcript{
|
||||||
|
Transcript {
|
||||||
|
mtranscript: merlin::Transcript::new(label.as_ref()),
|
||||||
|
transcript: String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_to_transcript(&mut self, label: &'static str, b :&[u8]) {
|
||||||
|
let op = format!("{} ({}) {};", label, b.len(), hex::encode(b));
|
||||||
|
self.transcript = format!("{}\n{}", self.transcript, op);
|
||||||
|
self.mtranscript.append_message(label.as_ref(), b);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn output_transcript_to_audit(&self) -> String {
|
||||||
|
self.transcript.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_protocol(&mut self, label: &str) {
|
||||||
|
let op = format!("---- new-protocol: {} ----", label);
|
||||||
|
self.transcript = format!("{}\n{}", self.transcript, op);
|
||||||
|
self.mtranscript.append_message("protocol".as_ref(), label.as_ref());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn commit_to_transcript(&mut self, label: &'static str) -> [u8;64] {
|
||||||
|
let mut result = [0u8;64];
|
||||||
|
self.mtranscript.challenge_bytes(label.as_ref(),result.as_mut_slice());
|
||||||
|
self.transcript = format!("{}\nextract {}: {}", self.transcript, label, hex::encode(result));
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::primitives::transcript::Transcript;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transcript() {
|
||||||
|
let mut t = Transcript::new_transcript("label");
|
||||||
|
t.add_to_transcript("action","test data".as_bytes());
|
||||||
|
if t.output_transcript_to_audit() != t.output_transcript_to_audit() {
|
||||||
|
panic!("Multiple Audit Calls should not impact underlying Transcript")
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{}",t.output_transcript_to_audit());
|
||||||
|
println!("{:?}", t.commit_to_transcript("first commit"));
|
||||||
|
println!("{}",t.output_transcript_to_audit());
|
||||||
|
println!("{:?}", t.commit_to_transcript("second commit"));
|
||||||
|
t.add_to_transcript("action","test data".as_bytes());
|
||||||
|
|
||||||
|
// Test vector from golang Tapir tests...
|
||||||
|
let test_vector :[u8;64] = [239,5,86,166,139,70,247,187,137,37,36,183,53,51,114,115,24,103,110,65,82,190,88,106,4,114,91,194,90,114,107,19,3,33,103,187,122,54,156,213,203,40,132,26,203,130,117,55,161,245,30,102,127,151,233,148,116,212,235,186,46,59,1,8];
|
||||||
|
assert_eq!(test_vector, t.commit_to_transcript("third commit"))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue