Ejemplo n.º 1
0
def handshake_initiate(private_key, redis_client):
    try:
        request = expect_json_request(bottle.request, INITIATE_SCHEMA)

        symmetric_key = redis_get_cookie(
            redis_client, request[INITIATE_COOKIE_FIELD])
        cookie_sbox = SecretBox(symmetric_key)
        cookie = cookie_sbox.decrypt(
            str(request[INITIATE_COOKIE_FIELD]), encoder=Base64Encoder)

        if len(cookie) != 2 * CURVE25519_KEY_BYTES:
            bottle.response.status = HTTP_INTERNAL_SERVER_ERROR
            return {'error': 'An invalid cookie was sent to the client.'}
        client_transient_pkey = PublicKey(cookie[0:CURVE25519_KEY_BYTES])
        transient_skey = PrivateKey(cookie[CURVE25519_KEY_BYTES:])

        if request[INITIATE_CLIENT_TRANSIENT_PKEY_FIELD] != \
           client_transient_pkey.encode(Base64Encoder):
            raise InvalidClientRequest(
                'Initiate: non matching transient public keys.')

        vouch_json = open_box(request[INITIATE_VOUCH_FIELD],
                              transient_skey, client_transient_pkey)
        vouch = parse_and_verify_json(vouch_json, VOUCH_SCHEMA)

        client_pkey = PublicKey(
            str(vouch[VOUCH_CLIENT_PKEY_FIELD]), encoder=Base64Encoder)
        vouch_for_transient_pkey = open_box(
            vouch[VOUCH_TRANSIENT_KEY_BOX_FIELD], private_key, client_pkey)
        if vouch_for_transient_pkey != client_transient_pkey.encode():
            raise InvalidClientRequest(
                'Initiate: non matching transient public keys.')

        resp = 'I believe you are {} and you want {}'.format(
            client_pkey.encode(Base64Encoder), vouch[VOUCH_MESSAGE_FIELD])
        print(resp)
        response_nonce = nacl.utils.random(Box.NONCE_SIZE)
        response_box = Box(transient_skey, client_transient_pkey)
        response_box_cipher = response_box.encrypt(
            resp, response_nonce, encoder=Base64Encoder)
        return {'response': response_box_cipher}
    except jsonschema.ValidationError as e:
        log.exception(e)
        bottle.response.status = HTTP_BAD_REQUEST
        return {'error': str(e)}
    except InvalidClientRequest as e:
        log.exception(e)
        bottle.response.status = HTTP_BAD_REQUEST
        return {'error': str(e)}
    except MissingCookie as e:
        log.exception(e)
        bottle.response.status = HTTP_BAD_REQUEST
        return {'error': str(e)}
    except CryptoError as e:
        log.exception(e)
        bottle.response.status = HTTP_BAD_REQUEST
        return {'error': 'Bad encryption in handshake.'}
    return {'error': ''}
def nacl_public_PublicKey():
    bytes32 = random(size=32)

    print(len(bytes32))

    pubkey1 = PublicKey(bytes32)
    pubkey2 = PublicKey(base64.b64encode(bytes32), encoder=Base64Encoder)

    print(pubkey1.encode())
    print(pubkey2.encode())
Ejemplo n.º 3
0
def auth():
    auth_host = '10.50.8.128'
    #with open('private_key', 'rb') as f:
    #f.write('Hello\n')
    with open('private_key') as f:
        encoded_private_key = f.read()
    with open('public_key') as f:
        encoded_public_key = f.read()
    loaded_public_key = PublicKey(encoded_public_key,
                                  encoder=nacl.encoding.Base64Encoder)
    loaded_private_key = PrivateKey(encoded_private_key,
                                    encoder=nacl.encoding.Base64Encoder)
    # assert loaded_public_key.encode() == loaded_private_key.public_key.encode()
    #assert loaded_public_key.encode() == loaded_private_key.public_key.encode()
    print(loaded_public_key.encode())
    print(loaded_private_key.public_key.encode())
    private_key = loaded_private_key
    public_key = loaded_public_key

    # return 1

    # private_key = private_key_b64.decode('base64')
    # public_key = public_key_b64.decode('base64')

    # with open('public_key', 'rb') as f:
    #     f.write(encoded_public_key)
    # with open('x.py') as f: s = f.read()
    # # Generate Bob's private key, as we've done in the Box example
    # skbob = PrivateKey.generate()
    # pkbob = skbob.public_key
    #return private_key

    # Alice wishes to send a encrypted message to Bob,
    # but prefers the message to be untraceable
    #sealed_box = SealedBox(pkbob)
    sealed_box = SealedBox(private_key)

    # This is Alice's message
    message = b"Kill all kittens"

    # Encrypt the message, it will carry the ephemeral key public part
    # to let Bob decrypt it
    encrypted = sealed_box.encrypt(message)
    msg = base64.b64encode(encrypted)
    msg = encoded_public_key
    #data = {'name': 'jtest', 'ipaddr': '192.168.0.2' 'public_key': b'\x8e\x05{\xe2\xcby:\x0b\xeb\xe69\xac|\x96\xff\xa4\xdaE\x89^\xa7\xaf\x90\x83\x14)bP\x0c\n\x85l'}
    #data = {'msg': ''}
    data = {'msg': msg}
    print(data)
    #return 1
    #r = requests.post('https://stats.rchain.me:30443/auth', data = data)
    r = requests.post(f'https://{auth_host}:30443/auth', data=data)
    print(r)
    text = r.text
    content = r.content
    print(text)
    print(content)
