我想在目前表示为8个字节在JavaCard的智能卡中的字节阵列的64位字上旋转的任意的量进行循环左移(ROTL)操作。
丑方式围绕是硬编码的ROTL上表示为8字节阵列的64位的字中的所有64个可能的排列,但是,这将简单地臃肿整个代码库起来。
如何使它更精简,这样我可以在即时做ROTL操作的任意量与仅使用byte
和short
类型的(由于Java卡上的64位字的需求(以字节数组)不承认更复杂的事情像int
或long
等等),而不需要硬编码的所有ROTL64排列。
下面的方法执行用于在缓冲器中的任何类型的数组向右旋转,而无需额外的输出或临时数组:
/**
* Rotates the indicated bytes in the given buffer to the right by a certain
* number of bits.
*
* @param buf
* the buffer in which the bits need to be rotated
* @param off
* the offset in the buffer where the rotation needs to start
* @param len
* the amount of bytes
* @param rot
* the amount to rotate (any 31 bit value allowed)
*/
public static void rotr(byte[] buf, short off, short len, short rot) {
if (len == 0) {
// nothing to rotate (avoid division by 0)
return;
}
final short lenBits = (short) (len * BYTE_SIZE);
// doesn't always work for edge cases close to MIN_SHORT / MAX_SHORT
rot = (short) ((rot + lenBits) % lenBits);
// reused variables for byte and bit shift
short shift, i;
byte t1, t2;
// --- byte shift
shift = (short) (rot / BYTE_SIZE);
// only shift when actually required
if (shift != 0) {
// values will never be used, src == start at the beginning
short start = -1, src = -1, dest;
// compiler is too stupid to see t1 will be assigned anyway
t1 = 0;
// go over all the bytes, but in stepwise fashion
for (i = 0; i < len; i++) {
// if we get back to the start
// ... then we need to continue one position to the right
if (src == start) {
start++;
t1 = buf[(short) (off + (++src))];
}
// calculate next location by stepping by the shift amount
// ... modulus the length of course
dest = (short) ((src + shift) % len);
// save value, this will be the next one to be stored
t2 = buf[(short) (off + dest)];
// store value, doing the actual shift
buf[(short) (off + dest)] = t1;
// do the step
src = dest;
// we're going to store t1, not t2
t1 = t2;
}
}
// --- bit shift
shift = (short) (rot % BYTE_SIZE);
// only shift when actually required
if (shift != 0) {
// t1 holds previous byte, at other side
t1 = buf[(short) (off + len - 1)];
for (i = 0; i < len; i++) {
t2 = buf[(short) (off + i)];
// take bits from previous byte and this byte together
buf[(short) (off + i)] = (byte) ((t1 << (BYTE_SIZE - shift)) | ((t2 & BYTE_MASK) >> shift));
// current byte is now previous byte
t1 = t2;
}
}
}
private static final short BYTE_MASK = 0xFF;
private static final short BYTE_SIZE = 8;
一个缺点是,它需要两个越过所有的数据:字节移位中的一个,一个用于位移位。它会跳过他们,当不需要他们(如果你知道跳绳是从未执行,那么你可以很容易地删除那些检查)。
这里是左旋转。左旋转有点更难实现自身 - 需要一些计算,因此额外的方法调用的成本可能针对被抵消。如果您使用文字旋转,那么你可以只使用当然rotr
功能,或计算的旋转量自己。
public static void rotl(byte[] buf, short off, short len, short bits) {
final short lenBits = (short) (len * BYTE_SIZE);
bits = (short) ((bits + lenBits) % lenBits);
// we don't care if we pass 0 or lenBits, rotr will adjust
rotr(buf, off, len, (short) (lenBits - bits));
}
注:在64位先前所需的版本旋转,更加的时间常数,并没有偏移量包括在内。它还需要具有环路常量一个64位特定阵列(现在是通过在字节移位的if
环的更通用内部for
语句取代)。看到其他版本的编辑。
旋转时的输出缓冲区可得要容易得多:此实现可以仅由初始化部分和最后4行代码。加密代码经常仅由一个恒定的尺寸和由奇数移位,所以只是最后4行然后可以使用,跳过的情况下,没有(位)移位需要被执行的优化。
我刻意用那所承担的旋转64位稍微不同的接口,是想说明一个稍微不同的实现。
public static void rotr64(byte[] inBuf, short inOff, byte[] outBuf, short outOff, short rot) {
short byteRot = (short) ((rot & 0b00111000) >> 3);
short bitRot = (short) (rot & 0b00000111);
if (bitRot == 0) {
if (byteRot == 0) {
// --- no rotation
return;
}
// --- only byte rotation
for (short i = 0; i < LONG_BYTES; i++) {
outBuf[(short) (outOff + (i + byteRot) % LONG_BYTES)] = inBuf[(short) (inOff + i)];
}
} else {
// --- bit- and possibly byte rotation
// note: also works for all other situations, but slower
// put the last byte in t_lo
short t = (short) (inBuf[inOff + LONG_BYTES - 1] & BYTE_MASK);
for (short i = 0; i < LONG_BYTES; i++) {
// shift t_lo into t_hi and add the next byte into t_lo
t = (short) (t << BYTE_SIZE | (inBuf[(short) (inOff + i)] & BYTE_MASK));
// find the byte to receive the shifted value within the short
outBuf[(short) (outOff + (i + byteRot) % LONG_BYTES)] = (byte) (t >> bitRot);
}
}
}
private static final int LONG_BYTES = 8;
private static final short BYTE_MASK = 0xFF;
private static final short BYTE_SIZE = 8;
并且它可以进一步简化如果偏移总是被设置为零。
这里是左旋转,如果你需要的是一个通用的功能:
public static void rotl64(byte[] inBuf, short inOff, byte[] outBuf, short outOff, short rot) {
rotr64(inBuf, inOff, outBuf, outOff, (short) (64 - rot & 0b00111111));
}
一切都对随机输入测试(一百万奔跑左右,这需要不到在Java SE第二),虽然我不提供对测试保修;请测试自己。
一个非常简单的实现,其接收在单独的参数的四个短裤
public static void rotateRight64(short x3, short x2, short x1, short x0,
short rotAmount, short[] out)
{
assert out.length() == 4;
rotAmount &= (1 << 6) - 1; // limit the range to 0..63
if (rotAmount >= 16)
rotateRight64(x0, x3, x2, x1, rotAmount - 16, out);
else
{
out[0] = (short)((x0 >>> rotAmount) | (x1 << (16-rotAmount)));
out[1] = (short)((x1 >>> rotAmount) | (x2 << (16-rotAmount)));
out[2] = (short)((x2 >>> rotAmount) | (x3 << (16-rotAmount)));
out[3] = (short)((x3 >>> rotAmount) | (x0 << (16-rotAmount)));
}
}
这是一个旋转的权利,但它很容易做到通过转动的右侧64 - rotAmount
留下了旋转
或者,它可以这样来完成,而不移动粗步
public static void rotateRight(short[] out, short[] in, short rotAmount) // in ror rotAmount
{
assert out.length() == 4 && in.length() == 4 && rotAmount >= 0 && rotAmount < 64;
const short shift = (short)(rotAmount % 16);
const short limbshift = (short)(rotAmount / 16);
short tmp = in[0];
for (short i = 0; i < 4; ++i)
{
short index = (short)((i + limbshift) % 4);
out[i] = (short)((in[index] >>> shift) | (in[index + 1] << (16 - shift)));
}
}
这种方式,可以容易地改变为任意精度移位/循环
如果输入数组是byte
那么你可以改变short[4]
到byte[8]
和更改所有从16→8和4→8常量事实上,他们可以毫无问题地一概而论,我只是硬编码保持它的简单和容易了解