cascada.bitvector.secondaryop module
Provide additional (secondary) bit-vector operators.
The bit-wise majority function. |
|
The bit-wise conditional (If) function. |
|
Count the number of 1's in the bit-vector. |
|
Reverse the bit-vector. |
|
Count the number of 1's of two bit-vectors. |
|
Count the number of 1's of three bit-vectors. |
|
Compute the difference of the hamming weight of two words. |
|
Return a bit-vector with the leading zeros set to one and the rest to zero. |
|
Base class for LUT-based operations. |
|
Base class for matrix-vector products. |
- class cascada.bitvector.secondaryop.BvMaj(**kwargs)[source]
Bases:
cascada.bitvector.operation.SecondaryOperation
The bit-wise majority function.
The bit-wise majority function
BvMaj(x, y, z)
is defined for each biti
as the most common bit in[x[i], y[i], z[i]]
.Since
BvMaj
is aSecondaryOperation
, by default it is fully evaluated (i.e.,Operation.eval
is used) when all the inputs are constants (see alsocontext.SecondaryOperationEvaluation
). On the other hand,pre_eval
is always called in the evaluation (even with symbolic inputs).>>> from cascada.bitvector.core import Constant, Variable >>> from cascada.bitvector.secondaryop import BvMaj >>> BvMaj(Constant(0b000, 3), Constant(0b001, 3), Constant(0b011, 3)) 0b001 >>> BvMaj(Variable("x", 4), Variable("y", 4), Variable("z", 4)) BvMaj(x, y, z) >>> BvMaj(Variable("x", 4), Variable("x", 4), Variable("z", 4)) x >>> BvMaj(Variable("x", 4), Variable("y", 4), Variable("z", 4)).doit() (x & y) | (x & z) | (y & z)
- classmethod condition(x, y, z)[source]
Check if the operands verify the restrictions of the operator.
- classmethod pre_eval(x, y, z)[source]
Evaluate the operator before
Operation.eval
.This is an internal method that assumes the list
args
has been parsed.
- class cascada.bitvector.secondaryop.BvIf(**kwargs)[source]
Bases:
cascada.bitvector.operation.SecondaryOperation
The bit-wise conditional (If) function.
The bit-wise conditional function
BvIf(x, y, z)
is defined for each biti
asy[i]
ifx[i]=1
otherwisez[i]
.>>> from cascada.bitvector.core import Constant, Variable >>> from cascada.bitvector.secondaryop import BvIf >>> BvIf(Constant(0b01, 2), Constant(0b00, 2), Constant(0b11, 2)) 0b10 >>> BvIf(Variable("x", 4), Variable("y", 4), Variable("z", 4)) BvIf(x, y, z) >>> BvIf(0, Variable("y", 4), Variable("z", 4)) z >>> BvIf(Variable("x", 4), Variable("y", 4), Variable("z", 4)).doit() (x & y) | (~x & z)
- classmethod condition(x, y, z)[source]
Check if the operands verify the restrictions of the operator.
- classmethod pre_eval(x, y, z)[source]
Evaluate the operator before
Operation.eval
.This is an internal method that assumes the list
args
has been parsed.
- class cascada.bitvector.secondaryop.PopCount(**kwargs)[source]
Bases:
cascada.bitvector.operation.SecondaryOperation
Count the number of 1’s in the bit-vector.
This operation is also known as the hamming weight of a bit-vector.
>>> from cascada.bitvector.core import Constant, Variable >>> from cascada.bitvector.secondaryop import PopCount >>> PopCount(Constant(0b1010, 4)) 0b010 >>> PopCount(Constant(0b101, 3)) 0b10 >>> PopCount(Variable("x", 4)).doit() (((x - ((x >> 0x1) & 0x5)) & 0x3) + (((x - ((x >> 0x1) & 0x5)) >> 0x2) & 0x3))[2:] >>> PopCount(Variable("x", 3)).doit() (0b0 :: (x[0])) + (0b0 :: (x[1])) + (0b0 :: (x[2]))
- class cascada.bitvector.secondaryop.Reverse(**kwargs)[source]
Bases:
cascada.bitvector.operation.SecondaryOperation
Reverse the bit-vector.
>>> from cascada.bitvector.core import Constant, Variable >>> from cascada.bitvector.secondaryop import PopCount >>> Reverse(Constant(0b1010, 4)) 0x5 >>> Reverse(Constant(0b001, 3)) 0b100 >>> Reverse(Variable("x", 4)).doit() (((((x & 0x5) << 0x1) | ((x >> 0x1) & 0x5)) & 0x3) << 0x2) | (((((x & 0x5) << 0x1) | ((x >> 0x1) & 0x5)) >> 0x2) & 0x3) >>> Reverse(Variable("x", 3)).doit() (x[0]) :: (x[1]) :: (x[2])
- class cascada.bitvector.secondaryop.PopCountSum2(**kwargs)[source]
Bases:
cascada.bitvector.operation.SecondaryOperation
Count the number of 1’s of two bit-vectors.
>>> from cascada.bitvector.core import Constant, Variable >>> from cascada.bitvector.secondaryop import PopCountSum2 >>> PopCountSum2(Constant(0b000, 3), Constant(0b001, 3)) 0b001 >>> PopCountSum2(~Constant(0, 15), ~Constant(0, 15)) 0b11110 >>> PopCountSum2(Variable("x", 4), Variable("y", 4)).doit() ((x - ((x >> 0x1) & 0x5)) & 0x3) + (((x - ((x >> 0x1) & 0x5)) >> 0x2) & 0x3) + ((y - ((y >> 0x1) & 0x5)) & 0x3) + (((y - ((y >> 0x1) & 0x5)) >> 0x2) & 0x3) >>> PopCountSum2(Variable("x", 3), Variable("y", 3)).doit() (0b0 :: ((0b0 :: (x[0])) + (0b0 :: (x[1])) + (0b0 :: (x[2])))) + (0b0 :: ((0b0 :: (y[0])) + (0b0 :: (y[1])) + (0b0 :: (y[2]))))
- class cascada.bitvector.secondaryop.PopCountSum3(**kwargs)[source]
Bases:
cascada.bitvector.operation.SecondaryOperation
Count the number of 1’s of three bit-vectors.
>>> from cascada.bitvector.core import Constant, Variable >>> from cascada.bitvector.secondaryop import PopCountSum3 >>> PopCountSum3(Constant(0b000, 3), Constant(0b001, 3), Constant(0b011, 3)) 0x3 >>> PopCountSum3(Variable("x", 4), Variable("y", 4), Variable("z", 4)).doit() ((x - ((x >> 0x1) & 0x5)) & 0x3) + (((x - ((x >> 0x1) & 0x5)) >> 0x2) & 0x3) + ((y - ((y >> 0x1) & 0x5)) & 0x3) + (((y - ((y >> 0x1) & 0x5)) >> 0x2) & 0x3) + ((z - ((z >> 0x1) & 0x5)) & 0x3) + (((z - ((z >> 0x1) & 0x5)) >> 0x2) & 0x3) >>> PopCountSum3(Variable("x", 3), Variable("y", 3), Constant(0, 3)).doit() (0b00 :: ((0b0 :: (x[0])) + (0b0 :: (x[1])) + (0b0 :: (x[2])))) + (0b00 :: ((0b0 :: (y[0])) + (0b0 :: (y[1])) + (0b0 :: (y[2]))))
- class cascada.bitvector.secondaryop.PopCountDiff(**kwargs)[source]
Bases:
cascada.bitvector.operation.SecondaryOperation
Compute the difference of the hamming weight of two words.
>>> from cascada.bitvector.core import Constant, Variable >>> from cascada.bitvector.secondaryop import PopCountDiff >>> PopCountDiff(Constant(0b011, 3), Constant(0b100, 3)) 0b01 >>> PopCountDiff(Variable("x", 4), Variable("y", 4)).doit() ((((x - ((x >> 0x1) & 0x5)) & 0x3) + (((x - ((x >> 0x1) & 0x5)) >> 0x2) & 0x3) + ((~y - ((~y >> 0x1) & 0x5)) & 0x3) + (((~y - ((~y >> 0x1) & 0x5)) >> 0x2) & 0x3)) - 0x4)[2:] >>> PopCountDiff(Variable("x", 3), Variable("y", 3)).doit() ((0b0 :: (x[0])) + (0b0 :: (x[1])) + (0b0 :: (x[2]))) - ((0b0 :: (y[0])) + (0b0 :: (y[1])) + (0b0 :: (y[2])))
- class cascada.bitvector.secondaryop.LeadingZeros(**kwargs)[source]
Bases:
cascada.bitvector.operation.SecondaryOperation
Return a bit-vector with the leading zeros set to one and the rest to zero.
>>> from cascada.bitvector.core import Constant, Variable >>> from cascada.bitvector.secondaryop import PopCount >>> LeadingZeros(Constant(0b11111, 5)) 0b00000 >>> LeadingZeros(Constant(0b001, 3)) 0b110 >>> LeadingZeros(Variable("x", 4)).doit() ~(x | (x >> 0x1) | ((x | (x >> 0x1)) >> 0x2)) >>> LeadingZeros(Variable("x", 3)).doit() ~(((0b0 :: x) | ((0b0 :: x) >> 0x1) | (((0b0 :: x) | ((0b0 :: x) >> 0x1)) >> 0x2))[2:])
- class cascada.bitvector.secondaryop.LutOperation(**kwargs)[source]
Bases:
cascada.bitvector.operation.SecondaryOperation
Base class for LUT-based operations.
Given a LUT (look-up table)
T
withConstant
elements, the (bit-vector) LUT-based operationT(x)
(with bit-vector inputx
) returns the bit-vector ofT
in the position given by the integer value ofx
.Since
LutOperation
is aSecondaryOperation
, it is only evaluated by default when the input is a constant value (see alsocontext.SecondaryOperationEvaluation
).This class is not meant to be instantiated but to provide a base class for LUT-based operations. To define a LUT-based operation, subclass
LutOperation
and provide the class attributelut
.>>> from cascada.bitvector.core import Constant, Variable >>> from cascada.bitvector.secondaryop import LutOperation >>> class MyInvertibleLut(LutOperation): lut = [Constant(i, 2) for i in reversed(range(2**2))] >>> MyInvertibleLut(Constant(0, 2)) 0b11 >>> MyInvertibleLut(Variable("a", 2)).doit() Ite(a == 0b00, 0b11, Ite(a == 0b01, 0b10, Ite(a == 0b10, 0b01, 0b00))) >>> class MyNonInvLut(LutOperation): lut = [Constant(0, 8), ~Constant(0, 8)] >>> MyNonInvLut(Variable("a", 1)).doit() Ite(a == 0b0, 0x00, 0xff)
- class cascada.bitvector.secondaryop.MatrixOperation(**kwargs)[source]
Bases:
cascada.bitvector.operation.SecondaryOperation
Base class for matrix-vector products.
Given a binary matrix as a 2-dimensional list
M
with 1-bitConstant
elements (M[i][j]
is the j-th column element in the i-th row), the (bit-vector) matrix-vector productM(x)
(with bit-vector inputx
) returns the bit-vector ofy
such the i-th bit ofy
is given byy[i] = (M[i][0] & x[0]) ^ (M[i][1] & x[1]) ^ ... ^ (M[i][n-1] & x[n-1])
wheren
is the number of columns and the width ofx
.Since
MatrixOperation
is aSecondaryOperation
, it is only evaluated by default when the input is a constant value (see alsocontext.SecondaryOperationEvaluation
).This class is not meant to be instantiated but to provide a base class for matrix-vector products. To define a matrix-vector product, subclass
MatrixOperation
and provide the class attributematrix
.Note
By default, a
MatrixOperation
subclass takes 1 input operandx
, butarity
can be changed to support multiple operands. In this case, the list of inputs(x_1, ..., x_n)
are concatenated intox
asx = x_n :: ... :: x_1
(x_1
denoting the least-significant part andx_n
denoting the most-significant part).In other words, a
MatrixOperation
subclass witharity == [n, 0]
and input(x_1, ..., x_n)
computesM(x_n :: ... :: x_1)
.Alternatively, binary matrices can be implemented as a sequence of
BvXor
operations (e.g., https://eprint.iacr.org/2021/1400).>>> from cascada.bitvector.core import Constant, Variable >>> from cascada.bitvector.secondaryop import MatrixOperation >>> matrix = [[0, 1], [1, 0]] # reverse >>> matrix = [[core.Constant(x, 1) for x in row] for row in matrix] >>> class MySquareMatrix(MatrixOperation): matrix = matrix >>> MySquareMatrix(Constant(0, 2)) 0b00 >>> MySquareMatrix(Variable("a", 2)).doit() (a[0]) :: (a[1]) >>> matrix = [[1, 0], [0, 1], [1, 0], [0, 1]] # 2 bit to 4 bit >>> matrix = [[core.Constant(x, 1) for x in row] for row in matrix] >>> class MyNonSqMatrix(MatrixOperation): arity, matrix = [2, 0], matrix >>> MyNonSqMatrix(Variable("a0", 1), Variable("a1", 1)).doit() a1 :: a0 :: a1 :: a0