Ejemplo n.º 4
0
    def on_post(self, req, resp):
        configuration = req.context['configuration']

        if req.content_length:
            data = json.load(req.bounded_stream)
        else:
            raise Exception("No data.")

        if 'public_key' not in data:
            raise Exception("No public key.")

        db_session = req.context['db_session']

        user_public_key = PublicKey(data['public_key'].encode('utf-8'),
                                    encoder=nacl.encoding.Base64Encoder)

        user = db_session.query(
            db.User).filter(db.User.public_key == user_public_key.encode(
                nacl.encoding.RawEncoder)).first()

        if user is None:
            raise Exception("Public key unknown.")

        challenge_answer = nacl.utils.random(Box.NONCE_SIZE)
        challenge_uuid = str(uuid.uuid4())

        box = Box(configuration['server_key_pair'], user_public_key)

        challenge_box = box.encrypt(plaintext=challenge_answer,
                                    encoder=nacl.encoding.Base64Encoder)

        challenge = db.Challenge(uuid=challenge_uuid,
                                 user=user,
                                 answer_hash=nacl.hash.sha256(
                                     challenge_answer,
                                     encoder=nacl.encoding.RawEncoder))

        db_session.add(challenge)
        db_session.commit()

        resp.status = falcon.HTTP_200
        resp.body = json.dumps({
            'uuid':
            challenge_uuid,
            'nonce':
            str(challenge_box.nonce.decode('utf-8')),
            'challenge':
            str(challenge_box.ciphertext.decode('utf-8'))
        })
Ejemplo n.º 5
0
def handshake_hello(private_key, redis_client):
    try:
        request = expect_json_request(bottle.request, HELLO_SCHEMA)
        client_transient_pkey = PublicKey(
            str(request[HELLO_CLIENT_TRANSIENT_PKEY_FIELD]), Base64Encoder)

        zeros = open_box(request[HELLO_ZEROS_BOX_FIELD],
                         private_key, client_transient_pkey)
        if len(zeros) != HELLO_PADDING_BYTES:
            raise InvalidClientRequest(
                'zeros_box should contain exactly %d bytes of padding' %
                HELLO_PADDING_BYTES)

        transient_skey = PrivateKey.generate()
        cookie_plain = client_transient_pkey.encode() + \
                       transient_skey.encode()
        cookie_nonce = nacl.utils.random(SecretBox.NONCE_SIZE)
        symmetric_key = nacl.utils.random(SecretBox.KEY_SIZE)
        cookie_sbox = SecretBox(symmetric_key)
        cookie = cookie_sbox.encrypt(
            cookie_plain, cookie_nonce, encoder=Base64Encoder)
        redis_set_cookie(redis_client, cookie, symmetric_key)

        cookie_box = Box(private_key, client_transient_pkey)
        cookie_box_nonce = nacl.utils.random(Box.NONCE_SIZE)
        server_tpkey = transient_skey.public_key.encode(Base64Encoder)
        cookie_box_cipher = cookie_box.encrypt(json.dumps({
            COOKIE_SERVER_TRANSIENT_PKEY_FIELD: server_tpkey,
            COOKIE_COOKIE_FIELD: cookie
        }), cookie_box_nonce, encoder=Base64Encoder)

        response = {COOKIE_COOKIE_BOX_FIELD: cookie_box_cipher}
        jsonschema.validate(response, COOKIE_SCHEMA)
        return response
    except jsonschema.ValidationError:
        log.exception(e)
        bottle.response.status = HTTP_INTERNAL_SERVER_ERROR
        return {'error': 'A packet with an invalid JSON schema was generated.'}
    except InvalidClientRequest as e:
        log.exception(e)
        bottle.response.status = HTTP_BAD_REQUEST
        return {'error': str(e)}
    except CryptoError as e:
        log.exception(e)
        bottle.response.status = HTTP_BAD_REQUEST
        return {'error': 'bad encryption'}
    return {'error': ''}
Ejemplo n.º 6
0
    def on_post(self, req, resp):
        configuration = req.context['configuration']

        if req.content_length:
            data = json.load(req.bounded_stream)
        else:
            raise Exception("No data.")

        if 'public_key' not in data:
            raise Exception("No public key.")

        db_session = req.context['db_session']

        user_public_key = PublicKey(data['public_key'].encode('utf-8'), encoder = nacl.encoding.Base64Encoder)

        user = db_session.query(db.User).filter(db.User.public_key == user_public_key.encode(nacl.encoding.RawEncoder)).first()

        if user is None:
            raise Exception("Public key unknown.")

        challenge_answer = nacl.utils.random(Box.NONCE_SIZE)
        challenge_uuid = str(uuid.uuid4())

        box = Box(configuration['server_key_pair'], user_public_key)

        challenge_box = box.encrypt(plaintext = challenge_answer, encoder = nacl.encoding.Base64Encoder)

        challenge = db.Challenge(
            uuid = challenge_uuid,
            user = user,
            answer_hash = nacl.hash.sha256(challenge_answer, encoder = nacl.encoding.RawEncoder)
        )

        db_session.add(challenge)
        db_session.commit()

        resp.status = falcon.HTTP_200
        resp.body = json.dumps({
            'uuid': challenge_uuid,
            'nonce': str(challenge_box.nonce.decode('utf-8')),
            'challenge': str(challenge_box.ciphertext.decode('utf-8'))
        })
