Exemplo n.º 1
0
    async def read(self):
        if self.closed:
            return None
        try:
            data = await self.reader.readexactly(HEADER_LENGTH)
        except IncompleteReadError:
            self.closed = True
            return None

        box = SecretBox(self.key)

        header = box.decrypt(data, self.nonce)

        if header == TERMINATION_HEADER:
            self.closed = True
            return None

        length = struct.unpack('>H', header[:2])[0]
        mac = header[2:]

        data = await self.reader.readexactly(length)

        body = box.decrypt(mac + data, inc_nonce(self.nonce))

        self.nonce = inc_nonce(inc_nonce(self.nonce))
        return body
Exemplo n.º 2
0
    def __enter__(self) -> typing.Tuple[PrivateKey, PrivateKey]:
        """
        Provides a pair of private keys.
        """
        # Derive the key from the passphrase.
        derived = util.derive_passphrase(self.passphrase)

        sign_box = SecretBox(derived)
        enc_box = SecretBox(derived)

        # Decrypt, using the two nonces.
        s_d = sign_box.decrypt(self.key._private_signing_seed, self.key._private_signing_nonce)
        e_d = enc_box.decrypt(self.key._private_key_raw, self.key._private_nonce)

        # Generate a SigningKey out of the seed.
        self.sign = SigningKey(s_d)
        self.encrypt = PrivateKey(e_d)

        # Update the key's public keys.
        if self.key._public_key is None:
            self.key._public_key = self.encrypt.public_key

        if self.key._public_signing_key is None:
            self.key._public_signing_key = self.sign.verify_key

        return self.encrypt, self.sign
    def test_records_good(self):
        # now make sure that outbound records are encrypted properly
        t, c, owner = self.make_connection()

        RECORD1 = b"record"
        c.send_record(RECORD1)
        buf = t.read_buf()
        expected = ("%08x" % (24+len(RECORD1)+16)).encode("ascii")
        self.assertEqual(hexlify(buf[:4]), expected)
        encrypted = buf[4:]
        receive_box = SecretBox(owner._sender_record_key())
        nonce_buf = encrypted[:SecretBox.NONCE_SIZE] # assume it's prepended
        nonce = int(hexlify(nonce_buf), 16)
        self.assertEqual(nonce, 0) # first message gets nonce 0
        decrypted = receive_box.decrypt(encrypted)
        self.assertEqual(decrypted, RECORD1)

        # second message gets nonce 1
        RECORD2 = b"record2"
        c.send_record(RECORD2)
        buf = t.read_buf()
        expected = ("%08x" % (24+len(RECORD2)+16)).encode("ascii")
        self.assertEqual(hexlify(buf[:4]), expected)
        encrypted = buf[4:]
        receive_box = SecretBox(owner._sender_record_key())
        nonce_buf = encrypted[:SecretBox.NONCE_SIZE] # assume it's prepended
        nonce = int(hexlify(nonce_buf), 16)
        self.assertEqual(nonce, 1)
        decrypted = receive_box.decrypt(encrypted)
        self.assertEqual(decrypted, RECORD2)

        # and that we can receive records properly
        inbound_records = []
        c.recordReceived = inbound_records.append

        RECORD3 = b"record3"
        send_box = SecretBox(owner._receiver_record_key())
        nonce_buf = unhexlify("%048x" % 0) # first nonce must be 0
        encrypted = send_box.encrypt(RECORD3, nonce_buf)
        length = unhexlify("%08x" % len(encrypted)) # always 4 bytes long
        c.dataReceived(length[:2])
        c.dataReceived(length[2:])
        c.dataReceived(encrypted[:-2])
        self.assertEqual(inbound_records, [])
        c.dataReceived(encrypted[-2:])
        self.assertEqual(inbound_records, [RECORD3])

        RECORD4 = b"record4"
        send_box = SecretBox(owner._receiver_record_key())
        nonce_buf = unhexlify("%048x" % 1) # nonces increment
        encrypted = send_box.encrypt(RECORD4, nonce_buf)
        length = unhexlify("%08x" % len(encrypted)) # always 4 bytes long
        c.dataReceived(length[:2])
        c.dataReceived(length[2:])
        c.dataReceived(encrypted[:-2])
        self.assertEqual(inbound_records, [RECORD3])
        c.dataReceived(encrypted[-2:])
        self.assertEqual(inbound_records, [RECORD3, RECORD4])
Exemplo n.º 4
0
def test_secret_box_wrong_lengths():
    with pytest.raises(ValueError):
        SecretBox(b"")

    box = SecretBox(b"ec2bee2d5be613ca82e377c96a0bf2220d823ce980cdff6279473edc52862798", encoder=HexEncoder)
    with pytest.raises(ValueError):
        box.encrypt(b"", b"")
    with pytest.raises(ValueError):
        box.decrypt(b"", b"")
Exemplo n.º 5
0
def test_secret_box_wrong_lengths():
    with pytest.raises(ValueError):
        SecretBox(b"")

    box = SecretBox(
        b"ec2bee2d5be613ca82e377c96a0bf2220d823ce980cdff6279473edc52862798",
        encoder=HexEncoder,
    )
    with pytest.raises(ValueError):
        box.encrypt(b"", b"")
    with pytest.raises(ValueError):
        box.decrypt(b"", b"")
Exemplo n.º 6
0
def decrypt_data(key, encrypted):
    assert isinstance(key, type(b"")), type(key)
    assert isinstance(encrypted, type(b"")), type(encrypted)
    assert len(key) == SecretBox.KEY_SIZE, len(key)
    box = SecretBox(key)
    data = box.decrypt(encrypted)
    return data
Exemplo n.º 7
0
def retrieve(sid, key):
    # Try to rename this sid's directory. This is an atomic operation on
    # POSIX file systems, meaning two concurrent requests cannot rename
    # the same directory -- for one of them, it will look like the
    # source directory does not exist. This also implicitly covers the
    # case where we try to retrieve an invalid sid.
    locked_sid = sid + '_locked'
    try:
        rename(join(DATA, sid), join(DATA, locked_sid))
    except OSError:
        return None, ALREADY_REVEALED

    # Now that we have "locked" this sid, we can safely read it and then
    # destroy it.
    with open(join(DATA, locked_sid, 'secret'), 'rb') as fp:
        secret_bytes = fp.read()
    run(['/usr/bin/shred', join(DATA, locked_sid, 'secret')])
    rmtree(join(DATA, locked_sid))

    # Restore padding. (No point in using something like a while loop
    # here, we checked for an explicit length earlier.)
    key += '='
    key = key.replace('_', '/')
    key_bytes = b64decode(key.encode('ASCII'))
    try:
        box = SecretBox(key_bytes)
        decrypted_bytes = box.decrypt(secret_bytes)
    except:
        return None, WRONG_KEY
    return decrypted_bytes.decode('UTF-8'), OK
Exemplo n.º 8
0
def decrypt(ciphertext, keyPath):
    """
    Decrypts a given message that was encrypted with the encrypt() method.

    :param ciphertext: The encrypted message (as a string).
    :param keyPath: A path to a file containing a 256-bit key (and nothing else).
    :type keyPath: str
    :rtype: str

    Raises an error if ciphertext was modified
    >>> import tempfile
    >>> k = tempfile.mktemp()
    >>> with open(k, 'w') as f:
    ...     f.write(nacl.utils.random(SecretBox.KEY_SIZE))
    >>> ciphertext = encrypt("testMessage", k)
    >>> ciphertext = chr(ord(ciphertext[0]) ^ 1) + ciphertext[1:]
    >>> decrypt(ciphertext, k)
    Traceback (most recent call last):
    ...
    CryptoError: Decryption failed. Ciphertext failed verification

    Otherwise works correctly
    >>> decrypt(encrypt("testMessage", k), k)
    'testMessage'
    """
    with open(keyPath) as f:
        key = f.read()
    if len(key) != SecretBox.KEY_SIZE:
        raise ValueError("Key is %d bytes, but must be exactly %d bytes" % (len(key),
                                                                            SecretBox.KEY_SIZE))
    sb = SecretBox(key)
    # The nonce is kept with the message.
    return sb.decrypt(ciphertext)
