BCryptTest.java
// Copyright (c) 2006 Damien Miller <djm@mindrot.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package com.jcraft.jsch.jbcrypt;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Arrays;
import org.junit.jupiter.api.Test;
/**
* JUnit unit tests for BCrypt routines
*
* @author Damien Miller
* @version 0.2
*/
public class BCryptTest {
String test_vectors[][] = {
{"", "$2a$06$DCq7YPn5Rq63x1Lad4cll.",
"$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s."},
{"", "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.",
"$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye"},
{"", "$2a$10$k1wbIrmNyFAPwPVPSVa/ze",
"$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW"},
{"", "$2a$12$k42ZFHFWqBp3vWli.nIn8u",
"$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO"},
{"a", "$2a$06$m0CrhHm10qJ3lXRY.5zDGO",
"$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe"},
{"a", "$2a$08$cfcvVd2aQ8CMvoMpP2EBfe",
"$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V."},
{"a", "$2a$10$k87L/MF28Q673VKh8/cPi.",
"$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u"},
{"a", "$2a$12$8NJH3LsPrANStV6XtBakCe",
"$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS"},
{"abc", "$2a$06$If6bvum7DFjUnE9p2uDeDu",
"$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i"},
{"abc", "$2a$08$Ro0CUfOqk6cXEKf3dyaM7O",
"$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm"},
{"abc", "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.",
"$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi"},
{"abc", "$2a$12$EXRkfkdmXn2gzds2SSitu.",
"$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q"},
{"abcdefghijklmnopqrstuvwxyz", "$2a$06$.rCVZVOThsIa97pEDOxvGu",
"$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC"},
{"abcdefghijklmnopqrstuvwxyz", "$2a$08$aTsUwsyowQuzRrDqFflhge",
"$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz."},
{"abcdefghijklmnopqrstuvwxyz", "$2a$10$fVH8e28OQRj9tqiDXs1e1u",
"$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq"},
{"abcdefghijklmnopqrstuvwxyz", "$2a$12$D4G5f18o7aMMfwasBL7Gpu",
"$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG"},
{"~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$06$fPIsBO8qRqkjj273rfaOI.",
"$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO"},
{"~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$08$Eq2r4G/76Wv39MzSX262hu",
"$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW"},
{"~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$10$LgfYWkbzEvQ4JakH7rOvHe",
"$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS"},
{"~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$12$WApznUOJfkEGSmYRfnkrPO",
"$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC"},};
/** Test method for 'BCrypt.hashpw(String, String)' */
@Test
public void testHashpw() {
// System.out.print("BCrypt.hashpw(): ");
for (int i = 0; i < test_vectors.length; i++) {
String plain = test_vectors[i][0];
String salt = test_vectors[i][1];
String expected = test_vectors[i][2];
String hashed = BCrypt.hashpw(plain, salt);
assertEquals(hashed, expected);
// System.out.print(".");
}
// System.out.println("");
}
/** Test method for 'BCrypt.gensalt(int)' */
@Test
public void testGensaltInt() {
// System.out.print("BCrypt.gensalt(log_rounds):");
for (int i = 4; i <= 12; i++) {
// System.out.print(" " + Integer.toString(i) + ":");
for (int j = 0; j < test_vectors.length; j += 4) {
String plain = test_vectors[j][0];
String salt = BCrypt.gensalt(i);
String hashed1 = BCrypt.hashpw(plain, salt);
String hashed2 = BCrypt.hashpw(plain, hashed1);
assertEquals(hashed1, hashed2);
// System.out.print(".");
}
}
// System.out.println("");
}
/** Test method for 'BCrypt.gensalt()' */
@Test
public void testGensalt() {
// System.out.print("BCrypt.gensalt(): ");
for (int i = 0; i < test_vectors.length; i += 4) {
String plain = test_vectors[i][0];
String salt = BCrypt.gensalt();
String hashed1 = BCrypt.hashpw(plain, salt);
String hashed2 = BCrypt.hashpw(plain, hashed1);
assertEquals(hashed1, hashed2);
// System.out.print(".");
}
// System.out.println("");
}
/** Test method for 'BCrypt.checkpw(String, String)' expecting success */
@Test
public void testCheckpw_success() {
// System.out.print("BCrypt.checkpw w/ good passwords: ");
for (int i = 0; i < test_vectors.length; i++) {
String plain = test_vectors[i][0];
String expected = test_vectors[i][2];
assertTrue(BCrypt.checkpw(plain, expected));
// System.out.print(".");
}
// System.out.println("");
}
/** Test method for 'BCrypt.checkpw(String, String)' expecting failure */
@Test
public void testCheckpw_failure() {
// System.out.print("BCrypt.checkpw w/ bad passwords: ");
for (int i = 0; i < test_vectors.length; i++) {
int broken_index = (i + 4) % test_vectors.length;
String plain = test_vectors[i][0];
String expected = test_vectors[broken_index][2];
assertFalse(BCrypt.checkpw(plain, expected));
// System.out.print(".");
}
// System.out.println("");
}
/** Test for correct hashing of non-US-ASCII passwords */
@Test
public void testInternationalChars() {
// System.out.print("BCrypt.hashpw w/ international chars: ");
String pw1 = "\u2605\u2605\u2605\u2605\u2605\u2605\u2605\u2605";
String pw2 = "????????";
String h1 = BCrypt.hashpw(pw1, BCrypt.gensalt());
assertFalse(BCrypt.checkpw(pw2, h1));
// System.out.print(".");
String h2 = BCrypt.hashpw(pw2, BCrypt.gensalt());
assertFalse(BCrypt.checkpw(pw1, h2));
// System.out.print(".");
// System.out.println("");
}
private static class BCryptHashTV {
private final byte[] pass;
private final byte[] salt;
private final byte[] out;
public BCryptHashTV(byte[] pass, byte[] salt, byte[] out) {
this.pass = pass;
this.salt = salt;
this.out = out;
}
}
BCryptHashTV[] bcrypt_hash_test_vectors = new BCryptHashTV[] {new BCryptHashTV(
new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,},
new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,},
new byte[] {(byte) 0x46, (byte) 0x02, (byte) 0x86, (byte) 0xe9, (byte) 0x72, (byte) 0xfa,
(byte) 0x83, (byte) 0x3f, (byte) 0x8b, (byte) 0x12, (byte) 0x83, (byte) 0xad, (byte) 0x8f,
(byte) 0xa9, (byte) 0x19, (byte) 0xfa, (byte) 0x29, (byte) 0xbd, (byte) 0xe2, (byte) 0x0e,
(byte) 0x23, (byte) 0x32, (byte) 0x9e, (byte) 0x77, (byte) 0x4d, (byte) 0x84, (byte) 0x22,
(byte) 0xba, (byte) 0xc0, (byte) 0xa7, (byte) 0x92, (byte) 0x6c,}),
new BCryptHashTV(
new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,},
new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,},
new byte[] {(byte) 0xc6, (byte) 0xa9, (byte) 0x5f, (byte) 0xe6, (byte) 0x41, (byte) 0x31,
(byte) 0x15, (byte) 0xfb, (byte) 0x57, (byte) 0xe9, (byte) 0x9f, (byte) 0x75,
(byte) 0x74, (byte) 0x98, (byte) 0xe8, (byte) 0x5d, (byte) 0xa3, (byte) 0xc6,
(byte) 0xe1, (byte) 0xdf, (byte) 0x0c, (byte) 0x3c, (byte) 0x93, (byte) 0xaa,
(byte) 0x97, (byte) 0x5c, (byte) 0x54, (byte) 0x8a, (byte) 0x34, (byte) 0x43,
(byte) 0x26, (byte) 0xf8,}),};
@Test
public void testBCryptHashTestVectors() throws Exception {
// System.out.print("BCrypt.hash w/ known vectors: ");
for (BCryptHashTV tv : bcrypt_hash_test_vectors) {
byte[] output = new byte[tv.out.length];
new BCrypt().hash(tv.pass, tv.salt, output);
assertEquals(Arrays.toString(tv.out), Arrays.toString(output));
// System.out.print(".");
}
// System.out.println("");
}
private static class BCryptPbkdfTV {
private final byte[] pass;
private final byte[] salt;
private final int rounds;
private final byte[] out;
public BCryptPbkdfTV(byte[] pass, byte[] salt, int rounds, byte[] out) {
this.pass = pass;
this.salt = salt;
this.rounds = rounds;
this.out = out;
}
}
BCryptPbkdfTV[] bcrypt_pbkdf_test_vectors = new BCryptPbkdfTV[] {
new BCryptPbkdfTV("password".getBytes(UTF_8), "salt".getBytes(UTF_8), 4,
new byte[] {(byte) 0x5b, (byte) 0xbf, (byte) 0x0c, (byte) 0xc2, (byte) 0x93, (byte) 0x58,
(byte) 0x7f, (byte) 0x1c, (byte) 0x36, (byte) 0x35, (byte) 0x55, (byte) 0x5c,
(byte) 0x27, (byte) 0x79, (byte) 0x65, (byte) 0x98, (byte) 0xd4, (byte) 0x7e,
(byte) 0x57, (byte) 0x90, (byte) 0x71, (byte) 0xbf, (byte) 0x42, (byte) 0x7e,
(byte) 0x9d, (byte) 0x8f, (byte) 0xbe, (byte) 0x84, (byte) 0x2a, (byte) 0xba,
(byte) 0x34, (byte) 0xd9,}),
new BCryptPbkdfTV("password".getBytes(UTF_8), "salt".getBytes(UTF_8), 8,
new byte[] {(byte) 0xe1, (byte) 0x36, (byte) 0x7e, (byte) 0xc5, (byte) 0x15, (byte) 0x1a,
(byte) 0x33, (byte) 0xfa, (byte) 0xac, (byte) 0x4c, (byte) 0xc1, (byte) 0xc1,
(byte) 0x44, (byte) 0xcd, (byte) 0x23, (byte) 0xfa, (byte) 0x15, (byte) 0xd5,
(byte) 0x54, (byte) 0x84, (byte) 0x93, (byte) 0xec, (byte) 0xc9, (byte) 0x9b,
(byte) 0x9b, (byte) 0x5d, (byte) 0x9c, (byte) 0x0d, (byte) 0x3b, (byte) 0x27,
(byte) 0xbe, (byte) 0xc7, (byte) 0x62, (byte) 0x27, (byte) 0xea, (byte) 0x66,
(byte) 0x08, (byte) 0x8b, (byte) 0x84, (byte) 0x9b, (byte) 0x20, (byte) 0xab,
(byte) 0x7a, (byte) 0xa4, (byte) 0x78, (byte) 0x01, (byte) 0x02, (byte) 0x46,
(byte) 0xe7, (byte) 0x4b, (byte) 0xba, (byte) 0x51, (byte) 0x72, (byte) 0x3f,
(byte) 0xef, (byte) 0xa9, (byte) 0xf9, (byte) 0x47, (byte) 0x4d, (byte) 0x65,
(byte) 0x08, (byte) 0x84, (byte) 0x5e, (byte) 0x8d}),
new BCryptPbkdfTV("password".getBytes(UTF_8), "salt".getBytes(UTF_8), 42,
new byte[] {(byte) 0x83, (byte) 0x3c, (byte) 0xf0, (byte) 0xdc, (byte) 0xf5, (byte) 0x6d,
(byte) 0xb6, (byte) 0x56, (byte) 0x08, (byte) 0xe8, (byte) 0xf0, (byte) 0xdc,
(byte) 0x0c, (byte) 0xe8, (byte) 0x82, (byte) 0xbd}),};
@Test
public void testBCryptPbkdfTestVectors() throws Exception {
// System.out.print("BCrypt.pbkdf w/ known vectors: ");
for (BCryptPbkdfTV tv : bcrypt_pbkdf_test_vectors) {
byte[] output = new byte[tv.out.length];
new BCrypt().pbkdf(tv.pass, tv.salt, tv.rounds, output);
assertEquals(Arrays.toString(tv.out), Arrays.toString(output));
// System.out.print(".");
}
// System.out.println("");
}
}