Merge commit '8036729' into logging_enabled
This commit is contained in:
commit
af45901f76
17 changed files with 899 additions and 94 deletions
88
Makefile
88
Makefile
|
@ -7,7 +7,7 @@ JS_OPTIMIZE_FLAGS ?= -O3
|
||||||
FUZZING_OPTIMIZE_FLAGS ?= -O3
|
FUZZING_OPTIMIZE_FLAGS ?= -O3
|
||||||
CC = gcc
|
CC = gcc
|
||||||
EMCC = emcc
|
EMCC = emcc
|
||||||
AFL_CC = afl_gcc
|
AFL_CC = afl-gcc
|
||||||
AFL_CXX = afl-g++
|
AFL_CXX = afl-g++
|
||||||
RELEASE_TARGET := $(BUILD_DIR)/libolm.so
|
RELEASE_TARGET := $(BUILD_DIR)/libolm.so
|
||||||
DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.so
|
DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.so
|
||||||
|
@ -15,20 +15,28 @@ JS_TARGET := javascript/olm.js
|
||||||
|
|
||||||
JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json
|
JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json
|
||||||
|
|
||||||
PUBLIC_HEADERS := include/olm/olm.h
|
PUBLIC_HEADERS := include/olm/olm.h include/olm/outbound_group_session.h include/olm/inbound_group_session.h
|
||||||
|
|
||||||
|
SOURCES := $(wildcard src/*.cpp) $(wildcard src/*.c) \
|
||||||
|
lib/crypto-algorithms/sha256.c \
|
||||||
|
lib/crypto-algorithms/aes.c \
|
||||||
|
lib/curve25519-donna/curve25519-donna.c
|
||||||
|
|
||||||
SOURCES := $(wildcard src/*.cpp) $(wildcard src/*.c)
|
|
||||||
RELEASE_OBJECTS := $(patsubst src/%,$(BUILD_DIR)/release/%,$(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES))))
|
|
||||||
DEBUG_OBJECTS := $(patsubst src/%,$(BUILD_DIR)/debug/%,$(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES))))
|
|
||||||
FUZZER_OBJECTS := $(patsubst src/%,$(BUILD_DIR)/fuzzers/objects/%,$(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES))))
|
|
||||||
FUZZER_SOURCES := $(wildcard fuzzers/fuzz_*.cpp) $(wildcard fuzzers/fuzz_*.c)
|
FUZZER_SOURCES := $(wildcard fuzzers/fuzz_*.cpp) $(wildcard fuzzers/fuzz_*.c)
|
||||||
FUZZER_BINARIES := $(patsubst fuzzers/%,$(BUILD_DIR)/fuzzers/%,$(patsubst %.c,%,$(patsubst %.cpp,%,$(FUZZER_SOURCES))))
|
|
||||||
FUZZER_DEBUG_BINARIES := $(patsubst $(BUILD_DIR)/fuzzers/fuzz_%,$(BUILD_DIR)/fuzzers/debug_%,$(FUZZER_BINARIES))
|
|
||||||
TEST_SOURCES := $(wildcard tests/test_*.cpp) $(wildcard tests/test_*.c)
|
TEST_SOURCES := $(wildcard tests/test_*.cpp) $(wildcard tests/test_*.c)
|
||||||
TEST_BINARIES := $(patsubst tests/%,$(BUILD_DIR)/tests/%,$(patsubst %.c,%,$(patsubst %.cpp,%,$(TEST_SOURCES))))
|
|
||||||
JS_OBJECTS := $(patsubst src/%,$(BUILD_DIR)/javascript/%,$(patsubst %.c,%.js.bc,$(patsubst %.cpp,%.js.bc,$(SOURCES))))
|
OBJECTS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES)))
|
||||||
|
RELEASE_OBJECTS := $(addprefix $(BUILD_DIR)/release/,$(OBJECTS))
|
||||||
|
DEBUG_OBJECTS := $(addprefix $(BUILD_DIR)/debug/,$(OBJECTS))
|
||||||
|
FUZZER_OBJECTS := $(addprefix $(BUILD_DIR)/fuzzers/objects/,$(OBJECTS))
|
||||||
|
FUZZER_BINARIES := $(addprefix $(BUILD_DIR)/,$(basename $(FUZZER_SOURCES)))
|
||||||
|
FUZZER_DEBUG_BINARIES := $(patsubst $(BUILD_DIR)/fuzzers/fuzz_%,$(BUILD_DIR)/fuzzers/debug_%,$(FUZZER_BINARIES))
|
||||||
|
TEST_BINARIES := $(patsubst tests/%,$(BUILD_DIR)/tests/%,$(basename $(TEST_SOURCES)))
|
||||||
|
JS_OBJECTS := $(addprefix $(BUILD_DIR)/javascript/,$(OBJECTS))
|
||||||
JS_PRE := $(wildcard javascript/*pre.js)
|
JS_PRE := $(wildcard javascript/*pre.js)
|
||||||
JS_POST := $(wildcard javascript/*post.js)
|
JS_POST := javascript/olm_outbound_group_session.js \
|
||||||
|
javascript/olm_inbound_group_session.js \
|
||||||
|
javascript/olm_post.js
|
||||||
|
|
||||||
CPPFLAGS += -Iinclude -Ilib
|
CPPFLAGS += -Iinclude -Ilib
|
||||||
# we rely on <stdint.h>, which was introduced in C99
|
# we rely on <stdint.h>, which was introduced in C99
|
||||||
|
@ -82,14 +90,6 @@ $(JS_TARGET): LDFLAGS += $(JS_OPTIMIZE_FLAGS)
|
||||||
lib: $(RELEASE_TARGET)
|
lib: $(RELEASE_TARGET)
|
||||||
.PHONY: lib
|
.PHONY: lib
|
||||||
|
|
||||||
# Make sure that the build directory exists.
|
|
||||||
# We can't check the build directory into git because it is empty.
|
|
||||||
makedirs:
|
|
||||||
mkdir -p $(BUILD_DIR)/release $(BUILD_DIR)/debug $(BUILD_DIR)/javascript\
|
|
||||||
$(BUILD_DIR)/tests $(BUILD_DIR)/fuzzers/objects
|
|
||||||
.PHONY: makedirs
|
|
||||||
|
|
||||||
|
|
||||||
$(RELEASE_TARGET): $(RELEASE_OBJECTS)
|
$(RELEASE_TARGET): $(RELEASE_OBJECTS)
|
||||||
$(CXX) $(LDFLAGS) --shared -fPIC \
|
$(CXX) $(LDFLAGS) --shared -fPIC \
|
||||||
-Wl,--version-script,version_script.ver \
|
-Wl,--version-script,version_script.ver \
|
||||||
|
@ -108,21 +108,11 @@ js: $(JS_TARGET)
|
||||||
|
|
||||||
$(JS_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS)
|
$(JS_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS)
|
||||||
$(EMCC_LINK) \
|
$(EMCC_LINK) \
|
||||||
--pre-js $(JS_PRE) --post-js $(JS_POST) \
|
$(foreach f,$(JS_PRE),--pre-js $(f)) \
|
||||||
|
$(foreach f,$(JS_POST),--post-js $(f)) \
|
||||||
-s "EXPORTED_FUNCTIONS=@$(JS_EXPORTED_FUNCTIONS)" \
|
-s "EXPORTED_FUNCTIONS=@$(JS_EXPORTED_FUNCTIONS)" \
|
||||||
$(JS_OBJECTS) -o $@
|
$(JS_OBJECTS) -o $@
|
||||||
|
|
||||||
clean:;
|
|
||||||
rm -rf $(RELEASE_OBJECTS) $(RELEASE_OBJECTS:.o=.d) \
|
|
||||||
$(DEBUG_OBJECTS) $(DEBUG_OBJECTS:.o=.d) \
|
|
||||||
$(TEST_BINARIES) $(TEST_BINARIES:=.d) \
|
|
||||||
$(JS_OBJECTS) $(JS_OBJECTS:.bc=.d) $(JS_TARGET) \
|
|
||||||
$(JS_EXPORTED_FUNCTIONS)\
|
|
||||||
$(RELEASE_TARGET) $(DEBUG_TARGET)\
|
|
||||||
$(FUZZER_OBJECTS) $(FUZZER_OBJECTS:.o=.d)\
|
|
||||||
$(FUZZER_BINARIES) $(FUZZER_BINARIES:=.d)\
|
|
||||||
$(FUZZER_DEBUG_BINARIES) $(FUZZER_DEBUG_BINARIES:=.d)\
|
|
||||||
|
|
||||||
build_tests: $(TEST_BINARIES)
|
build_tests: $(TEST_BINARIES)
|
||||||
|
|
||||||
test: build_tests
|
test: build_tests
|
||||||
|
@ -135,41 +125,55 @@ fuzzers: $(FUZZER_BINARIES) $(FUZZER_DEBUG_BINARIES)
|
||||||
.PHONY: fuzzers
|
.PHONY: fuzzers
|
||||||
|
|
||||||
$(JS_EXPORTED_FUNCTIONS): $(PUBLIC_HEADERS)
|
$(JS_EXPORTED_FUNCTIONS): $(PUBLIC_HEADERS)
|
||||||
perl -MJSON -ne '/(olm_[^( ]*)\(/ && push @f, "_$$1"; END { print encode_json \@f }' $^ > $@.tmp
|
perl -MJSON -ne '$$f{"_$$1"}=1 if /(olm_[^( ]*)\(/; END { @f=sort keys %f; print encode_json \@f }' $^ > $@.tmp
|
||||||
mv $@.tmp $@
|
mv $@.tmp $@
|
||||||
|
|
||||||
all: test js lib debug
|
all: test js lib debug
|
||||||
.PHONY: lib
|
.PHONY: all
|
||||||
|
|
||||||
|
clean:;
|
||||||
|
rm -rf $(BUILD_DIR)
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
### rules for building objects
|
### rules for building objects
|
||||||
$(BUILD_DIR)/release/%.o: src/%.c | makedirs
|
$(BUILD_DIR)/release/%.o: %.c
|
||||||
|
mkdir -p $(dir $@)
|
||||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/release/%.o: src/%.cpp | makedirs
|
$(BUILD_DIR)/release/%.o: %.cpp
|
||||||
|
mkdir -p $(dir $@)
|
||||||
$(COMPILE.cc) $(OUTPUT_OPTION) $<
|
$(COMPILE.cc) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/debug/%.o: src/%.c | makedirs
|
$(BUILD_DIR)/debug/%.o: %.c
|
||||||
|
mkdir -p $(dir $@)
|
||||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/debug/%.o: src/%.cpp | makedirs
|
$(BUILD_DIR)/debug/%.o: %.cpp
|
||||||
|
mkdir -p $(dir $@)
|
||||||
$(COMPILE.cc) $(OUTPUT_OPTION) $<
|
$(COMPILE.cc) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/javascript/%.js.bc: src/%.c | makedirs
|
$(BUILD_DIR)/javascript/%.o: %.c
|
||||||
|
mkdir -p $(dir $@)
|
||||||
$(EMCC.c) $(OUTPUT_OPTION) $<
|
$(EMCC.c) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/javascript/%.js.bc: src/%.cpp | makedirs
|
$(BUILD_DIR)/javascript/%.o: %.cpp
|
||||||
|
mkdir -p $(dir $@)
|
||||||
$(EMCC.cc) $(OUTPUT_OPTION) $<
|
$(EMCC.cc) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/tests/%: tests/%.c $(DEBUG_OBJECTS)
|
$(BUILD_DIR)/tests/%: tests/%.c $(DEBUG_OBJECTS)
|
||||||
|
mkdir -p $(dir $@)
|
||||||
$(LINK.c) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
$(LINK.c) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
||||||
|
|
||||||
$(BUILD_DIR)/tests/%: tests/%.cpp $(DEBUG_OBJECTS)
|
$(BUILD_DIR)/tests/%: tests/%.cpp $(DEBUG_OBJECTS)
|
||||||
|
mkdir -p $(dir $@)
|
||||||
$(LINK.cc) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
$(LINK.cc) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
||||||
|
|
||||||
$(BUILD_DIR)/fuzzers/objects/%.o: src/%.c | makedirs
|
$(BUILD_DIR)/fuzzers/objects/%.o: %.c
|
||||||
|
mkdir -p $(dir $@)
|
||||||
$(AFL.c) $(OUTPUT_OPTION) $<
|
$(AFL.c) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/fuzzers/objects/%.o: src/%.cpp | makedirs
|
$(BUILD_DIR)/fuzzers/objects/%.o: %.cpp
|
||||||
|
mkdir -p $(dir $@)
|
||||||
$(AFL.cc) $(OUTPUT_OPTION) $<
|
$(AFL.cc) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/fuzzers/fuzz_%: fuzzers/fuzz_%.c $(FUZZER_OBJECTS)
|
$(BUILD_DIR)/fuzzers/fuzz_%: fuzzers/fuzz_%.c $(FUZZER_OBJECTS)
|
||||||
|
@ -188,7 +192,7 @@ $(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.cpp $(DEBUG_OBJECTS)
|
||||||
|
|
||||||
-include $(RELEASE_OBJECTS:.o=.d)
|
-include $(RELEASE_OBJECTS:.o=.d)
|
||||||
-include $(DEBUG_OBJECTS:.o=.d)
|
-include $(DEBUG_OBJECTS:.o=.d)
|
||||||
-include $(JS_OBJECTS:.bc=.d)
|
-include $(JS_OBJECTS:.o=.d)
|
||||||
-include $(TEST_BINARIES:=.d)
|
-include $(TEST_BINARIES:=.d)
|
||||||
-include $(FUZZER_OBJECTS:.o=.d)
|
-include $(FUZZER_OBJECTS:.o=.d)
|
||||||
-include $(FUZZER_BINARIES:=.d)
|
-include $(FUZZER_BINARIES:=.d)
|
||||||
|
|
|
@ -61,4 +61,5 @@ int main(int argc, const char *argv[]) {
|
||||||
|
|
||||||
ignored = write(STDOUT_FILENO, plaintext, length);
|
ignored = write(STDOUT_FILENO, plaintext, length);
|
||||||
ignored = write(STDOUT_FILENO, "\n", 1);
|
ignored = write(STDOUT_FILENO, "\n", 1);
|
||||||
|
return ignored;
|
||||||
}
|
}
|
||||||
|
|
71
fuzzers/fuzz_group_decrypt.cpp
Normal file
71
fuzzers/fuzz_group_decrypt.cpp
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
#include "olm/olm.hh"
|
||||||
|
|
||||||
|
#include "fuzzing.hh"
|
||||||
|
|
||||||
|
int main(int argc, const char *argv[]) {
|
||||||
|
size_t ignored;
|
||||||
|
if (argc <= 2) {
|
||||||
|
const char * message = "Usage: decrypt <pickle_key> <group_session>\n";
|
||||||
|
ignored = write(STDERR_FILENO, message, strlen(message));
|
||||||
|
exit(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * key = argv[1];
|
||||||
|
size_t key_length = strlen(key);
|
||||||
|
|
||||||
|
|
||||||
|
int session_fd = check_errno(
|
||||||
|
"Error opening session file", open(argv[2], O_RDONLY)
|
||||||
|
);
|
||||||
|
|
||||||
|
uint8_t *session_buffer;
|
||||||
|
ssize_t session_length = check_errno(
|
||||||
|
"Error reading session file", read_file(session_fd, &session_buffer)
|
||||||
|
);
|
||||||
|
|
||||||
|
int message_fd = STDIN_FILENO;
|
||||||
|
uint8_t * message_buffer;
|
||||||
|
ssize_t message_length = check_errno(
|
||||||
|
"Error reading message file", read_file(message_fd, &message_buffer)
|
||||||
|
);
|
||||||
|
|
||||||
|
uint8_t * tmp_buffer = (uint8_t *) malloc(message_length);
|
||||||
|
memcpy(tmp_buffer, message_buffer, message_length);
|
||||||
|
|
||||||
|
uint8_t session_memory[olm_inbound_group_session_size()];
|
||||||
|
OlmInboundGroupSession * session = olm_inbound_group_session(session_memory);
|
||||||
|
check_error(
|
||||||
|
olm_inbound_group_session_last_error,
|
||||||
|
session,
|
||||||
|
"Error unpickling session",
|
||||||
|
olm_unpickle_inbound_group_session(
|
||||||
|
session, key, key_length, session_buffer, session_length
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
size_t max_length = check_error(
|
||||||
|
olm_inbound_group_session_last_error,
|
||||||
|
session,
|
||||||
|
"Error getting plaintext length",
|
||||||
|
olm_group_decrypt_max_plaintext_length(
|
||||||
|
session, tmp_buffer, message_length
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
uint8_t plaintext[max_length];
|
||||||
|
|
||||||
|
size_t length = check_error(
|
||||||
|
olm_inbound_group_session_last_error,
|
||||||
|
session,
|
||||||
|
"Error decrypting message",
|
||||||
|
olm_group_decrypt(
|
||||||
|
session,
|
||||||
|
message_buffer, message_length,
|
||||||
|
plaintext, max_length
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
ignored = write(STDOUT_FILENO, plaintext, length);
|
||||||
|
ignored = write(STDOUT_FILENO, "\n", 1);
|
||||||
|
return ignored;
|
||||||
|
}
|
|
@ -53,13 +53,15 @@ T check_errno(
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t check_session(
|
template<typename T, typename F>
|
||||||
OlmSession * session,
|
size_t check_error(
|
||||||
|
F f,
|
||||||
|
T * object,
|
||||||
const char * message,
|
const char * message,
|
||||||
size_t value
|
size_t value
|
||||||
) {
|
) {
|
||||||
if (value == olm_error()) {
|
if (value == olm_error()) {
|
||||||
const char * olm_message = olm_session_last_error(session);
|
const char * olm_message = f(object);
|
||||||
ssize_t ignored;
|
ssize_t ignored;
|
||||||
ignored = write(STDERR_FILENO, message, strlen(message));
|
ignored = write(STDERR_FILENO, message, strlen(message));
|
||||||
ignored = write(STDERR_FILENO, ": ", 2);
|
ignored = write(STDERR_FILENO, ": ", 2);
|
||||||
|
@ -70,3 +72,11 @@ size_t check_session(
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t check_session(
|
||||||
|
OlmSession * session,
|
||||||
|
const char * message,
|
||||||
|
size_t value
|
||||||
|
) {
|
||||||
|
return check_error(olm_session_last_error, session, message, value);
|
||||||
|
}
|
||||||
|
|
|
@ -35,7 +35,6 @@ extern "C" {
|
||||||
* The length of the buffer needed to hold a group message.
|
* The length of the buffer needed to hold a group message.
|
||||||
*/
|
*/
|
||||||
size_t _olm_encode_group_message_length(
|
size_t _olm_encode_group_message_length(
|
||||||
size_t group_session_id_length,
|
|
||||||
uint32_t chain_index,
|
uint32_t chain_index,
|
||||||
size_t ciphertext_length,
|
size_t ciphertext_length,
|
||||||
size_t mac_length
|
size_t mac_length
|
||||||
|
@ -45,8 +44,6 @@ size_t _olm_encode_group_message_length(
|
||||||
* Writes the message headers into the output buffer.
|
* Writes the message headers into the output buffer.
|
||||||
*
|
*
|
||||||
* version: version number of the olm protocol
|
* version: version number of the olm protocol
|
||||||
* session_id: group session identifier
|
|
||||||
* session_id_length: length of session_id
|
|
||||||
* message_index: message index
|
* message_index: message index
|
||||||
* ciphertext_length: length of the ciphertext
|
* ciphertext_length: length of the ciphertext
|
||||||
* output: where to write the output. Should be at least
|
* output: where to write the output. Should be at least
|
||||||
|
@ -58,8 +55,6 @@ size_t _olm_encode_group_message_length(
|
||||||
*/
|
*/
|
||||||
size_t _olm_encode_group_message(
|
size_t _olm_encode_group_message(
|
||||||
uint8_t version,
|
uint8_t version,
|
||||||
const uint8_t *session_id,
|
|
||||||
size_t session_id_length,
|
|
||||||
uint32_t message_index,
|
uint32_t message_index,
|
||||||
size_t ciphertext_length,
|
size_t ciphertext_length,
|
||||||
uint8_t *output,
|
uint8_t *output,
|
||||||
|
@ -69,8 +64,6 @@ size_t _olm_encode_group_message(
|
||||||
|
|
||||||
struct _OlmDecodeGroupMessageResults {
|
struct _OlmDecodeGroupMessageResults {
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
const uint8_t *session_id;
|
|
||||||
size_t session_id_length;
|
|
||||||
uint32_t message_index;
|
uint32_t message_index;
|
||||||
int has_message_index;
|
int has_message_index;
|
||||||
const uint8_t *ciphertext;
|
const uint8_t *ciphertext;
|
||||||
|
|
|
@ -23,3 +23,20 @@ Example:
|
||||||
bob_session.create_inbound(bob, bob_message);
|
bob_session.create_inbound(bob, bob_message);
|
||||||
var plaintext = bob_session.decrypt(message_1.type, bob_message);
|
var plaintext = bob_session.decrypt(message_1.type, bob_message);
|
||||||
bob.remove_one_time_keys(bob_session);
|
bob.remove_one_time_keys(bob_session);
|
||||||
|
|
||||||
|
|
||||||
|
Group chat:
|
||||||
|
|
||||||
|
var outbound_session = new Olm.OutboundGroupSession();
|
||||||
|
outbound_session.create();
|
||||||
|
|
||||||
|
// exchange these over a secure channel
|
||||||
|
var session_id = group_session.session_id();
|
||||||
|
var session_key = group_session.session_key();
|
||||||
|
var message_index = group_session.message_index();
|
||||||
|
|
||||||
|
var inbound_session = new Olm.InboundGroupSession();
|
||||||
|
inbound_session.create(message_index, session_key);
|
||||||
|
|
||||||
|
var ciphertext = outbound_session.encrypt("Hello");
|
||||||
|
var plaintext = inbound_session.decrypt(ciphertext);
|
||||||
|
|
8
javascript/demo/demo.css
Normal file
8
javascript/demo/demo.css
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
div.user {
|
||||||
|
width: 500px;
|
||||||
|
float: left;
|
||||||
|
overflow: scroll;
|
||||||
|
margin: 0px 20px 0px 0px;
|
||||||
|
border: 1px solid black;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
61
javascript/demo/group_demo.html
Normal file
61
javascript/demo/group_demo.html
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="demo.css"/>
|
||||||
|
<script src="../olm.js"></script>
|
||||||
|
<script src="group_demo.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="user1" class="user">
|
||||||
|
<h1>User1</h1>
|
||||||
|
|
||||||
|
<textarea class="user_plain_input"></textarea>
|
||||||
|
<button class="user_encrypt">Encrypt</button>
|
||||||
|
|
||||||
|
<h2>Outgoing</h2>
|
||||||
|
|
||||||
|
<h3>One-to-one output</h3>
|
||||||
|
<div class="user_cipher_output"></div>
|
||||||
|
|
||||||
|
<h3>Group output</h3>
|
||||||
|
<div class="group_output"></div>
|
||||||
|
|
||||||
|
<h2>Incoming</h2>
|
||||||
|
|
||||||
|
<h3>One-to-one Received</h3>
|
||||||
|
<div class="user_cipher_input"></div>
|
||||||
|
|
||||||
|
<h3>Group received</h3>
|
||||||
|
<div class="group_input"></div>
|
||||||
|
|
||||||
|
<h2>Tasks</h2>
|
||||||
|
<div class="user_progress"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="user2" class="user">
|
||||||
|
<h1>User 2</h1>
|
||||||
|
|
||||||
|
<textarea class="user_plain_input"></textarea>
|
||||||
|
<button class="user_encrypt">Encrypt</button>
|
||||||
|
|
||||||
|
<h2>Outgoing</h2>
|
||||||
|
|
||||||
|
<h3>One-to-one output</h3>
|
||||||
|
<div class="user_cipher_output"></div>
|
||||||
|
|
||||||
|
<h3>Group output</h3>
|
||||||
|
<div class="group_output"></div>
|
||||||
|
|
||||||
|
<h2>Incoming</h2>
|
||||||
|
|
||||||
|
<h3>One-to-one Received</h3>
|
||||||
|
<div class="user_cipher_input"></div>
|
||||||
|
|
||||||
|
<h3>Group received</h3>
|
||||||
|
<div class="group_input"></div>
|
||||||
|
|
||||||
|
<h2>Tasks</h2>
|
||||||
|
<div class="user_progress"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
489
javascript/demo/group_demo.js
Normal file
489
javascript/demo/group_demo.js
Normal file
|
@ -0,0 +1,489 @@
|
||||||
|
/* Javascript parts of the group demo. To use, load group_demo.html in your
|
||||||
|
* browser.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function buttonElement(buttonLabel, clickHandler) {
|
||||||
|
var button = document.createElement("button");
|
||||||
|
button.appendChild(document.createTextNode(buttonLabel));
|
||||||
|
button.addEventListener("click", clickHandler, false);
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buttonsAndText(textContent, buttonLabelToHandlerMap) {
|
||||||
|
var el = document.createElement("div");
|
||||||
|
for (var label in buttonLabelToHandlerMap) {
|
||||||
|
if (!buttonLabelToHandlerMap.hasOwnProperty(label)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var handler = buttonLabelToHandlerMap[label];
|
||||||
|
var button = buttonElement(label, handler);
|
||||||
|
el.appendChild(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
var message_element = document.createElement("tt");
|
||||||
|
el.appendChild(message_element);
|
||||||
|
|
||||||
|
var content = document.createTextNode(textContent);
|
||||||
|
message_element.appendChild(content);
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buttonAndTextElement(buttonLabel, textContent, clickHandler) {
|
||||||
|
var buttonMap = {};
|
||||||
|
buttonMap[buttonLabel] = clickHandler;
|
||||||
|
return buttonsAndText(textContent, buttonMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DemoUser(name) {
|
||||||
|
this.name = name;
|
||||||
|
this.olmAccount = new Olm.Account();
|
||||||
|
this.olmAccount.create();
|
||||||
|
|
||||||
|
this.idKey = this.getIdKeys()["curve25519"];
|
||||||
|
|
||||||
|
/* the people in our chat, indexed by their Curve25519 identity key.
|
||||||
|
*/
|
||||||
|
this.peers = {};
|
||||||
|
|
||||||
|
/* the Ed25519 signing key for each peer, indexed by their Curve25519 id key
|
||||||
|
*/
|
||||||
|
this.peerSigningKeys = {};
|
||||||
|
|
||||||
|
/* for each peer, a one-to-one session - indexed by id key and created on
|
||||||
|
* demand */
|
||||||
|
this.peerSessions = {};
|
||||||
|
|
||||||
|
/* for each peer, info on their sender session - indexed by id key and
|
||||||
|
* session id */
|
||||||
|
this.peerGroupSessions = {};
|
||||||
|
|
||||||
|
/* our outbound group session */
|
||||||
|
this.groupSession = undefined;
|
||||||
|
|
||||||
|
/* a list of pending tasks */
|
||||||
|
this.tasks = [];
|
||||||
|
this.taskWorker = undefined;
|
||||||
|
|
||||||
|
/* the operations our peers are allowed to do on us */
|
||||||
|
var publicOps = [
|
||||||
|
"getIdKeys", "getOneTimeKey",
|
||||||
|
"receiveOneToOne", "receiveGroup",
|
||||||
|
];
|
||||||
|
|
||||||
|
this.remoteOps = {};
|
||||||
|
for (var i=0; i<publicOps.length; i++) {
|
||||||
|
var op = publicOps[i];
|
||||||
|
this.remoteOps[op] = this[op].bind(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DemoUser.prototype._progress = function(message) {
|
||||||
|
var progress = this.progressElement;
|
||||||
|
|
||||||
|
var message_element = document.createElement("pre");
|
||||||
|
var start_content = document.createTextNode(message + "...");
|
||||||
|
function start() {
|
||||||
|
message_element.appendChild(start_content);
|
||||||
|
progress.appendChild(message_element);
|
||||||
|
}
|
||||||
|
function done(res) {
|
||||||
|
var done_content = document.createTextNode(message + "..." + res);
|
||||||
|
message_element.replaceChild(done_content, start_content);
|
||||||
|
}
|
||||||
|
return {start:start, done:done};
|
||||||
|
};
|
||||||
|
|
||||||
|
DemoUser.prototype._do_tasks = function() {
|
||||||
|
var self = this;
|
||||||
|
var task = self.tasks.shift();
|
||||||
|
var desc = task[0];
|
||||||
|
var func = task[1];
|
||||||
|
var callback = task[2];
|
||||||
|
|
||||||
|
var p = self._progress(desc);
|
||||||
|
p.start();
|
||||||
|
|
||||||
|
function done() {
|
||||||
|
p.done("Done");
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
try {
|
||||||
|
callback.apply(undefined, arguments)
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Uncaught exception in callback", e.stack || e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start_tasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
// sleep 50ms before actually doing the task
|
||||||
|
self.taskWorker = window.setTimeout(function() {
|
||||||
|
try {
|
||||||
|
task[1](done);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Uncaught exception in task", e.stack || e);
|
||||||
|
p.done("Failed: "+e);
|
||||||
|
start_tasks();
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
|
||||||
|
|
||||||
|
function start_tasks() {
|
||||||
|
if (self.tasks.length == 0) {
|
||||||
|
self.taskWorker = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.taskWorker = window.setTimeout(self._do_tasks.bind(self), 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add a function "task" to this user's queue of things to do.
|
||||||
|
*
|
||||||
|
* task is called with a single argument 'done' which is a function to call
|
||||||
|
* once the task is complete.
|
||||||
|
*
|
||||||
|
* 'callback' is called once the task is complete, with any arguments that
|
||||||
|
* were passed to 'done'.
|
||||||
|
*/
|
||||||
|
DemoUser.prototype.addTask = function(description, task, callback) {
|
||||||
|
this.tasks.push([description, task, callback]);
|
||||||
|
if(!this.taskWorker) {
|
||||||
|
this._do_tasks();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DemoUser.prototype.addPeer = function(peerOps) {
|
||||||
|
var keys = peerOps.getIdKeys();
|
||||||
|
var id = keys["curve25519"];
|
||||||
|
this.peers[id] = peerOps;
|
||||||
|
this.peerSigningKeys[id] = keys["ed25519"];
|
||||||
|
};
|
||||||
|
|
||||||
|
DemoUser.prototype.getIdKeys = function() {
|
||||||
|
return JSON.parse(this.olmAccount.identity_keys());
|
||||||
|
};
|
||||||
|
|
||||||
|
DemoUser.prototype.generateKeys = function(callback) {
|
||||||
|
var self = this;
|
||||||
|
this.addTask("generate one time key", function(done) {
|
||||||
|
self.olmAccount.generate_one_time_keys(1);
|
||||||
|
done();
|
||||||
|
}, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
DemoUser.prototype.getOneTimeKey = function() {
|
||||||
|
var self = this;
|
||||||
|
var keys = JSON.parse(self.olmAccount.one_time_keys())
|
||||||
|
.curve25519;
|
||||||
|
for (key_id in keys) {
|
||||||
|
if (keys.hasOwnProperty(key_id)) {
|
||||||
|
return keys[key_id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("No one-time-keys generated");
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ************************************************************************
|
||||||
|
*
|
||||||
|
* one-to-one messaging
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retrieve, or initiate, a one-to-one session to a given peer
|
||||||
|
*/
|
||||||
|
DemoUser.prototype.getPeerSession = function(peerId, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (this.peerSessions[peerId]) {
|
||||||
|
callback(this.peerSessions[peerId]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var peer = this.peers[peerId];
|
||||||
|
this.addTask("get peer keys", function(done) {
|
||||||
|
key = peer.getOneTimeKey();
|
||||||
|
done(key);
|
||||||
|
}, function(ot_key) {
|
||||||
|
self.addTask("create peer session", function(done) {
|
||||||
|
var session = new Olm.Session();
|
||||||
|
session.create_outbound(self.olmAccount, peerId, ot_key);
|
||||||
|
self.peerSessions[peerId] = session;
|
||||||
|
done(session);
|
||||||
|
}, callback);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* encrypt a one-to-one message and prepare it for sending to a peer
|
||||||
|
*/
|
||||||
|
DemoUser.prototype.sendToPeer = function(peerId, message, callback) {
|
||||||
|
var self = this;
|
||||||
|
this.getPeerSession(peerId, function(session) {
|
||||||
|
self.addTask("encrypt one-to-one message", function(done) {
|
||||||
|
var encrypted = session.encrypt(message);
|
||||||
|
var packet = {
|
||||||
|
sender_key: self.idKey,
|
||||||
|
ciphertext: encrypted,
|
||||||
|
};
|
||||||
|
var json = JSON.stringify(packet);
|
||||||
|
|
||||||
|
var el = buttonAndTextElement("send", json, function(ev) {
|
||||||
|
self.peers[peerId].receiveOneToOne(json);
|
||||||
|
});
|
||||||
|
self.cipherOutputDiv.appendChild(el);
|
||||||
|
done();
|
||||||
|
}, callback);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handler for receiving a one-to-one message
|
||||||
|
*/
|
||||||
|
DemoUser.prototype.receiveOneToOne = function(jsonpacket) {
|
||||||
|
var self = this;
|
||||||
|
var el = buttonAndTextElement("decrypt", jsonpacket, function(ev) {
|
||||||
|
var sender = JSON.parse(jsonpacket).sender_key;
|
||||||
|
self.decryptOneToOne(jsonpacket, function(result) {
|
||||||
|
|
||||||
|
var el2 = document.createElement("tt");
|
||||||
|
el.appendChild(el2);
|
||||||
|
|
||||||
|
var content = document.createTextNode(" -> "+result);
|
||||||
|
el2.appendChild(content);
|
||||||
|
|
||||||
|
var body = JSON.parse(result);
|
||||||
|
|
||||||
|
// create a new inbound session if we don't yet have one
|
||||||
|
if (!self.peerGroupSessions[sender] ||
|
||||||
|
!self.peerGroupSessions[sender][body.session_id]) {
|
||||||
|
self.createInboundSession(
|
||||||
|
sender, body.session_id, body.message_index, body.session_key
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.cipherInputDiv.appendChild(el);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add a task to decrypt a one-to-one message. Calls the callback with the
|
||||||
|
* decrypted plaintext
|
||||||
|
*/
|
||||||
|
DemoUser.prototype.decryptOneToOne = function(jsonpacket, callback) {
|
||||||
|
var self = this;
|
||||||
|
self.addTask("decrypt one-to-one message", function(done) {
|
||||||
|
var packet = JSON.parse(jsonpacket);
|
||||||
|
var peerId = packet.sender_key;
|
||||||
|
|
||||||
|
var session = self.peerSessions[peerId];
|
||||||
|
var plaintext;
|
||||||
|
if (session) {
|
||||||
|
plaintext = session.decrypt(packet.ciphertext.type, packet.ciphertext.body);
|
||||||
|
done(plaintext);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet.ciphertext.type != 0) {
|
||||||
|
throw new Error("Unknown one-to-one session");
|
||||||
|
}
|
||||||
|
|
||||||
|
session = new Olm.Session();
|
||||||
|
session.create_inbound(self.olmAccount, packet.ciphertext.body);
|
||||||
|
self.peerSessions[peerId] = session;
|
||||||
|
plaintext = session.decrypt(packet.ciphertext.type, packet.ciphertext.body);
|
||||||
|
done(plaintext);
|
||||||
|
}, callback)
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ************************************************************************
|
||||||
|
*
|
||||||
|
* group messaging
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retrieve, or initiate, an outbound group session
|
||||||
|
*/
|
||||||
|
DemoUser.prototype.getGroupSession = function() {
|
||||||
|
if (this.groupSession) {
|
||||||
|
return this.groupSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.groupSession = new Olm.OutboundGroupSession();
|
||||||
|
this.groupSession.create();
|
||||||
|
|
||||||
|
var keymsg = {
|
||||||
|
"session_id": this.groupSession.session_id(),
|
||||||
|
"session_key": this.groupSession.session_key(),
|
||||||
|
"message_index": this.groupSession.message_index(),
|
||||||
|
};
|
||||||
|
var jsonmsg = JSON.stringify(keymsg);
|
||||||
|
|
||||||
|
for (var peer in this.peers) {
|
||||||
|
if (!this.peers.hasOwnProperty(peer)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.sendToPeer(peer, jsonmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.groupSession;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add a task to create an inbound group session
|
||||||
|
*/
|
||||||
|
DemoUser.prototype.createInboundSession = function(
|
||||||
|
peer_id, session_id, message_index, session_key, callback
|
||||||
|
) {
|
||||||
|
var self = this;
|
||||||
|
this.addTask("init inbound session", function(done) {
|
||||||
|
session = new Olm.InboundGroupSession();
|
||||||
|
session.create(message_index, session_key);
|
||||||
|
if (!self.peerGroupSessions[peer_id]) {
|
||||||
|
self.peerGroupSessions[peer_id] = {};
|
||||||
|
}
|
||||||
|
self.peerGroupSessions[peer_id][session_id] = session;
|
||||||
|
done(session);
|
||||||
|
}, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handler for receiving a group message
|
||||||
|
*/
|
||||||
|
DemoUser.prototype.receiveGroup = function(jsonpacket) {
|
||||||
|
var self = this;
|
||||||
|
var el = buttonAndTextElement("decrypt", jsonpacket, function(ev) {
|
||||||
|
self.decryptGroup(jsonpacket, function(result) {
|
||||||
|
var el2 = document.createElement("tt");
|
||||||
|
el.appendChild(el2);
|
||||||
|
|
||||||
|
var content = document.createTextNode(" -> "+result);
|
||||||
|
el2.appendChild(content);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.groupInputDiv.appendChild(el);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add a task to decrypt a received group message. Calls the callback with the
|
||||||
|
* decrypted plaintext
|
||||||
|
*/
|
||||||
|
DemoUser.prototype.decryptGroup = function(jsonpacket, callback) {
|
||||||
|
var self = this;
|
||||||
|
this.addTask("decrypt group message", function(done) {
|
||||||
|
var packet = JSON.parse(jsonpacket);
|
||||||
|
|
||||||
|
var sender = packet.sender_key;
|
||||||
|
var session_id = packet.session_id;
|
||||||
|
|
||||||
|
var sender_signing_key = self.peerSigningKeys[sender];
|
||||||
|
if (!sender_signing_key) {
|
||||||
|
throw new Error("No known signing key for sender "+sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
var olmUtility = new Olm.Utility();
|
||||||
|
olmUtility.ed25519_verify(
|
||||||
|
sender_signing_key, packet.body, packet.signature
|
||||||
|
);
|
||||||
|
|
||||||
|
var peer_sessions = self.peerGroupSessions[sender];
|
||||||
|
if (!peer_sessions) {
|
||||||
|
throw new Error("No sessions for sender "+sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
var session = peer_sessions[session_id];
|
||||||
|
if (!session) {
|
||||||
|
throw new Error("Unknown session id " + session_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
var plaintext = session.decrypt(packet.body);
|
||||||
|
done(plaintext);
|
||||||
|
}, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add a task to encrypt, and prepare for sending, a group message.
|
||||||
|
*
|
||||||
|
* Will create a group session if necessary
|
||||||
|
*/
|
||||||
|
DemoUser.prototype.encrypt = function(message) {
|
||||||
|
var self = this;
|
||||||
|
var session = this.getGroupSession();
|
||||||
|
|
||||||
|
function sendJsonToPeers(json) {
|
||||||
|
for (var peer in self.peers) {
|
||||||
|
if (!self.peers.hasOwnProperty(peer)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self.peers[peer].receiveGroup(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
self.addTask("encrypt group message", function(done) {
|
||||||
|
var encrypted = session.encrypt(message);
|
||||||
|
var signature = self.olmAccount.sign(encrypted);
|
||||||
|
|
||||||
|
var packet = {
|
||||||
|
sender_key: self.idKey,
|
||||||
|
session_id: session.session_id(),
|
||||||
|
body: encrypted,
|
||||||
|
signature: signature,
|
||||||
|
};
|
||||||
|
var json = JSON.stringify(packet);
|
||||||
|
|
||||||
|
var el = buttonsAndText(json, {
|
||||||
|
send: function(ev) {
|
||||||
|
sendJsonToPeers(json);
|
||||||
|
},
|
||||||
|
"send corrupted": function(ev) {
|
||||||
|
var p = JSON.parse(json);
|
||||||
|
p.body += " ";
|
||||||
|
sendJsonToPeers(JSON.stringify(p));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
self.groupOutputDiv.appendChild(el);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
function initUserDiv(demoUser, div) {
|
||||||
|
demoUser.progressElement = div.getElementsByClassName("user_progress")[0];
|
||||||
|
demoUser.cipherOutputDiv = div.getElementsByClassName("user_cipher_output")[0];
|
||||||
|
demoUser.cipherInputDiv = div.getElementsByClassName("user_cipher_input")[0];
|
||||||
|
demoUser.groupOutputDiv = div.getElementsByClassName("group_output")[0];
|
||||||
|
demoUser.groupInputDiv = div.getElementsByClassName("group_input")[0];
|
||||||
|
|
||||||
|
var plain_input = div.getElementsByClassName("user_plain_input")[0];
|
||||||
|
var encrypt = div.getElementsByClassName("user_encrypt")[0];
|
||||||
|
|
||||||
|
encrypt.addEventListener("click", function() {
|
||||||
|
demoUser.encrypt(plain_input.value);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function startDemo() {
|
||||||
|
var user1 = new DemoUser();
|
||||||
|
initUserDiv(user1, document.getElementById("user1"));
|
||||||
|
user1.generateKeys();
|
||||||
|
|
||||||
|
var user2 = new DemoUser();
|
||||||
|
initUserDiv(user2, document.getElementById("user2"));
|
||||||
|
user2.generateKeys();
|
||||||
|
|
||||||
|
user1.addPeer(user2.remoteOps);
|
||||||
|
user2.addPeer(user1.remoteOps);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", startDemo, false);
|
|
@ -1,6 +1,6 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<script src="olm.js"></script>
|
<script src="../olm.js"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function (event) {
|
document.addEventListener("DOMContentLoaded", function (event) {
|
||||||
function progress(who, message) {
|
function progress(who, message) {
|
78
javascript/olm_inbound_group_session.js
Normal file
78
javascript/olm_inbound_group_session.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
function InboundGroupSession() {
|
||||||
|
var size = Module['_olm_inbound_group_session_size']();
|
||||||
|
this.buf = malloc(size);
|
||||||
|
this.ptr = Module['_olm_inbound_group_session'](this.buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
function inbound_group_session_method(wrapped) {
|
||||||
|
return function() {
|
||||||
|
var result = wrapped.apply(this, arguments);
|
||||||
|
if (result === OLM_ERROR) {
|
||||||
|
var message = Pointer_stringify(
|
||||||
|
Module['_olm_inbound_group_session_last_error'](arguments[0])
|
||||||
|
);
|
||||||
|
throw new Error("OLM." + message);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InboundGroupSession.prototype['free'] = function() {
|
||||||
|
Module['_olm_clear_inbound_group_session'](this.ptr);
|
||||||
|
free(this.ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
InboundGroupSession.prototype['pickle'] = restore_stack(function(key) {
|
||||||
|
var key_array = array_from_string(key);
|
||||||
|
var pickle_length = inbound_group_session_method(
|
||||||
|
Module['_olm_pickle_inbound_group_session_length']
|
||||||
|
)(this.ptr);
|
||||||
|
var key_buffer = stack(key_array);
|
||||||
|
var pickle_buffer = stack(pickle_length);
|
||||||
|
inbound_group_session_method(Module['_olm_pickle_inbound_group_session'])(
|
||||||
|
this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
|
||||||
|
);
|
||||||
|
return Pointer_stringify(pickle_buffer, pickle_length);
|
||||||
|
});
|
||||||
|
|
||||||
|
InboundGroupSession.prototype['unpickle'] = restore_stack(function(key, pickle) {
|
||||||
|
var key_array = array_from_string(key);
|
||||||
|
var key_buffer = stack(key_array);
|
||||||
|
var pickle_array = array_from_string(pickle);
|
||||||
|
var pickle_buffer = stack(pickle_array);
|
||||||
|
inbound_group_session_method(Module['_olm_unpickle_inbound_group_session'])(
|
||||||
|
this.ptr, key_buffer, key_array.length, pickle_buffer,
|
||||||
|
pickle_array.length
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
InboundGroupSession.prototype['create'] = restore_stack(function(message_index, session_key) {
|
||||||
|
var key_array = array_from_string(session_key);
|
||||||
|
var key_buffer = stack(key_array);
|
||||||
|
|
||||||
|
inbound_group_session_method(Module['_olm_init_inbound_group_session'])(
|
||||||
|
this.ptr, message_index, key_buffer, key_array.length
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
InboundGroupSession.prototype['decrypt'] = restore_stack(function(
|
||||||
|
message
|
||||||
|
) {
|
||||||
|
var message_array = array_from_string(message);
|
||||||
|
var message_buffer = stack(message_array);
|
||||||
|
var max_plaintext_length = session_method(
|
||||||
|
Module['_olm_group_decrypt_max_plaintext_length']
|
||||||
|
)(this.ptr, message_buffer, message_array.length);
|
||||||
|
// caculating the length destroys the input buffer.
|
||||||
|
// So we copy the array to a new buffer
|
||||||
|
var message_buffer = stack(message_array);
|
||||||
|
var plaintext_buffer = stack(max_plaintext_length);
|
||||||
|
var plaintext_length = session_method(Module["_olm_group_decrypt"])(
|
||||||
|
this.ptr,
|
||||||
|
message_buffer, message_array.length,
|
||||||
|
plaintext_buffer, max_plaintext_length
|
||||||
|
);
|
||||||
|
return Pointer_stringify(plaintext_buffer, plaintext_length);
|
||||||
|
});
|
||||||
|
|
||||||
|
olm_exports['InboundGroupSession'] = InboundGroupSession;
|
104
javascript/olm_outbound_group_session.js
Normal file
104
javascript/olm_outbound_group_session.js
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
|
||||||
|
function OutboundGroupSession() {
|
||||||
|
var size = Module['_olm_outbound_group_session_size']();
|
||||||
|
this.buf = malloc(size);
|
||||||
|
this.ptr = Module['_olm_outbound_group_session'](this.buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
function outbound_group_session_method(wrapped) {
|
||||||
|
return function() {
|
||||||
|
var result = wrapped.apply(this, arguments);
|
||||||
|
if (result === OLM_ERROR) {
|
||||||
|
var message = Pointer_stringify(
|
||||||
|
Module['_olm_outbound_group_session_last_error'](arguments[0])
|
||||||
|
);
|
||||||
|
throw new Error("OLM." + message);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutboundGroupSession.prototype['free'] = function() {
|
||||||
|
Module['_olm_clear_outbound_group_session'](this.ptr);
|
||||||
|
free(this.ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
OutboundGroupSession.prototype['pickle'] = restore_stack(function(key) {
|
||||||
|
var key_array = array_from_string(key);
|
||||||
|
var pickle_length = outbound_group_session_method(
|
||||||
|
Module['_olm_pickle_outbound_group_session_length']
|
||||||
|
)(this.ptr);
|
||||||
|
var key_buffer = stack(key_array);
|
||||||
|
var pickle_buffer = stack(pickle_length);
|
||||||
|
outbound_group_session_method(Module['_olm_pickle_outbound_group_session'])(
|
||||||
|
this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
|
||||||
|
);
|
||||||
|
return Pointer_stringify(pickle_buffer, pickle_length);
|
||||||
|
});
|
||||||
|
|
||||||
|
OutboundGroupSession.prototype['unpickle'] = restore_stack(function(key, pickle) {
|
||||||
|
var key_array = array_from_string(key);
|
||||||
|
var key_buffer = stack(key_array);
|
||||||
|
var pickle_array = array_from_string(pickle);
|
||||||
|
var pickle_buffer = stack(pickle_array);
|
||||||
|
outbound_group_session_method(Module['_olm_unpickle_outbound_group_session'])(
|
||||||
|
this.ptr, key_buffer, key_array.length, pickle_buffer,
|
||||||
|
pickle_array.length
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
OutboundGroupSession.prototype['create'] = restore_stack(function(key) {
|
||||||
|
var random_length = session_method(
|
||||||
|
Module['_olm_init_outbound_group_session_random_length']
|
||||||
|
)(this.ptr);
|
||||||
|
var random = random_stack(random_length);
|
||||||
|
outbound_group_session_method(Module['_olm_init_outbound_group_session'])(
|
||||||
|
this.ptr, random, random_length
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
OutboundGroupSession.prototype['encrypt'] = restore_stack(function(plaintext) {
|
||||||
|
var plaintext_array = array_from_string(plaintext);
|
||||||
|
var message_length = outbound_group_session_method(
|
||||||
|
Module['_olm_group_encrypt_message_length']
|
||||||
|
)(this.ptr, plaintext_array.length);
|
||||||
|
var plaintext_buffer = stack(plaintext_array);
|
||||||
|
var message_buffer = stack(message_length);
|
||||||
|
outbound_group_session_method(Module['_olm_group_encrypt'])(
|
||||||
|
this.ptr,
|
||||||
|
plaintext_buffer, plaintext_array.length,
|
||||||
|
message_buffer, message_length
|
||||||
|
);
|
||||||
|
return Pointer_stringify(message_buffer, message_length);
|
||||||
|
});
|
||||||
|
|
||||||
|
OutboundGroupSession.prototype['session_id'] = restore_stack(function(key) {
|
||||||
|
var length = outbound_group_session_method(
|
||||||
|
Module['_olm_outbound_group_session_id_length']
|
||||||
|
)(this.ptr);
|
||||||
|
var session_id = stack(length);
|
||||||
|
outbound_group_session_method(Module['_olm_outbound_group_session_id'])(
|
||||||
|
this.ptr, session_id, length
|
||||||
|
);
|
||||||
|
return Pointer_stringify(session_id, length);
|
||||||
|
});
|
||||||
|
|
||||||
|
OutboundGroupSession.prototype['session_key'] = restore_stack(function(key) {
|
||||||
|
var key_length = outbound_group_session_method(
|
||||||
|
Module['_olm_outbound_group_session_key_length']
|
||||||
|
)(this.ptr);
|
||||||
|
var key = stack(key_length);
|
||||||
|
outbound_group_session_method(Module['_olm_outbound_group_session_key'])(
|
||||||
|
this.ptr, key, key_length
|
||||||
|
);
|
||||||
|
return Pointer_stringify(key, key_length);
|
||||||
|
});
|
||||||
|
|
||||||
|
OutboundGroupSession.prototype['message_index'] = function() {
|
||||||
|
var idx = outbound_group_session_method(
|
||||||
|
Module['_olm_outbound_group_session_message_index']
|
||||||
|
)(this.ptr);
|
||||||
|
return idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
olm_exports['OutboundGroupSession'] = OutboundGroupSession;
|
|
@ -12,9 +12,6 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
#include "crypto-algorithms/sha256.c"
|
|
||||||
#include "crypto-algorithms/aes.c"
|
|
||||||
#include "curve25519-donna/curve25519-donna.c"
|
|
||||||
#define select ed25519_select
|
#define select ed25519_select
|
||||||
#include "ed25519/src/fe.c"
|
#include "ed25519/src/fe.c"
|
||||||
#include "ed25519/src/sc.c"
|
#include "ed25519/src/sc.c"
|
|
@ -231,9 +231,7 @@ static size_t _decrypt(
|
||||||
return (size_t)-1;
|
return (size_t)-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!decoded_results.has_message_index || !decoded_results.session_id
|
if (!decoded_results.has_message_index || !decoded_results.ciphertext ) {
|
||||||
|| !decoded_results.ciphertext
|
|
||||||
) {
|
|
||||||
session->last_error = OLM_BAD_MESSAGE_FORMAT;
|
session->last_error = OLM_BAD_MESSAGE_FORMAT;
|
||||||
return (size_t)-1;
|
return (size_t)-1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -348,18 +348,15 @@ void olm::decode_one_time_key_message(
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const std::uint8_t GROUP_SESSION_ID_TAG = 012;
|
static const std::uint8_t GROUP_MESSAGE_INDEX_TAG = 010;
|
||||||
static const std::uint8_t GROUP_MESSAGE_INDEX_TAG = 020;
|
static const std::uint8_t GROUP_CIPHERTEXT_TAG = 022;
|
||||||
static const std::uint8_t GROUP_CIPHERTEXT_TAG = 032;
|
|
||||||
|
|
||||||
size_t _olm_encode_group_message_length(
|
size_t _olm_encode_group_message_length(
|
||||||
size_t group_session_id_length,
|
|
||||||
uint32_t message_index,
|
uint32_t message_index,
|
||||||
size_t ciphertext_length,
|
size_t ciphertext_length,
|
||||||
size_t mac_length
|
size_t mac_length
|
||||||
) {
|
) {
|
||||||
size_t length = VERSION_LENGTH;
|
size_t length = VERSION_LENGTH;
|
||||||
length += 1 + varstring_length(group_session_id_length);
|
|
||||||
length += 1 + varint_length(message_index);
|
length += 1 + varint_length(message_index);
|
||||||
length += 1 + varstring_length(ciphertext_length);
|
length += 1 + varstring_length(ciphertext_length);
|
||||||
length += mac_length;
|
length += mac_length;
|
||||||
|
@ -369,19 +366,14 @@ size_t _olm_encode_group_message_length(
|
||||||
|
|
||||||
size_t _olm_encode_group_message(
|
size_t _olm_encode_group_message(
|
||||||
uint8_t version,
|
uint8_t version,
|
||||||
const uint8_t *session_id,
|
|
||||||
size_t session_id_length,
|
|
||||||
uint32_t message_index,
|
uint32_t message_index,
|
||||||
size_t ciphertext_length,
|
size_t ciphertext_length,
|
||||||
uint8_t *output,
|
uint8_t *output,
|
||||||
uint8_t **ciphertext_ptr
|
uint8_t **ciphertext_ptr
|
||||||
) {
|
) {
|
||||||
std::uint8_t * pos = output;
|
std::uint8_t * pos = output;
|
||||||
std::uint8_t * session_id_pos;
|
|
||||||
|
|
||||||
*(pos++) = version;
|
*(pos++) = version;
|
||||||
pos = encode(pos, GROUP_SESSION_ID_TAG, session_id_pos, session_id_length);
|
|
||||||
std::memcpy(session_id_pos, session_id, session_id_length);
|
|
||||||
pos = encode(pos, GROUP_MESSAGE_INDEX_TAG, message_index);
|
pos = encode(pos, GROUP_MESSAGE_INDEX_TAG, message_index);
|
||||||
pos = encode(pos, GROUP_CIPHERTEXT_TAG, *ciphertext_ptr, ciphertext_length);
|
pos = encode(pos, GROUP_CIPHERTEXT_TAG, *ciphertext_ptr, ciphertext_length);
|
||||||
return pos-output;
|
return pos-output;
|
||||||
|
@ -396,8 +388,6 @@ void _olm_decode_group_message(
|
||||||
std::uint8_t const * end = input + input_length - mac_length;
|
std::uint8_t const * end = input + input_length - mac_length;
|
||||||
std::uint8_t const * unknown = nullptr;
|
std::uint8_t const * unknown = nullptr;
|
||||||
|
|
||||||
results->session_id = nullptr;
|
|
||||||
results->session_id_length = 0;
|
|
||||||
bool has_message_index = false;
|
bool has_message_index = false;
|
||||||
results->message_index = 0;
|
results->message_index = 0;
|
||||||
results->ciphertext = nullptr;
|
results->ciphertext = nullptr;
|
||||||
|
@ -408,10 +398,6 @@ void _olm_decode_group_message(
|
||||||
results->version = *(pos++);
|
results->version = *(pos++);
|
||||||
|
|
||||||
while (pos != end) {
|
while (pos != end) {
|
||||||
pos = decode(
|
|
||||||
pos, end, GROUP_SESSION_ID_TAG,
|
|
||||||
results->session_id, results->session_id_length
|
|
||||||
);
|
|
||||||
pos = decode(
|
pos = decode(
|
||||||
pos, end, GROUP_MESSAGE_INDEX_TAG,
|
pos, end, GROUP_MESSAGE_INDEX_TAG,
|
||||||
results->message_index, has_message_index
|
results->message_index, has_message_index
|
||||||
|
|
|
@ -187,7 +187,7 @@ static size_t raw_message_length(
|
||||||
mac_length = megolm_cipher->ops->mac_length(megolm_cipher);
|
mac_length = megolm_cipher->ops->mac_length(megolm_cipher);
|
||||||
|
|
||||||
return _olm_encode_group_message_length(
|
return _olm_encode_group_message_length(
|
||||||
GROUP_SESSION_ID_LENGTH, session->ratchet.counter,
|
session->ratchet.counter,
|
||||||
ciphertext_length, mac_length);
|
ciphertext_length, mac_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +220,6 @@ static size_t _encrypt(
|
||||||
*/
|
*/
|
||||||
message_length = _olm_encode_group_message(
|
message_length = _olm_encode_group_message(
|
||||||
OLM_PROTOCOL_VERSION,
|
OLM_PROTOCOL_VERSION,
|
||||||
session->session_id, GROUP_SESSION_ID_LENGTH,
|
|
||||||
session->ratchet.counter,
|
session->ratchet.counter,
|
||||||
ciphertext_length,
|
ciphertext_length,
|
||||||
buffer,
|
buffer,
|
||||||
|
|
|
@ -67,12 +67,8 @@ assert_equals(message2, output, 35);
|
||||||
|
|
||||||
TestCase test_case("Group message encode test");
|
TestCase test_case("Group message encode test");
|
||||||
|
|
||||||
const uint8_t session_id[] = "sessionid";
|
size_t length = _olm_encode_group_message_length(200, 10, 8);
|
||||||
size_t session_id_len = 9;
|
size_t expected_length = 1 + (1+2) + (2+10) + 8;
|
||||||
|
|
||||||
size_t length = _olm_encode_group_message_length(
|
|
||||||
session_id_len, 200, 10, 8);
|
|
||||||
size_t expected_length = 1 + (2+session_id_len) + (1+2) + (2+10) + 8;
|
|
||||||
assert_equals(expected_length, length);
|
assert_equals(expected_length, length);
|
||||||
|
|
||||||
uint8_t output[50];
|
uint8_t output[50];
|
||||||
|
@ -80,7 +76,6 @@ assert_equals(message2, output, 35);
|
||||||
|
|
||||||
_olm_encode_group_message(
|
_olm_encode_group_message(
|
||||||
3,
|
3,
|
||||||
session_id, session_id_len,
|
|
||||||
200, // counter
|
200, // counter
|
||||||
10, // ciphertext length
|
10, // ciphertext length
|
||||||
output,
|
output,
|
||||||
|
@ -89,9 +84,8 @@ assert_equals(message2, output, 35);
|
||||||
|
|
||||||
uint8_t expected[] =
|
uint8_t expected[] =
|
||||||
"\x03"
|
"\x03"
|
||||||
"\x0A\x09sessionid"
|
"\x08\xC8\x01"
|
||||||
"\x10\xC8\x01"
|
"\x12\x0A";
|
||||||
"\x1A\x0A";
|
|
||||||
|
|
||||||
assert_equals(expected, output, sizeof(expected)-1);
|
assert_equals(expected, output, sizeof(expected)-1);
|
||||||
assert_equals(output+sizeof(expected)-1, ciphertext_ptr);
|
assert_equals(output+sizeof(expected)-1, ciphertext_ptr);
|
||||||
|
@ -103,17 +97,12 @@ assert_equals(message2, output, 35);
|
||||||
struct _OlmDecodeGroupMessageResults results;
|
struct _OlmDecodeGroupMessageResults results;
|
||||||
std::uint8_t message[] =
|
std::uint8_t message[] =
|
||||||
"\x03"
|
"\x03"
|
||||||
"\x0A\x09sessionid"
|
"\x08\xC8\x01"
|
||||||
"\x10\xC8\x01"
|
"\x12\x0A" "ciphertext"
|
||||||
"\x1A\x0A" "ciphertext"
|
|
||||||
"hmacsha2";
|
"hmacsha2";
|
||||||
|
|
||||||
const uint8_t expected_session_id[] = "sessionid";
|
|
||||||
|
|
||||||
_olm_decode_group_message(message, sizeof(message)-1, 8, &results);
|
_olm_decode_group_message(message, sizeof(message)-1, 8, &results);
|
||||||
assert_equals(std::uint8_t(3), results.version);
|
assert_equals(std::uint8_t(3), results.version);
|
||||||
assert_equals(std::size_t(9), results.session_id_length);
|
|
||||||
assert_equals(expected_session_id, results.session_id, 9);
|
|
||||||
assert_equals(1, results.has_message_index);
|
assert_equals(1, results.has_message_index);
|
||||||
assert_equals(std::uint32_t(200), results.message_index);
|
assert_equals(std::uint32_t(200), results.message_index);
|
||||||
assert_equals(std::size_t(10), results.ciphertext_length);
|
assert_equals(std::size_t(10), results.ciphertext_length);
|
||||||
|
|
Loading…
Reference in a new issue