1# Copyright (c) 2014 Amazon.com, Inc. or its affiliates.  All Rights Reserved
2#
3# Permission is hereby granted, free of charge, to any person obtaining a
4# copy of this software and associated documentation files (the
5# "Software"), to deal in the Software without restriction, including
6# without limitation the rights to use, copy, modify, merge, publish, dis-
7# tribute, sublicense, and/or sell copies of the Software, and to permit
8# persons to whom the Software is furnished to do so, subject to the fol-
9# lowing conditions:
10#
11# The above copyright notice and this permission notice shall be included
12# in all copies or substantial portions of the Software.
13#
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
17# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20# IN THE SOFTWARE.
21#
22
23import boto
24from boto.compat import json
25from boto.connection import AWSQueryConnection
26from boto.regioninfo import RegionInfo
27from boto.exception import JSONResponseError
28from boto.kms import exceptions
29from boto.compat import six
30import base64
31
32
33class KMSConnection(AWSQueryConnection):
34    """
35    AWS Key Management Service
36    AWS Key Management Service (KMS) is an encryption and key
37    management web service. This guide describes the KMS actions that
38    you can call programmatically. For general information about KMS,
39    see (need an address here). For the KMS developer guide, see (need
40    address here).
41
42    AWS provides SDKs that consist of libraries and sample code for
43    various programming languages and platforms (Java, Ruby, .Net,
44    iOS, Android, etc.). The SDKs provide a convenient way to create
45    programmatic access to KMS and AWS. For example, the SDKs take
46    care of tasks such as signing requests (see below), managing
47    errors, and retrying requests automatically. For more information
48    about the AWS SDKs, including how to download and install them,
49    see `Tools for Amazon Web Services`_.
50
51    We recommend that you use the AWS SDKs to make programmatic API
52    calls to KMS. However, you can also use the KMS Query API to make
53    to make direct calls to the KMS web service.
54
55    **Signing Requests**
56
57    Requests must be signed by using an access key ID and a secret
58    access key. We strongly recommend that you do not use your AWS
59    account access key ID and secret key for everyday work with KMS.
60    Instead, use the access key ID and secret access key for an IAM
61    user, or you can use the AWS Security Token Service to generate
62    temporary security credentials that you can use to sign requests.
63
64    All KMS operations require `Signature Version 4`_.
65
66    **Recording API Requests**
67
68    KMS supports AWS CloudTrail, a service that records AWS API calls
69    and related events for your AWS account and delivers them to an
70    Amazon S3 bucket that you specify. By using the information
71    collected by CloudTrail, you can determine what requests were made
72    to KMS, who made the request, when it was made, and so on. To
73    learn more about CloudTrail, including how to turn it on and find
74    your log files, see the `AWS CloudTrail User Guide`_
75
76    **Additional Resources**
77
78    For more information about credentials and request signing, see
79    the following:
80
81
82    + `AWS Security Credentials`_. This topic provides general
83      information about the types of credentials used for accessing AWS.
84    + `AWS Security Token Service`_. This guide describes how to
85      create and use temporary security credentials.
86    + `Signing AWS API Requests`_. This set of topics walks you
87      through the process of signing a request using an access key ID
88      and a secret access key.
89    """
90    APIVersion = "2014-11-01"
91    DefaultRegionName = "us-east-1"
92    DefaultRegionEndpoint = "kms.us-east-1.amazonaws.com"
93    ServiceName = "KMS"
94    TargetPrefix = "TrentService"
95    ResponseError = JSONResponseError
96
97    _faults = {
98        "InvalidGrantTokenException": exceptions.InvalidGrantTokenException,
99        "DisabledException": exceptions.DisabledException,
100        "LimitExceededException": exceptions.LimitExceededException,
101        "DependencyTimeoutException": exceptions.DependencyTimeoutException,
102        "InvalidMarkerException": exceptions.InvalidMarkerException,
103        "AlreadyExistsException": exceptions.AlreadyExistsException,
104        "InvalidCiphertextException": exceptions.InvalidCiphertextException,
105        "KeyUnavailableException": exceptions.KeyUnavailableException,
106        "InvalidAliasNameException": exceptions.InvalidAliasNameException,
107        "UnsupportedOperationException": exceptions.UnsupportedOperationException,
108        "InvalidArnException": exceptions.InvalidArnException,
109        "KMSInternalException": exceptions.KMSInternalException,
110        "InvalidKeyUsageException": exceptions.InvalidKeyUsageException,
111        "MalformedPolicyDocumentException": exceptions.MalformedPolicyDocumentException,
112        "NotFoundException": exceptions.NotFoundException,
113    }
114
115
116    def __init__(self, **kwargs):
117        region = kwargs.pop('region', None)
118        if not region:
119            region = RegionInfo(self, self.DefaultRegionName,
120                                self.DefaultRegionEndpoint)
121
122        if 'host' not in kwargs or kwargs['host'] is None:
123            kwargs['host'] = region.endpoint
124
125        super(KMSConnection, self).__init__(**kwargs)
126        self.region = region
127
128    def _required_auth_capability(self):
129        return ['hmac-v4']
130
131    def create_alias(self, alias_name, target_key_id):
132        """
133        Creates a display name for a customer master key. An alias can
134        be used to identify a key and should be unique. The console
135        enforces a one-to-one mapping between the alias and a key. An
136        alias name can contain only alphanumeric characters, forward
137        slashes (/), underscores (_), and dashes (-). An alias must
138        start with the word "alias" followed by a forward slash
139        (alias/). An alias that begins with "aws" after the forward
140        slash (alias/aws...) is reserved by Amazon Web Services (AWS).
141
142        :type alias_name: string
143        :param alias_name: String that contains the display name. Aliases that
144            begin with AWS are reserved.
145
146        :type target_key_id: string
147        :param target_key_id: An identifier of the key for which you are
148            creating the alias. This value cannot be another alias.
149
150        """
151        params = {
152            'AliasName': alias_name,
153            'TargetKeyId': target_key_id,
154        }
155        return self.make_request(action='CreateAlias',
156                                 body=json.dumps(params))
157
158    def create_grant(self, key_id, grantee_principal,
159                     retiring_principal=None, operations=None,
160                     constraints=None, grant_tokens=None):
161        """
162        Adds a grant to a key to specify who can access the key and
163        under what conditions. Grants are alternate permission
164        mechanisms to key policies. If absent, access to the key is
165        evaluated based on IAM policies attached to the user. By
166        default, grants do not expire. Grants can be listed, retired,
167        or revoked as indicated by the following APIs. Typically, when
168        you are finished using a grant, you retire it. When you want
169        to end a grant immediately, revoke it. For more information
170        about grants, see `Grants`_.
171
172        #. ListGrants
173        #. RetireGrant
174        #. RevokeGrant
175
176        :type key_id: string
177        :param key_id: A unique key identifier for a customer master key. This
178            value can be a globally unique identifier, an ARN, or an alias.
179
180        :type grantee_principal: string
181        :param grantee_principal: Principal given permission by the grant to
182            use the key identified by the `keyId` parameter.
183
184        :type retiring_principal: string
185        :param retiring_principal: Principal given permission to retire the
186            grant. For more information, see RetireGrant.
187
188        :type operations: list
189        :param operations: List of operations permitted by the grant. This can
190            be any combination of one or more of the following values:
191
192        #. Decrypt
193        #. Encrypt
194        #. GenerateDataKey
195        #. GenerateDataKeyWithoutPlaintext
196        #. ReEncryptFrom
197        #. ReEncryptTo
198        #. CreateGrant
199
200        :type constraints: dict
201        :param constraints: Specifies the conditions under which the actions
202            specified by the `Operations` parameter are allowed.
203
204        :type grant_tokens: list
205        :param grant_tokens: List of grant tokens.
206
207        """
208        params = {
209            'KeyId': key_id,
210            'GranteePrincipal': grantee_principal,
211        }
212        if retiring_principal is not None:
213            params['RetiringPrincipal'] = retiring_principal
214        if operations is not None:
215            params['Operations'] = operations
216        if constraints is not None:
217            params['Constraints'] = constraints
218        if grant_tokens is not None:
219            params['GrantTokens'] = grant_tokens
220        return self.make_request(action='CreateGrant',
221                                 body=json.dumps(params))
222
223    def create_key(self, policy=None, description=None, key_usage=None):
224        """
225        Creates a customer master key. Customer master keys can be
226        used to encrypt small amounts of data (less than 4K) directly,
227        but they are most commonly used to encrypt or envelope data
228        keys that are then used to encrypt customer data. For more
229        information about data keys, see GenerateDataKey and
230        GenerateDataKeyWithoutPlaintext.
231
232        :type policy: string
233        :param policy: Policy to be attached to the key. This is required and
234            delegates back to the account. The key is the root of trust.
235
236        :type description: string
237        :param description: Description of the key. We recommend that you
238            choose a description that helps your customer decide whether the
239            key is appropriate for a task.
240
241        :type key_usage: string
242        :param key_usage: Specifies the intended use of the key. Currently this
243            defaults to ENCRYPT/DECRYPT, and only symmetric encryption and
244            decryption are supported.
245
246        """
247        params = {}
248        if policy is not None:
249            params['Policy'] = policy
250        if description is not None:
251            params['Description'] = description
252        if key_usage is not None:
253            params['KeyUsage'] = key_usage
254        return self.make_request(action='CreateKey',
255                                 body=json.dumps(params))
256
257    def decrypt(self, ciphertext_blob, encryption_context=None,
258                grant_tokens=None):
259        """
260        Decrypts ciphertext. Ciphertext is plaintext that has been
261        previously encrypted by using the Encrypt function.
262
263        :type ciphertext_blob: blob
264        :param ciphertext_blob: Ciphertext including metadata.
265
266        :type encryption_context: map
267        :param encryption_context: The encryption context. If this was
268            specified in the Encrypt function, it must be specified here or the
269            decryption operation will fail. For more information, see
270            `Encryption Context`_.
271
272        :type grant_tokens: list
273        :param grant_tokens: A list of grant tokens that represent grants which
274            can be used to provide long term permissions to perform decryption.
275
276        """
277        if not isinstance(ciphertext_blob, six.binary_type):
278            raise TypeError(
279                "Value of argument ``ciphertext_blob`` "
280                "must be of type %s." % six.binary_type)
281        ciphertext_blob = base64.b64encode(ciphertext_blob)
282        params = {'CiphertextBlob': ciphertext_blob, }
283        if encryption_context is not None:
284            params['EncryptionContext'] = encryption_context
285        if grant_tokens is not None:
286            params['GrantTokens'] = grant_tokens
287        response = self.make_request(action='Decrypt',
288                                     body=json.dumps(params))
289        if response.get('Plaintext') is not None:
290            response['Plaintext'] = base64.b64decode(
291                response['Plaintext'].encode('utf-8'))
292        return response
293
294    def delete_alias(self, alias_name):
295        """
296        Deletes the specified alias.
297
298        :type alias_name: string
299        :param alias_name: The alias to be deleted.
300
301        """
302        params = {'AliasName': alias_name, }
303        return self.make_request(action='DeleteAlias',
304                                 body=json.dumps(params))
305
306    def describe_key(self, key_id):
307        """
308        Provides detailed information about the specified customer
309        master key.
310
311        :type key_id: string
312        :param key_id: Unique identifier of the customer master key to be
313            described. This can be an ARN, an alias, or a globally unique
314            identifier.
315
316        """
317        params = {'KeyId': key_id, }
318        return self.make_request(action='DescribeKey',
319                                 body=json.dumps(params))
320
321    def disable_key(self, key_id):
322        """
323        Marks a key as disabled, thereby preventing its use.
324
325        :type key_id: string
326        :param key_id: Unique identifier of the customer master key to be
327            disabled. This can be an ARN, an alias, or a globally unique
328            identifier.
329
330        """
331        params = {'KeyId': key_id, }
332        return self.make_request(action='DisableKey',
333                                 body=json.dumps(params))
334
335    def disable_key_rotation(self, key_id):
336        """
337        Disables rotation of the specified key.
338
339        :type key_id: string
340        :param key_id: Unique identifier of the customer master key for which
341            rotation is to be disabled. This can be an ARN, an alias, or a
342            globally unique identifier.
343
344        """
345        params = {'KeyId': key_id, }
346        return self.make_request(action='DisableKeyRotation',
347                                 body=json.dumps(params))
348
349    def enable_key(self, key_id):
350        """
351        Marks a key as enabled, thereby permitting its use. You can
352        have up to 25 enabled keys at one time.
353
354        :type key_id: string
355        :param key_id: Unique identifier of the customer master key to be
356            enabled. This can be an ARN, an alias, or a globally unique
357            identifier.
358
359        """
360        params = {'KeyId': key_id, }
361        return self.make_request(action='EnableKey',
362                                 body=json.dumps(params))
363
364    def enable_key_rotation(self, key_id):
365        """
366        Enables rotation of the specified customer master key.
367
368        :type key_id: string
369        :param key_id: Unique identifier of the customer master key for which
370            rotation is to be enabled. This can be an ARN, an alias, or a
371            globally unique identifier.
372
373        """
374        params = {'KeyId': key_id, }
375        return self.make_request(action='EnableKeyRotation',
376                                 body=json.dumps(params))
377
378    def encrypt(self, key_id, plaintext, encryption_context=None,
379                grant_tokens=None):
380        """
381        Encrypts plaintext into ciphertext by using a customer master
382        key.
383
384        :type key_id: string
385        :param key_id: Unique identifier of the customer master. This can be an
386            ARN, an alias, or the Key ID.
387
388        :type plaintext: blob
389        :param plaintext: Data to be encrypted.
390
391        :type encryption_context: map
392        :param encryption_context: Name:value pair that specifies the
393            encryption context to be used for authenticated encryption. For
394            more information, see `Authenticated Encryption`_.
395
396        :type grant_tokens: list
397        :param grant_tokens: A list of grant tokens that represent grants which
398            can be used to provide long term permissions to perform encryption.
399
400        """
401        if not isinstance(plaintext, six.binary_type):
402            raise TypeError(
403                "Value of argument ``plaintext`` "
404                "must be of type %s." % six.binary_type)
405        plaintext = base64.b64encode(plaintext)
406        params = {'KeyId': key_id, 'Plaintext': plaintext, }
407        if encryption_context is not None:
408            params['EncryptionContext'] = encryption_context
409        if grant_tokens is not None:
410            params['GrantTokens'] = grant_tokens
411        response = self.make_request(action='Encrypt',
412                                     body=json.dumps(params))
413        if response.get('CiphertextBlob') is not None:
414            response['CiphertextBlob'] = base64.b64decode(
415                response['CiphertextBlob'].encode('utf-8'))
416        return response
417
418    def generate_data_key(self, key_id, encryption_context=None,
419                          number_of_bytes=None, key_spec=None,
420                          grant_tokens=None):
421        """
422        Generates a secure data key. Data keys are used to encrypt and
423        decrypt data. They are wrapped by customer master keys.
424
425        :type key_id: string
426        :param key_id: Unique identifier of the key. This can be an ARN, an
427            alias, or a globally unique identifier.
428
429        :type encryption_context: map
430        :param encryption_context: Name/value pair that contains additional
431            data to be authenticated during the encryption and decryption
432            processes that use the key. This value is logged by AWS CloudTrail
433            to provide context around the data encrypted by the key.
434
435        :type number_of_bytes: integer
436        :param number_of_bytes: Integer that contains the number of bytes to
437            generate. Common values are 128, 256, 512, 1024 and so on. 1024 is
438            the current limit.
439
440        :type key_spec: string
441        :param key_spec: Value that identifies the encryption algorithm and key
442            size to generate a data key for. Currently this can be AES_128 or
443            AES_256.
444
445        :type grant_tokens: list
446        :param grant_tokens: A list of grant tokens that represent grants which
447            can be used to provide long term permissions to generate a key.
448
449        """
450        params = {'KeyId': key_id, }
451        if encryption_context is not None:
452            params['EncryptionContext'] = encryption_context
453        if number_of_bytes is not None:
454            params['NumberOfBytes'] = number_of_bytes
455        if key_spec is not None:
456            params['KeySpec'] = key_spec
457        if grant_tokens is not None:
458            params['GrantTokens'] = grant_tokens
459        response = self.make_request(action='GenerateDataKey',
460                                     body=json.dumps(params))
461        if response.get('CiphertextBlob') is not None:
462            response['CiphertextBlob'] = base64.b64decode(
463                response['CiphertextBlob'].encode('utf-8'))
464        if response.get('Plaintext') is not None:
465            response['Plaintext'] = base64.b64decode(
466                response['Plaintext'].encode('utf-8'))
467        return response
468
469    def generate_data_key_without_plaintext(self, key_id,
470                                            encryption_context=None,
471                                            key_spec=None,
472                                            number_of_bytes=None,
473                                            grant_tokens=None):
474        """
475        Returns a key wrapped by a customer master key without the
476        plaintext copy of that key. To retrieve the plaintext, see
477        GenerateDataKey.
478
479        :type key_id: string
480        :param key_id: Unique identifier of the key. This can be an ARN, an
481            alias, or a globally unique identifier.
482
483        :type encryption_context: map
484        :param encryption_context: Name:value pair that contains additional
485            data to be authenticated during the encryption and decryption
486            processes.
487
488        :type key_spec: string
489        :param key_spec: Value that identifies the encryption algorithm and key
490            size. Currently this can be AES_128 or AES_256.
491
492        :type number_of_bytes: integer
493        :param number_of_bytes: Integer that contains the number of bytes to
494            generate. Common values are 128, 256, 512, 1024 and so on.
495
496        :type grant_tokens: list
497        :param grant_tokens: A list of grant tokens that represent grants which
498            can be used to provide long term permissions to generate a key.
499
500        """
501        params = {'KeyId': key_id, }
502        if encryption_context is not None:
503            params['EncryptionContext'] = encryption_context
504        if key_spec is not None:
505            params['KeySpec'] = key_spec
506        if number_of_bytes is not None:
507            params['NumberOfBytes'] = number_of_bytes
508        if grant_tokens is not None:
509            params['GrantTokens'] = grant_tokens
510        response = self.make_request(action='GenerateDataKeyWithoutPlaintext',
511                                     body=json.dumps(params))
512        if response.get('CiphertextBlob') is not None:
513            response['CiphertextBlob'] = base64.b64decode(
514                response['CiphertextBlob'].encode('utf-8'))
515        return response
516
517    def generate_random(self, number_of_bytes=None):
518        """
519        Generates an unpredictable byte string.
520
521        :type number_of_bytes: integer
522        :param number_of_bytes: Integer that contains the number of bytes to
523            generate. Common values are 128, 256, 512, 1024 and so on. The
524            current limit is 1024 bytes.
525
526        """
527        params = {}
528        if number_of_bytes is not None:
529            params['NumberOfBytes'] = number_of_bytes
530        response = self.make_request(action='GenerateRandom',
531                                     body=json.dumps(params))
532        if response.get('Plaintext') is not None:
533            response['Plaintext'] = base64.b64decode(
534                response['Plaintext'].encode('utf-8'))
535        return response
536
537    def get_key_policy(self, key_id, policy_name):
538        """
539        Retrieves a policy attached to the specified key.
540
541        :type key_id: string
542        :param key_id: Unique identifier of the key. This can be an ARN, an
543            alias, or a globally unique identifier.
544
545        :type policy_name: string
546        :param policy_name: String that contains the name of the policy.
547            Currently, this must be "default". Policy names can be discovered
548            by calling ListKeyPolicies.
549
550        """
551        params = {'KeyId': key_id, 'PolicyName': policy_name, }
552        return self.make_request(action='GetKeyPolicy',
553                                 body=json.dumps(params))
554
555    def get_key_rotation_status(self, key_id):
556        """
557        Retrieves a Boolean value that indicates whether key rotation
558        is enabled for the specified key.
559
560        :type key_id: string
561        :param key_id: Unique identifier of the key. This can be an ARN, an
562            alias, or a globally unique identifier.
563
564        """
565        params = {'KeyId': key_id, }
566        return self.make_request(action='GetKeyRotationStatus',
567                                 body=json.dumps(params))
568
569    def list_aliases(self, limit=None, marker=None):
570        """
571        Lists all of the key aliases in the account.
572
573        :type limit: integer
574        :param limit: Specify this parameter when paginating results to
575            indicate the maximum number of aliases you want in each response.
576            If there are additional aliases beyond the maximum you specify, the
577            `Truncated` response element will be set to `true.`
578
579        :type marker: string
580        :param marker: Use this parameter when paginating results, and only in
581            a subsequent request after you've received a response where the
582            results are truncated. Set it to the value of the `NextMarker`
583            element in the response you just received.
584
585        """
586        params = {}
587        if limit is not None:
588            params['Limit'] = limit
589        if marker is not None:
590            params['Marker'] = marker
591        return self.make_request(action='ListAliases',
592                                 body=json.dumps(params))
593
594    def list_grants(self, key_id, limit=None, marker=None):
595        """
596        List the grants for a specified key.
597
598        :type key_id: string
599        :param key_id: Unique identifier of the key. This can be an ARN, an
600            alias, or a globally unique identifier.
601
602        :type limit: integer
603        :param limit: Specify this parameter only when paginating results to
604            indicate the maximum number of grants you want listed in the
605            response. If there are additional grants beyond the maximum you
606            specify, the `Truncated` response element will be set to `true.`
607
608        :type marker: string
609        :param marker: Use this parameter only when paginating results, and
610            only in a subsequent request after you've received a response where
611            the results are truncated. Set it to the value of the `NextMarker`
612            in the response you just received.
613
614        """
615        params = {'KeyId': key_id, }
616        if limit is not None:
617            params['Limit'] = limit
618        if marker is not None:
619            params['Marker'] = marker
620        return self.make_request(action='ListGrants',
621                                 body=json.dumps(params))
622
623    def list_key_policies(self, key_id, limit=None, marker=None):
624        """
625        Retrieves a list of policies attached to a key.
626
627        :type key_id: string
628        :param key_id: Unique identifier of the key. This can be an ARN, an
629            alias, or a globally unique identifier.
630
631        :type limit: integer
632        :param limit: Specify this parameter only when paginating results to
633            indicate the maximum number of policies you want listed in the
634            response. If there are additional policies beyond the maximum you
635            specify, the `Truncated` response element will be set to `true.`
636
637        :type marker: string
638        :param marker: Use this parameter only when paginating results, and
639            only in a subsequent request after you've received a response where
640            the results are truncated. Set it to the value of the `NextMarker`
641            in the response you just received.
642
643        """
644        params = {'KeyId': key_id, }
645        if limit is not None:
646            params['Limit'] = limit
647        if marker is not None:
648            params['Marker'] = marker
649        return self.make_request(action='ListKeyPolicies',
650                                 body=json.dumps(params))
651
652    def list_keys(self, limit=None, marker=None):
653        """
654        Lists the customer master keys.
655
656        :type limit: integer
657        :param limit: Specify this parameter only when paginating results to
658            indicate the maximum number of keys you want listed in the
659            response. If there are additional keys beyond the maximum you
660            specify, the `Truncated` response element will be set to `true.`
661
662        :type marker: string
663        :param marker: Use this parameter only when paginating results, and
664            only in a subsequent request after you've received a response where
665            the results are truncated. Set it to the value of the `NextMarker`
666            in the response you just received.
667
668        """
669        params = {}
670        if limit is not None:
671            params['Limit'] = limit
672        if marker is not None:
673            params['Marker'] = marker
674        return self.make_request(action='ListKeys',
675                                 body=json.dumps(params))
676
677    def put_key_policy(self, key_id, policy_name, policy):
678        """
679        Attaches a policy to the specified key.
680
681        :type key_id: string
682        :param key_id: Unique identifier of the key. This can be an ARN, an
683            alias, or a globally unique identifier.
684
685        :type policy_name: string
686        :param policy_name: Name of the policy to be attached. Currently, the
687            only supported name is "default".
688
689        :type policy: string
690        :param policy: The policy, in JSON format, to be attached to the key.
691
692        """
693        params = {
694            'KeyId': key_id,
695            'PolicyName': policy_name,
696            'Policy': policy,
697        }
698        return self.make_request(action='PutKeyPolicy',
699                                 body=json.dumps(params))
700
701    def re_encrypt(self, ciphertext_blob, destination_key_id,
702                   source_encryption_context=None,
703                   destination_encryption_context=None, grant_tokens=None):
704        """
705        Encrypts data on the server side with a new customer master
706        key without exposing the plaintext of the data on the client
707        side. The data is first decrypted and then encrypted. This
708        operation can also be used to change the encryption context of
709        a ciphertext.
710
711        :type ciphertext_blob: blob
712        :param ciphertext_blob: Ciphertext of the data to re-encrypt.
713
714        :type source_encryption_context: map
715        :param source_encryption_context: Encryption context used to encrypt
716            and decrypt the data specified in the `CiphertextBlob` parameter.
717
718        :type destination_key_id: string
719        :param destination_key_id: Key identifier of the key used to re-encrypt
720            the data.
721
722        :type destination_encryption_context: map
723        :param destination_encryption_context: Encryption context to be used
724            when the data is re-encrypted.
725
726        :type grant_tokens: list
727        :param grant_tokens: Grant tokens that identify the grants that have
728            permissions for the encryption and decryption process.
729
730        """
731        if not isinstance(ciphertext_blob, six.binary_type):
732            raise TypeError(
733                "Value of argument ``ciphertext_blob`` "
734                "must be of type %s." % six.binary_type)
735        ciphertext_blob = base64.b64encode(ciphertext_blob)
736        params = {
737            'CiphertextBlob': ciphertext_blob,
738            'DestinationKeyId': destination_key_id,
739        }
740        if source_encryption_context is not None:
741            params['SourceEncryptionContext'] = source_encryption_context
742        if destination_encryption_context is not None:
743            params['DestinationEncryptionContext'] = destination_encryption_context
744        if grant_tokens is not None:
745            params['GrantTokens'] = grant_tokens
746        response = self.make_request(action='ReEncrypt',
747                                     body=json.dumps(params))
748        if response.get('CiphertextBlob') is not None:
749            response['CiphertextBlob'] = base64.b64decode(
750                response['CiphertextBlob'].encode('utf-8'))
751        return response
752
753    def retire_grant(self, grant_token):
754        """
755        Retires a grant. You can retire a grant when you're done using
756        it to clean up. You should revoke a grant when you intend to
757        actively deny operations that depend on it.
758
759        :type grant_token: string
760        :param grant_token: Token that identifies the grant to be retired.
761
762        """
763        params = {'GrantToken': grant_token, }
764        return self.make_request(action='RetireGrant',
765                                 body=json.dumps(params))
766
767    def revoke_grant(self, key_id, grant_id):
768        """
769        Revokes a grant. You can revoke a grant to actively deny
770        operations that depend on it.
771
772        :type key_id: string
773        :param key_id: Unique identifier of the key associated with the grant.
774
775        :type grant_id: string
776        :param grant_id: Identifier of the grant to be revoked.
777
778        """
779        params = {'KeyId': key_id, 'GrantId': grant_id, }
780        return self.make_request(action='RevokeGrant',
781                                 body=json.dumps(params))
782
783    def update_key_description(self, key_id, description):
784        """
785
786
787        :type key_id: string
788        :param key_id:
789
790        :type description: string
791        :param description:
792
793        """
794        params = {'KeyId': key_id, 'Description': description, }
795        return self.make_request(action='UpdateKeyDescription',
796                                 body=json.dumps(params))
797
798    def make_request(self, action, body):
799        headers = {
800            'X-Amz-Target': '%s.%s' % (self.TargetPrefix, action),
801            'Host': self.region.endpoint,
802            'Content-Type': 'application/x-amz-json-1.1',
803            'Content-Length': str(len(body)),
804        }
805        http_request = self.build_base_http_request(
806            method='POST', path='/', auth_path='/', params={},
807            headers=headers, data=body)
808        response = self._mexe(http_request, sender=None,
809                              override_num_retries=10)
810        response_body = response.read().decode('utf-8')
811        boto.log.debug(response_body)
812        if response.status == 200:
813            if response_body:
814                return json.loads(response_body)
815        else:
816            json_body = json.loads(response_body)
817            fault_name = json_body.get('__type', None)
818            exception_class = self._faults.get(fault_name, self.ResponseError)
819            raise exception_class(response.status, response.reason,
820                                  body=json_body)
821
822