Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/java/org/spongycastle/crypto/macs/SipHash.java')
-rw-r--r--core/src/main/java/org/spongycastle/crypto/macs/SipHash.java216
1 files changed, 216 insertions, 0 deletions
diff --git a/core/src/main/java/org/spongycastle/crypto/macs/SipHash.java b/core/src/main/java/org/spongycastle/crypto/macs/SipHash.java
new file mode 100644
index 00000000..3489d4c1
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/macs/SipHash.java
@@ -0,0 +1,216 @@
+package org.spongycastle.crypto.macs;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Pack;
+
+/**
+ * Implementation of SipHash as specified in "SipHash: a fast short-input PRF", by Jean-Philippe
+ * Aumasson and Daniel J. Bernstein (https://131002.net/siphash/siphash.pdf).
+ * <p>
+ * "SipHash is a family of PRFs SipHash-c-d where the integer parameters c and d are the number of
+ * compression rounds and the number of finalization rounds. A compression round is identical to a
+ * finalization round and this round function is called SipRound. Given a 128-bit key k and a
+ * (possibly empty) byte string m, SipHash-c-d returns a 64-bit value..."
+ */
+public class SipHash
+ implements Mac
+{
+ protected final int c, d;
+
+ protected long k0, k1;
+ protected long v0, v1, v2, v3;
+
+ protected long m = 0;
+ protected int wordPos = 0;
+ protected int wordCount = 0;
+
+ /**
+ * SipHash-2-4
+ */
+ public SipHash()
+ {
+ // use of 'this' confuses the flow analyser on earlier JDKs.
+ this.c = 2;
+ this.d = 4;
+ }
+
+ /**
+ * SipHash-c-d
+ *
+ * @param c the number of compression rounds
+ * @param d the number of finalization rounds
+ */
+ public SipHash(int c, int d)
+ {
+ this.c = c;
+ this.d = d;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SipHash-" + c + "-" + d;
+ }
+
+ public int getMacSize()
+ {
+ return 8;
+ }
+
+ public void init(CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("'params' must be an instance of KeyParameter");
+ }
+ KeyParameter keyParameter = (KeyParameter)params;
+ byte[] key = keyParameter.getKey();
+ if (key.length != 16)
+ {
+ throw new IllegalArgumentException("'params' must be a 128-bit key");
+ }
+
+ this.k0 = Pack.littleEndianToLong(key, 0);
+ this.k1 = Pack.littleEndianToLong(key, 8);
+
+ reset();
+ }
+
+ public void update(byte input)
+ throws IllegalStateException
+ {
+ m >>>= 8;
+ m |= (input & 0xffL) << 56;
+
+ if (++wordPos == 8)
+ {
+ processMessageWord();
+ wordPos = 0;
+ }
+ }
+
+ public void update(byte[] input, int offset, int length)
+ throws DataLengthException,
+ IllegalStateException
+ {
+ int i = 0, fullWords = length & ~7;
+ if (wordPos == 0)
+ {
+ for (; i < fullWords; i += 8)
+ {
+ m = Pack.littleEndianToLong(input, offset + i);
+ processMessageWord();
+ }
+ for (; i < length; ++i)
+ {
+ m >>>= 8;
+ m |= (input[offset + i] & 0xffL) << 56;
+ }
+ wordPos = length - fullWords;
+ }
+ else
+ {
+ int bits = wordPos << 3;
+ for (; i < fullWords; i += 8)
+ {
+ long n = Pack.littleEndianToLong(input, offset + i);
+ m = (n << bits) | (m >>> -bits);
+ processMessageWord();
+ m = n;
+ }
+ for (; i < length; ++i)
+ {
+ m >>>= 8;
+ m |= (input[offset + i] & 0xffL) << 56;
+
+ if (++wordPos == 8)
+ {
+ processMessageWord();
+ wordPos = 0;
+ }
+ }
+ }
+ }
+
+ public long doFinal()
+ throws DataLengthException, IllegalStateException
+ {
+ // NOTE: 2 distinct shifts to avoid "64-bit shift" when wordPos == 0
+ m >>>= ((7 - wordPos) << 3);
+ m >>>= 8;
+ m |= (((wordCount << 3) + wordPos) & 0xffL) << 56;
+
+ processMessageWord();
+
+ v2 ^= 0xffL;
+
+ applySipRounds(d);
+
+ long result = v0 ^ v1 ^ v2 ^ v3;
+
+ reset();
+
+ return result;
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ long result = doFinal();
+ Pack.longToLittleEndian(result, out, outOff);
+ return 8;
+ }
+
+ public void reset()
+ {
+ v0 = k0 ^ 0x736f6d6570736575L;
+ v1 = k1 ^ 0x646f72616e646f6dL;
+ v2 = k0 ^ 0x6c7967656e657261L;
+ v3 = k1 ^ 0x7465646279746573L;
+
+ m = 0;
+ wordPos = 0;
+ wordCount = 0;
+ }
+
+ protected void processMessageWord()
+ {
+ ++wordCount;
+ v3 ^= m;
+ applySipRounds(c);
+ v0 ^= m;
+ }
+
+ protected void applySipRounds(int n)
+ {
+ long r0 = v0, r1 = v1, r2 = v2, r3 = v3;
+
+ for (int r = 0; r < n; ++r)
+ {
+ r0 += r1;
+ r2 += r3;
+ r1 = rotateLeft(r1, 13);
+ r3 = rotateLeft(r3, 16);
+ r1 ^= r0;
+ r3 ^= r2;
+ r0 = rotateLeft(r0, 32);
+ r2 += r1;
+ r0 += r3;
+ r1 = rotateLeft(r1, 17);
+ r3 = rotateLeft(r3, 21);
+ r1 ^= r2;
+ r3 ^= r0;
+ r2 = rotateLeft(r2, 32);
+ }
+
+ v0 = r0; v1 = r1; v2 = r2; v3 = r3;
+ }
+
+ protected static long rotateLeft(long x, int n)
+ {
+ return (x << n) | (x >>> -n);
+ }
+}