예제 #1
0
    def update_DA(self, s: socket.socket):
        """
        Send in the socket s, all certificates in personal CA and DA
        """
        self.DA.update(self.CA)

        for autorities in (self.CA, self.DA):
            # json is better with string, not bytes -> encode() / decode()
            _set = {}
            for autority in autorities:
                k = KeyPair.pubkey_pem(autority.issuer_key).decode()
                v = autority.cert_from_issuer.cert_pem().decode()
                _set[k] = v

            sendall(s, json.dumps(_set).encode())

            _received_json = recv_json(s)
            for (key, value) in _received_json.items():
                self.DA.add(
                    AutorityProof(
                        issuer_key=KeyPair.load_pub_from_pem(key.encode()),
                        cert_from_issuer=X509Certificate.load_from_pem(
                            value.encode()),
                    ))
        print("DA update finished")
예제 #2
0
 def test_chain_0_element(self):
     """
     Should raise an error cause there is no chain
     """
     chain = []
     kp = KeyPair()
     with pytest.raises(ValueError):
         X509Certificate.verify_chain(kp.public_key(), chain,
                                      kp.public_key())
예제 #3
0
 def test_selfsigned_cert(self):
     kp = KeyPair()
     cert = X509Certificate(
         issuer="issuer",
         subject="subject",
         public_key=kp.public_key(),
         private_key=kp.private_key(),
         validity_days=10,
     )
     X509Certificate.verify(cert, kp.public_key())
예제 #4
0
 def test_cert_to_pem_to_cert(self):
     kp = KeyPair()
     cert1 = X509Certificate(
         issuer="issuer",
         subject="subject",
         public_key=kp.public_key(),
         private_key=kp.private_key(),
         validity_days=10,
     )
     cert2 = X509Certificate.load_from_pem(cert1.cert_pem())
     assert cert1 == cert2
예제 #5
0
    def _fix_parameters(self, sock):
        """Receive the DH p, g parameters to generate a key pair"""
        raw = sock.recv(self.BUFFER_SIZE)
        recv_msg = Message.from_buffer(raw)

        assert recv_msg['code'] == Message.FIX_PARAMS, \
            'Unexpected message code during key exchange: %d' % recv_msg['code']

        dh_p = mpz(recv_msg['dh_p'])
        dh_g = mpz(recv_msg['dh_g'])

        self._keys = KeyPair(dh_p, dh_g)
예제 #6
0
    def test_wrong_pubkey(self):
        pubkey_to_signed = KeyPair().public_key()

        kp_root = KeyPair()

        cert = X509Certificate(
            issuer="issuer",
            subject="subject",
            public_key=pubkey_to_signed,
            private_key=kp_root.private_key(),
            validity_days=10,
        )
        assert not X509Certificate.verify(cert, pubkey_to_signed)
예제 #7
0
    def __init__(self):
        """Initialize a DH key pair and start listening for client connections"""
        self._keys = KeyPair()
        self._session_secret = None
        self._echo_msg = None

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind((self.TCP_IP, self.TCP_PORT))
        sock.listen(1)

        conn, addr = sock.accept()

        while True:
            self._handle(conn)
예제 #8
0
 def __init__(self, name: str, port: int):
     self.name = name
     self.port = port
     self.CA: Set[AutorityProof] = set()  # Certification autorities
     self.DA: Set[AutorityProof] = set()  # Derived autorities
     self.key_pair = KeyPair()
     self.certificate = X509Certificate(
         self.name,
         self.name,
         self.key_pair.public_key(),
         self.key_pair.private_key(),
         10,
     )
     print("Is my cert valid ? {} ".format(
         X509Certificate.verify(self.certificate,
                                self.key_pair.public_key())))
     print(self)
