<s> import functools <EOL> import os <EOL> import json <EOL> from base64 import b64decode <EOL> from collections import namedtuple <EOL> from Crypto . Cipher import AES <EOL> from Crypto . Hash import MD5 <EOL> from pbkdf2_ctypes import pbkdf2_bin <EOL> from onepyssword . py3k import PY3 , b <EOL> __all__ = ( <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' , <EOL> ) <EOL> KEYCHAIN_ENV_KEY = '<STR_LIT>' <EOL> DEFAULT_KEYCHAIN_PATH = '<STR_LIT>' <EOL> KeychainItem = namedtuple ( <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' <EOL> ) <EOL> EncryptionKey = namedtuple ( <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' <EOL> ) <EOL> SaltyString = namedtuple ( <EOL> '<STR_LIT>' , <EOL> '<STR_LIT>' <EOL> ) <EOL> def get_keychain_items ( ) : <EOL> path = os . path . join ( get_keychain_path ( ) , '<STR_LIT:data>' , '<STR_LIT:default>' , '<STR_LIT>' ) <EOL> with open ( path , '<STR_LIT:r>' ) as f : <EOL> item_list = json . load ( f ) <EOL> return dict ( <EOL> ( item [ <NUM_LIT:2> ] , KeychainItem ( type = item [ <NUM_LIT:1> ] , identifier = item [ <NUM_LIT:0> ] ) ) <EOL> for item in item_list <EOL> ) <EOL> def load_keychain_item_data ( identifier ) : <EOL> filename = '<STR_LIT>' . format ( identifier ) <EOL> path = os . path . join ( get_keychain_path ( ) , '<STR_LIT:data>' , '<STR_LIT:default>' , filename ) <EOL> with open ( path , '<STR_LIT:r>' ) as f : <EOL> data = json . load ( f ) <EOL> return data <EOL> def decrypt_keychain_item_password ( b64_data , decrypted_key , item_type ) : <EOL> decrypted_json = decrypt_encryption_key ( b64_data , decrypted_key ) <EOL> decrypted_json = decrypted_json . decode ( '<STR_LIT:utf-8>' ) if PY3 else decrypted_json <EOL> try : <EOL> data = json . loads ( decrypted_json ) <EOL> except ValueError : <EOL> data = json . loads ( decrypted_json [ : - <NUM_LIT:16> ] ) <EOL> if item_type == '<STR_LIT>' : <EOL> for field in data [ '<STR_LIT>' ] : <EOL> if field . get ( '<STR_LIT>' ) == '<STR_LIT:password>' or field . get ( '<STR_LIT:name>' ) == '<STR_LIT>' : <EOL> return field [ '<STR_LIT:value>' ] <EOL> elif item_type in ( '<STR_LIT>' , <EOL> '<STR_LIT>' , ) : <EOL> return data [ '<STR_LIT:password>' ] <EOL> else : <EOL> return None <EOL> def get_encryption_keys ( ) : <EOL> path = os . path . join ( get_keychain_path ( ) , '<STR_LIT:data>' , '<STR_LIT:default>' , <EOL> '<STR_LIT>' ) <EOL> with open ( path , '<STR_LIT:r>' ) as f : <EOL> keys_data = json . load ( f ) <EOL> return dict ( <EOL> ( key_data [ '<STR_LIT>' ] , init_encryption_key ( key_data ) ) <EOL> for key_data in keys_data [ '<STR_LIT:list>' ] <EOL> ) <EOL> def init_encryption_key ( key_data ) : <EOL> minimum_iterations = <NUM_LIT:1000> <EOL> return EncryptionKey ( <EOL> level = key_data [ '<STR_LIT>' ] , <EOL> validation = key_data [ '<STR_LIT>' ] , <EOL> iterations = max ( int ( key_data [ '<STR_LIT>' ] ) , minimum_iterations ) , <EOL> encrypted_key = get_santy_string ( key_data [ '<STR_LIT:data>' ] ) <EOL> ) <EOL> def get_encryption_key ( key_id , key_level ) : <EOL> encryption_keys = get_encryption_keys ( ) <EOL> if key_id : <EOL> return encryption_keys [ key_id ] <EOL> elif key_level : <EOL> for key in encryption_keys . values ( ) : <EOL> if key . level == key_level : <EOL> return key <EOL> return None <EOL> def get_santy_string ( base64_encoded_string ) : <EOL> salted_prefix = b ( '<STR_LIT>' ) <EOL> zero_init_vector = b ( '<STR_LIT:\x00>' ) * <NUM_LIT:16> <EOL> decoded_data = b64decode ( base64_encoded_string ) <EOL> if decoded_data . startswith ( salted_prefix ) : <EOL> return SaltyString ( salt = decoded_data [ <NUM_LIT:8> : <NUM_LIT:16> ] , data = decoded_data [ <NUM_LIT:16> : ] ) <EOL> return SaltyString ( salt = zero_init_vector , data = decoded_data ) <EOL> def unlock_encryption_key ( password , encryption_key ) : <EOL> key , iv = derive_pbkdf2 ( password , encryption_key . encrypted_key . salt , <EOL> encryption_key . iterations ) <EOL> decrypted_key = aes_decrypt ( key , iv , encryption_key . encrypted_key . data ) <EOL> decrypted_encr_key = decrypt_encryption_key ( encryption_key . validation , <EOL> decrypted_key ) <EOL> decrypted_key_valid = decrypted_encr_key == decrypted_key <EOL> return decrypted_key if decrypted_key_valid else None <EOL> def decrypt_encryption_key ( b64_data , decrypted_key ) : <EOL> encrypted = get_santy_string ( b64_data ) <EOL> key , iv = derive_openssl ( decrypted_key , encrypted . salt ) <EOL> return aes_decrypt ( key , iv , encrypted . data ) <EOL> def derive_pbkdf2 ( password , salt , iterations ) : <EOL> key_and_iv = pbkdf2_bin ( b ( password ) , salt , iterations = iterations , <EOL> keylen = <NUM_LIT:32> ) <EOL> return key_and_iv [ <NUM_LIT:0> : <NUM_LIT:16> ] , key_and_iv [ <NUM_LIT:16> : ] <EOL> def derive_openssl ( key , salt ) : <EOL> key = key [ <NUM_LIT:0> : - <NUM_LIT:16> ] <EOL> key_and_iv = b ( '<STR_LIT>' ) <EOL> prev = b ( '<STR_LIT>' ) <EOL> while len ( key_and_iv ) < <NUM_LIT:32> : <EOL> prev = MD5 . new ( prev + key + salt ) . digest ( ) <EOL> key_and_iv += prev <EOL> return key_and_iv [ <NUM_LIT:0> : <NUM_LIT:16> ] , key_and_iv [ <NUM_LIT:16> : ] <EOL> def aes_decrypt ( key , iv , encrypted_data ) : <EOL> aes = AES . new ( key , mode = AES . MODE_CBC , IV = iv ) <EOL> decrypted = aes . decrypt ( encrypted_data ) <EOL> padding_size = decrypted [ - <NUM_LIT:1> ] <EOL> padding_size = padding_size if PY3 else ord ( padding_size ) <EOL> if padding_size >= <NUM_LIT:16> : <EOL> return decrypted <EOL> return decrypted [ : - padding_size ] <EOL> _get_keychain_path = lambda : os . path . expanduser ( <EOL> os . environ . get ( KEYCHAIN_ENV_KEY , DEFAULT_KEYCHAIN_PATH ) <EOL> ) <EOL> if PY3 : <EOL> get_keychain_path = functools . lru_cache ( ) ( _get_keychain_path ) <EOL> else : <EOL> get_keychain_path = _get_keychain_path </s>
