mirror of https://github.com/octoleo/plantuml.git
1356 lines
42 KiB
Java
1356 lines
42 KiB
Java
|
|
/* This file is taken from
|
|
https://github.com/alphazero/Blake2b/
|
|
|
|
Original Author: Andreas Gadermaier <up.gadermaier@gmail.com>
|
|
because the repository artifact was not uploaded to maven central repository
|
|
|
|
*/
|
|
/*
|
|
A Java implementation of BLAKE2B cryptographic digest algorithm.
|
|
|
|
Joubin Mohammad Houshyar <alphazero@sensesay.net>
|
|
bushwick, nyc
|
|
02-14-2014
|
|
|
|
--
|
|
|
|
To the extent possible under law, the author(s) have dedicated all copyright
|
|
and related and neighboring rights to this software to the public domain
|
|
worldwide. This software is distributed without any warranty.
|
|
|
|
You should have received a copy of the CC0 Public Domain Dedication along with
|
|
this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
*/
|
|
package net.sourceforge.plantuml.argon2.blake2;
|
|
|
|
import static net.sourceforge.plantuml.argon2.blake2.Blake2b.Engine.Assert.assertFail;
|
|
import static net.sourceforge.plantuml.argon2.blake2.Blake2b.Engine.Assert.exclusiveLowerBound;
|
|
import static net.sourceforge.plantuml.argon2.blake2.Blake2b.Engine.Assert.inclusiveLowerBound;
|
|
import static net.sourceforge.plantuml.argon2.blake2.Blake2b.Engine.Assert.inclusiveUpperBound;
|
|
import static net.sourceforge.plantuml.argon2.blake2.Blake2b.Engine.LittleEndian.readInt;
|
|
import static net.sourceforge.plantuml.argon2.blake2.Blake2b.Engine.LittleEndian.readLong;
|
|
import static net.sourceforge.plantuml.argon2.blake2.Blake2b.Engine.LittleEndian.writeInt;
|
|
import static net.sourceforge.plantuml.argon2.blake2.Blake2b.Engine.LittleEndian.writeLong;
|
|
|
|
import java.io.PrintStream;
|
|
import java.security.Key;
|
|
import java.security.spec.AlgorithmParameterSpec;
|
|
import java.util.Arrays;
|
|
|
|
/** */
|
|
public interface Blake2b {
|
|
// ::remove folder when __HAXE__
|
|
/** */
|
|
void update(byte[] input);
|
|
|
|
// ---------------------------------------------------------------------
|
|
// API
|
|
// ---------------------------------------------------------------------
|
|
// TODO add ByteBuffer variants
|
|
|
|
/** */
|
|
void update(byte input);
|
|
|
|
/** */
|
|
void update(byte[] input, int offset, int len);
|
|
|
|
/** */
|
|
byte[] digest();
|
|
|
|
/** */
|
|
byte[] digest(byte[] input);
|
|
|
|
/** */
|
|
void digest(byte[] output, int offset, int len);
|
|
|
|
/** */
|
|
void reset();
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Specification
|
|
// ---------------------------------------------------------------------
|
|
public interface Spec {
|
|
/** pblock size of blake2b */
|
|
int param_bytes = 64;
|
|
|
|
/** pblock size of blake2b */
|
|
int block_bytes = 128;
|
|
|
|
/** maximum digest size */
|
|
int max_digest_bytes = 64;
|
|
|
|
/** maximum key sie */
|
|
int max_key_bytes = 64;
|
|
|
|
/** maximum salt size */
|
|
int max_salt_bytes = 16;
|
|
|
|
/** maximum personalization string size */
|
|
int max_personalization_bytes = 16;
|
|
|
|
/** length of h space vector array */
|
|
int state_space_len = 8;
|
|
|
|
/** max tree fanout value */
|
|
int max_tree_fantout = 0xFF;
|
|
|
|
/** max tree depth value */
|
|
int max_tree_depth = 0xFF;
|
|
|
|
/**
|
|
* max tree leaf length value.Note that this has uint32 semantics and thus
|
|
* 0xFFFFFFFF is used as max value limit.
|
|
*/
|
|
int max_tree_leaf_length = 0xFFFFFFFF;
|
|
|
|
/**
|
|
* max node offset value. Note that this has uint64 semantics and thus
|
|
* 0xFFFFFFFFFFFFFFFFL is used as max value limit.
|
|
*/
|
|
long max_node_offset = 0xFFFFFFFFFFFFFFFFL;
|
|
|
|
/** max tree inner length value */
|
|
int max_tree_inner_length = 0xFF;
|
|
|
|
/** initialization values map ref-Spec IV[i] slice iv[i*8:i*8+7] */
|
|
long[] IV = { 0x6a09e667f3bcc908L, 0xbb67ae8584caa73bL, 0x3c6ef372fe94f82bL, 0xa54ff53a5f1d36f1L,
|
|
0x510e527fade682d1L, 0x9b05688c2b3e6c1fL, 0x1f83d9abfb41bd6bL, 0x5be0cd19137e2179L };
|
|
|
|
/** sigma per spec used in compress func generation - for reference only */
|
|
static byte[][] sigma = { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
|
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
|
|
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
|
|
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
|
|
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
|
|
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
|
|
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
|
|
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
|
|
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
|
|
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
|
|
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
|
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } };
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Blake2b Message Digest
|
|
// ---------------------------------------------------------------------
|
|
|
|
/** Generalized Blake2b digest. */
|
|
public static class Digest extends Engine implements Blake2b {
|
|
private Digest(final Param p) {
|
|
super(p);
|
|
}
|
|
|
|
private Digest() {
|
|
super();
|
|
}
|
|
|
|
public static Digest newInstance() {
|
|
return new Digest();
|
|
}
|
|
|
|
public static Digest newInstance(final int digestLength) {
|
|
return new Digest(new Param().setDigestLength(digestLength));
|
|
}
|
|
|
|
public static Digest newInstance(Param p) {
|
|
return new Digest(p);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Blake2b Message Authentication Code
|
|
// ---------------------------------------------------------------------
|
|
|
|
/** Message Authentication Code (MAC) digest. */
|
|
public static class Mac extends Engine implements Blake2b {
|
|
private Mac(final Param p) {
|
|
super(p);
|
|
}
|
|
|
|
private Mac() {
|
|
super();
|
|
}
|
|
|
|
/** Blake2b.MAC 512 - using default Blake2b.Spec settings with given key */
|
|
public static Mac newInstance(final byte[] key) {
|
|
return new Mac(new Param().setKey(key));
|
|
}
|
|
|
|
/**
|
|
* Blake2b.MAC - using default Blake2b.Spec settings with given key, with given
|
|
* digest length
|
|
*/
|
|
public static Mac newInstance(final byte[] key, final int digestLength) {
|
|
return new Mac(new Param().setKey(key).setDigestLength(digestLength));
|
|
}
|
|
|
|
/**
|
|
* Blake2b.MAC - using default Blake2b.Spec settings with given
|
|
* java.security.Key, with given digest length
|
|
*/
|
|
public static Mac newInstance(final Key key, final int digestLength) {
|
|
return new Mac(new Param().setKey(key).setDigestLength(digestLength));
|
|
}
|
|
|
|
/**
|
|
* Blake2b.MAC - using the specified Parameters.
|
|
*
|
|
* @param p asserted valid configured Param with key
|
|
*/
|
|
public static Mac newInstance(Param p) {
|
|
assert p != null : "Param (p) is null";
|
|
assert p.hasKey() : "Param (p) not configured with a key";
|
|
return new Mac(p);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Blake2b Incremental Message Digest (Tree)
|
|
// ---------------------------------------------------------------------
|
|
|
|
/**
|
|
* Note that Tree is just a convenience class; incremental hash (tree) can be
|
|
* done directly with the Digest class. <br>
|
|
* Further node, that tree does NOT accumulate the leaf hashes -- you need to do
|
|
* that
|
|
*/
|
|
public static class Tree {
|
|
|
|
final int depth;
|
|
final int fanout;
|
|
final int leaf_length;
|
|
final int inner_length;
|
|
final int digest_length;
|
|
|
|
/**
|
|
*
|
|
* @param fanout
|
|
* @param depth
|
|
* @param leaf_length size of data input for leaf nodes.
|
|
* @param inner_length note this is used also as digest-length for non-root
|
|
* nodes.
|
|
* @param digest_length final hash out digest-length for the tree
|
|
*/
|
|
public Tree(final int depth, final int fanout, final int leaf_length, final int inner_length,
|
|
final int digest_length) {
|
|
this.fanout = fanout;
|
|
this.depth = depth;
|
|
this.leaf_length = leaf_length;
|
|
this.inner_length = inner_length;
|
|
this.digest_length = digest_length;
|
|
}
|
|
|
|
private Param treeParam() {
|
|
return new Param().setDepth(depth).setFanout(fanout).setLeafLength(leaf_length)
|
|
.setInnerLength(inner_length);
|
|
}
|
|
|
|
/** returns the Digest for tree node @ (depth, offset) */
|
|
public final Digest getNode(final int depth, final int offset) {
|
|
final Param nodeParam = treeParam().setNodeDepth(depth).setNodeOffset(offset).setDigestLength(inner_length);
|
|
return Digest.newInstance(nodeParam);
|
|
}
|
|
|
|
/** returns the Digest for root node */
|
|
public final Digest getRoot() {
|
|
final int depth = this.depth - 1;
|
|
final Param rootParam = treeParam().setNodeDepth(depth).setNodeOffset(0L).setDigestLength(digest_length);
|
|
return Digest.newInstance(rootParam);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Engine
|
|
// ---------------------------------------------------------------------
|
|
static class Engine implements Blake2b {
|
|
|
|
/* G0 sigmas */
|
|
static final int[] sig_g00 = { 0, 14, 11, 7, 9, 2, 12, 13, 6, 10, 0, 14, };
|
|
static final int[] sig_g01 = { 1, 10, 8, 9, 0, 12, 5, 11, 15, 2, 1, 10, };
|
|
|
|
/* G1 sigmas */
|
|
static final int[] sig_g10 = { 2, 4, 12, 3, 5, 6, 1, 7, 14, 8, 2, 4, };
|
|
static final int[] sig_g11 = { 3, 8, 0, 1, 7, 10, 15, 14, 9, 4, 3, 8, };
|
|
|
|
/* G2 sigmas */
|
|
static final int[] sig_g20 = { 4, 9, 5, 13, 2, 0, 14, 12, 11, 7, 4, 9, };
|
|
static final int[] sig_g21 = { 5, 15, 2, 12, 4, 11, 13, 1, 3, 6, 5, 15, };
|
|
|
|
/* G3 sigmas */
|
|
static final int[] sig_g30 = { 6, 13, 15, 11, 10, 8, 4, 3, 0, 1, 6, 13, };
|
|
static final int[] sig_g31 = { 7, 6, 13, 14, 15, 3, 10, 9, 8, 5, 7, 6, };
|
|
|
|
/* G4 sigmas */
|
|
static final int[] sig_g40 = { 8, 1, 10, 2, 14, 4, 0, 5, 12, 15, 8, 1, };
|
|
static final int[] sig_g41 = { 9, 12, 14, 6, 1, 13, 7, 0, 2, 11, 9, 12, };
|
|
|
|
/* G5 sigmas */
|
|
static final int[] sig_g50 = { 10, 0, 3, 5, 11, 7, 6, 15, 13, 9, 10, 0, };
|
|
static final int[] sig_g51 = { 11, 2, 6, 10, 12, 5, 3, 4, 7, 14, 11, 2, };
|
|
|
|
/* G6 sigmas */
|
|
static final int[] sig_g60 = { 12, 11, 7, 4, 6, 15, 9, 8, 1, 3, 12, 11, };
|
|
static final int[] sig_g61 = { 13, 7, 1, 0, 8, 14, 2, 6, 4, 12, 13, 7, };
|
|
|
|
/* G7 sigmas */
|
|
static final int[] sig_g70 = { 14, 5, 9, 15, 3, 1, 8, 2, 10, 13, 14, 5, };
|
|
static final int[] sig_g71 = { 15, 3, 4, 8, 13, 9, 11, 10, 5, 0, 15, 3, };
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Blake2b State(+) per reference implementation
|
|
// ---------------------------------------------------------------------
|
|
// REVU: address last_node TODO part of the Tree/incremental
|
|
/**
|
|
* read only
|
|
*/
|
|
private static byte[] zeropad = new byte[Spec.block_bytes];
|
|
/**
|
|
* per spec
|
|
*/
|
|
private final long[] h = new long[8];
|
|
/** per spec */
|
|
private final long[] t = new long[2];
|
|
/** per spec */
|
|
private final long[] f = new long[2];
|
|
/** pulled up 2b optimal */
|
|
private final long[] m = new long[16];
|
|
/** pulled up 2b optimal */
|
|
private final long[] v = new long[16];
|
|
|
|
/** compressor cache buffer */
|
|
private final byte[] buffer;
|
|
/** configuration params */
|
|
private final Param param;
|
|
/** digest length from init param - copied here on init */
|
|
private final int outlen;
|
|
/**
|
|
* per spec (tree)
|
|
*/
|
|
private boolean last_node = false;
|
|
/**
|
|
* compressor cache buffer offset/cached data length
|
|
*/
|
|
private int buflen;
|
|
/** to support update(byte) */
|
|
private byte[] oneByte;
|
|
|
|
/** Basic use constructor pending (TODO) JCA/JCE compliance */
|
|
Engine() {
|
|
this(new Param());
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Ctor & Initialization
|
|
// ---------------------------------------------------------------------
|
|
|
|
/** User provided Param for custom configurations */
|
|
Engine(final Param param) {
|
|
assert param != null : "param is null";
|
|
this.param = param;
|
|
this.buffer = new byte[Spec.block_bytes];
|
|
this.oneByte = new byte[1];
|
|
this.outlen = param.getDigestLength();
|
|
|
|
if (param.getDepth() > Param.Default.depth) {
|
|
final int ndepth = param.getNodeDepth();
|
|
final long nxoff = param.getNodeOffset();
|
|
if (ndepth == param.getDepth() - 1) {
|
|
last_node = true;
|
|
assert param.getNodeOffset() == 0 : "root must have offset of zero";
|
|
} else if (param.getNodeOffset() == param.getFanout() - 1) {
|
|
this.last_node = true;
|
|
}
|
|
}
|
|
|
|
initialize();
|
|
|
|
// Debug.dumpBuffer(System.out, "param bytes at init", param.getBytes());
|
|
|
|
}
|
|
|
|
public static void main(String... args) {
|
|
Blake2b mac = Blake2b.Mac.newInstance("LOVE".getBytes());
|
|
final byte[] hash = mac.digest("Salaam!".getBytes());
|
|
// Debug.dumpBuffer(System.out, "-- mac hash --", hash);
|
|
}
|
|
|
|
private void initialize() {
|
|
// state vector h - copy values to address reset() requests
|
|
System.arraycopy(param.initialized_H(), 0, this.h, 0, Spec.state_space_len);
|
|
|
|
// Debug.dumpArray("init H", this.h);
|
|
// if we have a key update initial block
|
|
// Note param has zero padded key_bytes to Spec.max_key_bytes
|
|
if (param.hasKey) {
|
|
this.update(param.key_bytes, 0, Spec.block_bytes);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
final public void reset() {
|
|
// reset cache
|
|
this.buflen = 0;
|
|
for (int i = 0; i < buffer.length; i++) {
|
|
buffer[i] = (byte) 0;
|
|
}
|
|
|
|
// reset flags
|
|
this.f[0] = 0L;
|
|
this.f[1] = 0L;
|
|
|
|
// reset counters
|
|
this.t[0] = 0L;
|
|
this.t[1] = 0L;
|
|
|
|
// reset state vector
|
|
// NOTE: keep as last stmt as init calls update0 for MACs.
|
|
initialize();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// interface: Blake2b API
|
|
// ---------------------------------------------------------------------
|
|
|
|
/** {@inheritDoc} */
|
|
@Override
|
|
final public void update(final byte[] b, int off, int len) {
|
|
if (b == null) {
|
|
throw new IllegalArgumentException("input buffer (b) is null");
|
|
}
|
|
/* zero or more calls to compress */
|
|
// REVU: possibly the double buffering of c-ref is more sensible ..
|
|
// regardless, the hotspot is in the compress, as expected.
|
|
while (len > 0) {
|
|
if (buflen == 0) {
|
|
/* try compressing direct from input ? */
|
|
while (len > Spec.block_bytes) {
|
|
this.t[0] += Spec.block_bytes;
|
|
this.t[1] += this.t[0] == 0 ? 1 : 0;
|
|
compress(b, off);
|
|
len -= Spec.block_bytes;
|
|
off += Spec.block_bytes;
|
|
}
|
|
} else if (buflen == Spec.block_bytes) {
|
|
/* flush */
|
|
this.t[0] += Spec.block_bytes;
|
|
this.t[1] += this.t[0] == 0 ? 1 : 0;
|
|
compress(buffer, 0);
|
|
buflen = 0;
|
|
continue;
|
|
}
|
|
|
|
// "are we there yet?"
|
|
if (len == 0)
|
|
return;
|
|
|
|
final int cap = Spec.block_bytes - buflen;
|
|
final int fill = len > cap ? cap : len;
|
|
System.arraycopy(b, off, buffer, buflen, fill);
|
|
buflen += fill;
|
|
len -= fill;
|
|
off += fill;
|
|
}
|
|
}
|
|
|
|
/** {@inheritDoc} */
|
|
@Override
|
|
final public void update(byte b) {
|
|
oneByte[0] = b;
|
|
update(oneByte, 0, 1);
|
|
}
|
|
|
|
/** {@inheritDoc} */
|
|
@Override
|
|
final public void update(byte[] input) {
|
|
update(input, 0, input.length);
|
|
}
|
|
|
|
/** {@inheritDoc} */
|
|
@Override
|
|
final public void digest(byte[] output, int off, int len) {
|
|
// zero pad last block; set last block flags; and compress
|
|
System.arraycopy(zeropad, 0, buffer, buflen, Spec.block_bytes - buflen);
|
|
if (buflen > 0) {
|
|
this.t[0] += buflen;
|
|
this.t[1] += this.t[0] == 0 ? 1 : 0;
|
|
}
|
|
|
|
this.f[flag.last_block] = 0xFFFFFFFFFFFFFFFFL;
|
|
this.f[flag.last_node] = this.last_node ? 0xFFFFFFFFFFFFFFFFL : 0x0L;
|
|
|
|
// compres and write final out (truncated to len) to output
|
|
compress(buffer, 0);
|
|
hashout(output, off, len);
|
|
|
|
reset();
|
|
}
|
|
|
|
/** {@inheritDoc} */
|
|
@Override
|
|
final public byte[] digest() throws IllegalArgumentException {
|
|
final byte[] out = new byte[outlen];
|
|
digest(out, 0, outlen);
|
|
return out;
|
|
}
|
|
|
|
/** {@inheritDoc} */
|
|
@Override
|
|
final public byte[] digest(byte[] input) {
|
|
update(input, 0, input.length);
|
|
return digest();
|
|
}
|
|
|
|
/**
|
|
* write out the digest output from the 'h' registers. truncate full output if
|
|
* necessary.
|
|
*/
|
|
private void hashout(final byte[] out, final int offset, final int hashlen) {
|
|
// write max number of whole longs
|
|
final int lcnt = hashlen >>> 3;
|
|
long v = 0;
|
|
int i = offset;
|
|
for (int w = 0; w < lcnt; w++) {
|
|
v = h[w];
|
|
out[i++] = (byte) v;
|
|
v >>>= 8;
|
|
out[i++] = (byte) v;
|
|
v >>>= 8;
|
|
out[i++] = (byte) v;
|
|
v >>>= 8;
|
|
out[i++] = (byte) v;
|
|
v >>>= 8;
|
|
out[i++] = (byte) v;
|
|
v >>>= 8;
|
|
out[i++] = (byte) v;
|
|
v >>>= 8;
|
|
out[i++] = (byte) v;
|
|
v >>>= 8;
|
|
out[i++] = (byte) v;
|
|
}
|
|
|
|
// basta?
|
|
if (hashlen == Spec.max_digest_bytes)
|
|
return;
|
|
|
|
// write the remaining bytes of a partial long value
|
|
v = h[lcnt];
|
|
i = lcnt << 3;
|
|
while (i < hashlen) {
|
|
out[offset + i] = (byte) v;
|
|
v >>>= 8;
|
|
++i;
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Internal Ops
|
|
// ---------------------------------------------------------------------
|
|
|
|
/** compress Spec.block_bytes data from b, from offset */
|
|
private void compress(final byte[] b, final int offset) {
|
|
|
|
// set m registers
|
|
// REVU: some small gains still possible here.
|
|
m[0] = ((long) b[offset] & 0xFF);
|
|
m[0] |= ((long) b[offset + 1] & 0xFF) << 8;
|
|
m[0] |= ((long) b[offset + 2] & 0xFF) << 16;
|
|
m[0] |= ((long) b[offset + 3] & 0xFF) << 24;
|
|
m[0] |= ((long) b[offset + 4] & 0xFF) << 32;
|
|
m[0] |= ((long) b[offset + 5] & 0xFF) << 40;
|
|
m[0] |= ((long) b[offset + 6] & 0xFF) << 48;
|
|
m[0] |= ((long) b[offset + 7]) << 56;
|
|
|
|
m[1] = ((long) b[offset + 8] & 0xFF);
|
|
m[1] |= ((long) b[offset + 9] & 0xFF) << 8;
|
|
m[1] |= ((long) b[offset + 10] & 0xFF) << 16;
|
|
m[1] |= ((long) b[offset + 11] & 0xFF) << 24;
|
|
m[1] |= ((long) b[offset + 12] & 0xFF) << 32;
|
|
m[1] |= ((long) b[offset + 13] & 0xFF) << 40;
|
|
m[1] |= ((long) b[offset + 14] & 0xFF) << 48;
|
|
m[1] |= ((long) b[offset + 15]) << 56;
|
|
|
|
m[2] = ((long) b[offset + 16] & 0xFF);
|
|
m[2] |= ((long) b[offset + 17] & 0xFF) << 8;
|
|
m[2] |= ((long) b[offset + 18] & 0xFF) << 16;
|
|
m[2] |= ((long) b[offset + 19] & 0xFF) << 24;
|
|
m[2] |= ((long) b[offset + 20] & 0xFF) << 32;
|
|
m[2] |= ((long) b[offset + 21] & 0xFF) << 40;
|
|
m[2] |= ((long) b[offset + 22] & 0xFF) << 48;
|
|
m[2] |= ((long) b[offset + 23]) << 56;
|
|
|
|
m[3] = ((long) b[offset + 24] & 0xFF);
|
|
m[3] |= ((long) b[offset + 25] & 0xFF) << 8;
|
|
m[3] |= ((long) b[offset + 26] & 0xFF) << 16;
|
|
m[3] |= ((long) b[offset + 27] & 0xFF) << 24;
|
|
m[3] |= ((long) b[offset + 28] & 0xFF) << 32;
|
|
m[3] |= ((long) b[offset + 29] & 0xFF) << 40;
|
|
m[3] |= ((long) b[offset + 30] & 0xFF) << 48;
|
|
m[3] |= ((long) b[offset + 31]) << 56;
|
|
|
|
m[4] = ((long) b[offset + 32] & 0xFF);
|
|
m[4] |= ((long) b[offset + 33] & 0xFF) << 8;
|
|
m[4] |= ((long) b[offset + 34] & 0xFF) << 16;
|
|
m[4] |= ((long) b[offset + 35] & 0xFF) << 24;
|
|
m[4] |= ((long) b[offset + 36] & 0xFF) << 32;
|
|
m[4] |= ((long) b[offset + 37] & 0xFF) << 40;
|
|
m[4] |= ((long) b[offset + 38] & 0xFF) << 48;
|
|
m[4] |= ((long) b[offset + 39]) << 56;
|
|
|
|
m[5] = ((long) b[offset + 40] & 0xFF);
|
|
m[5] |= ((long) b[offset + 41] & 0xFF) << 8;
|
|
m[5] |= ((long) b[offset + 42] & 0xFF) << 16;
|
|
m[5] |= ((long) b[offset + 43] & 0xFF) << 24;
|
|
m[5] |= ((long) b[offset + 44] & 0xFF) << 32;
|
|
m[5] |= ((long) b[offset + 45] & 0xFF) << 40;
|
|
m[5] |= ((long) b[offset + 46] & 0xFF) << 48;
|
|
m[5] |= ((long) b[offset + 47]) << 56;
|
|
|
|
m[6] = ((long) b[offset + 48] & 0xFF);
|
|
m[6] |= ((long) b[offset + 49] & 0xFF) << 8;
|
|
m[6] |= ((long) b[offset + 50] & 0xFF) << 16;
|
|
m[6] |= ((long) b[offset + 51] & 0xFF) << 24;
|
|
m[6] |= ((long) b[offset + 52] & 0xFF) << 32;
|
|
m[6] |= ((long) b[offset + 53] & 0xFF) << 40;
|
|
m[6] |= ((long) b[offset + 54] & 0xFF) << 48;
|
|
m[6] |= ((long) b[offset + 55]) << 56;
|
|
|
|
m[7] = ((long) b[offset + 56] & 0xFF);
|
|
m[7] |= ((long) b[offset + 57] & 0xFF) << 8;
|
|
m[7] |= ((long) b[offset + 58] & 0xFF) << 16;
|
|
m[7] |= ((long) b[offset + 59] & 0xFF) << 24;
|
|
m[7] |= ((long) b[offset + 60] & 0xFF) << 32;
|
|
m[7] |= ((long) b[offset + 61] & 0xFF) << 40;
|
|
m[7] |= ((long) b[offset + 62] & 0xFF) << 48;
|
|
m[7] |= ((long) b[offset + 63]) << 56;
|
|
|
|
m[8] = ((long) b[offset + 64] & 0xFF);
|
|
m[8] |= ((long) b[offset + 65] & 0xFF) << 8;
|
|
m[8] |= ((long) b[offset + 66] & 0xFF) << 16;
|
|
m[8] |= ((long) b[offset + 67] & 0xFF) << 24;
|
|
m[8] |= ((long) b[offset + 68] & 0xFF) << 32;
|
|
m[8] |= ((long) b[offset + 69] & 0xFF) << 40;
|
|
m[8] |= ((long) b[offset + 70] & 0xFF) << 48;
|
|
m[8] |= ((long) b[offset + 71]) << 56;
|
|
|
|
m[9] = ((long) b[offset + 72] & 0xFF);
|
|
m[9] |= ((long) b[offset + 73] & 0xFF) << 8;
|
|
m[9] |= ((long) b[offset + 74] & 0xFF) << 16;
|
|
m[9] |= ((long) b[offset + 75] & 0xFF) << 24;
|
|
m[9] |= ((long) b[offset + 76] & 0xFF) << 32;
|
|
m[9] |= ((long) b[offset + 77] & 0xFF) << 40;
|
|
m[9] |= ((long) b[offset + 78] & 0xFF) << 48;
|
|
m[9] |= ((long) b[offset + 79]) << 56;
|
|
|
|
m[10] = ((long) b[offset + 80] & 0xFF);
|
|
m[10] |= ((long) b[offset + 81] & 0xFF) << 8;
|
|
m[10] |= ((long) b[offset + 82] & 0xFF) << 16;
|
|
m[10] |= ((long) b[offset + 83] & 0xFF) << 24;
|
|
m[10] |= ((long) b[offset + 84] & 0xFF) << 32;
|
|
m[10] |= ((long) b[offset + 85] & 0xFF) << 40;
|
|
m[10] |= ((long) b[offset + 86] & 0xFF) << 48;
|
|
m[10] |= ((long) b[offset + 87]) << 56;
|
|
|
|
m[11] = ((long) b[offset + 88] & 0xFF);
|
|
m[11] |= ((long) b[offset + 89] & 0xFF) << 8;
|
|
m[11] |= ((long) b[offset + 90] & 0xFF) << 16;
|
|
m[11] |= ((long) b[offset + 91] & 0xFF) << 24;
|
|
m[11] |= ((long) b[offset + 92] & 0xFF) << 32;
|
|
m[11] |= ((long) b[offset + 93] & 0xFF) << 40;
|
|
m[11] |= ((long) b[offset + 94] & 0xFF) << 48;
|
|
m[11] |= ((long) b[offset + 95]) << 56;
|
|
|
|
m[12] = ((long) b[offset + 96] & 0xFF);
|
|
m[12] |= ((long) b[offset + 97] & 0xFF) << 8;
|
|
m[12] |= ((long) b[offset + 98] & 0xFF) << 16;
|
|
m[12] |= ((long) b[offset + 99] & 0xFF) << 24;
|
|
m[12] |= ((long) b[offset + 100] & 0xFF) << 32;
|
|
m[12] |= ((long) b[offset + 101] & 0xFF) << 40;
|
|
m[12] |= ((long) b[offset + 102] & 0xFF) << 48;
|
|
m[12] |= ((long) b[offset + 103]) << 56;
|
|
|
|
m[13] = ((long) b[offset + 104] & 0xFF);
|
|
m[13] |= ((long) b[offset + 105] & 0xFF) << 8;
|
|
m[13] |= ((long) b[offset + 106] & 0xFF) << 16;
|
|
m[13] |= ((long) b[offset + 107] & 0xFF) << 24;
|
|
m[13] |= ((long) b[offset + 108] & 0xFF) << 32;
|
|
m[13] |= ((long) b[offset + 109] & 0xFF) << 40;
|
|
m[13] |= ((long) b[offset + 110] & 0xFF) << 48;
|
|
m[13] |= ((long) b[offset + 111]) << 56;
|
|
|
|
m[14] = ((long) b[offset + 112] & 0xFF);
|
|
m[14] |= ((long) b[offset + 113] & 0xFF) << 8;
|
|
m[14] |= ((long) b[offset + 114] & 0xFF) << 16;
|
|
m[14] |= ((long) b[offset + 115] & 0xFF) << 24;
|
|
m[14] |= ((long) b[offset + 116] & 0xFF) << 32;
|
|
m[14] |= ((long) b[offset + 117] & 0xFF) << 40;
|
|
m[14] |= ((long) b[offset + 118] & 0xFF) << 48;
|
|
m[14] |= ((long) b[offset + 119]) << 56;
|
|
|
|
m[15] = ((long) b[offset + 120] & 0xFF);
|
|
m[15] |= ((long) b[offset + 121] & 0xFF) << 8;
|
|
m[15] |= ((long) b[offset + 122] & 0xFF) << 16;
|
|
m[15] |= ((long) b[offset + 123] & 0xFF) << 24;
|
|
m[15] |= ((long) b[offset + 124] & 0xFF) << 32;
|
|
m[15] |= ((long) b[offset + 125] & 0xFF) << 40;
|
|
m[15] |= ((long) b[offset + 126] & 0xFF) << 48;
|
|
m[15] |= ((long) b[offset + 127]) << 56;
|
|
// Debug.dumpArray("m @ compress", m);
|
|
//
|
|
// Debug.dumpArray("h @ compress", h);
|
|
// Debug.dumpArray("t @ compress", t);
|
|
// Debug.dumpArray("f @ compress", f);
|
|
|
|
// set v registers
|
|
v[0] = h[0];
|
|
v[1] = h[1];
|
|
v[2] = h[2];
|
|
v[3] = h[3];
|
|
v[4] = h[4];
|
|
v[5] = h[5];
|
|
v[6] = h[6];
|
|
v[7] = h[7];
|
|
v[8] = 0x6a09e667f3bcc908L;
|
|
v[9] = 0xbb67ae8584caa73bL;
|
|
v[10] = 0x3c6ef372fe94f82bL;
|
|
v[11] = 0xa54ff53a5f1d36f1L;
|
|
v[12] = t[0] ^ 0x510e527fade682d1L;
|
|
v[13] = t[1] ^ 0x9b05688c2b3e6c1fL;
|
|
v[14] = f[0] ^ 0x1f83d9abfb41bd6bL;
|
|
v[15] = f[1] ^ 0x5be0cd19137e2179L;
|
|
|
|
// Debug.dumpArray("v @ compress", v);
|
|
// the rounds
|
|
// REVU: let's try unrolling this again TODO do & bench
|
|
for (int r = 0; r < 12; r++) {
|
|
|
|
/** G (r, 0, 0, 4, 8, 12); */
|
|
|
|
v[0] = v[0] + v[4] + m[sig_g00[r]];
|
|
v[12] ^= v[0];
|
|
v[12] = (v[12] << 32) | (v[12] >>> 32);
|
|
v[8] = v[8] + v[12];
|
|
v[4] ^= v[8];
|
|
v[4] = (v[4] >>> 24) | (v[4] << 40);
|
|
v[0] = v[0] + v[4] + m[sig_g01[r]];
|
|
v[12] ^= v[0];
|
|
v[12] = (v[12] >>> 16) | (v[12] << 48);
|
|
v[8] = v[8] + v[12];
|
|
v[4] ^= v[8];
|
|
v[4] = (v[4] << 1) | (v[4] >>> 63);
|
|
|
|
/** G (r, 1, 1, 5, 9, 13); */
|
|
|
|
v[1] = v[1] + v[5] + m[sig_g10[r]];
|
|
v[13] ^= v[1];
|
|
v[13] = (v[13] << 32) | (v[13] >>> 32);
|
|
v[9] = v[9] + v[13];
|
|
v[5] ^= v[9];
|
|
v[5] = (v[5] >>> 24) | (v[5] << 40);
|
|
v[1] = v[1] + v[5] + m[sig_g11[r]];
|
|
v[13] ^= v[1];
|
|
v[13] = (v[13] >>> 16) | (v[13] << 48);
|
|
v[9] = v[9] + v[13];
|
|
v[5] ^= v[9];
|
|
v[5] = (v[5] << 1) | (v[5] >>> 63);
|
|
|
|
/** G (r, 2, 2, 6, 10, 14); */
|
|
|
|
v[2] = v[2] + v[6] + m[sig_g20[r]];
|
|
v[14] ^= v[2];
|
|
v[14] = (v[14] << 32) | (v[14] >>> 32);
|
|
v[10] = v[10] + v[14];
|
|
v[6] ^= v[10];
|
|
v[6] = (v[6] >>> 24) | (v[6] << 40);
|
|
v[2] = v[2] + v[6] + m[sig_g21[r]];
|
|
v[14] ^= v[2];
|
|
v[14] = (v[14] >>> 16) | (v[14] << 48);
|
|
v[10] = v[10] + v[14];
|
|
v[6] ^= v[10];
|
|
v[6] = (v[6] << 1) | (v[6] >>> 63);
|
|
|
|
/** G (r, 3, 3, 7, 11, 15); */
|
|
|
|
v[3] = v[3] + v[7] + m[sig_g30[r]];
|
|
v[15] ^= v[3];
|
|
v[15] = (v[15] << 32) | (v[15] >>> 32);
|
|
v[11] = v[11] + v[15];
|
|
v[7] ^= v[11];
|
|
v[7] = (v[7] >>> 24) | (v[7] << 40);
|
|
v[3] = v[3] + v[7] + m[sig_g31[r]];
|
|
v[15] ^= v[3];
|
|
v[15] = (v[15] >>> 16) | (v[15] << 48);
|
|
v[11] = v[11] + v[15];
|
|
v[7] ^= v[11];
|
|
v[7] = (v[7] << 1) | (v[7] >>> 63);
|
|
|
|
/** G (r, 4, 0, 5, 10, 15); */
|
|
|
|
v[0] = v[0] + v[5] + m[sig_g40[r]];
|
|
v[15] ^= v[0];
|
|
v[15] = (v[15] << 32) | (v[15] >>> 32);
|
|
v[10] = v[10] + v[15];
|
|
v[5] ^= v[10];
|
|
v[5] = (v[5] >>> 24) | (v[5] << 40);
|
|
v[0] = v[0] + v[5] + m[sig_g41[r]];
|
|
v[15] ^= v[0];
|
|
v[15] = (v[15] >>> 16) | (v[15] << 48);
|
|
v[10] = v[10] + v[15];
|
|
v[5] ^= v[10];
|
|
v[5] = (v[5] << 1) | (v[5] >>> 63);
|
|
|
|
/** G (r, 5, 1, 6, 11, 12); */
|
|
|
|
v[1] = v[1] + v[6] + m[sig_g50[r]];
|
|
v[12] ^= v[1];
|
|
v[12] = (v[12] << 32) | (v[12] >>> 32);
|
|
v[11] = v[11] + v[12];
|
|
v[6] ^= v[11];
|
|
v[6] = (v[6] >>> 24) | (v[6] << 40);
|
|
v[1] = v[1] + v[6] + +m[sig_g51[r]];
|
|
v[12] ^= v[1];
|
|
v[12] = (v[12] >>> 16) | (v[12] << 48);
|
|
v[11] = v[11] + v[12];
|
|
v[6] ^= v[11];
|
|
v[6] = (v[6] << 1) | (v[6] >>> 63);
|
|
|
|
/** G (r, 6, 2, 7, 8, 13); */
|
|
|
|
v[2] = v[2] + v[7] + m[sig_g60[r]];
|
|
v[13] ^= v[2];
|
|
v[13] = (v[13] << 32) | (v[13] >>> 32);
|
|
v[8] = v[8] + v[13];
|
|
v[7] ^= v[8];
|
|
v[7] = (v[7] >>> 24) | (v[7] << 40);
|
|
v[2] = v[2] + v[7] + m[sig_g61[r]];
|
|
v[13] ^= v[2];
|
|
v[13] = (v[13] >>> 16) | (v[13] << 48);
|
|
v[8] = v[8] + v[13];
|
|
v[7] ^= v[8];
|
|
v[7] = (v[7] << 1) | (v[7] >>> 63);
|
|
|
|
/** G (r, 7, 3, 4, 9, 14); */
|
|
|
|
v[3] = v[3] + v[4] + m[sig_g70[r]];
|
|
v[14] ^= v[3];
|
|
v[14] = (v[14] << 32) | (v[14] >>> 32);
|
|
v[9] = v[9] + v[14];
|
|
v[4] ^= v[9];
|
|
v[4] = (v[4] >>> 24) | (v[4] << 40);
|
|
v[3] = v[3] + v[4] + m[sig_g71[r]];
|
|
v[14] ^= v[3];
|
|
v[14] = (v[14] >>> 16) | (v[14] << 48);
|
|
v[9] = v[9] + v[14];
|
|
v[4] ^= v[9];
|
|
v[4] = (v[4] << 1) | (v[4] >>> 63);
|
|
}
|
|
|
|
// Update state vector h
|
|
h[0] ^= v[0] ^ v[8];
|
|
h[1] ^= v[1] ^ v[9];
|
|
h[2] ^= v[2] ^ v[10];
|
|
h[3] ^= v[3] ^ v[11];
|
|
h[4] ^= v[4] ^ v[12];
|
|
h[5] ^= v[5] ^ v[13];
|
|
h[6] ^= v[6] ^ v[14];
|
|
h[7] ^= v[7] ^ v[15];
|
|
|
|
// Debug.dumpArray("v @ compress end", v);
|
|
// Debug.dumpArray("h @ compress end", h);
|
|
/* kaamil */
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
/// Compression Kernel /////////////////////////////////////////// BEGIN
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* a little bit of semantics
|
|
*/
|
|
interface flag {
|
|
int last_block = 0;
|
|
int last_node = 1;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
/// Compression Kernel //////////////////////////////////////////// FINI
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
/* TEMP - remove at will */
|
|
public static class Debug {
|
|
public static void dumpState(Blake2b.Engine e, final String mark) {
|
|
System.out.format("-- MARK == @ %s @ ===========\n", mark);
|
|
dumpArray("register t", e.t);
|
|
dumpArray("register h", e.h);
|
|
dumpArray("register f", e.f);
|
|
dumpArray("register offset", new long[] { e.buflen });
|
|
System.out.format("-- END MARK =================\n");
|
|
}
|
|
|
|
public static void dumpArray(final String label, final long[] b) {
|
|
System.out.format("-- %s -- :\n{\n", label);
|
|
for (int j = 0; j < b.length; ++j) {
|
|
System.out.format(" [%2d] : %016X\n", j, b[j]);
|
|
}
|
|
System.out.format("}\n");
|
|
}
|
|
|
|
public static void dumpBuffer(final PrintStream out, final String label, final byte[] b) {
|
|
dumpBuffer(out, label, b, 0, b.length);
|
|
}
|
|
|
|
public static void dumpBuffer(final PrintStream out, final byte[] b) {
|
|
dumpBuffer(out, null, b, 0, b.length);
|
|
}
|
|
|
|
public static void dumpBuffer(final PrintStream out, final byte[] b, final int offset, final int len) {
|
|
dumpBuffer(out, null, b, offset, len);
|
|
}
|
|
|
|
public static void dumpBuffer(final PrintStream out, final String label, final byte[] b, final int offset,
|
|
final int len) {
|
|
if (label != null)
|
|
out.format("-- %s -- :\n", label);
|
|
out.format("{\n ", label);
|
|
for (int j = 0; j < len; ++j) {
|
|
out.format("%02X", b[j + offset]);
|
|
if (j + 1 < len) {
|
|
if ((j + 1) % 8 == 0)
|
|
out.print("\n ");
|
|
else
|
|
out.print(' ');
|
|
}
|
|
}
|
|
out.format("\n}\n");
|
|
}
|
|
}
|
|
/* TEMP - remove at will */
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Helper for assert error messages
|
|
// ---------------------------------------------------------------------
|
|
public static final class Assert {
|
|
public final static String exclusiveUpperBound = "'%s' %d is >= %d";
|
|
public final static String inclusiveUpperBound = "'%s' %d is > %d";
|
|
public final static String exclusiveLowerBound = "'%s' %d is <= %d";
|
|
public final static String inclusiveLowerBound = "'%s' %d is < %d";
|
|
|
|
static <T extends Number> String assertFail(final String name, final T v, final String err, final T spec) {
|
|
new Exception().printStackTrace();
|
|
return String.format(err, name, v, spec);
|
|
}
|
|
}
|
|
// ---------------------------------------------------------------------
|
|
// Little Endian Codecs (inlined in the compressor)
|
|
/*
|
|
* impl note: these are not library funcs and used in hot loops, so no null or
|
|
* bounds checks are performed. For our purposes, this is OK.
|
|
*/
|
|
// ---------------------------------------------------------------------
|
|
|
|
public static class LittleEndian {
|
|
private static final byte[] hex_digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c',
|
|
'd', 'e', 'f' };
|
|
private static final byte[] HEX_digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
|
|
'D', 'E', 'F' };
|
|
|
|
/** @return hex rep of byte (lower case). */
|
|
static public String toHexStr(final byte[] b) {
|
|
return toHexStr(b, false); // because String class is slower.
|
|
}
|
|
|
|
static public String toHexStr(final byte[] b, boolean upperCase) {
|
|
final int len = b.length;
|
|
final byte[] digits = new byte[len * 2];
|
|
final byte[] hex_rep = upperCase ? HEX_digits : hex_digits;
|
|
for (int i = 0; i < len; i++) {
|
|
digits[i * 2] = hex_rep[(byte) (b[i] >> 4 & 0x0F)];
|
|
digits[i * 2 + 1] = hex_rep[(byte) (b[i] & 0x0F)];
|
|
}
|
|
return new String(digits);
|
|
}
|
|
|
|
public static int readInt(final byte[] b, int off) {
|
|
int v0 = ((int) b[off++] & 0xFF);
|
|
v0 |= ((int) b[off++] & 0xFF) << 8;
|
|
v0 |= ((int) b[off++] & 0xFF) << 16;
|
|
v0 |= ((int) b[off]) << 24;
|
|
return v0;
|
|
}
|
|
|
|
/** Little endian - byte[] to long */
|
|
public static long readLong(final byte[] b, int off) {
|
|
long v0 = ((long) b[off++] & 0xFF);
|
|
v0 |= ((long) b[off++] & 0xFF) << 8;
|
|
v0 |= ((long) b[off++] & 0xFF) << 16;
|
|
v0 |= ((long) b[off++] & 0xFF) << 24;
|
|
v0 |= ((long) b[off++] & 0xFF) << 32;
|
|
v0 |= ((long) b[off++] & 0xFF) << 40;
|
|
v0 |= ((long) b[off++] & 0xFF) << 48;
|
|
v0 |= ((long) b[off]) << 56;
|
|
return v0;
|
|
}
|
|
|
|
/** */
|
|
/** Little endian - long to byte[] */
|
|
public static void writeLong(long v, final byte[] b, final int off) {
|
|
b[off] = (byte) v;
|
|
v >>>= 8;
|
|
b[off + 1] = (byte) v;
|
|
v >>>= 8;
|
|
b[off + 2] = (byte) v;
|
|
v >>>= 8;
|
|
b[off + 3] = (byte) v;
|
|
v >>>= 8;
|
|
b[off + 4] = (byte) v;
|
|
v >>>= 8;
|
|
b[off + 5] = (byte) v;
|
|
v >>>= 8;
|
|
b[off + 6] = (byte) v;
|
|
v >>>= 8;
|
|
b[off + 7] = (byte) v;
|
|
}
|
|
|
|
/** Little endian - int to byte[] */
|
|
public static void writeInt(int v, final byte[] b, final int off) {
|
|
b[off] = (byte) v;
|
|
v >>>= 8;
|
|
b[off + 1] = (byte) v;
|
|
v >>>= 8;
|
|
b[off + 2] = (byte) v;
|
|
v >>>= 8;
|
|
b[off + 3] = (byte) v;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// digest parameter (block)
|
|
// ---------------------------------------------------------------------
|
|
/** Blake2b configuration parameters block per spec */
|
|
// REVU: need to review a revert back to non-lazy impl TODO: do & bench
|
|
public static class Param implements AlgorithmParameterSpec {
|
|
/**
|
|
* default bytes of Blake2b parameter block
|
|
*/
|
|
final static byte[] default_bytes = new byte[Spec.param_bytes];
|
|
/**
|
|
* default Blake2b h vector
|
|
*/
|
|
final static long[] default_h = new long[Spec.state_space_len];
|
|
|
|
/** initialize default_bytes */
|
|
static {
|
|
default_bytes[Xoff.digest_length] = Default.digest_length;
|
|
default_bytes[Xoff.key_length] = Default.key_length;
|
|
default_bytes[Xoff.fanout] = Default.fanout;
|
|
default_bytes[Xoff.depth] = Default.depth;
|
|
/* def. leaf_length is 0 fill and already set by new byte[] */
|
|
/* def. node_offset is 0 fill and already set by new byte[] */
|
|
default_bytes[Xoff.node_depth] = Default.node_depth;
|
|
default_bytes[Xoff.inner_length] = Default.inner_length;
|
|
/* def. salt is 0 fill and already set by new byte[] */
|
|
/* def. personal is 0 fill and already set by new byte[] */
|
|
}
|
|
|
|
static {
|
|
default_h[0] = readLong(default_bytes, 0);
|
|
default_h[1] = readLong(default_bytes, 8);
|
|
default_h[2] = readLong(default_bytes, 16);
|
|
default_h[3] = readLong(default_bytes, 24);
|
|
default_h[4] = readLong(default_bytes, 32);
|
|
default_h[5] = readLong(default_bytes, 40);
|
|
default_h[6] = readLong(default_bytes, 48);
|
|
default_h[7] = readLong(default_bytes, 56);
|
|
|
|
default_h[0] ^= Spec.IV[0];
|
|
default_h[1] ^= Spec.IV[1];
|
|
default_h[2] ^= Spec.IV[2];
|
|
default_h[3] ^= Spec.IV[3];
|
|
default_h[4] ^= Spec.IV[4];
|
|
default_h[5] ^= Spec.IV[5];
|
|
default_h[6] ^= Spec.IV[6];
|
|
default_h[7] ^= Spec.IV[7];
|
|
}
|
|
|
|
/** */
|
|
private final long[] h = new long[Spec.state_space_len];
|
|
/** */
|
|
private boolean hasKey = false;
|
|
/** not sure how to make this secure - TODO */
|
|
private byte[] key_bytes = null;
|
|
/** */
|
|
private byte[] bytes = null;
|
|
|
|
/** */
|
|
public Param() {
|
|
System.arraycopy(default_h, 0, h, 0, Spec.state_space_len);
|
|
}
|
|
|
|
/** */
|
|
public long[] initialized_H() {
|
|
return h;
|
|
}
|
|
|
|
/** package only - copy returned - do not use in functional loops */
|
|
public byte[] getBytes() {
|
|
lazyInitBytes();
|
|
byte[] copy = new byte[bytes.length];
|
|
System.arraycopy(bytes, 0, copy, 0, bytes.length);
|
|
return copy;
|
|
}
|
|
|
|
final byte getByteParam(final int xoffset) {
|
|
byte[] _bytes = bytes;
|
|
if (_bytes == null)
|
|
_bytes = Param.default_bytes;
|
|
return _bytes[xoffset];
|
|
}
|
|
|
|
final int getIntParam(final int xoffset) {
|
|
byte[] _bytes = bytes;
|
|
if (_bytes == null)
|
|
_bytes = Param.default_bytes;
|
|
return readInt(_bytes, xoffset);
|
|
}
|
|
|
|
final long getLongParam(final int xoffset) {
|
|
byte[] _bytes = bytes;
|
|
if (_bytes == null)
|
|
_bytes = Param.default_bytes;
|
|
return readLong(_bytes, xoffset);
|
|
}
|
|
|
|
// TODO same for tree params depth, fanout, inner, node-depth, node-offset
|
|
public final int getDigestLength() {
|
|
return (int) getByteParam(Xoff.digest_length);
|
|
}
|
|
|
|
/* 0-7 inclusive */
|
|
public final Param setDigestLength(int len) {
|
|
assert len > 0 : assertFail("len", len, exclusiveLowerBound, 0);
|
|
assert len <= Spec.max_digest_bytes : assertFail("len", len, inclusiveUpperBound, Spec.max_digest_bytes);
|
|
|
|
lazyInitBytes();
|
|
bytes[Xoff.digest_length] = (byte) len;
|
|
h[0] = readLong(bytes, 0);
|
|
h[0] ^= Spec.IV[0];
|
|
return this;
|
|
}
|
|
|
|
public final int getKeyLength() {
|
|
return (int) getByteParam(Xoff.key_length);
|
|
}
|
|
|
|
public final int getFanout() {
|
|
return (int) getByteParam(Xoff.fanout);
|
|
}
|
|
|
|
public final Param setFanout(int fanout) {
|
|
assert fanout > 0 : assertFail("fanout", fanout, exclusiveLowerBound, 0);
|
|
|
|
lazyInitBytes();
|
|
bytes[Xoff.fanout] = (byte) fanout;
|
|
h[0] = readLong(bytes, 0);
|
|
h[0] ^= Spec.IV[0];
|
|
return this;
|
|
}
|
|
|
|
public final int getDepth() {
|
|
return (int) getByteParam(Xoff.depth);
|
|
}
|
|
|
|
public final Param setDepth(int depth) {
|
|
assert depth > 0 : assertFail("depth", depth, exclusiveLowerBound, 0);
|
|
|
|
lazyInitBytes();
|
|
bytes[Xoff.depth] = (byte) depth;
|
|
h[0] = readLong(bytes, 0);
|
|
h[0] ^= Spec.IV[0];
|
|
return this;
|
|
}
|
|
|
|
public final int getLeafLength() {
|
|
return getIntParam(Xoff.leaf_length);
|
|
}
|
|
|
|
public final Param setLeafLength(int leaf_length) {
|
|
assert leaf_length >= 0 : assertFail("leaf_length", leaf_length, inclusiveLowerBound, 0);
|
|
|
|
lazyInitBytes();
|
|
writeInt(leaf_length, bytes, Xoff.leaf_length);
|
|
h[0] = readLong(bytes, 0);
|
|
h[0] ^= Spec.IV[0];
|
|
return this;
|
|
}
|
|
|
|
public final long getNodeOffset() {
|
|
return getLongParam(Xoff.node_offset);
|
|
}
|
|
|
|
/* 8-15 inclusive */
|
|
public final Param setNodeOffset(long node_offset) {
|
|
assert node_offset >= 0 : assertFail("node_offset", node_offset, inclusiveLowerBound, 0);
|
|
|
|
lazyInitBytes();
|
|
writeLong(node_offset, bytes, Xoff.node_offset);
|
|
h[1] = readLong(bytes, Xoff.node_offset);
|
|
h[1] ^= Spec.IV[1];
|
|
return this;
|
|
}
|
|
|
|
public final int getNodeDepth() {
|
|
return (int) getByteParam(Xoff.node_depth);
|
|
}
|
|
|
|
/* 16-23 inclusive */
|
|
public final Param setNodeDepth(int node_depth) {
|
|
assert node_depth >= 0 : assertFail("node_depth", node_depth, inclusiveLowerBound, 0);
|
|
|
|
lazyInitBytes();
|
|
bytes[Xoff.node_depth] = (byte) node_depth;
|
|
h[2] = readLong(bytes, Xoff.node_depth);
|
|
h[2] ^= Spec.IV[2];
|
|
h[3] = readLong(bytes, Xoff.node_depth + 8);
|
|
h[3] ^= Spec.IV[3];
|
|
return this;
|
|
}
|
|
|
|
public final int getInnerLength() {
|
|
return (int) getByteParam(Xoff.inner_length);
|
|
}
|
|
|
|
public final Param setInnerLength(int inner_length) {
|
|
assert inner_length >= 0 : assertFail("inner_length", inner_length, inclusiveLowerBound, 0);
|
|
|
|
lazyInitBytes();
|
|
bytes[Xoff.inner_length] = (byte) inner_length;
|
|
h[2] = readLong(bytes, Xoff.node_depth);
|
|
h[2] ^= Spec.IV[2];
|
|
h[3] = readLong(bytes, Xoff.node_depth + 8);
|
|
h[3] ^= Spec.IV[3];
|
|
return this;
|
|
}
|
|
|
|
public final boolean hasKey() {
|
|
return this.hasKey;
|
|
}
|
|
|
|
@Override
|
|
public Param clone() {
|
|
final Param clone = new Param();
|
|
System.arraycopy(this.h, 0, clone.h, 0, h.length);
|
|
clone.lazyInitBytes();
|
|
System.arraycopy(this.bytes, 0, clone.bytes, 0, this.bytes.length);
|
|
|
|
if (this.hasKey) {
|
|
clone.hasKey = this.hasKey;
|
|
clone.key_bytes = new byte[Spec.max_key_bytes * 2];
|
|
System.arraycopy(this.key_bytes, 0, clone.key_bytes, 0, this.key_bytes.length);
|
|
}
|
|
return clone;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
/// lazy setters - write directly to the bytes image of param block ////
|
|
////////////////////////////////////////////////////////////////////////
|
|
final void lazyInitBytes() {
|
|
if (bytes == null) {
|
|
bytes = new byte[Spec.param_bytes];
|
|
System.arraycopy(Param.default_bytes, 0, bytes, 0, Spec.param_bytes);
|
|
}
|
|
}
|
|
|
|
public final Param setKey(final Key key) {
|
|
assert key != null : "key is null";
|
|
final byte[] keybytes = key.getEncoded();
|
|
assert keybytes != null : "key.encoded() is null";
|
|
|
|
return this.setKey(keybytes);
|
|
}
|
|
|
|
public final Param setKey(final byte[] key) {
|
|
assert key != null : "key is null";
|
|
assert key.length >= 0 : assertFail("key.length", key.length, inclusiveUpperBound, 0);
|
|
assert key.length <= Spec.max_key_bytes
|
|
: assertFail("key.length", key.length, inclusiveUpperBound, Spec.max_key_bytes);
|
|
|
|
// zeropad keybytes
|
|
this.key_bytes = new byte[Spec.max_key_bytes * 2];
|
|
System.arraycopy(key, 0, this.key_bytes, 0, key.length);
|
|
lazyInitBytes();
|
|
bytes[Xoff.key_length] = (byte) key.length; // checked c ref; this is correct
|
|
h[0] = readLong(bytes, 0);
|
|
h[0] ^= Spec.IV[0];
|
|
this.hasKey = true;
|
|
return this;
|
|
}
|
|
|
|
/* 32-47 inclusive */
|
|
public final Param setSalt(final byte[] salt) {
|
|
assert salt != null : "salt is null";
|
|
assert salt.length <= Spec.max_salt_bytes
|
|
: assertFail("salt.length", salt.length, inclusiveUpperBound, Spec.max_salt_bytes);
|
|
|
|
lazyInitBytes();
|
|
Arrays.fill(bytes, Xoff.salt, Xoff.salt + Spec.max_salt_bytes, (byte) 0);
|
|
System.arraycopy(salt, 0, bytes, Xoff.salt, salt.length);
|
|
h[4] = readLong(bytes, Xoff.salt);
|
|
h[4] ^= Spec.IV[4];
|
|
h[5] = readLong(bytes, Xoff.salt + 8);
|
|
h[5] ^= Spec.IV[5];
|
|
return this;
|
|
}
|
|
|
|
/* 48-63 inclusive */
|
|
public final Param setPersonal(byte[] personal) {
|
|
assert personal != null : "personal is null";
|
|
assert personal.length <= Spec.max_personalization_bytes : assertFail("personal.length", personal.length,
|
|
inclusiveUpperBound, Spec.max_personalization_bytes);
|
|
|
|
lazyInitBytes();
|
|
Arrays.fill(bytes, Xoff.personal, Xoff.personal + Spec.max_personalization_bytes, (byte) 0);
|
|
System.arraycopy(personal, 0, bytes, Xoff.personal, personal.length);
|
|
h[6] = readLong(bytes, Xoff.personal);
|
|
h[6] ^= Spec.IV[6];
|
|
h[7] = readLong(bytes, Xoff.personal + 8);
|
|
h[7] ^= Spec.IV[7];
|
|
return this;
|
|
}
|
|
|
|
/* 24-31 masked by reserved and remain unchanged */
|
|
|
|
interface Xoff {
|
|
int digest_length = 0;
|
|
int key_length = 1;
|
|
int fanout = 2;
|
|
int depth = 3;
|
|
int leaf_length = 4;
|
|
int node_offset = 8;
|
|
int node_depth = 16;
|
|
int inner_length = 17;
|
|
int reserved = 18;
|
|
int salt = 32;
|
|
int personal = 48;
|
|
}
|
|
|
|
public interface Default {
|
|
byte digest_length = Spec.max_digest_bytes;
|
|
byte key_length = 0;
|
|
byte fanout = 1;
|
|
byte depth = 1;
|
|
int leaf_length = 0;
|
|
long node_offset = 0;
|
|
byte node_depth = 0;
|
|
byte inner_length = 0;
|
|
}
|
|
////////////////////////////////////////////////////////////////////////
|
|
/// lazy setters /////////////////////////////////////////////////// END
|
|
////////////////////////////////////////////////////////////////////////
|
|
}
|
|
}
|