Exemplo n.º 9
0
    def decrypt(self,
                edata: Tuple[bytes, bytes],
                privkey: bytes = None) -> bytes:
        """
        Decrypt data encrypted by ECIES
        edata = (ekey, edata)
            ekey is needed to reconstruct a DH secret
            edata encrypted by the block cipher
            privkey is optional private key if we want to use something else
            than what keypair uses
        """
        if isinstance(edata[0], tuple) and isinstance(edata[1], tuple):
            # In case it was re-encrypted data
            return self.decrypt_reencrypted(edata)

        ekey, edata = edata
        # When it comes to decrypt(), ekey[1] is always None
        # we could use that and save 2 bytes,
        # but it makes the code less readable
        ekey = umbral.EncryptedKey(ekey=ec.deserialize(API.PRE.ecgroup,
                                                       ekey[0]),
                                   re_id=ekey[1])
        if privkey is None:
            privkey = self._priv_key
        else:
            privkey = ec.deserialize(API.PRE.ecgroup, privkey)

        key = self.pre.decapsulate(privkey, ekey)
        cipher = SecretBox(key)
        return cipher.decrypt(edata)
Exemplo n.º 10
0
class RecordPipe:
    def __init__(self, skt, send_key, receive_key):
        self.skt = skt
        self.send_box = SecretBox(send_key)
        self.send_nonce = 0
        self.receive_buf = ReceiveBuffer(self.skt)
        self.receive_box = SecretBox(receive_key)
        self.next_receive_nonce = 0

    def send_record(self, record):
        if not isinstance(record, type(b"")): raise UsageError
        assert SecretBox.NONCE_SIZE == 24
        assert self.send_nonce < 2**(8*24)
        assert len(record) < 2**(8*4)
        nonce = unhexlify("%048x" % self.send_nonce) # big-endian
        self.send_nonce += 1
        encrypted = self.send_box.encrypt(record, nonce)
        length = unhexlify("%08x" % len(encrypted)) # always 4 bytes long
        send_to(self.skt, length)
        send_to(self.skt, encrypted)

    def receive_record(self):
        length_buf = self.receive_buf.read(4)
        length = int(hexlify(length_buf), 16)
        encrypted = self.receive_buf.read(length)
        nonce_buf = encrypted[:SecretBox.NONCE_SIZE] # assume it's prepended
        nonce = int(hexlify(nonce_buf), 16)
        if nonce != self.next_receive_nonce:
            raise BadNonce("received out-of-order record")
        self.next_receive_nonce += 1
        record = self.receive_box.decrypt(encrypted)
        return record

    def close(self):
        self.skt.close()
Exemplo n.º 11
0
 def _decrypt_data(self, key, encrypted):
     assert isinstance(key, type(b"")), type(key)
     assert isinstance(encrypted, type(b"")), type(encrypted)
     if len(key) != SecretBox.KEY_SIZE: raise UsageError
     box = SecretBox(key)
     data = box.decrypt(encrypted)
     return data
Exemplo n.º 12
0
 def decrypt(self, ciphered: bytes) -> bytes:
     """
     Raises:
         CryptoError: if key is invalid.
     """
     box = SecretBox(self)
     return box.decrypt(ciphered)
Exemplo n.º 13
0
    def read_data(self, escrowed_data, sign_key, layer_count=None):
        self.log.info('reading data from %d escrowed bytes', len(escrowed_data))

        # TODO: check that we're logged in

        post_data = {
            'escrow_data': escrowed_data,
            'sign_key': serial.dumps(sign_key)
        }
        if layer_count is not None:
            post_data['layer_count'] = layer_count

        self.log.debug('requesting read_data')
        r = requests.post(
            self.url + 'data', params=self.get_auth_params(), data=post_data)
        boxed_data = r.content
        self.log.debug('got %d byte response from read_data', len(boxed_data))

        self.log.debug('unboxing response')
        key = bcrypt.kdf(
            self.password.encode('utf-8'),
            self.challenge,
            SecretBox.KEY_SIZE, ITERATIONS
        )
        box = SecretBox(key)
        data = box.decrypt(boxed_data)
        self.log.debug('unboxed response, got %d bytes', len(data))

        return data
Exemplo n.º 14
0
    def open_with_key(cls, key, multihash):
        box = SecretBox(bytes.fromhex(key))

        encrypted_data = cls.get_data(multihash)
        serialized_data = box.decrypt(encrypted_data)

        return cls(json.loads(serialized_data), key)
Exemplo n.º 15
0
    def recv_msg(self, user, packet):
        '''
		INPUT
			* user		: user message is from 
			* packet	: encrypted message received
		OUTPUT
			* Decrypted message with appropriate 
		'''
        try:
            dec_packet = encoder.decode(packet)
            self.send_ratc[user] = PublicKey(dec_packet[:32])
            mac_packet = dec_packet[32:64]
            enc_msg = dec_packet[64:]
            dh_sec = get_DH_secret(self.recv_ratc[user], self.send_ratc[user])
            salt = hashlib.md5(dh_sec).digest()
            self.root_keys[user] = kdf(self.root_keys[user], slt=salt)
            box = SecretBox(self.root_keys[user])
            msg = box.decrypt(enc_msg)
            mac = hashlib.pbkdf2_hmac('sha256', msg, self.ad_number[user],
                                      1414)
            if mac == mac_packet:
                return msg
            else:
                raise Exception('VERIFICATION FAILED: Connection to [' + user +
                                '] aborted.')
        except Exception as e:
            print '[SALSAJAR: recv_msg] ' + str(e)
Exemplo n.º 16
0
 def _decrypt_data(self, key, encrypted):
     assert isinstance(key, type(b"")), type(key)
     assert isinstance(encrypted, type(b"")), type(encrypted)
     if len(key) != SecretBox.KEY_SIZE: raise UsageError
     box = SecretBox(key)
     data = box.decrypt(encrypted)
     return data
Exemplo n.º 17
0
    def unpack_packet(self, data: bytes) -> Tuple[int, int, int, bytes]:
        """
        Unpacks a voice packet received from Discord.

        :param data: The data to unpack.
        :return: A tuple of (ssrc, sequence, timestamp, data).
        """
        header = data[:12]
        encrypted_data = data[12:]

        # unpack header data
        type_ = header[0]
        version = header[1]
        sequence = struct.unpack(">H", header[2:4])[0]
        timestamp = struct.unpack(">I", header[4:8])[0]
        ssrc = struct.unpack(">I", header[8:12])[0]

        # okay, for some reason discord sends malformed packets
        # 0x90 as type means we need to chop off the first 8 bytes from the decrypted data
        # because it's invalid opus
        # first, decrypt the data

        nonce = bytearray(24)
        nonce[:12] = header

        encryptor = SecretBox(bytes(self.vs_ws.secret_key))
        decrypted = encryptor.decrypt(encrypted_data, nonce=bytes(nonce))

        if type_ == 0x90:
            decrypted = decrypted[8:]

        pcm_frames = self.decoder.decode(decrypted, 960)

        return ssrc, sequence, timestamp, pcm_frames
Exemplo n.º 18
0
def encrypt_blocks(datablocks, debug):
    blockid_to_chunk = {}
    for V, block in enumerate(
        [datablocks[N] for N in range(0, len(datablocks))]):
        key = "%08x%08x" % (block.fileno, block.N)
        blockid_to_chunk[key] = V
    output = []
    phys_offs = 0
    for P, block in enumerate(datablocks):
        next_chunkid = blockid_to_chunk.get(
            "%08x%08x" % (block[0], block[1] + 1), 0xFFFFFFFF)
        nonce, encrypted_data = block_encrypt(block, nextid=next_chunkid)
        assert len(encrypted_data) == MAX_OUTER_LEN
        output.append(encrypted_data)
        if debug:
            key = sha512(block.secret, encoder=RawEncoder)
            box = SecretBox(key[:SecretBox.KEY_SIZE])
            data = box.decrypt(encrypted_data, nonce)
            assert len(data) == FRAME_LEN
            eprint("%4X %8X %s %4X %s %s %8X %s" %
                   (P, phys_offs, hexlify(encrypted_data[:4]), block.N,
                    sha512(block.secret)[:8], hexlify(
                        nonce[:4]), block.offset, sha512(data)[:8]))
        phys_offs += len(encrypted_data)
    return output
