def test_encrypt_decrypt_chunk():
    enckey, signkey = blobxfer.generate_aes256_keys()
    assert len(enckey) == blobxfer._AES256_KEYLENGTH_BYTES
    assert len(signkey) == blobxfer._AES256_KEYLENGTH_BYTES

    # test random binary data, unaligned
    iv = os.urandom(16)
    plaindata = os.urandom(31)
    encdata = blobxfer.encrypt_chunk(
        enckey, signkey, plaindata, blobxfer._ENCRYPTION_MODE_CHUNKEDBLOB,
        pad=True)
    assert encdata != plaindata
    decdata = blobxfer.decrypt_chunk(
        enckey, signkey, encdata, blobxfer._ENCRYPTION_MODE_CHUNKEDBLOB,
        unpad=True)
    assert decdata == plaindata
    with pytest.raises(RuntimeError):
        badsig = base64.b64encode(b'0')
        blobxfer.decrypt_chunk(
            enckey, badsig, encdata, blobxfer._ENCRYPTION_MODE_CHUNKEDBLOB,
            unpad=True)

    encdata = blobxfer.encrypt_chunk(
        enckey, signkey, plaindata, blobxfer._ENCRYPTION_MODE_FULLBLOB,
        iv=iv, pad=True)
    decdata = blobxfer.decrypt_chunk(
        enckey, signkey, encdata, blobxfer._ENCRYPTION_MODE_FULLBLOB,
        iv=iv, unpad=True)
    assert decdata == plaindata

    # test random binary data aligned on boundary
    plaindata = os.urandom(32)
    encdata = blobxfer.encrypt_chunk(
        enckey, signkey, plaindata, blobxfer._ENCRYPTION_MODE_FULLBLOB,
        iv=iv, pad=True)
    assert encdata != plaindata
    decdata = blobxfer.decrypt_chunk(
        enckey, signkey, encdata, blobxfer._ENCRYPTION_MODE_FULLBLOB,
        iv=iv, unpad=True)
    assert decdata == plaindata

    # test text data
    plaindata = b'attack at dawn!'
    encdata = blobxfer.encrypt_chunk(
        enckey, signkey, plaindata, blobxfer._ENCRYPTION_MODE_FULLBLOB,
        iv, pad=True)
    assert encdata != plaindata
    decdata = blobxfer.decrypt_chunk(
        enckey, signkey, encdata, blobxfer._ENCRYPTION_MODE_FULLBLOB,
        iv, unpad=True)
    assert decdata == plaindata
