def _3user(): eA = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) pA = nacl.crypto_scalarmult_curve25519_base(eA) print("A public: \t%s\nA exp: \t%s" % (b85encode(pA), b85encode(eA))) eB = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) pB = nacl.crypto_scalarmult_curve25519_base(eB) print("B public: \t%s\nB exp: \t%s" % (b85encode(pB), b85encode(eB))) eC = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) pC = nacl.crypto_scalarmult_curve25519_base(eC) print("C public: \t%s\nC exp: \t%s" % (b85encode(pC), b85encode(eC))) print() pAB = nacl.crypto_scalarmult_curve25519(eB, pA) print("public AB", b85encode(pAB)) pBA = nacl.crypto_scalarmult_curve25519(eA, pB) print("public BA", b85encode(pBA)) pCA = nacl.crypto_scalarmult_curve25519(eA, pC) print("public CA", b85encode(pCA)) print() key = nacl.crypto_scalarmult_curve25519(eB, pCA) print("key: \t%s" % (b85encode(key))) key = nacl.crypto_scalarmult_curve25519(eC, pBA) print("key: \t%s" % (b85encode(key))) key = nacl.crypto_scalarmult_curve25519(eC, pAB) print("key: \t%s" % (b85encode(key)))
def _3user(): eA = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) pA = nacl.crypto_scalarmult_curve25519_base(eA) print "A public: \t%s\nA exp: \t%s" % (b85encode(pA), b85encode(eA)) eB = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) pB = nacl.crypto_scalarmult_curve25519_base(eB) print "B public: \t%s\nB exp: \t%s" % (b85encode(pB), b85encode(eB)) eC = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) pC = nacl.crypto_scalarmult_curve25519_base(eC) print "C public: \t%s\nC exp: \t%s" % (b85encode(pC), b85encode(eC)) print pAB = nacl.crypto_scalarmult_curve25519(eB, pA) print "public AB", b85encode(pAB) pBA = nacl.crypto_scalarmult_curve25519(eA, pB) print "public BA", b85encode(pBA) pCA = nacl.crypto_scalarmult_curve25519(eA, pC) print "public CA", b85encode(pCA) print key = nacl.crypto_scalarmult_curve25519(eB, pCA) print "key: \t%s" % (b85encode(key)) key = nacl.crypto_scalarmult_curve25519(eC, pBA) print "key: \t%s" % (b85encode(key)) key = nacl.crypto_scalarmult_curve25519(eC, pAB) print "key: \t%s" % (b85encode(key))
def send(self, plain): # update context if self.peer_pub != (b'\0' * nacl.crypto_scalarmult_curve25519_BYTES): # calculate a new incoming key, and finish that DH, start a new for # outgoing keys. # only do this directly after receiving a packet, not on later sends # without receiving any acks before, we reset peer_pub to signal, that # an incoming request has been already once processed like this. self.e_in = nacl.randombytes( nacl.crypto_scalarmult_curve25519_BYTES) self.in_prev = self.in_k self.in_k = nacl.crypto_scalarmult_curve25519( self.e_in, self.peer_pub) self.peer_pub = (b'\0' * nacl.crypto_scalarmult_curve25519_BYTES) # generate e_out self.e_out = nacl.randombytes( nacl.crypto_scalarmult_curve25519_BYTES) elif self.out_k == (b'\0' * nacl.crypto_secretbox_KEYBYTES): # only for the very first packet necessary # we explicitly need to generate e_out self.e_out = nacl.randombytes( nacl.crypto_scalarmult_curve25519_BYTES) # compose packet dh1 = nacl.crypto_scalarmult_curve25519_base(self.e_out) dh2 = (nacl.crypto_scalarmult_curve25519_base(self.e_in) if self.e_in != (b'\0' * nacl.crypto_scalarmult_curve25519_BYTES) else (b'\0' * nacl.crypto_scalarmult_curve25519_BYTES)) plain = b''.join((dh1, dh2, plain)) # encrypt the whole packet return self.encrypt(plain)
def send(self,plain): # update context if self.peer_pub != (b'\0' * nacl.crypto_scalarmult_curve25519_BYTES): # calculate a new incoming key, and finish that DH, start a new for # outgoing keys. # only do this directly after receiving a packet, not on later sends # without receiving any acks before, we reset peer_pub to signal, that # an incoming request has been already once processed like this. self.e_in = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) self.in_prev = self.in_k self.in_k = nacl.crypto_scalarmult_curve25519(self.e_in, self.peer_pub) self.peer_pub = (b'\0' * nacl.crypto_scalarmult_curve25519_BYTES) # generate e_out self.e_out = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) elif self.out_k == (b'\0' * nacl.crypto_secretbox_KEYBYTES): # only for the very first packet necessary # we explicitly need to generate e_out self.e_out = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) #else: # axolotlize # print 'axolotl!' # self.out_k = nacl.crypto_generichash(self.out_k, # nacl.crypto_scalarmult_curve25519(self.me_id.cs, self.peer_id.cp), # nacl.crypto_scalarmult_curve25519_BYTES) # compose packet dh1 = nacl.crypto_scalarmult_curve25519_base(self.e_out) dh2 = (nacl.crypto_scalarmult_curve25519_base(self.e_in) if self.e_in != (b'\0' * nacl.crypto_scalarmult_curve25519_BYTES) else (b'\0' * nacl.crypto_scalarmult_curve25519_BYTES)) plain = b''.join((dh1, dh2, plain)) # encrypt the whole packet return self.encrypt(plain)
def _2user(): # 1st user exp1 = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) public1 = nacl.crypto_scalarmult_curve25519_base(exp1) #print("public1: \t%s\nexp1: \t%s" % (b85encode(public1), b85encode(exp1))) print() # 2nd user exp2 = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) public2 = nacl.crypto_scalarmult_curve25519_base(exp2) key = nacl.crypto_scalarmult_curve25519(exp2, public1) print("key: \t%s" % (b85encode(key))) #print("public2: \t%s\nkey: \t%s" % (b85encode(public2), b85encode(key))) print() # 1st user completing DH key = nacl.crypto_scalarmult_curve25519(exp1, public2) print("key: \t%s" % (b85encode(key)))
def _2user(): # 1st user exp1 = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) public1 = nacl.crypto_scalarmult_curve25519_base(exp1) # print "public1: \t%s\nexp1: \t%s" % (b85encode(public1), b85encode(exp1)) print # 2nd user exp2 = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) public2 = nacl.crypto_scalarmult_curve25519_base(exp2) key = nacl.crypto_scalarmult_curve25519(exp2, public1) print "key: \t%s" % (b85encode(key)) # print "public2: \t%s\nkey: \t%s" % (b85encode(public2), b85encode(key)) print # 1st user completing DH key = nacl.crypto_scalarmult_curve25519(exp1, public2) print "key: \t%s" % (b85encode(key))
def __generate_esk(self, topic, usage): # ephemeral keys are use once only, so always ok to overwrite # caller must hold self.__eklock prior to calling if not topic in self.__esk or not topic in self.__epk: self.__esk[topic] = {} self.__epk[topic] = {} self.__esk[topic][usage] = pysodium.randombytes(pysodium.crypto_scalarmult_curve25519_BYTES) self.__epk[topic][usage] = pysodium.crypto_scalarmult_curve25519_base(self.__esk[topic][usage])
def dh1_handler(): exp = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) public = nacl.crypto_scalarmult_curve25519_base(exp) (sys.stdout.buffer if hasattr(sys.stdout, 'buffer') else sys.stdout).write(b"public component " + b85encode(public) + b'\n') (sys.stdout.buffer if hasattr(sys.stdout, 'buffer') else sys.stdout).write(b"secret exponent " + b85encode(exp) + b'\n') clearmem(exp)
def dh2_handler(peer): # provides a high level interface to receive a DH key exchange # request peer contains the public component generated by the peer # when initiating an DH exchange exp = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) public = nacl.crypto_scalarmult_curve25519_base(exp) secret = nacl.crypto_scalarmult_curve25519(exp, b85decode(peer)) return (public, secret)
def dh2_handler(peer): # provides a high level interface to receive a DH key exchange # request peer contains the public component generated by the peer # when initiating an DH exchange exp = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) public = nacl.crypto_scalarmult_curve25519_base(exp) secret = nacl.crypto_scalarmult_curve25519(exp, peer) return (public, secret)
def mpecdh1(self, keyring = []): self.key = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) keyring = [nacl.crypto_scalarmult_curve25519(self.key, public) for public in keyring] keyring.append(nacl.crypto_scalarmult_curve25519_base(self.key)) if len(keyring) == int(self.peers): # we are last, remove our own secret self.secret = keyring[0] keyring = keyring[1:] return keyring
def dh2_handler(peer): exp = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) public = nacl.crypto_scalarmult_curve25519_base(exp) (sys.stdout.buffer if hasattr(sys.stdout, 'buffer') else sys.stdout).write(b"public component " + b85encode(public) + b'\n') secret = nacl.crypto_scalarmult_curve25519(exp, b85decode(peer)) (sys.stdout.buffer if hasattr(sys.stdout, 'buffer') else sys.stdout).write(b"shared secret " + b85encode(secret) + b'\n') clearmem(secret) clearmem(exp)
def mpecdh(self, keyring=[]): if self.secret: return self.secret if not self.key: self.key = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) keyring = [nacl.crypto_scalarmult_curve25519(self.key, public) for public in keyring] keyring.append(nacl.crypto_scalarmult_curve25519_base(self.key)) if len(keyring) == self.peers: # we are last, remove our own secret self.secret = keyring[0] keyring = keyring[1:] else: self.secret = nacl.crypto_scalarmult_curve25519(self.key, keyring[0]) keyring = [nacl.crypto_scalarmult_curve25519(self.key, public) for public in keyring[1:]] clearmem(self.key) self.key = None self.next.mpecdh(keyring)
def mpecdh(self, keyring = []): if self.secret: return self.secret if not self.key: self.key = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) keyring = [nacl.crypto_scalarmult_curve25519(self.key, public) for public in keyring] keyring.append(nacl.crypto_scalarmult_curve25519_base(self.key)) if len(keyring) == self.peers: # we are last, remove our own secret self.secret = keyring[0] keyring = keyring[1:] else: self.secret = nacl.crypto_scalarmult_curve25519(self.key, keyring[0]) keyring = [nacl.crypto_scalarmult_curve25519(self.key, public) for public in keyring[1:]] clearmem(self.key) self.key = None self.next.mpecdh(keyring)
def new(self): self.sk = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) self.pk = nacl.crypto_scalarmult_curve25519_base(self.sk) return self
def dh1_handler(): # provides a high level interface to start a DH key exchange exp = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) public = nacl.crypto_scalarmult_curve25519_base(exp) return (exp, public)
def test_crypto_scalarmut_curve25519_base(self): s = pysodium.crypto_scalarmult_curve25519_base(pysodium.randombytes(pysodium.crypto_scalarmult_BYTES)) r = pysodium.crypto_scalarmult_curve25519_base(pysodium.randombytes(pysodium.crypto_scalarmult_BYTES)) pysodium.crypto_scalarmult_curve25519(s, r)
def test_crypto_scalarmult_curve25519_base(self): s = pysodium.crypto_scalarmult_curve25519_base( pysodium.randombytes(pysodium.crypto_scalarmult_BYTES)) r = pysodium.crypto_scalarmult_curve25519_base( pysodium.randombytes(pysodium.crypto_scalarmult_BYTES)) pysodium.crypto_scalarmult_curve25519(s, r)
def test_crypto_box_pk_from_sk(self): pk1, sk = pysodium.crypto_box_keypair() pk2 = pysodium.crypto_scalarmult_curve25519_base(sk) self.assertEqual(pk1, pk2)
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 """
def __init__(self): self.secret = None self.key = nacl.randombytes(nacl.crypto_scalarmult_curve25519_BYTES) self.public = nacl.crypto_scalarmult_curve25519_base(self.key)