Ejemplo n.º 7
0
#PKs2_full = loads('{"kty":"EC", "crv":"Curve25519", "x":""}', object_pairs_hook=OrderedDict)
#PKp2_full = loads('{"kty":"EC", "crv":"Curve25519", "x":""}', object_pairs_hook=OrderedDict)

PKp_full['x'] = PKp_b64
PKs_full['x'] = PKs_b64
#PKs2_full['x'] = PKs2_b64
#PKp2_full['x'] = PKp2_b64

## Load peer and server private keys
SK_peer = PrivateKey(bytes.fromhex(SKp))
SK_server = PrivateKey(bytes.fromhex(SKs))
#SK2_peer = PrivateKey(bytes.fromhex(SKp2))
#SK2_server = PrivateKey(bytes.fromhex(SKs2))

## Derive shared secret
Z = scalarmult(SK_peer.encode(), PK_server.encode())
assert (Z == scalarmult(SK_server.encode(), PK_peer.encode()))
#Z2 = scalarmult(SK2_peer.encode(), PK2_server.encode())
#assert(Z2 == scalarmult(SK2_server.encode(), PK2_peer.encode()))

## KDF for completion exchange. Uses NIST Concat KDF.
KDF_input = b'EAP-NOOB' + base64url_decode(Np_b64) + base64url_decode(
    Ns_b64) + base64url_decode(Noob_b64)
KDF_out = KDF(algorithm=SHA256(),
              length=320,
              otherinfo=KDF_input,
              backend=default_backend()).derive(Z)
Kms = KDF_out[224:256]
Kmp = KDF_out[256:288]
Kz = KDF_out[288:320]
Ejemplo n.º 8
0
def encode_tansmission_public_key(key: PublicKey):
    return key.encode(encoder=URLSafeBase64Encoder)
