All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bouncycastle.pqc.crypto.sphincs.SPHINCS256Signer Maven / Gradle / Ivy

Go to download

The Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains JCE provider and lightweight API for the Bouncy Castle Cryptography APIs for Java 1.8 and later with debug enabled.

The newest version!
package org.bouncycastle.pqc.crypto.sphincs;

import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.pqc.crypto.MessageSigner;
import org.bouncycastle.util.Pack;

/**
 * SPHINCS-256 signer.
 * 

* This implementation is heavily based on the reference implementation in SUPERCOP, the main difference being the digests used * for message hashing and tree construction are now configurable (within limits...) and that the implementation produces * detached signatures. *

*

* The SPHINCS reference implementation is public domain, as per the statement in the second last paragraph of * section 1 in https://eprint.iacr.org/2014/795.pdf *

*/ public class SPHINCS256Signer implements MessageSigner { private final HashFunctions hashFunctions; private byte[] keyData; /** * Base constructor. * * @param nDigest the "n-digest" must produce 32 bytes of output - used for tree construction. * @param twoNDigest the "2n-digest" must produce 64 bytes of output - used for initial message/key/seed hashing. */ public SPHINCS256Signer(Digest nDigest, Digest twoNDigest) { if (nDigest.getDigestSize() != 32) { throw new IllegalArgumentException("n-digest needs to produce 32 bytes of output"); } if (twoNDigest.getDigestSize() != 64) { throw new IllegalArgumentException("2n-digest needs to produce 64 bytes of output"); } this.hashFunctions = new HashFunctions(nDigest, twoNDigest); } public void init(boolean forSigning, CipherParameters param) { if (forSigning) { if (param instanceof ParametersWithRandom) { // SPHINCS-256 signatures are deterministic, RNG is not required. keyData = ((SPHINCSPrivateKeyParameters)((ParametersWithRandom)param).getParameters()).getKeyData(); } else { keyData = ((SPHINCSPrivateKeyParameters)param).getKeyData(); } } else { keyData = ((SPHINCSPublicKeyParameters)param).getKeyData(); } } public byte[] generateSignature(byte[] message) { return crypto_sign(hashFunctions, message, keyData); } public boolean verifySignature(byte[] message, byte[] signature) { return verify(hashFunctions, message, signature, keyData); } static void validate_authpath(HashFunctions hs, byte[] root, byte[] leaf, int leafidx, byte[] authpath, int auOff, byte[] masks, int height) { int i, j; byte[] buffer = new byte[2 * SPHINCS256Config.HASH_BYTES]; if ((leafidx & 1) != 0) { for (j = 0; j < SPHINCS256Config.HASH_BYTES; j++) { buffer[SPHINCS256Config.HASH_BYTES + j] = leaf[j]; } for (j = 0; j < SPHINCS256Config.HASH_BYTES; j++) { buffer[j] = authpath[auOff + j]; } } else { for (j = 0; j < SPHINCS256Config.HASH_BYTES; j++) { buffer[j] = leaf[j]; } for (j = 0; j < SPHINCS256Config.HASH_BYTES; j++) { buffer[SPHINCS256Config.HASH_BYTES + j] = authpath[auOff + j]; } } int authOff = auOff + SPHINCS256Config.HASH_BYTES; for (i = 0; i < height - 1; i++) { leafidx >>>= 1; if ((leafidx & 1) != 0) { hs.hash_2n_n_mask(buffer, SPHINCS256Config.HASH_BYTES, buffer, 0, masks, 2 * (Wots.WOTS_LOG_L + i) * SPHINCS256Config.HASH_BYTES); for (j = 0; j < SPHINCS256Config.HASH_BYTES; j++) { buffer[j] = authpath[authOff + j]; } } else { hs.hash_2n_n_mask(buffer, 0, buffer, 0, masks, 2 * (Wots.WOTS_LOG_L + i) * SPHINCS256Config.HASH_BYTES); for (j = 0; j < SPHINCS256Config.HASH_BYTES; j++) { buffer[j + SPHINCS256Config.HASH_BYTES] = authpath[authOff + j]; } } authOff += SPHINCS256Config.HASH_BYTES; } hs.hash_2n_n_mask(root, 0, buffer, 0, masks, 2 * (Wots.WOTS_LOG_L + height - 1) * SPHINCS256Config.HASH_BYTES); } static void compute_authpath_wots(HashFunctions hs, byte[] root, byte[] authpath, int authOff, Tree.leafaddr a, byte[] sk, byte[] masks, int height) { int i, idx, j; Tree.leafaddr ta = new Tree.leafaddr(a); byte[] tree = new byte[2 * (1 << SPHINCS256Config.SUBTREE_HEIGHT) * SPHINCS256Config.HASH_BYTES]; byte[] seed = new byte[(1 << SPHINCS256Config.SUBTREE_HEIGHT) * SPHINCS256Config.SEED_BYTES]; byte[] pk = new byte[(1 << SPHINCS256Config.SUBTREE_HEIGHT) * Wots.WOTS_L * SPHINCS256Config.HASH_BYTES]; // level 0 for (ta.subleaf = 0; ta.subleaf < (1 << SPHINCS256Config.SUBTREE_HEIGHT); ta.subleaf++) { Seed.get_seed(hs, seed, (int)(ta.subleaf * SPHINCS256Config.SEED_BYTES), sk, ta); } Wots w = new Wots(); for (ta.subleaf = 0; ta.subleaf < (1 << SPHINCS256Config.SUBTREE_HEIGHT); ta.subleaf++) { w.wots_pkgen(hs, pk, (int)(ta.subleaf * Wots.WOTS_L * SPHINCS256Config.HASH_BYTES), seed, (int)(ta.subleaf * SPHINCS256Config.SEED_BYTES), masks, 0); } for (ta.subleaf = 0; ta.subleaf < (1 << SPHINCS256Config.SUBTREE_HEIGHT); ta.subleaf++) { Tree.l_tree(hs, tree, (int)((1 << SPHINCS256Config.SUBTREE_HEIGHT) * SPHINCS256Config.HASH_BYTES + ta.subleaf * SPHINCS256Config.HASH_BYTES), pk, (int)(ta.subleaf * Wots.WOTS_L * SPHINCS256Config.HASH_BYTES), masks, 0); } int level = 0; // tree for (i = (1 << SPHINCS256Config.SUBTREE_HEIGHT); i > 0; i >>>= 1) { for (j = 0; j < i; j += 2) { hs.hash_2n_n_mask(tree, (i >>> 1) * SPHINCS256Config.HASH_BYTES + (j >>> 1) * SPHINCS256Config.HASH_BYTES, tree, i * SPHINCS256Config.HASH_BYTES + j * SPHINCS256Config.HASH_BYTES, masks, 2 * (Wots.WOTS_LOG_L + level) * SPHINCS256Config.HASH_BYTES); } level++; } idx = (int)a.subleaf; // copy authpath for (i = 0; i < height; i++) { System.arraycopy(tree, ((1 << SPHINCS256Config.SUBTREE_HEIGHT) >>> i) * SPHINCS256Config.HASH_BYTES + ((idx >>> i) ^ 1) * SPHINCS256Config.HASH_BYTES, authpath, authOff + i * SPHINCS256Config.HASH_BYTES, SPHINCS256Config.HASH_BYTES); } // copy root System.arraycopy(tree, SPHINCS256Config.HASH_BYTES, root, 0, SPHINCS256Config.HASH_BYTES); } byte[] crypto_sign(HashFunctions hs, byte[] m, byte[] sk) { byte[] sm = new byte[SPHINCS256Config.CRYPTO_BYTES]; int i; long leafidx; byte[] R = new byte[SPHINCS256Config.MESSAGE_HASH_SEED_BYTES]; byte[] m_h = new byte[SPHINCS256Config.MSGHASH_BYTES]; long[] rnd = new long[8]; byte[] root = new byte[SPHINCS256Config.HASH_BYTES]; byte[] seed = new byte[SPHINCS256Config.SEED_BYTES]; byte[] masks = new byte[Horst.N_MASKS * SPHINCS256Config.HASH_BYTES]; int pk; byte[] tsk = new byte[SPHINCS256Config.CRYPTO_SECRETKEYBYTES]; for (i = 0; i < SPHINCS256Config.CRYPTO_SECRETKEYBYTES; i++) { tsk[i] = sk[i]; } // create leafidx deterministically { // shift scratch upwards so we can reuse msg later int scratch = SPHINCS256Config.CRYPTO_BYTES - SPHINCS256Config.SK_RAND_SEED_BYTES; // Copy secret random seed to scratch System.arraycopy(tsk, SPHINCS256Config.CRYPTO_SECRETKEYBYTES - SPHINCS256Config.SK_RAND_SEED_BYTES, sm, scratch, SPHINCS256Config.SK_RAND_SEED_BYTES); Digest d = hs.getMessageHash(); byte[] bRnd = new byte[d.getDigestSize()]; d.update(sm, scratch, SPHINCS256Config.SK_RAND_SEED_BYTES); d.update(m, 0, m.length); d.doFinal(bRnd, 0); // wipe sk zerobytes(sm, scratch, SPHINCS256Config.SK_RAND_SEED_BYTES); for (int j = 0; j != rnd.length; j++) { rnd[j] = Pack.littleEndianToLong(bRnd, j * 8); } leafidx = rnd[0] & 0xfffffffffffffffL; System.arraycopy(bRnd, 16, R, 0, SPHINCS256Config.MESSAGE_HASH_SEED_BYTES); // prepare msg_hash scratch = SPHINCS256Config.CRYPTO_BYTES - SPHINCS256Config.MESSAGE_HASH_SEED_BYTES - SPHINCS256Config.CRYPTO_PUBLICKEYBYTES; // cpy R System.arraycopy(R, 0, sm, scratch, SPHINCS256Config.MESSAGE_HASH_SEED_BYTES); // construct and cpy pk Tree.leafaddr b = new Tree.leafaddr(); b.level = SPHINCS256Config.N_LEVELS - 1; b.subtree = 0; b.subleaf = 0; pk = scratch + SPHINCS256Config.MESSAGE_HASH_SEED_BYTES; System.arraycopy(tsk, SPHINCS256Config.SEED_BYTES, sm, pk, Horst.N_MASKS * SPHINCS256Config.HASH_BYTES); Tree.treehash(hs, sm, pk + (Horst.N_MASKS * SPHINCS256Config.HASH_BYTES), SPHINCS256Config.SUBTREE_HEIGHT, tsk, b, sm, pk); d = hs.getMessageHash(); d.update(sm, scratch, SPHINCS256Config.MESSAGE_HASH_SEED_BYTES + SPHINCS256Config.CRYPTO_PUBLICKEYBYTES); d.update(m, 0, m.length); d.doFinal(m_h, 0); } Tree.leafaddr a = new Tree.leafaddr(); a.level = SPHINCS256Config.N_LEVELS; // Use unique value $d$ for HORST address. a.subleaf = (int)(leafidx & ((1 << SPHINCS256Config.SUBTREE_HEIGHT) - 1)); a.subtree = leafidx >>> SPHINCS256Config.SUBTREE_HEIGHT; for (i = 0; i < SPHINCS256Config.MESSAGE_HASH_SEED_BYTES; i++) { sm[i] = R[i]; } int smOff = SPHINCS256Config.MESSAGE_HASH_SEED_BYTES; System.arraycopy(tsk, SPHINCS256Config.SEED_BYTES, masks, 0, Horst.N_MASKS * SPHINCS256Config.HASH_BYTES); for (i = 0; i < (SPHINCS256Config.TOTALTREE_HEIGHT + 7) / 8; i++) { sm[smOff + i] = (byte)((leafidx >>> 8 * i) & 0xff); } smOff += (SPHINCS256Config.TOTALTREE_HEIGHT + 7) / 8; Seed.get_seed(hs, seed, 0, tsk, a); Horst ht = new Horst(); int horst_sigbytes = ht.horst_sign(hs, sm, smOff, root, seed, masks, m_h); smOff += horst_sigbytes; Wots w = new Wots(); for (i = 0; i < SPHINCS256Config.N_LEVELS; i++) { a.level = i; Seed.get_seed(hs, seed, 0, tsk, a); //XXX: Don't use the same address as for horst_sign here! w.wots_sign(hs, sm, smOff, root, seed, masks); smOff += Wots.WOTS_SIGBYTES; compute_authpath_wots(hs, root, sm, smOff, a, tsk, masks, SPHINCS256Config.SUBTREE_HEIGHT); smOff += SPHINCS256Config.SUBTREE_HEIGHT * SPHINCS256Config.HASH_BYTES; a.subleaf = (int)(a.subtree & ((1 << SPHINCS256Config.SUBTREE_HEIGHT) - 1)); a.subtree >>>= SPHINCS256Config.SUBTREE_HEIGHT; } zerobytes(tsk, 0, SPHINCS256Config.CRYPTO_SECRETKEYBYTES); return sm; } private void zerobytes(byte[] tsk, int off, int cryptoSecretkeybytes) { for (int i = 0; i != cryptoSecretkeybytes; i++) { tsk[off + i] = 0; } } boolean verify(HashFunctions hs, byte[] m, byte[] sm, byte[] pk) { int i; int smlen = sm.length; long leafidx = 0; byte[] wots_pk = new byte[Wots.WOTS_L * SPHINCS256Config.HASH_BYTES]; byte[] pkhash = new byte[SPHINCS256Config.HASH_BYTES]; byte[] root = new byte[SPHINCS256Config.HASH_BYTES]; byte[] sig = new byte[SPHINCS256Config.CRYPTO_BYTES]; int sigp; byte[] tpk = new byte[SPHINCS256Config.CRYPTO_PUBLICKEYBYTES]; if (smlen != SPHINCS256Config.CRYPTO_BYTES) { throw new IllegalArgumentException("signature wrong size"); } byte[] m_h = new byte[SPHINCS256Config.MSGHASH_BYTES]; for (i = 0; i < SPHINCS256Config.CRYPTO_PUBLICKEYBYTES; i++) { tpk[i] = pk[i]; } // construct message hash { byte[] R = new byte[SPHINCS256Config.MESSAGE_HASH_SEED_BYTES]; for (i = 0; i < SPHINCS256Config.MESSAGE_HASH_SEED_BYTES; i++) { R[i] = sm[i]; } System.arraycopy(sm, 0, sig, 0, SPHINCS256Config.CRYPTO_BYTES); Digest mHash = hs.getMessageHash(); // input R mHash.update(R, 0, SPHINCS256Config.MESSAGE_HASH_SEED_BYTES); // input pub key mHash.update(tpk, 0, SPHINCS256Config.CRYPTO_PUBLICKEYBYTES); // input message mHash.update(m, 0, m.length); mHash.doFinal(m_h, 0); } sigp = 0; sigp += SPHINCS256Config.MESSAGE_HASH_SEED_BYTES; smlen -= SPHINCS256Config.MESSAGE_HASH_SEED_BYTES; for (i = 0; i < (SPHINCS256Config.TOTALTREE_HEIGHT + 7) / 8; i++) { leafidx ^= ((long)(sig[sigp + i] & 0xff) << (8 * i)); } new Horst().horst_verify(hs, root, sig, sigp + (SPHINCS256Config.TOTALTREE_HEIGHT + 7) / 8, tpk, m_h); sigp += (SPHINCS256Config.TOTALTREE_HEIGHT + 7) / 8; smlen -= (SPHINCS256Config.TOTALTREE_HEIGHT + 7) / 8; sigp += Horst.HORST_SIGBYTES; smlen -= Horst.HORST_SIGBYTES; Wots w = new Wots(); for (i = 0; i < SPHINCS256Config.N_LEVELS; i++) { w.wots_verify(hs, wots_pk, sig, sigp, root, tpk); sigp += Wots.WOTS_SIGBYTES; smlen -= Wots.WOTS_SIGBYTES; Tree.l_tree(hs, pkhash, 0, wots_pk, 0, tpk, 0); validate_authpath(hs, root, pkhash, (int)(leafidx & 0x1f), sig, sigp, tpk, SPHINCS256Config.SUBTREE_HEIGHT); leafidx >>= 5; sigp += SPHINCS256Config.SUBTREE_HEIGHT * SPHINCS256Config.HASH_BYTES; smlen -= SPHINCS256Config.SUBTREE_HEIGHT * SPHINCS256Config.HASH_BYTES; } boolean verified = true; for (i = 0; i < SPHINCS256Config.HASH_BYTES; i++) { if (root[i] != tpk[i + Horst.N_MASKS * SPHINCS256Config.HASH_BYTES]) { verified = false; } } return verified; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy