def __init__(self, username: str, wallet_dir: str, k_sk_receiver: PrivateKey): self.username = username self.wallet_dir = wallet_dir self.k_sk_receiver = k_sk_receiver self.k_sk_receiver_bytes = \ k_sk_receiver.encode(encoder=encoding.RawEncoder)
class IDPrivateKey: def __init__(self, password = None, email = None): if email is None: self.salt = defaultsalt else: self.salt = email if password is not None: self.generate_key(password) def generate_key(self, password): self.pri = PrivateKey(scrypt(sha512(password,encoder = RawEncoder), self.salt, N = 1<<17, r = 8, p = 1, buflen = 32), encoder = RawEncoder) pub = PublicID() pub.salt = self.salt pub.key = self.pri.public_key.encode() self.pub = pub def to_private_key(self): return self.pri def to_public_key(self): return self.pub def pri_base58(self): return b58encode(self.pri.encode(encoder = RawEncoder)) def pub_base58(self): return self.pub.base58()
class Crypto: def __init__(self): """ Initialize crypto stuff """ try: with open("SecretKey", "rb") as SecKeyFile: key = SecKeyFile.read() except Exception: logging.info("No secret key yet -> Generate new one") key = None if key: self.secKey = PrivateKey(key, nacl.encoding.HexEncoder) self.pubKey = self.secKey.public_key else: self.secKey = PrivateKey.generate() self.pubKey = self.secKey.public_key self.secKey_string = self.secKey.encode(nacl.encoding.HexEncoder).upper() self.pubKey_string = self.pubKey.encode(nacl.encoding.HexEncoder).upper() try: with open("SecretKey", "wb") as SecKeyFile, open("PublicKey", "wb") as PubKeyFile: SecKeyFile.write(self.secKey_string) PubKeyFile.write(self.pubKey_string) except Exception: logging.exception("Could not save secret key!") sys.exit(1) @staticmethod def compute_checksum(pk, nospam): """ Return the checksum for the public key + NoSpam value """ checksum = [0, 0] # initializing for number, byte in enumerate(nacl.encoding.HexEncoder.decode(pk + nospam)): checksum[number % 2] ^= byte return "".join(hex(byte)[2:].zfill(2) for byte in checksum).upper() @staticmethod def decrypt_request(secKey, pubKey_client, ciphertext, nonce): """ Decrypt the ciphertext and return plaintext """ box = Box(secKey, pubKey_client) plain = box.decrypt(ciphertext, nonce) return plain
class XMMPPSex(): def __init__(self, jid): self.jid = jid self.pk = {} if os.path.isfile(SECKEY_FILE.format(jid=jid)): print("Reading secret key for {} from file.".format(jid)) with open(SECKEY_FILE.format(jid=jid), 'rb') as f: self.sk = PrivateKey(nacl.encoding.URLSafeBase64Encoder.decode(f.read())) else: print("Generating new secret key for {} and writing it to file.".format(jid)) self.sk = PrivateKey.generate() with open(SECKEY_FILE.format(jid=jid), 'wb') as f: f.write(self.sk.encode(encoder=nacl.encoding.URLSafeBase64Encoder)) if not os.path.isfile(PUBKEY_FILE.format(jid=jid)): print("Writing public key for {} to file.".format(jid)) with open(PUBKEY_FILE.format(jid=jid), 'wb') as f: f.write(self.sk.public_key.encode(encoder=nacl.encoding.URLSafeBase64Encoder)) def addPubkey(self, jid): print("Adding public key for {} from file.".format(jid)) with open(PUBKEY_FILE.format(jid=jid), 'rb') as f: self.pk[jid] = PublicKey(nacl.encoding.URLSafeBase64Encoder.decode(f.read())) def encryptTo(self, jid, message): if not jid in self.pk: self.addPubkey(jid) box = Box(self.sk, self.pk[jid]) nonce = nacl.utils.random(Box.NONCE_SIZE) return (nacl.encoding.URLSafeBase64Encoder.encode(nonce).decode('utf-8'), box.encrypt(message.encode('utf-8'), nonce, nacl.encoding.URLSafeBase64Encoder).ciphertext.decode('utf-8')) def decryptFrom(self, jid, message, nonce): if not jid in self.pk: self.addPubkey(jid) box = Box(self.sk, self.pk[jid]) return box.decrypt(message, nacl.encoding.URLSafeBase64Encoder.decode(nonce), nacl.encoding.URLSafeBase64Encoder).decode('utf-8')
#PKs2_full = loads('{"kty":"EC", "crv":"Curve25519", "x":""}', object_pairs_hook=OrderedDict) #PKp2_full = loads('{"kty":"EC", "crv":"Curve25519", "x":""}', object_pairs_hook=OrderedDict) PKp_full['x'] = PKp_b64 PKs_full['x'] = PKs_b64 #PKs2_full['x'] = PKs2_b64 #PKp2_full['x'] = PKp2_b64 ## Load peer and server private keys SK_peer = PrivateKey(bytes.fromhex(SKp)) SK_server = PrivateKey(bytes.fromhex(SKs)) #SK2_peer = PrivateKey(bytes.fromhex(SKp2)) #SK2_server = PrivateKey(bytes.fromhex(SKs2)) ## Derive shared secret Z = scalarmult(SK_peer.encode(), PK_server.encode()) assert (Z == scalarmult(SK_server.encode(), PK_peer.encode())) #Z2 = scalarmult(SK2_peer.encode(), PK2_server.encode()) #assert(Z2 == scalarmult(SK2_server.encode(), PK2_peer.encode())) ## KDF for completion exchange. Uses NIST Concat KDF. KDF_input = b'EAP-NOOB' + base64url_decode(Np_b64) + base64url_decode( Ns_b64) + base64url_decode(Noob_b64) KDF_out = KDF(algorithm=SHA256(), length=320, otherinfo=KDF_input, backend=default_backend()).derive(Z) Kms = KDF_out[224:256] Kmp = KDF_out[256:288] Kz = KDF_out[288:320]
class PrivateKeyProvider(object): """ Decrypts private keys. """ def __init__(self, key, passphrase: bytes): self.key = key # Secure! self.passphrase = passphrase self.sign = None self.encrypt = None def __enter__(self) -> typing.Tuple[PrivateKey, PrivateKey]: """ Provides a pair of private keys. """ # Derive the key from the passphrase. derived = util.derive_passphrase(self.passphrase) sign_box = SecretBox(derived) enc_box = SecretBox(derived) # Decrypt, using the two nonces. s_d = sign_box.decrypt(self.key._private_signing_seed, self.key._private_signing_nonce) e_d = enc_box.decrypt(self.key._private_key_raw, self.key._private_nonce) # Generate a SigningKey out of the seed. self.sign = SigningKey(s_d) self.encrypt = PrivateKey(e_d) # Update the key's public keys. if self.key._public_key is None: self.key._public_key = self.encrypt.public_key if self.key._public_signing_key is None: self.key._public_signing_key = self.sign.verify_key return self.encrypt, self.sign def __exit__(self, exc_type, exc_val, exc_tb): """ Re-encrypt. """ # Derive the key from the passphrase. derived = util.derive_passphrase(self.passphrase) # Generate two random nonces. nonce1 = random(SecretBox.NONCE_SIZE) nonce2 = random(SecretBox.NONCE_SIZE) sign_box = SecretBox(derived) enc_box = SecretBox(derived) s_p = self.sign.encode() e_p = self.encrypt.encode() s_e = sign_box.encrypt(s_p, nonce1) e_e = enc_box.encrypt(e_p, nonce2) # Update `self.key`. self.key._private_key_raw = e_e.ciphertext self.key._private_signing_key_raw = s_e.ciphertext # Bit of a mixed up name. self.key._private_nonce = e_e.nonce self.key._private_signing_nonce = s_e.nonce if exc_type is not None: raise exc_type(exc_val)
def _serialize_private_key(key: public.PrivateKey) -> str: str_key = key.encode(encoder=nacl.encoding.HexEncoder).decode('ascii') return str_key
# PKp2_full = loads('{"kty":"EC", "crv":"Curve25519", "x":""}', \ # object_pairs_hook=OrderedDict) PKp_full['x'] = PKp_b64 PKs_full['x'] = PKs_b64 #PKs2_full['x'] = PKs2_b64 #PKp2_full['x'] = PKp2_b64 ## Load peer and server private keys SK_peer = PrivateKey(bytes.fromhex(SKp)) SK_server = PrivateKey(bytes.fromhex(SKs)) #SK2_peer = PrivateKey(bytes.fromhex(SKp2)) #SK2_server = PrivateKey(bytes.fromhex(SKs2)) ## Derive shared secret Z = scalarmult(SK_peer.encode(), PK_server.encode()) assert(Z == scalarmult(SK_server.encode(), PK_peer.encode())) #Z2 = scalarmult(SK2_peer.encode(), PK2_server.encode()) #assert(Z2 == scalarmult(SK2_server.encode(), PK2_peer.encode())) ## KDF for completion exchange. Uses NIST Concat KDF. KDF_input = b'EAP-NOOB' + base64url_decode(Np_b64) + base64url_decode(Ns_b64) +\ base64url_decode(Noob_b64) KDF_out = KDF(algorithm=SHA256(), length=320, otherinfo=KDF_input, backend=default_backend()).derive(Z) Kms = KDF_out[224:256] Kmp = KDF_out[256:288] Kz = KDF_out[288:320] # KDF - for reconnect exchange. Uses NIST Concat KDF. This sample script does # not exchange new keys in the reconnect exchange and uses the Kz from the
class HTTPMailboxServer(BaseServer): """I am a local HTTP-based server, attached to our webapi port. I don't persist anything myself, but expect my creator to provide me with our persistent state. I can deliver messages to a local transport (endpoints inside our same process), or write messages to disk for later retrieval by remote clients. """ def __init__(self, web, enable_retrieval, desc): BaseServer.__init__(self) self.web = web self.privkey = PrivateKey(desc["transport_privkey"].decode("hex")) self.TID_privkey = desc["TID_private_key"].decode("hex") # If we feed a local transport, it will have just one TID. If we # queue messages for any other transports, they'll each have their # own TID and handler (e.g. a queue and some retrieval credentials). self.local_TID0 = desc["local_TID0"].decode("hex") self.local_TID_tokenid = desc["local_TID_tokenid"].decode("hex") # this is how we get messages from senders web.get_root().putChild("mailbox", ServerResource(self.handle_msgA)) if enable_retrieval: # add a second resource for clients to retrieve messages raise NotImplementedError() def get_retrieval_descriptor(self): return { "type": "local", "transport_privkey": self.privkey.encode().encode("hex"), "TID_private_key": self.TID_privkey.encode("hex"), "TID": self.local_TID0.encode("hex"), "TID_tokenid": self.local_TID_tokenid.encode("hex"), } def get_sender_descriptor(self): baseurl = self.web.get_baseurl() assert baseurl.endswith("/") pubkey = self.privkey.public_key return { "type": "http", # TODO: we must learn our local ipaddr and the webport "url": baseurl + "mailbox", "transport_pubkey": pubkey.encode().encode("hex"), } def register_local_transport_handler(self, handler): self.local_transport_handler = handler def handle_msgA(self, msgA): pubkey1_s, boxed = parseMsgA(msgA) msgB = Box(self.privkey, PublicKey(pubkey1_s)).decrypt(boxed) # this ends the observable errors eventually(self.handle_msgB, msgB) def handle_msgB(self, msgB): MSTID, msgC = parseMsgB(msgB) TID = rrid.decrypt(self.TID_privkey, MSTID) if TID == self.local_TID_tokenid: self.local_transport_handler(msgC) else: # TODO: look up registered transports, queue message self.signal_unrecognized_TID(TID) def signal_unrecognized_TID(self, TID): # this can be overridden by unit tests raise KeyError("unrecognized transport identifier")
def nacl_public_PrivateKey(): key1 = PrivateKey(random(size=PrivateKey.SIZE)) # 32 print(key1.encode()) key2 = PrivateKey.generate() print(key2.encode())
"result"] else [] context = zmq.Context() tag = 1 for s in states: pk = s["service_node_pubkey"] if pk in missed: missed.remove(pk) if "_connect" not in s: ip, port = s["public_ip"], s["quorumnet_port"] if not ip or not port: print("SN {} has no IP/qnet port: {}:{}".format(pk, ip, port)) else: ip, port = None, None socket = context.socket(zmq.DEALER) socket.curve_secretkey = x_key.encode() socket.curve_publickey = x_key.public_key.encode() socket.curve_serverkey = bytes.fromhex(s["pubkey_x25519"]) socket.setsockopt(zmq.CONNECT_TIMEOUT, 5000) socket.setsockopt(zmq.HANDSHAKE_IVL, 5000) socket.setsockopt(zmq.IMMEDIATE, 1) if "_connect" in s: socket.connect(s["_connect"]) print("Ping {} (for SN {})".format(s["_connect"], pk)) else: socket.connect("tcp://{}:{}".format(ip, port)) print("Ping {}:{} (for SN {})".format(ip, port, pk)) bt_tag = bytes("i{}e".format(tag), "utf-8") socket.send_multipart((b"ping.ping", b"d1:!" + bt_tag + b"e")) ponged = False
assert STORAGE_PIR_DIR[-1] == '/' assert HTTP_STATIC_DIR[-1] == '/' assert Box.NONCE_SIZE == NONCE_BYTES commandlineoptions = parse_options() keyfile = 'server.secret' if os.path.exists(keyfile): with open(keyfile, 'rb') as f: data = f.read() sk_bin = data[0:32] server_sk = PrivateKey(sk_bin) else: server_sk = PrivateKey.generate() sk_bin = server_sk.encode(encoder=nacl.encoding.RawEncoder) with open(keyfile, 'wb') as f: f.write(sk_bin) pk_bin = server_sk.public_key.encode(encoder=nacl.encoding.RawEncoder) print "Server public key: " + pk_bin.encode('hex') HOST, PORT = "0.0.0.0", commandlineoptions.port # Update the PIR manifest update_pir_manifest() # Create the server print "[INFO] Starting server at", str(HOST) + ":" + str(PORT) onionpir_server = SocketServer.TCPServer((HOST, PORT), NonPirTCPHandler)
class HTTPMailboxServer(BaseServer): """I am a local HTTP-based server, attached to our webapi port. I don't persist anything myself, but expect my creator to provide me with our persistent state. I can deliver messages to a local transport (endpoints inside our same process), or write messages to disk for later retrieval by remote clients. """ def __init__(self, web, enable_retrieval, desc): BaseServer.__init__(self) self.web = web self.privkey = PrivateKey(desc["transport_privkey"].decode("hex")) self.TID_privkey = desc["TID_private_key"].decode("hex") # If we feed a local transport, it will have just one TID. If we # queue messages for any other transports, they'll each have their # own TID and handler (e.g. a queue and some retrieval credentials). self.local_TID0 = desc["local_TID0"].decode("hex") self.local_TID_tokenid = desc["local_TID_tokenid"].decode("hex") # this is how we get messages from senders web.get_root().putChild("mailbox", ServerResource(self.handle_msgA)) if enable_retrieval: # add a second resource for clients to retrieve messages raise NotImplementedError() def get_retrieval_descriptor(self): return { "type": "local", "transport_privkey": self.privkey.encode().encode("hex"), "TID_private_key": self.TID_privkey.encode("hex"), "TID": self.local_TID0.encode("hex"), "TID_tokenid": self.local_TID_tokenid.encode("hex"), } def get_sender_descriptor(self): baseurl = self.web.get_baseurl() assert baseurl.endswith("/") pubkey = self.privkey.public_key return { "type": "http", # TODO: we must learn our local ipaddr and the webport "url": baseurl + "mailbox", "transport_pubkey": pubkey.encode().encode("hex"), } def register_local_transport_handler(self, handler): self.local_transport_handler = handler def handle_msgA(self, msgA): pubkey1_s, boxed = parseMsgA(msgA) msgB = Box(self.privkey, PublicKey(pubkey1_s)).decrypt(boxed) # this ends the observable errors eventually(self.handle_msgB, msgB) def handle_msgB(self, msgB): MSTID, msgC = parseMsgB(msgB) TID = rrid.decrypt(self.TID_privkey, MSTID) if TID == self.local_TID_tokenid: self.local_transport_handler(msgC) else: # TODO: look up registered transports, queue message self.signal_unrecognized_TID(TID) def signal_unrecognized_TID(self, TID): # this can be overridden by unit tests raise KeyError("unrecognized transport identifier")
class Keyring: keyring = '' # versioned keyring name (ex. 'cats/0') # for asymmetric encryption enc_key = None enc_key_id = None # for signing signing_key = None signing_key_id = None # for symmetric encryption _master_key = None _secret_box = None def __init__(self, user, keyring, filename=None): """ we want to keep all the cryptographic functions as side-effect free as possible, so when we instantiate a keyring we'll do as much of the setup and I/O as possible. """ self.keyring = keyring if not filename: filename = keyring.lstrip('/key').replace('/', '.') filename = os.path.join(DIR, '{}.{}'.format(user, filename)) try: self._load_from_private_keyfile(filename) except FileNotFoundError: self.enc_key = PrivateKey.generate() self.signing_key = nacl.signing.SigningKey.generate() self.enc_key_id = uuid.uuid4().hex self.signing_key_id = uuid.uuid4().hex self._to_private_keyfile(filename) def generate_master_key(self): """ create a new master key. the user will want to write this master key to the PDI by vouching for themselves and then vouch for any other users who want to use the same key. """ self._master_key = nacl.utils.random(nacl.secret.SecretBox.KEY_SIZE) self._secret_box = nacl.secret.SecretBox(self._master_key) def vouch(self, public_key): """ given the public key of another user, asymmetrically encrypt the master key. this encryption inherently includes authentication so we don't need to also sign this block. """ assert self._master_key is not None pkey = PublicKey(public_key, encoder=nacl.encoding.HexEncoder) box = Box(self.enc_key, pkey) encrypted = box.encrypt(self._master_key) data = {'version': VERSION, 'key': encrypted.hex()} return json.dumps(data).encode('ascii') def load_master_key(self, block, public_key): """ given a master key block from the PDI encrypted for this user, and the public key of the user that vouched for them, load the master key into memory and set up the symmetric encryption engine. Note that we can't do this in the constructor because only the first keyring user is going to want to generate a new master key. (Note: this is the reverse operation to 'vouch') """ pkey = PublicKey(public_key, encoder=nacl.encoding.HexEncoder) block = json.loads(block.decode('ascii'))['key'] msg = nacl.utils.EncryptedMessage.fromhex(block) box = Box(self.enc_key, pkey) plaintext = box.decrypt(msg) self._master_key = plaintext self._secret_box = nacl.secret.SecretBox(self._master_key) def encrypt(self, plaintext): """ symmetric encryption of the message body using the master key. asserts that we've loaded the master key onto the keyring """ assert self._master_key is not None assert self._secret_box is not None ciphertext = self._secret_box.encrypt(plaintext) return ciphertext def decrypt(self, ciphertext): """ symmetric decryption of the message body using the master key. asserts that we've loaded the master key onto the keyring """ assert self._master_key is not None assert self._secret_box is not None plaintext = self._secret_box.decrypt(ciphertext) return plaintext def sign(self, data): """ given a blob of data, add a message signature """ signed = self.signing_key.sign(data) return signed def verify(self, block, verify_key_hex): """ given a data block (which should be encrypted) and the encoded signing verify key of a user, verify the message signature. will raise nacl.exceptions.BadSignatureError if the check fails """ verify_key = nacl.signing.VerifyKey(verify_key_hex, encoder=nacl.encoding.HexEncoder) message = verify_key.verify(block) return message @property def public_key(self): return self.enc_key.public_key.encode(JSONSafeHexEncoder()) def to_public_keyblock(self): """ output the JSON representation of the asymmetric public key for writing to the PDI key keyring """ data = { 'version': VERSION, 'key': { 'crv': CURVE, 'kty': KEY_TYPE, 'kid': self.enc_key_id, 'use': 'enc', 'x': self.enc_key.public_key.encode(JSONSafeHexEncoder()) } } return json.dumps(data).encode('ascii') @property def verify_key(self): return self.signing_key.verify_key.encode(JSONSafeHexEncoder()) def to_verify_keyblock(self): """ output the JSON representation of the signing verify key for writing to the PDI key keyring """ data = { 'version': VERSION, 'key': { 'crv': CURVE, 'kty': KEY_TYPE, 'kid': self.signing_key_id, 'use': 'signing', 'x': self.signing_key.verify_key.encode(JSONSafeHexEncoder()) } } return json.dumps(data).encode('ascii') def _load_from_private_keyfile(self, filename): """ loads the user's private keys from a local file into memory. in a real SKI this will use the secure persistent implementation of the SKI's hardware backing """ try: with open(filename, 'r') as keyfile: data = json.loads(keyfile.read()) for key in data['keys']: if key['use'] == 'signing': hex_key = key['x'] self.signing_key_id = key['kid'] self.signing_key = nacl.signing.SigningKey( hex_key, nacl.encoding.HexEncoder()) elif key['use'] == 'enc': hex_key = key['x'] self.enc_key_id = key['kid'] self.enc_key = PrivateKey(hex_key, nacl.encoding.HexEncoder()) except PermissionError: # a keyfile exists but we're not allowed to get it. print('tried to access keyfile {} but got PermissionError'.format( filename)) sys.exit(77) except (KeyError, json.decoder.JSONDecodeError): print('private keyfile {} was in invalid format'.format(filename)) sys.exit(1) except FileNotFoundError: # let this bubble up so that we can create a new keyfile raise def _to_private_keyfile(self, filename): """ write the user's private keys to local file. in a real SKI this will have a secure persistence implementation taking advantage of the SKI's hardware backing """ data = { 'version': VERSION, 'keys': [{ 'crv': CURVE, 'kty': KEY_TYPE, 'kid': self.signing_key_id, 'use': 'signing', 'x': self.signing_key.encode(JSONSafeHexEncoder()) }, { 'crv': CURVE, 'kty': KEY_TYPE, 'kid': self.enc_key_id, 'use': 'enc', 'x': self.enc_key.encode(JSONSafeHexEncoder()) }] } try: with open(filename, 'w') as keyfile: keyfile.write(json.dumps(data)) except Exception: raise # just crash at this point so we can debug this