Exemplo n.º 19
0
    def decrypt(self, data, password, salt):
        salt = self._fix_salt(salt)
        key = hash_password_raw(password, hash_len=32, salt=salt)
        box = SecretBox(key)
        data = box.decrypt(data, encoder=nacl.encoding.Base64Encoder)

        return data
Exemplo n.º 20
0
def test_secret_box_decryption_combined(key, nonce, plaintext, ciphertext):
    box = SecretBox(key, encoder=HexEncoder)

    combined = binascii.hexlify(binascii.unhexlify(nonce) + binascii.unhexlify(ciphertext))
    decrypted = binascii.hexlify(box.decrypt(combined, encoder=HexEncoder))

    assert decrypted == plaintext
Exemplo n.º 21
0
 def _decrypt_data(self, key, encrypted):
     assert isinstance(key, type(b"")), type(key)
     assert isinstance(encrypted, type(b"")), type(encrypted)
     assert len(key) == SecretBox.KEY_SIZE, len(key)
     box = SecretBox(key)
     data = box.decrypt(encrypted)
     return data
Exemplo n.º 22
0
class EncryptedSerializer(object):
    """Encrypt session state using PyNaCl.

    :type secret: bytes
    :param secret: a 32-byte random secret for encrypting/decrypting the
                   pickled session state.
    :param serializer:
        An object with two methods: ``loads`` and ``dumps``. The ``loads``
        method should accept bytes and return a Python object. The ``dumps``
        method should accept a Python object and return bytes. A ``ValueError``
        should be raised for malformed inputs. Default: ``None``, which will
        use :class:`pyramid.session.PickleSerializer`.
    """
    def __init__(self, secret, serializer=None):
        if len(secret) != SecretBox.KEY_SIZE:
            raise ValueError(
                "Secret should be a random bytes string of length %d" %
                SecretBox.KEY_SIZE)
        self.box = SecretBox(secret)

        if serializer is None:
            serializer = PickleSerializer()

        self.serializer = serializer

    def loads(self, bstruct):
        """Decrypt session state.

        :type encrypted_state: bytes
        :param encrypted_state: the encrypted session state.

        :rtype: :class:`dict` / picklable mapping
        :returns: the decrypted, unpickled session state, as passed as
                  ``session_state`` to :meth:`dumps`.
        """
        try:
            b64padding = b'=' * (-len(bstruct) % 4)
            fstruct = urlsafe_b64decode(bstruct + b64padding)
        except (binascii.Error, TypeError) as e:
            raise ValueError('Badly formed base64 data: %s' % e)

        try:
            payload = self.box.decrypt(fstruct)
        except CryptoError as e:
            raise ValueError('Possible tampering: %s' % e)
        return self.serializer.loads(payload)

    def dumps(self, session_state):
        """Encrypt session state.

        :type session_state: :class:`dict` / picklable mapping
        :param session_state: the session state to be encrypted.

        :rtype: bytes
        :returns: the encrypted session state
        """
        cstruct = self.serializer.dumps(session_state)
        nonce = random(SecretBox.NONCE_SIZE)
        fstruct = self.box.encrypt(cstruct, nonce)
        return urlsafe_b64encode(fstruct).rstrip(b'=')
Exemplo n.º 23
0
def decrypt(ciphertext, keyPath):
    """
    Decrypts a given message that was encrypted with the encrypt() method.

    :param ciphertext: The encrypted message (as a string).
    :param keyPath: A path to a file containing a 256-bit key (and nothing else).
    :type keyPath: str
    :rtype: str

    Raises an error if ciphertext was modified
    >>> import tempfile
    >>> k = tempfile.mktemp()
    >>> with open(k, 'w') as f:
    ...     f.write(nacl.utils.random(SecretBox.KEY_SIZE))
    >>> ciphertext = encrypt("testMessage", k)
    >>> ciphertext = chr(ord(ciphertext[0]) ^ 1) + ciphertext[1:]
    >>> decrypt(ciphertext, k)
    Traceback (most recent call last):
    ...
    CryptoError: Decryption failed. Ciphertext failed verification

    Otherwise works correctly
    >>> decrypt(encrypt("testMessage", k), k)
    'testMessage'
    """
    with open(keyPath) as f:
        key = f.read()
    if len(key) != SecretBox.KEY_SIZE:
        raise ValueError("Key is %d bytes, but must be exactly %d bytes" %
                         (len(key), SecretBox.KEY_SIZE))
    sb = SecretBox(key)
    # The nonce is kept with the message.
    return sb.decrypt(ciphertext)
Exemplo n.º 24
0
    def decryptFile(self, inpPath, outPath, hexKey, chunkSize=65536):
        """Decrypt the data in the input file store the result in the output file.

        Args:
            inpPath ([type]): input file path (encrypted)
            outPath ([type]): output file path
            hexKey ([type]): encryption key
            chunkSize (int, optional): the size of incremental copy operations. Defaults to 65536.

        Returns:
            (bool): True for success or False otherwise
        """
        try:
            box = SecretBox(hexKey.encode("utf-8"), encoder=HexEncoder)
            tL = []
            with open(inpPath, "rb") as ifh:
                while True:
                    chunk = ifh.read(chunkSize)
                    if not chunk:
                        break
                    tL.append(chunk)
            #
            enctxt = b"".join(tL)
            #
            txt = box.decrypt(enctxt)
            tL = [txt[i:i + chunkSize] for i in range(0, len(txt), chunkSize)]
            logger.debug("Chunks %d size %d", len(tL), len(txt))
            #
            with open(outPath, "wb") as ofh:
                for tV in tL:
                    ofh.write(tV)
            return True
        except Exception as e:
            logger.exception("Failing with %r", str(e))
        return False
def run_exchange(transport, key, nonce):
    box = SecretBox(key)
    line = transport.receive_line()
    decrypted = box.decrypt(util.hexstr_to_bytes(line))
    transport.send_line(decrypted.decode('utf-8'))
    encrypted = util.bytes_to_hexstr(box.encrypt(decrypted, nonce))
    transport.send_line(encrypted)
    def handle(self, *args, **options):
        key = base64.b64decode(settings.PDK_BACKUP_KEY)

        for app in settings.INSTALLED_APPS:
            try:
                pdk_api = importlib.import_module(app + '.pdk_api')

                for encrypted_file in options['file']:
                    if os.path.exists(encrypted_file):
                        filename = os.path.basename(encrypted_file)

                        box = SecretBox(key)

                        with open(encrypted_file, 'rb') as backup_file:
                            content = box.decrypt(backup_file.read())

                            decompressed = bz2.decompress(content)

                            pdk_api.load_backup(filename, decompressed)
                    else:
                        raise RuntimeError(file + ' does not exist.')

            except ImportError:
                pass
            except AttributeError:
                pass
Exemplo n.º 27
0
class BoxStream(object):
    """
    i am a helper class for boxing datagrams in a unidirectional stream
    """
    key = attr.ib(validator=is_32bytes)
    initial_nonce = attr.ib(validator=is_24bytes)

    nonce = attr.ib(init=False, default=None)

    MAX_LEN = 4096

    def __attrs_post_init__(self):
        self.nonce = NonceCounter(self.initial_nonce,
                                  int(SecretBox.NONCE_SIZE))
        self.box = SecretBox(self.key)

    def encrypt(self, datagram):
        """
        SecretBox-encrypt the datagram and return ciphertext
        """
        assert len(datagram) < self.MAX_LEN
        encrypted_body = self.box.encrypt(datagram, nonce=self.nonce())
        return encrypted_body.ciphertext

    def decrypt(self, datagram):
        """
        given SecretBox`ed ciphertext, decrypt and return plaintext
        """
        assert len(datagram) < self.MAX_LEN
        payload = self.box.decrypt(datagram, nonce=self.nonce())
        return payload
