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

org.bouncycastle.crypto.generators.SCrypt Maven / Gradle / Ivy

There is a newer version: 1.70_1
Show newest version
package org.bouncycastle.crypto.generators;

import org.bouncycastle.crypto.PBEParametersGenerator;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.engines.Salsa20Engine;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Integers;
import org.bouncycastle.util.Pack;

/**
 * Implementation of the scrypt a password-based key derivation function.
 * 

* Scrypt was created by Colin Percival and is specified in RFC 7914 - The scrypt Password-Based Key Derivation Function */ public class SCrypt { private SCrypt() { // not used. } /** * Generate a key using the scrypt key derivation function. * * @param P the bytes of the pass phrase. * @param S the salt to use for this invocation. * @param N CPU/Memory cost parameter. Must be larger than 1, a power of 2 and less than * 2^(128 * r / 8). * @param r the block size, must be >= 1. * @param p Parallelization parameter. Must be a positive integer less than or equal to * Integer.MAX_VALUE / (128 * r * 8). * @param dkLen the length of the key to generate. * @return the generated key. */ public static byte[] generate(byte[] P, byte[] S, int N, int r, int p, int dkLen) { if (P == null) { throw new IllegalArgumentException("Passphrase P must be provided."); } if (S == null) { throw new IllegalArgumentException("Salt S must be provided."); } if (N <= 1 || !isPowerOf2(N)) { throw new IllegalArgumentException("Cost parameter N must be > 1 and a power of 2"); } // Only value of r that cost (as an int) could be exceeded for is 1 if (r == 1 && N >= 65536) { throw new IllegalArgumentException("Cost parameter N must be > 1 and < 65536."); } if (r < 1) { throw new IllegalArgumentException("Block size r must be >= 1."); } int maxParallel = Integer.MAX_VALUE / (128 * r * 8); if (p < 1 || p > maxParallel) { throw new IllegalArgumentException("Parallelisation parameter p must be >= 1 and <= " + maxParallel + " (based on block size r of " + r + ")"); } if (dkLen < 1) { throw new IllegalArgumentException("Generated key length dkLen must be >= 1."); } return MFcrypt(P, S, N, r, p, dkLen); } private static byte[] MFcrypt(byte[] P, byte[] S, int N, int r, int p, int dkLen) { int MFLenBytes = r * 128; byte[] bytes = SingleIterationPBKDF2(P, S, p * MFLenBytes); int[] B = null; try { int BLen = bytes.length >>> 2; B = new int[BLen]; Pack.littleEndianToInt(bytes, 0, B); /* * Chunk memory allocations; We choose 'd' so that there will be 2**d chunks, each not * larger than 32KiB, except that the minimum chunk size is 2 * r * 32. */ int d = 0, total = N * r; while ((N - d) > 2 && total > (1 << 10)) { ++d; total >>>= 1; } int MFLenWords = MFLenBytes >>> 2; for (int BOff = 0; BOff < BLen; BOff += MFLenWords) { // TODO These can be done in parallel threads SMix(B, BOff, N, d, r); } Pack.intToLittleEndian(B, bytes, 0); return SingleIterationPBKDF2(P, bytes, dkLen); } finally { Clear(bytes); Clear(B); } } private static byte[] SingleIterationPBKDF2(byte[] P, byte[] S, int dkLen) { PBEParametersGenerator pGen = new PKCS5S2ParametersGenerator(new SHA256Digest()); pGen.init(P, S, 1); KeyParameter key = (KeyParameter)pGen.generateDerivedMacParameters(dkLen * 8); return key.getKey(); } private static void SMix(int[] B, int BOff, int N, int d, int r) { int powN = Integers.numberOfTrailingZeros(N); int blocksPerChunk = N >>> d; int chunkCount = 1 << d, chunkMask = blocksPerChunk - 1, chunkPow = powN - d; int BCount = r * 32; int[] blockX1 = new int[16]; int[] blockX2 = new int[16]; int[] blockY = new int[BCount]; int[] X = new int[BCount]; int[][] VV = new int[chunkCount][]; try { System.arraycopy(B, BOff, X, 0, BCount); for (int c = 0; c < chunkCount; ++c) { int[] V = new int[blocksPerChunk * BCount]; VV[c] = V; int off = 0; for (int i = 0; i < blocksPerChunk; i += 2) { System.arraycopy(X, 0, V, off, BCount); off += BCount; BlockMix(X, blockX1, blockX2, blockY, r); System.arraycopy(blockY, 0, V, off, BCount); off += BCount; BlockMix(blockY, blockX1, blockX2, X, r); } } int mask = N - 1; for (int i = 0; i < N; ++i) { int j = X[BCount - 16] & mask; int[] V = VV[j >>> chunkPow]; int VOff = (j & chunkMask) * BCount; System.arraycopy(V, VOff, blockY, 0, BCount); Xor(blockY, X, 0, blockY); BlockMix(blockY, blockX1, blockX2, X, r); } System.arraycopy(X, 0, B, BOff, BCount); } finally { ClearAll(VV); ClearAll(new int[][]{X, blockX1, blockX2, blockY}); } } private static void BlockMix(int[] B, int[] X1, int[] X2, int[] Y, int r) { System.arraycopy(B, B.length - 16, X1, 0, 16); int BOff = 0, YOff = 0, halfLen = B.length >>> 1; for (int i = 2 * r; i > 0; --i) { Xor(X1, B, BOff, X2); Salsa20Engine.salsaCore(8, X2, X1); System.arraycopy(X1, 0, Y, YOff, 16); YOff = halfLen + BOff - YOff; BOff += 16; } } private static void Xor(int[] a, int[] b, int bOff, int[] output) { for (int i = output.length - 1; i >= 0; --i) { output[i] = a[i] ^ b[bOff + i]; } } private static void Clear(byte[] array) { if (array != null) { Arrays.fill(array, (byte)0); } } private static void Clear(int[] array) { if (array != null) { Arrays.fill(array, 0); } } private static void ClearAll(int[][] arrays) { for (int i = 0; i < arrays.length; ++i) { Clear(arrays[i]); } } // note: we know X is non-zero private static boolean isPowerOf2(int x) { return ((x & (x - 1)) == 0); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy