Commit 429ddd1f authored by Rémi Huguet's avatar Rémi Huguet
Browse files

feat: add expected key function to AES ready-to-use selection functions.

parent 7351d0b2
Loading
Loading
Loading
Loading
Loading
+56 −11
Original line number Diff line number Diff line
@@ -28,6 +28,14 @@ def _delta_last_rounds(data, guesses):
    ).swapaxes(0, 1)


def _first_key(key):
    return aes.key_schedule(key)[0]


def _last_key(key):
    return aes.key_schedule(key)[-1]


class FirstAddRoundKey:
    """Build an attack selection function which computes intermediate values after AES encrypt round key operation at first round, for guesses values.

@@ -39,9 +47,15 @@ class FirstAddRoundKey:

    """

    def __new__(cls, guesses=_np.arange(256, dtype='uint8'), words=None, plaintext_tag='plaintext'):
        return _decorated_selection_function(_AttackSelectionFunctionWrapped, _add_round_key, words=words, guesses=guesses, target_tag=plaintext_tag)
        # self.target_tag = plaintext_tag
    def __new__(cls, guesses=_np.arange(256, dtype='uint8'), words=None, plaintext_tag='plaintext', key_tag='key'):
        return _decorated_selection_function(
            _AttackSelectionFunctionWrapped,
            _add_round_key,
            expected_key_function=_first_key,
            words=words,
            guesses=guesses,
            target_tag=plaintext_tag,
            key_tag=key_tag)


class LastAddRoundKey:
@@ -55,8 +69,15 @@ class LastAddRoundKey:

    """

    def __new__(cls, guesses=_np.arange(256, dtype='uint8'), words=None, ciphertext_tag='ciphertext'):
        return _decorated_selection_function(_AttackSelectionFunctionWrapped, _add_round_key, words=words, guesses=guesses, target_tag=ciphertext_tag)
    def __new__(cls, guesses=_np.arange(256, dtype='uint8'), words=None, ciphertext_tag='ciphertext', key_tag='key'):
        return _decorated_selection_function(
            _AttackSelectionFunctionWrapped,
            _add_round_key,
            expected_key_function=_last_key,
            words=words, guesses=guesses,
            target_tag=ciphertext_tag,
            key_tag=key_tag
        )


class FirstSubBytes:
@@ -70,8 +91,16 @@ class FirstSubBytes:

    """

    def __new__(cls, guesses=_np.arange(256, dtype='uint8'), words=None, plaintext_tag='plaintext'):
        return _decorated_selection_function(_AttackSelectionFunctionWrapped, _sub_bytes, words=words, guesses=guesses, target_tag=plaintext_tag)
    def __new__(cls, guesses=_np.arange(256, dtype='uint8'), words=None, plaintext_tag='plaintext', key_tag='key'):
        return _decorated_selection_function(
            _AttackSelectionFunctionWrapped,
            _sub_bytes,
            expected_key_function=_first_key,
            words=words,
            guesses=guesses,
            target_tag=plaintext_tag,
            key_tag=key_tag
        )


class LastSubBytes:
@@ -85,8 +114,16 @@ class LastSubBytes:

    """

    def __new__(cls, guesses=_np.arange(256, dtype='uint8'), words=None, ciphertext_tag='ciphertext'):
        return _decorated_selection_function(_AttackSelectionFunctionWrapped, _inv_sub_bytes, words=words, guesses=guesses, target_tag=ciphertext_tag)
    def __new__(cls, guesses=_np.arange(256, dtype='uint8'), words=None, ciphertext_tag='ciphertext', key_tag='key'):
        return _decorated_selection_function(
            _AttackSelectionFunctionWrapped,
            _inv_sub_bytes,
            expected_key_function=_last_key,
            words=words,
            guesses=guesses,
            target_tag=ciphertext_tag,
            key_tag=key_tag
        )


class DeltaRLastRounds:
@@ -99,5 +136,13 @@ class DeltaRLastRounds:
            values from the metadata dict when selection function is called.
    """

    def __new__(cls, guesses=_np.arange(256, dtype='uint8'), words=None, ciphertext_tag='ciphertext'):
        return _decorated_selection_function(_AttackSelectionFunctionWrapped, _delta_last_rounds, words=words, guesses=guesses, target_tag=ciphertext_tag)
    def __new__(cls, guesses=_np.arange(256, dtype='uint8'), words=None, ciphertext_tag='ciphertext', key_tag='key'):
        return _decorated_selection_function(
            _AttackSelectionFunctionWrapped,
            _delta_last_rounds,
            expected_key_function=_last_key,
            words=words,
            guesses=guesses,
            target_tag=ciphertext_tag,
            key_tag=key_tag
        )