Exemplo n.º 28
0
class RecordPipe:
    def __init__(self, skt, send_key, receive_key):
        self.skt = skt
        self.send_box = SecretBox(send_key)
        self.send_nonce = 0
        self.receive_buf = ReceiveBuffer(self.skt)
        self.receive_box = SecretBox(receive_key)
        self.next_receive_nonce = 0

    def send_record(self, record):
        if not isinstance(record, type(b"")): raise UsageError
        assert SecretBox.NONCE_SIZE == 24
        assert self.send_nonce < 2**(8 * 24)
        assert len(record) < 2**(8 * 4)
        nonce = unhexlify("%048x" % self.send_nonce)  # big-endian
        self.send_nonce += 1
        encrypted = self.send_box.encrypt(record, nonce)
        length = unhexlify("%08x" % len(encrypted))  # always 4 bytes long
        send_to(self.skt, length)
        send_to(self.skt, encrypted)

    def receive_record(self):
        length_buf = self.receive_buf.read(4)
        length = int(hexlify(length_buf), 16)
        encrypted = self.receive_buf.read(length)
        nonce_buf = encrypted[:SecretBox.NONCE_SIZE]  # assume it's prepended
        nonce = int(hexlify(nonce_buf), 16)
        if nonce != self.next_receive_nonce:
            raise BadNonce("received out-of-order record")
        self.next_receive_nonce += 1
        record = self.receive_box.decrypt(encrypted)
        return record

    def close(self):
        self.skt.close()
Exemplo n.º 29
0
def test_secret_box_decryption(key, nonce, plaintext, ciphertext):
    box = SecretBox(key, encoder=HexEncoder)

    nonce = binascii.unhexlify(nonce)
    decrypted = binascii.hexlify(
        box.decrypt(ciphertext, nonce, encoder=HexEncoder), )

    assert decrypted == plaintext
Exemplo n.º 30
0
def test_secret_box_optional_nonce(key, nonce, plaintext, ciphertext):
    box = SecretBox(key, encoder=HexEncoder)

    encrypted = box.encrypt(binascii.unhexlify(plaintext), encoder=HexEncoder)

    decrypted = binascii.hexlify(box.decrypt(encrypted, encoder=HexEncoder))

    assert decrypted == plaintext
Exemplo n.º 31
0
def test_secret_box_decryption_combined(key, nonce, plaintext, ciphertext):
    box = SecretBox(key, encoder=HexEncoder)

    combined = binascii.hexlify(
        binascii.unhexlify(nonce) + binascii.unhexlify(ciphertext), )
    decrypted = binascii.hexlify(box.decrypt(combined, encoder=HexEncoder))

    assert decrypted == plaintext
Exemplo n.º 32
0
def decrypt_list_entry(boxed, symkey, tmppub):
    sbox = SecretBox(symkey)
    msg = remove_prefix(sbox.decrypt(boxed), "list:", NotListResponseError)
    (got_tmppub, fetch_token, delete_token,
     length) = struct.unpack(">32s32s32sQ", msg)
    if not equal(got_tmppub, tmppub):
        raise WrongPubkeyError
    return fetch_token, delete_token, length
Exemplo n.º 33
0
def test_secret_box_optional_nonce(key, nonce, plaintext, ciphertext):
    box = SecretBox(key, encoder=HexEncoder)

    encrypted = box.encrypt(binascii.unhexlify(plaintext), encoder=HexEncoder)

    decrypted = binascii.hexlify(box.decrypt(encrypted, encoder=HexEncoder))

    assert decrypted == plaintext
Exemplo n.º 34
0
def decrypt(ciphertext, key):
    with open(key, mode="rb") as key_file:
        key_bytes = key_file.read()
    with open(ciphertext, mode="rb") as ciphertext_file:
        ciphertext = ciphertext_file.read()
    secret_box = SecretBox(key_bytes)
    plaintext = secret_box.decrypt(ciphertext)
    print(f"Plaintext: {plaintext.decode('ascii')}")
Exemplo n.º 35
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': ''}
Exemplo n.º 36
0
def decrypt(ciphertext, key):
    """
    Decrypt a bytestring with the given key.
    :param ciphertext: a bytestring to decrypt
    :param key: a 32 byte long bytestring
    :return: resulting plaintext
    """
    sb = SecretBox(key)
    return sb.decrypt(ciphertext)
Exemplo n.º 37
0
def decrypt_list_entry(boxed, symkey, tmppub):
    sbox = SecretBox(symkey)
    msg = remove_prefix(sbox.decrypt(boxed),
                        "list:", NotListResponseError)
    (got_tmppub, fetch_token, delete_token,
     length) = struct.unpack(">32s32s32sQ", msg)
    if not equal(got_tmppub, tmppub):
        raise WrongPubkeyError
    return fetch_token, delete_token, length
Exemplo n.º 38
0
def test_secret_box_decryption(key, nonce, plaintext, ciphertext):
    box = SecretBox(key, encoder=HexEncoder)

    nonce = binascii.unhexlify(nonce)
    decrypted = binascii.hexlify(
        box.decrypt(ciphertext, nonce, encoder=HexEncoder),
    )

    assert decrypted == plaintext
Exemplo n.º 39
0
def secret_box_decrypt(key_material: bytes, salt: bytes,
                       ciphertext: bytes) -> bytes:
    wrapping_key = derive_wrapping_key_from_key_material(key_material, salt)
    secret_box = SecretBox(wrapping_key)
    try:
        plaintext = secret_box.decrypt(ciphertext)
    except CryptoError as e:
        raise SecretBoxAuthenticationError from e
    return plaintext
Exemplo n.º 40
0
 def descifra_mensaje(self, mensaje):
     """
     Descrifra un mensaje con una Sbox
     :param mensaje: mensaje cifrado en base 64 y con Sbox
     :return: el mensaje descifrado
     """
     sb = SecretBox(self.krecib)
     dec = sb.decrypt(base64.b64decode(mensaje))
     return dec
Exemplo n.º 41
0
def decrypt(data, privkey):
    j,enc = data.split(sep=_separator, maxsplit=1)
    jsondata = json.loads(j.decode("utf-8"))
    key = base64.b64decode(jsondata["key"].encode("utf-8"))
    version = jsondata["version"]
    if version == "0.1":
        box = SecretBox(key)
        return box.decrypt(enc).decode("utf-8")
    else:
        raise NotImplementedError("Filetype not supported.")
Exemplo n.º 42
0
def symm_decrypt(key: bytes, ciphertext: bytes) -> bytes:
    """
    Decrypts ciphertext performed with nacl.SecretBox.

    :param key: Key to decrypt with
    :param ciphertext: Nacl.SecretBox ciphertext to decrypt

    :return: Decrypted Plaintext
    """
    cipher = SecretBox(key)
    return cipher.decrypt(ciphertext)
Exemplo n.º 43
0
def test_secret_box_bad_decryption():
    box = SecretBox(b"\x11" * 32)
    ciphertext = box.encrypt(b"hello")

    with pytest.raises(CryptoError):
        # changes the nonce
        box.decrypt(flip_byte(ciphertext, 0))
    with pytest.raises(CryptoError):
        # changes ciphertext
        box.decrypt(flip_byte(ciphertext, 24))
    with pytest.raises(CryptoError):
        # changes MAC tag
        box.decrypt(flip_byte(ciphertext, len(ciphertext) - 1))

    with pytest.raises(CryptoError):
        # completely changes ciphertext and tag
        box.decrypt(ciphertext + b"\x00")
    with pytest.raises(CryptoError):
        # completely changes everything
        box.decrypt(b"\x00" + ciphertext)
Exemplo n.º 44
0
def test_secret_box_bad_decryption():
    box = SecretBox(b"\x11" * 32)
    ciphertext = box.encrypt(b"hello")

    with pytest.raises(CryptoError):
        # changes the nonce
        box.decrypt(flip_byte(ciphertext, 0))
    with pytest.raises(CryptoError):
        # changes ciphertext
        box.decrypt(flip_byte(ciphertext, 24))
    with pytest.raises(CryptoError):
        # changes MAC tag
        box.decrypt(flip_byte(ciphertext, len(ciphertext) - 1))

    with pytest.raises(CryptoError):
        # completely changes ciphertext and tag
        box.decrypt(ciphertext + b"\x00")
    with pytest.raises(CryptoError):
        # completely changes everything
        box.decrypt(b"\x00" + ciphertext)