예제 #9
0
    def test_gen_good_chain_line(self):
        """
        tests weither equipment A can create a chain to B via C

        B -> C -> A
        """
        equipmentA = Equipment(name="name", port=8888)

        chain = set()
        kp_B = KeyPair()
        kp_C = KeyPair()
        kp_A = equipmentA.key_pair

        cert_BC = X509Certificate(
            issuer="B",
            subject="C",
            public_key=kp_C.public_key(),
            private_key=kp_B.private_key(),
            validity_days=10,
        )

        cert_CA = X509Certificate(
            issuer="C",
            subject="A",
            public_key=kp_A.public_key(),
            private_key=kp_C.private_key(),
            validity_days=10,
        )

        equipmentA.DA.add(
            AutorityProof(issuer_key=kp_B.public_key(), cert_from_issuer=cert_BC)
        )
        equipmentA.DA.add(
            AutorityProof(issuer_key=kp_C.public_key(), cert_from_issuer=cert_CA)
        )

        assert equipmentA.create_cert_chain(kp_B.public_key()) == [cert_BC, cert_CA]
예제 #10
0
    def test_chain_X_elements(self, cert_count):
        """
        Chain with X valid certificates
        """
        chain = []
        kp_root = KeyPair()

        privkey = kp_root.private_key()
        for i in range(cert_count):
            kp_next = KeyPair()
            pubkey = kp_next.public_key()
            cert = X509Certificate(
                issuer="issuer",
                subject="subject",
                public_key=kp_next.public_key(),
                private_key=privkey,
                validity_days=10,
            )
            privkey = kp_next.private_key()
            chain.append(cert)

        X509Certificate.verify_chain(kp_root.public_key(), chain, pubkey)
예제 #11
0
    def test_certEqcert_kp(self):
        kp1 = KeyPair()
        cert1 = X509Certificate(
            issuer="issuer",
            subject="subject",
            public_key=kp1.public_key(),
            private_key=kp1.private_key(),
            validity_days=10,
        )
        kp2 = KeyPair()
        cert2 = X509Certificate(
            issuer="issuer",
            subject="subject",
            public_key=kp2.public_key(),
            private_key=kp2.private_key(),
            validity_days=10,
        )
        with pytest.raises(AssertionError):
            assert cert1 == cert2

        assert cert1 != cert2
예제 #12
0
 def test_chain_1_element(self):
     """"
     An equipment automaticaly verify a chain with only it inside
     """
     chain = []
     kp = KeyPair()
     cert = X509Certificate(
         issuer="issuer",
         subject="subject",
         public_key=kp.public_key(),
         private_key=kp.private_key(),
         validity_days=10,
     )
     chain.append(cert)
     X509Certificate.verify_chain(kp.public_key(), chain, kp.public_key())
