summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/catlfish.erl9
-rwxr-xr-xtools/merge.py49
-rwxr-xr-xverifycert.erl41
3 files changed, 92 insertions, 7 deletions
diff --git a/src/catlfish.erl b/src/catlfish.erl
index ed75495..d98e741 100644
--- a/src/catlfish.erl
+++ b/src/catlfish.erl
@@ -5,7 +5,7 @@
-export([add_chain/3, entries/2, entry_and_proof/2]).
-export([known_roots/0, update_known_roots/0]).
-export([init_cache_table/0]).
--export([entryhash_from_entry/1]).
+-export([entryhash_from_entry/1, chain_from_entry/1]).
-include_lib("eunit/include/eunit.hrl").
-define(PROTOCOL_VERSION, 0).
@@ -252,7 +252,7 @@ deserialise_extra_data(ExtraData) ->
[E | deserialise_extra_data(Rest)]
end.
-entryhash_from_entry(Entry) ->
+chain_from_entry(Entry) ->
{MTLText, ExtraDataPacked} = unpack_entry(Entry),
{ExtraData, <<>>} = decode_tls_vector(ExtraDataPacked, 3),
MTL = deserialise_mtl(MTLText),
@@ -266,7 +266,10 @@ entryhash_from_entry(Entry) ->
precert_entry ->
Chain
end,
- crypto:hash(sha256, Data).
+ Data.
+
+entryhash_from_entry(Entry) ->
+ crypto:hash(sha256, chain_from_entry(Entry)).
%% Private functions.
-spec unpack_entry(binary()) -> {binary(), binary()}.
diff --git a/tools/merge.py b/tools/merge.py
index f9c93d9..ce3bf0b 100755
--- a/tools/merge.py
+++ b/tools/merge.py
@@ -16,9 +16,11 @@ import hashlib
import urlparse
import os
import yaml
+import select
+import struct
from certtools import build_merkle_tree, create_sth_signature, \
check_sth_signature, get_eckey_from_file, timing_point, http_request, \
- get_public_key_from_file
+ get_public_key_from_file, get_leaf_hash, decode_certificate_chain
parser = argparse.ArgumentParser(description="")
parser.add_argument('--config', help="System configuration", required=True)
@@ -61,7 +63,7 @@ def write_chain(key, value):
try:
os.makedirs(path)
except Exception, e:
- print e
+ pass
else:
path = chainsdir
f = open(path + "/" + filename, "w")
@@ -131,7 +133,7 @@ def sendlog(node, baseurl, submission):
return json.loads(result)
except urllib2.HTTPError, e:
print "ERROR: sendlog", e.read()
- sys.exit(1)
+ return None
except ValueError, e:
print "==== FAILED REQUEST ===="
print submission
@@ -206,6 +208,32 @@ for storagenode in storagenodes:
new_entries.update(new_entries_per_node[storagenode["name"]])
entries_to_fetch[storagenode["name"]] = []
+def unpack_entry(entry):
+ pieces = []
+ while len(entry):
+ (length,) = struct.unpack(">I", entry[0:4])
+ data = entry[4:4+length]
+ entry = entry[4+length:]
+ pieces.append(data)
+ return pieces
+
+import subprocess
+
+def verify_entry(verifycert, entry, hash):
+ unpacked = unpack_entry(entry)
+ mtl = unpacked[0]
+ assert hash == get_leaf_hash(mtl)
+ s = struct.pack(">I", len(entry)) + entry
+ verifycert.stdin.write(s)
+ result_length_packed = verifycert.stdout.read(4)
+ (result_length,) = struct.unpack(">I", result_length_packed)
+ result = verifycert.stdout.read(result_length)
+ assert len(result) == result_length
+ (error_code,) = struct.unpack("B", result[0:1])
+ if error_code != 0:
+ print >>sys.stderr, result[1:]
+ sys.exit(1)
+
timing_point(timing, "get new entries")
new_entries -= certsinlog
@@ -221,6 +249,8 @@ for hash in new_entries:
entries_to_fetch[storagenode["name"]].append(hash)
break
+verifycert = subprocess.Popen(["../verifycert.erl"],
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE)
added_entries = 0
for storagenode in storagenodes:
@@ -229,6 +259,7 @@ for storagenode in storagenodes:
entries = get_entries(storagenode["name"], "https://%s/" % storagenode["address"], chunk)
for hash in chunk:
entry = entries[hash]
+ verify_entry(verifycert, entry, hash)
write_chain(hash, entry)
add_to_logorder(hash)
logorder.append(hash)
@@ -237,6 +268,8 @@ for storagenode in storagenodes:
timing_point(timing, "add entries")
print "added", added_entries, "entries"
+verifycert.communicate(struct.pack("I", 0))
+
tree = build_merkle_tree(logorder)
tree_size = len(logorder)
root_hash = tree[-1][0]
@@ -277,7 +310,15 @@ for frontendnode in frontendnodes:
print "current position", curpos
entries = [base64.b64encode(entry) for entry in logorder[curpos:]]
for chunk in chunks(entries, 1000):
- sendlogresult = sendlog(nodename, nodeaddress, {"start": curpos, "hashes": chunk})
+ for trynumber in range(5, 0, -1):
+ sendlogresult = sendlog(nodename, nodeaddress, {"start": curpos, "hashes": chunk})
+ if sendlogresult == None:
+ if trynumber == 1:
+ sys.exit(1)
+ select.select([], [], [], 10.0)
+ print "tries left:", trynumber
+ continue
+ break
if sendlogresult["result"] != "ok":
print "sendlog:", sendlogresult
sys.exit(1)
diff --git a/verifycert.erl b/verifycert.erl
new file mode 100755
index 0000000..b9a3753
--- /dev/null
+++ b/verifycert.erl
@@ -0,0 +1,41 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+%%! -pa ebin -pa lib/catlfish-0.6.0-dev.ez/catlfish-0.6.0-dev/ebin -pa lib/lager-2.1.1.ez/lager-2.1.1/ebin
+
+write_reply(Bin) ->
+ Length = size(Bin),
+ file:write(standard_io, <<Length:32, Bin/binary>>).
+
+verify(RootCerts, DBEntry) ->
+ try
+ Chain = catlfish:chain_from_entry(DBEntry),
+ %% XXX: doesn't verify that MTL is derived from Chain
+ case x509:normalise_chain(RootCerts, Chain) of
+ {ok, _} ->
+ write_reply(<<0:8>>);
+ {error, Reason} ->
+ ReasonBin = list_to_binary(io_lib:format("~p", [Reason])),
+ write_reply(<<1:8, ReasonBin/binary>>)
+ end
+ catch
+ Type:What ->
+ [CrashFunction | Stack] = erlang:get_stacktrace(),
+ ErrorBin = list_to_binary(io_lib:format("Crash: ~p ~p~n~p~n~p~n", [Type, What, CrashFunction, Stack])),
+ write_reply(<<2:8, ErrorBin/binary>>)
+ end.
+
+loop(RootCerts) ->
+ {ok, LengthBin} = file:read(standard_io, 4),
+ <<Length:32>> = list_to_binary(LengthBin),
+ case Length of
+ 0 ->
+ none;
+ _ ->
+ {ok, DBEntry} = file:read(standard_io, Length),
+ verify(RootCerts, list_to_binary(DBEntry)),
+ loop(RootCerts)
+ end.
+
+main(_) ->
+ Certs = x509:read_pemfiles_from_dir("tests/known_roots/"),
+ loop(Certs).