Exemplo n.º 45
0
class PassphraseBox(object):

    ITERATIONS = 90000

    # Given passphrase and plaintext as strings, returns a dict
    # containing the ciphertext and other values needed for later
    # decryption.  Binary values are encoded as hexadecimal strings.
    @classmethod
    def encrypt(cls, passphrase, plaintext):
        box = cls(passphrase)
        return box._encrypt(plaintext)

    # encrypted = dict(salt=salt, nonce=nonce, ciphertext=ciphertext)
    # PassphraseBox.decrypt("my great password", encrypted)
    @classmethod
    def decrypt(cls, passphrase, encrypted):
        salt = encrypted['salt']
        iterations = encrypted['iterations']

        return cls(passphrase, salt, iterations)._decrypt(
            encrypted['ciphertext'], encrypted['nonce'])

    # Initialize with an existing salt and iterations to allow
    # decryption.  Otherwise, creates new values for these, meaning
    # it creates an entirely new secret box.
    def __init__(self, passphrase, salt=None, iterations=None):
        passphrase = passphrase.encode('utf-8')
        self.salt = salt.decode('hex') if salt else urandom(16)
        if iterations:
            self.iterations = iterations
        else:
            # per OWASP, use a random number of iterations between 90k and 110k
            self.iterations = self.ITERATIONS + randint(0,20000)

        key = pbkdf2_bin(passphrase,
                         salt=self.salt,
                         iterations=self.iterations,
                         keylen=32)

        self.box = SecretBox(key)


    def _encrypt(self, plaintext):
        plaintext = plaintext.encode('utf-8')
        nonce = urandom(SecretBox.NONCE_SIZE)
        encrypted = self.box.encrypt(plaintext, nonce)
        ciphertext = encrypted.ciphertext
        return dict(
            salt=self.salt.encode('hex'), iterations=self.iterations,
            nonce=nonce.encode('hex'), ciphertext=ciphertext.encode('hex')
        )

    def _decrypt(self, ciphertext, nonce):
        return self.box.decrypt(ciphertext.decode('hex'), nonce.decode('hex'))
Exemplo n.º 46
0
class PassphraseBox:

    ITERATIONS = 10000

    @classmethod
    def encrypt(cls, passphrase, plaintext):
        box = cls(passphrase)
        return box._encrypt(plaintext)

    @classmethod
    def decrypt(cls, passphrase, encrypted):
        salt = encrypted['salt']
        iterations = encrypted['iterations']

        ppbox = cls(passphrase, salt, iterations)
        return ppbox._decrypt(encrypted['ciphertext'], encrypted['nonce'])

    def __init__(self, passphrase, salt=None, iterations=None):
        passphrase = passphrase.encode('utf-8')
        if salt is None:
            salt = random(16)
            iterations = self.ITERATIONS
        else:
            salt = salt.decode('hex')

        key = PBKDF2(passphrase, salt, 32, iterations)

        self.salt = salt
        self.iterations = iterations
        self.box = SecretBox(key)


    def _encrypt(self, plaintext):
        plaintext = plaintext.encode('utf-8')
        nonce = random(SecretBox.NONCE_SIZE)
        encrypted = self.box.encrypt(plaintext, nonce)
        ciphertext = encrypted.ciphertext
        return dict(
            salt=self.salt.encode('hex'), iterations=self.iterations,
            nonce=nonce.encode('hex'), ciphertext=ciphertext.encode('hex')
        )

    def _decrypt(self, ciphertext, nonce):
        return self.box.decrypt(ciphertext.decode('hex'), nonce.decode('hex'))
Exemplo n.º 47
0
class EncryptingPickleSerializer(object):
    """Encrypt pickled session state using PyNaCl.

    :type secret: bytes
    :param secret: a 32-byte random secret for encrypting/decrypting the
                   pickled session state.
    """
    def __init__(self, secret):
        if len(secret) != SecretBox.KEY_SIZE:
            raise ValueError(
                "Secret should be a random bytes string of length %d"
                    % SecretBox.KEY_SIZE)
        self.box = SecretBox(secret)

    def loads(self, encrypted_state):
        """Decrypt session state.

        :type encrypted_state: bytes
        :param encrypted_state: the encrypted session state.

        :rtype: :class:`dict` / picklable mapping
        :returns: the decrypted, unpickled session state, as passed as
                  ``session_state`` to :meth:`dumps`.
        """
        payload = self.box.decrypt(urlsafe_b64decode(encrypted_state))
        return pickle.loads(payload)

    def dumps(self, session_state):
        """Encrypt session state.

        :type session_state: :class:`dict` / picklable mapping
        :param session_state: the session state to be encrypted.

        :rtype: bytes
        :returns: the encrypted session state
        """
        pickled = pickle.dumps(session_state)
        nonce = random(SecretBox.NONCE_SIZE)
        return urlsafe_b64encode(self.box.encrypt(pickled, nonce))
Exemplo n.º 48
0
class NaclPassphraseBox(object):

    # FIXME:  PassphraseBox in Ruby has the default iterations set
    # to 100,000.  One or the other needs to change.
    ITERATIONS = 10000

    # encrypted = dict(salt=salt, nonce=nonce, ciphertext=ciphertext)
    # PassphraseBox.decrypt("my great password", encrypted)
    @classmethod
    def decrypt(cls, passphrase, encrypted):
        salt = encrypted['salt']
        iterations = encrypted['iterations']

        ppbox = cls(passphrase, salt, iterations)
        return ppbox._decrypt(encrypted['ciphertext'], encrypted['nonce'])

    # Initialize with an existing salt and iterations to allow
    # decryption.  Otherwise, creates new values for these, meaning
    # it creates an entirely new secret box.
    def __init__(self, passphrase, salt=None, iterations=None):
        warn(("CoinOp no longer uses libsodium - you should only see this "
              "message in migration utilities."), DeprecationWarning)
        passphrase = passphrase.encode('utf-8')
        if salt is None:
            salt = random(16)
            iterations = self.ITERATIONS
        else:
            salt = salt.decode('hex')

        key = PBKDF2(passphrase, salt, 32, iterations)

        self.salt = salt
        self.iterations = iterations
        self.box = SecretBox(key)

    def _decrypt(self, ciphertext, nonce):
        return self.box.decrypt(ciphertext.decode('hex'), nonce.decode('hex'))
Exemplo n.º 49
0
def init(server, username, keydir, action, message, recipients):
    """ SHSM CLI client. """

    global serverurl
    serverurl = server

    if action == "register":
        master_signing_key = SigningKey.generate()
        device_signing_key = SigningKey.generate()
        device_private_key = PrivateKey.generate()

        enc_master_verify_key = master_signing_key.verify_key.encode(encoder=HexEncoder)
        register(username, enc_master_verify_key)

        # TODO: make sure keydir exists

        save_key(master_signing_key.encode(encoder=HexEncoder), keydir + "/master_signing_key")
        save_key(device_signing_key.encode(encoder=HexEncoder), keydir + "/device_signing_key")
        save_key(device_private_key.encode(encoder=HexEncoder), keydir + "/device_private_key")

    else:
        try:
            master_signing_key = SigningKey(load_key(keydir + "/master_signing_key"), encoder=HexEncoder)
            device_signing_key = SigningKey(load_key(keydir + "/device_signing_key"), encoder=HexEncoder)
            device_private_key = PrivateKey(load_key(keydir + "/device_private_key"), encoder=HexEncoder)
        except TypeError:
            print "bad key, exiting."
            exit()

        if action == "add-device":

            enc_device_verify_key = device_signing_key.verify_key.encode(encoder=HexEncoder)
            enc_signed_device_verify_key = b64encode(master_signing_key.sign(enc_device_verify_key))

            enc_device_public_key = device_private_key.public_key.encode(encoder=HexEncoder)
            enc_signed_device_public_key = b64encode(master_signing_key.sign(enc_device_public_key))

            add_device(username, enc_signed_device_verify_key, enc_signed_device_public_key)

        if action == "send-message":

            ephemeral_key = PrivateKey.generate()
            enc_ephemeral_public_key = b64encode(
                device_signing_key.sign(ephemeral_key.public_key.encode(encoder=HexEncoder))
            )

            # TODO:: should sign binary text, no? b"bob"
            destination_usernames = recipients.split(",")
            enc_dest_usernames = b64encode(
                device_signing_key.sign(json.dumps({"destination_usernames": destination_usernames}))
            )
            symmetric_key = random(SecretBox.KEY_SIZE)
            symmetric_box = SecretBox(symmetric_key)
            nonce = random(SecretBox.NONCE_SIZE)
            msg_manifest = {}
            msg_manifest["recipients"] = {}
            msg_manifest["msg"] = b64encode(symmetric_box.encrypt(str(message), nonce))

            for dest_user in destination_usernames:
                msg_manifest["recipients"][dest_user] = {}

                for recipient_key in get_recipient_keys(
                    device_signing_key.verify_key.encode(encoder=HexEncoder),
                    b64encode(device_signing_key.sign(str(dest_user))),
                ):

                    # TODO:: should sign binary text, no?
                    crypt_box = Box(ephemeral_key, recipient_key)
                    nonce = random(Box.NONCE_SIZE)
                    crypt_key = b64encode(crypt_box.encrypt(symmetric_key, nonce))
                    dest_key = recipient_key.encode(encoder=HexEncoder)
                    msg_manifest["recipients"][dest_user][dest_key] = crypt_key

            enc_signed_crypt_msg = b64encode(device_signing_key.sign(json.dumps(msg_manifest)))

            send_message(
                device_signing_key.verify_key.encode(encoder=HexEncoder),
                enc_dest_usernames,
                enc_signed_crypt_msg,
                enc_ephemeral_public_key,
            )

        if action == "get-messages":

            enc_device_verify_key = device_signing_key.verify_key.encode(encoder=HexEncoder)
            enc_signed_device_verify_key = b64encode(device_signing_key.sign(enc_device_verify_key))
            messages = get_messages(enc_device_verify_key, enc_signed_device_verify_key)

            for message_public_key in messages["messages"].keys():
                try:
                    crypto_box = Box(device_private_key, PublicKey(b64decode(message_public_key), encoder=HexEncoder))
                except TypeError:
                    print "not a valid public key"
                    exit()
                packed_msg = json.loads(messages["messages"][message_public_key])
                msg_manifest = json.loads(b64decode(packed_msg["message_manifest"]))
                dest_pub_key = device_private_key.public_key.encode(encoder=HexEncoder)
                symmetric_key = crypto_box.decrypt(b64decode(msg_manifest["recipients"][username][dest_pub_key]))
                symmetric_box = SecretBox(symmetric_key)
                print ("From: %s\nMessage: %s") % (
                    packed_msg["reply_to"],
                    symmetric_box.decrypt(b64decode(msg_manifest["msg"])),
                )