Ejemplo n.º 9
0
class Invitation:
    # This has a brief lifetime: one is created in response to the rendezvous
    # client discovering new messages for us, used for one reactor tick, then
    # dereferenced. It holds onto a few values during that tick (which may
    # process multiple messages for a single invitation, e.g. A's second poll
    # will receive both B-m1 and B-m2 together). But all persistent state
    # beyond that one tick is stored in the database.
    def __init__(self, iid, db, manager):
        self.iid = iid
        self.db = db
        self.manager = manager
        c = self.db.execute("SELECT petname, inviteID, inviteKey," # 0,1,2
                            " theirTempPubkey," # 3
                            " nextExpectedMessage," # 4
                            " myMessages," # 5
                            " theirMessages" # 6
                            " FROM invitations WHERE id = ?", (iid,))
        res = c.fetchone()
        if not res:
            raise KeyError("no pending Invitation for '%d'" % iid)
        self.petname = res[0]
        self.inviteID = res[1]
        self.inviteKey = SigningKey(res[2].decode("hex"))
        self.theirTempPubkey = None
        if res[3]:
            self.theirTempPubkey = PublicKey(res[3].decode("hex"))
        self.nextExpectedMessage = int(res[4])
        self.myMessages = splitMessages(res[5])
        self.theirMessages = splitMessages(res[6])

    def getAddressbookID(self):
        c = self.db.execute("SELECT addressbook_id FROM invitations"
                            " WHERE id = ?", (self.iid,))
        return c.fetchone()[0]

    def getMyTempPrivkey(self):
        c = self.db.execute("SELECT myTempPrivkey FROM invitations"
                            " WHERE id = ?", (self.iid,))
        return PrivateKey(c.fetchone()[0].decode("hex"))

    def getMySigningKey(self):
        c = self.db.execute("SELECT mySigningKey FROM invitations"
                            " WHERE id = ?", (self.iid,))
        return SigningKey(c.fetchone()[0].decode("hex"))

    def getMyPublicChannelRecord(self):
        c = self.db.execute("SELECT my_channel_record FROM invitations"
                            " WHERE id = ?", (self.iid,))
        return c.fetchone()[0]

    def getMyPrivateChannelData(self):
        c = self.db.execute("SELECT my_private_channel_data FROM invitations"
                            " WHERE id = ?", (self.iid,))
        return json.loads(c.fetchone()[0])


    def sendFirstMessage(self):
        pub = self.getMyTempPrivkey().public_key.encode()
        self.send("i0:m1:"+pub)
        self.db.update("UPDATE invitations SET myMessages=? WHERE id=?",
                       (",".join(self.myMessages), self.iid),
                       "invitations", self.iid)
        # that will be commited by our caller

    def processMessages(self, messages):
        # These messages are neither version-checked nor signature-checked.
        # Also, we may have already processed some of them.
        #print "processMessages", messages
        #print " my", self.myMessages
        #print " theirs", self.theirMessages
        assert isinstance(messages, set), type(messages)
        assert None not in messages, messages
        assert None not in self.myMessages, self.myMessages
        assert None not in self.theirMessages, self.theirMessages
        # Send anything that didn't make it to the server. This covers the
        # case where we commit our outbound message in send() but crash
        # before finishing delivery.
        for m in self.myMessages - messages:
            #print "resending", m
            self.manager.sendToAll(self.inviteID, m)

        newMessages = messages - self.myMessages - self.theirMessages
        #print " %d new messages" % len(newMessages)
        if not newMessages:
            print " huh, no new messages, stupid rendezvous client"

        # check signatures, extract bodies. invalid messages kill the channel
        # and the invitation. MAYBE TODO: lose the one channel, keep using
        # the others.
        bodies = set()
        for m in newMessages:
            #print " new inbound message", m
            try:
                if not m.startswith("r0:"):
                    print "unrecognized rendezvous message prefix"
                if not VALID_MESSAGE.search(m):
                    raise CorruptChannelError()
                m = m[len("r0:"):].decode("hex")
                bodies.add(self.inviteKey.verify_key.verify(m))
            except (BadSignatureError, CorruptChannelError) as e:
                print "channel %s is corrupt" % self.inviteID
                if isinstance(e, BadSignatureError):
                    print " (bad sig)"
                self.unsubscribe(self.inviteID)
                # TODO: mark invitation as failed, destroy it
                return

        #print " new inbound bodies:", ", ".join([repr(b[:10])+" ..." for b in bodies])

        # these handlers will update self.myMessages with sent messages, and
        # will increment self.nextExpectedMessage. We can handle multiple
        # (sequential) messages in a single pass.
        if self.nextExpectedMessage == 1:
            self.findPrefixAndCall("i0:m1:", bodies, self.processM1)
            # no elif here: self.nextExpectedMessage may have incremented
        if self.nextExpectedMessage == 2:
            self.findPrefixAndCall("i0:m2:", bodies, self.processM2)
        if self.nextExpectedMessage == 3:
            self.findPrefixAndCall("i0:m3:", bodies, self.processM3)

        self.db.update("UPDATE invitations SET"
                       "  myMessages=?,"
                       "  theirMessages=?,"
                       "  nextExpectedMessage=?"
                       " WHERE id=?",
                       (",".join(self.myMessages),
                        ",".join(self.theirMessages | newMessages),
                        self.nextExpectedMessage,
                        self.iid),
                       "invitations", self.iid)
        #print " db.commit"
        self.db.commit()

    def findPrefixAndCall(self, prefix, bodies, handler):
        for msg in bodies:
            if msg.startswith(prefix):
                return handler(msg[len(prefix):])
        return None

    def send(self, msg, persist=True):
        #print "send", repr(msg[:10]), "..."
        signed = "r0:%s" % self.inviteKey.sign(msg).encode("hex")
        if persist: # m4-destroy is not persistent
            self.myMessages.add(signed) # will be persisted by caller
            # This will be added to the DB, and committed, by our caller, to
            # get it into the same transaction as the update to which inbound
            # messages we've processed.
        assert VALID_MESSAGE.search(signed), signed
        self.manager.sendToAll(self.inviteID, signed)

    def processM1(self, msg):
        #print "processM1", self.petname
        self.theirTempPubkey = PublicKey(msg)
        self.db.update("UPDATE invitations SET theirTempPubkey=?"
                       " WHERE id=?",
                       (self.theirTempPubkey.encode(Hex), self.iid),
                       "invitations", self.iid)
        # theirTempPubkey will committed by our caller, in the same txn as
        # the message send

        my_privkey = self.getMyTempPrivkey()
        my_channel_record = self.getMyPublicChannelRecord()
        b = Box(my_privkey, self.theirTempPubkey)
        signedBody = b"".join([self.theirTempPubkey.encode(),
                               my_privkey.public_key.encode(),
                               my_channel_record.encode("utf-8")])
        my_sign = self.getMySigningKey()
        body = b"".join([b"i0:m2a:",
                         my_sign.verify_key.encode(),
                         my_sign.sign(signedBody)
                         ])
        nonce = os.urandom(Box.NONCE_SIZE)
        nonce_and_ciphertext = b.encrypt(body, nonce)
        #print "ENCRYPTED n+c", len(nonce_and_ciphertext), nonce_and_ciphertext.encode("hex")
        #print " nonce", nonce.encode("hex")
        msg2 = "i0:m2:"+nonce_and_ciphertext
        self.send(msg2)
        self.nextExpectedMessage = 2

    def processM2(self, msg):
        #print "processM2", repr(msg[:10]), "...", self.petname
        assert self.theirTempPubkey
        nonce_and_ciphertext = msg
        my_privkey = self.getMyTempPrivkey()
        b = Box(my_privkey, self.theirTempPubkey)
        #nonce = msg[:Box.NONCE_SIZE]
        #ciphertext = msg[Box.NONCE_SIZE:]
        #print "DECRYPTING n+ct", len(msg), msg.encode("hex")
        body = b.decrypt(nonce_and_ciphertext)
        if not body.startswith("i0:m2a:"):
            raise ValueError("expected i0:m2a:, got '%r'" % body[:20])
        verfkey_and_signedBody = body[len("i0:m2a:"):]
        theirVerfkey = VerifyKey(verfkey_and_signedBody[:32])
        signedBody = verfkey_and_signedBody[32:]
        body = theirVerfkey.verify(signedBody)
        check_myTempPubkey = body[:32]
        check_theirTempPubkey = body[32:64]
        their_channel_record_json = body[64:].decode("utf-8")
        #print " binding checks:"
        #print " check_myTempPubkey", check_myTempPubkey.encode("hex")
        #print " my real tempPubkey", my_privkey.public_key.encode(Hex)
        #print " check_theirTempPubkey", check_theirTempPubkey.encode("hex")
        #print " first theirTempPubkey", self.theirTempPubkey.encode(Hex)
        if check_myTempPubkey != my_privkey.public_key.encode():
            raise ValueError("binding failure myTempPubkey")
        if check_theirTempPubkey != self.theirTempPubkey.encode():
            raise ValueError("binding failure theirTempPubkey")

        them = json.loads(their_channel_record_json)
        me = self.getMyPrivateChannelData()
        addressbook_id = self.db.insert(
            "INSERT INTO addressbook"
            " (petname, acked,"
            "  next_outbound_seqnum, my_signkey,"
            "  their_channel_record_json,"
            "  my_CID_key, next_CID_token,"
            "  highest_inbound_seqnum,"
            "  my_old_channel_privkey, my_new_channel_privkey,"
            "  they_used_new_channel_key, their_verfkey)"
            " VALUES (?,?, "
            "         ?,?,"
            "         ?,"
            "         ?,?," # my_CID_key, next_CID_token
            "         ?,"   # highest_inbound_seqnum
            "         ?,?,"
            "         ?,?)",
            (self.petname, 0,
             1, me["my_signkey"],
             json.dumps(them),
             me["my_CID_key"], None,
             0,
             me["my_old_channel_privkey"],
             me["my_new_channel_privkey"],
             0, theirVerfkey.encode(Hex) ),
            "addressbook")
        self.db.update("UPDATE invitations SET addressbook_id=?"
                       " WHERE id=?", (addressbook_id, self.iid),
                       "invitations", self.iid)

        msg3 = "i0:m3:ACK-"+os.urandom(16)
        self.send(msg3)
        self.nextExpectedMessage = 3

    def processM3(self, msg):
        #print "processM3", repr(msg[:10]), "..."
        if not msg.startswith("ACK-"):
            raise ValueError("bad ACK")
        cid = self.getAddressbookID()
        self.db.update("UPDATE addressbook SET acked=1 WHERE id=?", (cid,),
                       "addressbook", cid )
        self.db.delete("DELETE FROM invitations WHERE id=?", (self.iid,),
                       "invitations", self.iid)

        # we no longer care about the channel
        msg4 = "i0:destroy:"+os.urandom(16)
        self.send(msg4, persist=False)
        self.manager.unsubscribe(self.inviteID)
