o
    diLY                     @   s  d dl Z d dlZd dlZd dlZd dlZd dlZd dlZd dlZd dlm	Z	 d dl
mZmZmZmZmZmZ d dlZd dlmZ d dlmZ d dlmZ d dlmZ d dlmZmZmZ d d	lmZ d d
l m!Z! d dl"m#Z# d dl$m%Z%m&Z& d dl'm(Z( e)dZ*dZ+dZ,dZ-G dd de.Z/G dd de0Z1G dd de.Z2G dd de2Z3G dd de2Z4G dd de2Z5G dd de5Z6G d d! d!e.Z7d"e8d#e9d$e8fd%d&Z:d'e9d(e9d$ee9e9f fd)d*Z;d+e9d$e9fd,d-Z<d+e9d$e9fd.d/Z=d"e8d$e8fd0d1Z>d2e8d3e9d$e8fd4d5Z?dS )6    N)BytesIO)DictUnionIOOptionalAnyTuple)AESGCM)default_backend)Cipher)AES)CBCCTRECB)PKCS7)
InvalidTag)padding)PrivateKeyTypesPublicKeyTypes)serializationz"bytes=(?P<start>\d+)-(?P<end>\d+)*      l    c                   @   s@   e Zd ZdZdefddZdddZdd	 Zd
d Zdd Z	dS )DummyAIOFilezHSo that response['Body'].read() presents the same way as a normal S3 getdatac                 C   s   t || _d S N)r   file)selfr    r   C/home/ubuntu/.local/lib/python3.10/site-packages/aioboto3/s3/cse.py__init__#   s   zDummyAIOFile.__init__c                       | j |S r   r   readr   nr   r   r   r#   &      zDummyAIOFile.readc                    s   | j  S r   r"   r   r   r   r   readany)   s   
zDummyAIOFile.readanyc                    r!   r   r"   r$   r   r   r   readexactly,   r&   zDummyAIOFile.readexactlyc                    s   | j  dfS )NTr"   r'   r   r   r   	readchunk/   s   zDummyAIOFile.readchunkN)r    )
__name__
__module____qualname____doc__bytesr   r#   r(   r)   r*   r   r   r   r   r       s    
r   c                   @   s   e Zd ZdS )DecryptErrorN)r+   r,   r-   r   r   r   r   r0   3   s    r0   c                   @   sZ   e Zd Zdd Zdd Zdedeeef defdd	Z	de
eeeef ef fd
dZdS )CryptoContextc                       dS )z0
        Coroutine to perform any setup
        Nr   r'   r   r   r   setup8   s   zCryptoContext.setupc                    r2   )z3
        Coroutine to perform any teardown
        Nr   r'   r   r   r   close>   s    zCryptoContext.closekeymaterial_descriptionreturnc                       t  )z
        Get decryption key for a given S3 object

        :param key: Base64 decoded version of x-amz-key-v2
        :param material_description: JSON decoded x-amz-matdesc
        :return: Raw AES key bytes
        NotImplementedErrorr   r5   r6   r   r   r   get_decryption_aes_keyC   s   z$CryptoContext.get_decryption_aes_keyc                    r8   )z
        Get encryption key to encrypt an S3 object

        :return: Raw AES key bytes, Stringified JSON x-amz-matdesc, Base64 encoded x-amz-key-v2
        r9   r'   r   r   r   get_encryption_aes_keyM   s   z$CryptoContext.get_encryption_aes_keyN)r+   r,   r-   r3   r4   r/   r   strr   r<   r   r=   r   r   r   r   r1   7   s
    $
r1   c                   @   s   e Zd ZdZ		ddee dee deej fddZ	de
d	eeef d
e
fddZd
ee
eeef ef fddZede
d
efddZedde
dee d
efddZdS )AsymmetricCryptoContexta!  
    Crypto context which uses public-private key cryptography.

    The public and private keys need to be loaded in by ``cryptography.hazmat.primitives.serialization.*``

    :param public_key: Public key object
    :param private_key: Private key object
    :param loop: Event loop
    N
