Skip to content

Commit

Permalink
Merge pull request #54 from cisco/new-api-ish
Browse files Browse the repository at this point in the history
New API
  • Loading branch information
bifurcation committed Aug 2, 2019
2 parents 38f783d + d8749b3 commit 7d3dade
Show file tree
Hide file tree
Showing 31 changed files with 1,007 additions and 705 deletions.
12 changes: 4 additions & 8 deletions CMakeLists.txt
Expand Up @@ -4,7 +4,7 @@

cmake_minimum_required(VERSION 3.3)

project(mls)
project(mlspp)

set_property(GLOBAL PROPERTY USE_FOLDERS ON)

Expand Down Expand Up @@ -56,16 +56,12 @@ set(LIB_NAME "${PROJECT_NAME}")

set(LIBRARY_SRC_PATH "${PROJECT_SOURCE_DIR}/src" )
set(LIBRARY_INCLUDE_PATH "${PROJECT_SOURCE_DIR}/include")
set(LIBRARY_PRIVATE_INCLUDE_PATH "${LIBRARY_SRC_PATH}/include")

include_directories(${OPENSSL_INCLUDE_DIR})
include_directories(${LIBRARY_INCLUDE_PATH})
include_directories(${LIBRARY_PRIVATE_INCLUDE_PATH})

file(GLOB LIB_HEADER_FILES "${LIBRARY_INCLUDE_PATH}/*.h")
file(GLOB LIB_SOURCE_FILES "${LIBRARY_SRC_PATH}/*.cpp")

add_library(${LIB_NAME} STATIC ${LIB_SOURCE_FILES} ${LIB_HEADER_FILES})
add_library(${LIB_NAME} STATIC ${LIB_SOURCE_FILES})
target_include_directories(${LIB_NAME} PRIVATE ${OPENSSL_INCLUDE_DIR}
PRIVATE ${LIBRARY_INCLUDE_PATH})
target_link_libraries(${LIB_NAME} ${OPENSSL_LIBRARIES})

if(CLANG_TIDY_EXE)
Expand Down
8 changes: 7 additions & 1 deletion Makefile
Expand Up @@ -7,19 +7,25 @@ CLANG_FORMAT=clang-format -i -style=mozilla
TEST_VECTOR_DIR=./build/test/vectors
TEST_GEN=./build/cmd/test_gen/test_gen

all: ${BUILD_DIR} format src/* test/*
all: ${BUILD_DIR} format src/* include/** test/*
cmake --build ${BUILD_DIR}

${BUILD_DIR}: CMakeLists.txt test/CMakeLists.txt cmd/CMakeLists.txt
cmake -H. -B${BUILD_DIR} -DMLSPP_LINT=${MLSPP_LINT} -DCMAKE_BUILD_TYPE=Debug

lint: ${BUILD_DIR}
cmake -H. -B${BUILD_DIR} -DMLSPP_LINT=ON -DCMAKE_BUILD_TYPE=Debug

test: all
cd ${BUILD_DIR} && ctest

gen: all
mkdir -p ${TEST_VECTOR_DIR}
cd ${TEST_VECTOR_DIR} && ../../../${TEST_GEN}

example: all
./build/cmd/api_example/api_example

clean:
cd ${BUILD_DIR} && make clean

Expand Down
1 change: 1 addition & 0 deletions cmd/CMakeLists.txt
@@ -1 +1,2 @@
add_subdirectory(test_gen)
add_subdirectory(api_example)
11 changes: 11 additions & 0 deletions cmd/api_example/CMakeLists.txt
@@ -0,0 +1,11 @@
set(APP_NAME "api_example")
set(APP_SRC_PATH "${PROJECT_SOURCE_DIR}/cmd/api_example")

file(GLOB APP_SOURCE_FILES "${APP_SRC_PATH}/*.cpp")
set(APP_SOURCE_FILES ${APP_SOURCE_FILES} PARENT_SCOPE)

add_executable(${APP_NAME} ${APP_SOURCE_FILES})
target_include_directories(${APP_NAME} PRIVATE ${LIBRARY_INCLUDE_PATH}
PRIVATE ${OPENSSL_INCLUDE_DIR})
target_link_libraries(${APP_NAME} ${OPENSSL_LIBRARIES})
target_link_libraries(${APP_NAME} ${LIB_NAME})
153 changes: 153 additions & 0 deletions cmd/api_example/main.cpp
@@ -0,0 +1,153 @@
#include "credential.h"
#include "crypto.h"
#include "messages.h"
#include "session.h"

#include <iostream>
#include <stdexcept>
#include <string>

using namespace mls;

const auto suites =
std::vector<CipherSuite>{ CipherSuite::X25519_SHA256_AES128GCM };
const auto scheme = SignatureScheme::Ed25519;

class User
{
public:
User(const std::string& name)
{
auto priv = SignaturePrivateKey::generate(scheme);
auto id = bytes(name.begin(), name.end());
_cred = Credential::basic(id, priv);
}

ClientInitKey temp_cik()
{
auto cikID = random_bytes(16);
auto init = random_bytes(32);
return ClientInitKey{ cikID, suites, init, _cred };
}

ClientInitKey fresh_cik()
{
auto cik = temp_cik();
_ciks.emplace(cik.client_init_key_id, cik);
return cik;
}

ClientInitKey find_cik(const bytes& cik_id)
{
if (_ciks.count(cik_id) == 0) {
throw std::runtime_error("Unkown CIK");
}

return _ciks.at(cik_id);
}

private:
Credential _cred;
std::map<bytes, ClientInitKey> _ciks;
};

void
verify_send(std::string label, Session& send, Session& recv)
{
auto plaintext = bytes{ 0, 1, 2, 3 };
auto encrypted = send.protect(plaintext);
auto decrypted = recv.unprotect(encrypted);
if (plaintext != decrypted) {
throw std::runtime_error(label + ": send/receive failure");
}
}

void
verify(std::string label, Session& alice, Session& bob)
{
if (alice != bob) {
throw std::runtime_error(label + ": not equal");
}

verify_send(label, alice, bob);
verify_send(label, bob, alice);
}

int
main()
{
////////// DRAMATIS PERSONAE ///////////

auto alice = User{ "alice" };
auto bob = User{ "bob" };
auto charlie = User{ "charlie" };

////////// ACT I: CREATION ///////////

// Bob posts a ClientInitKey
auto cikB1 = bob.fresh_cik();

// Bob starts a session with alice
auto cikA = alice.temp_cik();
auto group_id = bytes{ 0, 1, 2, 3 };
auto session_welcome_add = Session::start(group_id, cikA, cikB1);
auto sessionA = std::get<0>(session_welcome_add);
auto welcome = std::get<1>(session_welcome_add);
auto add = std::get<2>(session_welcome_add);

// Alice looks up her CIK based on the welcome, and initializes
// her session
auto cikB2 = bob.find_cik(welcome.client_init_key_id);
auto sessionB = Session::join(cikB2, welcome, add);

// Alice and Bob should now be on the same page
verify("create", sessionA, sessionB);

////////// ACT II: ADDITION ///////////

// Charlie posts a ClientInitKey
auto cikC1 = charlie.fresh_cik();

// Alice adds Charlie to the session
std::tie(welcome, add) = sessionA.add(cikC1);

// Charlie initializes his session
auto cikC2 = charlie.find_cik(welcome.client_init_key_id);
auto sessionC = Session::join(cikC2, welcome, add);

// Alice and Bob updates their sessions to reflect Charlie's addition
sessionA.handle(add);
sessionB.handle(add);

verify("add A->B", sessionA, sessionB);
verify("add A->C", sessionA, sessionC);
verify("add B->C", sessionB, sessionC);

////////// ACT III: UPDATE ///////////

// Bob updates his key
auto update = sessionB.update(random_bytes(32));

// Everyone processes the update
sessionA.handle(update);
sessionB.handle(update);
sessionC.handle(update);

verify("update A->B", sessionA, sessionB);
verify("update A->C", sessionA, sessionC);
verify("update B->C", sessionB, sessionC);

////////// ACT IV: REMOVE ///////////

// Charlie removes Bob
auto remove = sessionC.remove(random_bytes(32), 1);

// Alice and Charlie process the message (Bob is gone)
sessionA.handle(remove);
sessionC.handle(remove);

verify("remove A->C", sessionA, sessionC);

std::cout << "ok" << std::endl;
return 0;
}
9 changes: 4 additions & 5 deletions cmd/test_gen/CMakeLists.txt
Expand Up @@ -4,14 +4,13 @@ set(APP_SRC_PATH "${PROJECT_SOURCE_DIR}/cmd/test_gen")
set(TEST_INCLUDE_PATH "${PROJECT_SOURCE_DIR}/test")
set(TEST_VECTORS_SRC "${TEST_INCLUDE_PATH}/test_vectors.cpp")

include_directories(${LIBRARY_INCLUDE_PATH})
include_directories(${LIBRARY_PRIVATE_INCLUDE_PATH})
include_directories(${TEST_INCLUDE_PATH})

file(GLOB APP_SOURCE_FILES "${APP_SRC_PATH}/*.cpp")
set(APP_SOURCE_FILES ${APP_SOURCE_FILES} PARENT_SCOPE)

add_executable(${APP_NAME} ${APP_SOURCE_FILES})
target_sources(${APP_NAME} PUBLIC ${TEST_VECTORS_SRC})
target_sources(${APP_NAME} PRIVATE ${TEST_VECTORS_SRC})
target_include_directories(${APP_NAME} PRIVATE ${LIBRARY_INCLUDE_PATH}
PRIVATE ${OPENSSL_INCLUDE_DIR}
PRIVATE ${TEST_INCLUDE_PATH})
target_link_libraries(${APP_NAME} ${OPENSSL_LIBRARIES})
target_link_libraries(${APP_NAME} ${LIB_NAME})
53 changes: 27 additions & 26 deletions cmd/test_gen/main.cpp
Expand Up @@ -107,7 +107,7 @@ generate_crypto()
test_case->derive_key_pair_pub = pub;

// HPKE
test::DeterministicHPKE lock;
DeterministicHPKE lock;
test_case->ecies_out = pub.encrypt(tv.ecies_plaintext);
}

Expand Down Expand Up @@ -216,7 +216,7 @@ generate_app_key_schedule()
}

TreeTestVectors::TreeCase
tree_to_case(const test::TestRatchetTree& tree)
tree_to_case(const TestRatchetTree& tree)
{
auto nodes = tree.nodes();
TreeTestVectors::TreeCase tc(nodes.size());
Expand Down Expand Up @@ -260,7 +260,7 @@ generate_tree()
auto scheme = schemes[i];
auto test_case = cases[i];

test::TestRatchetTree tree{ suite };
TestRatchetTree tree{ suite };

// Add the leaves
for (uint32_t j = 0; j < n_leaves; ++j) {
Expand Down Expand Up @@ -321,7 +321,7 @@ generate_messages()
cik_all.client_init_key_id = tv.client_init_key_id;
for (const auto& suite : suites) {
auto priv = DHPrivateKey::derive(suite, tv.dh_seed);
cik_all.add_init_key(priv.public_key());
cik_all.add_init_key(priv);
}

auto identity_priv =
Expand All @@ -332,7 +332,7 @@ generate_messages()
tv.client_init_key_all = tls::marshal(cik_all);

// Construct a test case for each suite
test::DeterministicHPKE lock;
DeterministicHPKE lock;
for (size_t i = 0; i < suites.size(); ++i) {
auto suite = suites[i];
auto scheme = schemes[i];
Expand All @@ -358,7 +358,7 @@ generate_messages()
// Construct CIK
auto client_init_key = ClientInitKey{};
client_init_key.client_init_key_id = tv.client_init_key_id;
client_init_key.add_init_key(dh_key);
client_init_key.add_init_key(dh_priv);
client_init_key.credential = cred;
client_init_key.signature = tv.random;

Expand Down Expand Up @@ -431,48 +431,49 @@ generate_basic_session()
tv.group_size = 5;
tv.group_id = bytes(16, 0xA0);

test::DeterministicHPKE lock;
DeterministicHPKE lock;
for (size_t i = 0; i < suites.size(); ++i) {
auto suite = suites[i];
auto scheme = schemes[i];
const bytes client_init_key_id = { 0, 1, 2, 3 };

std::vector<SessionTestVectors::Epoch> transcript;

// Initialize empty sessions
std::vector<test::TestSession> sessions;
std::vector<ClientInitKey> client_init_keys;
std::vector<TestSession> sessions;
std::vector<bytes> seeds;
auto ciphersuites = CipherList{ suite };
for (size_t j = 0; j < tv.group_size; ++j) {
bytes seed = { uint8_t(j), 0 };
auto identity_priv = SignaturePrivateKey::derive(scheme, seed);
auto cred = Credential::basic(seed, identity_priv);
seeds.push_back(seed);
sessions.emplace_back(ciphersuites, seed, identity_priv, cred);
}

std::vector<tls::opaque<4>> ciks;
for (const auto& session : sessions) {
ciks.push_back(session.client_init_key());
client_init_keys.emplace_back(
client_init_key_id, ciphersuites, seed, cred);
}

// Add everyone
for (size_t j = 1; j < tv.group_size; ++j) {
auto cik = sessions[j].client_init_key();

std::pair<bytes, bytes> welcome_add;
Welcome welcome;
bytes add;
if (j == 1) {
welcome_add = sessions[0].start(tv.group_id, cik);
auto session_welcome_add =
Session::start(tv.group_id, client_init_keys[0], client_init_keys[1]);
sessions.push_back(std::get<0>(session_welcome_add));
welcome = std::get<1>(session_welcome_add);
add = std::get<2>(session_welcome_add);
} else {
welcome_add = sessions[j - 1].add(cik);
std::tie(welcome, add) = sessions[j - 1].add(client_init_keys[j]);
for (size_t k = 0; k < j; ++k) {
sessions[k].handle(welcome_add.second);
sessions[k].handle(add);
}
}

sessions[j].join(welcome_add.first, welcome_add.second);
auto joiner = Session::join(client_init_keys[j], welcome, add);
sessions.push_back(joiner);

transcript.emplace_back(
welcome_add.first, welcome_add.second, sessions[0]);
transcript.emplace_back(welcome, add, sessions[0]);
}

// Update everyone (L->R)
Expand All @@ -483,7 +484,7 @@ generate_basic_session()
session.handle(update);
}

transcript.emplace_back(bytes{}, update, sessions[0]);
transcript.emplace_back(std::nullopt, update, sessions[0]);
}

// Remove everyone (R->L)
Expand All @@ -500,11 +501,11 @@ generate_basic_session()
}
}

transcript.emplace_back(bytes{}, remove, sessions[0]);
transcript.emplace_back(std::nullopt, remove, sessions[0]);
}

// Construct the test case
*cases[i] = { suite, scheme, ciks, transcript };
*cases[i] = { suite, scheme, client_init_keys, transcript };
}

return tv;
Expand Down

0 comments on commit 7d3dade

Please sign in to comment.