+11 −3
Original line number Diff line number Diff line
@@ -80,7 +80,9 @@ class _AttackSelectionFunction(SelectionFunction):
                try:
                    kargs[name] = kwargs[name]
                except KeyError as e:
                    raise SelectionFunctionError(f'Missing key values in metadata {list(kwargs.keys())} for expected argument {e} of compute expected function {self}.')
                    raise SelectionFunctionError(
                        f'Missing key values in metadata {list(kwargs.keys())} for expected argument {e} of compute expected function {self}.'
                    )

            return self.expected_key_function(**kargs)

@@ -137,11 +139,17 @@ def reverse_selection_function(function=None, words=None):

class _AttackSelectionFunctionWrapped(_AttackSelectionFunction):

    def __init__(self, function, guesses, words, target_tag=None, target_name='data'):
        super().__init__(function=function, words=words, guesses=guesses)
    def __init__(self, function, guesses, words, expected_key_function=None, target_tag=None, key_tag=None, target_name='data', key_name='key'):
        super().__init__(function=function, words=words, guesses=guesses, expected_key_function=expected_key_function)
        self.target_tag = target_tag
        self.target_name = target_name
        self.key_name = key_name
        self.key_tag = key_tag

    def __call__(self, **kwargs):
        kwargs[self.target_name] = kwargs[self.target_tag]
        return super().__call__(**kwargs)

    def compute_expected_key(self, **kwargs):
        kwargs[self.key_name] = kwargs[self.key_tag]
        return super().compute_expected_key(**kwargs)
