Merge branch 'master' of github.com:research/chocolate

This commit is contained in:
Peter Eckersley
2012-07-07 13:13:13 -07:00
10 changed files with 58 additions and 24 deletions
+1 -18
View File
@@ -2,23 +2,6 @@ In this directory are tools that will run on webservers for sysadmins to
automatically obtain their certs automatically obtain their certs
Set CHOCOLATESERVER environment variable for "make deploy" and client.py! Set CHOCOLATESERVER environment variable for client.py!
chocolate.py - server-side, requires web.py (python-webpy),
PyCrypto (python-crypto), redis, python-redis, python-protobuf,
python-nss
probably wants to run under a web server like lighttpd with fastcgi
client.py - experimental tool for making requests and parsing replies client.py - experimental tool for making requests and parsing replies
chocolate_protocol.proto - protocol definition; needs protobuf-compiler
sni_challenge -
Assumes Apache server with name based virtual hosts is running
(for intended address).
Call perform_sni_cert_challenge(address, r, nonce) to do the whole
challenge.
Example code is given in main method
Right now requires full path specification of CSR/KEY in the Global
Variables (how should this be specified?)
@@ -3,6 +3,7 @@ deploy: chocolate_protocol_pb2.py chocolate.py CSR.py pkcs10.py daemon.py
chocolate_protocol_pb2.py: chocolate_protocol.proto chocolate_protocol_pb2.py: chocolate_protocol.proto
protoc chocolate_protocol.proto --python_out=. protoc chocolate_protocol.proto --python_out=.
cp -p chocolate_protocol_pb2.py ../client-webserver/
clean: clean:
rm -f *.pyc rm -f *.pyc
+20
View File
@@ -1,2 +1,22 @@
In this directory is a reference CA implementation of the Chocolate protocol, In this directory is a reference CA implementation of the Chocolate protocol,
DV and signing mechanism. DV and signing mechanism.
Set CHOCOLATESERVER environment variable for "make deploy"!
chocolate.py - server-side, requires web.py (python-webpy),
PyCrypto (python-crypto) 2.3 (not 2.1!!), redis, python-redis,
python-protobuf, python-nss
probably wants to run under a web server like lighttpd with fastcgi
chocolate_protocol.proto - protocol definition; needs protobuf-compiler
sni_challenge -
Assumes Apache server with name based virtual hosts is running
(for intended address).
Call perform_sni_cert_challenge(address, r, nonce) to do the whole
challenge.
Example code is given in main method
Right now requires full path specification of CSR/KEY in the Global
Variables (how should this be specified?)
@@ -3,6 +3,7 @@
import web, redis, time import web, redis, time
import CSR import CSR
from Crypto.Hash import SHA256, HMAC from Crypto.Hash import SHA256, HMAC
from Crypto.PublicKey import RSA
from Crypto import Random from Crypto import Random
from chocolate_protocol_pb2 import chocolatemessage from chocolate_protocol_pb2 import chocolatemessage
from google.protobuf.message import DecodeError from google.protobuf.message import DecodeError
@@ -102,6 +103,10 @@ class session(object):
"""Has there already been a signing request made in this session?""" """Has there already been a signing request made in this session?"""
return sessions.hget(self.id, "state") is not None return sessions.hget(self.id, "state") is not None
def pubkey(self):
"""Return the PEM-formatted subject public key from the CSR."""
return CSR.pubkey(sessions.hget(self.id, "csr"))
def cert(self): def cert(self):
"""Return the issued certificate.""" """Return the issued certificate."""
return sessions.hget(self.id, "cert") return sessions.hget(self.id, "cert")
@@ -276,8 +281,12 @@ class session(object):
chall.type = int(c["type"]) chall.type = int(c["type"])
chall.name = c["name"] chall.name = c["name"]
chall.succeeded = (c["satisfied"] == "True") # TODO: this contradicts comment in protocol about meaning of "succeeded" chall.succeeded = (c["satisfied"] == "True") # TODO: this contradicts comment in protocol about meaning of "succeeded"
chall.data.append(c["dvsni:r"]) # Calculate y
dvsni_r = c["dvsni:r"]
y = RSA.importKey(self.pubkey()).encrypt(dvsni_r, None)[0]
# In dvsni, we send nonce, y, ext
chall.data.append(c["dvsni:nonce"]) chall.data.append(c["dvsni:nonce"])
chall.data.append(y)
chall.data.append(c["dvsni:ext"]) chall.data.append(c["dvsni:ext"])
def POST(self): def POST(self):
@@ -26,7 +26,7 @@
# If the client never checks in, the daemon can keep advancing # If the client never checks in, the daemon can keep advancing
# the request's state, which may not be the right behavior. # the request's state, which may not be the right behavior.
import redis, time import redis, time, CSR
r = redis.Redis() r = redis.Redis()
from Crypto.Hash import SHA256, HMAC from Crypto.Hash import SHA256, HMAC
@@ -132,8 +132,10 @@ def issue(session):
return return
# Note that we can push this back into the original queue. # Note that we can push this back into the original queue.
# TODO: need to add a way to make sure we don't test the same # TODO: need to add a way to make sure we don't test the same
# TODO: actually issue the cert # TODO: actually make this call issue the cert
r.hset(session, "cert", "----ISSUED CERT GOES HERE----") csr = r.hget(session, "csr")
cert = CSR.issue(csr)
r.hset(session, "cert", cert)
if False: # once issuing cert succeeded if False: # once issuing cert succeeded
r.hset(session, "state", "done") r.hset(session, "state", "done")
r.lpush("pending-done", session) r.lpush("pending-done", session)
@@ -3,6 +3,7 @@ from Crypto import Random
import sni_support import sni_support
import hmac import hmac
import hashlib import hashlib
import binascii
S_SIZE = 32 S_SIZE = 32
NONCE_SIZE = 32 NONCE_SIZE = 32
@@ -15,6 +16,13 @@ def byteToHex(byteStr):
return ''.join(["%02X" % ord(x) for x in byteStr]).strip() return ''.join(["%02X" % ord(x) for x in byteStr]).strip()
def check_challenge_value(ext_value, r): def check_challenge_value(ext_value, r):
"""
Checks that a challenge response actually passes the challenge
ext_value: string returned by client-webserver's X.509 cert
chocolate extension
r: secret random key (binary string) chosen by server-CA
"""
s = ext_value[0:S_SIZE] s = ext_value[0:S_SIZE]
mac = ext_value[S_SIZE:] mac = ext_value[S_SIZE:]
expected_mac = hmac.new(r, str(s), hashlib.sha256).digest() expected_mac = hmac.new(r, str(s), hashlib.sha256).digest()
@@ -30,6 +38,17 @@ def check_challenge_value(ext_value, r):
return False return False
def verify_challenge(address, r, nonce): def verify_challenge(address, r, nonce):
"""
Verifies an SNI challenge at address (assumes port 443)
address: string host (e.g. "127.0.0.1")
r: secret random key (binary string)
nonce: ascii string of nonce (e.g. "66f58cfb...")
returns (result, reason)
result: True/False for passed/failed verification
reason: Human-readable string describing reason for result
"""
sni_name = nonce + ".chocolate" sni_name = nonce + ".chocolate"
context = M2Crypto.SSL.Context() context = M2Crypto.SSL.Context()
@@ -72,7 +91,7 @@ def main():
r = Random.get_random_bytes(NONCE_SIZE) r = Random.get_random_bytes(NONCE_SIZE)
r = "testValueForR" r = "testValueForR"
encryptedValue = testkey.encrypt(r, 0) encryptedValue = testkey.encrypt(r, 0)
valid, response = verify_challenge("127.0.0.1", r, nonce) valid, response = verify_challenge("127.0.0.1", r, binascii.hexlify(nonce))
print response print response
if __name__ == "__main__": if __name__ == "__main__":