Ejemplo n.º 10
0
    def on_post(self, req, resp):
        configuration = req.context['configuration']

        if req.content_length:
            data = json.load(req.bounded_stream)
        else:
            raise Exception("No data.")

        if 'public_key' not in data:
            raise Exception("No public key.")

        if 'captcha' not in data:
            raise Exception("No captcha.")

        if 'uuid' not in data['captcha']:
            raise Exception("No captcha UUID.")

        if 'encrypted_answer' not in data['captcha']:
            raise Exception("No captcha encrypted answer.")

        if 'nonce' not in data['captcha']:
            raise Exception("No captcha nonce.")

        # Decrypt captcha answer

        user_public_key = PublicKey(data['public_key'].encode('utf-8'),
                                    encoder=nacl.encoding.Base64Encoder)

        captcha_encrypted_answer = base64.b64decode(
            data['captcha']['encrypted_answer'].encode('utf-8'))
        captcha_nonce = base64.b64decode(
            data['captcha']['nonce'].encode('utf-8'))

        box = Box(configuration['server_key_pair'], user_public_key)
        captcha_answer = box.decrypt(captcha_encrypted_answer,
                                     captcha_nonce).decode('utf-8').lower()

        # Get captcha from database

        db_session = req.context['db_session']
        captcha = db_session.query(db.Captcha).filter(
            db.Captcha.uuid == data['captcha']['uuid']).first()

        # Whatever happens, delete the captcha from database
        db_session.delete(captcha)
        db_session.commit()

        # Check the IP hash of the user who requested the captcha matches the current client's IP hash
        ip_address_hash = nacl.hash.sha256(str.encode(captcha.uuid +
                                                      req.remote_addr),
                                           encoder=nacl.encoding.RawEncoder)
        user_agent_hash = nacl.hash.sha256(str.encode(captcha.uuid +
                                                      req.user_agent),
                                           encoder=nacl.encoding.RawEncoder)
        answer_hash = nacl.hash.sha256(str.encode(captcha.uuid +
                                                  captcha_answer),
                                       encoder=nacl.encoding.RawEncoder)

        if (ip_address_hash != captcha.ip_address_hash) or (
                user_agent_hash != captcha.user_agent_hash):
            raise Exception("Captcha answer incorrect.")

        # Check the captcha answer
        if captcha.answer_hash != answer_hash:
            raise Exception("Captcha answer incorrect.")

        # Create new user
        user = db.User(
            public_key=user_public_key.encode(nacl.encoding.RawEncoder))

        db_session.add(user)
        db_session.commit()

        resp.status = falcon.HTTP_201
        resp.body = json.dumps({})
