def prepare_pack_recipient_keys(to_verkeys: Sequence[bytes], from_secret: bytes = None) -> (str, bytes): """ Assemble the recipients block of a packed message. Args: to_verkeys: Verkeys of recipients from_secret: Secret to use for signing keys Returns: A tuple of (json result, key) """ cek = pysodium.crypto_secretstream_xchacha20poly1305_keygen() recips = [] for target_vk in to_verkeys: target_pk = pysodium.crypto_sign_pk_to_box_pk(target_vk) if from_secret: sender_pk, sender_sk = create_keypair(from_secret) sender_vk = bytes_to_b58(sender_pk).encode("ascii") enc_sender = pysodium.crypto_box_seal(sender_vk, target_pk) sk = pysodium.crypto_sign_sk_to_box_sk(sender_sk) nonce = pysodium.randombytes(pysodium.crypto_box_NONCEBYTES) enc_cek = pysodium.crypto_box(cek, nonce, target_pk, sk) else: enc_sender = None nonce = None enc_cek = pysodium.crypto_box_seal(cek, target_pk) recips.append( OrderedDict([ ("encrypted_key", bytes_to_b64(enc_cek, urlsafe=True)), ( "header", OrderedDict([ ("kid", bytes_to_b58(target_vk)), ( "sender", bytes_to_b64(enc_sender, urlsafe=True) if enc_sender else None, ), ( "iv", bytes_to_b64(nonce, urlsafe=True) if nonce else None, ), ]), ), ])) data = OrderedDict([ ("enc", "xchacha20poly1305_ietf"), ("typ", "JWM/1.0"), ("alg", "Authcrypt" if from_secret else "Anoncrypt"), ("recipients", recips), ]) return json.dumps(data), cek
def auth_crypt_message(message: bytes, to_verkey: bytes, from_verkey: bytes, from_sigkey: bytes) -> bytes: """ Apply auth_crypt to a binary message. Args: message: The message to encrypt to_verkey: To recipient's verkey from_verkey: Sender verkey, included in combo box for verification from_sigkey: Sender sigkey, included to authenticated encrypt the message Returns: The encrypted message """ nonce = pysodium.randombytes(pysodium.crypto_box_NONCEBYTES) target_pk = pysodium.crypto_sign_pk_to_box_pk(to_verkey) sk = pysodium.crypto_sign_sk_to_box_sk(from_sigkey) enc_body = pysodium.crypto_box(message, nonce, target_pk, sk) combo_box = OrderedDict([ ("msg", bytes_to_b64(enc_body)), ("sender", bytes_to_b58(from_verkey)), ("nonce", bytes_to_b64(nonce)), ]) combo_box_bin = msgpack.packb(combo_box, use_bin_type=True) enc_message = pysodium.crypto_box_seal(combo_box_bin, target_pk) return enc_message
def auth_crypt_message(message: bytes, to_verkey: bytes, from_secret: bytes) -> bytes: """ Apply auth_crypt to a binary message. Args: message: The message to encrypt to_verkey: To recipient's verkey from_secret: The seed to use Returns: The encrypted message """ nonce = pysodium.randombytes(pysodium.crypto_box_NONCEBYTES) target_pk = pysodium.crypto_sign_pk_to_box_pk(to_verkey) sender_pk, sender_sk = create_keypair(from_secret) sk = pysodium.crypto_sign_sk_to_box_sk(sender_sk) enc_body = pysodium.crypto_box(message, nonce, target_pk, sk) combo_box = OrderedDict( [ ("msg", bytes_to_b64(enc_body)), ("sender", bytes_to_b58(sender_pk)), ("nonce", bytes_to_b64(nonce)), ] ) combo_box_bin = msgpack.packb(combo_box, use_bin_type=True) enc_message = pysodium.crypto_box_seal(combo_box_bin, target_pk) return enc_message
def respond(chal, id, secret=None): path = os.path.expanduser(datadir + binascii.hexlify(id).decode()) if not secret: try: secret = readf(path + '/key') except ValueError: return b'fail' # key not found if len(secret) != sphinxlib.DECAF_255_SCALAR_BYTES: if verbose: print("secret wrong size") return b'fail' try: rule = readf(path + '/rule') except ValueError: return b'fail' # key not found with open(path + '/xpub', 'rb') as fd: xpk = fd.read() rule = pysodium.crypto_box_seal(rule, xpk) try: return sphinxlib.respond(chal, secret) + rule except ValueError: if verbose: print("respond fail") return b'fail'
def add(self, msg_text, to=None): # TODO where should this function be inserted? # TODO how do we do commit/push? if to is None: # TODO do we really encode public messages? encoded_msg = b64encode(msg_text) message = { "msg": encoded_msg, "time": current_time() } else: team = Team(name=to) team_pk = team['crypt_pk'] # TODO check if msg really needs to be encoded before encrypted # (and encoded after encrypted too) msg_text = b64encode(msg_text) encrypted_msg = pysodium.crypto_box_seal(msg, team_pk) encoded_msg = b64encode(encrypted_msg) message = { "msg": encoded_msg, "to": team['name'], "time": current_time() } self.append(message) self.save() dest = message["to"] if "to" in message else "all" SubRepo.push(commit_message='Added news to %s' % dest)
def __init_ratchet(self, file): self._logger.warning("Initializing new Ratchet file %s", file) with open(file, "wb") as f: rb = pysodium.randombytes(Ratchet.SECRETSIZE) f.write(msgpack.packb([0,rb], use_bin_type=True)) _ss0_escrow = unhexlify(b'7e301be3922d8166e30be93c9ecc2e18f71400fe9e6407fd744f4a542bcab934') self._logger.warning(" Escrow public key: %s", _ss0_escrow.hex()) self._logger.warning(" Escrow value: %s", pysodium.crypto_box_seal(rb,_ss0_escrow).hex()) self._logger.warning(" Ratchet Initialized.")
def encrypt_report(self, report): """ Encrypts a report using libnacl public encryption Args: report: The report as json string Returns: The encrypted blob of the report in base64 encoding ready for submission """ return base64.b64encode( pysodium.crypto_box_seal(report, self.receiver_pub_key))
def doSphinx(self, message, b, pwd, cb): signed = pysodium.crypto_sign(message, self.getkey()) sepk = self.getserverkey() sxpk = pysodium.crypto_sign_pk_to_box_pk(sepk) sealed = pysodium.crypto_box_seal(signed, sxpk) loop = asyncio.get_event_loop() coro = loop.create_connection( lambda: SphinxClientProtocol(sealed, loop, b, pwd, self, cb), address, port) try: loop.run_until_complete(coro) loop.run_forever() except: raise
def test0(): if not pysodium.sodium_version_check(1, 0, 9): return pk, sk = pysodium.crypto_box_keypair() #print(pk) #print(sk) p = binascii.hexlify(pk) s = binascii.hexlify(sk) print(p) print(s) c = pysodium.crypto_box_seal(b"passwd", pk) print(binascii.hexlify(c)) print(pysodium.crypto_box_seal_open(c, pk, sk))
def anon_crypt_message(message: bytes, to_verkey: bytes) -> bytes: """ Apply anon_crypt to a binary message. Args: message: The message to encrypt to_verkey: The verkey to encrypt the message for Returns: The anon encrypted message """ pk = pysodium.crypto_sign_pk_to_box_pk(to_verkey) enc_message = pysodium.crypto_box_seal(message, pk) return enc_message
def add(self, msg_text, to=None): current_time = int(time.time()) if to is None: message = {"msg": msg_text, "time": current_time} else: team_pk = Team(name=to)['crypt_pk'] encrypted_msg = pysodium.crypto_box_seal(msg_text.encode("utf-8"), team_pk) encoded_msg = b64encode(encrypted_msg) message = {"msg": encoded_msg.decode("utf-8"), "to": to, "time": current_time} self.append(message) self.save() saveToFirebase(NEWS_RTDB, self._serialize())
def add(self, msg_text, to=None): current_time = int(time.time()) if to is None: message = {"msg": msg_text, "time": current_time} else: team_pk = Team(name=to)['crypt_pk'] encrypted_msg = pysodium.crypto_box_seal(msg_text.encode("utf-8"), team_pk) encoded_msg = b64encode(encrypted_msg) message = { "msg": encoded_msg.decode("utf-8"), "to": to, "time": current_time } self.append(message) self.save() dest = message["to"] if "to" in message else "all" SubRepo.push(commit_message='Added news to %s' % dest, merge_request=False)
def test_crypto_box_seal(self): pk, sk = pysodium.crypto_box_keypair() c = pysodium.crypto_box_seal(b"howdy", pk) self.assertEqual(pysodium.crypto_box_seal_open(c, pk, sk), b'howdy')
f.seek(0, 0) f.write(msgpack.packb([idx, rb], use_bin_type=True)) f.flush() f.truncate() else: with open(nodeID + ".seed", "wb") as f: idx = 0 rb = pysodium.randombytes(Ratchet.SECRETSIZE) f.write(msgpack.packb([idx, rb], use_bin_type=True)) if len(_ss0_escrow) > 0: print( 'Escrow key used for initial shared secret. If you lose connectivity for an extended period of time, you will need the following (and the private key for the escrow public key) to access data' ) print('Escrow public key:', hexlify(_ss0_escrow)) print(nodeID + ' escrow value: ', hexlify(pysodium.crypto_box_seal(rb, _ss0_escrow)), " (key index", idx, ")") else: print( 'No escrow key for initial shared secret. If you lose connectivity for an extended period of time, you may lose access to data unless you store the following value in a secure location:' ) print(nodeID + ':', hexlify(rb), " (key index", idx, ")") # Second, generate identify keypair and chain, and write cryptokey config file if path.exists(nodeID + ".crypto"): with open(nodeID + ".crypto", "rb+") as f: sk, rb = msgpack.unpackb(f.read(), raw=True) pk = pysodium.crypto_sign_sk_to_pk(sk) f.seek(0, 0) f.write(msgpack.packb([sk, rb], use_bin_type=True)) f.flush()
def prepare_pack_recipient_keys(to_verkeys: Sequence[bytes], from_verkey: bytes = None, from_sigkey: bytes = None) -> (str, bytes): """ Assemble the recipients block of a packed message. Args: to_verkeys: Verkeys of recipients from_verkey: Sender Verkey needed to authcrypt package from_sigkey: Sender Sigkey needed to authcrypt package Returns: A tuple of (json result, key) """ if from_verkey is not None and from_sigkey is None or \ from_sigkey is not None and from_verkey is None: raise CryptoError( 'Both verkey and sigkey needed to authenticated encrypt message') cek = pysodium.crypto_secretstream_xchacha20poly1305_keygen() recips = [] for target_vk in to_verkeys: target_pk = pysodium.crypto_sign_pk_to_box_pk(target_vk) if from_verkey: sender_vk = bytes_to_b58(from_verkey).encode("ascii") enc_sender = pysodium.crypto_box_seal(sender_vk, target_pk) sk = pysodium.crypto_sign_sk_to_box_sk(from_sigkey) nonce = pysodium.randombytes(pysodium.crypto_box_NONCEBYTES) enc_cek = pysodium.crypto_box(cek, nonce, target_pk, sk) else: enc_sender = None nonce = None enc_cek = pysodium.crypto_box_seal(cek, target_pk) recips.append( OrderedDict([ ("encrypted_key", bytes_to_b64(enc_cek, urlsafe=True)), ( "header", OrderedDict([ ("kid", bytes_to_b58(target_vk)), ( "sender", bytes_to_b64(enc_sender, urlsafe=True) if enc_sender else None, ), ( "iv", bytes_to_b64(nonce, urlsafe=True) if nonce else None, ), ]), ), ])) data = OrderedDict([ ("enc", "xchacha20poly1305_ietf"), ("typ", "JWM/1.0"), ("alg", "Authcrypt" if from_verkey else "Anoncrypt"), ("recipients", recips), ]) return json.dumps(data), cek
def test_crypto_box_seal(self): if not pysodium.sodium_version_check(1, 0, 3): return pk, sk = pysodium.crypto_box_keypair() c = pysodium.crypto_box_seal(b"howdy", pk) self.assertEqual(pysodium.crypto_box_seal_open(c, pk, sk), b'howdy')
def test_pysodium(): """ Test all the functions needed from pysodium libarary (libsodium) """ # crypto_sign signatures with Ed25519 keys # create keypair without seed verkey, sigkey = pysodium.crypto_sign_keypair() assert len(verkey) == 32 == pysodium.crypto_sign_PUBLICKEYBYTES assert len(sigkey) == 64 == pysodium.crypto_sign_SECRETKEYBYTES assert 32 == pysodium.crypto_sign_SEEDBYTES sigseed = pysodium.randombytes(pysodium.crypto_sign_SEEDBYTES) assert len(sigseed) == 32 # seed = (b'J\xeb\x06\xf2BA\xd6/T\xe1\xe2\xe2\x838\x8a\x99L\xd9\xb5(\\I\xccRb\xc8\xd5\xc7Y\x1b\xb6\xf0') # Ann's seed sigseed = ( b'PTi\x15\xd5\xd3`\xf1u\x15}^r\x9bfH\x02l\xc6\x1b\x1d\x1c\x0b9\xd7{\xc0_\xf2K\x93`' ) assert len(sigseed) == 32 # try key stretching from 16 bytes using pysodium.crypto_pwhash() assert 16 == pysodium.crypto_pwhash_SALTBYTES salt = pysodium.randombytes(pysodium.crypto_pwhash_SALTBYTES) assert len(salt) == 16 # salt = b'\x19?\xfa\xc7\x8f\x8b\x7f\x8b\xdbS"$\xd7[\x85\x87' # algorithm default is argon2id sigseed = pysodium.crypto_pwhash( outlen=32, passwd="", salt=salt, opslimit=pysodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, memlimit=pysodium.crypto_pwhash_MEMLIMIT_INTERACTIVE, alg=pysodium.crypto_pwhash_ALG_DEFAULT) assert len(sigseed) == 32 # seed = (b'\xa9p\x89\x7f+\x0e\xc4\x9c\xf2\x01r\xafTI\xc0\xfa\xac\xd5\x99\xf8O\x8f=\x843\xa2\xb6e\x9fO\xff\xd0') # creates signing/verification key pair from seed verkey, sigkey = pysodium.crypto_sign_seed_keypair(sigseed) assert len(verkey) == 32 assert len(sigkey) == 64 # sigkey is seed and verkey concatenated. Libsodium does this as an optimization # because the signing scheme needs both the private key (seed) and the public key so # instead of recomputing the public key each time from the secret key it requires # the public key as an input of and instead of two separate inputs, one for the # secret key and one for the public key, it uses a concatenated form. # Essentially crypto_sign_seed_keypair and crypto_sign_keypair return redundant # information in the duple (verkey, sigkey) because sigkey includes verkey # so one could just store sigkey and extract verkey or sigseed when needed # or one could just store verkey and sigseed and reconstruct sigkey when needed. # crypto_sign_detached requires sigkey (sigseed + verkey) # crypto_sign_verify_detached reqires verkey only # https://crypto.stackexchange.com/questions/54353/why-are-nacl-secret-keys-64-bytes-for-signing-but-32-bytes-for-box assert sigseed == sigkey[:32] assert verkey == sigkey[32:] assert sigkey == sigseed + verkey # vk = (b'B\xdd\xbb}8V\xa0\xd6lk\xcf\x15\xad9\x1e\xa7\xa1\xfe\xe0p<\xb6\xbex\xb0s\x8d\xd6\xf5\xa5\xe8Q') # utility function to extract seed from secret sigkey (really just extracting from front half) assert sigseed == pysodium.crypto_sign_sk_to_seed(sigkey) assert 64 == pysodium.crypto_sign_BYTES msg = "The lazy dog jumped over the river" msgb = msg.encode( "utf-8") # must convert unicode string to bytes in order to sign it assert msgb == b'The lazy dog jumped over the river' sig = pysodium.crypto_sign_detached(msgb, sigseed + verkey) # sigkey = seed + verkey assert len(sig) == 64 """ sig = (b"\x99\xd2<9$$0\x9fk\xfb\x18\xa0\x8c@r\x122.k\xb2\xc7\x1fp\x0e'm\x8f@" b'\xaa\xa5\x8c\xc8n\x85\xc8!\xf6q\x91p\xa9\xec\xcf\x92\xaf)\xde\xca' b'\xfc\x7f~\xd7o|\x17\x82\x1d\xd4<o"\x81&\t') """ #siga = pysodium.crypto_sign(msg.encode("utf-8"), sk)[:pysodium.crypto_sign_BYTES] #assert len(siga) == 64 #assert sig == siga try: # verify returns None if valid else raises ValueError result = pysodium.crypto_sign_verify_detached(sig, msgb, verkey) except Exception as ex: assert False assert not result assert result is None sigbad = sig[:-1] sigbad += b'A' try: # verify returns None if valid else raises ValueError result = pysodium.crypto_sign_verify_detached(sigbad, msgb, verkey) except Exception as ex: assert True assert isinstance(ex, ValueError) # crypto_box authentication encryption with X25519 keys apubkey, aprikey = pysodium.crypto_box_keypair() assert len(apubkey) == 32 == pysodium.crypto_box_SECRETKEYBYTES assert len(aprikey) == 32 == pysodium.crypto_box_PUBLICKEYBYTES repubkey = pysodium.crypto_scalarmult_curve25519_base(aprikey) assert repubkey == apubkey assert 32 == pysodium.crypto_box_SEEDBYTES boxseed = pysodium.randombytes(pysodium.crypto_box_SEEDBYTES) assert len(boxseed) == 32 bpubkey, bprikey = pysodium.crypto_box_seed_keypair(boxseed) assert len(bpubkey) == 32 assert len(bprikey) == 32 repubkey = pysodium.crypto_scalarmult_curve25519_base(bprikey) assert repubkey == bpubkey assert 24 == pysodium.crypto_box_NONCEBYTES nonce = pysodium.randombytes(pysodium.crypto_box_NONCEBYTES) assert len(nonce) == 24 # nonce = b'\x11\xfbi<\xf2\xb6k\xa05\x0c\xf9\x86t\x07\x8e\xab\x8a\x97nG\xe8\x87,\x94' atob_tx = "Hi Bob I'm Alice" atob_txb = atob_tx.encode("utf-8") # Detached recomputes shared key every time. # A encrypt to B acrypt, amac = pysodium.crypto_box_detached(atob_txb, nonce, bpubkey, aprikey) amacl = pysodium.crypto_box_MACBYTES assert amacl == 16 # amac = b'\xa1]\xc6ML\xe2\xa9:\xc0\xdc\xab\xa5\xc4\xc7\xf4\xdb' # acrypt = (b'D\n\x17\xb6z\xd8+t)\xcc`y\x1d\x10\x0cTC\x02\xb5@\xe2\xf2\xc9-(\xec*O\xb8~\xe2\x1a\xebO') # when transmitting prepend amac to crypt acipher = pysodium.crypto_box(atob_txb, nonce, bpubkey, aprikey) assert acipher == amac + acrypt atob_rxb = pysodium.crypto_box_open_detached(acrypt, amac, nonce, apubkey, bprikey) atob_rx = atob_rxb.decode("utf-8") assert atob_rx == atob_tx assert atob_rxb == atob_txb atob_rxb = pysodium.crypto_box_open(acipher, nonce, apubkey, bprikey) atob_rx = atob_rxb.decode("utf-8") assert atob_rx == atob_tx assert atob_rxb == atob_txb btoa_tx = "Hello Alice I am Bob" btoa_txb = btoa_tx.encode("utf-8") # B encrypt to A bcrypt, bmac = pysodium.crypto_box_detached(btoa_txb, nonce, apubkey, bprikey) # bmac = b'\x90\xe07=\xd22\x8fh2\xff\xdd\x84tC\x053' # bcrypt = (b'8\xb5\xba\xe7\xcc\xae B\xefx\xe6{U\xf7\xefA\x00\xc7|\xdbu\xcfc\x01$\xa9\xa2P\xa7\x84\xa5\xae\x180') # when transmitting prepend amac to crypt bcipher = pysodium.crypto_box(btoa_txb, nonce, apubkey, bprikey) assert bcipher == bmac + bcrypt btoa_rxb = pysodium.crypto_box_open_detached(bcrypt, bmac, nonce, bpubkey, aprikey) btoa_rx = btoa_rxb.decode("utf-8") assert btoa_rx == btoa_tx assert btoa_rxb == btoa_txb btoa_rxb = pysodium.crypto_box_open(bcipher, nonce, bpubkey, aprikey) btoa_rx = btoa_rxb.decode("utf-8") assert btoa_rx == btoa_tx assert btoa_rxb == btoa_txb # compute shared key asymkey = pysodium.crypto_box_beforenm(bpubkey, aprikey) bsymkey = pysodium.crypto_box_beforenm(apubkey, bprikey) assert asymkey == bsymkey acipher = pysodium.crypto_box_afternm(atob_txb, nonce, asymkey) atob_rxb = pysodium.crypto_box_open_afternm(acipher, nonce, bsymkey) assert atob_rxb == atob_txb bcipher = pysodium.crypto_box_afternm(btoa_txb, nonce, bsymkey) btoa_rxb = pysodium.crypto_box_open_afternm(bcipher, nonce, asymkey) assert btoa_rxb == btoa_txb # crypto_box_seal public key encryption with X25519 keys # uses same X25519 type of keys as crypto_box authenticated encryption # so when converting sign key Ed25519 to X25519 can use for both types of encryption pubkey, prikey = pysodium.crypto_box_keypair() assert len(pubkey) == 32 == pysodium.crypto_box_PUBLICKEYBYTES assert len(prikey) == 32 == pysodium.crypto_box_SECRETKEYBYTES assert 48 == pysodium.crypto_box_SEALBYTES msg_txb = "Catch me if you can.".encode("utf-8") assert msg_txb == b'Catch me if you can.' cipher = pysodium.crypto_box_seal(msg_txb, pubkey) assert len(cipher) == 48 + len(msg_txb) msg_rxb = pysodium.crypto_box_seal_open(cipher, pubkey, prikey) assert msg_rxb == msg_txb # convert Ed25519 key pair to X25519 key pair # https://blog.filippo.io/using-ed25519-keys-for-encryption/ # https://libsodium.gitbook.io/doc/advanced/ed25519-curve25519 # crypto_sign_ed25519_pk_to_curve25519 # crypto_sign_ed25519_sk_to_curve25519 pubkey = pysodium.crypto_sign_pk_to_box_pk(verkey) assert len(pubkey) == pysodium.crypto_box_PUBLICKEYBYTES prikey = pysodium.crypto_sign_sk_to_box_sk(sigkey) assert len(prikey) == pysodium.crypto_box_SECRETKEYBYTES repubkey = pysodium.crypto_scalarmult_curve25519_base(prikey) assert repubkey == pubkey msg_txb = "Encoded using X25519 key converted from Ed25519 key".encode( "utf-8") cipher = pysodium.crypto_box_seal(msg_txb, pubkey) assert len(cipher) == 48 + len(msg_txb) msg_rxb = pysodium.crypto_box_seal_open(cipher, pubkey, prikey) assert msg_rxb == msg_txb """