Initial Commit with Primitives Identity and Transcript

This commit is contained in:
Sarah Jamie Lewis 2020-11-19 23:48:56 -08:00
commit ed506c3f4a
16 changed files with 305 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
Cargo.lock

8
.idea/.gitignore vendored Normal file
View File

@ -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/

6
.idea/misc.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>

8
.idea/modules.xml Normal file
View File

@ -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>

11
.idea/tapir-rs.iml Normal file
View File

@ -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>

6
.idea/vcs.xml Normal file
View File

@ -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>

16
Cargo.toml Normal file
View File

@ -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"

17
README.md Normal file
View File

@ -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

10
src/applications/mod.rs Normal file
View File

@ -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);
}

View File

@ -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())
}
}

14
src/lib.rs Normal file
View File

@ -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);
}
}

1
src/networks/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod tor;

31
src/networks/tor/mod.rs Normal file
View File

@ -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());
}
}

View File

@ -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());
}
}

2
src/primitives/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod identity;
pub mod transcript;

View File

@ -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"))
}
}