예제 #13
0
class MessageServer(object):
    """Simple message server for encrypted communications
     using Diffie-Hellman key exchange"""

    TCP_IP = '127.0.0.1'
    TCP_PORT = 9090
    BUFFER_SIZE = 1024
    AES_IV_SIZE = 16

    def __init__(self):
        """Initialize a DH key pair and start listening for client connections"""
        self._keys = KeyPair()
        self._session_secret = None
        self._echo_msg = None

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind((self.TCP_IP, self.TCP_PORT))
        sock.listen(1)

        conn, addr = sock.accept()

        while True:
            self._handle(conn)

    def _handle(self, conn):
        # Run through protocol...
        # 1) Fix parameters
        self._fix_parameters(conn)

        # 2) Exchange keys
        self._key_exchange(conn)

        # 3) Read encrypted msg
        self._read_msg(conn)

        # 4) Echo encrypted reply
        self._send_echo(conn)

        conn.close()

    def _fix_parameters(self, conn):
        """Fix the DH p, g parameters to use in key exchange"""
        fixed_p = KeyPair.P_NIST.digits(16)
        fixed_g = KeyPair.G_NIST.digits(16)

        # Create the message object with our DH parameters
        msg = Message(Message.FIX_PARAMS, dh_p=fixed_p, dh_g=fixed_g)

        # Send the message buffer
        conn.send(msg.buffer)

    def _key_exchange(self, conn):
        """Initiate a DH key exchange (send/receive public keys)"""
        public_key = self._keys.get_public().to_hex()

        # Create the message object with server public key
        msg = Message(Message.KEY_EXCHG, public_key=public_key)

        # Send the message buffer
        conn.send(msg.buffer)

        # Read the client public key
        raw = conn.recv(self.BUFFER_SIZE)
        recv_msg = Message.from_buffer(raw)

        assert recv_msg['code'] == Message.KEY_EXCHG, \
            'Unexpected message code during key exchange: %d' % recv_msg['code']

        # Create the shared secret
        buf = Buffer.from_hex(recv_msg['public_key'])
        self._session_secret = self._keys.session_key(buf)

    def _read_msg(self, conn):
        """Receive the encrypted client response"""
        raw = conn.recv(self.BUFFER_SIZE)
        recv_msg = Message.from_buffer(raw)

        assert recv_msg['code'] == Message.RECV_ENC, \
            'Unexpected message code during receive: %d' % recv_msg['code']

        ciphertext = recv_msg['client_msg']

        assert len(ciphertext) > self.AES_IV_SIZE, 'Invalid client message size'

        # Decrypt the received ciphertext using AES-CBC (16-bit IV prepended)
        iv = ciphertext[0:self.AES_IV_SIZE]
        ciphertext = ciphertext[self.AES_IV_SIZE:]
        cipher = AES.new(self._session_secret, AES.MODE_CBC, iv)

        # Store the decrypted message
        self._echo_msg = cipher.decrypt(ciphertext)

    def _send_echo(self, conn):
        """Send an encrypted server echo response"""
        # Re-encrypt the echo message
        iv = Random.new().read(self.AES_IV_SIZE)
        cipher = AES.new(self._session_secret, AES.MODE_CBC, iv)
        ciphertext = iv
        ciphertext += cipher.encrypt(self._echo_msg)

        # Create the message object with encoded echo message
        echo_msg = Message(Message.SEND_ENC, server_msg=ciphertext)

        # Send the message buffer
        conn.send(echo_msg.buffer)
 def load(cls, tupl: Tuple[bytes, bytes]) -> "AutorityProof":
     return cls(KeyPair.load_pub_from_pem(tupl[0]),
                X509Certificate.load_from_pem(tupl[1]))
 def pem(self) -> Tuple[bytes, bytes]:
     return (KeyPair.pubkey_pem(self.issuer_key),
             self.cert_from_issuer.cert_pem())
예제 #16
0
    def test_gen_good_chain_dual(self):
        r"""
        tests weither equipment A can create a chain to B and then to D

        B -> C -> A
          \      /
           D -> E 
        """
        equipmentA = Equipment(name="name", port=8888)

        chain = set()
        kp_B = KeyPair()
        kp_C = KeyPair()
        kp_A = equipmentA.key_pair
        kp_D = KeyPair()
        kp_E = KeyPair()

        cert_BC = X509Certificate(
            issuer="B",
            subject="C",
            public_key=kp_C.public_key(),
            private_key=kp_B.private_key(),
            validity_days=10,
        )
        equipmentA.DA.add(AutorityProof(kp_B.public_key(), cert_BC))

        cert_BD = X509Certificate(
            issuer="B",
            subject="D",
            public_key=kp_D.public_key(),
            private_key=kp_B.private_key(),
            validity_days=10,
        )
        equipmentA.DA.add(AutorityProof(kp_B.public_key(), cert_BD))

        cert_DE = X509Certificate(
            issuer="D",
            subject="E",
            public_key=kp_E.public_key(),
            private_key=kp_D.private_key(),
            validity_days=10,
        )
        equipmentA.DA.add(AutorityProof(kp_D.public_key(), cert_DE))

        cert_EA = X509Certificate(
            issuer="E",
            subject="A",
            public_key=kp_A.public_key(),
            private_key=kp_E.private_key(),
            validity_days=10,
        )
        equipmentA.DA.add(AutorityProof(kp_E.public_key(), cert_EA))

        cert_CA = X509Certificate(
            issuer="C",
            subject="A",
            public_key=kp_A.public_key(),
            private_key=kp_C.private_key(),
            validity_days=10,
        )
        equipmentA.DA.add(AutorityProof(kp_C.public_key(), cert_CA))

        assert equipmentA.create_cert_chain(kp_B.public_key()) == [cert_BC, cert_CA]
        assert equipmentA.create_cert_chain(kp_D.public_key()) == [cert_DE, cert_EA]