Ejemplo n.º 11
0
 def _serialize_public_key(key: public.PublicKey) -> str:
     str_key = key.encode(encoder=nacl.encoding.HexEncoder).decode('ascii')
     return str_key
Ejemplo n.º 12
0
# PKp2_full = loads('{"kty":"EC", "crv":"Curve25519", "x":""}', \
#     object_pairs_hook=OrderedDict)

PKp_full['x'] = PKp_b64
PKs_full['x'] = PKs_b64
#PKs2_full['x'] = PKs2_b64
#PKp2_full['x'] = PKp2_b64

## Load peer and server private keys
SK_peer = PrivateKey(bytes.fromhex(SKp))
SK_server = PrivateKey(bytes.fromhex(SKs))
#SK2_peer = PrivateKey(bytes.fromhex(SKp2))
#SK2_server = PrivateKey(bytes.fromhex(SKs2))

## Derive shared secret
Z = scalarmult(SK_peer.encode(), PK_server.encode())
assert(Z == scalarmult(SK_server.encode(), PK_peer.encode()))
#Z2 = scalarmult(SK2_peer.encode(), PK2_server.encode())
#assert(Z2 == scalarmult(SK2_server.encode(), PK2_peer.encode()))

## KDF for completion exchange. Uses NIST Concat KDF.
KDF_input = b'EAP-NOOB' + base64url_decode(Np_b64) + base64url_decode(Ns_b64) +\
    base64url_decode(Noob_b64)
KDF_out = KDF(algorithm=SHA256(), length=320, otherinfo=KDF_input,
    backend=default_backend()).derive(Z)
Kms = KDF_out[224:256]
Kmp = KDF_out[256:288]
Kz = KDF_out[288:320]

