Java 卡 v.2.2.2 上的 SHA-3 实现

SHA-3 implementation on Java Card v.2.2.2

我目前正在尝试在智能卡上实施算法 SHA-3。我设法成功实施,但算法的输出似乎与 NIST 的测试向量不匹配。我的全部源代码如下。

final static byte CLA_KECCAK = (byte) 0x80;
final static byte INS_KECCAKF = (byte) 0x10; 
short[] ram_B = JCSystem.makeTransientShortArray((short) 5, JCSystem.CLEAR_ON_DESELECT);
short[] ram_tmp = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_DESELECT);
final static short c = (short) 256;
final static short r = (short) (400 - c);
final static short rbyte = (short) (r / 8);
final static short outputLength = (short) 224;
private static short rot(short n, short i) {
    return (short) ((n << i) | (n >>> ((short) (16 - i))));
}
private static final short[] roundConstants = { (short) 0x0001, (short) 0x8082, (short) 0x808A, (short) 0x8000,
        (short) 0x808B, (short) 0x0001, (short) 0x8081, (short) 0x8009, (short) 0x008A, (short) 0x0088,
        (short) 0x8009, (short) 0x000A, (short) 0x808B, (short) 0x008B, (short) 0x8089, (short) 0x8003,
        (short) 0x8002, (short) 0x0080, (short) 0x800A, (short) 0x000A };
private static final short rotationConstants[] = { 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25,
        43, 62, 18, 39, 61, 20, 44 };
private static final short indexPi[] = { 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14,
        22, 9, 6, 1 };
private static final short index[] = { 0, 1, 2, 3, 4, 0, 1, 2, 3, 4 };

private TestSha() {
}

public static void install(byte bArray[], short bOffset, byte bLength) throws ISOException {
    new TestSha().register();
}

public void process(APDU apdu) throws ISOException {
    if (selectingApplet()) {
        return;
    }
    byte[] buf = apdu.getBuffer();
    byte[] hash;
    switch (buf[ISO7816.OFFSET_INS]) {
        case INS_KECCAKF:
            short lgth = apdu.setIncomingAndReceive();
            if (lgth == 0) {
                ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
            }
            byte[] data = new byte[lgth];
            Util.arrayCopy(buf, ISO7816.OFFSET_CDATA, data, (short) 0, lgth);
            hash = KeccakF(data); 
            Util.arrayCopyNonAtomic(hash, (short) 0, buf, (short) 0, (short) hash.length);
            apdu.setOutgoingAndSend((short) 0, (short) hash.length);
            break;
        default:
            ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
    }
}

private static short[] byteToShort(byte[] b) {
    short[] c = new short[(short) (b.length / 2)];
    byte aux[] = new byte[2];
    for (short i = 0; i < c.length; i++) {
        for (short j = 0; j < 2; j++) {
            aux[j] = b[(short) (2 * i + j)];
        }
        c[i] = (short) ((((short) aux[1]) & 0xFF) | ((((short) aux[0]) & 0xFF) << 8));
    }
    return c;
}

private static byte[] shortToByte(short[] b) {
    byte[] c = new byte[(short) (b.length * 2)];
    short y = 0;
    for (short x = 0; x < c.length; x += 2) {
        c[(short) (x + 1)] = (byte) (b[y] & 0xFF);
        c[x] = (byte) ((b[y] >> 8) & 0xFF);
        y++;
    }
    return c;
}

public byte[] KeccakF(byte[] M) {
    short i = 0;
    short x = 0;
    short Mlen = (short) M.length; // padding
    x = (short) (rbyte - (Mlen % (rbyte)));
    byte[] P = new byte[(short) (Mlen + x)];
    Util.arrayCopy(M, (short) 0, P, (short) 0, Mlen);
    if ((Mlen % rbyte) != 0) {
        P[Mlen] = (byte) 0x01;
        P[(short) (P.length - 1)] |= (byte) 0x80;
    }
    // absorbing
    short S[] = new short[25];
    byte[] Pi = new byte[rbyte];
    short[] tmp = new short[rbyte / 2];
    short Plen = (short) P.length;
    for (i = 0; i < (short) (Plen / rbyte); i++) {
        Util.arrayCopy(P, (short) (i * Plen), Pi, (short) 0, rbyte);
        tmp = byteToShort(Pi);
        for (x = 0; x < (short) (rbyte / 2); x++) {
            S[x] ^= tmp[x];
        }
        S = Keccak400(S);
    }
    x = outputLength;
    short[] Z = new short[outputLength / 16];
    short Zlen = (short) Z.length;
    while (x > 0) {
        for (i = 0; i < Zlen; i++) {
            Z[i] = S[i];
        }
        x -= r;
        if (x > 0) {
            S = Keccak400(S);
        }
    }
    return shortToByte(Z);
}

private short[] Keccak400(short[] A) {
    short x = 0;
    short y = 0;
    short round = 0;
    for (round = 0; round < 20; round++) {
        // Theta Step
        for (x = 0; x < 5; x++) {
            ram_B[x] = (short) (A[x] ^ A[(short) (5 + x)] ^ A[(short) (10 + x)] ^ A[(short) (15 + x)]
                    ^ A[(short) (20 + x)]);
        }
        for (x = 0; x < 5; x++) {
            ram_tmp[0] = (short) (ram_B[index[(short) (x + 4)]] ^ rot(ram_B[index[(short) (x + 1)]], (short) 1));
            for (y = 0; y < 25; y += 5) {
                A[(short) (x + y)] ^= ram_tmp[0];
            }
        }
        // Rho and Pi Steps
        ram_tmp[0] = A[1];
        short t = 0;
        for (x = 0; x < 24; x++) {
            t = indexPi[x];
            ram_B[0] = A[t];
            A[t] = rot(ram_tmp[0], rotationConstants[x]);
            ram_tmp[0] = ram_B[0];
        }
        for (y = 0; y < 25; y += 5) {
            ram_B[0] = A[(short) (y + 0)];
            ram_B[1] = A[(short) (y + 1)];
            ram_B[2] = A[(short) (y + 2)];
            ram_B[3] = A[(short) (y + 3)];
            ram_B[4] = A[(short) (y + 4)];
            for (x = 0; x < 5; x++) {
                A[(short) (y + x)] = (short) (ram_B[x]
                        ^ ((~ram_B[index[(short) (x + 1)]]) & ram_B[index[(short) (x + 2)]]));
            }
        }
        // Iota Step
        A[0] ^= roundConstants[round];
    }
    return A;
}

代码未优化,SHA-3 方差为 Keccak-224。

如果您的测试向量不匹配,那么要么您的测试无效,要么 - 更可能的是 - 您的算法尚未生效。您应该尝试针对另一个实现验证中间值。您可以使用 BouncyCastle 作为中间值,使用 jCardSim 来帮助调试。