def encrypt_sym(data, key): """ Encrypt data using AES-256 cipher in CTR mode. :param data: The data to be encrypted. :type data: str :param key: The key used to encrypt data (must be 256 bits long). :type key: str :return: A tuple with the initialization vector and the encrypted data. :rtype: (long, str) """ soledad_assert_type(key, str) soledad_assert( len(key) == 32, # 32 x 8 = 256 bits. 'Wrong key size: %s bits (must be 256 bits long).' % (len(key) * 8)) iv = os.urandom(16) backend = MultiBackend([OpenSSLBackend()]) cipher = Cipher(algorithms.AES(key), modes.CTR(iv), backend=backend) encryptor = cipher.encryptor() ciphertext = encryptor.update(data) + encryptor.finalize() return binascii.b2a_base64(iv), ciphertext
def decrypt_sym(data, key, iv): """ Decrypt some data previously encrypted using AES-256 cipher in CTR mode. :param data: The data to be decrypted. :type data: str :param key: The symmetric key used to decrypt data (must be 256 bits long). :type key: str :param iv: The initialization vector. :type iv: long :return: The decrypted data. :rtype: str """ soledad_assert_type(key, str) # assert params soledad_assert( len(key) == 32, # 32 x 8 = 256 bits. 'Wrong key size: %s (must be 256 bits long).' % len(key)) backend = MultiBackend([OpenSSLBackend()]) iv = binascii.a2b_base64(iv) cipher = Cipher(algorithms.AES(key), modes.CTR(iv), backend=backend) decryptor = cipher.decryptor() return decryptor.update(data) + decryptor.finalize()
def decrypt_sym(data, key, method, **kwargs): """ Decrypt data using symmetric secret. Currently, the only encryption method supported is AES-256 CTR mode. :param data: The data to be decrypted. :type data: str :param key: The key used to decrypt C{data} (must be 256 bits long). :type key: str :param method: The encryption method to use. :type method: str :param kwargs: Other parameters specific to each encryption method. :type kwargs: dict :return: The decrypted data. :rtype: str :raise UnknownEncryptionMethodError: Raised when C{method} is unknown. """ soledad_assert_type(key, str) # assert params soledad_assert( len(key) == 32, # 32 x 8 = 256 bits. 'Wrong key size: %s (must be 256 bits long).' % len(key)) soledad_assert('iv' in kwargs, '%s needs an initial value.' % method) _assert_known_encryption_method(method) # AES-256 in CTR mode if method == crypto.EncryptionMethods.AES_256_CTR: return AES(key=key, iv=binascii.a2b_base64(kwargs['iv'])).process(data) elif method == crypto.EncryptionMethods.XSALSA20: return XSalsa20(key=key, iv=binascii.a2b_base64(kwargs['iv'])).process(data)
def encrypt_sym(data, key, method): """ Encrypt C{data} using a {password}. Currently, the only encryption methods supported are AES-256 in CTR mode and XSalsa20. :param data: The data to be encrypted. :type data: str :param key: The key used to encrypt C{data} (must be 256 bits long). :type key: str :param method: The encryption method to use. :type method: str :return: A tuple with the initial value and the encrypted data. :rtype: (long, str) """ soledad_assert_type(key, str) soledad_assert( len(key) == 32, # 32 x 8 = 256 bits. 'Wrong key size: %s bits (must be 256 bits long).' % (len(key) * 8)) iv = None # AES-256 in CTR mode if method == EncryptionMethods.AES_256_CTR: iv = os.urandom(16) ciphertext = AES(key=key, iv=iv).process(data) # XSalsa20 elif method == EncryptionMethods.XSALSA20: iv = os.urandom(24) ciphertext = XSalsa20(key=key, iv=iv).process(data) else: # raise if method is unknown raise UnknownEncryptionMethod('Unkwnown method: %s' % method) return binascii.b2a_base64(iv), ciphertext
def decrypt_sym(data, key, method, **kwargs): """ Decrypt data using symmetric secret. Currently, the only encryption method supported is AES-256 CTR mode. :param data: The data to be decrypted. :type data: str :param key: The key used to decrypt C{data} (must be 256 bits long). :type key: str :param method: The encryption method to use. :type method: str :param kwargs: Other parameters specific to each encryption method. :type kwargs: dict :return: The decrypted data. :rtype: str :raise UnknownEncryptionMethodError: Raised when C{method} is unknown. """ soledad_assert_type(key, str) # assert params soledad_assert(len(key) == 32, "Wrong key size: %s (must be 256 bits long)." % len(key)) # 32 x 8 = 256 bits. soledad_assert("iv" in kwargs, "%s needs an initial value." % method) _assert_known_encryption_method(method) # AES-256 in CTR mode if method == crypto.EncryptionMethods.AES_256_CTR: return AES(key=key, iv=binascii.a2b_base64(kwargs["iv"])).process(data) elif method == crypto.EncryptionMethods.XSALSA20: return XSalsa20(key=key, iv=binascii.a2b_base64(kwargs["iv"])).process(data)
def _init_config_with_defaults(self): """ Initialize configuration using default values for missing params. """ soledad_assert_type(self._passphrase, unicode) initialize = lambda attr, val: getattr(self, attr, None) is None and setattr(self, attr, val) initialize("_secrets_path", os.path.join(self.default_prefix, self.secrets_file_name)) initialize("_local_db_path", os.path.join(self.default_prefix, self.local_db_file_name)) # initialize server_url soledad_assert(self._server_url is not None, "Missing URL for Soledad server.")
def _init_config_with_defaults(self): """ Initialize configuration using default values for missing params. """ soledad_assert_type(self.passphrase, unicode) def initialize(attr, val): return ((getattr(self, attr, None) is None) and setattr(self, attr, val)) initialize("_secrets_path", os.path.join(self.default_prefix, self.secrets_file_name)) initialize("_local_db_path", os.path.join(self.default_prefix, self.local_db_file_name))
def _init_config_with_defaults(self): """ Initialize configuration using default values for missing params. """ soledad_assert_type(self._passphrase, unicode) initialize = lambda attr, val: getattr( self, attr, None) is None and setattr(self, attr, val) initialize("_secrets_path", os.path.join(self.default_prefix, self.secrets_file_name)) initialize("_local_db_path", os.path.join(self.default_prefix, self.local_db_file_name)) # initialize server_url soledad_assert(self._server_url is not None, 'Missing URL for Soledad server.')
def change_passphrase(self, new_passphrase): """ Change the passphrase that encrypts the storage secret. :param new_passphrase: The new passphrase. :type new_passphrase: unicode :raise NoStorageSecret: Raised if there's no storage secret available. """ # maybe we want to add more checks to guarantee passphrase is # reasonable? soledad_assert_type(new_passphrase, unicode) if len(new_passphrase) < self.MINIMUM_PASSPHRASE_LENGTH: raise PassphraseTooShort( 'Passphrase must be at least %d characters long!' % self.MINIMUM_PASSPHRASE_LENGTH) # ensure there's a secret for which the passphrase will be changed. if not self._has_secret(): raise NoStorageSecret() secret = self._get_storage_secret() # generate random salt new_salt = os.urandom(self.SALT_LENGTH) # get a 256-bit key key = scrypt.hash(new_passphrase.encode('utf-8'), new_salt, buflen=32) iv, ciphertext = self._crypto.encrypt_sym(secret, key) # XXX update all secrets in the dict self._secrets[self._secret_id] = { # leap.soledad.crypto submodule uses AES256 for symmetric # encryption. self.KDF_KEY: self.KDF_SCRYPT, # TODO: remove hard coded kdf self.KDF_SALT_KEY: binascii.b2a_base64(new_salt), self.KDF_LENGTH_KEY: len(key), self.CIPHER_KEY: self.CIPHER_AES256, self.LENGTH_KEY: len(secret), self.SECRET_KEY: '%s%s%s' % (str(iv), self.IV_SEPARATOR, binascii.b2a_base64(ciphertext)), } self._passphrase = new_passphrase self._store_secrets() self._put_secrets_in_shared_db()
def encrypt_sym(data, key): """ Encrypt data using AES-256 cipher in CTR mode. :param data: The data to be encrypted. :type data: str :param key: The key used to encrypt data (must be 256 bits long). :type key: str :return: A tuple with the initialization vector and the encrypted data. :rtype: (long, str) """ soledad_assert_type(key, str) soledad_assert( len(key) == 32, # 32 x 8 = 256 bits. 'Wrong key size: %s bits (must be 256 bits long).' % (len(key) * 8)) iv = os.urandom(16) ciphertext = AES(key=key, iv=iv).process(data) return binascii.b2a_base64(iv), ciphertext
def decrypt_sym(data, key, iv): """ Decrypt some data previously encrypted using AES-256 cipher in CTR mode. :param data: The data to be decrypted. :type data: str :param key: The symmetric key used to decrypt data (must be 256 bits long). :type key: str :param iv: The initialization vector. :type iv: long :return: The decrypted data. :rtype: str """ soledad_assert_type(key, str) # assert params soledad_assert( len(key) == 32, # 32 x 8 = 256 bits. 'Wrong key size: %s (must be 256 bits long).' % len(key)) return AES(key=key, iv=binascii.a2b_base64(iv)).process(data)
def change_passphrase(self, new_passphrase): """ Change the passphrase that encrypts the storage secret. :param new_passphrase: The new passphrase. :type new_passphrase: unicode :raise NoStorageSecret: Raised if there's no storage secret available. """ # maybe we want to add more checks to guarantee passphrase is # reasonable? soledad_assert_type(new_passphrase, unicode) if len(new_passphrase) < self.MINIMUM_PASSPHRASE_LENGTH: raise PassphraseTooShort( 'Passphrase must be at least %d characters long!' % self.MINIMUM_PASSPHRASE_LENGTH) # ensure there's a secret for which the passphrase will be changed. if not self._has_secret(): raise NoStorageSecret() secret = self._get_storage_secret() # generate random salt new_salt = os.urandom(self.SALT_LENGTH) # get a 256-bit key key = scrypt.hash(new_passphrase.encode('utf-8'), new_salt, buflen=32) iv, ciphertext = self._crypto.encrypt_sym(secret, key) # XXX update all secrets in the dict self._secrets[self._secret_id] = { # leap.soledad.crypto submodule uses AES256 for symmetric # encryption. self.KDF_KEY: self.KDF_SCRYPT, # TODO: remove hard coded kdf self.KDF_SALT_KEY: binascii.b2a_base64(new_salt), self.KDF_LENGTH_KEY: len(key), self.CIPHER_KEY: self.CIPHER_AES256, self.LENGTH_KEY: len(secret), self.SECRET_KEY: '%s%s%s' % ( str(iv), self.IV_SEPARATOR, binascii.b2a_base64(ciphertext)), } self._passphrase = new_passphrase self._store_secrets() self._put_secrets_in_shared_db()
def decrypt_sym(data, key, iv): """ Decrypt some data previously encrypted using AES-256 cipher in CTR mode. :param data: The data to be decrypted. :type data: str :param key: The symmetric key used to decrypt data (must be 256 bits long). :type key: str :param iv: The initialization vector. :type iv: long :return: The decrypted data. :rtype: str """ soledad_assert_type(key, str) # assert params soledad_assert( len(key) == 32, # 32 x 8 = 256 bits. 'Wrong key size: %s (must be 256 bits long).' % len(key)) return AES( key=key, iv=binascii.a2b_base64(iv)).process(data)
def change_passphrase(self, new_passphrase): """ Change the passphrase that encrypts the storage secret. :param new_passphrase: The new passphrase. :type new_passphrase: unicode :raise NoStorageSecret: Raised if there's no storage secret available. """ # TODO: maybe we want to add more checks to guarantee passphrase is # reasonable? soledad_assert_type(new_passphrase, unicode) if len(new_passphrase) < self.MINIMUM_PASSPHRASE_LENGTH: raise PassphraseTooShort( 'Passphrase must be at least %d characters long!' % self.MINIMUM_PASSPHRASE_LENGTH) # ensure there's a secret for which the passphrase will be changed. if not self._has_secret(): raise NoStorageSecret() self._passphrase = new_passphrase self._store_secrets() self._put_secrets_in_shared_db()
def encrypt_sym(data, key): """ Encrypt data using AES-256 cipher in CTR mode. :param data: The data to be encrypted. :type data: str :param key: The key used to encrypt data (must be 256 bits long). :type key: str :return: A tuple with the initialization vector and the encrypted data. :rtype: (long, str) """ soledad_assert_type(key, str) soledad_assert( len(key) == 32, # 32 x 8 = 256 bits. 'Wrong key size: %s bits (must be 256 bits long).' % (len(key) * 8)) iv = os.urandom(16) cipher = Cipher(algorithms.AES(key), modes.CTR(iv), backend=crypto_backend) encryptor = cipher.encryptor() ciphertext = encryptor.update(data) + encryptor.finalize() return binascii.b2a_base64(iv), ciphertext
def decrypt_sym(self, data, key, method=EncryptionMethods.AES_256_CTR, **kwargs): """ Decrypt data using symmetric secret. Currently, the only encryption method supported is AES-256 CTR mode. :param data: The data to be decrypted. :type data: str :param key: The key used to decrypt C{data} (must be 256 bits long). :type key: str :param method: The encryption method to use. :type method: str :param kwargs: Other parameters specific to each encryption method. :type kwargs: dict :return: The decrypted data. :rtype: str """ soledad_assert_type(key, str) # assert params soledad_assert( len(key) == 32, # 32 x 8 = 256 bits. 'Wrong key size: %s (must be 256 bits long).' % len(key)) soledad_assert( 'iv' in kwargs, '%s needs an initial value.' % method) # AES-256 in CTR mode if method == EncryptionMethods.AES_256_CTR: return AES( key=key, iv=binascii.a2b_base64(kwargs['iv'])).process(data) elif method == EncryptionMethods.XSALSA20: return XSalsa20( key=key, iv=binascii.a2b_base64(kwargs['iv'])).process(data) # raise if method is unknown raise UnknownEncryptionMethod('Unkwnown method: %s' % method)
def __init__(self, uuid, passphrase, secrets_path, local_db_path, server_url, cert_file, auth_token=None, secret_id=None, defer_encryption=False): """ Initialize configuration, cryptographic keys and dbs. :param uuid: User's uuid. :type uuid: str :param passphrase: The passphrase for locking and unlocking encryption secrets for local and remote storage. :type passphrase: unicode :param secrets_path: Path for storing encrypted key used for symmetric encryption. :type secrets_path: str :param local_db_path: Path for local encrypted storage db. :type local_db_path: str :param server_url: URL for Soledad server. This is used either to sync with the user's remote db and to interact with the shared recovery database. :type server_url: str :param cert_file: Path to the certificate of the ca used to validate the SSL certificate used by the remote soledad server. :type cert_file: str :param auth_token: Authorization token for accessing remote databases. :type auth_token: str :param secret_id: The id of the storage secret to be used. :type secret_id: str :param defer_encryption: Whether to defer encryption/decryption of documents, or do it inline while syncing. :type defer_encryption: bool :raise BootstrapSequenceError: Raised when the secret generation and storage on server sequence has failed for some reason. """ # get config params self._uuid = uuid soledad_assert_type(passphrase, unicode) self._passphrase = passphrase # init crypto variables self._secrets = {} self._secret_id = secret_id self._defer_encryption = defer_encryption self._init_config(secrets_path, local_db_path, server_url) self._set_token(auth_token) self._shared_db_instance = None # configure SSL certificate global SOLEDAD_CERT SOLEDAD_CERT = cert_file # initiate bootstrap sequence self._bootstrap() # might raise BootstrapSequenceError()