예제 #17
0
class Equipment:
    def __init__(self, name: str, port: int):
        self.name = name
        self.port = port
        self.CA: Set[AutorityProof] = set()  # Certification autorities
        self.DA: Set[AutorityProof] = set()  # Derived autorities
        self.key_pair = KeyPair()
        self.certificate = X509Certificate(
            self.name,
            self.name,
            self.key_pair.public_key(),
            self.key_pair.private_key(),
            10,
        )
        print("Is my cert valid ? {} ".format(
            X509Certificate.verify(self.certificate,
                                   self.key_pair.public_key())))
        print(self)

    def __repr__(self):
        s = "My name is {} and my port is {}\n".format(self.name, self.port)
        s += "My public key is {}\n".format(
            self.key_pair.public_key().public_numbers())
        s += "My certificate is:\n{}\n".format(self.certificate)
        return s

    def server(self):
        print("I play server and wait on port {}".format(self.port))
        with socket.socket() as s:
            s.setsockopt(
                socket.SOL_SOCKET, socket.SO_REUSEADDR,
                1)  # from python doc do reuse socket in TIME-WAIT state
            s.bind(("localhost", self.port))
            s.listen()
            conn, addr = s.accept()  # conn is a socket connected to addr
            with conn:
                print("Connected by", addr)
                self.a_la_pgp_process(conn=conn)

        print("Closing connection")

    def client(self, addr: str, port: int):
        print("I play client and want to acces {} on port {}".format(
            addr, port))
        with socket.socket() as conn:
            conn.connect((addr, port))
            print("Connected to", (addr, port))
            self.a_la_pgp_process(conn=conn)

        print("Closing connection")

    def show_certs(self):
        CA_certs = {autority.cert_from_issuer for autority in self.CA}
        DA_certs = {autority.cert_from_issuer for autority in self.DA}
        print("#" * 8 + " CA " + "#" * 8 + "\n{}".format(CA_certs))
        print("#" * 8 + " DA " + "#" * 8 + "\n{}".format(DA_certs))

    def show_certs_couple(self):
        print("#" * 8 + " CA " + "#" * 8 + "\n{}".format(self.CA))
        print("#" * 8 + " DA " + "#" * 8 + "\n{}".format(self.DA))

    def update_CA(self, s: socket.socket, subject, pubkey):
        """
        Equipments don't know each other and they need to exchange their
        certificate. The auth by DA failed.

        :pubkey is the pubkey of the other equipment
        """
        # send our certificate on its public key
        cert_to_sent = X509Certificate(
            issuer=self.name,
            subject=subject,
            public_key=pubkey,
            private_key=self.key_pair.private_key(),
            validity_days=10,
        )
        sendall(s, cert_to_sent.cert_pem())

        # received its certificate on our public key
        cert_received = X509Certificate.load_from_pem(recv_all(s))
        if not X509Certificate.verify(cert_received, pubkey):
            print("update_CA FAILS cause the cert received is not valid")
            return

        self.CA.add(AutorityProof(pubkey, cert_received))
        print("CA update finished")

    def update_DA(self, s: socket.socket):
        """
        Send in the socket s, all certificates in personal CA and DA
        """
        self.DA.update(self.CA)

        for autorities in (self.CA, self.DA):
            # json is better with string, not bytes -> encode() / decode()
            _set = {}
            for autority in autorities:
                k = KeyPair.pubkey_pem(autority.issuer_key).decode()
                v = autority.cert_from_issuer.cert_pem().decode()
                _set[k] = v

            sendall(s, json.dumps(_set).encode())

            _received_json = recv_json(s)
            for (key, value) in _received_json.items():
                self.DA.add(
                    AutorityProof(
                        issuer_key=KeyPair.load_pub_from_pem(key.encode()),
                        cert_from_issuer=X509Certificate.load_from_pem(
                            value.encode()),
                    ))
        print("DA update finished")

    def hand_shake(self, s: socket.socket) -> X509Certificate:
        """
        Exchanges selfsigned certificate with another equipment and verifies it
        """
        self.clean_sets()

        # send selfsigned certificate
        sendall(s, self.certificate.cert_pem())

        # received the other's selfsigned certificate
        cert_selfsigned_received = X509Certificate.load_from_pem(recv_all(s))
        other_cert_pubkey = cert_selfsigned_received.public_key()

        if not X509Certificate.verify(cert_selfsigned_received,
                                      other_cert_pubkey):
            raise NotValidCertificate(
                "hand_shake FAILS cause of invalid certificate")
        return cert_selfsigned_received

    def is_in_CA(self, issuer_key) -> bool:
        """ 
        check if the a issuer_key is a known issuer_key of an AutorityProof is CA 
        and if the associated cert is good
        """
        certs = [
            autority.cert_from_issuer for autority in self.CA
            if autority.issuer_key.public_numbers() ==
            issuer_key.public_numbers()
        ]
        if len(certs) == 0:
            return False
        else:
            cert = certs.pop()
            if not X509Certificate.verify(cert, issuer_key):
                return False
            return True

    def create_cert_chain(self, pubkey_other) -> List[X509Certificate]:
        graph = Graph([])
        for autority in self.DA:
            graph.add_edge(
                n1=autority.issuer_key.public_numbers(),
                n2=autority.cert_from_issuer.public_key().public_numbers(),
                cost=1,
                both_ends=False,
            )
        dq = graph.dijkstra(
            source=pubkey_other.public_numbers(),
            dest=self.certificate.public_key().public_numbers(),
        )
        if len(dq) <= 1:
            # no chain
            return []

        cert_chain: List[X509Certificate] = []
        n1 = dq.popleft()
        while len(dq) != 0:
            n2 = dq.popleft()

            for autority in self.DA:
                if (autority.issuer_key.public_numbers() == n1 and autority.
                        cert_from_issuer.public_key().public_numbers() == n2):
                    cert_chain.append(autority.cert_from_issuer)
                    break
            n1 = n2
        return cert_chain

    def is_known_by_DA(self, s: socket.socket, pubkey_other) -> bool:
        cert_chain = self.create_cert_chain(pubkey_other)

        set_to_send = []
        for cert in cert_chain:
            d = cert.cert_pem().decode()
            set_to_send.append(d)

        sendall(s, json.dumps(set_to_send).encode())

        cert_chain_received = recv_json(s)
        cert_chain_received = [
            X509Certificate.load_from_pem(cert.encode())
            for cert in cert_chain_received
        ]

        print("Cert chain exchanged")

        if len(cert_chain) != 0:
            print("I CAN reach the other one, no more question on my side")
            return True

        elif len(cert_chain_received) == 0:
            print("NONE of us can reach the other one with a cert chain")
            return False

        elif not X509Certificate.verify_chain(
                self.key_pair.public_key(), cert_chain_received,
                pubkey_other):  # the other one a cert chain. Is it valid ?

            print(
                "I cannot generate a cert chain and the cert chain received is NOT valid"
            )
            return False

        print(
            "I cannot generate a cert chain BUT the cert chain received IS valid"
        )
        return True

    def a_la_pgp_process(self, conn: socket.socket):
        """
        Define the action taken by both server and equipment.
        They do exactly the same
        """

        cert_received = self.hand_shake(conn)
        if self.is_in_CA(cert_received.public_key()):
            print("I already know you (CA)")
            self.update_DA(s=conn)
        elif self.is_known_by_DA(s=conn,
                                 pubkey_other=cert_received.public_key()):
            self.update_CA(
                s=conn,
                subject=cert_received.issuer(),
                pubkey=cert_received.public_key(),
            )
            self.update_DA(s=conn)
            print("One of us show that we belong to the same network (DA)")
        else:
            while True:
                resp = input("Connect a new device ? (y/n)")
                if resp == "y":
                    self.update_CA(
                        s=conn,
                        subject=cert_received.issuer(),
                        pubkey=cert_received.public_key(),
                    )
                    self.update_DA(s=conn)
                    break
                elif resp == "n":
                    print("Nothing happened")
                    break
                else:
                    print("Invalid answer")

    def clean_sets(self):
        """
        clean CA and DA sets before any communications with other equipment.
        Out of date certificates are deleted
        """

        for autority_set in (self.CA, self.DA):
            autority_set = {
                autority
                for autority in autority_set if X509Certificate.verify(
                    autority.cert_from_issuer, autority.issuer_key)
            }