def test_generate_xferspec_download(tmpdir):
    lpath = str(tmpdir.join('test.tmp'))
    args = MagicMock()
    args.rsakey = None
    args.storageaccount = 'blobep'
    args.container = 'container'
    args.storageaccountkey = 'saskey'
    args.chunksizebytes = 5
    args.timeout = None
    sa_in_queue = queue.PriorityQueue()

    session = requests.Session()
    adapter = requests_mock.Adapter()
    session.mount('mock', adapter)

    with requests_mock.mock() as m:
        m.head('mock://blobepcontainer/blob?saskey', headers={
            'content-length': '-1', 'content-md5': 'md5'})
        sbs = blobxfer.SasBlobService('mock://blobep', 'saskey', None)
        with pytest.raises(ValueError):
            blobxfer.generate_xferspec_download(
                sbs, args, sa_in_queue, lpath, 'blob', True,
                [None, None, None])
        assert sa_in_queue.qsize() == 0
        m.head('mock://blobepcontainer/blob?saskey', headers={
            'content-length': '6', 'content-md5': 'md5'})
        cl, nsops, md5, fd = blobxfer.generate_xferspec_download(
            sbs, args, sa_in_queue, lpath, 'blob', True, [None, None, None])
        assert sa_in_queue.qsize() == 2
        assert 2 == nsops
        assert 6 == cl
        assert 2 == nsops
        assert 'md5' == md5
        assert fd is not None
        fd.close()
        cl, nsops, md5, fd = blobxfer.generate_xferspec_download(
            sbs, args, sa_in_queue, lpath, 'blob', False, [None, None, None])
        assert 2 == nsops
        assert fd is None
        assert sa_in_queue.qsize() == 4
        with open(lpath, 'wt') as f:
            f.write('012345')
        m.head('mock://blobepcontainer/blob?saskey', headers={
            'content-length': '6', 'content-md5': '1qmpM8iq/FHlWsBmK25NSg=='})
        cl, nsops, md5, fd = blobxfer.generate_xferspec_download(
            sbs, args, sa_in_queue, lpath, 'blob', True, [None, None, None])
        assert nsops is None
        assert cl is None
        assert sa_in_queue.qsize() == 4

        sa_in_queue = queue.PriorityQueue()
        args.rsaprivatekey = _RSAKEY
        args.rsapublickey = None
        symkey, signkey = blobxfer.generate_aes256_keys()
        args.encmode = blobxfer._ENCRYPTION_MODE_CHUNKEDBLOB
        metajson = blobxfer.EncryptionMetadataJson(
            args, symkey, signkey, iv=b'0', encdata_signature=b'0',
            preencrypted_md5=None)
        encmeta = metajson.construct_metadata_json()
        goodencjson = json.loads(encmeta[blobxfer._ENCRYPTION_METADATA_NAME])
        goodauthjson = json.loads(
            encmeta[blobxfer._ENCRYPTION_METADATA_AUTH_NAME])
        metajson2 = blobxfer.EncryptionMetadataJson(
            args, None, None, None, None, None)
        metajson2.parse_metadata_json(
            'blob', args.rsaprivatekey, args.rsapublickey, encmeta)
        assert metajson2.symkey == symkey
        assert metajson2.signkey == signkey
        assert metajson2.encmode == args.encmode
        assert metajson2.chunksizebytes == args.chunksizebytes + \
            blobxfer._AES256CBC_HMACSHA256_OVERHEAD_BYTES + 1
        encjson = json.loads(encmeta[blobxfer._ENCRYPTION_METADATA_NAME])
        encjson[blobxfer._ENCRYPTION_METADATA_LAYOUT][
            blobxfer._ENCRYPTION_METADATA_CHUNKSTRUCTURE] = 'X'
        headers = {
            'content-length': '64',
            'content-md5': 'md5',
            'x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME:
            json.dumps(encjson),
            'x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_AUTH_NAME:
            json.dumps(goodauthjson),
        }
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(
                sbs, args, sa_in_queue, lpath, 'blob', False,
                [None, None, None])

        # switch to full blob mode tests
        args.encmode = blobxfer._ENCRYPTION_MODE_FULLBLOB
        metajson = blobxfer.EncryptionMetadataJson(
            args, symkey, signkey, iv=b'0', encdata_signature=b'0',
            preencrypted_md5=None)
        encmeta = metajson.construct_metadata_json()
        goodencjson = json.loads(encmeta[blobxfer._ENCRYPTION_METADATA_NAME])
        goodauthjson = json.loads(
            encmeta[blobxfer._ENCRYPTION_METADATA_AUTH_NAME])
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(goodencjson)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_AUTH_NAME] = \
            json.dumps(goodauthjson)

        encjson = copy.deepcopy(goodencjson)
        encjson[blobxfer._ENCRYPTION_METADATA_AGENT][
            blobxfer._ENCRYPTION_METADATA_PROTOCOL] = 'X'
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(encjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(
                sbs, args, sa_in_queue, lpath, 'blob', False,
                [None, None, None])

        encjson = copy.deepcopy(goodencjson)
        encjson[blobxfer._ENCRYPTION_METADATA_AGENT][
            blobxfer._ENCRYPTION_METADATA_ENCRYPTION_ALGORITHM] = 'X'
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(encjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(
                sbs, args, sa_in_queue, lpath, 'blob', False,
                [None, None, None])

        encjson = copy.deepcopy(goodencjson)
        encjson[blobxfer._ENCRYPTION_METADATA_INTEGRITY_AUTH][
            blobxfer._ENCRYPTION_METADATA_ALGORITHM] = 'X'
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(encjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(
                sbs, args, sa_in_queue, lpath, 'blob', False,
                [None, None, None])

        encjson = copy.deepcopy(goodencjson)
        encjson[blobxfer._ENCRYPTION_METADATA_WRAPPEDCONTENTKEY][
            blobxfer._ENCRYPTION_METADATA_ALGORITHM] = 'X'
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(encjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(
                sbs, args, sa_in_queue, lpath, 'blob', False,
                [None, None, None])

        authjson = copy.deepcopy(goodauthjson)
        authjson.pop(blobxfer._ENCRYPTION_METADATA_AUTH_METAAUTH, None)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(goodencjson)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_AUTH_NAME] = \
            json.dumps(authjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(
                sbs, args, sa_in_queue, lpath, 'blob', False,
                [None, None, None])

        authjson = copy.deepcopy(goodauthjson)
        authjson[blobxfer._ENCRYPTION_METADATA_AUTH_METAAUTH].pop(
            blobxfer._ENCRYPTION_METADATA_AUTH_ENCODING, None)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(goodencjson)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_AUTH_NAME] = \
            json.dumps(authjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(
                sbs, args, sa_in_queue, lpath, 'blob', False,
                [None, None, None])

        authjson = copy.deepcopy(goodauthjson)
        authjson[blobxfer._ENCRYPTION_METADATA_AUTH_METAAUTH][
            blobxfer._ENCRYPTION_METADATA_ALGORITHM] = 'X'
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(goodencjson)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_AUTH_NAME] = \
            json.dumps(authjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(
                sbs, args, sa_in_queue, lpath, 'blob', False,
                [None, None, None])

        authjson = copy.deepcopy(goodauthjson)
        authjson[blobxfer._ENCRYPTION_METADATA_AUTH_METAAUTH][
            blobxfer._ENCRYPTION_METADATA_MAC] = blobxfer.base64encode(b'X')
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(goodencjson)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_AUTH_NAME] = \
            json.dumps(authjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(
                sbs, args, sa_in_queue, lpath, 'blob', False,
                [None, None, None])

        args.chunksizebytes = 5
        metajson.chunksizebytes = args.chunksizebytes
        metajson.md5 = headers['content-md5']
        args.encmode = blobxfer._ENCRYPTION_MODE_FULLBLOB
        encjson = copy.deepcopy(goodencjson)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(encjson)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_AUTH_NAME] = \
            json.dumps(goodauthjson)
        hcl = int(headers['content-length'])
        cl, nsops, md5, fd = blobxfer.generate_xferspec_download(
            sbs, args, sa_in_queue, lpath, 'blob', False,
            [hcl, headers['content-md5'], metajson])
        assert hcl == cl
        calcops = hcl // args.chunksizebytes
        hclmod = hcl % args.chunksizebytes
        if hclmod > 0:
            calcops += 1
        assert calcops == nsops
        assert headers['content-md5'] == md5
        assert fd is None
        assert sa_in_queue.qsize() == nsops
        data = sa_in_queue.get()
        assert data is not None
def test_generate_xferspec_download(tmpdir):
    lpath = str(tmpdir.join('test.tmp'))
    args = MagicMock()
    args.rsakey = None
    args.storageaccount = 'blobep'
    args.container = 'container'
    args.storageaccountkey = 'saskey'
    args.chunksizebytes = 5
    args.timeout = None
    sa_in_queue = queue.PriorityQueue()

    session = requests.Session()
    adapter = requests_mock.Adapter()
    session.mount('mock', adapter)

    with requests_mock.mock() as m:
        m.head('mock://blobepcontainer/blob?saskey',
               headers={
                   'content-length': '-1',
                   'content-md5': 'md5'
               })
        sbs = blobxfer.SasBlobService('mock://blobep', 'saskey', None)
        with pytest.raises(ValueError):
            blobxfer.generate_xferspec_download(sbs, args, sa_in_queue, lpath,
                                                'blob', True,
                                                [None, None, None])
        assert sa_in_queue.qsize() == 0
        m.head('mock://blobepcontainer/blob?saskey',
               headers={
                   'content-length': '6',
                   'content-md5': 'md5'
               })
        cl, nsops, md5, fd = blobxfer.generate_xferspec_download(
            sbs, args, sa_in_queue, lpath, 'blob', True, [None, None, None])
        assert sa_in_queue.qsize() == 2
        assert 2 == nsops
        assert 6 == cl
        assert 2 == nsops
        assert 'md5' == md5
        assert fd is not None
        fd.close()
        cl, nsops, md5, fd = blobxfer.generate_xferspec_download(
            sbs, args, sa_in_queue, lpath, 'blob', False, [None, None, None])
        assert 2 == nsops
        assert fd is None
        assert sa_in_queue.qsize() == 4
        with open(lpath, 'wt') as f:
            f.write('012345')
        m.head('mock://blobepcontainer/blob?saskey',
               headers={
                   'content-length': '6',
                   'content-md5': '1qmpM8iq/FHlWsBmK25NSg=='
               })
        cl, nsops, md5, fd = blobxfer.generate_xferspec_download(
            sbs, args, sa_in_queue, lpath, 'blob', True, [None, None, None])
        assert nsops is None
        assert cl is None
        assert sa_in_queue.qsize() == 4

        sa_in_queue = queue.PriorityQueue()
        args.rsaprivatekey = _RSAKEY
        args.rsapublickey = None
        symkey, signkey = blobxfer.generate_aes256_keys()
        args.encmode = blobxfer._ENCRYPTION_MODE_CHUNKEDBLOB
        metajson = blobxfer.EncryptionMetadataJson(args,
                                                   symkey,
                                                   signkey,
                                                   iv=b'0',
                                                   encdata_signature=b'0',
                                                   preencrypted_md5=None)
        encmeta = metajson.construct_metadata_json()
        goodencjson = json.loads(encmeta[blobxfer._ENCRYPTION_METADATA_NAME])
        goodauthjson = json.loads(
            encmeta[blobxfer._ENCRYPTION_METADATA_AUTH_NAME])
        metajson2 = blobxfer.EncryptionMetadataJson(args, None, None, None,
                                                    None, None)
        metajson2.parse_metadata_json('blob', args.rsaprivatekey,
                                      args.rsapublickey, encmeta)
        assert metajson2.symkey == symkey
        assert metajson2.signkey == signkey
        assert metajson2.encmode == args.encmode
        assert metajson2.chunksizebytes == args.chunksizebytes + \
            blobxfer._AES256CBC_HMACSHA256_OVERHEAD_BYTES + 1
        encjson = json.loads(encmeta[blobxfer._ENCRYPTION_METADATA_NAME])
        encjson[blobxfer._ENCRYPTION_METADATA_LAYOUT][
            blobxfer._ENCRYPTION_METADATA_CHUNKSTRUCTURE] = 'X'
        headers = {
            'content-length':
            '64',
            'content-md5':
            'md5',
            'x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME:
            json.dumps(encjson),
            'x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_AUTH_NAME:
            json.dumps(goodauthjson),
        }
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(sbs, args, sa_in_queue, lpath,
                                                'blob', False,
                                                [None, None, None])

        # switch to full blob mode tests
        args.encmode = blobxfer._ENCRYPTION_MODE_FULLBLOB
        metajson = blobxfer.EncryptionMetadataJson(args,
                                                   symkey,
                                                   signkey,
                                                   iv=b'0',
                                                   encdata_signature=b'0',
                                                   preencrypted_md5=None)
        encmeta = metajson.construct_metadata_json()
        goodencjson = json.loads(encmeta[blobxfer._ENCRYPTION_METADATA_NAME])
        goodauthjson = json.loads(
            encmeta[blobxfer._ENCRYPTION_METADATA_AUTH_NAME])
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(goodencjson)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_AUTH_NAME] = \
            json.dumps(goodauthjson)

        encjson = copy.deepcopy(goodencjson)
        encjson[blobxfer._ENCRYPTION_METADATA_AGENT][
            blobxfer._ENCRYPTION_METADATA_PROTOCOL] = 'X'
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(encjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(sbs, args, sa_in_queue, lpath,
                                                'blob', False,
                                                [None, None, None])

        encjson = copy.deepcopy(goodencjson)
        encjson[blobxfer._ENCRYPTION_METADATA_AGENT][
            blobxfer._ENCRYPTION_METADATA_ENCRYPTION_ALGORITHM] = 'X'
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(encjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(sbs, args, sa_in_queue, lpath,
                                                'blob', False,
                                                [None, None, None])

        encjson = copy.deepcopy(goodencjson)
        encjson[blobxfer._ENCRYPTION_METADATA_INTEGRITY_AUTH][
            blobxfer._ENCRYPTION_METADATA_ALGORITHM] = 'X'
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(encjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(sbs, args, sa_in_queue, lpath,
                                                'blob', False,
                                                [None, None, None])

        encjson = copy.deepcopy(goodencjson)
        encjson[blobxfer._ENCRYPTION_METADATA_WRAPPEDCONTENTKEY][
            blobxfer._ENCRYPTION_METADATA_ALGORITHM] = 'X'
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(encjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(sbs, args, sa_in_queue, lpath,
                                                'blob', False,
                                                [None, None, None])

        authjson = copy.deepcopy(goodauthjson)
        authjson.pop(blobxfer._ENCRYPTION_METADATA_AUTH_METAAUTH, None)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(goodencjson)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_AUTH_NAME] = \
            json.dumps(authjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(sbs, args, sa_in_queue, lpath,
                                                'blob', False,
                                                [None, None, None])

        authjson = copy.deepcopy(goodauthjson)
        authjson[blobxfer._ENCRYPTION_METADATA_AUTH_METAAUTH].pop(
            blobxfer._ENCRYPTION_METADATA_AUTH_ENCODING, None)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(goodencjson)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_AUTH_NAME] = \
            json.dumps(authjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(sbs, args, sa_in_queue, lpath,
                                                'blob', False,
                                                [None, None, None])

        authjson = copy.deepcopy(goodauthjson)
        authjson[blobxfer._ENCRYPTION_METADATA_AUTH_METAAUTH][
            blobxfer._ENCRYPTION_METADATA_ALGORITHM] = 'X'
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(goodencjson)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_AUTH_NAME] = \
            json.dumps(authjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(sbs, args, sa_in_queue, lpath,
                                                'blob', False,
                                                [None, None, None])

        authjson = copy.deepcopy(goodauthjson)
        authjson[blobxfer._ENCRYPTION_METADATA_AUTH_METAAUTH][
            blobxfer._ENCRYPTION_METADATA_MAC] = blobxfer.base64encode(b'X')
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(goodencjson)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_AUTH_NAME] = \
            json.dumps(authjson)
        m.head('mock://blobepcontainer/blob?saskey', headers=headers)
        with pytest.raises(RuntimeError):
            blobxfer.generate_xferspec_download(sbs, args, sa_in_queue, lpath,
                                                'blob', False,
                                                [None, None, None])

        args.chunksizebytes = 5
        metajson.chunksizebytes = args.chunksizebytes
        metajson.md5 = headers['content-md5']
        args.encmode = blobxfer._ENCRYPTION_MODE_FULLBLOB
        encjson = copy.deepcopy(goodencjson)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_NAME] = \
            json.dumps(encjson)
        headers['x-ms-meta-' + blobxfer._ENCRYPTION_METADATA_AUTH_NAME] = \
            json.dumps(goodauthjson)
        hcl = int(headers['content-length'])
        cl, nsops, md5, fd = blobxfer.generate_xferspec_download(
            sbs, args, sa_in_queue, lpath, 'blob', False,
            [hcl, headers['content-md5'], metajson])
        assert hcl == cl
        calcops = hcl // args.chunksizebytes
        hclmod = hcl % args.chunksizebytes
        if hclmod > 0:
            calcops += 1
        assert calcops == nsops
        assert headers['content-md5'] == md5
        assert fd is None
        assert sa_in_queue.qsize() == nsops
        data = sa_in_queue.get()
        assert data is not None
def test_encrypt_decrypt_chunk():
    enckey, signkey = blobxfer.generate_aes256_keys()
    assert len(enckey) == blobxfer._AES256_KEYLENGTH_BYTES
    assert len(signkey) == blobxfer._AES256_KEYLENGTH_BYTES

    # test random binary data, unaligned
    iv = os.urandom(16)
    plaindata = os.urandom(31)
    encdata = blobxfer.encrypt_chunk(enckey,
                                     signkey,
                                     plaindata,
                                     blobxfer._ENCRYPTION_MODE_CHUNKEDBLOB,
                                     pad=True)
    assert encdata != plaindata
    decdata = blobxfer.decrypt_chunk(enckey,
                                     signkey,
                                     encdata,
                                     blobxfer._ENCRYPTION_MODE_CHUNKEDBLOB,
                                     unpad=True)
    assert decdata == plaindata
    with pytest.raises(RuntimeError):
        badsig = base64.b64encode(b'0')
        blobxfer.decrypt_chunk(enckey,
                               badsig,
                               encdata,
                               blobxfer._ENCRYPTION_MODE_CHUNKEDBLOB,
                               unpad=True)

    encdata = blobxfer.encrypt_chunk(enckey,
                                     signkey,
                                     plaindata,
                                     blobxfer._ENCRYPTION_MODE_FULLBLOB,
                                     iv=iv,
                                     pad=True)
    decdata = blobxfer.decrypt_chunk(enckey,
                                     signkey,
                                     encdata,
                                     blobxfer._ENCRYPTION_MODE_FULLBLOB,
                                     iv=iv,
                                     unpad=True)
    assert decdata == plaindata

    # test random binary data aligned on boundary
    plaindata = os.urandom(32)
    encdata = blobxfer.encrypt_chunk(enckey,
                                     signkey,
                                     plaindata,
                                     blobxfer._ENCRYPTION_MODE_FULLBLOB,
                                     iv=iv,
                                     pad=True)
    assert encdata != plaindata
    decdata = blobxfer.decrypt_chunk(enckey,
                                     signkey,
                                     encdata,
                                     blobxfer._ENCRYPTION_MODE_FULLBLOB,
                                     iv=iv,
                                     unpad=True)
    assert decdata == plaindata

    # test text data
    plaindata = b'attack at dawn!'
    encdata = blobxfer.encrypt_chunk(enckey,
                                     signkey,
                                     plaindata,
                                     blobxfer._ENCRYPTION_MODE_FULLBLOB,
                                     iv,
                                     pad=True)
    assert encdata != plaindata
    decdata = blobxfer.decrypt_chunk(enckey,
                                     signkey,
                                     encdata,
                                     blobxfer._ENCRYPTION_MODE_FULLBLOB,
                                     iv,
                                     unpad=True)
    assert decdata == plaindata