22 | * 0 - everything ok (verified)
23 | * 1 - could not verify hash
24 | * 2 - error while parsing cli arguments
25 | * 3 - invalid bcrypt hash while verifying
26 | * 4 - general error
27 | *
28 | * @param args
29 | */
30 | public static void main(String[] args) {
31 | System.exit(execute(CLIParser.parse(args), System.out, System.err));
32 | }
33 |
34 | /**
35 | * Execute the given arguments and executes the appropriate actions
36 | *
37 | * @param arguments
38 | * @param stream
39 | * @param errorStream
40 | * @return the exit code of the tool
41 | */
42 | static int execute(Arg arguments, PrintStream stream, PrintStream errorStream) {
43 | if (arguments == null) {
44 | return 2;
45 | }
46 |
47 | if (arguments.checkBcryptHash != null) { // verify mode
48 | BCrypt.Result result = BCrypt.verifyer().verify(arguments.password, arguments.checkBcryptHash);
49 | if (!result.validFormat) {
50 | System.err.println("Invalid bcrypt format.");
51 | return 3;
52 | }
53 |
54 | if (result.verified) {
55 | stream.println("Hash verified.");
56 | } else {
57 | errorStream.println("Provided hash does not verify against given password.");
58 | return 1;
59 | }
60 | } else { // hash mode
61 | byte[] salt = arguments.salt == null ? Bytes.random(16).array() : arguments.salt;
62 | byte[] hash = BCrypt.withDefaults().hash(arguments.costFactor, salt, charArrayToByteArray(arguments.password, StandardCharsets.UTF_8));
63 | stream.println(new String(hash, StandardCharsets.UTF_8));
64 | }
65 | return 0;
66 | }
67 |
68 | private static byte[] charArrayToByteArray(char[] charArray, Charset charset) {
69 | ByteBuffer bb = charset.encode(CharBuffer.wrap(charArray));
70 | byte[] bytes = new byte[bb.remaining()];
71 | bb.get(bytes);
72 | return bytes;
73 | }
74 |
75 | static String jarVersion() {
76 | return BcryptTool.class.getPackage().getImplementationVersion();
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/modules/bcrypt-cli/src/main/java/at/favre/lib/crypto/bcrypt/cli/CLIParser.java:
--------------------------------------------------------------------------------
1 | package at.favre.lib.crypto.bcrypt.cli;
2 |
3 | import at.favre.lib.bytes.Bytes;
4 | import org.apache.commons.cli.CommandLine;
5 | import org.apache.commons.cli.CommandLineParser;
6 | import org.apache.commons.cli.DefaultParser;
7 | import org.apache.commons.cli.HelpFormatter;
8 | import org.apache.commons.cli.Option;
9 | import org.apache.commons.cli.OptionGroup;
10 | import org.apache.commons.cli.Options;
11 |
12 | /**
13 | * Parses the command line input and converts it to a structured model ({@link Arg}
14 | */
15 | public final class CLIParser {
16 |
17 | public static final String ARG_HASH = "b";
18 | public static final String ARG_CHECK = "c";
19 |
20 | private CLIParser() {
21 | }
22 |
23 | public static Arg parse(String[] inputArgs) {
24 | Options options = setupOptions();
25 | CommandLineParser parser = new DefaultParser();
26 | Arg argument = new Arg();
27 |
28 | try {
29 | CommandLine commandLine = parser.parse(options, inputArgs);
30 |
31 | if (commandLine.hasOption("h") || commandLine.hasOption("help")) {
32 | printHelp(options);
33 | return null;
34 | }
35 |
36 | if (commandLine.hasOption("v") || commandLine.hasOption("version")) {
37 | System.out.println("Version: " + CLIParser.class.getPackage().getImplementationVersion());
38 | return null;
39 | }
40 |
41 | if (commandLine.getArgs().length == 0) {
42 | throw new IllegalArgumentException("First parameter must be password (e.g. bcrypt 'mysecretpassword' -" + ARG_HASH + " 12)");
43 | }
44 |
45 | char[] password = commandLine.getArgs()[0].toCharArray();
46 |
47 | if (commandLine.hasOption(ARG_HASH)) {
48 |
49 | return handleHash(commandLine, password);
50 | } else if (commandLine.hasOption(ARG_CHECK)) {
51 | return handleCheck(commandLine, password);
52 | }
53 | } catch (Exception e) {
54 | String msg = e.getMessage();
55 | System.err.println(msg.length() > 80 ? msg.substring(0, 80) + "..." : msg);
56 |
57 | CLIParser.printHelp(options);
58 |
59 | argument = null;
60 | }
61 |
62 | return argument;
63 | }
64 |
65 | private static Arg handleHash(CommandLine commandLine, char[] password) {
66 | String[] hashParams = commandLine.getOptionValues(ARG_HASH);
67 |
68 | if (hashParams == null || hashParams.length == 0) {
69 | throw new IllegalArgumentException("Hash mode expects at least the cost parameter. (e.g. '-" + ARG_HASH + " 12')");
70 | }
71 |
72 | final int costFactor;
73 | try {
74 | costFactor = Integer.parseInt(hashParams[0]);
75 | } catch (Exception e) {
76 | throw new IllegalArgumentException("First parameter of hash expected to be integer type, was " + hashParams[0]);
77 | }
78 |
79 | byte[] salt = null;
80 | if (hashParams.length > 1) {
81 | try {
82 | salt = Bytes.parseHex(hashParams[1]).array();
83 | } catch (Exception e) {
84 | throw new IllegalArgumentException("Salt parameter could not be parsed as hex [0-9a-f], was " + hashParams[1]);
85 | }
86 |
87 | if (salt.length != 16) {
88 | throw new IllegalArgumentException("Salt parameter must be exactly 16 bytes (32 characters hex)");
89 | }
90 | }
91 | return new Arg(password, salt, costFactor);
92 | }
93 |
94 | private static Arg handleCheck(CommandLine commandLine, char[] password) {
95 | String refBcrypt = commandLine.getOptionValue(ARG_CHECK);
96 |
97 | if (refBcrypt == null || refBcrypt.trim().length() != 60) {
98 | throw new IllegalArgumentException("Reference bcrypt hash must be exactly 60 characters, e.g. '$2a$10$6XBbrUraPyfq7nxeaYsR4u.3.ZuGNCy3tOT4reneAI/qoWvP6AX/e' was " + refBcrypt);
99 | }
100 |
101 | return new Arg(password, refBcrypt);
102 | }
103 |
104 | static Options setupOptions() {
105 | Options options = new Options();
106 | Option optHash = Option.builder(ARG_HASH).longOpt("bhash").argName("cost> <[16-hex-byte-salt]").hasArgs().desc("Use this flag if you want to compute the bcrypt hash. Pass the logarithm cost factor (4-31) and optionally the used salt" +
107 | " as hex encoded byte array (must be exactly 16 bytes/32 characters hex). Example: '--bhash 12 8e270d6129fd45f30a9b3fe44b4a8d9a'").required().build();
108 | Option optCheck = Option.builder(ARG_CHECK).longOpt("check").argName("bcrypt-hash").hasArg().desc("Use this flag if you want to verify a hash against a given password. Example: '--check $2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i'").build();
109 |
110 | Option help = Option.builder("h").longOpt("help").desc("Prints help docs.").build();
111 | Option version = Option.builder("v").longOpt("version").desc("Prints current version.").build();
112 |
113 | OptionGroup mainArgs = new OptionGroup();
114 | mainArgs.addOption(optCheck).addOption(optHash).addOption(help).addOption(version);
115 | mainArgs.setRequired(true);
116 |
117 | options.addOptionGroup(mainArgs);
118 | return options;
119 | }
120 |
121 | private static void printHelp(Options options) {
122 | HelpFormatter help = new HelpFormatter();
123 | help.setWidth(110);
124 | help.setLeftPadding(4);
125 | help.printHelp("bcrypt
16 | * Which consists of the version identifier:
17 | *
18 | *
22 | * the cost factor:
23 | *
24 | *
28 | * 16 bytes Radix64 encoded UTF-8 bytes of salt:
29 | *
30 | *
34 | * and 23 Radix64 encoded UTF-8 bytes of actual bcrypt hash
35 | *
36 | *
40 | * The literal
42 | * see: modular_crypt_format
43 | */
44 | public interface BCryptFormatter {
45 |
46 | /**
47 | * Create a message for the given raw hash data
48 | *
49 | * @param hashData to create a message from
50 | * @return message as bytes which might be UTF-8 encoded
51 | */
52 | byte[] createHashMessage(BCrypt.HashData hashData);
53 |
54 | /**
55 | * Default implantation following the Modular Crypt Format
56 | */
57 | final class Default implements BCryptFormatter {
58 |
59 | private final Radix64Encoder encoder;
60 | private final Charset defaultCharset;
61 |
62 | public Default(Radix64Encoder encoder, Charset defaultCharset) {
63 | this.encoder = encoder;
64 | this.defaultCharset = defaultCharset;
65 | }
66 |
67 | @Override
68 | public byte[] createHashMessage(BCrypt.HashData hashData) {
69 | byte[] saltEncoded = encoder.encode(hashData.rawSalt);
70 | byte[] hashEncoded = encoder.encode(hashData.rawHash);
71 | byte[] costFactorBytes = String.format(Locale.US, "%02d", hashData.cost).getBytes(defaultCharset);
72 |
73 | try {
74 | ByteBuffer byteBuffer = ByteBuffer.allocate(hashData.version.versionIdentifier.length +
75 | costFactorBytes.length + 3 + saltEncoded.length + hashEncoded.length);
76 | byteBuffer.put(BCrypt.SEPARATOR);
77 | byteBuffer.put(hashData.version.versionIdentifier);
78 | byteBuffer.put(BCrypt.SEPARATOR);
79 | byteBuffer.put(costFactorBytes);
80 | byteBuffer.put(BCrypt.SEPARATOR);
81 | byteBuffer.put(saltEncoded);
82 | byteBuffer.put(hashEncoded);
83 | return byteBuffer.array();
84 | } finally {
85 | Bytes.wrapNullSafe(saltEncoded).mutable().secureWipe();
86 | Bytes.wrapNullSafe(hashEncoded).mutable().secureWipe();
87 | Bytes.wrapNullSafe(costFactorBytes).mutable().secureWipe();
88 | }
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/BCryptOpenBSDProtocol.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2006 Damien Miller
20 | * Blowfish is notable among block ciphers for its expensive key setup phase. It starts off with subkeys in a standard state, then uses this
21 | * state to perform a block encryption using part of the key, and uses the result of that encryption (which is more accurately a hashing) to
22 | * replace some of the subkeys. Then it uses this modified state to encrypt another part of the key, and uses the result to replace more of
23 | * the subkeys. It proceeds in this fashion, using a progressively modified state to hash the key and replace bits of state, until all subkeys
24 | * have been set.
25 | *
26 | * Provos and Mazières took advantage of this, and took it further. They developed a new key setup algorithm for Blowfish, dubbing the resulting
27 | * cipher "Eksblowfish" ("expensive key schedule Blowfish"). The key setup begins with a modified form of the standard Blowfish key setup, in
28 | * which both the salt and password are used to set all subkeys. There are then a number of rounds in which the standard Blowfish keying algorithm
29 | * is applied, using alternatively the salt and the password as the key, each round starting with the subkey state from the previous round.
30 | * In theory, this is no stronger than the standard Blowfish key schedule, but the number of rekeying rounds is configurable; this process can
31 | * therefore be made arbitrarily slow, which helps deter brute-force attacks upon the hash or salt.
32 | *
33 | * The bcrypt algorithm is the result of encrypting the text "OrpheanBeholderScryDoubt" 64 times using Blowfish. In bcrypt the usual Blowfish
34 | * key setup function is replaced with an expensive key setup (EksBlowfishSetup) function.
35 | */
36 | final class BCryptOpenBSDProtocol {
37 |
38 | // Blowfish parameters
39 | private static final int BLOWFISH_NUM_ROUNDS = 16;
40 |
41 | // Initial contents of key schedule
42 | private static final int[] P_orig = {
43 | 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
44 | 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
45 | 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
46 | 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
47 | 0x9216d5d9, 0x8979fb1b
48 | };
49 | private static final int[] S_orig = {
50 | 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
51 | 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
52 | 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
53 | 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
54 | 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
55 | 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
56 | 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
57 | 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
58 | 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
59 | 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
60 | 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
61 | 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
62 | 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
63 | 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
64 | 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
65 | 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
66 | 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
67 | 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
68 | 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
69 | 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
70 | 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
71 | 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
72 | 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
73 | 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
74 | 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
75 | 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
76 | 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
77 | 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
78 | 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
79 | 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
80 | 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
81 | 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
82 | 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
83 | 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
84 | 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
85 | 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
86 | 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
87 | 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
88 | 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
89 | 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
90 | 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
91 | 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
92 | 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
93 | 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
94 | 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
95 | 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
96 | 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
97 | 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
98 | 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
99 | 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
100 | 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
101 | 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
102 | 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
103 | 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
104 | 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
105 | 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
106 | 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
107 | 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
108 | 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
109 | 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
110 | 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
111 | 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
112 | 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
113 | 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
114 | 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
115 | 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
116 | 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
117 | 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
118 | 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
119 | 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
120 | 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
121 | 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
122 | 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
123 | 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
124 | 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
125 | 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
126 | 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
127 | 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
128 | 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
129 | 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
130 | 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
131 | 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
132 | 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
133 | 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
134 | 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
135 | 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
136 | 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
137 | 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
138 | 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
139 | 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
140 | 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
141 | 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
142 | 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
143 | 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
144 | 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
145 | 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
146 | 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
147 | 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
148 | 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
149 | 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
150 | 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
151 | 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
152 | 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
153 | 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
154 | 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
155 | 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
156 | 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
157 | 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
158 | 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
159 | 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
160 | 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
161 | 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
162 | 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
163 | 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
164 | 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
165 | 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
166 | 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
167 | 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
168 | 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
169 | 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
170 | 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
171 | 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
172 | 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
173 | 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
174 | 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
175 | 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
176 | 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
177 | 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
178 | 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
179 | 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
180 | 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
181 | 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
182 | 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
183 | 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
184 | 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
185 | 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
186 | 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
187 | 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
188 | 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
189 | 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
190 | 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
191 | 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
192 | 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
193 | 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
194 | 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
195 | 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
196 | 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
197 | 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
198 | 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
199 | 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
200 | 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
201 | 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
202 | 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
203 | 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
204 | 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
205 | 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
206 | 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
207 | 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
208 | 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
209 | 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
210 | 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
211 | 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
212 | 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
213 | 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
214 | 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
215 | 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
216 | 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
217 | 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
218 | 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
219 | 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
220 | 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
221 | 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
222 | 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
223 | 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
224 | 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
225 | 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
226 | 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
227 | 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
228 | 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
229 | 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
230 | 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
231 | 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
232 | 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
233 | 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
234 | 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
235 | 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
236 | 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
237 | 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
238 | 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
239 | 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
240 | 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
241 | 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
242 | 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
243 | 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
244 | 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
245 | 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
246 | 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
247 | 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
248 | 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
249 | 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
250 | 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
251 | 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
252 | 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
253 | 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
254 | 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
255 | 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
256 | 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
257 | 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
258 | 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
259 | 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
260 | 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
261 | 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
262 | 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
263 | 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
264 | 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
265 | 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
266 | 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
267 | 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
268 | 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
269 | 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
270 | 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
271 | 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
272 | 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
273 | 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
274 | 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
275 | 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
276 | 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
277 | 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
278 | 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
279 | 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
280 | 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
281 | 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
282 | 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
283 | 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
284 | 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
285 | 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
286 | 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
287 | 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
288 | 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
289 | 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
290 | 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
291 | 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
292 | 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
293 | 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
294 | 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
295 | 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
296 | 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
297 | 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
298 | 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
299 | 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
300 | 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
301 | 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
302 | 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
303 | 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
304 | 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
305 | 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
306 | };
307 |
308 | // bcrypt IV: "OrpheanBeholderScryDoubt". The C implementation calls
309 | // this "ciphertext", but it is really plaintext or an IV. We keep
310 | // the name to make code comparison easier.
311 | private static final int[] bf_crypt_ciphertext = {
312 | 0x4f727068, 0x65616e42, 0x65686f6c,
313 | 0x64657253, 0x63727944, 0x6f756274
314 | };
315 |
316 | BCryptOpenBSDProtocol() {
317 | }
318 |
319 | byte[] cryptRaw(long rounds, byte[] salt, byte[] password) {
320 | return cryptRaw(rounds, salt, password, bf_crypt_ciphertext.clone());
321 | }
322 |
323 | /**
324 | * Perform the central password hashing step in the
325 | * bcrypt scheme
326 | *
327 | * @param rounds the actual rounds, not the binary logarithm
328 | * @param salt the binary salt to hash with the password
329 | * @param password the password to hash
330 | * of rounds of hashing to apply
331 | * @param cdata the plaintext to encrypt
332 | * @return an array containing the binary hashed password
333 | */
334 | byte[] cryptRaw(long rounds, byte[] salt, byte[] password, int[] cdata) {
335 | if (rounds < 0) {
336 | throw new IllegalArgumentException("rounds must not be negative");
337 | }
338 |
339 | int clen = cdata.length;
340 |
341 | if (salt.length != BCrypt.SALT_LENGTH) {
342 | throw new IllegalArgumentException("bad salt length");
343 | }
344 |
345 | int[] P = P_orig.clone();
346 | int[] S = S_orig.clone();
347 |
348 | enhancedKeySchedule(P, S, salt, password);
349 |
350 | for (long roundCount = 0; roundCount != rounds; roundCount++) {
351 | key(P, S, password);
352 | key(P, S, salt);
353 | }
354 |
355 | for (int i = 0; i < 64; i++) {
356 | for (int j = 0; j < (clen >> 1); j++) {
357 | encipher(P, S, cdata, j << 1);
358 | }
359 | }
360 |
361 | byte[] ret = new byte[clen * 4];
362 | for (int i = 0, j = 0; i < clen; i++) {
363 | ret[j++] = (byte) ((cdata[i] >> 24) & 0xff);
364 | ret[j++] = (byte) ((cdata[i] >> 16) & 0xff);
365 | ret[j++] = (byte) ((cdata[i] >> 8) & 0xff);
366 | ret[j++] = (byte) (cdata[i] & 0xff);
367 | }
368 |
369 | return ret;
370 | }
371 |
372 | /**
373 | * Perform the "enhanced key schedule" step described by
374 | * Provos and Mazieres in "A Future-Adaptable Password Scheme"
375 | * bcrypt-paper.ps
376 | *
377 | *
378 | * @param data salt information
379 | * @param key password information
380 | */
381 | private void enhancedKeySchedule(int[] P, int[] S, byte[] data, byte[] key) {
382 | int i;
383 | int[] koffp = {0}, doffp = {0};
384 | int[] lr = {0, 0};
385 | int plen = P.length, slen = S.length;
386 |
387 | for (i = 0; i < plen; i++) {
388 | P[i] = P[i] ^ streamToWord(key, koffp);
389 | }
390 |
391 | //noinspection Duplicates
392 | for (i = 0; i < plen; i += 2) {
393 | lr[0] ^= streamToWord(data, doffp);
394 | lr[1] ^= streamToWord(data, doffp);
395 | encipher(P, S, lr, 0);
396 | P[i] = lr[0];
397 | P[i + 1] = lr[1];
398 | }
399 |
400 | //noinspection Duplicates
401 | for (i = 0; i < slen; i += 2) {
402 | lr[0] ^= streamToWord(data, doffp);
403 | lr[1] ^= streamToWord(data, doffp);
404 | encipher(P, S, lr, 0);
405 | S[i] = lr[0];
406 | S[i + 1] = lr[1];
407 | }
408 | }
409 |
410 | /**
411 | * Cyclically extract a word of key material
412 | *
413 | * @param data the string to extract the data from
414 | * @param offp a "pointer" (as a one-entry array) to the
415 | * current offset into data
416 | * @return the next word of material from data
417 | */
418 | private static int streamToWord(byte[] data, int[] offp) {
419 | int i;
420 | int word = 0;
421 | int off = offp[0];
422 |
423 | for (i = 0; i < 4; i++) {
424 | word = (word << 8) | (data[off] & 0xff);
425 | off = (off + 1) % data.length;
426 | }
427 |
428 | offp[0] = off;
429 | return word;
430 | }
431 |
432 | /**
433 | * Key the Blowfish cipher
434 | *
435 | * @param key an array containing the key
436 | */
437 | private void key(int[] P, int[] S, byte[] key) {
438 | int i;
439 | int[] koffp = {0};
440 | int[] lr = {0, 0};
441 | int plen = P.length, slen = S.length;
442 |
443 | for (i = 0; i < plen; i++)
444 | P[i] = P[i] ^ streamToWord(key, koffp);
445 |
446 | for (i = 0; i < plen; i += 2) {
447 | encipher(P, S, lr, 0);
448 | P[i] = lr[0];
449 | P[i + 1] = lr[1];
450 | }
451 |
452 | for (i = 0; i < slen; i += 2) {
453 | encipher(P, S, lr, 0);
454 | S[i] = lr[0];
455 | S[i + 1] = lr[1];
456 | }
457 | }
458 |
459 | /**
460 | * Blowfish encipher a single 64-bit block encoded as
461 | * two 32-bit halves
462 | *
463 | * @param lr an array containing the two 32-bit half blocks
464 | * @param off the position in the array of the blocks
465 | */
466 | private void encipher(int[] P, int[] S, int[] lr, int off) {
467 | int i, n, l = lr[off], r = lr[off + 1];
468 |
469 | l ^= P[0];
470 | for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2; ) {
471 | // Feistel substitution on left word
472 | n = S[(l >> 24) & 0xff];
473 | n += S[0x100 | ((l >> 16) & 0xff)];
474 | n ^= S[0x200 | ((l >> 8) & 0xff)];
475 | n += S[0x300 | (l & 0xff)];
476 | r ^= n ^ P[++i];
477 |
478 | // Feistel substitution on right word
479 | n = S[(r >> 24) & 0xff];
480 | n += S[0x100 | ((r >> 16) & 0xff)];
481 | n ^= S[0x200 | ((r >> 8) & 0xff)];
482 | n += S[0x300 | (r & 0xff)];
483 | l ^= n ^ P[++i];
484 | }
485 | lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1];
486 | lr[off + 1] = l;
487 | }
488 | }
489 |
--------------------------------------------------------------------------------
/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/BCryptParser.java:
--------------------------------------------------------------------------------
1 | package at.favre.lib.crypto.bcrypt;
2 |
3 | import at.favre.lib.bytes.Bytes;
4 |
5 | import java.nio.ByteBuffer;
6 | import java.nio.charset.Charset;
7 |
8 | import static at.favre.lib.crypto.bcrypt.BCrypt.SEPARATOR;
9 |
10 | /**
11 | * A simple parser which is able to parse Modular Crypt Format specifically for bcrypt.
12 | *
13 | * It will gather the parts of the format:
14 | *
6 | * Heavily used in {@link BCryptParser}
7 | */
8 | public class IllegalBCryptFormatException extends Exception {
9 |
10 | public IllegalBCryptFormatException(String s) {
11 | super(s);
12 | }
13 |
14 | @Override
15 | public String getMessage() {
16 | return super.getMessage() + " - example of expected hash format: '$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i'" +
17 | " which includes 16 bytes salt and 23 bytes hash value encoded in a base64 flavor";
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/LongPasswordStrategies.java:
--------------------------------------------------------------------------------
1 | package at.favre.lib.crypto.bcrypt;
2 |
3 | import java.util.Objects;
4 |
5 | /**
6 | * Factory for default {@link LongPasswordStrategy} implementations
7 | */
8 | @SuppressWarnings("WeakerAccess")
9 | public final class LongPasswordStrategies {
10 | private LongPasswordStrategies() {
11 | }
12 |
13 | /**
14 | * See {@link at.favre.lib.crypto.bcrypt.LongPasswordStrategy.TruncateStrategy}
15 | *
16 | * @param version required to get the max allowed pw length
17 | * @return new instance
18 | */
19 | public static LongPasswordStrategy truncate(BCrypt.Version version) {
20 | return new LongPasswordStrategy.TruncateStrategy(Objects.requireNonNull(version).allowedMaxPwLength);
21 | }
22 |
23 | /**
24 | * See {@link at.favre.lib.crypto.bcrypt.LongPasswordStrategy.Sha512DerivationStrategy}
25 | *
26 | * @param version required to get the max allowed pw length
27 | * @return new instance
28 | */
29 | public static LongPasswordStrategy hashSha512(BCrypt.Version version) {
30 | return new LongPasswordStrategy.Sha512DerivationStrategy(Objects.requireNonNull(version).allowedMaxPwLength);
31 | }
32 |
33 | /**
34 | * See {@link at.favre.lib.crypto.bcrypt.LongPasswordStrategy.StrictMaxPasswordLengthStrategy}
35 | *
36 | * @param version required to get the max allowed pw length
37 | * @return new instance
38 | */
39 | public static LongPasswordStrategy strict(BCrypt.Version version) {
40 | return new LongPasswordStrategy.StrictMaxPasswordLengthStrategy(Objects.requireNonNull(version).allowedMaxPwLength);
41 | }
42 |
43 | /**
44 | * See {@link LongPasswordStrategy.PassThroughStrategy}
45 | *
46 | * @return new instance
47 | */
48 | public static LongPasswordStrategy none() {
49 | return new LongPasswordStrategy.PassThroughStrategy();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/LongPasswordStrategy.java:
--------------------------------------------------------------------------------
1 | package at.favre.lib.crypto.bcrypt;
2 |
3 | import at.favre.lib.bytes.Bytes;
4 | import at.favre.lib.bytes.BytesTransformer;
5 |
6 | /**
7 | * Strategy if the password is longer than supported by Bcrypt itself (71 bytes + null terminator with $2a$)
8 | */
9 | public interface LongPasswordStrategy {
10 |
11 | /**
12 | * Derives (hashes, shortens, etc) the given password to a desired max length.
13 | *
14 | * @param rawPassword to check and derive
15 | * @return the derived (shortened) password, or the same reference if short enough
16 | */
17 | byte[] derive(byte[] rawPassword);
18 |
19 | /**
20 | * Default base implementation
21 | */
22 | abstract class BaseLongPasswordStrategy implements LongPasswordStrategy {
23 | final int maxLength;
24 |
25 | private BaseLongPasswordStrategy(int maxLength) {
26 | this.maxLength = maxLength;
27 | }
28 |
29 | abstract byte[] innerDerive(byte[] input);
30 |
31 | @Override
32 | public byte[] derive(byte[] rawPassword) {
33 | if (rawPassword.length >= maxLength) {
34 | return innerDerive(rawPassword);
35 | }
36 | return rawPassword;
37 | }
38 | }
39 |
40 | /**
41 | * This strategy will always throw an exception to force passwords under the max length
42 | */
43 | final class StrictMaxPasswordLengthStrategy extends BaseLongPasswordStrategy {
44 | StrictMaxPasswordLengthStrategy(int maxLength) {
45 | super(maxLength);
46 | }
47 |
48 | @Override
49 | public byte[] innerDerive(byte[] rawPassword) {
50 | throw new IllegalArgumentException("password must not be longer than " + maxLength + " bytes plus null terminator encoded in utf-8, was " + rawPassword.length);
51 | }
52 | }
53 |
54 | /**
55 | * Will use sha512 to hash given password to generate fixed 64 byte length hash value
56 | */
57 | final class Sha512DerivationStrategy extends BaseLongPasswordStrategy {
58 | Sha512DerivationStrategy(int maxLength) {
59 | super(maxLength);
60 | }
61 |
62 | @Override
63 | public byte[] innerDerive(byte[] rawPassword) {
64 | return Bytes.wrap(rawPassword).hash("SHA-512").array();
65 | }
66 | }
67 |
68 | /**
69 | * Truncates the password the max possible length.
70 | *
71 | * NOTE: This is not recommended, only for compatibility with current hashes; uses {@link Sha512DerivationStrategy}
72 | * if you need to support passwords with arbitrary lengths.
73 | */
74 | final class TruncateStrategy extends BaseLongPasswordStrategy {
75 |
76 | TruncateStrategy(int maxLength) {
77 | super(maxLength);
78 | }
79 |
80 | @Override
81 | public byte[] innerDerive(byte[] rawPassword) {
82 | return Bytes.wrap(rawPassword).resize(maxLength, BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_ZERO_INDEX).array();
83 | }
84 | }
85 |
86 | /**
87 | * A simple strategy that just returns the provided password without changing it.
88 | */
89 | final class PassThroughStrategy implements LongPasswordStrategy {
90 | @Override
91 | public byte[] derive(byte[] rawPassword) {
92 | return rawPassword;
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/Radix64Encoder.java:
--------------------------------------------------------------------------------
1 | package at.favre.lib.crypto.bcrypt;
2 |
3 | /**
4 | * Encoder for the custom Base64 variant of BCrypt (called Radix64 here). It has the same rules as Base64 but uses a
5 | * different mapping table than the various RFCs
6 | *
7 | * According to Wikipedia:
8 | *
9 | *
54 | * Original author: Alexander Y. Kleymenov
55 | *
56 | * @see Okio
57 | */
58 | class Default implements Radix64Encoder {
59 | private static final byte[] DECODE_TABLE = {
60 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
61 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
62 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, 56, 57,
63 | 58, 59, 60, 61, 62, 63, -1, -1, -1, -2, -1, -1, -1, 2, 3, 4, 5, 6, 7,
64 | 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
65 | 26, 27, -1, -1, -1, -1, -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
66 | 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53};
67 |
68 | private static final byte[] MAP = new byte[]{
69 | '.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
70 | 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
71 | 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
72 | 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
73 | 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
74 | '6', '7', '8', '9'
75 | };
76 |
77 | @Override
78 | public byte[] encode(byte[] in) {
79 | return encode(in, MAP);
80 | }
81 |
82 | @Override
83 | public byte[] decode(byte[] in) {
84 | // Ignore trailing '=' padding and whitespace from the input.
85 | int limit = in.length;
86 | for (; limit > 0; limit--) {
87 | byte c = in[limit - 1];
88 | if (c != '=' && c != '\n' && c != '\r' && c != ' ' && c != '\t') {
89 | break;
90 | }
91 | }
92 |
93 | // If the input includes whitespace, this output array will be longer than necessary.
94 | byte[] out = new byte[(int) (limit * 6L / 8L)];
95 | int outCount = 0;
96 | int inCount = 0;
97 |
98 | int word = 0;
99 | for (int pos = 0; pos < limit; pos++) {
100 | byte c = in[pos];
101 |
102 | int bits;
103 | if (c == '.' || c == '/' || (c >= 'A' && c <= 'z') || (c >= '0' && c <= '9')) {
104 | bits = DECODE_TABLE[c];
105 | } else if (c == '\n' || c == '\r' || c == ' ' || c == '\t') {
106 | continue;
107 | } else {
108 | throw new IllegalArgumentException("invalid character to decode: " + c);
109 | }
110 |
111 | // Append this char's 6 bits to the word.
112 | word = (word << 6) | (byte) bits & 0xff;
113 |
114 | // For every 4 chars of input, we accumulate 24 bits of output. Emit 3 bytes.
115 | inCount++;
116 | if (inCount % 4 == 0) {
117 | out[outCount++] = (byte) (word >> 16);
118 | out[outCount++] = (byte) (word >> 8);
119 | out[outCount++] = (byte) word;
120 | }
121 | }
122 |
123 | int lastWordChars = inCount % 4;
124 | if (lastWordChars == 1) {
125 | // We read 1 char followed by "===". But 6 bits is a truncated byte! Fail.
126 | return new byte[0];
127 | } else if (lastWordChars == 2) {
128 | // We read 2 chars followed by "==". Emit 1 byte with 8 of those 12 bits.
129 | word = word << 12;
130 | out[outCount++] = (byte) (word >> 16);
131 | } else if (lastWordChars == 3) {
132 | // We read 3 chars, followed by "=". Emit 2 bytes for 16 of those 18 bits.
133 | word = word << 6;
134 | out[outCount++] = (byte) (word >> 16);
135 | out[outCount++] = (byte) (word >> 8);
136 | }
137 |
138 | // If we sized our out array perfectly, we're done.
139 | if (outCount == out.length) return out;
140 |
141 | // Copy the decoded bytes to a new, right-sized array.
142 | byte[] prefix = new byte[outCount];
143 | System.arraycopy(out, 0, prefix, 0, outCount);
144 | return prefix;
145 | }
146 |
147 | private static byte[] encode(byte[] in, byte[] map) {
148 | int length = 4 * (in.length / 3) + (in.length % 3 == 0 ? 0 : in.length % 3 + 1);
149 | byte[] out = new byte[length];
150 | int index = 0, end = in.length - in.length % 3;
151 | for (int i = 0; i < end; i += 3) {
152 | out[index++] = map[(in[i] & 0xff) >> 2];
153 | out[index++] = map[((in[i] & 0x03) << 4) | ((in[i + 1] & 0xff) >> 4)];
154 | out[index++] = map[((in[i + 1] & 0x0f) << 2) | ((in[i + 2] & 0xff) >> 6)];
155 | out[index++] = map[(in[i + 2] & 0x3f)];
156 | }
157 | switch (in.length % 3) {
158 | case 1:
159 | out[index++] = map[(in[end] & 0xff) >> 2];
160 | out[index] = map[(in[end] & 0x03) << 4];
161 | break;
162 | case 2:
163 | out[index++] = map[(in[end] & 0xff) >> 2];
164 | out[index++] = map[((in[end] & 0x03) << 4) | ((in[end + 1] & 0xff) >> 4)];
165 | out[index] = map[((in[end + 1] & 0x0f) << 2)];
166 | break;
167 | }
168 | return out;
169 | }
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/BCryptFormatterTest.java:
--------------------------------------------------------------------------------
1 | package at.favre.lib.crypto.bcrypt;
2 |
3 | import at.favre.lib.bytes.Bytes;
4 | import at.favre.lib.crypto.bcrypt.misc.Repeat;
5 | import at.favre.lib.crypto.bcrypt.misc.RepeatRule;
6 | import org.junit.Rule;
7 | import org.junit.Test;
8 |
9 | import java.nio.charset.StandardCharsets;
10 | import java.util.Random;
11 |
12 | import static at.favre.lib.crypto.bcrypt.BcryptTest.UTF_8;
13 | import static org.junit.Assert.assertArrayEquals;
14 | import static org.junit.Assert.assertEquals;
15 |
16 | public class BCryptFormatterTest {
17 |
18 | @Rule
19 | public RepeatRule repeatRule = new RepeatRule();
20 | private final BCryptFormatter formatter = new BCryptFormatter.Default(new Radix64Encoder.Default(), UTF_8);
21 | private final BCryptParser parser = new BCryptParser.Default(new Radix64Encoder.Default(), UTF_8);
22 |
23 | @Test
24 | @Repeat(25)
25 | public void createRandomMessageAndVerify() throws IllegalBCryptFormatException {
26 | int cost = new Random().nextInt(27) + 4;
27 | byte[] salt = Bytes.random(16).array();
28 | byte[] hash = Bytes.random(23).array();
29 | BCrypt.Version version = BCrypt.Version.SUPPORTED_VERSIONS.get(new Random().nextInt(BCrypt.Version.SUPPORTED_VERSIONS.size()));
30 | BCrypt.HashData hashData = new BCrypt.HashData(cost, version, salt, hash);
31 | byte[] bcryptHash = formatter.createHashMessage(hashData);
32 | BCrypt.HashData parsed = parser.parse(bcryptHash);
33 |
34 | assertEquals(hashData, parsed);
35 | }
36 |
37 | @Test
38 | public void testAgainstReferenceHash1() {
39 | testAgainstReferenceHash(
40 | new BCrypt.HashData(6, BCrypt.Version.VERSION_2A,
41 | new byte[]{0x14, 0x4B, 0x3D, 0x69, 0x1A, 0x7B, 0x4E, (byte) 0xCF, 0x39, (byte) 0xCF, 0x73, 0x5C, (byte) 0x7F, (byte) 0xA7, (byte) 0xA7, (byte) 0x9C},
42 | new byte[]{0x55, 0x7E, (byte) 0x94, (byte) 0xF3, 0x4B, (byte) 0xF2, (byte) 0x86, (byte) 0xE8, 0x71, (byte) 0x9A, 0x26, (byte) 0xBE, (byte) 0x94, (byte) 0xAC, 0x1E, 0x16, (byte) 0xD9, 0x5E, (byte) 0xF9, (byte) 0xF8, 0x19, (byte) 0xDE, (byte) 0xE0}),
43 | "$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s."
44 | );
45 | }
46 |
47 | @Test
48 | public void testAgainstReferenceHash2() {
49 | testAgainstReferenceHash(
50 | new BCrypt.HashData(12, BCrypt.Version.VERSION_2Y,
51 | new byte[]{0x17, (byte) 0xA2, 0x3B, (byte) 0x87, (byte) 0x7F, (byte) 0xAA, (byte) 0xF5, (byte) 0xC3, (byte) 0x8E, (byte) 0x87, 0x27, 0x2E, 0x0C, (byte) 0xDF, 0x48, (byte) 0xAF},
52 | new byte[]{0x49, (byte) 0x8C, 0x11, (byte) 0xE6, (byte) 0xB9, (byte) 0xAD, 0x6E, (byte) 0xD4, 0x02, (byte) 0xA6, (byte) 0xC4, 0x40, 0x76, (byte) 0x88, 0x35, 0x74, (byte) 0xEA, 0x62, 0x01, 0x2C, (byte) 0x8B, 0x06, (byte) 0xB2}),
53 | "$2y$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG"
54 | );
55 | }
56 |
57 | @Test
58 | public void testAgainstReferenceHash3() {
59 | testAgainstReferenceHash(
60 | new BCrypt.HashData(8, BCrypt.Version.VERSION_2B,
61 | new byte[]{0x26, (byte) 0xC6, 0x30, 0x33, (byte) 0xC0, 0x4F, (byte) 0x8B, (byte) 0xCB, (byte) 0xA2, (byte) 0xFE, 0x24, (byte) 0xB5, 0x74, (byte) 0xDB, 0x62, 0x74},
62 | new byte[]{0x56, 0x70, 0x1B, 0x26, 0x16, 0x4D, (byte) 0x8F, 0x1B, (byte) 0xC1, 0x52, 0x25, (byte) 0xF4, 0x62, 0x34, (byte) 0xAC, (byte) 0x8A, (byte) 0xC7, (byte) 0x9B, (byte) 0xF5, (byte) 0xBC, 0x16, (byte) 0xBF, 0x48}),
63 | "$2b$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye"
64 | );
65 | }
66 |
67 | private void testAgainstReferenceHash(BCrypt.HashData hashData, String refHash) {
68 | byte[] bcryptHash = formatter.createHashMessage(hashData);
69 | assertArrayEquals(refHash.getBytes(StandardCharsets.UTF_8), bcryptHash);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/BCryptHighCostTest.java:
--------------------------------------------------------------------------------
1 | package at.favre.lib.crypto.bcrypt;
2 |
3 | import org.junit.Rule;
4 | import org.junit.Test;
5 | import org.junit.internal.runners.statements.FailOnTimeout;
6 | import org.junit.rules.Timeout;
7 | import org.junit.runner.Description;
8 | import org.junit.runners.model.Statement;
9 | import org.junit.runners.model.TestTimedOutException;
10 |
11 | import java.util.concurrent.TimeoutException;
12 |
13 | public class BCryptHighCostTest {
14 |
15 | private final char[] password = "1234567890abcdefABCDEF_.,".toCharArray();
16 |
17 | private static final int MIN_TIMEOUT = 100;
18 |
19 | @Rule
20 | public Timeout timeout = new Timeout(MIN_TIMEOUT) {
21 | public Statement apply(Statement base, Description description) {
22 | return new FailOnTimeout(base, MIN_TIMEOUT) {
23 | @Override
24 | public void evaluate() throws Throwable {
25 | try {
26 | super.evaluate();
27 | throw new TimeoutException();
28 | } catch (Exception e) {
29 | }
30 | }
31 | };
32 | }
33 | };
34 |
35 | @Test(expected = TestTimedOutException.class)
36 | public void testHashWithMaxCostFactorAndTimeout() {
37 | BCrypt.withDefaults().hash(31, password);
38 | }
39 |
40 | @Test(expected = TestTimedOutException.class)
41 | public void testHashWith30CostFactorAndTimeout() {
42 | BCrypt.withDefaults().hash(30, password);
43 | }
44 |
45 | @Test(expected = TestTimedOutException.class)
46 | public void testHashWith29CostFactorAndTimeout() {
47 | BCrypt.withDefaults().hash(29, password);
48 | }
49 |
50 | @Test(expected = TestTimedOutException.class)
51 | public void testHashWith28CostFactorAndTimeout() {
52 | BCrypt.withDefaults().hash(28, password);
53 | }
54 |
55 | @Test(expected = TestTimedOutException.class)
56 | public void testHashWith27CostFactorAndTimeout() {
57 | BCrypt.withDefaults().hash(27, password);
58 | }
59 |
60 | @Test(expected = TestTimedOutException.class)
61 | public void testHashWith26CostFactorAndTimeout() {
62 | BCrypt.withDefaults().hash(26, password);
63 | }
64 |
65 | @Test(expected = TestTimedOutException.class)
66 | public void testHashWith25CostFactorAndTimeout() {
67 | BCrypt.withDefaults().hash(25, password);
68 | }
69 |
70 | @Test(expected = TestTimedOutException.class)
71 | public void testHashWith24CostFactorAndTimeout() {
72 | BCrypt.withDefaults().hash(24, password);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/BCryptParserTest.java:
--------------------------------------------------------------------------------
1 | package at.favre.lib.crypto.bcrypt;
2 |
3 | import at.favre.lib.bytes.Bytes;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 |
7 | import java.nio.charset.StandardCharsets;
8 |
9 | import static org.junit.Assert.*;
10 |
11 | public class BCryptParserTest {
12 | private BCryptParser parser;
13 |
14 | @Before
15 | public void setUp() {
16 | parser = new BCryptParser.Default(new Radix64Encoder.Default(), StandardCharsets.UTF_8);
17 | }
18 |
19 | @Test
20 | public void parseDifferentCostFactors() throws Exception {
21 | for (int cost = 4; cost < 10; cost++) {
22 | byte[] salt = Bytes.random(16).array();
23 | byte[] hash = BCrypt.withDefaults().hash(cost, salt, "12345".getBytes());
24 |
25 | BCrypt.HashData parts = parser.parse(hash);
26 | assertEquals(cost, parts.cost);
27 | assertEquals(BCrypt.Version.VERSION_2A, parts.version);
28 | assertArrayEquals(salt, parts.rawSalt);
29 | assertEquals(23, parts.rawHash.length);
30 |
31 | System.out.println(parts);
32 | }
33 | }
34 |
35 | @Test
36 | public void parseDifferentVersions() throws Exception {
37 | for (BCrypt.Version version : BCrypt.Version.SUPPORTED_VERSIONS) {
38 | byte[] salt = Bytes.random(16).array();
39 | byte[] hash = BCrypt.with(version).hash(6, salt, "hs61i1oAJhdasdÄÄ".getBytes(StandardCharsets.UTF_8));
40 | BCrypt.HashData parts = parser.parse(hash);
41 | assertEquals(version, parts.version);
42 | assertEquals(6, parts.cost);
43 | assertArrayEquals(salt, parts.rawSalt);
44 | assertEquals(23, parts.rawHash.length);
45 |
46 | System.out.println(parts);
47 | }
48 | }
49 |
50 | @Test
51 | public void parseDoubleDigitCost() throws Exception {
52 | byte[] salt = Bytes.random(16).array();
53 | byte[] hash = BCrypt.with(BCrypt.Version.VERSION_2A).hash(11, salt, "i27ze8172eaidh asdhsd".getBytes(StandardCharsets.UTF_8));
54 | BCrypt.HashData parts = parser.parse(hash);
55 | assertEquals(BCrypt.Version.VERSION_2A, parts.version);
56 | assertEquals(11, parts.cost);
57 | assertArrayEquals(salt, parts.rawSalt);
58 | assertEquals(23, parts.rawHash.length);
59 |
60 | System.out.println(parts);
61 | }
62 |
63 | @Test(expected = IllegalBCryptFormatException.class)
64 | public void parseErrorMissingVersion() throws Exception {
65 | parser.parse("$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i".getBytes());
66 | }
67 |
68 | @Test(expected = IllegalBCryptFormatException.class)
69 | public void parseErrorMissingLeadingZero() throws Exception {
70 | parser.parse("$2a$6$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i".getBytes());
71 | }
72 |
73 | @Test(expected = IllegalBCryptFormatException.class)
74 | public void parseErrorMissingSeparator() throws Exception {
75 | parser.parse("$2a$06If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i".getBytes());
76 | }
77 |
78 | @Test(expected = IllegalBCryptFormatException.class)
79 | public void parseErrorMissingSeparator2() throws Exception {
80 | parser.parse("$2a06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i".getBytes());
81 | }
82 |
83 | @Test(expected = IllegalBCryptFormatException.class)
84 | public void parseErrorInvalidVersion() throws Exception {
85 | parser.parse("$2$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i".getBytes());
86 | }
87 |
88 | @Test(expected = IllegalBCryptFormatException.class)
89 | public void parseErrorInvalidVersion2() throws Exception {
90 | parser.parse("$3a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i".getBytes());
91 | }
92 |
93 | @Test(expected = IllegalBCryptFormatException.class)
94 | public void parseErrorInvalidVersion3() throws Exception {
95 | parser.parse("$2l$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i".getBytes());
96 | }
97 |
98 | @Test(expected = IllegalBCryptFormatException.class)
99 | public void parseErrorMissingSaltAndHas() throws Exception {
100 | parser.parse("$2a$06$".getBytes());
101 | }
102 |
103 | @Test(expected = IllegalBCryptFormatException.class)
104 | public void parseErrorMissingHash() throws Exception {
105 | parser.parse("$2a$06$If6bvum7DFjUnE9p2uDeDu".getBytes());
106 | }
107 |
108 | @Test(expected = IllegalBCryptFormatException.class)
109 | public void parseErrorMissingChar() throws Exception {
110 | parser.parse("$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0".getBytes());
111 | }
112 |
113 | @Test(expected = IllegalBCryptFormatException.class)
114 | public void parseErrorTooLong() throws Exception {
115 | parser.parse("$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i9".getBytes());
116 | }
117 |
118 | @Test(expected = IllegalArgumentException.class)
119 | public void parseErrorNullHash() throws Exception {
120 | parser.parse(null);
121 | }
122 |
123 | @Test(expected = IllegalArgumentException.class)
124 | public void parseErrorZeroLengthHash() throws Exception {
125 | parser.parse(new byte[0]);
126 | }
127 |
128 | @Test(expected = IllegalBCryptFormatException.class)
129 | public void parseErrorWayTooShort() throws Exception {
130 | parser.parse("$2a".getBytes());
131 | }
132 |
133 | @Test
134 | public void parseErrorTooLongGetExceptionMessage() {
135 | try {
136 | parser.parse("$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i9".getBytes());
137 | fail();
138 | } catch (IllegalBCryptFormatException e) {
139 | assertNotNull(e.getMessage());
140 | assertTrue(e.getMessage().length() > 20);
141 | System.out.println(e.getMessage());
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/BcBcryptTestCases.java:
--------------------------------------------------------------------------------
1 | package at.favre.lib.crypto.bcrypt;
2 |
3 | import at.favre.lib.bytes.Bytes;
4 | import at.favre.lib.crypto.bcrypt.misc.Repeat;
5 | import at.favre.lib.crypto.bcrypt.misc.RepeatRule;
6 | import org.junit.Rule;
7 | import org.junit.Test;
8 |
9 | import java.util.Arrays;
10 | import java.util.Date;
11 | import java.util.Random;
12 |
13 | import static at.favre.lib.crypto.bcrypt.BcryptTest.UTF_8;
14 | import static org.junit.Assert.assertArrayEquals;
15 |
16 | /**
17 | * Tests against the Bouncy Castle implementation of BCrypt
18 | *
19 | * See: https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/generators/BCrypt.java
20 | */
21 | public class BcBcryptTestCases {
22 | @Rule
23 | public RepeatRule repeatRule = new RepeatRule();
24 |
25 | // see: https://www.programcreek.com/java-api-examples/?code=ttt43ttt/gwt-crypto/gwt-crypto-master/src/test/java/org/bouncycastle/crypto/test/BCryptTest.java
26 | private static final Object[][] testVectors = {
27 | {"00", "144b3d691a7b4ecf39cf735c7fa7a79c", 6, "557e94f34bf286e8719a26be94ac1e16d95ef9f819dee092"},
28 | {"00", "26c63033c04f8bcba2fe24b574db6274", 8, "56701b26164d8f1bc15225f46234ac8ac79bf5bc16bf48ba"},
29 | {"00", "9b7c9d2ada0fd07091c915d1517701d6", 10, "7b2e03106a43c9753821db688b5cc7590b18fdf9ba544632"},
30 | {"6100", "a3612d8c9a37dac2f99d94da03bd4521", 6, "e6d53831f82060dc08a2e8489ce850ce48fbf976978738f3"},
31 | {"6100", "7a17b15dfe1c4be10ec6a3ab47818386", 8, "a9f3469a61cbff0a0f1a1445dfe023587f38b2c9c40570e1"},
32 | {"6100", "9bef4d04e1f8f92f3de57323f8179190", 10, "5169fd39606d630524285147734b4c981def0ee512c3ace1"},
33 | {"61626300", "2a1f1dc70a3d147956a46febe3016017", 6, "d9a275b493bcbe1024b0ff80d330253cfdca34687d8f69e5"},
34 | {"61626300", "4ead845a142c9bc79918c8797f470ef5", 8, "8d4131a723bfbbac8a67f2e035cae08cc33b69f37331ea91"},
35 | {"61626300", "631c554493327c32f9c26d9be7d18e4c", 10, "8cd0b863c3ff0860e31a2b42427974e0283b3af7142969a6"},
36 | {"6162636465666768696a6b6c6d6e6f707172737475767778797a00", "02d1176d74158ee29cffdac6150cf123", 6, "4d38b523ce9dc6f2f6ff9fb3c2cd71dfe7f96eb4a3baf19f"},
37 | {"6162636465666768696a6b6c6d6e6f707172737475767778797a00", "715b96caed2ac92c354ed16c1e19e38a", 8, "98bf9ffc1f5be485f959e8b1d526392fbd4ed2d5719f506b"},
38 | {"6162636465666768696a6b6c6d6e6f707172737475767778797a00", "85727e838f9049397fbec90566ede0df", 10, "cebba53f67bd28af5a44c6707383c231ac4ef244a6f5fb2b"},
39 | {"7e21402324255e262a28292020202020207e21402324255e262a2829504e4246524400", "8512ae0d0fac4ec9a5978f79b6171028", 6, "26f517fe5345ad575ba7dfb8144f01bfdb15f3d47c1e146a"},
40 | {"7e21402324255e262a28292020202020207e21402324255e262a2829504e4246524400", "1ace2de8807df18c79fced54678f388f", 8, "d51d7cdf839b91a25758b80141e42c9f896ae80fd6cd561f"},
41 | {"7e21402324255e262a28292020202020207e21402324255e262a2829504e4246524400", "36285a6267751b14ba2dc989f6d43126", 10, "db4fab24c1ff41c1e2c966f8b3d6381c76e86f52da9e15a9"},
42 | {"c2a300", "144b3d691a7b4ecf39cf735c7fa7a79c", 6, "5a6c4fedb23980a7da9217e0442565ac6145b687c7313339"}};
43 |
44 | @Test
45 | @Repeat(10)
46 | public void testRandomAgainstJBcrypt() {
47 | int cost = new Random().nextInt(3) + 4;
48 | String pw = Bytes.random(8 + new Random().nextInt(24)).encodeBase64();
49 | byte[] salt = Bytes.random(16).array();
50 |
51 | //BC will only return the hash without the salt, cost factor and version identifier and does not add a null terminator
52 | byte[] bcryptHashOnly = org.bouncycastle.crypto.generators.BCrypt.generate(Bytes.from(pw).array(), salt, cost);
53 | BCrypt.HashData hash = BCrypt.with(BCrypt.Version.VERSION_BC).hashRaw(cost, salt, pw.getBytes(UTF_8));
54 | assertArrayEquals(hash.rawHash, bcryptHashOnly);
55 | }
56 |
57 | @Test
58 | public void testBcRefVectors() {
59 | Date start = new Date();
60 | System.out.println("Bouncy Castle Test Vector Suite ID: " + Bytes.from(Arrays.hashCode(testVectors)).encodeHex() + " [" + testVectors.length + "] (" + start.toString() + ")");
61 | for (Object[] testVector : testVectors) {
62 | byte[] pw = Bytes.parseHex((String) testVector[0]).array();
63 | byte[] salt = Bytes.parseHex((String) testVector[1]).array();
64 | int cost = (int) testVector[2];
65 | byte[] refHash = Bytes.parseHex((String) testVector[3]).array();
66 |
67 | BCrypt.HashData hash = BCrypt.with(BCrypt.Version.VERSION_BC).hashRaw(cost, salt, pw);
68 | assertArrayEquals(refHash, hash.rawHash);
69 | }
70 | System.out.println("finished (" + (new Date().getTime() - start.getTime()) + " ms)");
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/BenchmarkTest.java:
--------------------------------------------------------------------------------
1 | package at.favre.lib.crypto.bcrypt;
2 |
3 | import at.favre.lib.crypto.bcrypt.misc.BcryptMicroBenchmark;
4 | import org.junit.Ignore;
5 | import org.junit.Test;
6 |
7 | public class BenchmarkTest {
8 |
9 | @Test
10 | public void quickBenchmark() {
11 | new BcryptMicroBenchmark(1500, new int[]{4, 5, 6, 7}, 0, true).benchmark();
12 | }
13 |
14 | @Test
15 | @Ignore
16 | public void fullBenchmark() {
17 | new BcryptMicroBenchmark(819200, new int[]{4, 6, 8, 9, 10, 11, 12, 14, 15}, 2, false).benchmark();
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/FavreBcryptReferenceTests.java:
--------------------------------------------------------------------------------
1 | package at.favre.lib.crypto.bcrypt;
2 |
3 | import at.favre.lib.bytes.Bytes;
4 | import at.favre.lib.crypto.bcrypt.misc.BcryptTestEntriesGenerator;
5 | import at.favre.lib.crypto.bcrypt.misc.BcryptTestEntry;
6 | import org.junit.Ignore;
7 | import org.junit.Test;
8 |
9 | import java.util.Arrays;
10 | import java.util.Date;
11 |
12 | /**
13 | * Test Vectors generated for this implementation - use to test other implementations
14 | */
15 | public class FavreBcryptReferenceTests {
16 |
17 | private BcryptTestEntry[] testEntries = new BcryptTestEntry[]{
18 | // empty pw - same salt
19 | new BcryptTestEntry("", 4, "zVHmKQtGGQob.b/Nc7l9NO", "$2a$04$zVHmKQtGGQob.b/Nc7l9NO8UlrYcW05FiuCj/SxsFO/ZtiN9.mNzy"),
20 | new BcryptTestEntry("", 5, "zVHmKQtGGQob.b/Nc7l9NO", "$2a$05$zVHmKQtGGQob.b/Nc7l9NOWES.1hkVBgy5IWImh9DOjKNU8atY4Iy"),
21 | new BcryptTestEntry("", 6, "zVHmKQtGGQob.b/Nc7l9NO", "$2a$06$zVHmKQtGGQob.b/Nc7l9NOjOl7l4oz3WSh5fJ6414Uw8IXRAUoiaO"),
22 | new BcryptTestEntry("", 7, "zVHmKQtGGQob.b/Nc7l9NO", "$2a$07$zVHmKQtGGQob.b/Nc7l9NOBsj1dQpBA1HYNGpIETIByoNX9jc.hOi"),
23 | new BcryptTestEntry("", 8, "zVHmKQtGGQob.b/Nc7l9NO", "$2a$08$zVHmKQtGGQob.b/Nc7l9NOiLTUh/9MDpX86/DLyEzyiFjqjBFePgO"),
24 |
25 | // random pw & salt - short pw
26 | new BcryptTestEntry("<.S.2K(Zq'", 4, "VYAclAMpaXY/oqAo9yUpku", "$2a$04$VYAclAMpaXY/oqAo9yUpkuWmoYywaPzyhu56HxXpVltnBIfmO9tgu"),
27 | new BcryptTestEntry("5.rApO%5jA", 5, "kVNDrnYKvbNr5AIcxNzeIu", "$2a$05$kVNDrnYKvbNr5AIcxNzeIuRcyIF5cZk6UrwHGxENbxP5dVv.WQM/G"),
28 | new BcryptTestEntry("oW++kSrQW^", 6, "QLKkRMH9Am6irtPeSKN5sO", "$2a$06$QLKkRMH9Am6irtPeSKN5sObJGr3j47cO6Pdf5JZ0AsJXuze0IbsNm"),
29 | new BcryptTestEntry("ggJ\\KbTnDG", 7, "4H896R09bzjhapgCPS/LYu", "$2a$07$4H896R09bzjhapgCPS/LYuMzAQluVgR5iu/ALF8L8Aln6lzzYXwbq"),
30 | new BcryptTestEntry("49b0:;VkH/", 8, "hfvO2retKrSrx5f2RXikWe", "$2a$08$hfvO2retKrSrx5f2RXikWeFWdtSesPlbj08t/uXxCeZoHRWDz/xFe"),
31 | new BcryptTestEntry(">9N^5jc##'", 9, "XZLvl7rMB3EvM0c1.JHivu", "$2a$09$XZLvl7rMB3EvM0c1.JHivuIDPJWeNJPTVrpjZIEVRYYB/mF6cYgJK"),
32 | new BcryptTestEntry("\\$ch)s4WXp", 10, "aIjpMOLK5qiS9zjhcHR5TO", "$2a$10$aIjpMOLK5qiS9zjhcHR5TOU7v2NFDmcsBmSFDt5EHOgp/jeTF3O/q"),
33 | new BcryptTestEntry("RYoj\\_>2P7", 12, "esIAHiQAJNNBrsr5V13l7.", "$2a$12$esIAHiQAJNNBrsr5V13l7.RFWWJI2BZFtQlkFyiWXjou05GyuREZa"),
34 |
35 | // random pw & salt - long pw
36 | new BcryptTestEntry("^Q&\"]A`%/A(BVGt>QaX0M-#
21 | * See: https://github.com/jeremyh/jBCrypt/blob/master/src/test/java/org/mindrot/TestBCrypt.java
22 | */
23 | public class JBcryptTestCases {
24 | @Rule
25 | public RepeatRule repeatRule = new RepeatRule();
26 |
27 | private final BcryptTestEntry[] testEntries = new BcryptTestEntry[]{
28 | new BcryptTestEntry("abc", 6, "If6bvum7DFjUnE9p2uDeDu", "$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i"),
29 | new BcryptTestEntry("abc", 8, "Ro0CUfOqk6cXEKf3dyaM7O", "$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm"),
30 | new BcryptTestEntry("abc", 10, "WvvTPHKwdBJ3uk0Z37EMR.", "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi"),
31 | new BcryptTestEntry("abc", 12, "EXRkfkdmXn2gzds2SSitu.", "$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q"),
32 |
33 | new BcryptTestEntry("a", 6, "m0CrhHm10qJ3lXRY.5zDGO", "$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe"),
34 | new BcryptTestEntry("a", 8, "cfcvVd2aQ8CMvoMpP2EBfe", "$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V."),
35 | new BcryptTestEntry("a", 10, "k87L/MF28Q673VKh8/cPi.", "$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u"),
36 | new BcryptTestEntry("a", 12, "8NJH3LsPrANStV6XtBakCe", "$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS"),
37 |
38 | new BcryptTestEntry("", 6, "DCq7YPn5Rq63x1Lad4cll.", "$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s."),
39 | new BcryptTestEntry("", 8, "HqWuK6/Ng6sg9gQzbLrgb.", "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye"),
40 | new BcryptTestEntry("", 10, "k1wbIrmNyFAPwPVPSVa/ze", "$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW"),
41 | new BcryptTestEntry("", 12, "k42ZFHFWqBp3vWli.nIn8u", "$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO"),
42 |
43 | new BcryptTestEntry("abcdefghijklmnopqrstuvwxyz", 6, ".rCVZVOThsIa97pEDOxvGu", "$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC"),
44 | new BcryptTestEntry("abcdefghijklmnopqrstuvwxyz", 8, "aTsUwsyowQuzRrDqFflhge", "$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz."),
45 | new BcryptTestEntry("abcdefghijklmnopqrstuvwxyz", 10, "fVH8e28OQRj9tqiDXs1e1u", "$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq"),
46 | new BcryptTestEntry("abcdefghijklmnopqrstuvwxyz", 12, "D4G5f18o7aMMfwasBL7Gpu", "$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG"),
47 |
48 | new BcryptTestEntry("~!@#$%^&*() ~!@#$%^&*()PNBFRD", 6, "fPIsBO8qRqkjj273rfaOI.", "$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO"),
49 | new BcryptTestEntry("~!@#$%^&*() ~!@#$%^&*()PNBFRD", 8, "Eq2r4G/76Wv39MzSX262hu", "$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW"),
50 | new BcryptTestEntry("~!@#$%^&*() ~!@#$%^&*()PNBFRD", 10, "LgfYWkbzEvQ4JakH7rOvHe", "$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS"),
51 | new BcryptTestEntry("~!@#$%^&*() ~!@#$%^&*()PNBFRD", 12, "WApznUOJfkEGSmYRfnkrPO", "$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC"),
52 | };
53 |
54 | @Test
55 | public void testAgainstReferenceHashes() {
56 | Date start = new Date();
57 | System.out.println("jBcrypt Test Vector Suite ID: " + Bytes.from(Arrays.hashCode(testEntries)).encodeHex() + " [" + testEntries.length + "] (" + start.toString() + ")");
58 | BcryptTestEntry.testEntries(testEntries);
59 | System.out.println("finished (" + (new Date().getTime() - start.getTime()) + " ms)");
60 | }
61 |
62 | @Test
63 | @Repeat(8)
64 | public void testRandomAgainstJBcrypt() throws IllegalBCryptFormatException {
65 | int cost = new Random().nextInt(3) + 4;
66 | String pw = Bytes.random(8 + new Random().nextInt(24)).encodeBase64();
67 | String jbcryptHash = org.mindrot.jbcrypt.BCrypt.hashpw(pw, org.mindrot.jbcrypt.BCrypt.gensalt(cost));
68 | BCrypt.HashData hashData = new BCryptParser.Default(new Radix64Encoder.Default(), StandardCharsets.UTF_8)
69 | .parse(jbcryptHash.getBytes(UTF_8));
70 |
71 | byte[] hash = BCrypt.with(BCrypt.Version.VERSION_2A).hash(cost, hashData.rawSalt, pw.getBytes(UTF_8));
72 |
73 | assertArrayEquals(jbcryptHash.getBytes(UTF_8), hash);
74 | }
75 |
76 | @Test
77 | @Repeat(8)
78 | public void testCheckPwAgainstFavreLib() {
79 | int cost = new Random().nextInt(5) + 4;
80 | String pw = "aAöoi. --~!@#$%^&*(kjlöoi" + new Random(999999999);
81 | byte[] hash = BCrypt.with(BCrypt.Version.VERSION_2A).hash(cost, pw.toCharArray());
82 | org.mindrot.jbcrypt.BCrypt.checkpw(pw, new String(hash, UTF_8));
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/LongPasswordStrategyTest.java:
--------------------------------------------------------------------------------
1 | package at.favre.lib.crypto.bcrypt;
2 |
3 | import at.favre.lib.bytes.Bytes;
4 | import at.favre.lib.bytes.BytesTransformer;
5 | import org.junit.Test;
6 |
7 | import static org.junit.Assert.*;
8 |
9 | public class LongPasswordStrategyTest {
10 |
11 | private final int maxLength = 72;
12 | private static final BCrypt.Version DEFAULT_VERSION = BCrypt.Version.VERSION_2A;
13 |
14 | @Test
15 | public void testFactory() {
16 | assertNotNull(LongPasswordStrategies.hashSha512(DEFAULT_VERSION).derive(Bytes.random(100).array()));
17 | assertNotNull(LongPasswordStrategies.truncate(DEFAULT_VERSION).derive(Bytes.random(100).array()));
18 | assertNotNull(LongPasswordStrategies.none().derive(Bytes.random(100).array()));
19 | }
20 |
21 | @Test(expected = IllegalArgumentException.class)
22 | public void testFactoryForStrictShouldThrowException() {
23 | LongPasswordStrategies.strict(DEFAULT_VERSION).derive(Bytes.random(100).array());
24 | }
25 |
26 | @Test
27 | public void testStrictLengthStrategy() {
28 | LongPasswordStrategy strategy = new LongPasswordStrategy.StrictMaxPasswordLengthStrategy(maxLength);
29 | byte[] byteArray;
30 |
31 | for (int i = 1; i < maxLength; i++) {
32 | byteArray = Bytes.random(i).array();
33 | assertSame(byteArray, strategy.derive(byteArray));
34 | }
35 |
36 | checkExpectToFail(maxLength, strategy);
37 |
38 | for (int i = 1; i < maxLength; i++) {
39 | checkExpectToFail(maxLength + i, strategy);
40 | }
41 | }
42 |
43 | private void checkExpectToFail(int maxLength, LongPasswordStrategy strategy) {
44 | byte[] byteArray;
45 | try {
46 | byteArray = Bytes.random(maxLength).array();
47 | assertArrayEquals(byteArray, strategy.derive(byteArray));
48 | fail();
49 | } catch (IllegalArgumentException ignored) {
50 | } catch (Exception e) {
51 | fail();
52 | }
53 | }
54 |
55 | @Test
56 | public void testTruncateStrategy() {
57 | LongPasswordStrategy strategy = new LongPasswordStrategy.TruncateStrategy(maxLength);
58 | byte[] byteArray;
59 |
60 | for (int i = 1; i < maxLength; i++) {
61 | byteArray = Bytes.random(i).array();
62 | assertSame(byteArray, strategy.derive(byteArray));
63 | }
64 |
65 | testTooLongTruncate(maxLength, maxLength, strategy);
66 |
67 | for (int i = 1; i < maxLength; i++) {
68 | testTooLongTruncate(maxLength + i, maxLength, strategy);
69 | }
70 | }
71 |
72 | private void testTooLongTruncate(int length, int maxLength, LongPasswordStrategy strategy) {
73 | byte[] byteArray;
74 | byteArray = Bytes.random(length).array();
75 | byte[] out = strategy.derive(byteArray);
76 | assertEquals(maxLength, out.length);
77 | assertArrayEquals(Bytes.wrap(byteArray).resize(maxLength, BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_ZERO_INDEX).array(), out);
78 | }
79 |
80 | @Test
81 | public void testSha512HashStrategy() {
82 | LongPasswordStrategy strategy = new LongPasswordStrategy.Sha512DerivationStrategy(maxLength);
83 | byte[] byteArray;
84 |
85 | for (int i = 1; i < maxLength; i++) {
86 | byteArray = Bytes.random(i).array();
87 | assertSame(byteArray, strategy.derive(byteArray));
88 | }
89 |
90 | for (int i = maxLength; i < maxLength * 2; i++) {
91 | byteArray = Bytes.random(maxLength).array();
92 | assertArrayEquals(Bytes.wrap(byteArray).hash("SHA-512").array(), strategy.derive(byteArray));
93 | assertTrue(byteArray.length <= maxLength);
94 | System.out.println(Bytes.wrap(byteArray).encodeHex());
95 | }
96 | }
97 |
98 | @Test
99 | public void testPassThroughStrategy() {
100 | LongPasswordStrategy strategy = new LongPasswordStrategy.PassThroughStrategy();
101 | byte[] byteArray;
102 |
103 | for (int i = 1; i < 64; i++) {
104 | byteArray = Bytes.random(i).array();
105 | assertSame(byteArray, strategy.derive(byteArray));
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/Radix64Test.java:
--------------------------------------------------------------------------------
1 | package at.favre.lib.crypto.bcrypt;
2 |
3 | import at.favre.lib.bytes.Bytes;
4 | import at.favre.lib.crypto.bcrypt.misc.Repeat;
5 | import at.favre.lib.crypto.bcrypt.misc.RepeatRule;
6 | import org.junit.Before;
7 | import org.junit.Ignore;
8 | import org.junit.Rule;
9 | import org.junit.Test;
10 |
11 | import java.nio.charset.StandardCharsets;
12 | import java.util.Arrays;
13 |
14 | import static org.junit.Assert.assertArrayEquals;
15 |
16 | public class Radix64Test {
17 |
18 | @Rule
19 | public RepeatRule repeatRule = new RepeatRule();
20 | private Radix64Encoder encoder;
21 |
22 | private TestCase[] referenceRadix64Table = new TestCase[]{
23 | new TestCase("Gu", new byte[]{0x23}),
24 | new TestCase("u2O", new byte[]{(byte) 0xC3, (byte) 0x84}),
25 | new TestCase("9txf", new byte[]{(byte) 0xFE, (byte) 0xFC, (byte) 0xE1}),
26 | new TestCase("hNxTH.", new byte[]{(byte) 0x8C, (byte) 0xFC, (byte) 0xD5, 0x24}),
27 | new TestCase("id8RFNq", new byte[]{(byte) 0x91, (byte) 0xFF, (byte) 0x93, 0x1C, (byte) 0xFB}),
28 | new TestCase("wQVuTsH4", new byte[]{(byte) 0xC9, 0x25, (byte) 0xF0, 0x56, (byte) 0xE2, 0x7A}),
29 | new TestCase("AN1d.KXq4u", new byte[]{0x08, (byte) 0xFD, (byte) 0xDF, 0x00, (byte) 0xC6, 0x6C, (byte) 0xEB}),
30 | new TestCase("h6BXVr084r2", new byte[]{(byte) 0x8F, (byte) 0xC0, (byte) 0xD9, 0x5E, (byte) 0xDD, (byte) 0xBE, (byte) 0xEA, (byte) 0xDE}),
31 | new TestCase("0dT6vifCI8aW", new byte[]{(byte) 0xD9, (byte) 0xF5, 0x7C, (byte) 0xC6, 0x48, 0x44, 0x2B, (byte) 0xE7, 0x18}),
32 | new TestCase("zzfD4GfFebPj6e", new byte[]{(byte) 0xD7, 0x58, 0x45, (byte) 0xE8, (byte) 0x88, 0x47, (byte) 0x81, (byte) 0xD4, 0x65, (byte) 0xF2}),
33 | new TestCase("P09R9uV5cCDV11K", new byte[]{0x47, 0x6F, (byte) 0xD3, (byte) 0xFF, 0x05, (byte) 0xFB, 0x78, 0x41, 0x57, (byte) 0xDF, 0x73}),
34 | new TestCase("D7EUIpVIW0l7qkNM", new byte[]{0x17, (byte) 0xD1, (byte) 0x96, 0x2A, (byte) 0xB5, (byte) 0xCA, 0x63, 0x69, (byte) 0xFD, (byte) 0xB2, 0x63, (byte) 0xCE}),
35 | new TestCase("Vtolm7ldZl8KcHYvcu", new byte[]{0x5E, (byte) 0xFA, (byte) 0xA7, (byte) 0xA3, (byte) 0xD9, (byte) 0xDF, 0x6E, (byte) 0x7F, (byte) 0x8C, 0x78, (byte) 0x96, (byte) 0xB1, 0x7B}),
36 | new TestCase("87MSgioX97qxvzFfoPG", new byte[]{(byte) 0xFB, (byte) 0xD3, (byte) 0x94, (byte) 0x8A, 0x4A, (byte) 0x99, (byte) 0xFF, (byte) 0xDB, 0x33, (byte) 0xC7, 0x51, (byte) 0xE1, (byte) 0xA9, 0x12}),
37 | new TestCase("hv442zAAczwYOFY6SHF5", new byte[]{(byte) 0x8F, 0x1E, (byte) 0xBA, (byte) 0xE3, 0x50, (byte) 0x82, 0x7B, 0x5C, (byte) 0x9A, 0x40, 0x76, (byte) 0xBC, 0x50, (byte) 0x91, (byte) 0xFB}),
38 | new TestCase("5Ao7p22a75KWm6/LcL/uXO", new byte[]{(byte) 0xEC, 0x2A, (byte) 0xBD, (byte) 0xAF, (byte) 0x8E, 0x1C, (byte) 0xF7, (byte) 0xB3, 0x18, (byte) 0xA3, (byte) 0xC0, 0x4D, 0x78, (byte) 0xD0, 0x70, 0x65}),
39 | new TestCase("K7Mk6gzVXVCZoz4br8mjxVe", new byte[]{0x33, (byte) 0xD3, (byte) 0xA6, (byte) 0xF2, 0x2D, 0x57, 0x65, 0x71, 0x1B, (byte) 0xAB, 0x5E, (byte) 0x9D, (byte) 0xB7, (byte) 0xEA, 0x25, (byte) 0xCD, 0x78}),
40 | new TestCase("CJHqewYFU0WqKS3fJ0cgLE3Q", new byte[]{0x10, (byte) 0xB2, 0x6C, (byte) 0x83, 0x26, (byte) 0x87, 0x5B, 0x66, 0x2C, 0x31, 0x4E, 0x61, 0x2F, 0x67, (byte) 0xA2, 0x34, 0x6E, 0x52}),
41 | new TestCase("h0kTxLSkPD4mL1WMyflwMdssje", new byte[]{(byte) 0x8F, 0x69, (byte) 0x95, (byte) 0xCC, (byte) 0xD5, 0x26, 0x44, 0x5E, (byte) 0xA8, 0x37, 0x76, 0x0E, (byte) 0xD2, 0x19, (byte) 0xF2, 0x39, (byte) 0xFB, (byte) 0xAE, (byte) 0x96}),
42 | new TestCase("LuOXwx4u9ZzV5/t36yMTAslb8Wy", new byte[]{0x37, 0x04, 0x19, (byte) 0xCB, 0x3E, (byte) 0xB0, (byte) 0xFD, (byte) 0xBD, 0x57, (byte) 0xEC, 0x1B, (byte) 0xF9, (byte) 0xF3, 0x43, (byte) 0x95, 0x0A, (byte) 0xE9, (byte) 0xDD, (byte) 0xF9, (byte) 0x8D}),
43 | new TestCase("mTt2C3/SMa9uLnnfr6WEy3kL163K", new byte[]{(byte) 0xA1, 0x5B, (byte) 0xF8, 0x13, (byte) 0x90, 0x54, 0x39, (byte) 0xCF, (byte) 0xF0, 0x36, (byte) 0x9A, 0x61, (byte) 0xB7, (byte) 0xC6, 0x06, (byte) 0xD3, (byte) 0x99, (byte) 0x8D, (byte) 0xDF, (byte) 0xCE, 0x4C}),
44 | new TestCase("nnuqg7AQGgp6FBBOj/w5tfPg/xnGtu", new byte[]{(byte) 0xA6, (byte) 0x9C, 0x2C, (byte) 0x8B, (byte) 0xD0, (byte) 0x92, 0x22, 0x2A, (byte) 0xFC, 0x1C, 0x30, (byte) 0xD0, (byte) 0x94, 0x1C, (byte) 0xBB, (byte) 0xBE, 0x14, 0x62, 0x07, 0x3A, 0x48, (byte) 0xBF}),
45 | new TestCase("PFXsTdmWq6za6S5F1B9D28li7Dlwv4S", new byte[]{0x44, 0x76, 0x6E, 0x55, (byte) 0xFA, 0x18, (byte) 0xB3, (byte) 0xCD, 0x5C, (byte) 0xF1, 0x4E, (byte) 0xC7, (byte) 0xDC, 0x3F, (byte) 0xC5, (byte) 0xE3, (byte) 0xE9, (byte) 0xE4, (byte) 0xF4, 0x59, (byte) 0xF2, (byte) 0xC7, (byte) 0xA5}),
46 | new TestCase("LusfR3hiP7bGKst1tlckI6S.Ju0rmCIe", new byte[]{0x37, 0x0B, (byte) 0xA1, 0x4F, (byte) 0x98, (byte) 0xE4, 0x47, (byte) 0xD7, 0x48, 0x32, (byte) 0xEB, (byte) 0xF7, (byte) 0xBE, 0x77, (byte) 0xA6, 0x2B, (byte) 0xC5, 0x00, 0x2F, 0x0D, (byte) 0xAD, (byte) 0xA0, 0x42, (byte) 0xA0}),
47 | new TestCase("ArmQQ/Y7hPv4gii00nXAPe", new byte[]{0x0A, (byte) 0xDA, 0x12, 0x48, 0x16, (byte) 0xBD, (byte) 0x8D, 0x1C, 0x7A, (byte) 0x8A, 0x49, 0x36, (byte) 0xDA, (byte) 0x96, 0x42, 0x46}),
48 | new TestCase("WNwJW0zlHtW2pawQ76HXV.", new byte[]{0x60, (byte) 0xFC, (byte) 0x8B, 0x63, 0x6D, 0x67, 0x26, (byte) 0xF6, 0x38, (byte) 0xAD, (byte) 0xCC, (byte) 0x92, (byte) 0xF7, (byte) 0xC2, 0x59, 0x5C}),
49 | new TestCase("Ihd0Lx0pXQquyPZgTFXd5.", new byte[]{0x2A, 0x37, (byte) 0xF6, 0x37, 0x3D, (byte) 0xAB, 0x65, 0x2B, 0x30, (byte) 0xD1, 0x16, (byte) 0xE2, 0x54, 0x76, 0x5F, (byte) 0xEC}),
50 | new TestCase("sdKbHDooFgr0OOEVXgTKTe", new byte[]{(byte) 0xB9, (byte) 0xF3, 0x1D, 0x24, 0x5A, (byte) 0xAA, 0x1E, 0x2B, 0x76, 0x41, 0x01, (byte) 0x97, 0x66, 0x25, 0x4C, 0x56}),
51 | new TestCase("gA3GRpUX1hmMUUmyF98Gn.", new byte[]{(byte) 0x88, 0x2E, 0x48, 0x4E, (byte) 0xB5, (byte) 0x99, (byte) 0xDE, 0x3A, 0x0E, 0x59, 0x6A, 0x34, 0x1F, (byte) 0xFF, (byte) 0x88, (byte) 0xA4}),
52 | new TestCase("SVYk8TP3/CfBLBi/YGiqle", new byte[]{0x51, 0x76, (byte) 0xA6, (byte) 0xF9, 0x54, 0x79, 0x04, 0x48, 0x43, 0x34, 0x39, 0x01, 0x68, (byte) 0x89, 0x2C, (byte) 0x9E}),
53 | new TestCase("Qk8HfY6w82UI1K8Lf6ZIlO", new byte[]{0x4A, 0x6F, (byte) 0x89, (byte) 0x85, (byte) 0xAF, 0x32, (byte) 0xFB, (byte) 0x85, (byte) 0x8A, (byte) 0xDC, (byte) 0xCF, (byte) 0x8D, (byte) 0x87, (byte) 0xC6, (byte) 0xCA, (byte) 0x9D}),
54 | new TestCase("5ad9sLQa1ecexexrFsntee", new byte[]{(byte) 0xED, (byte) 0xC7, (byte) 0xFF, (byte) 0xB8, (byte) 0xD4, (byte) 0x9C, (byte) 0xDE, 0x07, (byte) 0xA0, (byte) 0xCE, 0x0C, (byte) 0xED, 0x1E, (byte) 0xEA, 0x6F, (byte) 0x82}),
55 | new TestCase("sgGn3fI2PiM1ss2HRnlycu", new byte[]{(byte) 0xBA, 0x22, 0x29, (byte) 0xE6, 0x12, (byte) 0xB8, 0x46, 0x43, (byte) 0xB7, (byte) 0xBA, (byte) 0xEE, 0x09, 0x4E, (byte) 0x99, (byte) 0xF4, 0x7B}),
56 | new TestCase("jsKqqO5SQg.ZDtpq/k5r4O", new byte[]{(byte) 0x96, (byte) 0xE3, 0x2C, (byte) 0xB1, 0x0E, (byte) 0xD4, 0x4A, 0x20, 0x1B, 0x16, (byte) 0xFA, (byte) 0xEC, 0x06, 0x6E, (byte) 0xED, (byte) 0xE9}),
57 | new TestCase("iL4JMZdQBwXVVXKTfFQRpO", new byte[]{(byte) 0x90, (byte) 0xDE, (byte) 0x8B, 0x39, (byte) 0xB7, (byte) 0xD2, 0x0F, 0x26, 0x57, 0x5D, (byte) 0x93, 0x15, (byte) 0x84, 0x74, (byte) 0x93, (byte) 0xAD}),
58 | new TestCase("vmSnTF28TuP2KKBAOPbPNu", new byte[]{(byte) 0xC6, (byte) 0x85, 0x29, 0x54, 0x7E, 0x3E, 0x57, 0x04, 0x78, 0x30, (byte) 0xC0, (byte) 0xC2, 0x41, 0x17, 0x51, 0x3F}),
59 | new TestCase("UwioCLPPqh8EWrHxalX/Qu", new byte[]{0x5B, 0x29, 0x2A, 0x10, (byte) 0xD4, 0x51, (byte) 0xB2, 0x3F, (byte) 0x86, 0x62, (byte) 0xD2, 0x73, 0x72, 0x76, 0x41, 0x4B}),
60 | new TestCase("Ljfcg2dt2q9mPyk4blNd76/aOFGN8ca", new byte[]{0x36, 0x58, 0x5E, (byte) 0x8B, (byte) 0x87, (byte) 0xEF, (byte) 0xE2, (byte) 0xCF, (byte) 0xE8, 0x47, 0x49, (byte) 0xBA, 0x76, 0x73, (byte) 0xDF, (byte) 0xF7, (byte) 0xC0, 0x5C, 0x40, 0x72, 0x0F, (byte) 0xF9, (byte) 0xE7}),
61 | new TestCase("4ydWZwAWzZa9YGSf8oEBTOCGY8uBDcO", new byte[]{(byte) 0xEB, 0x47, (byte) 0xD8, 0x6F, 0x20, (byte) 0x98, (byte) 0xD5, (byte) 0xB7, 0x3F, 0x68, (byte) 0x85, 0x21, (byte) 0xFA, (byte) 0xA1, (byte) 0x83, 0x55, 0x01, 0x08, 0x6B, (byte) 0xEC, 0x03, 0x15, (byte) 0xE4}),
62 | new TestCase("gk3KmMtQnp9Bf3R3z83Qd7WsPcRcPSO", new byte[]{(byte) 0x8A, 0x6E, 0x4C, (byte) 0xA0, (byte) 0xEB, (byte) 0xD2, (byte) 0xA6, (byte) 0xBF, (byte) 0xC3, (byte) 0x87, (byte) 0x94, (byte) 0xF9, (byte) 0xD7, (byte) 0xEE, 0x52, (byte) 0x7F, (byte) 0xD6, 0x2E, 0x45, (byte) 0xE4, (byte) 0xDE, 0x45, 0x44}),
63 | new TestCase("etOoPrjz6LO5t3FfVB5fP2LGrIPzUBC", new byte[]{(byte) 0x82, (byte) 0xF4, 0x2A, 0x46, (byte) 0xD9, 0x75, (byte) 0xF0, (byte) 0xD4, 0x3B, (byte) 0xBF, (byte) 0x91, (byte) 0xE1, 0x5C, 0x3E, (byte) 0xE1, 0x47, (byte) 0x83, 0x48, (byte) 0xB4, (byte) 0xA4, 0x75, 0x58, 0x31}),
64 | new TestCase("nztm8zNUXB.UUj3y5rBkrU/ZqHhYDIS", new byte[]{(byte) 0xA7, 0x5B, (byte) 0xE8, (byte) 0xFB, 0x53, (byte) 0xD6, 0x64, 0x30, 0x16, 0x5A, 0x5E, 0x74, (byte) 0xEE, (byte) 0xD0, (byte) 0xE6, (byte) 0xB5, 0x60, 0x5B, (byte) 0xB0, (byte) 0x98, (byte) 0xDA, 0x14, (byte) 0xA5}),
65 | new TestCase("uxrP8zpLLgWciOUIwobyMvRhEXLiyGS", new byte[]{(byte) 0xC3, 0x3B, 0x51, (byte) 0xFB, 0x5A, (byte) 0xCD, 0x36, 0x26, 0x1E, (byte) 0x91, 0x05, (byte) 0x8A, (byte) 0xCA, (byte) 0xA7, 0x74, 0x3B, 0x14, (byte) 0xE3, 0x19, (byte) 0x93, 0x64, (byte) 0xD0, (byte) 0x85}),
66 | new TestCase("rs4Ic/w8OunqfpYVu4dn4YoWbvyt6ba", new byte[]{(byte) 0xB6, (byte) 0xEE, (byte) 0x8A, 0x78, 0x1C, (byte) 0xBE, 0x43, 0x0A, 0x6C, (byte) 0x86, (byte) 0xB6, (byte) 0x97, (byte) 0xC3, (byte) 0xA7, (byte) 0xE9, (byte) 0xE9, (byte) 0xAA, (byte) 0x98, 0x77, 0x1D, 0x2F, (byte) 0xF1, (byte) 0xD7}),
67 | new TestCase("oVrtbl/4uLejWb.wZDhoH3.IhVzHo2K", new byte[]{(byte) 0xA9, 0x7B, 0x6F, 0x76, 0x70, 0x7A, (byte) 0xC0, (byte) 0xD8, 0x25, 0x61, (byte) 0xD0, 0x32, 0x6C, 0x58, (byte) 0xEA, 0x27, (byte) 0x90, 0x0A, (byte) 0x8D, 0x7D, 0x49, (byte) 0xAB, (byte) 0x83}),
68 | new TestCase("D2iAsq5QhDY3irJSpNuqhiG1MDaJ0C6", new byte[]{0x17, (byte) 0x89, 0x02, (byte) 0xBA, (byte) 0xCE, (byte) 0xD2, (byte) 0x8C, 0x56, (byte) 0xB9, (byte) 0x92, (byte) 0xD2, (byte) 0xD4, (byte) 0xAC, (byte) 0xFC, 0x2C, (byte) 0x8E, 0x42, 0x37, 0x38, 0x57, 0x0B, (byte) 0xD8, 0x4F}),
69 | new TestCase("1y4csFZglKVmKSXsA6K9suWMzoLA66a", new byte[]{(byte) 0xDF, 0x4E, (byte) 0x9E, (byte) 0xB8, 0x76, (byte) 0xE2, (byte) 0x9C, (byte) 0xC5, (byte) 0xE8, 0x31, 0x46, 0x6E, 0x0B, (byte) 0xC3, 0x3F, (byte) 0xBB, 0x06, 0x0E, (byte) 0xD6, (byte) 0xA3, 0x42, (byte) 0xF3, (byte) 0xC7}),
70 | new TestCase("9d96OFn4zlo73U69M4eaq/WXqQKcj8C", new byte[]{(byte) 0xFD, (byte) 0xFF, (byte) 0xFC, 0x40, 0x7A, 0x7A, (byte) 0xD6, 0x7A, (byte) 0xBD, (byte) 0xE5, 0x6F, 0x3F, 0x3B, (byte) 0xA8, 0x1C, (byte) 0xB0, 0x16, 0x19, (byte) 0xB1, 0x23, 0x1E, (byte) 0x97, (byte) 0xE1}),
71 | new TestCase("XP9MELvw..kp0ycxUOlroP7L7Kd0pem", new byte[]{0x65, 0x1F, (byte) 0xCE, 0x18, (byte) 0xDC, 0x72, 0x00, 0x09, (byte) 0xAB, (byte) 0xDB, 0x47, (byte) 0xB3, 0x59, 0x09, (byte) 0xED, (byte) 0xA9, 0x1F, 0x4D, (byte) 0xF4, (byte) 0xC7, (byte) 0xF6, (byte) 0xAE, 0x0A}),
72 | new TestCase("1ynTIxbFvG/5IXCSzNe1VIFlEEgm9cm", new byte[]{(byte) 0xDF, 0x4A, 0x55, 0x2B, 0x37, 0x47, (byte) 0xC4, (byte) 0x80, 0x7B, 0x29, (byte) 0x91, 0x14, (byte) 0xD4, (byte) 0xF8, 0x37, 0x5C, (byte) 0xA1, (byte) 0xE7, 0x18, 0x68, (byte) 0xA8, (byte) 0xFD, (byte) 0xEA}),
73 | new TestCase("p2RefvNV3Sg1D4xqLRVCZxnd1LAq.Tq", new byte[]{(byte) 0xAF, (byte) 0x84, (byte) 0xE0, (byte) 0x87, 0x13, (byte) 0xD7, (byte) 0xE5, 0x48, (byte) 0xB7, 0x17, (byte) 0xAC, (byte) 0xEC, 0x35, 0x35, (byte) 0xC4, 0x6F, 0x3A, 0x5F, (byte) 0xDC, (byte) 0xD0, (byte) 0xAC, 0x01, 0x5B}),
74 | new TestCase("WrcjAiGNm/.ZGAi9Hacv0uPJyFcgplS", new byte[]{0x62, (byte) 0xD7, (byte) 0xA5, 0x0A, 0x42, 0x0F, (byte) 0xA0, 0x10, 0x1B, 0x20, 0x29, 0x3F, 0x25, (byte) 0xC7, (byte) 0xB1, (byte) 0xDB, 0x04, 0x4B, (byte) 0xD0, 0x77, (byte) 0xA2, (byte) 0xAE, 0x75}),
75 | new TestCase("QwEnfhbWteZue4ywQ1O081lIovkxPUC", new byte[]{0x4B, 0x21, (byte) 0xA9, (byte) 0x86, 0x37, 0x58, (byte) 0xBE, 0x06, (byte) 0xF0, (byte) 0x83, (byte) 0xAD, 0x32, 0x4B, 0x74, 0x36, (byte) 0xFB, 0x79, (byte) 0xCA, (byte) 0xAB, 0x19, (byte) 0xB3, 0x45, 0x61}),
76 | new TestCase("zG7NQPAtyuGdrsjiC8v3BAvenhdEdJi", new byte[]{(byte) 0xD4, (byte) 0x8F, 0x4F, 0x49, 0x10, (byte) 0xAF, (byte) 0xD3, 0x02, 0x1F, (byte) 0xB6, (byte) 0xE9, 0x64, 0x13, (byte) 0xEC, 0x79, 0x0C, 0x2C, 0x60, (byte) 0xA6, 0x37, (byte) 0xC6, 0x7C, (byte) 0xB9}),
77 | new TestCase("X5oJYjyuptr0gkCRbE5Kst0JmJ48fLO", new byte[]{0x67, (byte) 0xBA, (byte) 0x8B, 0x6A, 0x5D, 0x30, (byte) 0xAE, (byte) 0xFB, 0x76, (byte) 0x8A, 0x61, 0x13, 0x74, 0x6E, (byte) 0xCC, (byte) 0xBA, (byte) 0xFD, (byte) 0x8B, (byte) 0xA0, (byte) 0xBE, (byte) 0xBE, (byte) 0x84, (byte) 0xD4}),
78 | new TestCase("0W6X11nLm2q.a8Uj2duJn8jiiswNjyG", new byte[]{(byte) 0xD9, (byte) 0x8F, 0x19, (byte) 0xDF, 0x7A, 0x4D, (byte) 0xA3, (byte) 0x8B, 0x00, 0x73, (byte) 0xE5, (byte) 0xA5, (byte) 0xE1, (byte) 0xFC, 0x0B, (byte) 0xA7, (byte) 0xE9, 0x64, (byte) 0x92, (byte) 0xEC, (byte) 0x8F, (byte) 0x97, 0x42}),
79 | new TestCase("JbnQ16saEdRAjVd4pQsyt95cJkV3qRW", new byte[]{0x2D, (byte) 0xDA, 0x52, (byte) 0xDF, (byte) 0xCB, (byte) 0x9C, 0x19, (byte) 0xF4, (byte) 0xC2, (byte) 0x95, 0x77, (byte) 0xFA, (byte) 0xAD, 0x2B, (byte) 0xB4, (byte) 0xBF, (byte) 0xFE, (byte) 0xDE, 0x2E, 0x65, (byte) 0xF9, (byte) 0xB1, 0x36})};
80 |
81 | @Before
82 | public void setUp() {
83 | encoder = new Radix64Encoder.Default();
84 | }
85 |
86 | @Test
87 | @Repeat(3)
88 | public void testEncodeDifferentLengths() {
89 | for (int i = 1; i < 128; i++) {
90 | testSingleEncode(i, encoder);
91 | }
92 | }
93 |
94 | @Test
95 | public void testEncode16Bytes() {
96 | for (int i = 0; i < 256; i++) {
97 | testSingleEncode(16, encoder);
98 | }
99 | }
100 |
101 | @Test
102 | public void testEncode23Bytes() {
103 | for (int i = 0; i < 256; i++) {
104 | testSingleEncode(23, encoder);
105 | }
106 | }
107 |
108 | private static void testSingleEncode(int length, Radix64Encoder encoder) {
109 | byte[] rnd = Bytes.random(length).array();
110 | byte[] encoded = encoder.encode(rnd);
111 | byte[] decoded = encoder.decode(encoded);
112 |
113 | assertArrayEquals(rnd, decoded);
114 | if (length < 1024) {
115 | System.out.println(Bytes.wrap(encoded).encodeUtf8());
116 | } else {
117 | System.out.println(Bytes.wrap(encoded).toString());
118 | }
119 | //System.out.println("new EncodeTestCase(\"" + Bytes.wrap(encoded).encodeUtf8() + "\"," + new JavaByteArrayEncoder().encode(rnd) + "),");
120 | }
121 |
122 |
123 | @Test
124 | public void testEncodeAgainstRefTable() {
125 | for (TestCase encodeTestCase : referenceRadix64Table) {
126 | byte[] encoded = encoder.encode(encodeTestCase.raw);
127 | assertArrayEquals("ref test for '" + encodeTestCase.encoded + "' did not pass - expected " +
128 | Bytes.wrap(encodeTestCase.encoded.getBytes(StandardCharsets.UTF_8)).encodeHex() + " actual " +
129 | Bytes.wrap(encoded).encodeHex(), encodeTestCase.encoded.getBytes(StandardCharsets.UTF_8), encoded);
130 | }
131 | }
132 |
133 | @Test
134 | public void testDecodeAgainstRefTable() {
135 | for (TestCase encodeTestCase : referenceRadix64Table) {
136 | byte[] decoded = encoder.decode(encodeTestCase.encoded.getBytes(StandardCharsets.UTF_8));
137 | assertArrayEquals(encodeTestCase.raw, decoded);
138 | }
139 | }
140 |
141 | @Test
142 | public void testBigBlob() {
143 | testSingleEncode(1024 * 1024 * 10, encoder);
144 | }
145 |
146 | @Test
147 | public void testEmptyDecode() {
148 | assertArrayEquals(new byte[0], encoder.decode(new byte[0]));
149 | }
150 |
151 | @Test
152 | public void testSingleCharDecode() {
153 | assertArrayEquals(new byte[0], encoder.decode("A".getBytes(StandardCharsets.UTF_8)));
154 | }
155 |
156 | @Test
157 | public void testDecodeEmpytString() {
158 | assertArrayEquals(new byte[0], encoder.decode(new byte[0]));
159 | }
160 |
161 | static final class TestCase {
162 | private final String encoded;
163 | private final byte[] raw;
164 |
165 | TestCase(String encoded, byte[] raw) {
166 | this.encoded = encoded;
167 | this.raw = raw;
168 | }
169 | }
170 |
171 | @Test
172 | @Ignore
173 | public void calculate6bitBaseDecodeTable() {
174 | final char[] toBase64 = {
175 | '.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
176 | 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
177 | 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
178 | 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
179 | 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
180 | '6', '7', '8', '9'
181 | };
182 | final int[] fromBase64 = new int[256];
183 | Arrays.fill(fromBase64, -1);
184 | for (int i = 0; i < toBase64.length; i++) {
185 | fromBase64[toBase64[i]] = i;
186 | }
187 | fromBase64['='] = -2;
188 |
189 | for (int i = 0; i < fromBase64.length; i++) {
190 | System.out.print(fromBase64[i] + ", ");
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/misc/BcryptMicroBenchmark.java:
--------------------------------------------------------------------------------
1 | package at.favre.lib.crypto.bcrypt.misc;
2 |
3 | import at.favre.lib.bytes.Bytes;
4 | import at.favre.lib.crypto.bcrypt.BCrypt;
5 |
6 | import java.nio.charset.StandardCharsets;
7 | import java.util.Arrays;
8 | import java.util.Collections;
9 | import java.util.HashMap;
10 | import java.util.List;
11 | import java.util.Map;
12 | import java.util.Random;
13 | import java.util.TreeMap;
14 | import java.util.concurrent.TimeUnit;
15 |
16 | public class BcryptMicroBenchmark {
17 |
18 | private final Random rnd = new Random();
19 | private Map
13 | * $2a$12$US00g/uMhoSBm.HiuieBjeMtoN69SN.GE25fCpldebzkryUyopws6
14 | *
15 | *
19 | * $2a$
20 | *
21 | *
25 | * 12$
26 | *
27 | *
31 | * uMhoSBm.HiuieBjeMtoN69
32 | *
33 | *
37 | * MtoN69SN.GE25fCpldebzkryUyopws6
38 | *
39 | * $
is a simple separator
41 | *
15 | *
20 | *
21 | * see: {@link BCryptFormatter}
22 | * see: modular_crypt_format
23 | */
24 | public interface BCryptParser {
25 |
26 | /**
27 | * Read and parse given bcrypt hash
28 | *
29 | * @param bcryptHash raw UTF-8 encoded byte array of the encoded hash
30 | * @return decoded parts of the bcrypt hash string
31 | * @throws IllegalBCryptFormatException if the format is not correct Modular Crypt Format
32 | */
33 | BCrypt.HashData parse(byte[] bcryptHash) throws IllegalBCryptFormatException;
34 |
35 | /**
36 | * Default implementation
37 | */
38 | final class Default implements BCryptParser {
39 |
40 | private final Charset defaultCharset;
41 | private final Radix64Encoder encoder;
42 |
43 | Default(Radix64Encoder encoder, Charset defaultCharset) {
44 | this.defaultCharset = defaultCharset;
45 | this.encoder = encoder;
46 | }
47 |
48 | @Override
49 | public BCrypt.HashData parse(byte[] bcryptHash) throws IllegalBCryptFormatException {
50 |
51 | if (bcryptHash == null || bcryptHash.length == 0) {
52 | throw new IllegalArgumentException("must provide non-null, non-empty hash");
53 | }
54 |
55 | if (bcryptHash.length < 7) {
56 | throw new IllegalBCryptFormatException("hash prefix meta must be at least 7 bytes long e.g. '$2a$10$'");
57 | }
58 |
59 | ByteBuffer byteBuffer = ByteBuffer.wrap(bcryptHash);
60 |
61 | if (byteBuffer.get() != SEPARATOR) {
62 | throw new IllegalBCryptFormatException("hash must start with " + Bytes.from(SEPARATOR).encodeUtf8());
63 | }
64 |
65 | BCrypt.Version usedVersion = null;
66 | for (BCrypt.Version versionToTest : BCrypt.Version.SUPPORTED_VERSIONS) {
67 | for (int i = 0; i < versionToTest.versionIdentifier.length; i++) {
68 | if (byteBuffer.get() != versionToTest.versionIdentifier[i]) {
69 | byteBuffer.position(byteBuffer.position() - (i + 1));
70 | break;
71 | }
72 |
73 | if (i == versionToTest.versionIdentifier.length - 1) {
74 | usedVersion = versionToTest;
75 | }
76 | }
77 | if (usedVersion != null) break;
78 | }
79 |
80 | if (usedVersion == null) {
81 | throw new IllegalBCryptFormatException("unknown bcrypt version");
82 | }
83 |
84 | if (byteBuffer.get() != SEPARATOR) {
85 | throw new IllegalBCryptFormatException("expected separator " + Bytes.from(SEPARATOR).encodeUtf8() + " after version identifier and before cost factor");
86 | }
87 |
88 | byte[] costBytes = new byte[]{byteBuffer.get(), byteBuffer.get()};
89 |
90 | int parsedCostFactor;
91 | try {
92 | parsedCostFactor = Integer.parseInt(new String(costBytes, defaultCharset));
93 | } catch (NumberFormatException e) {
94 | throw new IllegalBCryptFormatException("cannot parse cost factor '" + new String(costBytes, defaultCharset) + "'");
95 | }
96 |
97 | if (byteBuffer.get() != SEPARATOR) {
98 | throw new IllegalBCryptFormatException("expected separator " + Bytes.from(SEPARATOR).encodeUtf8() + " after cost factor");
99 | }
100 |
101 | if (bcryptHash.length != 7 + 22 + 31) {
102 | throw new IllegalBCryptFormatException("hash expected to be exactly 60 bytes");
103 | }
104 |
105 | byte[] salt = new byte[22];
106 | byte[] hash = new byte[31];
107 | byteBuffer.get(salt);
108 | byteBuffer.get(hash);
109 |
110 | return new BCrypt.HashData(parsedCostFactor, usedVersion, encoder.decode(salt), encoder.decode(hash));
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/IllegalBCryptFormatException.java:
--------------------------------------------------------------------------------
1 | package at.favre.lib.crypto.bcrypt;
2 |
3 | /**
4 | * Exception thrown on parsing if an illegal format has been detected.
5 | *
10 | * Unix stores password hashes computed with crypt in the /etc/passwd file using radix-64 encoding called B64. It uses a
11 | * mostly-alphanumeric set of characters, plus . and /. Its 64-character set is "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".
12 | * Padding is not used.
13 | *
14 | */
15 | public interface Radix64Encoder {
16 |
17 |
18 | /**
19 | * Encode given raw byte array to a Radix64 style, UTF-8 encoded byte array.
20 | *
21 | * @param rawBytes to encode
22 | * @return UTF-8 encoded string representing radix64 encoded data
23 | */
24 | byte[] encode(byte[] rawBytes);
25 |
26 | /**
27 | * From a UTF-8 encoded string representing radix64 encoded data as byte array, decodes the raw bytes from it.
28 | *
29 | * @param utf8EncodedRadix64String from a string get it with "m0CrhHm10qJ3lXRY.5zDGO".getBytes(StandardCharsets.UTF8)
30 | * @return the raw bytes encoded by this utf-8 radix4 string
31 | */
32 | byte[] decode(byte[] utf8EncodedRadix64String);
33 |
34 | /*
35 | * Licensed to the Apache Software Foundation (ASF) under one or more
36 | * contributor license agreements. See the NOTICE file distributed with
37 | * this work for additional information regarding copyright ownership.
38 | * The ASF licenses this file to You under the Apache License, Version 2.0
39 | * (the "License"); you may not use this file except in compliance with
40 | * the License. You may obtain a copy of the License at
41 | *
42 | * http://www.apache.org/licenses/LICENSE-2.0
43 | *
44 | * Unless required by applicable law or agreed to in writing, software
45 | * distributed under the License is distributed on an "AS IS" BASIS,
46 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
47 | * See the License for the specific language governing permissions and
48 | * limitations under the License.
49 | */
50 |
51 | /**
52 | * A mod of Square's Okio Base64 encoder
53 | *