# KDF - for reconnect exchange. Uses NIST Concat KDF. This sample script does
# not exchange new keys in the reconnect exchange and uses the Kz from the
Ejemplo n.º 13
0
class Invitation:
    # This has a brief lifetime: one is created in response to the rendezvous
    # client discovering new messages for us, used for one reactor tick, then
    # dereferenced. It holds onto a few values during that tick (which may
    # process multiple messages for a single invitation, e.g. A's second poll
    # will receive both B-m1 and B-m2 together). But all persistent state
    # beyond that one tick is stored in the database.
    def __init__(self, iid, db, manager):
        self.iid = iid
        self.db = db
        self.manager = manager
        c = self.db.execute(
            "SELECT petname, inviteID, inviteKey,"  # 0,1,2
            " theirTempPubkey,"  # 3
            " nextExpectedMessage,"  # 4
            " myMessages,"  # 5
            " theirMessages"  # 6
            " FROM invitations WHERE id = ?",
            (iid, ))
        res = c.fetchone()
        if not res:
            raise KeyError("no pending Invitation for '%d'" % iid)
        self.petname = res[0]
        self.inviteID = res[1]
        self.inviteKey = SigningKey(res[2].decode("hex"))
        self.theirTempPubkey = None
        if res[3]:
            self.theirTempPubkey = PublicKey(res[3].decode("hex"))
        self.nextExpectedMessage = int(res[4])
        self.myMessages = splitMessages(res[5])
        self.theirMessages = splitMessages(res[6])

    def getAddressbookID(self):
        c = self.db.execute(
            "SELECT addressbook_id FROM invitations"
            " WHERE id = ?", (self.iid, ))
        return c.fetchone()[0]

    def getMyTempPrivkey(self):
        c = self.db.execute(
            "SELECT myTempPrivkey FROM invitations"
            " WHERE id = ?", (self.iid, ))
        return PrivateKey(c.fetchone()[0].decode("hex"))

    def getMySigningKey(self):
        c = self.db.execute(
            "SELECT mySigningKey FROM invitations"
            " WHERE id = ?", (self.iid, ))
        return SigningKey(c.fetchone()[0].decode("hex"))

    def getMyPublicChannelRecord(self):
        c = self.db.execute(
            "SELECT my_channel_record FROM invitations"
            " WHERE id = ?", (self.iid, ))
        return c.fetchone()[0]

    def getMyPrivateChannelData(self):
        c = self.db.execute(
            "SELECT my_private_channel_data FROM invitations"
            " WHERE id = ?", (self.iid, ))
        return json.loads(c.fetchone()[0])

    def sendFirstMessage(self):
        pub = self.getMyTempPrivkey().public_key.encode()
        self.send("i0:m1:" + pub)
        self.db.update("UPDATE invitations SET myMessages=? WHERE id=?",
                       (",".join(self.myMessages), self.iid), "invitations",
                       self.iid)
        # that will be commited by our caller

    def processMessages(self, messages):
        # These messages are neither version-checked nor signature-checked.
        # Also, we may have already processed some of them.
        #print "processMessages", messages
        #print " my", self.myMessages
        #print " theirs", self.theirMessages
        assert isinstance(messages, set), type(messages)
        assert None not in messages, messages
        assert None not in self.myMessages, self.myMessages
        assert None not in self.theirMessages, self.theirMessages
        # Send anything that didn't make it to the server. This covers the
        # case where we commit our outbound message in send() but crash
        # before finishing delivery.
        for m in self.myMessages - messages:
            #print "resending", m
            self.manager.sendToAll(self.inviteID, m)

        newMessages = messages - self.myMessages - self.theirMessages
        #print " %d new messages" % len(newMessages)
        if not newMessages:
            print " huh, no new messages, stupid rendezvous client"

        # check signatures, extract bodies. invalid messages kill the channel
        # and the invitation. MAYBE TODO: lose the one channel, keep using
        # the others.
        bodies = set()
        for m in newMessages:
            #print " new inbound message", m
            try:
                if not m.startswith("r0:"):
                    print "unrecognized rendezvous message prefix"
                if not VALID_MESSAGE.search(m):
                    raise CorruptChannelError()
                m = m[len("r0:"):].decode("hex")
                bodies.add(self.inviteKey.verify_key.verify(m))
            except (BadSignatureError, CorruptChannelError) as e:
                print "channel %s is corrupt" % self.inviteID
                if isinstance(e, BadSignatureError):
                    print " (bad sig)"
                self.unsubscribe(self.inviteID)
                # TODO: mark invitation as failed, destroy it
                return

        #print " new inbound bodies:", ", ".join([repr(b[:10])+" ..." for b in bodies])

        # these handlers will update self.myMessages with sent messages, and
        # will increment self.nextExpectedMessage. We can handle multiple
        # (sequential) messages in a single pass.
        if self.nextExpectedMessage == 1:
            self.findPrefixAndCall("i0:m1:", bodies, self.processM1)
            # no elif here: self.nextExpectedMessage may have incremented
        if self.nextExpectedMessage == 2:
            self.findPrefixAndCall("i0:m2:", bodies, self.processM2)
        if self.nextExpectedMessage == 3:
            self.findPrefixAndCall("i0:m3:", bodies, self.processM3)

        self.db.update(
            "UPDATE invitations SET"
            "  myMessages=?,"
            "  theirMessages=?,"
            "  nextExpectedMessage=?"
            " WHERE id=?", (",".join(
                self.myMessages), ",".join(self.theirMessages | newMessages),
                            self.nextExpectedMessage, self.iid), "invitations",
            self.iid)
        #print " db.commit"
        self.db.commit()

    def findPrefixAndCall(self, prefix, bodies, handler):
        for msg in bodies:
            if msg.startswith(prefix):
                return handler(msg[len(prefix):])
        return None

    def send(self, msg, persist=True):
        #print "send", repr(msg[:10]), "..."
        signed = "r0:%s" % self.inviteKey.sign(msg).encode("hex")
        if persist:  # m4-destroy is not persistent
            self.myMessages.add(signed)  # will be persisted by caller
            # This will be added to the DB, and committed, by our caller, to
            # get it into the same transaction as the update to which inbound
            # messages we've processed.
        assert VALID_MESSAGE.search(signed), signed
        self.manager.sendToAll(self.inviteID, signed)

    def processM1(self, msg):
        #print "processM1", self.petname
        self.theirTempPubkey = PublicKey(msg)
        self.db.update(
            "UPDATE invitations SET theirTempPubkey=?"
            " WHERE id=?", (self.theirTempPubkey.encode(Hex), self.iid),
            "invitations", self.iid)
        # theirTempPubkey will committed by our caller, in the same txn as
        # the message send

        my_privkey = self.getMyTempPrivkey()
        my_channel_record = self.getMyPublicChannelRecord()
        b = Box(my_privkey, self.theirTempPubkey)
        signedBody = b"".join([
            self.theirTempPubkey.encode(),
            my_privkey.public_key.encode(),
            my_channel_record.encode("utf-8")
        ])
        my_sign = self.getMySigningKey()
        body = b"".join([
            b"i0:m2a:",
            my_sign.verify_key.encode(),
            my_sign.sign(signedBody)
        ])
        nonce = os.urandom(Box.NONCE_SIZE)
        nonce_and_ciphertext = b.encrypt(body, nonce)
        #print "ENCRYPTED n+c", len(nonce_and_ciphertext), nonce_and_ciphertext.encode("hex")
        #print " nonce", nonce.encode("hex")
        msg2 = "i0:m2:" + nonce_and_ciphertext
        self.send(msg2)
        self.nextExpectedMessage = 2

    def processM2(self, msg):
        #print "processM2", repr(msg[:10]), "...", self.petname
        assert self.theirTempPubkey
        nonce_and_ciphertext = msg
        my_privkey = self.getMyTempPrivkey()
        b = Box(my_privkey, self.theirTempPubkey)
        #nonce = msg[:Box.NONCE_SIZE]
        #ciphertext = msg[Box.NONCE_SIZE:]
        #print "DECRYPTING n+ct", len(msg), msg.encode("hex")
        body = b.decrypt(nonce_and_ciphertext)
        if not body.startswith("i0:m2a:"):
            raise ValueError("expected i0:m2a:, got '%r'" % body[:20])
        verfkey_and_signedBody = body[len("i0:m2a:"):]
        theirVerfkey = VerifyKey(verfkey_and_signedBody[:32])
        signedBody = verfkey_and_signedBody[32:]
        body = theirVerfkey.verify(signedBody)
        check_myTempPubkey = body[:32]
        check_theirTempPubkey = body[32:64]
        their_channel_record_json = body[64:].decode("utf-8")
        #print " binding checks:"
        #print " check_myTempPubkey", check_myTempPubkey.encode("hex")
        #print " my real tempPubkey", my_privkey.public_key.encode(Hex)
        #print " check_theirTempPubkey", check_theirTempPubkey.encode("hex")
        #print " first theirTempPubkey", self.theirTempPubkey.encode(Hex)
        if check_myTempPubkey != my_privkey.public_key.encode():
            raise ValueError("binding failure myTempPubkey")
        if check_theirTempPubkey != self.theirTempPubkey.encode():
            raise ValueError("binding failure theirTempPubkey")

        them = json.loads(their_channel_record_json)
        me = self.getMyPrivateChannelData()
        addressbook_id = self.db.insert(
            "INSERT INTO addressbook"
            " (petname, acked,"
            "  next_outbound_seqnum, my_signkey,"
            "  their_channel_record_json,"
            "  my_CID_key, next_CID_token,"
            "  highest_inbound_seqnum,"
            "  my_old_channel_privkey, my_new_channel_privkey,"
            "  they_used_new_channel_key, their_verfkey)"
            " VALUES (?,?, "
            "         ?,?,"
            "         ?,"
            "         ?,?,"  # my_CID_key, next_CID_token
            "         ?,"  # highest_inbound_seqnum
            "         ?,?,"
            "         ?,?)",
            (self.petname, 0, 1, me["my_signkey"], json.dumps(them),
             me["my_CID_key"], None, 0, me["my_old_channel_privkey"],
             me["my_new_channel_privkey"], 0, theirVerfkey.encode(Hex)),
            "addressbook")
        self.db.update("UPDATE invitations SET addressbook_id=?"
                       " WHERE id=?", (addressbook_id, self.iid),
                       "invitations", self.iid)

        msg3 = "i0:m3:ACK-" + os.urandom(16)
        self.send(msg3)
        self.nextExpectedMessage = 3

    def processM3(self, msg):
        #print "processM3", repr(msg[:10]), "..."
        if not msg.startswith("ACK-"):
            raise ValueError("bad ACK")
        cid = self.getAddressbookID()
        self.db.update("UPDATE addressbook SET acked=1 WHERE id=?", (cid, ),
                       "addressbook", cid)
        self.db.delete("DELETE FROM invitations WHERE id=?", (self.iid, ),
                       "invitations", self.iid)

        # we no longer care about the channel
        msg4 = "i0:destroy:" + os.urandom(16)
        self.send(msg4, persist=False)
        self.manager.unsubscribe(self.inviteID)
