pythonaws-lambdaamazon-cognitosendgridamazon-cognito-triggers

aws-encryption-sdk-python decrypt error: '65 is not a valid SerializationVersion'


I'm trying to implement Cognito MFA using email. Following the documentation here: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-sms-sender.html

I'm using a Python lambda that gets triggered by Cognito when the user enters their user+pass. However, I'm getting the following error with the aws-encryption-sdk-python library:

Traceback (most recent call last):
  File "/var/task/aws_encryption_sdk/internal/formatting/deserialize.py", line 87, in _verified_version_from_id
    return SerializationVersion(version_id)
  File "/var/lang/lib/python3.9/enum.py", line 384, in __call__
    return cls.__new__(cls, value)
  File "/var/lang/lib/python3.9/enum.py", line 702, in __new__
    raise ve_exc
ValueError: 65 is not a valid SerializationVersion

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/var/task/aws_encryption_sdk/__init__.py", line 186, in decrypt
    plaintext = decryptor.read()
  File "/var/task/aws_encryption_sdk/streaming_client.py", line 250, in read
    self._prep_message()
  File "/var/task/aws_encryption_sdk/streaming_client.py", line 782, in _prep_message
    self._header, self.header_auth = self._read_header()
  File "/var/task/aws_encryption_sdk/streaming_client.py", line 797, in _read_header
    header, raw_header = deserialize_header(self.source_stream, self.config.max_encrypted_data_keys)
  File "/var/task/aws_encryption_sdk/internal/formatting/deserialize.py", line 336, in deserialize_header
    version = _verified_version_from_id(version_id)
  File "/var/task/aws_encryption_sdk/internal/formatting/deserialize.py", line 89, in _verified_version_from_id
    raise NotSupportedError("Unsupported version 
{}
".format(version_id), error)
aws_encryption_sdk.exceptions.NotSupportedError: ('Unsupported version 65', ValueError('65 is not a valid SerializationVersion'))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/var/task/aws_encryption_sdk/streaming_client.py", line 218, in __exit__
    self.close()
  File "/var/task/aws_encryption_sdk/streaming_client.py", line 985, in close
    raise SerializationError("Footer not read")
aws_encryption_sdk.exceptions.SerializationError: Footer not read

I'm not setting any version 65 anywhere. This is my code and I already verified the env vars are passed correctly:

import os
import json
import boto3
from botocore.exceptions import ClientError
from common.utils import *
from common.sqlUtils import *
from common.authentication import *
from common.authorization import *
from common.exceptions import GeneralException
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail
import aws_encryption_sdk
from aws_encryption_sdk.identifiers import CommitmentPolicy

# Configure the encryption SDK client with the KMS key from the environment variables.
awsEncryptionSdkClient = aws_encryption_sdk.EncryptionSDKClient(
    commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT
)
decryptionKeyArn = os.environ["cognitoCodeEncryptionKeyArn"]
kms_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(
    key_ids=[decryptionKeyArn]
)

def sendEmail(event, context):
    try:
        #TODO check if email is verified, user is confirmed, etc

        plaintextCode = None
        if "request" in event and "code" in event["request"]:
            print("Line 35: ", event)
            encryptedCode = event["request"]["code"]
            print("encryptedCode: ", encryptedCode)
            print("decryptionKeyArn ", decryptionKeyArn)
            plaintextCode, plaintextHeader = awsEncryptionSdkClient.decrypt(
                source=encryptedCode,
                key_provider=kms_key_provider
            )
            print("plaintextCode:", plaintextCode)

        subject = None
        html_content = None
        if plaintextCode is not None:
            subject = 'Code'
            html_content = f'<strong>Your code is: {plaintextCode}</strong>'

        message = Mail(
            from_email='no-reply@mydomain.com',
            to_emails=event["request"]["userAttributes"]["email"],
            subject=subject,
            html_content=html_content
        )

        sendgridSecret = getSecret(os.environ['cognitoSendgridSecretArn'])
        if isJson(sendgridSecret):
            sendgridSecret = json.loads(sendgridSecret)['SENDGRID_API_KEY']

        sg = SendGridAPIClient(sendgridSecret)
        response = sg.send(message)
        print(response.status_code)
        print(response.body)
        print(response.headers)

        #TODO check if email was sent successfully

        return json_response({"sendgrid message": response})

    except Exception as e:
        httpCode = 500
        if isinstance(e, GeneralException):
            httpCode = e.httpCode
        print(str(e))
        return json_response({"message": str(e)}, httpCode)

Any pointers would be appreciated!


Solution

  • When the cipher text code is provided to the lambda function, it is base64 encoded. When passing the encrypted code in here, it needs to be base64 decoded and converted into binary.

    import base64
    
    ...
    
    plaintextCode, plaintextHeader = awsEncryptionSdkClient.decrypt(
        source=base64.b64decode(encryptedCode),
        key_provider=kms_key_provider
    )