def encrypt_blob(blob): # todo implement padding sk = get_sealkey() nonce = pysodium.randombytes(pysodium.crypto_secretbox_NONCEBYTES) ct = pysodium.crypto_secretbox(blob, nonce, sk) clearmem(sk) return nonce + ct
def main(): loop = asyncio.get_event_loop() # Each client connection will create a new protocol instance coro = loop.create_server(SphinxOracleProtocol, address, port) server = loop.run_until_complete(coro) esk, xsk, xpk = getkey(keydir) if None in (esk, xsk, xpk): print("no server keys available.\nabort") sys.exit(1) clearmem(xsk) clearmem(esk) # Serve requests until Ctrl+C is pressed if verbose: print('Serving on {}'.format(server.sockets[0].getsockname())) try: loop.run_forever() except KeyboardInterrupt: pass # Close the server server.close() loop.run_until_complete(server.wait_closed()) loop.close()
def clear(self): clearmem(self.e_in) clearmem(self.e_out) clearmem(self.out_k) clearmem(self.in_k) clearmem(self.in_prev) self.me_id.clear()
def decrypt_blob(blob): # todo implement padding sk = get_sealkey() nonce = blob[:pysodium.crypto_secretbox_NONCEBYTES] blob = blob[pysodium.crypto_secretbox_NONCEBYTES:] res = pysodium.crypto_secretbox_open(blob, nonce, sk) clearmem(sk) return res
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 decrypt_handler(infile=None, outfile=None, self=None, basedir=None): # provides a high level function to do decryption of files # infile specifies the filename of the input file, # if '-' or not specified it uses stdin # outfile specifies the filename of the output file, if not specified # it uses the same filename with '.pbp' appended # self specifies the recipient of the message for using pk crypto # basedir provides a root for the keystores needed for pk crypto # if self is specified pk crypto is used, otherwise symmetric # this function also handles buffering. fd = inputfd(infile) outfd = outputfd(outfile) key = None _type=struct.unpack('B',fd.read(1))[0] # asym if _type == ASYM_CIPHER: if not self: print >>sys.stderr, "Error: need to specify your own key using the --self param" raise ValueError size = struct.unpack('>L',fd.read(4))[0] r = [] for _ in xrange(size): rnonce = fd.read(nacl.crypto_box_NONCEBYTES) ct = fd.read(struct.unpack('B', fd.read(1))[0]) r.append((rnonce,ct)) me = publickey.Identity(self, basedir=basedir) me.clear() sender, key = me.keydecrypt(r) if not sender: # print >>sys.stderr, 'good key from', sender #else: raise ValueError('decryption failed') # sym elif _type == BLOCK_CIPHER: pwd = getpass.getpass('Passphrase for decrypting: ') key = scrypt.hash(pwd, scrypt_salt)[:nacl.crypto_secretbox_KEYBYTES] sender = None clearmem(pwd) else: raise ValueError('decryption failed') if key: nonce = fd.read(nacl.crypto_secretbox_NONCEBYTES) while len(nonce) == nacl.crypto_secretbox_NONCEBYTES: buf = fd.read(BLOCK_SIZE + (nacl.crypto_secretbox_ZEROBYTES - nacl.crypto_secretbox_BOXZEROBYTES)) if not buf: raise ValueError('decryption failed') break outfd.write(decrypt((nonce, buf), k = key)) nonce = fd.read(nacl.crypto_secretbox_NONCEBYTES) clearmem(key) if 0 < len(nonce) < nacl.crypto_secretbox_NONCEBYTES: raise ValueError('decryption failed') if fd != sys.stdin: fd.close() if outfd != sys.stdout and type(outfd) == file: outfd.close() return sender
def encrypt(msg, pwd=None, k=None): # symmetric nonce = nacl.randombytes(nacl.crypto_secretbox_NONCEBYTES) cleark = (k is None) if not k: k = getkey(nacl.crypto_secretbox_KEYBYTES, pwd=pwd) ciphertext = nacl.crypto_secretbox(msg, nonce, k) if cleark: clearmem(k) return (nonce, ciphertext)
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_start_handler(id, peer_count, self, infile = None, outfile = None, basedir = None): ctx = ecdh.MPECDH(id, peers = peer_count, me = self, basedir = basedir) keychain = ctx.mpecdh1(ecdh.load_dh_keychain(infile)) if not hasattr(ctx,'secret'): ctx.save() clearmem(ctx.key) ctx.key=None ecdh.save_dh_keychain(outfile, keychain) if hasattr(ctx,'secret'): return ctx.secret
def decrypt_handler(infile=None, outfile=None, self=None, basedir=None): if not infile or infile == '-': fd = sys.stdin.buffer if hasattr(sys.stdin, 'buffer') else sys.stdin else: fd = open(infile, 'rb') if not outfile or outfile == '-': outfd = sys.stdout.buffer if hasattr(sys.stdout, 'buffer') else sys.stdout else: outfd = open(outfile, 'wb') key = None type = struct.unpack('B', fd.read(1))[0] # asym if type == ASYM_CIPHER: if not self: sys.stderr.write( "Error: need to specify your own key using the --self param\n") raise ValueError size = struct.unpack('>L', fd.read(4))[0] r = [] for _ in range(size): rnonce = fd.read(nacl.crypto_box_NONCEBYTES) ct = fd.read(struct.unpack('B', fd.read(1))[0]) r.append((rnonce, ct)) me = publickey.Identity(self, basedir=basedir) me.clear() sender, key = me.keydecrypt(r) if sender: sys.stderr.write('good key from %s\n' % sender) else: sys.stderr.write('decryption failed\n') # sym elif type == BLOCK_CIPHER: pwd = getpass.getpass('Passphrase for decrypting: ') key = scrypt.hash(pwd, scrypt_salt)[:nacl.crypto_secretbox_KEYBYTES] clearmem(pwd) else: sys.stderr.write('decryption failed\n') if key: nonce = fd.read(nacl.crypto_secretbox_NONCEBYTES) while len(nonce) == nacl.crypto_secretbox_NONCEBYTES: buf = fd.read(BLOCK_SIZE) if not buf: sys.stderr.write('decryption failed\n') break outfd.write(decrypt((nonce, buf), k=key)) nonce = fd.read(nacl.crypto_secretbox_NONCEBYTES) clearmem(key) if 0 < len(nonce) < nacl.crypto_secretbox_NONCEBYTES: sys.stderr.write('decryption failed\n') if infile != sys.stdin: fd.close() if outfile != sys.stdout: outfd.close()
def get_signkey(id, rwd): mk = get_masterkey() seed = pysodium.crypto_generichash(SIGN_CTX, mk) clearmem(mk) # rehash with rwd so the user always contributes his pwd and the sphinx server it's seed seed = pysodium.crypto_generichash(seed, id) if rwd_keys: seed = pysodium.crypto_generichash(seed, rwd) pk, sk = pysodium.crypto_sign_seed_keypair(seed) clearmem(seed) return sk, pk
def decrypt(pkt, pwd=None, basedir=None, k=None): # symmetric cleark = (pwd is None) clearpwd = (k is None) if not k: if not pwd: pwd = getpass.getpass('Passphrase for decrypting: ') k = scrypt.hash(pwd, scrypt_salt)[:nacl.crypto_secretbox_KEYBYTES] if clearpwd: clearmem(pwd) res = nacl.crypto_secretbox_open(pkt[1], pkt[0], k) if cleark: clearmem(k) return res
def clear(self): clearmem(self.e_in) self.e_in = None clearmem(self.e_out) self.e_out = None clearmem(self.out_k) self.out_k = None clearmem(self.in_k) self.in_k = None clearmem(self.in_prev) self.in_prev = None self.me_id.clear()
def encrypt(msg, pwd=None, k=None): # encrypts a message symmetrically using crypto_secretbox # k specifies an encryption key, which if not supplied, is derived from # pwd which is queried from the user, if also not specified. # returns a (nonce, ciphertext) tuple nonce = nacl.randombytes(nacl.crypto_secretbox_NONCEBYTES) cleark = (k is None) if not k: k = getkey(nacl.crypto_secretbox_KEYBYTES, pwd=pwd) ciphertext = nacl.crypto_secretbox(msg, nonce, k) if cleark: clearmem(k) return (nonce, ciphertext)
def clear(self): clearmem(self.e_in) self.e_in=None clearmem(self.e_out) self.e_out=None clearmem(self.out_k) self.out_k=None clearmem(self.in_k) self.in_k=None clearmem(self.in_prev) self.in_prev=None self.me_id.clear()
def generate_password(chars, length, get_parts=False, secure=True): """Generate a string by randomly concatenating strings from a list Args: chars (list): list of str to generate password from length (int): length of password get_parts (bool): return strings password is made from, these strings will not be deleted by secure flag secure (bool): delete strings in character list from memory after generating password. WARNING: will yield individual strings inaccessible, e.g. if one string is 'a' and secure is enabled, the rest of the program calling this function cannot access the char 'a' anymore. Also, the actual password will remain in memory, only chars are deleted. Returns: tuple: (password (str), parts (list)), password and strings constituting passwords. parts in tuple is None if get_parts flag is False Example: Note: Will not pass docstring test >>> generate_password(['a', 'b', 'c'], 5) ('abcba', None) """ max_char = len(chars) - 1 # Construct Password parts = [] for i in range(0, length): parts.append(chars[random.SystemRandom().randint(0, max_char)]) password = ''.join(parts) # Erase each password character from memory if not part of password if secure is True: for i in chars: if get_parts is False or i not in parts: # Don't clear only part because it clears password if length == 1 and i in parts: continue clearmem(i) if get_parts is False: parts = None return password, parts
def qrcode(output, key): mk = get_masterkey() if key else b'' data = (bytes([1 * key + 2 * rwd_keys]) + mk + struct.pack("!H", port) + hostname.encode("utf8")) qr = QrCode.encode_binary(data, QrCode.Ecc.LOW) if key: clearmem(mk) clearmem(data) if output == 'txt': print_qr(qr) else: print(qr.to_svg_str(2))
def encrypt_handler(infile=None, outfile=None, recipient=None, self=None, basedir=None): # provides a high level function to do encryption of files # infile specifies the filename of the input file, # if '-' or not specified it uses stdin # outfile specifies the filename of the output file, if not specified # it uses the same filename with '.pbp' appended # recipient specifies the name of the recipient for using public key crypto # self specifies the sender for signing the message using pk crypto # basedir provides a root for the keystores needed for pk crypto # if both self and recipient is specified pk crypto is used, otherwise symmetric # this function also handles buffering. fd = inputfd(infile) outfd = outputfd(outfile or (infile + '.pbp' if infile not in [None, '-'] else '-')) if recipient and self: # let's do public key encryption key = nacl.randombytes(nacl.crypto_secretbox_KEYBYTES) me = publickey.Identity(self, basedir=basedir) size = struct.pack('>H', len(recipient)) # write out encrypted message key (nonce, c(key+recplen)) for each recipient for r in recipient: r = publickey.Identity(r, basedir=basedir, publicOnly=True) nonce = nacl.randombytes(nacl.crypto_box_NONCEBYTES) outfd.write(nonce) outfd.write(nacl.crypto_box(key + size, nonce, r.cp, me.cs)) me.clear() else: # let's do symmetric crypto key = getkey(nacl.crypto_secretbox_KEYBYTES) buf = fd.read(BLOCK_SIZE) if buf: nonce, cipher = encrypt(buf, k=key) outfd.write(nonce) outfd.write(cipher) buf = fd.read(BLOCK_SIZE) while buf: nonce = inc_nonce(nonce) nonce, cipher = encrypt(buf, k=key, nonce=nonce) outfd.write(cipher) buf = fd.read(BLOCK_SIZE) clearmem(key) key = None if fd != sys.stdin: fd.close() if outfd != sys.stdout and isinstance(outfd, file): outfd.close()
def init(self, peer): """ as per https://github.com/trevp/axolotl/wiki/newversion (Nov 19, 2013 · 41 revisions) Key Agreement -------------- - Parties exchange identity keys (A,B) and handshake keys (Ah,Ai) and (Bh,Bi) - Parties assign themselves "Alice" or "Bob" roles by comparing public keys - Parties perform triple-DH with (A,B,Ah,Bh) and derive initial keys: Alice: KDF from triple-DH: RK, HKs, HKr, NHKs, NHKr, CKs, CKr DHIs, DHIr = A, B DHRs, DHRr = <none>, Bi Ns, Nr = 0, 0 PNs = 0 bobs_first_message = False Bob: KDF from triple-DH: RK, HKr, HKs, NHKr, NHKs, CKr, CKs DHIs, DHIr = B, A DHRs, DHRr = Bi, <none> Ns, Nr = 0, 0 PNs = 0 bobs_first_message = True """ self.peer = peer self.DHIr = peer['identitykey'] self.isalice = self.me.identitykey.pk <= peer['identitykey'] mk = self.tripledh() self.RK = nacl.crypto_generichash(mk, "RK",KEY_SIZE) if self.isalice: self.DHRr = peer['DHRs'] self.DHRs = None self.HKs = nacl.crypto_generichash(mk, "HKs", KEY_SIZE) self.HKr = nacl.crypto_generichash(mk, "HKr", KEY_SIZE) self.NHKs = nacl.crypto_generichash(mk, "NHKs", KEY_SIZE) self.NHKr = nacl.crypto_generichash(mk, "NHKr", KEY_SIZE) self.CKs = nacl.crypto_generichash(mk, "CKs", KEY_SIZE) self.CKr = nacl.crypto_generichash(mk, "CKr", KEY_SIZE) self.bobs1stmsg = False else: self.HKs = nacl.crypto_generichash(mk, "HKr", KEY_SIZE) self.HKr = nacl.crypto_generichash(mk, "HKs", KEY_SIZE) self.NHKs = nacl.crypto_generichash(mk, "NHKr", KEY_SIZE) self.NHKr = nacl.crypto_generichash(mk, "NHKs", KEY_SIZE) self.CKs = nacl.crypto_generichash(mk, "CKr", KEY_SIZE) self.CKr = nacl.crypto_generichash(mk, "CKs", KEY_SIZE) self.bobs1stmsg = True clearmem(mk) mk = None
def init(self, peer): """ as per https://github.com/trevp/axolotl/wiki/newversion (Nov 19, 2013 · 41 revisions) Key Agreement -------------- - Parties exchange identity keys (A,B) and handshake keys (Ah,Ai) and (Bh,Bi) - Parties assign themselves "Alice" or "Bob" roles by comparing public keys - Parties perform triple-DH with (A,B,Ah,Bh) and derive initial keys: Alice: KDF from triple-DH: RK, HKs, HKr, NHKs, NHKr, CKs, CKr DHIs, DHIr = A, B DHRs, DHRr = <none>, Bi Ns, Nr = 0, 0 PNs = 0 bobs_first_message = False Bob: KDF from triple-DH: RK, HKr, HKs, NHKr, NHKs, CKr, CKs DHIs, DHIr = B, A DHRs, DHRr = Bi, <none> Ns, Nr = 0, 0 PNs = 0 bobs_first_message = True """ self.peer = peer self.DHIr = peer['identitykey'] self.isalice = self.me.identitykey.pk <= peer['identitykey'] mk = self.tripledh() self.RK = nacl.crypto_generichash(mk, "RK", KEY_SIZE) if self.isalice: self.DHRr = peer['DHRs'] self.DHRs = None self.HKs = nacl.crypto_generichash(mk, "HKs", KEY_SIZE) self.HKr = nacl.crypto_generichash(mk, "HKr", KEY_SIZE) self.NHKs = nacl.crypto_generichash(mk, "NHKs", KEY_SIZE) self.NHKr = nacl.crypto_generichash(mk, "NHKr", KEY_SIZE) self.CKs = nacl.crypto_generichash(mk, "CKs", KEY_SIZE) self.CKr = nacl.crypto_generichash(mk, "CKr", KEY_SIZE) self.bobs1stmsg = False else: self.HKs = nacl.crypto_generichash(mk, "HKr", KEY_SIZE) self.HKr = nacl.crypto_generichash(mk, "HKs", KEY_SIZE) self.NHKs = nacl.crypto_generichash(mk, "NHKr", KEY_SIZE) self.NHKr = nacl.crypto_generichash(mk, "NHKs", KEY_SIZE) self.CKs = nacl.crypto_generichash(mk, "CKr", KEY_SIZE) self.CKr = nacl.crypto_generichash(mk, "CKs", KEY_SIZE) self.bobs1stmsg = True clearmem(mk) mk = None
def init_key(): kfile = os.path.join(datadir, 'masterkey') if os.path.exists(kfile): print("Already initialized.") return 1 if not os.path.exists(datadir): os.makedirs(datadir, 0o700, exist_ok=True) mk = pysodium.randombytes(32) try: with open(kfile, 'wb') as fd: if not win: os.fchmod(fd.fileno(), 0o600) fd.write(mk) finally: clearmem(mk) return 0
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 encrypt_handler(infile=None, outfile=None, recipient=None, self=None, basedir=None): if not infile or infile == '-': fd = sys.stdin.buffer if hasattr(sys.stdin, 'buffer') else sys.stdin else: fd = open(infile, 'rb') if outfile == '-': outfd = sys.stdout.buffer if hasattr(sys.stdout, 'buffer') else sys.stdout else: outfd = open(outfile or infile + '.pbp', 'wb') if recipient and self: # let's do public key encryption key = nacl.randombytes(nacl.crypto_secretbox_KEYBYTES) me = publickey.Identity(self, basedir=basedir) peerkeys = me.keyencrypt(key, recipients=[ publickey.Identity(x, basedir=basedir) for x in recipient ]) me.clear() outfd.write(struct.pack("B", ASYM_CIPHER)) outfd.write(struct.pack(">L", len(peerkeys))) for rnonce, ct in peerkeys: outfd.write(rnonce) outfd.write(struct.pack("B", len(ct))) outfd.write(ct) else: # let's do symmetric crypto key = getkey(nacl.crypto_secretbox_KEYBYTES) outfd.write(struct.pack("B", BLOCK_CIPHER)) buf = fd.read(BLOCK_SIZE) while buf: nonce, cipher = encrypt(buf, k=key) outfd.write(nonce) outfd.write(cipher) buf = fd.read(BLOCK_SIZE) clearmem(key) if infile != sys.stdin: fd.close() if outfile != sys.stdout: outfd.close()
def encrypt_handler(infile=None, outfile=None, recipient=None, self=None, basedir=None): # provides a high level function to do encryption of files # infile specifies the filename of the input file, # if '-' or not specified it uses stdin # outfile specifies the filename of the output file, if not specified # it uses the same filename with '.pbp' appended # recipient specifies the name of the recipient for using public key crypto # self specifies the sender for signing the message using pk crypto # basedir provides a root for the keystores needed for pk crypto # if both self and recipient is specified pk crypto is used, otherwise symmetric # this function also handles buffering. fd = inputfd(infile) outfd = outputfd(outfile or (infile+'.pbp' if infile not in [None,'-'] else '-')) if recipient and self: # let's do public key encryption key = nacl.randombytes(nacl.crypto_secretbox_KEYBYTES) me = publickey.Identity(self, basedir=basedir) size = struct.pack('>H',len(recipient)) # write out encrypted message key (nonce, c(key+recplen)) for each recipient for r in recipient: r = publickey.Identity(r, basedir=basedir, publicOnly=True) nonce = nacl.randombytes(nacl.crypto_box_NONCEBYTES) outfd.write(nonce) outfd.write(nacl.crypto_box(key+size, nonce, r.cp, me.cs)) me.clear() else: # let's do symmetric crypto key = getkey(nacl.crypto_secretbox_KEYBYTES) buf = fd.read(BLOCK_SIZE) if buf: nonce, cipher = encrypt(buf, k=key) outfd.write(nonce) outfd.write(cipher) buf = fd.read(BLOCK_SIZE) while buf: nonce = inc_nonce(nonce) nonce, cipher = encrypt(buf, k=key, nonce=nonce) outfd.write(cipher) buf = fd.read(BLOCK_SIZE) clearmem(key) key=None if fd != sys.stdin: fd.close() if outfd != sys.stdout: outfd.close()
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 delete(s, pwd, user, host): # run sphinx to recover rwd for authentication id = getid(host, user) r, alpha = sphinxlib.challenge(pwd) msg = b''.join([DELETE, id, alpha]) s.send(msg) # alpha rwd = auth(s, id, pwd, r) # delete user from user list for this host # a malicous server could correlate all accounts on this services to this users here # first query user record for this host id = getid(host, '') s.send(id) # wait for user blob bsize = s.recv(2) bsize = struct.unpack('!H', bsize)[0] if bsize == 0: # this should not happen, it means something is corrupt print("error: server has no associated user record for this host") return blob = s.recv(bsize) # todo handle this if blob == b'fail': return blob = decrypt_blob(blob) users = set(blob.decode().split('\x00')) # todo/fix? we do not recognize if the user is already included in this list # this should not happen, but maybe it's a sign of corruption? users.remove(user) blob = ('\x00'.join(sorted(users))).encode() # notice we do not add rwd to encryption of user blobs blob = encrypt_blob(blob) bsize = len(blob) if bsize >= 2**16: raise ValueError("error: blob is bigger than 64KB.") blob = struct.pack("!H", bsize) + blob blob = sign_blob(blob, id, b'') s.send(blob) if b'ok' != s.recv(2): return clearmem(rwd) return True
def auth(s, id, pwd=None, r=None): if r is None: nonce = s.recv(32) if len(nonce) != 32: return False rwd = b'' else: msg = s.recv(64) if len(msg) != 64: return False beta = msg[:32] nonce = msg[32:] rwd = sphinxlib.finish(pwd, r, beta, id) sk, pk = get_signkey(id, rwd) sig = pysodium.crypto_sign_detached(nonce, sk) clearmem(sk) s.send(sig) return rwd
def encrypt_handler(infile=None, outfile=None, recipient=None, self=None, basedir=None): # provides a high level function to do encryption of files # infile specifies the filename of the input file, # if '-' or not specified it uses stdin # outfile specifies the filename of the output file, if not specified # it uses the same filename with '.pbp' appended # recipient specifies the name of the recipient for using public key crypto # self specifies the sender for signing the message using pk crypto # basedir provides a root for the keystores needed for pk crypto # if both self and recipient is specified pk crypto is used, otherwise symmetric # this function also handles buffering. fd = inputfd(infile) outfd = outputfd(outfile or infile+'.pbp') if recipient and self: # let's do public key encryption key = nacl.randombytes(nacl.crypto_secretbox_KEYBYTES) me = publickey.Identity(self, basedir=basedir) peerkeys = me.keyencrypt(key, recipients=[publickey.Identity(x, basedir=basedir) for x in recipient]) me.clear() outfd.write(struct.pack("B", ASYM_CIPHER)) outfd.write(struct.pack(">L", len(peerkeys))) for rnonce, ct in peerkeys: outfd.write(rnonce) outfd.write(struct.pack("B", len(ct))) outfd.write(ct) else: # let's do symmetric crypto key = getkey(nacl.crypto_secretbox_KEYBYTES) outfd.write(struct.pack("B", BLOCK_CIPHER)) buf = fd.read(BLOCK_SIZE) while buf: nonce, cipher = encrypt(buf, k=key) outfd.write(nonce) outfd.write(cipher) buf = fd.read(BLOCK_SIZE) clearmem(key) if fd != sys.stdin: fd.close() if outfd != sys.stdout: outfd.close()
def encrypt(msg, pwd=None, k=None, nonce=None): # encrypts a message symmetrically using crypto_secretbox # k specifies an encryption key, which if not supplied, is derived from # pwd which is queried from the user, if also not specified. # returns a (nonce, ciphertext) tuple if nonce == None: nonce = nacl.randombytes(nacl.crypto_secretbox_NONCEBYTES) clearpwd = (pwd is None) cleark = (k is None) if not k: k = getkey(nacl.crypto_secretbox_KEYBYTES, pwd=pwd) ciphertext = nacl.crypto_secretbox(msg, nonce, k) if cleark and k: clearmem(k) k = None if clearpwd and pwd: clearmem(pwd) pwd = None return (nonce, ciphertext)
def doSphinx(s, op, pwd, user, host): id = getid(host, user) r, alpha = sphinxlib.challenge(pwd) msg = b''.join([op, id, alpha]) s.send(msg) if op != GET: # == CHANGE, UNDO, COMMIT # auth: do sphinx with current seed, use it to sign the nonce auth(s, id, pwd, r) resp = s.recv(32 + RULE_SIZE) # beta + sealed rules if resp == b'\x00\x04fail' or len(resp) != 32 + RULE_SIZE: raise ValueError("error: sphinx protocol failure.") beta = resp[:32] rules = resp[32:] rwd = sphinxlib.finish(pwd, r, beta, id) try: classes, size = unpack_rule(rules) except ValueError: return if op != GET: # == CHANGE, UNDO, COMMIT # in case of undo/commit we also need to rewrite the rules and pub auth signing key blob if op in {UNDO, COMMIT}: sk, pk = get_signkey(id, rwd) clearmem(sk) rule = encrypt_blob(pack_rule(classes, size)) # send over new signed(pubkey, rule) msg = b''.join([pk, rule]) msg = sign_blob(msg, id, rwd) s.send(msg) if s.recv(2) != b'ok': print( "ohoh, something is corrupt, and this is a bad, very bad error message in so many ways" ) return ret = bin2pass.derive(pysodium.crypto_generichash(PASS_CTX, rwd), classes, size).decode() clearmem(rwd) return ret
def update_rec( s, host, item ): # this is only for user blobs. a UI feature offering a list of potential usernames. id = getid(host, '') s.send(id) # wait for user blob bsize = s.recv(2) bsize = struct.unpack('!H', bsize)[0] # todo oracle can also just say fail - without a pktsize if bsize == 0: # it is a new blob, we need to attach an auth signing pubkey sk, pk = get_signkey(id, b'') clearmem(sk) # we encrypt with an empty rwd, so that the user list is independent of the master pwd blob = encrypt_blob(item.encode()) bsize = len(blob) if bsize >= 2**16: raise ValueError("error: blob is bigger than 64KB.") blob = struct.pack("!H", bsize) + blob # writes need to be signed, and sinces its a new blob, we need to attach the pubkey blob = b''.join([pk, blob]) # again no rwd, to be independent of the master pwd blob = sign_blob(blob, id, b'') else: blob = s.recv(bsize) if blob == b'fail': print("error: reading blob failed") return blob = decrypt_blob(blob) items = set(blob.decode().split('\x00')) # todo/fix? we do not recognize if the user is already included in this list # this should not happen, but maybe it's a sign of corruption? items.add(item) blob = ('\x00'.join(sorted(items))).encode() # notice we do not add rwd to encryption of user blobs blob = encrypt_blob(blob) bsize = len(blob) if bsize + 2 >= 2**16: raise ValueError("error: blob is bigger than 64KB.") blob = struct.pack("!H", bsize) + blob blob = sign_blob(blob, id, b'') s.send(blob)
def data_received(self, data): res = b'' if verbose: print('Data received: {!r}'.format(data)) esk, xsk, xpk = getkey(keydir) data = pysodium.crypto_box_seal_open(data, xpk, xsk) clearmem(xsk) if data[64] == 0: res = self.create(data) elif data[64] == GET: # needs id, challenge, sig(id) # returns output from ./response | fail res = self.get(data) elif data[64] == CHANGE: # needs id, challenge, sig(id) # changes stored secret # returns output from ./response | fail res = self.change(data) elif data[64] == DELETE: # needs id, sig(id) # returns ok|fail res = self.delete(data) elif data[64] == COMMIT: # needs id, sig(id) # returns ok|fail res = self.commit(data) if verbose: print('Send: {!r}'.format(res)) res = pysodium.crypto_sign(res, esk) clearmem(esk) self.transport.write(res) if verbose: print('Close the client socket') self.transport.close()
def decrypt(pkt, pwd=None, k=None, retries=3): # decrypts a message symmetrically using crypto_secretbox # pkt is a (nonce, ciphertext) tuple # k specifies an encryption key, which if not supplied, is derived from # pwd which is queried from the user, if also not specified. clearpwd = (pwd is None) cleark = (k is None) cnt = 0 res = None while cnt < retries: if not k: if not pwd: pwd = getpass.getpass('\nPassphrase for decrypting: ') k = scrypt.hash(pwd, scrypt_salt)[:nacl.crypto_secretbox_KEYBYTES] if clearpwd: clearmem(pwd) pwd = None try: res = nacl.crypto_secretbox_open(pkt[1], pkt[0], k) except ValueError: cnt += 1 if cleark: clearmem(k) k = None continue break if cleark and k: clearmem(k) k = None if res: return res
def decrypt(pkt, pwd=None, k=None, retries=3): # decrypts a message symmetrically using crypto_secretbox # pkt is a (nonce, ciphertext) tuple # k specifies an encryption key, which if not supplied, is derived from # pwd which is queried from the user, if also not specified. cleark = (pwd is None) clearpwd = (k is None) cnt=0 res = None while cnt<retries: if not k: if not pwd: pwd = getpass.getpass('\nPassphrase for decrypting: ') k = scrypt.hash(pwd, scrypt_salt)[:nacl.crypto_secretbox_KEYBYTES] if clearpwd: clearmem(pwd) pwd = None try: res = nacl.crypto_secretbox_open(pkt[1], pkt[0], k) except ValueError: cnt += 1 if cleark: clearmem(k) k = None continue break if cleark: clearmem(k) if res: return res
def send(self, msg): """ as per https://github.com/trevp/axolotl/wiki/newversion (Nov 19, 2013 · 41 revisions) Sending messages ----------------- Local variables: MK : message key if DHRs == <none>: DHRs = generateECDH() MK = HASH(CKs || "0") msg = Enc(HKs, Ns || PNs || DHRs) || Enc(MK, plaintext) Ns = Ns + 1 CKs = HASH(CKs || "1") return msg """ if self.DHRs == None: self.DHRs = Key().new() self.PNs = self.Ns # wtf: not in spec, but seems needed self.Ns = 0 # wtf: not in spec, but seems needed mk = nacl.crypto_generichash(self.CKs, 'MK', nacl.crypto_secretbox_KEYBYTES) hnonce = nacl.randombytes(nacl.crypto_secretbox_NONCEBYTES) mnonce = nacl.randombytes(nacl.crypto_secretbox_NONCEBYTES) msg = ''.join( (hnonce, mnonce, nacl.crypto_secretbox( ''.join((struct.pack('>I', self.Ns), struct.pack('>I', self.PNs), self.DHRs.pk)), hnonce, self.HKs), nacl.crypto_secretbox(msg, mnonce, mk))) clearmem(mk) mk = None self.Ns += 1 self.CKs = nacl.crypto_generichash(self.CKs, "CK", nacl.crypto_secretbox_KEYBYTES) return msg
def send(self, msg): """ as per https://github.com/trevp/axolotl/wiki/newversion (Nov 19, 2013 · 41 revisions) Sending messages ----------------- Local variables: MK : message key if DHRs == <none>: DHRs = generateECDH() MK = HASH(CKs || "0") msg = Enc(HKs, Ns || PNs || DHRs) || Enc(MK, plaintext) Ns = Ns + 1 CKs = HASH(CKs || "1") return msg """ if self.DHRs == None: self.DHRs = Key().new() self.PNs = self.Ns # wtf: not in spec, but seems needed self.Ns = 0 # wtf: not in spec, but seems needed mk = nacl.crypto_generichash(self.CKs, 'MK', nacl.crypto_secretbox_KEYBYTES) hnonce = nacl.randombytes(nacl.crypto_secretbox_NONCEBYTES) mnonce = nacl.randombytes(nacl.crypto_secretbox_NONCEBYTES) msg = ''.join((hnonce, mnonce, nacl.crypto_secretbox( ''.join((struct.pack('>I',self.Ns), struct.pack('>I',self.PNs), self.DHRs.pk)), hnonce, self.HKs), nacl.crypto_secretbox(msg, mnonce, mk))) clearmem(mk) mk = None self.Ns += 1 self.CKs = nacl.crypto_generichash(self.CKs, "CK", nacl.crypto_secretbox_KEYBYTES) return msg
def create(s, pwd, user, host, char_classes, size=0): # 1st step OPRF on the new seed id = getid(host, user) r, alpha = sphinxlib.challenge(pwd) msg = b''.join([CREATE, id, alpha]) s.send(msg) # wait for response from sphinx server beta = s.recv(32) if beta == b'\x00\x04fail': raise ValueError("error: sphinx protocol failure.") rwd = sphinxlib.finish(pwd, r, beta, id) # second phase, derive new auth signing pubkey sk, pk = get_signkey(id, rwd) clearmem(sk) try: size = int(size) except: raise ValueError("error: size has to be integer.") rule = encrypt_blob(pack_rule(char_classes, size)) # send over new signed(pubkey, rule) msg = b''.join([pk, rule]) msg = sign_blob(msg, id, rwd) s.send(msg) # add user to user list for this host # a malicous server could correlate all accounts on this services to this users here update_rec(s, host, user) ret = bin2pass.derive(pysodium.crypto_generichash(PASS_CTX, rwd), char_classes, size).decode() clearmem(rwd) return ret
def clear(self): if 'ms' in self.__dict__.keys(): clearmem(self.ms) del self.ms if 'ms' in self.__dict__.keys(): clearmem(self.cs) del self.cs if 'ms' in self.__dict__.keys(): clearmem(self.ss) del self.ss
def main(args): """Control program flow based on arguments. See --help and README for args Args: args (ArgumentParser): argparse ArgumentParser class """ # Print README.md and exit if args.tool == 'readme': with resource_stream('aspgen', 'README.md') as in_handle: to_print = True if args.header is None else False if args.list_headers is True: # Don't print non-headers to_print = False header_depth = None for line in in_handle: if line[0] == '#' and args.header is not None: header = line.split(' ', 1)[-1] # Start printing at matching header if header.strip().lower() == args.header.lower(): to_print = True header_depth = line.count('#') # Stop reading and printing at next header of equal depth elif header_depth is not None and \ line.count('#') == header_depth: break if args.list_headers is True and line[0] == '#': print(line.strip()) if to_print is True: print(line.strip()) sys.exit(0) # Decrypt file and exit if args.tool == 'decrypter': key = args.key_file.read() args.key_file.close() print(decrypt_file(key, args.report_file)) sys.exit(0) # Begin actual password generation and analysis portion of program # Check flags if hasattr(args, 'guess_speeds') and hasattr(args, 'stats'): if args.guess_speeds is not None and args.stats is False: raise AttributeError('--guess_speeds requires --stats') # Open memory file for report setattr(args, 'mem_report_file', cStringIO.StringIO()) if hasattr(args, 'encrypt') and args.encrypt is not None: try: assert args.report is not None except AssertionError: raise AssertionError('--encrypt requires --report') # Secure Environment resource.RLIMIT_CORE = 0 # Prevent core dumps if args.tool == 'generator' or args.tool == 'dict_generator': entropy = 'Unavailable' try: entropy = open('/proc/sys/kernel/random/entropy_avail', 'r').read() entropy = int(entropy.strip()) except IOError: print('Cannot read entropy from ' '/proc/sys/kernel/random/entropy_avail') entropy = 'Unavailable' if entropy < args.system_entropy: print('System entropy is below {0}' .format(str(args.system_entropy))) print('Type commands, open applications, read files, etc. to ') print('raise system entropy. (Type Ctrl+C to end program)') if entropy != 'Unavailable': while entropy < args.system_entropy: sleep(1) entropy = open('/proc/sys/kernel/random/entropy_avail', 'r') \ .read() entropy = int(entropy.strip()) # Easter Egg # 3.5e+8: gizmodo/5966169/the-hardware-hackers-use-to-crack-your-passwords # 4.0+12 AntMiner S7 # 1.0e+16 NSA? if hasattr(args, 'guess_speeds') is True and args.guess_speeds == [0]: args.guess_speeds = [3.4e+8, 4.0e+12, 1.0e+16] # Print fanciful output to record password generation information if args.report is not None: par('-' * 79 + os.linesep, report=args.mem_report_file) par('aspgen V{0}'.format(__version__).center(79) + os.linesep, report=args.mem_report_file) par('-' * 79 + os.linesep, report=args.mem_report_file) par(os.linesep, report=args.mem_report_file) par('Parameters' + os.linesep, report=args.mem_report_file) par('----------' + os.linesep, report=args.mem_report_file) par(os.linesep, report=args.mem_report_file) lines = textwrap.wrap('Command: {0}'.format( os.linesep.join(sys.argv[:])), 79) for line in lines: par(line + os.linesep, report=args.mem_report_file) for arg in vars(args): par('{0}: {1}'.format(arg, getattr(args, arg)) + os.linesep, report=args.mem_report_file) par(os.linesep, report=args.mem_report_file) par('Environmental Data' + os.linesep, report=args.mem_report_file) par('------------------' + os.linesep, report=args.mem_report_file) par(os.linesep, report=args.mem_report_file) par('Core Dumps: Disabled' + os.linesep, report=args.mem_report_file) par('System Entropy: ' + str(entropy) + os.linesep, report=args.mem_report_file) par(os.linesep, report=args.mem_report_file) par('Password' + os.linesep, report=args.mem_report_file) par('--------' + os.linesep, report=args.mem_report_file) par(os.linesep, report=args.mem_report_file) if args.tool == 'generator' or args.tool == 'analyzer': if args.tool == 'generator': # Disable --all if any other flag is given if args.lower_letters is True or args.upper_letters is True or \ args.special_characters is True or args.numbers is True: args.all = False # Modify flags to make --alphanumeric work, overrides all flags if args.alphanumeric is True: args.all = False args.lower_letters = True args.upper_letters = True args.numbers = True args.special_characters = False chars = password_characters(all=args.all, lower_letters=args.lower_letters, upper_letters=args.upper_letters, numbers=args.numbers, special_chars=args.special_characters) num_chars = len(chars) password = generate_password(chars, args.length, secure=False)[0] message = 'Password: {0}'.format(password) par(message + os.linesep, to_print=True, report=args.mem_report_file) clearmem(message) # Obliterate password par(os.linesep, report=args.mem_report_file) elif args.tool == 'analyzer': password = getpass() par('Password: '******'Detected lowercase letters in password' + os.linesep, report=args.mem_report_file) num_chars += len(lower_letters) if len(pass_set.intersection(upper_letters)) != 0: par('Detected uppercase letters in password' + os.linesep, report=args.mem_report_file) num_chars += len(upper_letters) if len(pass_set.intersection(numbers)) != 0: par('Detected numbers in password' + os.linesep, report=args.mem_report_file) num_chars += len(numbers) if len(pass_set.intersection(special_chars)) != 0: par('Detected special characters in password' + os.linesep, report=args.mem_report_file) num_chars += len(special_chars) par(os.linesep, report=args.mem_report_file) # Analyze password and par stats if args.tool == 'analyzer' or args.stats is True: par('Password Stats' + os.linesep, report=args.mem_report_file) par('--------------' + os.linesep, report=args.mem_report_file) par(os.linesep, report=args.mem_report_file) stats = password_stats(len(password), num_chars, guess_speeds=args.guess_speeds) c, e = print_stats(stats['combinations'], stats['entropy']) par(c + os.linesep, to_print=True, report=args.mem_report_file) par(e + os.linesep, to_print=True, report=args.mem_report_file) if stats['guess_table'] is not None: par('{0}Average Time to Cracked Password'.format(os.linesep) + os.linesep, to_print=True, report=args.mem_report_file) par(str(stats['guess_table']), to_print=True, report=args.mem_report_file) par(os.linesep, report=args.mem_report_file) par(os.linesep, report=args.mem_report_file) clearmem(password) # Obliterate password elif args.tool == 'dict_analyzer' or args.tool == 'dict_generator': if args.tool == 'dict_generator': # Raise error if lengths would produce zero words to choose from if args.max_length < args.min_length: raise ValueError('Max word length is less than min word ' 'length') # Obtain words matching user requirements dict_words = [] if args.uncommon is True: with resource_stream('aspgen', 'words.txt') as in_handle: for word in in_handle: word = word.strip() if args.min_length <= len(word) <= args.max_length: dict_words.append(word) else: with resource_stream('aspgen', 'common_words.txt') as \ in_handle: for word in in_handle: word = word.strip() if args.min_length <= len(word) <= args.max_length: dict_words.append(word) password, words = generate_password(dict_words, args.length, get_parts=True) message = 'Words in Password: {0}'.format(' '.join(words)) par(message + os.linesep, to_print=True, report=args.mem_report_file) clearmem(message) # Obliterate words in password if args.length > 1: # clearmem will clear password if one word for word in words: clearmem(word) # Obliterate words in password message = 'Password: {0}'.format(password) par(message + os.linesep, to_print=True, report=args.mem_report_file) clearmem(message) # Obliterate password par(os.linesep, report=args.mem_report_file) elif args.tool == 'dict_analyzer': # Obtain common words dict_words = [] with resource_stream('aspgen', 'common_words.txt') as in_handle: for word in in_handle: word = word.strip() dict_words.append(word) password = getpass() par('Password: '******'dict_generator' and args.stats is True: par('Password Stats' + os.linesep, report=args.mem_report_file) par('--------------' + os.linesep, report=args.mem_report_file) par(os.linesep, report=args.mem_report_file) stats = password_stats(args.length, len(dict_words), guess_speeds=args.guess_speeds) stats['combinations_raw'] = 26 ** len(password) stats['entropy_raw'] = log(stats['combinations_raw'], 2) elif args.tool == 'dict_analyzer': par('Password Stats' + os.linesep, report=args.mem_report_file) par('--------------' + os.linesep, report=args.mem_report_file) par(os.linesep, report=args.mem_report_file) stats = dict_stats(password, dict_words, guess_speeds=args.guess_speeds) par('Words in Password: {0}{1}' .format(' '.join(stats['words']), os.linesep), to_print=True, report=args.mem_report_file) if args.tool == 'dict_analyzer' or args.stats is True: c, e = print_stats(stats['combinations'], stats['entropy']) par(c + os.linesep, to_print=True, report=args.mem_report_file) par(e + os.linesep, to_print=True, report=args.mem_report_file) if args.guess_speeds is not None: par('{0}Average Time to Cracked Password'.format(os.linesep) + os.linesep, to_print=True, report=args.mem_report_file) par(str(stats['guess_table']), to_print=True, report=args.mem_report_file) par(os.linesep, report=args.mem_report_file) # Warn user if password is more vulnerable to brute force attacks if stats is not None and stats['entropy_raw'] < stats['entropy']: par(os.linesep, to_print=True, report=args.mem_report_file) par('!' * 79 + os.linesep, to_print=True, report=args.mem_report_file) par('!' + 'WARNING'.center(77) + '!' + os.linesep, to_print=True, report=args.mem_report_file) par('!' * 79 + os.linesep, to_print=True, report=args.mem_report_file) par(os.linesep, to_print=True, report=args.mem_report_file) par('Your password is more vulnerable to brute force ' + os.linesep, to_print=True, report=args.mem_report_file) par('attacks than dictionary attacks. Consider generating ' + os.linesep, to_print=True, report=args.mem_report_file) par('a dictionary password with more words or longer words.' + os.linesep, to_print=True, report=args.mem_report_file) par(os.linesep, to_print=True, report=args.mem_report_file) par('Brute Force Stats' + os.linesep, to_print=True, report=args.mem_report_file) par('-----------------' + os.linesep, to_print=True, report=args.mem_report_file) par(os.linesep, to_print=True, report=args.mem_report_file) c, e = print_stats(stats['combinations_raw'], stats['entropy_raw']) par(c + os.linesep, to_print=True, report=args.mem_report_file) par(e + os.linesep, to_print=True, report=args.mem_report_file) par(os.linesep, report=args.mem_report_file) for word in dict_words: clearmem(word) # Obliterate words in password clearmem(password) # Obliterate password # Print end of report par('-' * 79 + os.linesep, report=args.mem_report_file) par('Exiting aspgen V{0}'.format(__version__).center( 79) + os.linesep, report=args.mem_report_file) par('-' * 79 + os.linesep, report=args.mem_report_file) # Securely encrypt report file if args.encrypt is not None: chars = password_characters() encrypt_password = generate_password(chars, 32)[0] hash = SHA256.new() hash.update(encrypt_password) key = hash.digest() args.encrypt.write(key) args.encrypt.close() encrypt_file(key, args.mem_report_file, args.report) args.report.close() # Write report file to disk if not encrypted if args.report is not None and args.encrypt is None: args.report.write(args.mem_report_file.getvalue()) args.report.close()
def tripledh(self): """Triple DH performs cross DH between two peers having two keys each: - an identity key (Ai,Bi), and - an ephemeral key (Ae, Be). the cross DH is then performed on these pairs: (Ai,Be)+(Bi,Ae)+(Ae,Be) The order of the parameters to these operations depends on the order in which the peers are acting. """ if self.isalice: p1 = nacl.crypto_scalarmult_curve25519(self.me.identitykey.sk, self.peer['ephemeralkey']) p2 = nacl.crypto_scalarmult_curve25519(self.ephemeralkey.sk, self.peer['identitykey']) p3 = nacl.crypto_scalarmult_curve25519(self.ephemeralkey.sk, self.peer['ephemeralkey']) sec = p1+p2+p3 clearmem(p1) clearmem(p2) clearmem(p3) res = nacl.crypto_generichash(sec, '', nacl.crypto_secretbox_KEYBYTES) clearmem(sec) return res p1 = nacl.crypto_scalarmult_curve25519(self.ephemeralkey.sk, self.peer['identitykey']) p2 = nacl.crypto_scalarmult_curve25519(self.me.identitykey.sk, self.peer['ephemeralkey']) p3 = nacl.crypto_scalarmult_curve25519(self.ephemeralkey.sk, self.peer['ephemeralkey']) sec = p1+p2+p3 clearmem(p1) clearmem(p2) clearmem(p3) res = nacl.crypto_generichash(sec, '', nacl.crypto_secretbox_KEYBYTES) clearmem(sec) return res
def main(): # main command line handler for pbp parser = argparse.ArgumentParser(description='pbp') group = parser.add_mutually_exclusive_group() group.add_argument('--gen-key', '-g', dest='action', action='store_const', const='g', help="generates a new key") group.add_argument('--encrypt', '-c', dest='action', action='store_const', const='c',help="encrypts") group.add_argument('--decrypt', '-d', dest='action', action='store_const', const='d',help="decrypts") group.add_argument('--sign', '-s', dest='action', action='store_const', const='s',help="signs") group.add_argument('--master-sign', '-m', dest='action', action='store_const', const='m',help="signs keys with your masterkey") group.add_argument('--verify', '-v', dest='action', action='store_const', const='v',help="verifies") group.add_argument('--hash', '-H', dest='action', action='store_const', const='h',help="hashes") group.add_argument('--list', '-l', dest='action', action='store_const', const='l',help="lists public keys") group.add_argument('--list-secret', '-L', dest='action', action='store_const', const='L',help="Lists secret keys") group.add_argument('--export-key', '-x', dest='action', action='store_const', const='x',help="export public key") group.add_argument('--import-key', '-I', dest='action', action='store_const', const='i',help="import public key") group.add_argument('--check-sigs', '-C', dest='action', action='store_const', const='C',help="lists all known sigs on a public key") group.add_argument('--fcrypt', '-e', dest='action', action='store_const', const='e',help="encrypts a message using PFS to a peer") group.add_argument('--fdecrypt', '-E', dest='action', action='store_const', const='E',help="decrypts a message using PFS to a peer") group.add_argument( '-D1', dest='action', action='store_const', const='d1',help="initiates an ECDH key exchange") group.add_argument( '-D2', dest='action', action='store_const', const='d2',help="responds to an ECDH key request") group.add_argument( '-D3', dest='action', action='store_const', const='d3',help="finalizes an ECDH key exchange") group.add_argument('--dh-start', '-Ds', dest='action', action='store_const', const='ds',help="initiates an ECDH key exchange") group.add_argument('--dh-end', '-De', dest='action', action='store_const', const='de',help="finalizes an ECDH key exchange") group.add_argument('--rand-stream', '-R', dest='action', action='store_const', const='R',help="generate arbitrary random stream") if PITCHFORK: parser.add_argument('--pitchfork', '-P', dest='PITCHFORK', action='store_const', const='P',help="arms PITCHFORK", default=False) parser.add_argument('--signature', '-z', help="sets the pitchfork sig to verify") parser.add_argument('--recipient', '-r', action='append', help="designates a recipient for public key encryption") parser.add_argument('--name', '-n', help="sets the name for a new key") parser.add_argument('--max-recipients', help="set the number of recipients to check when decrypting", default=20) parser.add_argument('--sender', help="set the key of the sender") parser.add_argument('--basedir', '-b', '--base-dir', help="set the base directory for all key storage needs", default=defaultbase) parser.add_argument('--self', '-S', help="sets your own key") parser.add_argument('--key', '-k', help="some password or secret") parser.add_argument('--dh-param', '-DP',help="public parameter for ECDH key exchange") parser.add_argument('--dh-exp', '-DE',help="secret exp for final step of a ECDH key exchange") parser.add_argument('--size', '-Rs',help="size of random stream to generate") parser.add_argument('--dh-peers', '-Dp',help="the number of peers participating in a ECDH key exchange") parser.add_argument('--infile', '-i', help="file to operate on") parser.add_argument('--armor', '-a', action='store_true', help="ascii armors the output") parser.add_argument('--outfile', '-o', help="file to output to") opts=parser.parse_args() opts.basedir=os.path.expandvars( os.path.expanduser(opts.basedir)) if os.path.exists(opts.basedir): mode = os.stat(opts.basedir).st_mode & 0o777 if mode not in [0o700, 0o600]: print('[pbp] ABORT: unsafe permissions %s on basedir %s' % (oct(mode), opts.basedir), file=sys.stderr) # Generate key if opts.action=='g': ensure_name_specified(opts) publickey.Identity(opts.name, create=True, basedir=opts.basedir) # list public keys elif opts.action=='l': if PITCHFORK and opts.PITCHFORK: pitchfork.init() res = pitchfork.listkeys(opts.name) if(res): keys, stats = res pitchfork.print_keys(keys) pitchfork.storage_stats(stats, keys) else: print('none') else: for i in publickey.get_public_keys(opts.basedir): print(('valid' if i.valid > datetime.datetime.utcnow() > i.created else 'INVALID'), i.keyid(), i.name) # list secret keys elif opts.action=='L': for i in publickey.get_secret_keys(opts.basedir): print(('valid' if i.valid > datetime.datetime.utcnow() > i.created else 'INVALID'), i.keyid(), i.name) # encrypt elif opts.action=='c': if PITCHFORK and opts.PITCHFORK: ensure_recipient_specified(opts) pitchfork.init() res=pitchfork.encrypt(opts.recipient[0], infile=opts.infile, outfile=opts.outfile) if res: print(b85encode(res), file=sys.stderr) return if opts.recipient or opts.self: ensure_self_specified(opts) ensure_recipient_specified(opts) encrypt_handler(infile=opts.infile, outfile=opts.outfile, recipient=opts.recipient, self=opts.self, basedir=opts.basedir) # decrypt elif opts.action=='d': if PITCHFORK and opts.PITCHFORK: ensure_recipient_specified(opts) pitchfork.init() res=pitchfork.decrypt(opts.recipient[0], infile=opts.infile, outfile=opts.outfile) else: try: sender = decrypt_handler(infile=opts.infile, outfile=opts.outfile, self=opts.self, max_recipients=int(opts.max_recipients), peer=opts.sender, basedir=opts.basedir) except ValueError as e: print(e) sys.exit(1) else: if sender: print('[pbp] good message from', sender, file=sys.stderr) # sign elif opts.action=='s': if PITCHFORK and opts.PITCHFORK: ensure_recipient_specified(opts) pitchfork.init() res=pitchfork.sign(opts.recipient[0], infile=opts.infile, outfile=opts.outfile) if res: print(b85encode(res[0]), b85encode(res[1]), file=sys.stderr) return ensure_self_specified(opts) sign_handler(infile=opts.infile, outfile=opts.outfile, self=opts.self, armor=opts.armor, basedir=opts.basedir) # verify elif opts.action=='v': if PITCHFORK and opts.PITCHFORK: ensure_signature_specified(opts) ensure_recipient_specified(opts) pitchfork.init() res=pitchfork.verify(opts.signature, opts.recipient[0], infile=opts.infile, outfile=opts.outfile) else: res = verify_handler(infile=opts.infile, outfile=opts.outfile, basedir=opts.basedir) if res: print("[pbp] good message from", res, file=sys.stderr) else: print('[pbp] VERIFICATION FAILED', file=sys.stderr) # key sign elif opts.action=='m': ensure_name_specified(opts) ensure_self_specified(opts) sig = keysign_handler(name=opts.name, self=opts.self, basedir=opts.basedir) if sig: print("[pbp] key signed in", sig) else: print('[pbp] SIGNATURE FAILED', file=sys.stderr) # lists signatures owners on public keys elif opts.action=='C': ensure_name_specified(opts) sigs = keycheck_handler(name=opts.name, basedir=opts.basedir) if sigs: print('[pbp] good signatures on', opts.name, 'from', ', '.join(sigs), file=sys.stderr) else: print('[pbp] NO GOOD SIGS FOUND', file=sys.stderr) # export public key elif opts.action=='x': ensure_self_specified(opts) k = export_handler(opts.self, basedir=opts.basedir) print(k) # import public key elif opts.action=='i': n = import_handler(infile=opts.infile, basedir=opts.basedir) if n: print('[pbp] Success: imported public keys for', n) else: print('[pbp] IMPORT FAILED') # forward encrypt elif opts.action=='e': ensure_recipient_specified(opts) ensure_only_one_recipient(opts) # TODO could try to find out this automatically if non-ambiguous ensure_self_specified(opts) chaining_encrypt_handler(opts.infile, outfile=opts.outfile, recipient=opts.recipient[0], self=opts.self, basedir=opts.basedir) # forward decrypt elif opts.action=='E': ensure_recipient_specified(opts) ensure_only_one_recipient(opts) # TODO could try to find out this automatically if non-ambiguous ensure_self_specified(opts) chaining_decrypt_handler(opts.infile, outfile=opts.outfile, recipient=opts.recipient[0], self=opts.self, basedir=opts.basedir) # start ECDH elif opts.action=='d1': if PITCHFORK and opts.PITCHFORK: ensure_recipient_specified(opts) pitchfork.init() params = pitchfork.start_ecdh(opts.recipient[0]) else: params = dh1_handler() if params: print("[pbp] secret exponent", b85encode(params[0])) print("[pbp] public component", b85encode(params[1])) clearmem(params[0]) # receive ECDH elif opts.action=='d2': ensure_dhparam_specified(opts) if PITCHFORK and opts.PITCHFORK: ensure_recipient_specified(opts) pitchfork.init() params = pitchfork.resp_ecdh(opts.dh_param, opts.recipient[0]) else: params = dh2_handler(binascii.unhexlify(opts.dh_param)) if params: print("[pbp] shared secret", b85encode(params[1])) print("[pbp] public component", b85encode(params[0])) clearmem(params[0]) clearmem(params[1]) # finish ECDH elif opts.action=='d3': ensure_dhparam_specified(opts) ensure_dhexp_specified(opts) if PITCHFORK and opts.PITCHFORK: pitchfork.init() sec = pitchfork.end_ecdh(opts.dh_param, opts.dh_exp) else: sec = dh3_handler(binascii.unhexlify(opts.dh_param), binascii.unhexlify(opts.dh_exp)) if sec: print("[pbp] shared secret", b85encode(sec)) clearmem(sec) # start MPECDH elif opts.action=='ds': ensure_self_specified(opts) ensure_dhpeers_specified(opts) ensure_name_specified(opts) sec = mpecdh_start_handler(opts.name, opts.dh_peers, opts.self, opts.infile, opts.outfile, opts.basedir) if sec: print("[pbp] pushed shared secret, hash", b85encode(nacl.crypto_generichash(sec, outlen=6)), file=sys.stderr) clearmem(sec) sec = None # finish MPECDH elif opts.action=='de': ensure_self_specified(opts) ensure_name_specified(opts) sec = mpecdh_end_handler(opts.name, opts.self, opts.infile, opts.outfile, opts.basedir) if sec: print("[pbp] pushed shared secret, hash", b85encode(nacl.crypto_generichash(sec, outlen=6)), file=sys.stderr) clearmem(sec) sec = None elif opts.action=='R': ensure_size_good(opts) if PITCHFORK and opts.PITCHFORK: pitchfork.init() pitchfork.rng(int(opts.size), opts.outfile) else: random_stream_handler(opts.outfile, opts.size) elif opts.action=='h': hsum = hash_handler(opts.infile, k=load_key(opts.key), outlen=int(opts.size or '16')) if hsum: print(' '.join(split_by_n(binascii.hexlify(hsum),4)))
def clear(self): clearmem(self.sk) self.sk = None self.pk = None
ctx.save() clearmem(ctx.key) ctx.key=None ecdh.save_dh_keychain(outfile, keychain) if hasattr(ctx,'secret'): return ctx.secret def mpecdh_end_handler(id, self, infile = None, outfile = None, basedir = None): ctx = ecdh.MPECDH(id, me = self, basedir = basedir) ctx.load() keychain = ctx.mpecdh2(ecdh.load_dh_keychain(infile)) if keychain: ecdh.save_dh_keychain(outfile, keychain) return ctx.secret def test(): mpecdh_start_handler('1st', 3, 'alice', '/dev/null', '/tmp/step1', 'test-pbp') mpecdh_start_handler('1st', 3, 'bob', '/tmp/step1', '/tmp/step2', 'test-pbp') mpecdh_start_handler('1st', 3, 'carol', '/tmp/step2', '/tmp/step3', 'test-pbp') mpecdh_end_handler('1st', 'alice', '/tmp/step3', '/tmp/step4', 'test-pbp') mpecdh_end_handler('1st', 'bob', '/tmp/step4', '/tmp/step5', 'test-pbp') if __name__ == '__main__': #test() #sys.exit() from main import main lockmem() main() clearmem(_prev_passphrase)
ensure_self_specified(opts) chaining_decrypt_handler( opts.infile, outfile=opts.outfile, recipient=opts.recipient[0], self=opts.self, basedir=opts.basedir ) # start ECDH elif opts.action == "d1": if PITCHFORK and opts.PITCHFORK: ensure_recipient_specified(opts) pitchfork.init() params = pitchfork.start_ecdh(opts.recipient[0]) else: params = dh1_handler() if params: print "[pbp] secret exponent", b85encode(params[0]) print "[pbp] public component", b85encode(params[1]) clearmem(params[0]) # receive ECDH elif opts.action == "d2": ensure_dhparam_specified(opts) if PITCHFORK and opts.PITCHFORK: ensure_recipient_specified(opts) pitchfork.init() params = pitchfork.resp_ecdh(opts.dh_param, opts.recipient[0]) else: params = dh2_handler(binascii.unhexlify(opts.dh_param)) if params: print "[pbp] shared secret", b85encode(params[1]) print "[pbp] public component", b85encode(params[0]) clearmem(params[0]) clearmem(params[1]) # finish ECDH
outfile=opts.outfile, recipient=opts.recipient[0], self=opts.self, basedir=opts.basedir) # start ECDH elif opts.action=='d1': if PITCHFORK: ensure_recipient_specified(opts) pitchfork.init() params = pitchfork.start_ecdh(opts.recipient[0]) else: params = dh1_handler() if params: print "secret exponent", b85encode(params[0]) print "public component", b85encode(params[1]) clearmem(params[0]) # receive ECDH elif opts.action=='d2': ensure_dhparam_specified(opts) if PITCHFORK: ensure_recipient_specified(opts) pitchfork.init() params = pitchfork.resp_ecdh(opts.dh_param, opts.recipient[0]) else: params = dh2_handler(binascii.unhexlify(opts.dh_param)) if params: print "shared secret", b85encode(params[1]) print "public component", b85encode(params[0]) clearmem(params[0]) clearmem(params[1]) # finish ECDH
def decrypt_handler(infile=None, outfile=None, self=None, peer=None, max_recipients = 20, basedir=None): # provides a high level function to do decryption of files # infile specifies the filename of the input file, # if '-' or not specified it uses stdin # outfile specifies the filename of the output file, if not specified # it uses the same filename with '.pbp' appended # self specifies the recipient of the message for using pk crypto # basedir provides a root for the keystores needed for pk crypto # if self is specified pk crypto is used, otherwise symmetric # this function also handles buffering. fd = inputfd(infile) outfd = outputfd(outfile) key = None # asym if self: me = publickey.Identity(self, basedir=basedir) if peer: peer = publickey.Identity(peer, basedir=basedir, publicOnly=True) sender = None size = None i=0 while i < (max_recipients if not size else size): i+=1 rnonce = fd.read(nacl.crypto_box_NONCEBYTES) ct = fd.read(nacl.crypto_secretbox_KEYBYTES+2+nacl.crypto_secretbox_MACBYTES) if sender: continue for keys in ([peer] if peer else publickey.get_public_keys(basedir=basedir)): try: tmp = nacl.crypto_box_open(ct, rnonce, keys.cp, me.cs) except ValueError: continue key = tmp[:nacl.crypto_secretbox_KEYBYTES] size = struct.unpack('>H',tmp[nacl.crypto_secretbox_KEYBYTES:])[0] sender = keys.name break me.clear() if not sender: raise ValueError('decryption failed') # sym else: pwd = getpass.getpass('Passphrase for decrypting: ') key = scrypt.hash(pwd, scrypt_salt)[:nacl.crypto_secretbox_KEYBYTES] sender = None clearmem(pwd) pwd=None if key: nonce = fd.read(nacl.crypto_secretbox_NONCEBYTES) buf = fd.read(BLOCK_SIZE + nacl.crypto_secretbox_MACBYTES) while buf: outfd.write(decrypt((nonce, buf), k = key)) nonce = inc_nonce(nonce) buf = fd.read(BLOCK_SIZE + nacl.crypto_secretbox_MACBYTES) clearmem(key) key = None if fd != sys.stdin: fd.close() if outfd != sys.stdout: outfd.close() return sender
def decrypt_handler(infile=None, outfile=None, self=None, peer=None, max_recipients=20, basedir=None): # provides a high level function to do decryption of files # infile specifies the filename of the input file, # if '-' or not specified it uses stdin # outfile specifies the filename of the output file, if not specified # it uses the same filename with '.pbp' appended # self specifies the recipient of the message for using pk crypto # basedir provides a root for the keystores needed for pk crypto # if self is specified pk crypto is used, otherwise symmetric # this function also handles buffering. fd = inputfd(infile) outfd = outputfd(outfile) key = None # asym if self: me = publickey.Identity(self, basedir=basedir) if peer: peer = publickey.Identity(peer, basedir=basedir, publicOnly=True) sender = None size = None i = 0 while i < (max_recipients if not size else size): i += 1 rnonce = fd.read(nacl.crypto_box_NONCEBYTES) ct = fd.read(nacl.crypto_secretbox_KEYBYTES + 2 + nacl.crypto_secretbox_MACBYTES) if sender: continue for keys in ([peer] if peer else publickey.get_public_keys( basedir=basedir)): try: tmp = nacl.crypto_box_open(ct, rnonce, keys.cp, me.cs) except ValueError: continue key = tmp[:nacl.crypto_secretbox_KEYBYTES] size = struct.unpack('>H', tmp[nacl.crypto_secretbox_KEYBYTES:])[0] sender = keys.name break me.clear() if not sender: raise ValueError('decryption failed') # sym else: pwd = getpass.getpass('Passphrase for decrypting: ') key = scrypt.hash(pwd, scrypt_salt)[:nacl.crypto_secretbox_KEYBYTES] sender = None clearmem(pwd) pwd = None if key: nonce = fd.read(nacl.crypto_secretbox_NONCEBYTES) buf = fd.read(BLOCK_SIZE + nacl.crypto_secretbox_MACBYTES) while buf: outfd.write(decrypt((nonce, buf), k=key)) nonce = inc_nonce(nonce) buf = fd.read(BLOCK_SIZE + nacl.crypto_secretbox_MACBYTES) clearmem(key) key = None if fd != sys.stdin: fd.close() if outfd != sys.stdout and type(outfd) == file: outfd.close() return sender