Ejemplo n.º 14
0
    def on_post(self, req, resp):
        configuration = req.context['configuration']

        if req.content_length:
            data = json.load(req.bounded_stream)
        else:
            raise Exception("No data.")

        if 'public_key' not in data:
            raise Exception("No public key.")

        if 'captcha' not in data:
            raise Exception("No captcha.")

        if 'uuid' not in data['captcha']:
            raise Exception("No captcha UUID.")

        if 'encrypted_answer' not in data['captcha']:
            raise Exception("No captcha encrypted answer.")

        if 'nonce' not in data['captcha']:
            raise Exception("No captcha nonce.")

        # Decrypt captcha answer

        user_public_key = PublicKey(data['public_key'].encode('utf-8'), encoder = nacl.encoding.Base64Encoder)

        captcha_encrypted_answer = base64.b64decode(data['captcha']['encrypted_answer'].encode('utf-8'))
        captcha_nonce = base64.b64decode(data['captcha']['nonce'].encode('utf-8'))

        box = Box(configuration['server_key_pair'], user_public_key)
        captcha_answer = box.decrypt(captcha_encrypted_answer, captcha_nonce).decode('utf-8').lower()

        # Get captcha from database

        db_session = req.context['db_session']
        captcha = db_session.query(db.Captcha).filter(db.Captcha.uuid == data['captcha']['uuid']).first()

        # Whatever happens, delete the captcha from database
        db_session.delete(captcha)
        db_session.commit()

        # Check the IP hash of the user who requested the captcha matches the current client's IP hash
        ip_address_hash = nacl.hash.sha256(str.encode(captcha.uuid + req.remote_addr), encoder = nacl.encoding.RawEncoder)
        user_agent_hash = nacl.hash.sha256(str.encode(captcha.uuid + req.user_agent), encoder = nacl.encoding.RawEncoder)
        answer_hash = nacl.hash.sha256(str.encode(captcha.uuid + captcha_answer), encoder = nacl.encoding.RawEncoder)

        if (ip_address_hash != captcha.ip_address_hash) or (user_agent_hash != captcha.user_agent_hash):
            raise Exception("Captcha answer incorrect.")

        # Check the captcha answer
        if captcha.answer_hash != answer_hash:
            raise Exception("Captcha answer incorrect.")

        # Create new user
        user = db.User(
            public_key = user_public_key.encode(nacl.encoding.RawEncoder)
        )

        db_session.add(user)
        db_session.commit()

        resp.status = falcon.HTTP_201
        resp.body = json.dumps({})