public_keyprivate_keyloopc                 C   s(   || _ || _|| _|st | _d S d S r   )r@   rA   _loopasyncioget_event_loop)r   r@   rA   rB   r   r   r   r   a   s   z AsymmetricCryptoContext.__init__r5   r6   r7   c                    s6   j du r
tdjd fddI dH }|S )
        Get decryption key for a given S3 object

        :param key: Base64 decoded version of x-amz-key
        :param material_description: JSON decoded x-amz-matdesc
        :return: Raw AES key bytes
        NzQPrivate key not provided during initialisation, cannot decrypt key encrypting keyc                         j  t S r   )rA   decryptr   PKCS1v15r   r5   r   r   r   <lambda>v       z@AsymmetricCryptoContext.get_decryption_aes_key.<locals>.<lambda>)rA   
ValueErrorrC   run_in_executor)r   r5   r6   	plaintextr   rJ   r   r<   k   s
   
z.AsymmetricCryptoContext.get_decryption_aes_keyc                    sP   j du r
tdtd jd fddI dH } i t| fS )
        Get encryption key to encrypt an S3 object

        :return: Raw AES key bytes, Stringified JSON x-amz-matdesc, Base64 encoded x-amz-key
        NzPPublic key not provided during initialisation, cannot encrypt key encrypting key    c                      rG   r   )r@   encryptr   rI   r   random_bytesr   r   r   rK      rL   z@AsymmetricCryptoContext.get_encryption_aes_key.<locals>.<lambda>)	r@   rM   osurandomrC   rN   base64	b64encodedecode)r   
ciphertextr   rS   r   r=   z   s   


z.AsymmetricCryptoContext.get_encryption_aes_keyr   c                 C   s   t | t S )zr
        Convert public key in DER encoding to a Public key object

        :param data: public key bytes
        )r   load_der_public_keyr
   )r   r   r   r   from_der_public_key   s   z+AsymmetricCryptoContext.from_der_public_keypasswordc                 C   s   t | |t S )z
        Convert private key in DER encoding to a Private key object

        :param data: private key bytes
        :param password: password the private key is encrypted with
        )r   load_der_private_keyr
   )r   r]   r   r   r   from_der_private_key   s   z,AsymmetricCryptoContext.from_der_private_keyNNNr   )r+   r,   r-   r.   r   r   r   rD   AbstractEventLoopr   r/   r   r>   r   r<   r   r=   staticmethodr\   r_   r   r   r   r   r?   V   s    


 	"r?   c                   @   sh   e Zd ZdZddedeej fddZdede	e
ef defd	d
Zdeee	e
e
f e
f fddZdS )SymmetricCryptoContextz
    Crypto context which uses symmetric cryptography.

    The key field should be a valid AES key.

    :param key: Key bytes
    :param loop: Event loop
    Nr5   rB   c                 C   sD   || _ t | _tt| j t | jd| _|| _|s t	 | _d S d S )Nbackend)
r5   r
   _backendr   r   r   _cipherrC   rD   rE   )r   r5   rB   r   r   r   r      s   zSymmetricCryptoContext.__init__r6   r7   c                    sZ   | j   | jd fddI dH ttj | jdfddI dH }|S )rF   Nc                              S r   updatefinalizer   )aesecbr5   r   r   rK      rL   z?SymmetricCryptoContext.get_decryption_aes_key.<locals>.<lambda>c                             S r   ri   r   padded_resultunpadderr   r   rK      rL   )rg   	decryptorrC   rN   r   r   
block_sizerp   )r   r5   r6   resultr   )rl   r5   ro   rp   r   r<      s   

z-SymmetricCryptoContext.get_decryption_aes_keyc                    st   t dttj | jdfddI dH | j	  | jd fddI dH }i t
| fS )rP   rQ   Nc                      rh   r   ri   r   )padderrT   r   r   rK      rL   z?SymmetricCryptoContext.get_encryption_aes_key.<locals>.<lambda>c                      rh   r   ri   r   )rl   ro   r   r   rK      rL   )rU   rV   r   r   rr   rt   rC   rN   rg   	encryptorrW   rX   rY   )r   encrypted_resultr   )rl   ro   rt   rT   r   r=      s   