Exemplo n.º 50
0
 def _extract_caveat_key(self, signature, caveat):
     key = truncate_or_pad(signature)
     box = SecretBox(key=key)
     decrypted = box.decrypt(caveat._verification_key_id)
     return decrypted
Exemplo n.º 51
0
class Connection(protocol.Protocol, policies.TimeoutMixin):
    def __init__(self, owner, relay_handshake, start):
        self.state = "too-early"
        self.buf = b""
        self.owner = owner
        self.relay_handshake = relay_handshake
        self.start = start
        self._negotiation_d = defer.Deferred(self._cancel)
        self._error = None
        self._consumer = None
        self._inbound_records = collections.deque()
        self._waiting_reads = collections.deque()

    def connectionMade(self):
        debug("handle %r" %  (self.transport,))
        self.setTimeout(TIMEOUT) # does timeoutConnection() when it expires
        self.factory.connectionWasMade(self, self.transport.getPeer())

    def startNegotiation(self, description):
        self.description = description
        if self.relay_handshake is not None:
            self.transport.write(self.relay_handshake)
            self.state = "relay"
        else:
            self.state = "start"
        self.dataReceived(b"") # cycle the state machine
        return self._negotiation_d

    def _cancel(self, d):
        self.state = "hung up" # stop reacting to anything further
        self._error = defer.CancelledError()
        self.transport.loseConnection()
        # if connectionLost isn't called synchronously, then our
        # self._negotiation_d will have been errbacked by Deferred.cancel
        # (which is our caller). So if it's still around, clobber it
        if self._negotiation_d:
            self._negotiation_d = None


    def dataReceived(self, data):
        try:
            self._dataReceived(data)
        except Exception as e:
            self.setTimeout(None)
            self._error = e
            self.transport.loseConnection()
            self.state = "hung up"
            if not isinstance(e, BadHandshake):
                raise

    def _check_and_remove(self, expected):
        # any divergence is a handshake error
        if not self.buf.startswith(expected[:len(self.buf)]):
            raise BadHandshake("got %r want %r" % (self.buf, expected))
        if len(self.buf) < len(expected):
            return False # keep waiting
        self.buf = self.buf[len(expected):]
        return True

    def _dataReceived(self, data):
        # protocol is:
        #  (maybe: send relay handshake, wait for ok)
        #  send (send|receive)_handshake
        #  wait for (receive|send)_handshake
        #  sender: decide, send "go" or hang up
        #  receiver: wait for "go"
        self.buf += data

        assert self.state != "too-early"
        if self.state == "relay":
            if not self._check_and_remove(b"ok\n"):
                return
            self.state = "start"
        if self.state == "start":
            self.transport.write(self.owner._send_this())
            self.state = "handshake"
        if self.state == "handshake":
            if not self._check_and_remove(self.owner._expect_this()):
                return
            self.state = self.owner.connection_ready(self, self.description)
            # If we're the receiver, we'll be moved to state
            # "wait-for-decision", which means we're waiting for the other
            # side (the sender) to make a decision. If we're the sender,
            # we'll either be moved to state "go" (send GO and move directly
            # to state "records") or state "nevermind" (send NEVERMIND and
            # hang up).

        if self.state == "wait-for-decision":
            if not self._check_and_remove(b"go\n"):
                return
            self._negotiationSuccessful()
        if self.state == "go":
            GO = b"go\n"
            self.transport.write(GO)
            self._negotiationSuccessful()
        if self.state == "nevermind":
            self.transport.write(b"nevermind\n")
            raise BadHandshake("abandoned")
        if self.state == "records":
            return self.dataReceivedRECORDS()
        if isinstance(self.state, Exception): # for tests
            raise self.state

    def _negotiationSuccessful(self):
        self.state = "records"
        self.setTimeout(None)
        send_key = self.owner._sender_record_key()
        self.send_box = SecretBox(send_key)
        self.send_nonce = 0
        receive_key = self.owner._receiver_record_key()
        self.receive_box = SecretBox(receive_key)
        self.next_receive_nonce = 0
        d, self._negotiation_d = self._negotiation_d, None
        d.callback(self)

    def dataReceivedRECORDS(self):
        if len(self.buf) < 4:
            return
        length = int(hexlify(self.buf[:4]), 16)
        if len(self.buf) < 4+length:
            return
        encrypted, self.buf = self.buf[4:4+length], self.buf[4+length:]

        record = self._decrypt_record(encrypted)
        self.recordReceived(record)

    def _decrypt_record(self, encrypted):
        nonce_buf = encrypted[:SecretBox.NONCE_SIZE] # assume it's prepended
        nonce = int(hexlify(nonce_buf), 16)
        if nonce != self.next_receive_nonce:
            raise BadNonce("received out-of-order record: got %d, expected %d"
                           % (nonce, self.next_receive_nonce))
        self.next_receive_nonce += 1
        record = self.receive_box.decrypt(encrypted)
        return record

    def send_record(self, record):
        if not isinstance(record, type(b"")): raise UsageError
        assert SecretBox.NONCE_SIZE == 24
        assert self.send_nonce < 2**(8*24)
        assert len(record) < 2**(8*4)
        nonce = unhexlify("%048x" % self.send_nonce) # big-endian
        self.send_nonce += 1
        encrypted = self.send_box.encrypt(record, nonce)
        length = unhexlify("%08x" % len(encrypted)) # always 4 bytes long
        self.transport.write(length)
        self.transport.write(encrypted)

    def recordReceived(self, record):
        if self._consumer:
            self._consumer.write(record)
            return
        self._inbound_records.append(record)
        self._deliverRecords()

    def receive_record(self):
        d = defer.Deferred()
        self._waiting_reads.append(d)
        self._deliverRecords()
        return d

    def _deliverRecords(self):
        while self._inbound_records and self._waiting_reads:
            r = self._inbound_records.popleft()
            d = self._waiting_reads.popleft()
            d.callback(r)

    def close(self):
        self.transport.loseConnection()
        while self._waiting_reads:
            d = self._waiting_reads.popleft()
            d.errback(error.ConnectionClosed())

    def timeoutConnection(self):
        self._error = BadHandshake("timeout")
        self.transport.loseConnection()

    def connectionLost(self, reason=None):
        self.setTimeout(None)
        d, self._negotiation_d = self._negotiation_d, None
        # the Deferred is only relevant until negotiation finishes, so skip
        # this if it's alredy been fired
        if d:
            # Each call to loseConnection() sets self._error first, so we can
            # deliver useful information to the Factory that's waiting on
            # this (although they'll generally ignore the specific error,
            # except for logging unexpected ones). The possible cases are:
            #
            # cancel: defer.CancelledError
            # far-end disconnect: BadHandshake("connection lost")
            # handshake error (something we didn't like): BadHandshake(what)
            # other error: some other Exception
            # timeout: BadHandshake("timeout")

            d.errback(self._error or BadHandshake("connection lost"))

    # IConsumer methods, for outbound flow-control. We pass these through to
    # the transport. The 'producer' is something like a t.p.basic.FileSender
    def registerProducer(self, producer, streaming):
        assert interfaces.IConsumer.providedBy(self.transport)
        self.transport.registerProducer(producer, streaming)
    def unregisterProducer(self):
        self.transport.unregisterProducer()
    def write(self, data):
        self.send_record(data)

    # IProducer methods, for inbound flow-control. We pass these through to
    # the transport.
    def stopProducing(self):
        self.transport.stopProducing()
    def pauseProducing(self):
        self.transport.pauseProducing()
    def resumeProducing(self):
        self.transport.resumeProducing()

    # Helper method to glue an instance of e.g. t.p.ftp.FileConsumer to us.
    # Inbound records will be written as bytes to the consumer.
    def connectConsumer(self, consumer):
        if self._consumer:
            raise RuntimeError("A consumer is already attached: %r" %
                               self._consumer)
        self._consumer = consumer
        # drain any pending records
        while self._inbound_records:
            r = self._inbound_records.popleft()
            consumer.write(r)
        consumer.registerProducer(self, True)

    def disconnectConsumer(self):
        self._consumer.unregisterProducer()
        self._consumer = None