예제 #18
0
class MessageClient(object):
    """Simple message client for encrypted communications
        using Diffie-Hellman key exchange
    """

    SERVER_IP = '127.0.0.1'
    SERVER_PORT = 9090
    BUFFER_SIZE = 1024
    AES_IV_SIZE = 16

    def __init__(self, message):
        """Connect to the message server and perform a secure echo exchange"""
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((self.SERVER_IP, self.SERVER_PORT))

        self._message = message
        self._keys = None
        self._session_secret = None

        self._check_echo(sock)

    def _check_echo(self, sock):
        # Run through protocol...
        # 1) Fix parameters
        self._fix_parameters(sock)

        # 2) Exchange keys
        self._key_exchange(sock)

        # 3) Send an encrypted message
        self._send_msg(sock)

        # 4) Read the encrypted echo reply
        self._read_echo(sock)

        sock.close()

    def _fix_parameters(self, sock):
        """Receive the DH p, g parameters to generate a key pair"""
        raw = sock.recv(self.BUFFER_SIZE)
        recv_msg = Message.from_buffer(raw)

        assert recv_msg['code'] == Message.FIX_PARAMS, \
            'Unexpected message code during key exchange: %d' % recv_msg['code']

        dh_p = mpz(recv_msg['dh_p'])
        dh_g = mpz(recv_msg['dh_g'])

        self._keys = KeyPair(dh_p, dh_g)

    def _key_exchange(self, sock):
        """Initiate a DH key exchange (receive/send public keys)"""
        # Read the server public key
        raw = sock.recv(self.BUFFER_SIZE)
        recv_msg = Message.from_buffer(raw)

        assert recv_msg['code'] == Message.KEY_EXCHG, \
            'Unexpected message code during key exchange: %d' % recv_msg['code']

        # Create the shared secret
        buf = Buffer.from_hex(recv_msg['public_key'])
        self._session_secret = self._keys.session_key(buf)

        # Send back client public key
        public_key = self._keys.get_public().to_hex()

        # Create the message object with client public key
        msg = Message(Message.KEY_EXCHG, public_key=public_key)

        # Send the message buffer
        sock.send(msg.buffer)

    def _send_msg(self, sock):
        """Send an encrypted message to the server"""
        # Encrypt the message with AES
        iv = Random.new().read(self.AES_IV_SIZE)
        cipher = AES.new(self._session_secret, AES.MODE_CBC, iv)
        ciphertext = iv
        ciphertext += cipher.encrypt(self._message)

        # Create the message object with encoded message
        msg = Message(Message.RECV_ENC, client_msg=ciphertext)

        # Send the message buffer
        sock.send(msg.buffer)

    def _read_echo(self, sock):
        """Receive the encrypted server echo response"""
        raw = sock.recv(self.BUFFER_SIZE)
        recv_msg = Message.from_buffer(raw)

        assert recv_msg['code'] == Message.SEND_ENC, \
            'Unexpected message code during receive: %d' % recv_msg['code']

        ciphertext = recv_msg['server_msg']

        assert len(ciphertext) > self.AES_IV_SIZE, 'Invalid server message size'

        # Decrypt the received ciphertext using AES-CBC (16-bit IV prepended)
        iv = ciphertext[0:self.AES_IV_SIZE]
        ciphertext = ciphertext[self.AES_IV_SIZE:]
        cipher = AES.new(self._session_secret, AES.MODE_CBC, iv)

        # Check the decrypted message matches
        echo_msg = cipher.decrypt(ciphertext)
        assert echo_msg == self._message, "Decrypted echo response did not match"
        print "[*] Successful echo..."