diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java b/core/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java index da22fa48cc..815544ee8a 100644 --- a/core/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java +++ b/core/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java @@ -7,11 +7,62 @@ import org.bouncycastle.crypto.util.Pack; import org.bouncycastle.util.Arrays; +/** + * Implementation of the scrypt a password-based key derivation function. + *

+ * Scrypt was created by Colin Percival and is specified in draft-josefsson-scrypt-kd + * + */ public class SCrypt { - // TODO Validate arguments + /** + * 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) + { + throw new IllegalArgumentException("Cost parameter N must be > 1."); + } + // 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); } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SCryptTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SCryptTest.java index 1471dafa24..b017a1da51 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/SCryptTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SCryptTest.java @@ -20,6 +20,54 @@ public String getName() } public void performTest() throws Exception + { + testParameters(); + testVectors(); + } + + public void testParameters() + { + checkOK("Minimal values", new byte[0], new byte[0], 2, 1, 1, 1); + checkIllegal("Cost parameter must be > 1", new byte[0], new byte[0], 1, 1, 1, 1); + checkOK("Cost parameter 65536 OK for r == 1", new byte[0], new byte[0], 65536, 1, 1, 1); + checkIllegal("Cost parameter must <= 65536 for r == 1", new byte[0], new byte[0], 65537, 1, 1, 1); + checkIllegal("Block size must be >= 1", new byte[0], new byte[0], 2, 0, 2, 1); + checkIllegal("Parallelisation parameter must be >= 1", new byte[0], new byte[0], 2, 1, 0, 1); + // checkOK("Parallelisation parameter 65535 OK for r = 4", new byte[0], new byte[0], 2, 32, + // 65535, 1); + checkIllegal("Parallelisation parameter must be < 65535 for r = 4", new byte[0], new byte[0], 2, 32, 65536, 1); + + checkIllegal("Len parameter must be > 1", new byte[0], new byte[0], 2, 1, 1, 0); + } + + private void checkOK(String msg, byte[] pass, byte[] salt, int N, int r, int p, int len) + { + try + { + SCrypt.generate(pass, salt, N, r, p, len); + } + catch (IllegalArgumentException e) + { + e.printStackTrace(); + fail(msg); + } + } + + private void checkIllegal(String msg, byte[] pass, byte[] salt, int N, int r, int p, int len) + { + try + { + SCrypt.generate(pass, salt, N, r, p, len); + fail(msg); + } + catch (IllegalArgumentException e) + { + // e.printStackTrace(); + } + } + + public void testVectors() + throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader( getClass().getResourceAsStream("SCryptTestVectors.txt")));