Exemplo n.º 52
0
 def _decrypt_data(self, key, encrypted):
     assert len(key) == SecretBox.KEY_SIZE
     box = SecretBox(key)
     data = box.decrypt(encrypted)
     return data
Exemplo n.º 53
0
 def decrypt(self, signature, field_data):
     key = truncate_or_pad(signature)
     box = SecretBox(key=key)
     encoded = convert_to_bytes(field_data[len(self.signifier):])
     decrypted = box.decrypt(standard_b64decode(encoded))
     return convert_to_string(decrypted)
Exemplo n.º 54
0
class CurveCPServerDispatcher(DatagramProtocol):
    def __init__(self, reactor, serverKey, factory):
        self.reactor = reactor
        self.serverKey = serverKey
        self.factory = factory
        self.transports = {}
        self._secretBox = SecretBox(os.urandom(SecretBox.KEY_SIZE))

    def _replyWithCookie(self, data, host_port):
        if len(data) != _helloStruct.size:
            return
        serverExtension, clientExtension, clientShortPubkey, nonce, encrypted = _helloStruct.unpack(data)
        serverLongClientShort = Box(self.serverKey.key, PublicKey(clientShortPubkey))
        try:
            serverLongClientShort.decrypt(encrypted, 'CurveCP-client-H' + nonce)
        except CryptoError:
            return
        serverShortKey = PrivateKey.generate()
        unencryptedCookie = clientShortPubkey + str(serverShortKey)
        cookieNonce = self.serverKey.nonce(longterm=True)
        cookie = cookieNonce + self._secretBox.encrypt(unencryptedCookie, 'c' * 8 + cookieNonce).ciphertext
        boxData = str(serverShortKey.public_key) + cookie
        cookiePacket = (
            'RL3aNMXK'
            + clientExtension
            + serverExtension
            + cookieNonce
            + serverLongClientShort.encrypt(boxData, 'CurveCPK' + cookieNonce).ciphertext)
        self.transport.write(cookiePacket, host_port)

    def _checkInitiate(self, clientID, data, host_port):
        cookieNonce, encryptedCookie, nonce = _initiateStruct.unpack_from(data)
        try:
            decryptedCookie = self._secretBox.decrypt(encryptedCookie, 'c' * 8 + cookieNonce)
        except CryptoError:
            return
        clientShortPubkey = PublicKey(decryptedCookie[:32])
        serverShortKey = PrivateKey(decryptedCookie[32:])
        serverShortClientShort = Box(serverShortKey, clientShortPubkey)
        try:
            decrypted = serverShortClientShort.decrypt(data[176:], 'CurveCP-client-I' + nonce)
        except CryptoError:
            return
        clientPubkeyString, vouchNonce, encryptedVouch, serverDomain = _initiateInnerStruct.unpack_from(decrypted)
        clientPubkey = PublicKey(clientPubkeyString)
        serverLongClientLong = Box(self.serverKey.key, clientPubkey)
        try:
            vouchKey = serverLongClientLong.decrypt(encryptedVouch, 'CurveCPV' + vouchNonce)
        except CryptoError:
            return
        if vouchKey != str(clientShortPubkey):
            return
        transport = CurveCPServerTransport(
            self.reactor, self.serverKey, self.factory, clientID,
            clientPubkey, host_port, serverShortClientShort, dnsToName(serverDomain))
        return transport, decrypted[352:]

    def datagramReceived(self, data, host_port):
        l = len(data)
        if l < 80 or l > 1184 or l & 0xf:
            return

        if data[:8] == 'QvnQ5XlH':
            self._replyWithCookie(data, host_port)
            return

        clientID = data[8:72]
        if clientID in self.transports:
            self.transports[clientID].datagramReceived(data, host_port)
            return

        if data[:8] != 'QvnQ5XlI':
            return
        result = self._checkInitiate(clientID, data, host_port)
        if result is None:
            return
        log.msg('new client: %s' % clientID.encode('hex'), category='success')
        transport, message = result
        self.transports[clientID] = transport
        transport.transport = self.transport
        transport.startProtocol()
        transport._parseMessage(transport._now(), message)
        transport.notifyFinish().addCallback(self._clientFinished, clientID)

    def _clientFinished(self, ign, clientID):
        del self.transports[clientID]
Exemplo n.º 55
0
 def _decrypt(self, bin_data, key):
     bin_data = bin_data.split(':::')
     derived_key = self._get_key(key, bin_data[0])
     private_box = SecretBox(derived_key[1])
     return str(private_box.decrypt(bin_data[1]))
