def encrypt_data(key, plaintext): assert isinstance(key, type(b"")), type(key) assert isinstance(plaintext, type(b"")), type(plaintext) assert len(key) == SecretBox.KEY_SIZE, len(key) box = SecretBox(key) nonce = utils.random(SecretBox.NONCE_SIZE) return box.encrypt(plaintext, nonce)
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
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 _encrypt_data(self, key, data): assert isinstance(key, type(b"")), type(key) assert isinstance(data, type(b"")), type(data) if len(key) != SecretBox.KEY_SIZE: raise UsageError box = SecretBox(key) nonce = utils.random(SecretBox.NONCE_SIZE) return box.encrypt(data, nonce)
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
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
def encrypt_for_node(identity: Identity, public_key: str, payload: Union[str, bytes]) -> str: """ encrypt payload using a nacl.SecretBox with a shared key derived from the public key of the node and private key of the user Args: identity(Identity): the identity object that contains the key pair of the user public_key(str): public key of the node, hex-encoded payload(Union[str, bytes]): any data you want to encrypt Returns: str: hex-encoded encrypted data. you can use this safely into your reservation data """ user_private = identity.nacl.signing_key.to_curve25519_private_key( ).encode() node_verify_bin = binascii.unhexlify(public_key) node_public = VerifyKey(node_verify_bin).to_curve25519_public_key() node_public = node_public.encode() shared_secret = crypto_scalarmult(user_private, node_public) h = blake2b(shared_secret, digest_size=32) key = h.digest() if isinstance(payload, str): payload = payload.encode() box = SecretBox(key) encrypted = box.encrypt(payload) return binascii.hexlify(encrypted)
def step4_api2(): hkdf_salt = random(32) nodeid = do_priv("get_nodeid") to_server_key = get_subkey("to_server", hkdf_salt) ciphertext = SecretBox(to_server_key).encrypt(nodeid.encode()) try: with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: s.settimeout(5) s.connect((bootstrap_ipv6, 1337)) assert(len(hkdf_salt) < 256) s.sendall(len(hkdf_salt).to_bytes(1, byteorder='big') + hkdf_salt) assert(len(ciphertext) < 256) s.sendall(len(ciphertext).to_bytes(1, byteorder='big') + ciphertext) num_bytes = int.from_bytes(s.recv(1), byteorder='big') resp=b'' while len(resp) < num_bytes: resp += s.recv(num_bytes - len(resp)) except (OSError, socket.timeout): abort(404, "Not found") from_server_key = get_subkey("from_server", hkdf_salt) global network_id network_id = SecretBox(from_server_key).decrypt(resp).decode() #XXX Do we need to leave networks with overlapping IP space? Better to warn? do_priv("leaveall private") do_priv("join %s" % network_id) return "ok"
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()
def _encrypt_data(self, key, data): assert isinstance(key, type(b"")), type(key) assert isinstance(data, type(b"")), type(data) assert len(key) == SecretBox.KEY_SIZE, len(key) box = SecretBox(key) nonce = utils.random(SecretBox.NONCE_SIZE) return box.encrypt(data, nonce)
def encrypt_message(secret, topic, message): """Encrypt message.""" keylen = SecretBox.KEY_SIZE if isinstance(secret, dict): key = secret.get(topic) else: key = secret if key is None: _LOGGER.warning( "Unable to encrypt payload because no decryption key known " "for topic %s", topic, ) return None key = key.encode("utf-8") key = key[:keylen] key = key.ljust(keylen, b"\0") try: message = message.encode("utf-8") payload = SecretBox(key).encrypt(message, encoder=Base64Encoder) _LOGGER.debug("Encrypted message: %s to %s", message, payload) return payload.decode("utf-8") except ValueError: _LOGGER.warning("Unable to encrypt message for topic %s", topic) return None
def encrypt(message, keyPath): """ Encrypts a message given a path to a local file containing a key. :param message: The message to be encrypted. :param keyPath: A path to a file containing a 256-bit key (and nothing else). :type message: str :type keyPath: str :rtype: str A constant overhead is added to every encrypted message (for the nonce and MAC). >>> import tempfile >>> k = tempfile.mktemp() >>> with open(k, 'w') as f: ... f.write(nacl.utils.random(SecretBox.KEY_SIZE)) >>> message = 'test' >>> len(encrypt(message, k)) == overhead + len(message) True """ 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) # We generate the nonce using secure random bits. For long enough # nonce size, the chance of a random nonce collision becomes # *much* smaller than the chance of a subtle coding error causing # a nonce reuse. Currently the nonce size is 192 bits--the chance # of a collision is astronomically low. (This approach is # recommended in the libsodium documentation.) nonce = nacl.utils.random(SecretBox.NONCE_SIZE) assert len(nonce) == SecretBox.NONCE_SIZE return str(sb.encrypt(message, nonce))
def add_third_party_caveat(self, macaroon, location, key, key_id, nonce=None, **kwargs): derived_key = truncate_or_pad( generate_derived_key(convert_to_bytes(key)) ) old_key = truncate_or_pad(binascii.unhexlify(macaroon.signature_bytes)) box = SecretBox(key=old_key) nonce = nonce or nacl.utils.random(box.NONCE_SIZE) verification_key_id = box.encrypt( derived_key, nonce=nonce ) caveat = Caveat( caveat_id=key_id, location=location, verification_key_id=verification_key_id ) macaroon.caveats.append(caveat) encode_key = binascii.unhexlify(macaroon.signature_bytes) macaroon.signature = sign_third_party_caveat( encode_key, caveat._verification_key_id, caveat._caveat_id ) return macaroon
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
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
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
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'=')
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)
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
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 __exit__(self, exc_type, exc_val, exc_tb): """ Re-encrypt. """ # Derive the key from the passphrase. derived = util.derive_passphrase(self.passphrase) # Generate two random nonces. nonce1 = random(SecretBox.NONCE_SIZE) nonce2 = random(SecretBox.NONCE_SIZE) sign_box = SecretBox(derived) enc_box = SecretBox(derived) s_p = self.sign.encode() e_p = self.encrypt.encode() s_e = sign_box.encrypt(s_p, nonce1) e_e = enc_box.encrypt(e_p, nonce2) # Update `self.key`. self.key._private_key_raw = e_e.ciphertext self.key._private_signing_key_raw = s_e.ciphertext # Bit of a mixed up name. self.key._private_nonce = e_e.nonce self.key._private_signing_nonce = s_e.nonce if exc_type is not None: raise exc_type(exc_val)
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
def store(secret): while True: try: # Again, mkdir is an atomic operation on POSIX file systems. # Two concurrent requests cannot store data into the same # directory. sid = generate_sid() mkdir(join(DATA, sid)) break except FileExistsError: continue key_bytes = random(SecretBox.KEY_SIZE) box = SecretBox(key_bytes) with open(join(DATA, sid, 'secret'), 'wb') as fp: fp.write(box.encrypt(secret.encode('UTF-8'))) # Turn key into base64 and remove padding, because it has the # potential of confusing users. ("Is this part of the URL?") key = str(b64encode(key_bytes), 'ASCII') key = key.replace('/', '_') key = key.rstrip('=') return sid, key
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
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)
def encryptCredential(self, key, data): """Credential will be only encrypted here. Decryption is done via Javascript later at client browser.""" assert type(key) == bytes and len(key) == 32 assert type(data) == bytes box = SecretBox(key) return base64.b64encode(box.encrypt(data)).decode("ascii")
def encrypt(message, key, output): message_bytes = message.encode('ascii') with open(key, mode="rb") as key_file: key = key_file.read() with open(output, "wb") as output_file: secret_box = SecretBox(key) output_file.write(secret_box.encrypt(message_bytes))
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
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)
def add_third_party_caveat(self, macaroon, location, key, key_id, **kwargs): derived_key = truncate_or_pad( generate_derived_key(convert_to_bytes(key)) ) old_key = truncate_or_pad(binascii.unhexlify(macaroon.signature_bytes)) box = SecretBox(key=old_key) verification_key_id = box.encrypt( derived_key, nonce=kwargs.get('nonce') ) caveat = Caveat( caveat_id=key_id, location=location, verification_key_id=verification_key_id, version=macaroon.version ) macaroon.caveats.append(caveat) encode_key = binascii.unhexlify(macaroon.signature_bytes) macaroon.signature = sign_third_party_caveat( encode_key, caveat._verification_key_id, caveat._caveat_id ) return macaroon
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
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
def encrypt(self, data: bytes) -> bytes: """ Raises: CryptoError: if key is invalid. """ box = SecretBox(self) return box.encrypt(data)
def encrypt(message, keyPath): """ Encrypts a message given a path to a local file containing a key. :param message: The message to be encrypted. :param keyPath: A path to a file containing a 256-bit key (and nothing else). :type message: str :type keyPath: str :rtype: str A constant overhead is added to every encrypted message (for the nonce and MAC). >>> import tempfile >>> k = tempfile.mktemp() >>> with open(k, 'w') as f: ... f.write(nacl.utils.random(SecretBox.KEY_SIZE)) >>> message = 'test' >>> len(encrypt(message, k)) == encryptionOverhead + len(message) True """ 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) # We generate the nonce using secure random bits. For long enough # nonce size, the chance of a random nonce collision becomes # *much* smaller than the chance of a subtle coding error causing # a nonce reuse. Currently the nonce size is 192 bits--the chance # of a collision is astronomically low. (This approach is # recommended in the libsodium documentation.) nonce = nacl.utils.random(SecretBox.NONCE_SIZE) assert len(nonce) == SecretBox.NONCE_SIZE return str(sb.encrypt(message, nonce))
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
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()
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 decrypt(self, ciphered: bytes) -> bytes: """ Raises: CryptoError: if key is invalid. """ box = SecretBox(self) return box.decrypt(ciphered)
def encrypt(file, privkey, recipients): # ignores privkey and recipients for now key = random(SecretBox.KEY_SIZE) box = SecretBox(key) enc = box.encrypt(file.encode("utf-8")) jsondata = json.dumps({"version":"0.1","key":base64.b64encode(key).decode("utf-8")}) return jsondata.encode("utf-8")+_separator+enc
def test_secret_box_encryption_generates_different_nonces(key, nonce, plaintext, ciphertext): box = SecretBox(key, encoder=HexEncoder) nonce_0 = box.encrypt(binascii.unhexlify(plaintext), encoder=HexEncoder).nonce nonce_1 = box.encrypt(binascii.unhexlify(plaintext), encoder=HexEncoder).nonce assert nonce_0 != nonce_1
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
def handshake_initiate(private_key, redis_client): try: request = expect_json_request(bottle.request, INITIATE_SCHEMA) symmetric_key = redis_get_cookie( redis_client, request[INITIATE_COOKIE_FIELD]) cookie_sbox = SecretBox(symmetric_key) cookie = cookie_sbox.decrypt( str(request[INITIATE_COOKIE_FIELD]), encoder=Base64Encoder) if len(cookie) != 2 * CURVE25519_KEY_BYTES: bottle.response.status = HTTP_INTERNAL_SERVER_ERROR return {'error': 'An invalid cookie was sent to the client.'} client_transient_pkey = PublicKey(cookie[0:CURVE25519_KEY_BYTES]) transient_skey = PrivateKey(cookie[CURVE25519_KEY_BYTES:]) if request[INITIATE_CLIENT_TRANSIENT_PKEY_FIELD] != \ client_transient_pkey.encode(Base64Encoder): raise InvalidClientRequest( 'Initiate: non matching transient public keys.') vouch_json = open_box(request[INITIATE_VOUCH_FIELD], transient_skey, client_transient_pkey) vouch = parse_and_verify_json(vouch_json, VOUCH_SCHEMA) client_pkey = PublicKey( str(vouch[VOUCH_CLIENT_PKEY_FIELD]), encoder=Base64Encoder) vouch_for_transient_pkey = open_box( vouch[VOUCH_TRANSIENT_KEY_BOX_FIELD], private_key, client_pkey) if vouch_for_transient_pkey != client_transient_pkey.encode(): raise InvalidClientRequest( 'Initiate: non matching transient public keys.') resp = 'I believe you are {} and you want {}'.format( client_pkey.encode(Base64Encoder), vouch[VOUCH_MESSAGE_FIELD]) print(resp) response_nonce = nacl.utils.random(Box.NONCE_SIZE) response_box = Box(transient_skey, client_transient_pkey) response_box_cipher = response_box.encrypt( resp, response_nonce, encoder=Base64Encoder) return {'response': response_box_cipher} except jsonschema.ValidationError as e: log.exception(e) bottle.response.status = HTTP_BAD_REQUEST return {'error': str(e)} except InvalidClientRequest as e: log.exception(e) bottle.response.status = HTTP_BAD_REQUEST return {'error': str(e)} except MissingCookie as e: log.exception(e) bottle.response.status = HTTP_BAD_REQUEST return {'error': str(e)} except CryptoError as e: log.exception(e) bottle.response.status = HTTP_BAD_REQUEST return {'error': 'Bad encryption in handshake.'} return {'error': ''}
def 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"")
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
def test_secret_box_encryption(key, nonce, plaintext, ciphertext): box = SecretBox(key, encoder=HexEncoder) encrypted = box.encrypt(binascii.unhexlify(plaintext), binascii.unhexlify(nonce), encoder=HexEncoder) expected = binascii.hexlify(binascii.unhexlify(nonce) + binascii.unhexlify(ciphertext)) assert encrypted == expected assert encrypted.nonce == nonce assert encrypted.ciphertext == ciphertext
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
def create_list_entry(symkey, tmppub, length, nonce=None, fetch_token=None, delete_token=None): assert len(tmppub) == 32 fetch_token = fetch_token or os.urandom(32) delete_token = delete_token or os.urandom(32) msg = "list:" + struct.pack(">32s32s32sQ", tmppub, fetch_token, delete_token, length) nonce = nonce or os.urandom(24) sbox = SecretBox(symkey) return sbox.encrypt(msg, nonce), fetch_token, delete_token
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'))
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 _encrypt_data(self, key, data): # Without predefined roles, we can't derive predictably unique keys # for each side, so we use the same key for both. We use random # nonces to keep the messages distinct, and we automatically ignore # reflections. # TODO: HKDF(side, nonce, key) ?? include 'side' to prevent # reflections, since we no longer compare messages assert isinstance(key, type(b"")), type(key) assert isinstance(data, type(b"")), type(data) assert len(key) == SecretBox.KEY_SIZE, len(key) box = SecretBox(key) nonce = utils.random(SecretBox.NONCE_SIZE) return box.encrypt(data, nonce)
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])
def handshake_hello(private_key, redis_client): try: request = expect_json_request(bottle.request, HELLO_SCHEMA) client_transient_pkey = PublicKey( str(request[HELLO_CLIENT_TRANSIENT_PKEY_FIELD]), Base64Encoder) zeros = open_box(request[HELLO_ZEROS_BOX_FIELD], private_key, client_transient_pkey) if len(zeros) != HELLO_PADDING_BYTES: raise InvalidClientRequest( 'zeros_box should contain exactly %d bytes of padding' % HELLO_PADDING_BYTES) transient_skey = PrivateKey.generate() cookie_plain = client_transient_pkey.encode() + \ transient_skey.encode() cookie_nonce = nacl.utils.random(SecretBox.NONCE_SIZE) symmetric_key = nacl.utils.random(SecretBox.KEY_SIZE) cookie_sbox = SecretBox(symmetric_key) cookie = cookie_sbox.encrypt( cookie_plain, cookie_nonce, encoder=Base64Encoder) redis_set_cookie(redis_client, cookie, symmetric_key) cookie_box = Box(private_key, client_transient_pkey) cookie_box_nonce = nacl.utils.random(Box.NONCE_SIZE) server_tpkey = transient_skey.public_key.encode(Base64Encoder) cookie_box_cipher = cookie_box.encrypt(json.dumps({ COOKIE_SERVER_TRANSIENT_PKEY_FIELD: server_tpkey, COOKIE_COOKIE_FIELD: cookie }), cookie_box_nonce, encoder=Base64Encoder) response = {COOKIE_COOKIE_BOX_FIELD: cookie_box_cipher} jsonschema.validate(response, COOKIE_SCHEMA) return response except jsonschema.ValidationError: log.exception(e) bottle.response.status = HTTP_INTERNAL_SERVER_ERROR return {'error': 'A packet with an invalid JSON schema was generated.'} except InvalidClientRequest as e: log.exception(e) bottle.response.status = HTTP_BAD_REQUEST return {'error': str(e)} except CryptoError as e: log.exception(e) bottle.response.status = HTTP_BAD_REQUEST return {'error': 'bad encryption'} return {'error': ''}
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'))
async def test_webhook_handle_decryption(webhook_client, # noqa: F811 create_registrations): # noqa: F401, F811, E501 """Test that we can encrypt/decrypt properly.""" try: # pylint: disable=unused-import from nacl.secret import SecretBox # noqa: F401 from nacl.encoding import Base64Encoder # noqa: F401 except (ImportError, OSError): pytest.skip("libnacl/libsodium is not installed") return import json keylen = SecretBox.KEY_SIZE key = create_registrations[0]['secret'].encode("utf-8") key = key[:keylen] key = key.ljust(keylen, b'\0') payload = json.dumps(RENDER_TEMPLATE['data']).encode("utf-8") data = SecretBox(key).encrypt(payload, encoder=Base64Encoder).decode("utf-8") container = { 'type': 'render_template', 'encrypted': True, 'encrypted_data': data, } resp = await webhook_client.post( '/api/webhook/{}'.format(create_registrations[0]['webhook_id']), json=container ) assert resp.status == 200 webhook_json = await resp.json() assert 'encrypted_data' in webhook_json decrypted_data = SecretBox(key).decrypt(webhook_json['encrypted_data'], encoder=Base64Encoder) decrypted_data = decrypted_data.decode("utf-8") assert json.loads(decrypted_data) == {'one': 'Hello world'}