// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.util;

import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

public class BCryptTest {

	@Test
	public void testBCrypt() {
		String hash = BCrypt.hashpw("password", BCrypt.gensalt(7));
		assertTrue(BCrypt.checkpw("password", hash));
	}

	@Test
	public void testBCryptRandom() {
		for (int rounds = 0; rounds < 1000; rounds++) {
			String text = RandomString.getInstance().randomAlphaNumeric(10);

			String hash = BCrypt.hashpw(text, BCrypt.gensalt(4));
			assertTrue(BCrypt.checkpw(text, hash));
		}
	}

	static 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() {
		for (String[] test_vector : test_vectors) {
			String plain = test_vector[0];
			String salt = test_vector[1];
			String expected = test_vector[2];
			String hashed = BCrypt.hashpw(plain, salt);
			assertEquals(hashed, expected);
		}
	}

	/**
	 * Test method for 'BCrypt.gensalt(int)'
	 */
	@Test
	public void testGensaltInt() {
		for (int i = 4; i <= 12; 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);
			}
		}
	}

	/**
	 * Test method for 'BCrypt.gensalt()'
	 */
	@Test
	public void testGensalt() {
		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);
		}
	}

	/**
	 * Test method for 'BCrypt.checkpw(String, String)'
	 * expecting success
	 */
	@Test
	public void testCheckpw_success() {
		for (String[] test_vector : test_vectors) {
			String plain = test_vector[0];
			String expected = test_vector[2];
			assertTrue(BCrypt.checkpw(plain, expected));
		}
	}

	/**
	 * Test method for 'BCrypt.checkpw(String, String)'
	 * expecting failure
	 */
	@Test
	public void testCheckpw_failure() {
		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));
		}
	}

	/**
	 * Test for correct hashing of non-US-ASCII passwords
	 */
	@Test
	public void testInternationalChars() {
		String pw1 = "\u2605\u2605\u2605\u2605\u2605\u2605\u2605\u2605";
		String pw2 = "????????";

		String h1 = BCrypt.hashpw(pw1, BCrypt.gensalt());
		assertFalse(BCrypt.checkpw(pw2, h1));

		String h2 = BCrypt.hashpw(pw2, BCrypt.gensalt());
		assertFalse(BCrypt.checkpw(pw1, h2));
	}

}