+99 −10
Original line number Diff line number Diff line
@@ -8,29 +8,38 @@ def test_aes_encrypt_first_round_key_with_default_arguments():
    assert sf.guesses.tolist() == list(range(256))
    assert sf.words is Ellipsis
    assert sf.target_tag == 'plaintext'
    assert sf.key_tag == 'key'
    assert isinstance(sf, selection_functions.SelectionFunction)
    data = np.random.randint(0, 255, (10, 16), dtype='uint8')
    expected = np.empty((10, 256, 16), dtype='uint8')
    for i in np.arange(256, dtype='uint8'):
        expected[:, i, :] = np.bitwise_xor(data, i)
    assert np.array_equal(expected, sf(plaintext=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[0]
    assert np.array_equal(expected_key, sf.compute_expected_key(key=master_key))


def test_aes_encrypt_first_round_key_with_alternative_args():
    sf = aes.selection_functions.encrypt.FirstAddRoundKey(
        plaintext_tag='plain',
        words=6,
        guesses=np.arange(16, dtype='uint8')
        guesses=np.arange(16, dtype='uint8'),
        key_tag='thekey'
    )
    assert sf.guesses.tolist() == list(range(16))
    assert sf.words == 6
    assert sf.target_tag == 'plain'
    assert sf.key_tag == 'thekey'
    assert isinstance(sf, selection_functions.SelectionFunction)
    data = np.random.randint(0, 255, (10, 16), dtype='uint8')
    expected = np.empty((10, 16, 16), dtype='uint8')
    for i in np.arange(16, dtype='uint8'):
        expected[:, i, :] = np.bitwise_xor(data, i)
    assert np.array_equal(expected[:, :, 6], sf(plain=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[0]
    assert np.array_equal(expected_key, sf.compute_expected_key(thekey=master_key))


def test_aes_encrypt_last_round_key_with_default_arguments():
@@ -38,29 +47,38 @@ def test_aes_encrypt_last_round_key_with_default_arguments():
    assert sf.guesses.tolist() == list(range(256))
    assert sf.words is Ellipsis
    assert sf.target_tag == 'ciphertext'
    assert sf.key_tag == 'key'
    assert isinstance(sf, selection_functions.SelectionFunction)
    data = np.random.randint(0, 255, (10, 16), dtype='uint8')
    expected = np.empty((10, 256, 16), dtype='uint8')
    for i in np.arange(256, dtype='uint8'):
        expected[:, i, :] = np.bitwise_xor(data, i)
    assert np.array_equal(expected, sf(ciphertext=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[-1]
    assert np.array_equal(expected_key, sf.compute_expected_key(key=master_key))


def test_aes_encrypt_last_round_key_with_alternative_args():
    sf = aes.selection_functions.encrypt.LastAddRoundKey(
        ciphertext_tag='nop',
        words=6,
        guesses=np.arange(16, dtype='uint8')
        guesses=np.arange(16, dtype='uint8'),
        key_tag='thekey'
    )
    assert sf.guesses.tolist() == list(range(16))
    assert sf.words == 6
    assert sf.target_tag == 'nop'
    assert sf.key_tag == 'thekey'
    assert isinstance(sf, selection_functions.SelectionFunction)
    data = np.random.randint(0, 255, (10, 16), dtype='uint8')
    expected = np.empty((10, 16, 16), dtype='uint8')
    for i in np.arange(16, dtype='uint8'):
        expected[:, i, :] = np.bitwise_xor(data, i)
    assert np.array_equal(expected[:, :, 6], sf(nop=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[-1]
    assert np.array_equal(expected_key, sf.compute_expected_key(thekey=master_key))


def test_aes_encrypt_first_sub_bytes_with_default_arguments():
@@ -68,6 +86,7 @@ def test_aes_encrypt_first_sub_bytes_with_default_arguments():
    assert sf.guesses.tolist() == list(range(256))
    assert sf.words is Ellipsis
    assert sf.target_tag == 'plaintext'
    assert sf.key_tag == 'key'
    assert isinstance(sf, selection_functions.SelectionFunction)
    data = np.random.randint(0, 255, (10, 16), dtype='uint8')
    expected = np.empty((10, 256, 16), dtype='uint8')
@@ -75,17 +94,22 @@ def test_aes_encrypt_first_sub_bytes_with_default_arguments():
        expected[:, i, :] = np.bitwise_xor(data, i)
    expected = aes.sub_bytes(expected)
    assert np.array_equal(expected, sf(plaintext=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[0]
    assert np.array_equal(expected_key, sf.compute_expected_key(key=master_key))


def test_aes_encrypt_first_sub_bytes_with_alternative_args():
    sf = aes.selection_functions.encrypt.FirstSubBytes(
        plaintext_tag='foo',
        words=slice(2, 8),
        guesses=np.arange(16, dtype='uint8')
        guesses=np.arange(16, dtype='uint8'),
        key_tag='thekey'
    )
    assert sf.guesses.tolist() == list(range(16))
    assert sf.words == slice(2, 8, None)
    assert sf.target_tag == 'foo'
    assert sf.key_tag == 'thekey'
    assert isinstance(sf, selection_functions.SelectionFunction)
    data = np.random.randint(0, 255, (10, 16), dtype='uint8')
    expected = np.empty((10, 16, 16), dtype='uint8')
@@ -93,6 +117,9 @@ def test_aes_encrypt_first_sub_bytes_with_alternative_args():
        expected[:, i, :] = np.bitwise_xor(data, i)
    expected = aes.sub_bytes(expected)
    assert np.array_equal(expected[:, :, slice(2, 8)], sf(foo=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[0]
    assert np.array_equal(expected_key, sf.compute_expected_key(thekey=master_key))


def test_aes_encrypt_last_sub_bytes_with_default_arguments():
@@ -100,6 +127,7 @@ def test_aes_encrypt_last_sub_bytes_with_default_arguments():
    assert sf.guesses.tolist() == list(range(256))
    assert sf.words is Ellipsis
    assert sf.target_tag == 'ciphertext'
    assert sf.key_tag == 'key'
    assert isinstance(sf, selection_functions.SelectionFunction)
    data = np.random.randint(0, 255, (10, 16), dtype='uint8')
    expected = np.empty((10, 256, 16), dtype='uint8')
@@ -107,17 +135,22 @@ def test_aes_encrypt_last_sub_bytes_with_default_arguments():
        expected[:, i, :] = np.bitwise_xor(data, i)
    expected = aes.inv_sub_bytes(expected)
    assert np.array_equal(expected, sf(ciphertext=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[-1]
    assert np.array_equal(expected_key, sf.compute_expected_key(key=master_key))


def test_aes_encrypt_last_sub_bytes_with_alternative_args():
    sf = aes.selection_functions.encrypt.LastSubBytes(
        ciphertext_tag='foo',
        words=slice(2, 8),
        guesses=np.arange(16, dtype='uint8')
        guesses=np.arange(16, dtype='uint8'),
        key_tag='thekey'
    )
    assert sf.guesses.tolist() == list(range(16))
    assert sf.words == slice(2, 8, None)
    assert sf.target_tag == 'foo'
    assert sf.key_tag == 'thekey'
    assert isinstance(sf, selection_functions.SelectionFunction)
    data = np.random.randint(0, 255, (10, 16), dtype='uint8')
    expected = np.empty((10, 16, 16), dtype='uint8')
@@ -125,6 +158,9 @@ def test_aes_encrypt_last_sub_bytes_with_alternative_args():
        expected[:, i, :] = np.bitwise_xor(data, i)
    expected = aes.inv_sub_bytes(expected)
    assert np.array_equal(expected[:, :, slice(2, 8)], sf(foo=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[-1]
    assert np.array_equal(expected_key, sf.compute_expected_key(thekey=master_key))


def test_aes_encrypt_delta_r_last_rounds_with_default_arguments():
@@ -132,6 +168,7 @@ def test_aes_encrypt_delta_r_last_rounds_with_default_arguments():
    assert sf.guesses.tolist() == list(range(256))
    assert sf.words is Ellipsis
    assert sf.target_tag == 'ciphertext'
    assert sf.key_tag == 'key'
    assert isinstance(sf, selection_functions.SelectionFunction)
    data = np.random.randint(0, 255, (10, 16), dtype='uint8')
    expected = np.empty((10, 256, 16), dtype='uint8')
@@ -140,13 +177,17 @@ def test_aes_encrypt_delta_r_last_rounds_with_default_arguments():
    s = aes.inv_sub_bytes(state=expected)
    expected = np.bitwise_xor(aes.shift_rows(data), s.swapaxes(0, 1)).swapaxes(0, 1)
    assert np.array_equal(expected, sf(ciphertext=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[-1]
    assert np.array_equal(expected_key, sf.compute_expected_key(key=master_key))


def test_aes_encrypt_delta_r_last_rounds_with_alternative_args():
    sf = aes.selection_functions.encrypt.DeltaRLastRounds(
        ciphertext_tag='foo',
        words=slice(2, 8),
        guesses=np.arange(16, dtype='uint8')
        guesses=np.arange(16, dtype='uint8'),
        key_tag='thekey'
    )
    assert sf.guesses.tolist() == list(range(16))
    assert sf.words == slice(2, 8, None)
@@ -159,6 +200,9 @@ def test_aes_encrypt_delta_r_last_rounds_with_alternative_args():
    s = aes.inv_sub_bytes(state=expected)
    expected = np.bitwise_xor(aes.shift_rows(data), s.swapaxes(0, 1)).swapaxes(0, 1)
    assert np.array_equal(expected[:, :, slice(2, 8)], sf(foo=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[-1]
    assert np.array_equal(expected_key, sf.compute_expected_key(thekey=master_key))


def test_aes_decrypt_first_round_key_with_default_arguments():
@@ -166,29 +210,38 @@ def test_aes_decrypt_first_round_key_with_default_arguments():
    assert sf.guesses.tolist() == list(range(256))
    assert sf.words is Ellipsis
    assert sf.target_tag == 'ciphertext'
    assert sf.key_tag == 'key'
    assert isinstance(sf, selection_functions.SelectionFunction)
    data = np.random.randint(0, 255, (10, 16), dtype='uint8')
    expected = np.empty((10, 256, 16), dtype='uint8')
    for i in np.arange(256, dtype='uint8'):
        expected[:, i, :] = np.bitwise_xor(data, i)
    assert np.array_equal(expected, sf(ciphertext=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[-1]
    assert np.array_equal(expected_key, sf.compute_expected_key(key=master_key))


def test_aes_decrypt_first_round_key_with_alternative_args():
    sf = aes.selection_functions.decrypt.FirstAddRoundKey(
        ciphertext_tag='cif',
        words=6,
        guesses=np.arange(16, dtype='uint8')
        guesses=np.arange(16, dtype='uint8'),
        key_tag='thekey'
    )
    assert sf.guesses.tolist() == list(range(16))
    assert sf.words == 6
    assert sf.target_tag == 'cif'
    assert sf.key_tag == 'thekey'
    assert isinstance(sf, selection_functions.SelectionFunction)
    data = np.random.randint(0, 255, (10, 16), dtype='uint8')
    expected = np.empty((10, 16, 16), dtype='uint8')
    for i in np.arange(16, dtype='uint8'):
        expected[:, i, :] = np.bitwise_xor(data, i)
    assert np.array_equal(expected[:, :, 6], sf(cif=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[-1]
    assert np.array_equal(expected_key, sf.compute_expected_key(thekey=master_key))


def test_aes_decrypt_last_round_key_with_default_arguments():
@@ -202,13 +255,18 @@ def test_aes_decrypt_last_round_key_with_default_arguments():
    for i in np.arange(256, dtype='uint8'):
        expected[:, i, :] = np.bitwise_xor(data, i)
    assert np.array_equal(expected, sf(plaintext=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[0]
    assert np.array_equal(expected_key, sf.compute_expected_key(key=master_key))
    assert sf.key_tag == 'key'


def test_aes_decrypt_last_round_key_with_alternative_args():
    sf = aes.selection_functions.decrypt.LastAddRoundKey(
        plaintext_tag='nop',
        words=6,
        guesses=np.arange(16, dtype='uint8')
        guesses=np.arange(16, dtype='uint8'),
        key_tag='thekey'
    )
    assert sf.guesses.tolist() == list(range(16))
    assert sf.words == 6
@@ -219,6 +277,10 @@ def test_aes_decrypt_last_round_key_with_alternative_args():
    for i in np.arange(16, dtype='uint8'):
        expected[:, i, :] = np.bitwise_xor(data, i)
    assert np.array_equal(expected[:, :, 6], sf(nop=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[0]
    assert np.array_equal(expected_key, sf.compute_expected_key(thekey=master_key))
    assert sf.key_tag == 'thekey'


def test_aes_decrypt_first_sub_bytes_with_default_arguments():
@@ -233,13 +295,18 @@ def test_aes_decrypt_first_sub_bytes_with_default_arguments():
        expected[:, i, :] = np.bitwise_xor(data, i)
    expected = aes.inv_sub_bytes(expected)
    assert np.array_equal(expected, sf(ciphertext=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[-1]
    assert np.array_equal(expected_key, sf.compute_expected_key(key=master_key))
    assert sf.key_tag == 'key'


def test_aes_decrypt_first_sub_bytes_with_alternative_args():
    sf = aes.selection_functions.decrypt.FirstSubBytes(
        ciphertext_tag='foo',
        words=slice(2, 8),
        guesses=np.arange(16, dtype='uint8')
        guesses=np.arange(16, dtype='uint8'),
        key_tag='thekey'
    )
    assert sf.guesses.tolist() == list(range(16))
    assert sf.words == slice(2, 8, None)
@@ -251,6 +318,10 @@ def test_aes_decrypt_first_sub_bytes_with_alternative_args():
        expected[:, i, :] = np.bitwise_xor(data, i)
    expected = aes.inv_sub_bytes(expected)
    assert np.array_equal(expected[:, :, slice(2, 8)], sf(foo=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[-1]
    assert np.array_equal(expected_key, sf.compute_expected_key(thekey=master_key))
    assert sf.key_tag == 'thekey'


def test_aes_decrypt_last_sub_bytes_with_default_arguments():
@@ -265,13 +336,18 @@ def test_aes_decrypt_last_sub_bytes_with_default_arguments():
        expected[:, i, :] = np.bitwise_xor(data, i)
    expected = aes.sub_bytes(expected)
    assert np.array_equal(expected, sf(plaintext=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[0]
    assert np.array_equal(expected_key, sf.compute_expected_key(key=master_key))
    assert sf.key_tag == 'key'


def test_aes_decrypt_last_sub_bytes_with_alternative_args():
    sf = aes.selection_functions.decrypt.LastSubBytes(
        plaintext_tag='foo',
        words=slice(2, 8),
        guesses=np.arange(16, dtype='uint8')
        guesses=np.arange(16, dtype='uint8'),
        key_tag='thekey'
    )
    assert sf.guesses.tolist() == list(range(16))
    assert sf.words == slice(2, 8, None)
@@ -283,6 +359,10 @@ def test_aes_decrypt_last_sub_bytes_with_alternative_args():
        expected[:, i, :] = np.bitwise_xor(data, i)
    expected = aes.sub_bytes(expected)
    assert np.array_equal(expected[:, :, slice(2, 8)], sf(foo=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[0]
    assert np.array_equal(expected_key, sf.compute_expected_key(thekey=master_key))
    assert sf.key_tag == 'thekey'


def test_aes_decrypt_delta_r_first_rounds_with_default_arguments():
@@ -298,13 +378,18 @@ def test_aes_decrypt_delta_r_first_rounds_with_default_arguments():
    s = aes.inv_sub_bytes(state=expected)
    expected = np.bitwise_xor(aes.shift_rows(data), s.swapaxes(0, 1)).swapaxes(0, 1)
    assert np.array_equal(expected, sf(ciphertext=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[-1]
    assert np.array_equal(expected_key, sf.compute_expected_key(key=master_key))
    assert sf.key_tag == 'key'


def test_aes_decrypt_delta_r_first_rounds_with_alternative_args():
    sf = aes.selection_functions.decrypt.DeltaRFirstRounds(
        ciphertext_tag='foo',
        words=slice(2, 8),
        guesses=np.arange(16, dtype='uint8')
        guesses=np.arange(16, dtype='uint8'),
        key_tag='thekey'
    )
    assert sf.guesses.tolist() == list(range(16))
    assert sf.words == slice(2, 8, None)
@@ -317,3 +402,7 @@ def test_aes_decrypt_delta_r_first_rounds_with_alternative_args():
    s = aes.inv_sub_bytes(state=expected)
    expected = np.bitwise_xor(aes.shift_rows(data), s.swapaxes(0, 1)).swapaxes(0, 1)
    assert np.array_equal(expected[:, :, slice(2, 8)], sf(foo=data))
    master_key = np.random.randint(0, 255, (16,), dtype='uint8')
    expected_key = aes.key_schedule(master_key)[-1]
    assert np.array_equal(expected_key, sf.compute_expected_key(thekey=master_key))
    assert sf.key_tag == 'thekey'