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

This commit is contained in:
Peter Eckersley 2012-07-07 13:13:13 -07:00
commit 93cec72f7a
10 changed files with 58 additions and 24 deletions

View file

@ -2,23 +2,6 @@ In this directory are tools that will run on webservers for sysadmins to
automatically obtain their certs
Set CHOCOLATESERVER environment variable for "make deploy" and 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
Set CHOCOLATESERVER environment variable for client.py!
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?)

View file

@ -3,6 +3,7 @@ deploy: chocolate_protocol_pb2.py chocolate.py CSR.py pkcs10.py daemon.py
chocolate_protocol_pb2.py: chocolate_protocol.proto
protoc chocolate_protocol.proto --python_out=.
cp -p chocolate_protocol_pb2.py ../client-webserver/
clean:
rm -f *.pyc

View file

@ -1,2 +1,22 @@
In this directory is a reference CA implementation of the Chocolate protocol,
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?)

View file

@ -3,7 +3,8 @@
import web, redis, time
import CSR
from Crypto.Hash import SHA256, HMAC
from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto import Random
from chocolate_protocol_pb2 import chocolatemessage
from google.protobuf.message import DecodeError
@ -102,6 +103,10 @@ class session(object):
"""Has there already been a signing request made in this session?"""
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):
"""Return the issued certificate."""
return sessions.hget(self.id, "cert")
@ -276,8 +281,12 @@ class session(object):
chall.type = int(c["type"])
chall.name = c["name"]
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(y)
chall.data.append(c["dvsni:ext"])
def POST(self):

View file

@ -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
import redis, time, CSR
r = redis.Redis()
from Crypto.Hash import SHA256, HMAC
@ -132,8 +132,10 @@ def issue(session):
return
# 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: actually issue the cert
r.hset(session, "cert", "----ISSUED CERT GOES HERE----")
# TODO: actually make this call issue the cert
csr = r.hget(session, "csr")
cert = CSR.issue(csr)
r.hset(session, "cert", cert)
if False: # once issuing cert succeeded
r.hset(session, "state", "done")
r.lpush("pending-done", session)

View file

@ -3,6 +3,7 @@ from Crypto import Random
import sni_support
import hmac
import hashlib
import binascii
S_SIZE = 32
NONCE_SIZE = 32
@ -15,6 +16,13 @@ def byteToHex(byteStr):
return ''.join(["%02X" % ord(x) for x in byteStr]).strip()
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]
mac = ext_value[S_SIZE:]
expected_mac = hmac.new(r, str(s), hashlib.sha256).digest()
@ -30,6 +38,17 @@ def check_challenge_value(ext_value, r):
return False
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"
context = M2Crypto.SSL.Context()
@ -72,7 +91,7 @@ def main():
r = Random.get_random_bytes(NONCE_SIZE)
r = "testValueForR"
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
if __name__ == "__main__":