z-SymmetricCryptoContext.get_encryption_aes_keyr   )r+   r,   r-   r.   r/   r   rD   ra   r   r   r>   r   r<   r   r=   r   r   r   r   rc      s
    		$rc   c                   @   s   e Zd ZdZ		ddee dee defddZd	d
 Z	dd Z
dedeeef defddZdeeeeef ef fddZdS )KMSCryptoContexta  
    Crypto context which uses symmetric cryptography.

    The key field should be a valid AES key.

    E.g. if you wanted to set the KMS region, add kms_client_args={'region_name': 'eu-west-1'}

    :param key: Key bytes
    :param kms_client_args: Will be expanded when getting a KMS client
    :param authenticated_encryption: Uses AES-GCM instead of AES-CBC (also allows range gets of files)
    :param loop: Event loop
    NTkeyidkms_client_argsauthenticated_encryptionc                 C   s*   || _ || _d | _|r|ni | _d | _d S r   )kms_keyrz   _kms_client_kms_client_args_session)r   rx   ry   rz   r   r   r   r      s
   
zKMSCryptoContext.__init__c                    s0   t  | _| jjdi | j I d H | _d S )Nkms)r   )aioboto3Sessionr~   clientr}   
__aenter__r|   r'   r   r   r   r3      s   
$zKMSCryptoContext.setupc                    s   | j  I d H  d S r   )r|   r4   r'   r   r   r   r4      s   zKMSCryptoContext.closer5   r6   r7   c                    s    | j j||dI d H }|d S )N)CiphertextBlobEncryptionContext	Plaintext)r|   rH   )r   r5   r6   kms_datar   r   r   r<      s   z'KMSCryptoContext.get_decryption_aes_keyc                    sT   | j d u r
tdd| j i}| jj| j |ddI d H }|d |t|d  fS )NzLKMS Key not provided during initalisation, cannot decrypt key encrypting key
kms_cmk_idAES_256)KeyIdr   KeySpecr   r   )r{   rM   r|   generate_data_keyrW   rX   rY   )r   encryption_contextkms_respr   r   r   r=      s   

z'KMSCryptoContext.get_encryption_aes_key)NNT)r+   r,   r-   r.   r   r>   dictboolr   r3   r4   r/   r   r   r<   r   r=   r   r   r   r   rw      s    

$rw   c                	       s   e Zd Z	ddedededef fddZdd	 Zd
d Zdede	e
ef defddZdeee	e
e
f e
f fddZ  ZS )MockKMSCryptoContextTaes_keyr6   encrypted_keyrz   c                    s*   t t|   || _|| _|| _|| _d S r   )superr   r   r   r6   r   rz   )r   r   r6   r   rz   	__class__r   r   r     s
   
zMockKMSCryptoContext.__init__c                       d S r   r   r'   r   r   r   r3        zMockKMSCryptoContext.setupc                    r   r   r   r'   r   r   r   r4     r   zMockKMSCryptoContext.closer5   r7   c                    s   | j S r   )r   r;   r   r   r   r<     s   z+MockKMSCryptoContext.get_decryption_aes_keyc                    s    | j | j t| j fS r   )r   r6   copyrW   rX   r   rY   r'   r   r   r   r=     s   z+MockKMSCryptoContext.get_encryption_aes_key)T)r+   r,   r-   r/   r   r   r   r3   r4   r   r>   r   r<   r   r=   __classcell__r   r   r   r   r     s    (r   c                   @   s   e Zd ZdZd"dedee fddZdd Zd	d
 Z	dd Z
dd ZdededefddZd"dedeeef dee defddZ		d#dedeeef dedee dee dee defddZd"deeef dededefd d!ZdS )$S3CSEa|  
    S3 Client-side encryption wrapper.

    To change S3 region add s3_client_args={'region_name': 'eu-west-1'}

    To use this object, either use it with ``async with S3CSE(...) as s3_cse:``
    Or run the setup() and close() coro's respectively

    :param crypto_context: Takes a cryto context object from above
    :param s3_client_args: Optional dict of S3 client args
    Ncrypto_contexts3_client_argsc                 C   s8   d | _ t | _|| _d | _d | _|r|| _d S i | _d S r   )rC   r
   rf   _crypto_contextr~   
_s3_client_s3_client_args)r   r   r   r   r   r   r   /  s   zS3CSE.__init__c                    s`   t jdk rt | _nt | _t | _| jj	di | j
 I d H | _| j I d H  d S )N)      s3)r   )sysversion_inforD   rE   rC   get_running_loopr   r   r~   r   r   r   r   r   r3   r'   r   r   r   r3   8  s   


 zS3CSE.setupc                    s&   | j  I d H  | j I d H  d S r   )r   r4   r   r'   r   r   r   r4   B  s   zS3CSE.closec                    s   |   I d H  | S r   )r3   r'   r   r   r   r   F  s   zS3CSE.__aenter__c                    s   |   I d H  d S r   )r4   )r   exc_typeexc_valexc_tbr   r   r   	__aexit__J  s   zS3CSE.__aexit__BucketKeyr7   c                    sV  | j du r|  I dH  |d}d}d}d}|rNt|}|s(td|t|d}|d}|du r;d}nt|}t	||\}}	d||	|d< | j j
d||d|I dH }
|
d	 }t|
d
 d d }d|vrud|vru|
S d|v r|
d  I dH }| |||I dH }n|
d  I dH }| ||||||I dH }t||
d< |
S )z
        S3 GetObject. Takes same args as Boto3 documentation

        Decrypts any CSE

        :param Bucket: S3 Bucket
        :param Key: S3 Key (filepath)
        :return: returns same response as a normal S3 get_object
        NRangez$Dont understand this range value {0}      l    zbytes={0}-{1})r   r   MetadataResponseMetadataHTTPHeaderszcontent-length	x-amz-keyx-amz-key-v2Bodyr   )r   r3   getRANGE_REGEXmatchrM   formatintgroup_get_adjusted_crypto_range
get_objectr#   _decrypt_v1_decrypt_v2r   )r   r   r   kwargs_rangeactual_range_startdesired_range_startdesired_range_endrange_matchactual_range_ends3_responsemetadatawhole_file_length	file_databodyr   r   r   r   N  sB   






zS3CSE.get_objectr   r   range_startc           	         s   |rt dt|d }t|d }| j||I d H }t|d }tt|t	|| j
d  | jd  fddI d H ttj | jd fddI d H }|S )	Nz/Cant do range get when not using KMS encryptionr   x-amz-matdescx-amz-ivrd   c                      rh   r   ri   r   aescbcr   r   r   rK     rL   z#S3CSE._decrypt_v1.<locals>.<lambda>c                      rm   r   ri   r   rn   r   r   rK     rL   )r0   rW   	b64decodejsonloadsr   r<   r   r   r   rf   rq   rC   rN   r   rr   rp   )	r   r   r   r   decryption_keyr6   r   ivrs   r   )r   r   ro   rp   r   r     s   zS3CSE._decrypt_v1entire_file_lengthdesired_startdesired_endc                    s  t |d }t|d }| j||I d H }	t |d |dddkr|d urlt|tt	|	t
| jd | jd fdd	I d H }
t|d
 d }|| d }||krb|n|}|
|| }
|
S t|	z| jd fdd	I d H }
W |
S  ty   tdw |rtdtt	|	t| jd  | jd  fdd	I d H tt	j | jd fdd	I d H }
|
S )Nr   r   r   x-amz-cek-algAES/CBC/PKCS5PaddingAES/GCM/NoPaddingrd   c                      rh   r   ri   r   )aesctrr   r   r   rK     rL   z#S3CSE._decrypt_v2.<locals>.<lambda>x-amz-tag-len   r   c                      s     d S r   )rH   r   )aesgcmr   r   r   r   rK         zJFailed to decrypt, AEAD tag is incorrect. Possible key or IV are incorrectz&Cannot decrypt AES-CBC file with rangec                      rh   r   ri   r   r   r   r   rK     rL   c                      rm   r   ri   r   rn   r   r   rK     rL   )rW   r   r   r   r   r<   r   _adjust_iv_for_ranger   r   r   rf   rq   rC   rN   r   r	   r   r0   r   r   rr   rp   )r   r   r   r   r   r   r   r   r6   r   rs   aead_tag_len
max_offsetr   )r   r   r   r   r   ro   rp   r   r     sB   

"

zS3CSE._decrypt_v2r   r   c                    s  | j du r|  I dH  t dr$t jr   I dH  n   t| jt}|o/| jj	}|dur6|ni }| j
 I dH \}}	}
|rk|rkd|d< tt|d< tdt|| jd fddI dH }n>|rqd	|d< td
ttj | jd fddI dH tt|t| jd | jdfddI dH }tt |d< t |d< t|	|d< |rd|d< |
|d< n|
|d< | j j d||||d|I dH  dS )z
        PutObject. Takes same args as Boto3 documentation

        Encrypts files

        :param: Body: File data
        :param Bucket: S3 Bucket
        :param Key: S3 Key (filepath)
        Nr#   r   r   r      c                      s     d S r   )rR   r   )r   r   r   r   r   rK     r   z"S3CSE.put_object.<locals>.<lambda>r   r   c                      rm   r   ri   r   )r   rt   r   r   rK     rL   rd   c                      rh   r   ri   r   )r   ro   r   r   rK     rL   z x-amz-unencrypted-content-lengthr   r   r   zx-amz-wrap-algr   r   )r   r   r   r   r   )!r   r3   hasattrinspectiscoroutinefunctionr#   
isinstancer   rw   rz   r=   r>   AES_BLOCK_SIZErU   rV   r	   rC   rN   r   r   rr   rt   r   r   rf   ru   lenrW   rX   rY   r   dumps
put_object)r   r   r   r   r   r   is_kmsauthenticated_cryptor   matdesc_metadatakey_metadatars   r   )r   r   r   r   ro   rt   r   r     sN   



"

zS3CSE.put_objectr   r`   )r+   r,   r-   r.   r1   r   r   r   r3   r4   r   r   r>   r   r/   r   r   r   r   r   r   r   r   r   r   r   r   "  s*    	
(@
(@r   r   byte_offsetr7   c                 C   sF   t | dkr
tdt}|| }|| |krtdt| }t||S )Nr   z(IV must be 12 bytes long for AES-GCM/CTRzLRange size invalid. Should never hit this as range should be adjusted by now)r   RuntimeErrorr   _compute_j0_increment_blocks)r   r   rr   block_offsetj0r   r   r   r   /  s   
r   startendc                 C   s   t | } t|}| |fS r   )_get_cipher_block_lower_bound_get_cipher_block_upper_bound)r   r   r   r   r   r   <  s   r   valuec                 C   s   | | t   t  }t|dS )Nr   )r   max)r   lower_boundr   r   r   r   C  s   
r   c                 C   s"   t | t   }| | t  }t|tS r   )r   minJAVA_LONG_MAX_VALUE)r   offsetupper_boundr   r   r   r   H  s   
r   c                 C   s   | dt d   d }t|dS )N          r   )AES_BLOCK_SIZE_BYTESr   )r   r   r   r   r   r   N  s   
r   counterblock_deltac                 C   s   |dkr| S | rt | dkrtddgd }d}|dkr-| | ||d < |d7 }|dkstdtdt|d | }| d d |d	d  } | S )
Nr   r   zCounter must be 16 bytes longr   r      r   z>Q   )r   rM   structpackunpackr/   )r  r  byte_bufferirs   r   r   r   r   T  s   
 r   )@rD   rW   r   r   rU   rer   r
  ior   typingr   r   r   r   r   r   r   +cryptography.hazmat.primitives.ciphers.aeadr	   cryptography.hazmat.backendsr
   &cryptography.hazmat.primitives.ciphersr   1cryptography.hazmat.primitives.ciphers.algorithmsr   ,cryptography.hazmat.primitives.ciphers.modesr   r   r   &cryptography.hazmat.primitives.paddingr   cryptography.exceptionsr   )cryptography.hazmat.primitives.asymmetricr   /cryptography.hazmat.primitives.asymmetric.typesr   r   cryptography.hazmat.primitivesr   compiler   r   r  r   objectr   	Exceptionr0   r1   r?   rc   rw   r   r   r/   r   r   r   r   r   r   r   r   r   r   r   <module>   sR     
I94  