From f9078993583af856a485408d0e4a6df640d332c0 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Thu, 12 Jul 2012 12:38:13 -0700 Subject: [PATCH 1/5] slight tolerance for requests timestamped in the future --- server-ca/chocolate.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server-ca/chocolate.py b/server-ca/chocolate.py index 9790defb6..c57265e7b 100755 --- a/server-ca/chocolate.py +++ b/server-ca/chocolate.py @@ -211,8 +211,11 @@ class session(object): if not all([safe("recipient", recipient), safe("csr", csr)]): self.die(r, r.BadRequest, uri="https://ca.example.com/failures/illegalcharacter") return - if timestamp > time.time() or time.time() - timestamp > 100: - self.die(r, r.BadRequest, uri="https://ca.example.com/failures/time") + if timestamp - time.time() > 5: + self.die(r, r.BadRequest, uri="https://ca.example.com/failures/future") + return + if time.time() - timestamp > 100: + self.die(r, r.BadRequest, uri="https://ca.example.com/failures/past") return if recipient != "ca.example.com": self.die(r, r.BadRequest, uri="https://ca.example.com/failures/recipient") From 49d70c0966d05ceaaec5f3efd6795f3355522dc9 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Thu, 12 Jul 2012 12:39:54 -0700 Subject: [PATCH 2/5] it's fine to use M2Crypto, but you must import it :-) --- server-ca/chocolate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-ca/chocolate.py b/server-ca/chocolate.py index c57265e7b..ca8120989 100755 --- a/server-ca/chocolate.py +++ b/server-ca/chocolate.py @@ -4,7 +4,7 @@ import web, redis, time import CSR import hashlib import hmac -from Crypto.PublicKey import RSA +import M2Crypto from Crypto import Random from chocolate_protocol_pb2 import chocolatemessage from google.protobuf.message import DecodeError From d44135571514d3b1931aee1766977573bee3e6e7 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Thu, 12 Jul 2012 14:30:56 -0700 Subject: [PATCH 3/5] make daemon exit cleanly after interrupt signals --- server-ca/daemon.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/server-ca/daemon.py b/server-ca/daemon.py index 289a6c47d..5126a5868 100644 --- a/server-ca/daemon.py +++ b/server-ca/daemon.py @@ -26,7 +26,7 @@ # If the client never checks in, the daemon can keep advancing # the request's state, which may not be the right behavior. -import redis, time, CSR, sys +import redis, time, CSR, sys, signal r = redis.Redis() from sni_challenge.verify import verify_challenge @@ -34,6 +34,14 @@ from Crypto.Hash import SHA256, HMAC from Crypto import Random debug = "debug" in sys.argv +clean_shutdown = False + +def signal_handler(a, b): + global clean_shutdown + clean_shutdown = True + +signal.signal(signal.SIGTERM, signal_handler) +signal.signal(signal.SIGINT, signal_handler) def sha256(m): return SHA256.new(m).hexdigest() @@ -190,6 +198,7 @@ def issue(session): r.lpush("pending-issue", session) while True: + if clean_shutdown: break session = r.rpop("pending-makechallenge") if session: if debug: print "going to makechallenge for", session From b8a814a13f00ca2148e0d00625d9dc6b9ce5a940 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Thu, 12 Jul 2012 14:36:39 -0700 Subject: [PATCH 4/5] make client use M2Crypto also --- client-webserver/client.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/client-webserver/client.py b/client-webserver/client.py index a1f4c8743..d8a9367d8 100644 --- a/client-webserver/client.py +++ b/client-webserver/client.py @@ -2,6 +2,7 @@ from chocolate_protocol_pb2 import chocolatemessage from Crypto.Hash import SHA256 +import M2Crypto import urllib2, os, sys, time, random, CSR def sha256(m): @@ -52,5 +53,13 @@ while r.proceed.IsInitialized(): r = decode(do(k)) print r -for chall in r.challenges: +sni_todo = [] +for chall in r.challenge: print chall + if chall.type == r.DomainValidateSNI: + key = M2Crypto.RSA.load_key_string(open("key.pem").read()) + dvsni_nonce, dvsni_y, dvsni_ext = chall.data + dvsni_r = key.private_decrypt(dvsni_y, M2Crypto.RSA.pkcs1_oaep_padding) + sni_todo.append( (chall.name, dvsni_nonce, dvsni_r) ) + +print sni_todo From 6d64bab45e841f1fa6c841ad3f8a34318482aa55 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Thu, 12 Jul 2012 14:48:32 -0700 Subject: [PATCH 5/5] wow, but M2Crypto is annoying! - make a BIO for the public key It turns out that M2Crypto.RSA.load_key_string() requires a keypair, not a public key. There is no M2Crypto.RSA.load_pub_key_string(), only M2Crypto.RSA.load_pub_key_bio(), which requires an OpenSSL BIO object. --- server-ca/chocolate.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server-ca/chocolate.py b/server-ca/chocolate.py index ca8120989..781341779 100755 --- a/server-ca/chocolate.py +++ b/server-ca/chocolate.py @@ -303,7 +303,9 @@ class session(object): chall.succeeded = (c["satisfied"] == "True") # TODO: this contradicts comment in protocol about meaning of "succeeded" # Calculate y dvsni_r = c["dvsni:r"] - pubkey = M2Crypto.RSA.load_key_string(self.pubkey()) + bio = M2Crypto.BIO.MemoryBuffer() + bio.write(self.pubkey()) + pubkey = M2Crypto.RSA.load_pub_key_bio(bio) y = pubkey.public_encrypt(dvsni_r, M2Crypto.RSA.pkcs1_oaep_padding) # In dvsni, we send nonce, y, ext chall.data.append(c["dvsni:nonce"])