向左旋转在Java卡一个64位字的字节数组

问题描述 投票:1回答:2

我想在目前表示为8个字节在JavaCard的智能卡中的字节阵列的64位字上旋转的任意的量进行循环左移(ROTL)操作。

丑方式围绕是硬编码的ROTL上表示为8字节阵列的64位的字中的所有64个可能的排列,但是,这将简单地臃肿整个代码库起来。

如何使它更精简,这样我可以在即时做ROTL操作的任意量与仅使用byteshort类型的(由于Java卡上的64位字的需求(以字节数组)不承认更复杂的事情像intlong等等),而不需要硬编码的所有ROTL64排列。

bit-manipulation 64bit javacard
2个回答
1
投票

下面的方法执行用于在缓冲器中的任何类型的数组向右旋转,而无需额外的输出或临时数组:

/**
 * 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第二),虽然我不提供对测试保修;请测试自己。


1
投票

一个非常简单的实现,其接收在单独的参数的四个短裤

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常量事实上,他们可以毫无问题地一概而论,我只是硬编码保持它的简单和容易了解

© www.soinside.com 2019 - 2024. All rights reserved.