
                             d Z ddlmZ ddlmZ ddlmZ ddlmZ ddlZddlZddlm	Z	 ddl
Z
ddlZddlZddlmZ dd	lmZ d
Z ed       Z G d de      Z G d de      Zd Zd Zd Zd Zd Zd Zd Zy)zGHelper functions for dealing with encryption keys used with cloud APIs.    )absolute_import)print_function)division)unicode_literalsN)sha256)CommandException)LazyWrapperd   c                  ,    t        j                  d      S )Nzqprojects/([^/]+)/locations/([a-zA-Z0-9_-]{1,63})/keyRings/([a-zA-Z0-9_-]{1,63})/cryptoKeys/([a-zA-Z0-9_-]{1,63})$)recompile     0platform/gsutil/gslib/utils/encryption_helper.py<lambda>r   "   s    BJJ ; <r   c                       e Zd ZdZdZdZy)CryptoKeyTypezDEnum of valid types of encryption keys used with cloud API requests.CSEKCMEKN)__name__
__module____qualname____doc__r   r   r   r   r   r   r   (   s    L	$	$r   r   c                       e Zd ZdZd Zy)CryptoKeyWrapperzClass describing a crypto key used with cloud API requests.

  This class should be instantiated via the `CryptoKeyWrapperFromKey` method.
  c                 Z   || _         t        |      dk(  r.t        j                  | _        d| _        	 t        |      | _        y	 t        |       t        j                  | _        d| _        d| _        y#  t        d      xY w# t        $ r}t        d|j                  z        d}~ww xY w)a  Initialize the CryptoKeyWrapper.

    Args:
      crypto_key: Base64-encoded string of a CSEK, or the name of a Cloud KMS
          CMEK.

    Raises:
      CommandException: The specified crypto key was neither a CMEK key name nor
          a valid base64-encoded string of a CSEK.
    ,   AES256zConfigured encryption_key or decryption_key looked like a CSEK, but it was not a valid 44-character base64 string. Please double-check your configuration and ensure the key is correct.zaConfigured encryption_key or decryption_key looked like a CMEK, but the key failed validation:
%sN)
crypto_keylenr   r   crypto_type
crypto_alg#Base64Sha256FromBase64EncryptionKeycrypto_key_sha256r   ValidateCMEKreasonr   )selfr   es      r   __init__zCryptoKeyWrapper.__init__4   s     !DO :"&++d doN!DZ!P=Z 
 '++ddo#dNMN 	N  =13488<= 	==s#   A4 B 4B	B*B%%B*N)r   r   r   r   r)   r   r   r   r   r   .   s    
!$r   r   c                      | rt        |       S dS )z>Returns a CryptoKeyWrapper for crypto_key, or None for no key.N)r   )r   s    r   CryptoKeyWrapperFromKeyr+   X   s    )3	*	%==r   c           
         t         j                  r!t        | t              s| j	                  d      } t        |j                  ddd            }|8|j                  t        j                  k(  r|j                  | k(  r|j                  S t        t              D ]n  }|dz   }t        |j                  ddt        |      z  d            }| y|j                  t        j                  k(  sR|j                  | k(  sb|j                  c S  y)a~  Searches boto_config for a CSEK with the given base64-encoded SHA256 hash.

  Args:
    key_sha256: (str) Base64-encoded SHA256 hash of the AES256 encryption key.
    boto_config: (boto.pyami.config.Config) The boto config in which to check
        for a matching encryption key.

  Returns:
    (str) Base64-encoded encryption key string if a match is found, None
    otherwise.
  asciiGSUtilencryption_keyN   zdecryption_key%s)sixPY3
isinstancebytesencoder+   getr!   r   r   r$   r   rangeMAX_DECRYPTION_KEYSstr)
key_sha256boto_config
keywrapperi
key_numbers        r   FindMatchingCSEKInBotoConfigr?   ]   s     	WWj%($$W-j&ooh 0$79* 2 22""j0   $%aQJ("4s:"FMOJ 

 
 M$6$6
6

&
&*
4""" &r   c                 F    | j                  ddd      }|rt        |      S dS )a  Returns a CryptoKeyWrapper for the configured encryption key.

  Reads in the value of the "encryption_key" attribute in boto_config, and if
  present, verifies it is a valid base64-encoded string and returns a
  CryptoKeyWrapper for it.

  Args:
    boto_config: (boto.pyami.config.Config) The boto config in which to check
        for a matching encryption key.

  Returns:
    CryptoKeyWrapper for the specified encryption key, or None if no encryption
    key was specified in boto_config.
  r.   r/   N)r6   r   r;   r/   s     r   GetEncryptionKeyWrapperrB      s*     ??8-=tD.-;	.	)EEr   c                    t         j                  r!t        | t              s| j	                  d      } t        j                  |       }t        |      }t        j                  |      }t        j                  |      }|j                  dd      S )Nr-      
r   )r1   r2   r3   r4   r5   base64	b64decode_CalculateSha256FromStringbinascii	unhexlify	b64encodereplace)csek_encryption_keydecoded_bytesr:   sha256_bytessha256_base64s        r   r#   r#      ss    WW)51/66w?""#67-)-8*##J/,""<0-			uc	**r   c                     | st        d      | j                  d      rt        d| z        t               j                  |       st        d| z        y )NzKMS key is empty./z5KMS key should not start with leading slash (/): "%s"zInvalid KMS key name: "%s".
KMS keys should follow the format "projects/<project-id>/locations/<location>/keyRings/<keyring>/cryptoKeys/<key-name>")r   
startswithVALID_CMEK_REmatch)keys    r   r%   r%      se    	
.
//^^C
?#EG G 
		s	#
	!#&	'( ( 
$r   c                 X    t               }|j                  |        |j                         S )N)r   update	hexdigest)input_stringsha256_hashs     r   rG   rG      s&    +\"				  r   c                     | j                  ddd      }|r	 t        j                  |       |S |S #  t        d      xY w)aC  Reads the encryption key from boto_config and ensures it is base64-encoded.

  Args:
    boto_config: (boto.pyami.config.Config) The boto config in which to check
        for a matching encryption key.

  Returns:
    (str) Base64-encoded encryption key string, or None if no encryption key
    exists in configuration.

  r.   r/   NzConfigured encryption_key is not a valid base64 string. Please double-check your configuration and ensure the key is valid and in base64 format.)r6   rE   rF   r   rA   s     r    _GetAndVerifyBase64EncryptionKeyr\      sS     ??8-=tD.~& 
 s   0 =)r   
__future__r   r   r   r   rE   rH   hashlibr   r   sysr1   gslib.exceptionr   gslib.lazy_wrapperr	   r8   rS   objectr   r   r+   r?   rB   r#   r%   rG   r\   r   r   r   <module>rc      s    N & %  '    	 
 
 , * <=F '$v '$T>
!#HF&+(!r   