def test_keys(): privkey = PrivateKey() pubkey = privkey.public_key() message = "Hello World" sig = privkey.sign(message.encode("utf-8")) pubkey.verify(sig, message.encode("utf-8")) c = pubkey.encrypt(message.encode("utf-8")) m = privkey.decrypt(c).decode("utf-8") assert (m == message) privkey2 = PrivateKey() sig2 = privkey2.sign(message.encode("utf-8")) with pytest.raises(SignatureVerificationError): pubkey.verify(sig2, message.encode("utf-8")) bytes = privkey.bytes("testPass32") PrivateKey.read_bytes(bytes, "testPass32") privkey.write("test.pem", "testPass32") PrivateKey.read("test.pem", "testPass32") bytes = pubkey.bytes() pubkey2 = PublicKey.read_bytes(bytes) assert (bytes == pubkey2.bytes()) long_message = str([random.getrandbits(8) for _ in range(4096)]).encode("utf-8") c = pubkey.encrypt(long_message) m = privkey.decrypt(c) assert (m == long_message) os.unlink("test.pem") data = pubkey.to_data() pubkey2 = PublicKey.from_data(data) assert (pubkey.bytes() == pubkey2.bytes()) data = privkey.to_data("testPass33") privkey2 = PrivateKey.from_data(data, "testPass33") assert (privkey == privkey2)
def validate_password(self, password, otpcode, remember_device=False, device_secret=None): """Validate that the passed password and one-time-code are valid. If they are, then do nothing. Otherwise raise an exception. If 'remember_device' is true, then this returns the provisioning uri needed to initialise the OTP code for this account """ if not self.is_active(): raise UserValidationError( "Cannot validate against an inactive account") # see if we can decrypt the private key using the password privkey = _PrivateKey.read_bytes(self._privkey, password) if device_secret: # decrypt the passed device secret and check the supplied # otpcode for that... otp = _OTP.decrypt(_string_to_bytes(device_secret), privkey) otp.verify(otpcode) else: # now decrypt the secret otp and validate the supplied otpcode otp = _OTP.decrypt(self._otp_secret, privkey) otp.verify(otpcode) if remember_device: # create a new OTP that is unique for this device and return # this together with the provisioning code otp = _OTP() otpsecret = _bytes_to_string(otp.encrypt(privkey.public_key())) return (otpsecret, otp.provisioning_uri(self.username()))
def _get_wallet_key(self): """Return the private key used to encrypt everything in the wallet. This will ask for the users password """ if self._wallet_key: return self._wallet_key wallet_dir = ServiceWallet._wallet_dir() keyfile = "%s/wallet_key.pem" % wallet_dir if not _os.path.exists(keyfile): self._wallet_key = self._create_wallet_key(keyfile) return self._wallet_key # read the keyfile and decrypt with open(keyfile, "rb") as FILE: bytes = FILE.read() # get the user password wallet_key = None while not wallet_key: password = _getpass.getpass( prompt="Please enter your wallet password: "******"Invalid password. Please try again.") self._wallet_key = wallet_key return wallet_key
def reset_admin_password(self, password, otpcode, new_password): """Change the admin password for this service. Note that you must pass in a valid password and otpcode to make the change""" self.verify_admin_user(password, otpcode) if password == new_password: return key = _PrivateKey.read_bytes(self._admin_password, password) otp = _OTP.decrypt(self._otpsecret, key) otp.verify(otpcode) newkey = _PrivateKey() self._admin_password = newkey.bytes(new_password) self._otpsecret = otp.encrypt(newkey.public_key())
def verify_admin_user(self, password, otpcode, remember_device=False): """Verify that we are the admin user verifying that the passed password and otpcode are correct. This does nothing if they are correct, but raises an exception if they are wrong. If 'remember_device' this this returns the provisioning_uri for the OTP generator""" try: key = _PrivateKey.read_bytes(self._admin_password, password) except Exception as e: raise ServiceError("Could not log into admin account: %s" % str(e)) try: otp = _OTP.decrypt(self._otpsecret, key) otp.verify(otpcode) if remember_device: return otp.provisioning_uri("admin", self.service_url()) except Exception as e: raise ServiceError("Could not log into admin account: %s" % str(e))
def run(args): """This function will allow a user to register an account with a username and password""" status = 0 message = None provisioning_uri = None username = args["username"] password = args["password"] # generate a sanitised version of the username user_account = UserAccount(username) # generate the encryption keys and otp secret privkey = PrivateKey() pubkey = privkey.public_key() otp = OTP() provisioning_uri = otp.provisioning_uri(username) # save the encrypted private key (encrypted using the user's password) # and encrypted OTP secret (encrypted using the public key) user_account.set_keys(privkey.bytes(password), pubkey.bytes(), otp.encrypt(pubkey)) # remove the key and password from memory privkey = None password = None # now log into the central identity account to either register # the user, or to update to a new password bucket = login_to_service_account() account_key = "accounts/%s" % user_account.sanitised_name() try: existing_data = ObjectStore.get_object_from_json(bucket, account_key) except: existing_data = None message = "Created a new account for '%s'" % username status = 0 if existing_data is None: # save the new account details ObjectStore.set_object_from_json(bucket, account_key, user_account.to_data()) # need to update the "whois" database with the uuid of this user ObjectStore.set_string_object(bucket, "whois/%s" % user_account.uuid(), user_account.username()) else: # The account already exists. See if this is a password change # request old_password = None try: old_password = args["old_password"] except: raise ExistingAccountError( "An account by this name already exists!") if old_password != password: # this is a change of password request - validate that # the existing password unlocks the existing key user_account = UserAccount.from_data(existing_data) testkey = PrivateKey.read_bytes(user_account.private_key(), old_password) # decrypt the old secret old_secret = testkey.decrypt(user_account.otp_secret()) # now encrypt the secret with the new key new_key = PublicKey.read_bytes(pubkey) new_secret = new_key.encrypt(old_secret) user_account.set_keys(privkey, pubkey, new_secret) # save the new account details ObjectStore.set_object_from_json(bucket, account_key, user_account.to_data()) message = "Updated the password for '%s'" % username else: message = "No need to change account '%s'" % username return_value = create_return_value(status, message) if provisioning_uri: return_value["provisioning_uri"] = provisioning_uri return return_value