Browse Source

inital commit

master
Benedikt Kristinsson 6 years ago
commit
759b892691
  1. 64
      .gitignore
  2. 29
      README.md
  3. 23
      messages.py
  4. 10
      network.py
  5. 7
      node.py
  6. 33
      proof.py
  7. 35
      tests/test_messages.py

64
.gitignore

@ -0,0 +1,64 @@
*~
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
# Sphinx documentation
docs/_build/
# PyBuilder
target/
#Ipython Notebook
.ipynb_checkpoints

29
README.md

@ -0,0 +1,29 @@
# NameChain (Proof of Concept)
This is meant to be a proof-of-concept implementation of NameChain, a peer-to-peer verification of HTTP/SSL sites instead of the traditional CA system.
A blockchain may or may not be involved.
## Brief protocol explenation
I feel that it is pointless to list out the flaws of the CA system. This is not the place to do that, this is merely an exploration of an alternative idea.
The idea is based on both first-trust (like SSH) and the idea that a HTTPS/SSL server should be serving the same SSL certificates to any clients on the internet.
When a new HTTP/SSL site is "found" (mined?), the certificate fingerprint is broadcast through the p2p network, along with a proof-of-check. Each participant in the p2p network will (with a random probability) then check the fingerprint and given proof-of-check. The probability that each given client will perform the check is determined by the number of participants in the network, to avoid hugging new sites to death.
A new site can be found by anyone, not just it's owner. However, clients probably shouldn't automatically send out any new sites they encounter, as that would tell the whole network what this user is looking at.
Sites could be manually entered into the system, but that seems tedious. Consider this an open practical question.
Depending on the scale, nodes in the p2p network might not be able to hold the full NameChain.
## proof-of-check
Based on the concept of proof-of-work in Bitcoin.
```
proof-of-check = HMAC(key=node_id, url || ssl_fingerprint)
```
Proves that a node opened an SSL connection to a server at `url` and read the fingerprint. To verify, a node will connect to `url` and read the fingerprint byt itself, then calculate the HMAC to verify that it saw the same certificate that the node given by `nodeid` did.

23
messages.py

@ -0,0 +1,23 @@
import hmac
import json
import node
def create_hello(nodeid, version):
msg = {'nodeid': nodeid,
'versyion': version}
sign = hmac.new(nodeid, json.dumps(msg))
envelope = {'msg': msg,
'sign': sign.hexdigest()}
return json.dumps(envelope)
def validate_hello(envelope):
envelope = json.loads(envelope)
nodeid = str(envelope['msg']['nodeid'])
signature = str(envelope['sign'])
msg = json.dumps(envelope['msg'])
verify_sign = hmac.new(nodeid, msg)
return hmac.compare_digest(verify_sign.hexdigest(), signature)

10
network.py

@ -0,0 +1,10 @@
from twisted.internet import protocol, reator, endpoints
import messages
import node
my_nodeid = node.generate_nodeid()
class NCServer(protocol.Protocol):
def dataReceived(self, data):

7
node.py

@ -0,0 +1,7 @@
import os
import hashlib
bootstrap_list = ["localhost:5005"]
def generate_nodeid():
return hashlib.sha256(os.urandom(256/8)).hexdigest()

33
proof.py

@ -0,0 +1,33 @@
import ssl
from hashlib import sha256
class ProofError(Exception):
pass
def get_https_fingerprint(url):
pem = ssl.get_server_certificate((url, 443), ca_certs=None)
# not the correct way to get fingerprint
return sha256(pem).hexdigest()
def construct_proof(url, fingerprint, nodeid):
"""The variable `nodeid` serves as a nonce and should perhaps be
substituted for a random integer?
The idea here is to calculate a unique hash that requires actually
checking the server certificate.
"""
return sha256(fingerprint + url + nodeid).hexdigest()
def proof_of_check(url, nodeid):
fingerprint = get_https_fingerprint(url)
proof = construct_proof(url, fingerprint, nodeid)
return fingerprint, proof
def verify_proof(url, fingerprint, nodeid, claimed_proof):
if get_https_fingerprint(url) != fingerprint:
return False
elif construct_proof(url, fingerprint, nodeid) != claimed_proof:
return False
else:
return True

35
tests/test_messages.py

@ -0,0 +1,35 @@
import sys
sys.path.append("..")
import unittest
import proof
import messages
import node
class TestMessages(unittest.TestCase):
def setUp(self):
self.nodeid = node.generate_nodeid()
def tearDown(self):
pass
def test_hello(self):
hello = messages.create_hello(self.nodeid, 0)
self.assertTrue(messages.validate_hello(hello))
class TestProof(unittest.TestCase):
def setUp(self):
self.nodeid = node.generate_nodeid()
def tearDown(self):
pass
def test_proof(self):
for url in ["sudo.is", "lokun.is", "microsoft.com"]:
pocheck = proof.proof_of_check(url, self.nodeid)
ver = proof.verify_proof(url, pocheck[0], self.nodeid, pocheck[1])
self.assertTrue(ver)
if __name__ == "__main__":
unittest.main()
Loading…
Cancel
Save