def sg_decrypt_secret(to_decrypt, api_token):
    '''
    Decypt an item from your database using your api_token.
    '''

    prefix, encoding, seed_and_nonce, b64_iv_and_ciphertext = to_decrypt.split('$')

    seed_pub_hash_hex, nonce = seed_and_nonce.split('@')

    is_valid, err_msg = is_valid_seed_hex(seed_pub_hash_hex)
    if not is_valid:
        raise Exception('Invalid `seed_pub_hash_hex`: %s' % err_msg)

    # TODO: batch these API calls
    api_response = get_decryption_info(
            seed_pub_hash=seed_pub_hash_hex,
            api_token=api_token,
            nonce=nonce,
            version='v1',
            )

    if 'error' in api_response:
        raise Exception(api_response['error'])
    if 'errors' in api_response:
        raise Exception(api_response['errors'])

    key = api_response['key'][:KEY_LENGTH_IN_BYTES]

    b64_text_to_decrypt = '$'.join(
            (
                prefix,
                encoding,
                # no seed_and_nonce
                b64_iv_and_ciphertext
                )
            )

    return decrypt(
            b64_text_to_decrypt=b64_text_to_decrypt,
            key=key,
            )
def sg_decrypt_from_priv_seed(to_decrypt, private_seed_hex):
    '''
    For use with a private seed on your local server.

    WARNING: placing the private seed on your local server defeats the whole
    purpose of rate limiting. If an attacker has access to your encrypted
    database and gets access to this seed on your server, they can easily
    decrypt the whole database. Only use this method if you know what you're
    doing. It's main purpose is recovery in case SecondGuard is down.
    '''
    prefix, encoding, seed_and_nonce_str, b64_iv_and_ciphertext = to_decrypt.split('$')
    seed_pub_hash_hex, nonce = seed_and_nonce_str.split('@')

    valid_pair = is_seed_hash_pair(
            private_seed_hex=private_seed_hex,
            seed_public_hash_hex=seed_pub_hash_hex,
            )
    if not valid_pair:
        err_msg = seed_pub_hash_hex
        err_msg += ' is not a valid private seed for the data you are trying to decrypt.'
        raise Exception(err_msg)

    unique_key = derive_child_key(
            private_seed_hex=private_seed_hex,
            nonce=nonce,
            )

    b64_text_to_decrypt = '$'.join(
            (
                prefix,
                encoding,
                # no seed_and_nonce_str
                b64_iv_and_ciphertext,
                )
            )

    return decrypt(
            b64_text_to_decrypt=b64_text_to_decrypt,
            key=unique_key[:KEY_LENGTH_IN_BYTES],
            )
 def test_encryption(self):
     for key in (self.byte_key, self.str_key):
         for secret_message in (self.byte_message, self.str_message):
             assert decrypt(encrypt(secret_message, key), key) == secret_message