Exemplo n.º 56
0
class Connection(protocol.Protocol, policies.TimeoutMixin):
    def __init__(self, owner, relay_handshake, start, description):
        self.state = "too-early"
        self.buf = b""
        self.owner = owner
        self.relay_handshake = relay_handshake
        self.start = start
        self._description = description
        self._negotiation_d = defer.Deferred(self._cancel)
        self._error = None
        self._consumer = None
        self._consumer_bytes_written = 0
        self._consumer_bytes_expected = None
        self._consumer_deferred = None
        self._inbound_records = deque()
        self._waiting_reads = deque()

    def connectionMade(self):
        self.setTimeout(TIMEOUT) # does timeoutConnection() when it expires
        self.factory.connectionWasMade(self)

    def startNegotiation(self):
        if self.relay_handshake is not None:
            self.transport.write(self.relay_handshake)
            self.state = "relay"
        else:
            self.state = "start"
        self.dataReceived(b"") # cycle the state machine
        return self._negotiation_d

    def _cancel(self, d):
        self.state = "hung up" # stop reacting to anything further
        self._error = defer.CancelledError()
        self.transport.loseConnection()
        # if connectionLost isn't called synchronously, then our
        # self._negotiation_d will have been errbacked by Deferred.cancel
        # (which is our caller). So if it's still around, clobber it
        if self._negotiation_d:
            self._negotiation_d = None


    def dataReceived(self, data):
        try:
            self._dataReceived(data)
        except Exception as e:
            self.setTimeout(None)
            self._error = e
            self.transport.loseConnection()
            self.state = "hung up"
            if not isinstance(e, BadHandshake):
                raise

    def _check_and_remove(self, expected):
        # any divergence is a handshake error
        if not self.buf.startswith(expected[:len(self.buf)]):
            raise BadHandshake("got %r want %r" % (self.buf, expected))
        if len(self.buf) < len(expected):
            return False # keep waiting
        self.buf = self.buf[len(expected):]
        return True

    def _dataReceived(self, data):
        # protocol is:
        #  (maybe: send relay handshake, wait for ok)
        #  send (send|receive)_handshake
        #  wait for (receive|send)_handshake
        #  sender: decide, send "go" or hang up
        #  receiver: wait for "go"
        self.buf += data

        assert self.state != "too-early"
        if self.state == "relay":
            if not self._check_and_remove(b"ok\n"):
                return
            self.state = "start"
        if self.state == "start":
            self.transport.write(self.owner._send_this())
            self.state = "handshake"
        if self.state == "handshake":
            if not self._check_and_remove(self.owner._expect_this()):
                return
            self.state = self.owner.connection_ready(self)
            # If we're the receiver, we'll be moved to state
            # "wait-for-decision", which means we're waiting for the other
            # side (the sender) to make a decision. If we're the sender,
            # we'll either be moved to state "go" (send GO and move directly
            # to state "records") or state "nevermind" (send NEVERMIND and
            # hang up).

        if self.state == "wait-for-decision":
            if not self._check_and_remove(b"go\n"):
                return
            self._negotiationSuccessful()
        if self.state == "go":
            GO = b"go\n"
            self.transport.write(GO)
            self._negotiationSuccessful()
        if self.state == "nevermind":
            self.transport.write(b"nevermind\n")
            raise BadHandshake("abandoned")
        if self.state == "records":
            return self.dataReceivedRECORDS()
        if self.state == "hung up":
            return
        if isinstance(self.state, Exception): # for tests
            raise self.state
        raise ValueError("internal error: unknown state %s" % (self.state,))

    def _negotiationSuccessful(self):
        self.state = "records"
        self.setTimeout(None)
        send_key = self.owner._sender_record_key()
        self.send_box = SecretBox(send_key)
        self.send_nonce = 0
        receive_key = self.owner._receiver_record_key()
        self.receive_box = SecretBox(receive_key)
        self.next_receive_nonce = 0
        d, self._negotiation_d = self._negotiation_d, None
        d.callback(self)

    def dataReceivedRECORDS(self):
        while True:
            if len(self.buf) < 4:
                return
            length = int(hexlify(self.buf[:4]), 16)
            if len(self.buf) < 4+length:
                return
            encrypted, self.buf = self.buf[4:4+length], self.buf[4+length:]

            record = self._decrypt_record(encrypted)
            self.recordReceived(record)

    def _decrypt_record(self, encrypted):
        nonce_buf = encrypted[:SecretBox.NONCE_SIZE] # assume it's prepended
        nonce = int(hexlify(nonce_buf), 16)
        if nonce != self.next_receive_nonce:
            raise BadNonce("received out-of-order record: got %d, expected %d"
                           % (nonce, self.next_receive_nonce))
        self.next_receive_nonce += 1
        record = self.receive_box.decrypt(encrypted)
        return record

    def describe(self):
        return self._description

    def send_record(self, record):
        if not isinstance(record, type(b"")): raise InternalError
        assert SecretBox.NONCE_SIZE == 24
        assert self.send_nonce < 2**(8*24)
        assert len(record) < 2**(8*4)
        nonce = unhexlify("%048x" % self.send_nonce) # big-endian
        self.send_nonce += 1
        encrypted = self.send_box.encrypt(record, nonce)
        length = unhexlify("%08x" % len(encrypted)) # always 4 bytes long
        self.transport.write(length)
        self.transport.write(encrypted)

    def recordReceived(self, record):
        if self._consumer:
            self._writeToConsumer(record)
            return
        self._inbound_records.append(record)
        self._deliverRecords()

    def receive_record(self):
        d = defer.Deferred()
        self._waiting_reads.append(d)
        self._deliverRecords()
        return d

    def _deliverRecords(self):
        while self._inbound_records and self._waiting_reads:
            r = self._inbound_records.popleft()
            d = self._waiting_reads.popleft()
            d.callback(r)

    def close(self):
        self.transport.loseConnection()
        while self._waiting_reads:
            d = self._waiting_reads.popleft()
            d.errback(error.ConnectionClosed())

    def timeoutConnection(self):
        self._error = BadHandshake("timeout")
        self.transport.loseConnection()

    def connectionLost(self, reason=None):
        self.setTimeout(None)
        d, self._negotiation_d = self._negotiation_d, None
        # the Deferred is only relevant until negotiation finishes, so skip
        # this if it's alredy been fired
        if d:
            # Each call to loseConnection() sets self._error first, so we can
            # deliver useful information to the Factory that's waiting on
            # this (although they'll generally ignore the specific error,
            # except for logging unexpected ones). The possible cases are:
            #
            # cancel: defer.CancelledError
            # far-end disconnect: BadHandshake("connection lost")
            # handshake error (something we didn't like): BadHandshake(what)
            # other error: some other Exception
            # timeout: BadHandshake("timeout")

            d.errback(self._error or BadHandshake("connection lost"))
        if self._consumer_deferred:
            self._consumer_deferred.errback(error.ConnectionClosed())

    # IConsumer methods, for outbound flow-control. We pass these through to
    # the transport. The 'producer' is something like a t.p.basic.FileSender
    def registerProducer(self, producer, streaming):
        assert interfaces.IConsumer.providedBy(self.transport)
        self.transport.registerProducer(producer, streaming)
    def unregisterProducer(self):
        self.transport.unregisterProducer()
    def write(self, data):
        self.send_record(data)

    # IProducer methods, for inbound flow-control. We pass these through to
    # the transport.
    def stopProducing(self):
        self.transport.stopProducing()
    def pauseProducing(self):
        self.transport.pauseProducing()
    def resumeProducing(self):
        self.transport.resumeProducing()

    # Helper methods

    def connectConsumer(self, consumer, expected=None):
        """Helper method to glue an instance of e.g. t.p.ftp.FileConsumer to
        us. Inbound records will be written as bytes to the consumer.

        Set 'expected' to an integer to automatically disconnect when at
        least that number of bytes have been written. This function will then
        return a Deferred (that fires with the number of bytes actually
        received). If the connection is lost while this Deferred is
        outstanding, it will errback. If 'expected' is 0, the Deferred will
        fire right away.

        If 'expected' is None, then this function returns None instead of a
        Deferred, and you must call disconnectConsumer() when you are done."""

        if self._consumer:
            raise RuntimeError("A consumer is already attached: %r" %
                               self._consumer)

        # be aware of an ordering hazard: when we call the consumer's
        # .registerProducer method, they are likely to immediately call
        # self.resumeProducing, which we'll deliver to self.transport, which
        # might call our .dataReceived, which may cause more records to be
        # available. By waiting to set self._consumer until *after* we drain
        # any pending records, we avoid delivering records out of order,
        # which would be bad.
        consumer.registerProducer(self, True)
        # There might be enough data queued to exceed 'expected' before we
        # leave this function. We must be sure to register the producer
        # before it gets unregistered.

        self._consumer = consumer
        self._consumer_bytes_written = 0
        self._consumer_bytes_expected = expected
        d = None
        if expected is not None:
            d = defer.Deferred()
        self._consumer_deferred = d
        if expected == 0:
            # write empty record to kick consumer into shutdown
            self._writeToConsumer(b"")
        # drain any pending records
        while self._consumer and self._inbound_records:
            r = self._inbound_records.popleft()
            self._writeToConsumer(r)
        return d

    def _writeToConsumer(self, record):
        self._consumer.write(record)
        self._consumer_bytes_written += len(record)
        if self._consumer_bytes_expected is not None:
            if self._consumer_bytes_written >= self._consumer_bytes_expected:
                d = self._consumer_deferred
                self.disconnectConsumer()
                d.callback(self._consumer_bytes_written)

    def disconnectConsumer(self):
        self._consumer.unregisterProducer()
        self._consumer = None
        self._consumer_bytes_expected = None
        self._consumer_deferred = None

    # Helper method to write a known number of bytes to a file. This has no
    # flow control: the filehandle cannot push back. 'progress' is an
    # optional callable which will be called on each write (with the number
    # of bytes written). Returns a Deferred that fires (with the number of
    # bytes written) when the count is reached or the RecordPipe is closed.

    def writeToFile(self, f, expected, progress=None, hasher=None):
        fc = FileConsumer(f, progress, hasher)
        return self.connectConsumer(fc, expected)