F# version of the Java implementation of BCrypt found at http://anoncvs.mindrot.org/index.cgi/jBCrypt/BCrypt.java?view=markup
The copyright notice relates to the Java version.
BCrypt
// Copyright (c) 2006 Damien Miller <E-mail masqué>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
open System
open System.Collections.Generic
open System.Globalization
open System.Security.Cryptography
open System.Text
type BCrypt private () =
static let GENSALT_DEFAULT_LOG2_ROUNDS = 10
static let BCRYPT_SALT_LEN = 16
// Blowfish parameters.
static let BLOWFISH_NUM_ROUNDS = 16;
// Initial contents of key schedule.
static let P_ORIG =
[|0x243f6a88L; 0x85a308d3L; 0x13198a2eL; 0x03707344L;
0xa4093822L; 0x299f31d0L; 0x082efa98L; 0xec4e6c89L;
0x452821e6L; 0x38d01377L; 0xbe5466cfL; 0x34e90c6cL;
0xc0ac29b7L; 0xc97c50ddL; 0x3f84d5b5L; 0xb5470917L;
0x9216d5d9L; 0x8979fb1bL
|]
static let S_ORIG =
[|0xd1310ba6L; 0x98dfb5acL; 0x2ffd72dbL; 0xd01adfb7L;
0xb8e1afedL; 0x6a267e96L; 0xba7c9045L; 0xf12c7f99L;
0x24a19947L; 0xb3916cf7L; 0x0801f2e2L; 0x858efc16L;
0x636920d8L; 0x71574e69L; 0xa458fea3L; 0xf4933d7eL;
0x0d95748fL; 0x728eb658L; 0x718bcd58L; 0x82154aeeL;
0x7b54a41dL; 0xc25a59b5L; 0x9c30d539L; 0x2af26013L;
0xc5d1b023L; 0x286085f0L; 0xca417918L; 0xb8db38efL;
0x8e79dcb0L; 0x603a180eL; 0x6c9e0e8bL; 0xb01e8a3eL;
0xd71577c1L; 0xbd314b27L; 0x78af2fdaL; 0x55605c60L;
0xe65525f3L; 0xaa55ab94L; 0x57489862L; 0x63e81440L;
0x55ca396aL; 0x2aab10b6L; 0xb4cc5c34L; 0x1141e8ceL;
0xa15486afL; 0x7c72e993L; 0xb3ee1411L; 0x636fbc2aL;
0x2ba9c55dL; 0x741831f6L; 0xce5c3e16L; 0x9b87931eL;
0xafd6ba33L; 0x6c24cf5cL; 0x7a325381L; 0x28958677L;
0x3b8f4898L; 0x6b4bb9afL; 0xc4bfe81bL; 0x66282193L;
0x61d809ccL; 0xfb21a991L; 0x487cac60L; 0x5dec8032L;
0xef845d5dL; 0xe98575b1L; 0xdc262302L; 0xeb651b88L;
0x23893e81L; 0xd396acc5L; 0x0f6d6ff3L; 0x83f44239L;
0x2e0b4482L; 0xa4842004L; 0x69c8f04aL; 0x9e1f9b5eL;
0x21c66842L; 0xf6e96c9aL; 0x670c9c61L; 0xabd388f0L;
0x6a51a0d2L; 0xd8542f68L; 0x960fa728L; 0xab5133a3L;
0x6eef0b6cL; 0x137a3be4L; 0xba3bf050L; 0x7efb2a98L;
0xa1f1651dL; 0x39af0176L; 0x66ca593eL; 0x82430e88L;
0x8cee8619L; 0x456f9fb4L; 0x7d84a5c3L; 0x3b8b5ebeL;
0xe06f75d8L; 0x85c12073L; 0x401a449fL; 0x56c16aa6L;
0x4ed3aa62L; 0x363f7706L; 0x1bfedf72L; 0x429b023dL;
0x37d0d724L; 0xd00a1248L; 0xdb0fead3L; 0x49f1c09bL;
0x075372c9L; 0x80991b7bL; 0x25d479d8L; 0xf6e8def7L;
0xe3fe501aL; 0xb6794c3bL; 0x976ce0bdL; 0x04c006baL;
0xc1a94fb6L; 0x409f60c4L; 0x5e5c9ec2L; 0x196a2463L;
0x68fb6fafL; 0x3e6c53b5L; 0x1339b2ebL; 0x3b52ec6fL;
0x6dfc511fL; 0x9b30952cL; 0xcc814544L; 0xaf5ebd09L;
0xbee3d004L; 0xde334afdL; 0x660f2807L; 0x192e4bb3L;
0xc0cba857L; 0x45c8740fL; 0xd20b5f39L; 0xb9d3fbdbL;
0x5579c0bdL; 0x1a60320aL; 0xd6a100c6L; 0x402c7279L;
0x679f25feL; 0xfb1fa3ccL; 0x8ea5e9f8L; 0xdb3222f8L;
0x3c7516dfL; 0xfd616b15L; 0x2f501ec8L; 0xad0552abL;
0x323db5faL; 0xfd238760L; 0x53317b48L; 0x3e00df82L;
0x9e5c57bbL; 0xca6f8ca0L; 0x1a87562eL; 0xdf1769dbL;
0xd542a8f6L; 0x287effc3L; 0xac6732c6L; 0x8c4f5573L;
0x695b27b0L; 0xbbca58c8L; 0xe1ffa35dL; 0xb8f011a0L;
0x10fa3d98L; 0xfd2183b8L; 0x4afcb56cL; 0x2dd1d35bL;
0x9a53e479L; 0xb6f84565L; 0xd28e49bcL; 0x4bfb9790L;
0xe1ddf2daL; 0xa4cb7e33L; 0x62fb1341L; 0xcee4c6e8L;
0xef20cadaL; 0x36774c01L; 0xd07e9efeL; 0x2bf11fb4L;
0x95dbda4dL; 0xae909198L; 0xeaad8e71L; 0x6b93d5a0L;
0xd08ed1d0L; 0xafc725e0L; 0x8e3c5b2fL; 0x8e7594b7L;
0x8ff6e2fbL; 0xf2122b64L; 0x8888b812L; 0x900df01cL;
0x4fad5ea0L; 0x688fc31cL; 0xd1cff191L; 0xb3a8c1adL;
0x2f2f2218L; 0xbe0e1777L; 0xea752dfeL; 0x8b021fa1L;
0xe5a0cc0fL; 0xb56f74e8L; 0x18acf3d6L; 0xce89e299L;
0xb4a84fe0L; 0xfd13e0b7L; 0x7cc43b81L; 0xd2ada8d9L;
0x165fa266L; 0x80957705L; 0x93cc7314L; 0x211a1477L;
0xe6ad2065L; 0x77b5fa86L; 0xc75442f5L; 0xfb9d35cfL;
0xebcdaf0cL; 0x7b3e89a0L; 0xd6411bd3L; 0xae1e7e49L;
0x00250e2dL; 0x2071b35eL; 0x226800bbL; 0x57b8e0afL;
0x2464369bL; 0xf009b91eL; 0x5563911dL; 0x59dfa6aaL;
0x78c14389L; 0xd95a537fL; 0x207d5ba2L; 0x02e5b9c5L;
0x83260376L; 0x6295cfa9L; 0x11c81968L; 0x4e734a41L;
0xb3472dcaL; 0x7b14a94aL; 0x1b510052L; 0x9a532915L;
0xd60f573fL; 0xbc9bc6e4L; 0x2b60a476L; 0x81e67400L;
0x08ba6fb5L; 0x571be91fL; 0xf296ec6bL; 0x2a0dd915L;
0xb6636521L; 0xe7b9f9b6L; 0xff34052eL; 0xc5855664L;
0x53b02d5dL; 0xa99f8fa1L; 0x08ba4799L; 0x6e85076aL;
0x4b7a70e9L; 0xb5b32944L; 0xdb75092eL; 0xc4192623L;
0xad6ea6b0L; 0x49a7df7dL; 0x9cee60b8L; 0x8fedb266L;
0xecaa8c71L; 0x699a17ffL; 0x5664526cL; 0xc2b19ee1L;
0x193602a5L; 0x75094c29L; 0xa0591340L; 0xe4183a3eL;
0x3f54989aL; 0x5b429d65L; 0x6b8fe4d6L; 0x99f73fd6L;
0xa1d29c07L; 0xefe830f5L; 0x4d2d38e6L; 0xf0255dc1L;
0x4cdd2086L; 0x8470eb26L; 0x6382e9c6L; 0x021ecc5eL;
0x09686b3fL; 0x3ebaefc9L; 0x3c971814L; 0x6b6a70a1L;
0x687f3584L; 0x52a0e286L; 0xb79c5305L; 0xaa500737L;
0x3e07841cL; 0x7fdeae5cL; 0x8e7d44ecL; 0x5716f2b8L;
0xb03ada37L; 0xf0500c0dL; 0xf01c1f04L; 0x0200b3ffL;
0xae0cf51aL; 0x3cb574b2L; 0x25837a58L; 0xdc0921bdL;
0xd19113f9L; 0x7ca92ff6L; 0x94324773L; 0x22f54701L;
0x3ae5e581L; 0x37c2dadcL; 0xc8b57634L; 0x9af3dda7L;
0xa9446146L; 0x0fd0030eL; 0xecc8c73eL; 0xa4751e41L;
0xe238cd99L; 0x3bea0e2fL; 0x3280bba1L; 0x183eb331L;
0x4e548b38L; 0x4f6db908L; 0x6f420d03L; 0xf60a04bfL;
0x2cb81290L; 0x24977c79L; 0x5679b072L; 0xbcaf89afL;
0xde9a771fL; 0xd9930810L; 0xb38bae12L; 0xdccf3f2eL;
0x5512721fL; 0x2e6b7124L; 0x501adde6L; 0x9f84cd87L;
0x7a584718L; 0x7408da17L; 0xbc9f9abcL; 0xe94b7d8cL;
0xec7aec3aL; 0xdb851dfaL; 0x63094366L; 0xc464c3d2L;
0xef1c1847L; 0x3215d908L; 0xdd433b37L; 0x24c2ba16L;
0x12a14d43L; 0x2a65c451L; 0x50940002L; 0x133ae4ddL;
0x71dff89eL; 0x10314e55L; 0x81ac77d6L; 0x5f11199bL;
0x043556f1L; 0xd7a3c76bL; 0x3c11183bL; 0x5924a509L;
0xf28fe6edL; 0x97f1fbfaL; 0x9ebabf2cL; 0x1e153c6eL;
0x86e34570L; 0xeae96fb1L; 0x860e5e0aL; 0x5a3e2ab3L;
0x771fe71cL; 0x4e3d06faL; 0x2965dcb9L; 0x99e71d0fL;
0x803e89d6L; 0x5266c825L; 0x2e4cc978L; 0x9c10b36aL;
0xc6150ebaL; 0x94e2ea78L; 0xa5fc3c53L; 0x1e0a2df4L;
0xf2f74ea7L; 0x361d2b3dL; 0x1939260fL; 0x19c27960L;
0x5223a708L; 0xf71312b6L; 0xebadfe6eL; 0xeac31f66L;
0xe3bc4595L; 0xa67bc883L; 0xb17f37d1L; 0x018cff28L;
0xc332ddefL; 0xbe6c5aa5L; 0x65582185L; 0x68ab9802L;
0xeecea50fL; 0xdb2f953bL; 0x2aef7dadL; 0x5b6e2f84L;
0x1521b628L; 0x29076170L; 0xecdd4775L; 0x619f1510L;
0x13cca830L; 0xeb61bd96L; 0x0334fe1eL; 0xaa0363cfL;
0xb5735c90L; 0x4c70a239L; 0xd59e9e0bL; 0xcbaade14L;
0xeecc86bcL; 0x60622ca7L; 0x9cab5cabL; 0xb2f3846eL;
0x648b1eafL; 0x19bdf0caL; 0xa02369b9L; 0x655abb50L;
0x40685a32L; 0x3c2ab4b3L; 0x319ee9d5L; 0xc021b8f7L;
0x9b540b19L; 0x875fa099L; 0x95f7997eL; 0x623d7da8L;
0xf837889aL; 0x97e32d77L; 0x11ed935fL; 0x16681281L;
0x0e358829L; 0xc7e61fd6L; 0x96dedfa1L; 0x7858ba99L;
0x57f584a5L; 0x1b227263L; 0x9b83c3ffL; 0x1ac24696L;
0xcdb30aebL; 0x532e3054L; 0x8fd948e4L; 0x6dbc3128L;
0x58ebf2efL; 0x34c6ffeaL; 0xfe28ed61L; 0xee7c3c73L;
0x5d4a14d9L; 0xe864b7e3L; 0x42105d14L; 0x203e13e0L;
0x45eee2b6L; 0xa3aaabeaL; 0xdb6c4f15L; 0xfacb4fd0L;
0xc742f442L; 0xef6abbb5L; 0x654f3b1dL; 0x41cd2105L;
0xd81e799eL; 0x86854dc7L; 0xe44b476aL; 0x3d816250L;
0xcf62a1f2L; 0x5b8d2646L; 0xfc8883a0L; 0xc1c7b6a3L;
0x7f1524c3L; 0x69cb7492L; 0x47848a0bL; 0x5692b285L;
0x095bbf00L; 0xad19489dL; 0x1462b174L; 0x23820e00L;
0x58428d2aL; 0x0c55f5eaL; 0x1dadf43eL; 0x233f7061L;
0x3372f092L; 0x8d937e41L; 0xd65fecf1L; 0x6c223bdbL;
0x7cde3759L; 0xcbee7460L; 0x4085f2a7L; 0xce77326eL;
0xa6078084L; 0x19f8509eL; 0xe8efd855L; 0x61d99735L;
0xa969a7aaL; 0xc50c06c2L; 0x5a04abfcL; 0x800bcadcL;
0x9e447a2eL; 0xc3453484L; 0xfdd56705L; 0x0e1e9ec9L;
0xdb73dbd3L; 0x105588cdL; 0x675fda79L; 0xe3674340L;
0xc5c43465L; 0x713e38d8L; 0x3d28f89eL; 0xf16dff20L;
0x153e21e7L; 0x8fb03d4aL; 0xe6e39f2bL; 0xdb83adf7L;
0xe93d5a68L; 0x948140f7L; 0xf64c261cL; 0x94692934L;
0x411520f7L; 0x7602d4f7L; 0xbcf46b2eL; 0xd4a20068L;
0xd4082471L; 0x3320f46aL; 0x43b7d4b7L; 0x500061afL;
0x1e39f62eL; 0x97244546L; 0x14214f74L; 0xbf8b8840L;
0x4d95fc1dL; 0x96b591afL; 0x70f4ddd3L; 0x66a02f45L;
0xbfbc09ecL; 0x03bd9785L; 0x7fac6dd0L; 0x31cb8504L;
0x96eb27b3L; 0x55fd3941L; 0xda2547e6L; 0xabca0a9aL;
0x28507825L; 0x530429f4L; 0x0a2c86daL; 0xe9b66dfbL;
0x68dc1462L; 0xd7486900L; 0x680ec0a4L; 0x27a18deeL;
0x4f3ffea2L; 0xe887ad8cL; 0xb58ce006L; 0x7af4d6b6L;
0xaace1e7cL; 0xd3375fecL; 0xce78a399L; 0x406b2a42L;
0x20fe9e35L; 0xd9f385b9L; 0xee39d7abL; 0x3b124e8bL;
0x1dc9faf7L; 0x4b6d1856L; 0x26a36631L; 0xeae397b2L;
0x3a6efa74L; 0xdd5b4332L; 0x6841e7f7L; 0xca7820fbL;
0xfb0af54eL; 0xd8feb397L; 0x454056acL; 0xba489527L;
0x55533a3aL; 0x20838d87L; 0xfe6ba9b7L; 0xd096954bL;
0x55a867bcL; 0xa1159a58L; 0xcca92963L; 0x99e1db33L;
0xa62a4a56L; 0x3f3125f9L; 0x5ef47e1cL; 0x9029317cL;
0xfdf8e802L; 0x04272f70L; 0x80bb155cL; 0x05282ce3L;
0x95c11548L; 0xe4c66d22L; 0x48c1133fL; 0xc70f86dcL;
0x07f9c9eeL; 0x41041f0fL; 0x404779a4L; 0x5d886e17L;
0x325f51ebL; 0xd59bc0d1L; 0xf2bcc18fL; 0x41113564L;
0x257b7834L; 0x602a9c60L; 0xdff8e8a3L; 0x1f636c1bL;
0x0e12b4c2L; 0x02e1329eL; 0xaf664fd1L; 0xcad18115L;
0x6b2395e0L; 0x333e92e1L; 0x3b240b62L; 0xeebeb922L;
0x85b2a20eL; 0xe6ba0d99L; 0xde720c8cL; 0x2da2f728L;
0xd0127845L; 0x95b794fdL; 0x647d0862L; 0xe7ccf5f0L;
0x5449a36fL; 0x877d48faL; 0xc39dfd27L; 0xf33e8d1eL;
0x0a476341L; 0x992eff74L; 0x3a6f6eabL; 0xf4f8fd37L;
0xa812dc60L; 0xa1ebddf8L; 0x991be14cL; 0xdb6e6b0dL;
0xc67b5510L; 0x6d672c37L; 0x2765d43bL; 0xdcd0e804L;
0xf1290dc7L; 0xcc00ffa3L; 0xb5390f92L; 0x690fed0bL;
0x667b9ffbL; 0xcedb7d9cL; 0xa091cf0bL; 0xd9155ea3L;
0xbb132f88L; 0x515bad24L; 0x7b9479bfL; 0x763bd6ebL;
0x37392eb3L; 0xcc115979L; 0x8026e297L; 0xf42e312dL;
0x6842ada7L; 0xc66a2b3bL; 0x12754cccL; 0x782ef11cL;
0x6a124237L; 0xb79251e7L; 0x06a1bbe6L; 0x4bfb6350L;
0x1a6b1018L; 0x11caedfaL; 0x3d25bdd8L; 0xe2e1c3c9L;
0x44421659L; 0x0a121386L; 0xd90cec6eL; 0xd5abea2aL;
0x64af674eL; 0xda86a85fL; 0xbebfe988L; 0x64e4c3feL;
0x9dbc8057L; 0xf0f7c086L; 0x60787bf8L; 0x6003604dL;
0xd1fd8346L; 0xf6381fb0L; 0x7745ae04L; 0xd736fcccL;
0x83426b33L; 0xf01eab71L; 0xb0804187L; 0x3c005e5fL;
0x77a057beL; 0xbde8ae24L; 0x55464299L; 0xbf582e61L;
0x4e58f48fL; 0xf2ddfda2L; 0xf474ef38L; 0x8789bdc2L;
0x5366f9c3L; 0xc8b38e74L; 0xb475f255L; 0x46fcd9b9L;
0x7aeb2661L; 0x8b1ddf84L; 0x846a0e79L; 0x915f95e2L;
0x466e598eL; 0x20b45770L; 0x8cd55591L; 0xc902de4cL;
0xb90bace1L; 0xbb8205d0L; 0x11a86248L; 0x7574a99eL;
0xb77f19b6L; 0xe0a9dc09L; 0x662d09a1L; 0xc4324633L;
0xe85a1f02L; 0x09f0be8cL; 0x4a99a025L; 0x1d6efe10L;
0x1ab93d1dL; 0x0ba5a4dfL; 0xa186f20fL; 0x2868f169L;
0xdcb7da83L; 0x573906feL; 0xa1e2ce9bL; 0x4fcd7f52L;
0x50115e01L; 0xa70683faL; 0xa002b5c4L; 0x0de6d027L;
0x9af88c27L; 0x773f8641L; 0xc3604c06L; 0x61a806b5L;
0xf0177a28L; 0xc0f586e0L; 0x006058aaL; 0x30dc7d62L;
0x11e69ed7L; 0x2338ea63L; 0x53c2dd94L; 0xc2c21634L;
0xbbcbee56L; 0x90bcb6deL; 0xebfc7da1L; 0xce591d76L;
0x6f05e409L; 0x4b7c0188L; 0x39720a3dL; 0x7c927c24L;
0x86e3725fL; 0x724d9db9L; 0x1ac15bb4L; 0xd39eb8fcL;
0xed545578L; 0x08fca5b5L; 0xd83d7cd3L; 0x4dad0fc4L;
0x1e50ef5eL; 0xb161e6f8L; 0xa28514d9L; 0x6c51133cL;
0x6fd5c7e7L; 0x56e14ec4L; 0x362abfceL; 0xddc6c837L;
0xd79a3234L; 0x92638212L; 0x670efa8eL; 0x406000e0L;
0x3a39ce37L; 0xd3faf5cfL; 0xabc27737L; 0x5ac52d1bL;
0x5cb0679eL; 0x4fa33742L; 0xd3822740L; 0x99bc9bbeL;
0xd5118e9dL; 0xbf0f7315L; 0xd62d1c7eL; 0xc700c47bL;
0xb78c1b6bL; 0x21a19045L; 0xb26eb1beL; 0x6a366eb4L;
0x5748ab2fL; 0xbc946e79L; 0xc6a376d2L; 0x6549c2c8L;
0x530ff8eeL; 0x468dde7dL; 0xd5730a1dL; 0x4cd04dc6L;
0x2939bbdbL; 0xa9ba4650L; 0xac9526e8L; 0xbe5ee304L;
0xa1fad5f0L; 0x6a2d519aL; 0x63ef8ce2L; 0x9a86ee22L;
0xc089c2b8L; 0x43242ef6L; 0xa51e03aaL; 0x9cf2d0a4L;
0x83c061baL; 0x9be96a4dL; 0x8fe51550L; 0xba645bd6L;
0x2826a2f9L; 0xa73a3ae1L; 0x4ba99586L; 0xef5562e9L;
0xc72fefd3L; 0xf752f7daL; 0x3f046f69L; 0x77fa0a59L;
0x80e4a915L; 0x87b08601L; 0x9b09e6adL; 0x3b3ee593L;
0xe990fd5aL; 0x9e34d797L; 0x2cf0b7d9L; 0x022b8b51L;
0x96d5ac3aL; 0x017da67dL; 0xd1cf3ed6L; 0x7c7d2d28L;
0x1f9f25cfL; 0xadf2b89bL; 0x5ad6b472L; 0x5a88f54cL;
0xe029ac71L; 0xe019a5e6L; 0x47b0acfdL; 0xed93fa9bL;
0xe8d3c48dL; 0x283b57ccL; 0xf8d56629L; 0x79132e28L;
0x785f0191L; 0xed756055L; 0xf7960e44L; 0xe3d35e8cL;
0x15056dd4L; 0x88f46dbaL; 0x03a16125L; 0x0564f0bdL;
0xc3eb9e15L; 0x3c9057a2L; 0x97271aecL; 0xa93a072aL;
0x1b3f6d9bL; 0x1e6321f5L; 0xf59c66fbL; 0x26dcf319L;
0x7533d928L; 0xb155fdf5L; 0x03563482L; 0x8aba3cbbL;
0x28517711L; 0xc20ad9f8L; 0xabcc5167L; 0xccad925fL;
0x4de81751L; 0x3830dc8eL; 0x379d5862L; 0x9320f991L;
0xea7a90c2L; 0xfb3e7bceL; 0x5121ce64L; 0x774fbe32L;
0xa8b6e37eL; 0xc3293d46L; 0x48de5369L; 0x6413e680L;
0xa2ae0810L; 0xdd6db224L; 0x69852dfdL; 0x09072166L;
0xb39a460aL; 0x6445c0ddL; 0x586cdecfL; 0x1c20c8aeL;
0x5bbef7ddL; 0x1b588d40L; 0xccd2017fL; 0x6bb4e3bbL;
0xdda26a7eL; 0x3a59ff45L; 0x3e350a44L; 0xbcb4cdd5L;
0x72eacea8L; 0xfa6484bbL; 0x8d6612aeL; 0xbf3c6f47L;
0xd29be463L; 0x542f5d9eL; 0xaec2771bL; 0xf64e6370L;
0x740e0d8dL; 0xe75b1357L; 0xf8721671L; 0xaf537d5dL;
0x4040cb08L; 0x4eb4e2ccL; 0x34d2466aL; 0x0115af84L;
0xe1b00428L; 0x95983a1dL; 0x06b89fb4L; 0xce6ea048L;
0x6f3f3b82L; 0x3520ab82L; 0x011a1d4bL; 0x277227f8L;
0x611560b1L; 0xe7933fdcL; 0xbb3a792bL; 0x344525bdL;
0xa08839e1L; 0x51ce794bL; 0x2f32c9b7L; 0xa01fbac9L;
0xe01cc87eL; 0xbcc7d1f6L; 0xcf0111c3L; 0xa1e8aac7L;
0x1a908749L; 0xd44fbd9aL; 0xd0dadecbL; 0xd50ada38L;
0x0339c32aL; 0xc6913667L; 0x8df9317cL; 0xe0b12b4fL;
0xf79e59b7L; 0x43f5bb3aL; 0xf2d519ffL; 0x27d9459cL;
0xbf97222cL; 0x15e6fc2aL; 0x0f91fc71L; 0x9b941525L;
0xfae59361L; 0xceb69cebL; 0xc2a86459L; 0x12baa8d1L;
0xb6c1075eL; 0xe3056a0cL; 0x10d25065L; 0xcb03a442L;
0xe0ec6e0eL; 0x1698db3bL; 0x4c98a0beL; 0x3278e964L;
0x9f1f9532L; 0xe0d392dfL; 0xd3a0342bL; 0x8971f21eL;
0x1b0a7441L; 0x4ba3348cL; 0xc5be7120L; 0xc37632d8L;
0xdf359f8dL; 0x9b992f2eL; 0xe60b6f47L; 0x0fe3f11dL;
0xe54cda54L; 0x1edad891L; 0xce6279cfL; 0xcd3e7e6fL;
0x1618b166L; 0xfd2c1d05L; 0x848fd2c5L; 0xf6fb2299L;
0xf523f357L; 0xa6327623L; 0x93a83531L; 0x56cccd02L;
0xacf08162L; 0x5a75ebb5L; 0x6e163697L; 0x88d273ccL;
0xde966292L; 0x81b949d0L; 0x4c50901bL; 0x71c65614L;
0xe6c6c7bdL; 0x327a140aL; 0x45e1d006L; 0xc3f27b9aL;
0xc9aa53fdL; 0x62a80f00L; 0xbb25bfe2L; 0x35bdd2f6L;
0x71126905L; 0xb2040222L; 0xb6cbcf7cL; 0xcd769c2bL;
0x53113ec0L; 0x1640e3d3L; 0x38abbd60L; 0x2547adf0L;
0xba38209cL; 0xf746ce76L; 0x77afa1c5L; 0x20756060L;
0x85cbfe4eL; 0x8ae88dd8L; 0x7aaaf9b0L; 0x4cf9aa7eL;
0x1948c25cL; 0x02fb8a8cL; 0x01c36ae4L; 0xd6ebe1f9L;
0x90d4f869L; 0xa65cdea0L; 0x3f09252dL; 0xc208e69fL;
0xb74e6132L; 0xce77e25bL; 0x578fdfe3L; 0x3ac372e6L
|]
// bcrypt IV: "OrpheanBeholderScryDoubt".
static let BF_CRYPT_CIPHERTEXT =
[|0x4f727068L; 0x65616e42L; 0x65686f6cL;
0x64657253L; 0x63727944L; 0x6f756274L
|]
// Table for Base64 encoding.
static let BASE64_CODE =
[|"."; "/"; "A"; "B"; "C"; "D"; "E"; "F"; "G"; "H"; "I"; "J";
"K"; "L"; "M"; "N"; "O"; "P"; "Q"; "R"; "S"; "T"; "U"; "V";
"W"; "X"; "Y"; "Z"; "a"; "b"; "c"; "d"; "e"; "f"; "g"; "h";
"i"; "j"; "k"; "l"; "m"; "n"; "o"; "p"; "q"; "r"; "S"; "t";
"u"; "v"; "w"; "x"; "y"; "z"; "0"; "1"; "2"; "3"; "4"; "5";
"6"; "7"; "8"; "9"
|]
// Table for Base64 decoding.
static let INDEX_64 =
[|-1; -1; -1; -1; -1; -1; -1; -1; -1; -1;
-1; -1; -1; -1; -1; -1; -1; -1; -1; -1;
-1; -1; -1; -1; -1; -1; -1; -1; -1; -1;
-1; -1; -1; -1; -1; -1; -1; -1; -1; -1;
-1; -1; -1; -1; -1; -1; 0; 1; 54; 55;
56; 57; 58; 59; 60; 61; 62; 63; -1; -1;
-1; -1; -1; -1; -1; 2; 3; 4; 5; 6;
7; 8; 9; 10; 11; 12; 13; 14; 15; 16;
17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27;
-1; -1; -1; -1; -1; -1; 28; 29; 30;
31; 32; 33; 34; 35; 36; 37; 38; 39; 40;
41; 42; 43; 44; 45; 46; 47; 48; 49; 50;
51; 52; 53; -1; -1; -1; -1; -1
|]
// Expanded Blowfish key.
static let mutable P : int64[] = [||]
static let mutable S : int64[] = [||]
/// Encode a byte array using bcrypt'S slightly-modified
/// Base64 encoding scheme. Note that this is _not_ compatible
/// with the standard MIME-Base64 encoding.
/// <param name="d">The byte array to encode</param>
/// <param name="length">The number of bytes to encode</param>
/// <returns>A Base64-encoded string</returns>
static let encodeBase64 (d:byte[], length) =
if length <= 0 || length > d.Length then
raise <| new ArgumentOutOfRangeException("length", length, null)
let rs = new StringBuilder(length * 2)
let mutable offset = 0
let mutable c1 = 0uy
let mutable c2 = 0uy
let append (idx:byte) =
Printf.bprintf rs "%s" BASE64_CODE.[int idx]
while offset < length do
c1 <- d.[offset] &&& 0xffuy
offset <- offset + 1
(c1 >>> 2) &&& 0x3fuy |> append
c1 <- (c1 &&& 0x03uy) <<< 4
if offset >= length then
c1 &&& 0x3fuy |> append
else
c2 <- d.[offset] &&& 0xffuy
offset <- offset + 1
c1 <- c1 ||| ((c2 >>> 4) &&& 0x0fuy)
c1 &&& 0x3fuy |> append
c1 <- (0x0fuy &&& c2) <<< 2
if offset >= length then
c1 &&& 0x3fuy |> append
else
c2 <- d.[offset] &&& 0xffuy
offset <- offset + 1
c1 <- c1 ||| ((c2 >>> 6) &&& 0x03uy)
c1 &&& 0x3fuy |> append
c2 &&& 0x3fuy |> append
rs.ToString()
/// Look up the 3 bits base64-encoded by the specified
/// character, range-checking against the conversion
/// table.
/// <param name="c">The Base64-encoded value</param>
/// <returns>The decoded value of <c>x</c></returns>
static let char64 (c:char) =
let i = int c
if i < 0 || i > INDEX_64.Length then -1 else INDEX_64.[i]
/// Decode a string encoded using BCrypt'S Base64 scheme to a
/// byte array. Note that this is _not_ compatible with the standard
/// MIME-Base64 encoding.
/// <param name="S">The string to decode</param>
/// <param name="maximumLength">The maximum number of bytes to decode</param>
/// <returns>An array containing the decoded bytes</returns>
static let decodeBase64(s, maximumLength) =
if maximumLength <= 0 then
raise <| new ArgumentOutOfRangeException("maximumLength", maximumLength, null)
let bytes = new ResizeArray<byte>(min maximumLength (String.length s))
let mutable offset = 0
let mutable length = 0
while offset < s.Length - 1 && length < maximumLength do
let c1 = char64 s.[offset]
offset <- offset + 1
let c2 = char64 s.[offset]
offset <- offset + 1
if c1 <> -1 && c2 <> -1 then
let a = c1 <<< 2
let b = (c2 &&& 0x30) >>> 4
a ||| b |> byte |> bytes.Add
length <- length + 1
if length < maximumLength && offset < s.Length then
let c3 = char64 s.[offset]
offset <- offset + 1
if c3 <> -1 then
let a = (c2 &&& 0x0f) <<< 4
let b = (c3 &&& 0x3c) >>> 2
a ||| b |> byte |> bytes.Add
length <- length + 1
if length < maximumLength && offset < s.Length then
let c4 = char64 s.[offset]
offset <- offset + 1
let a = (c3 &&& 0x03) <<< 6
a ||| c4 |> byte |> bytes.Add
length <- length + 1
Array.of_seq bytes
///
/// Blowfish encipher a single 64-bit block encoded as two 32-bit
/// halves.
///
/// <param name="block">An array containing the two 32-bit half
/// blocks.</param>
/// <param name="offset">The position in the array of the
/// blocks.</param>
static let encipher(block:int64[], offset) =
let mutable n = block.[offset]
let mutable l = block.[offset]
let mutable r = block.[offset + 1]
l <- l ^^^ P.[0]
for i in 0 .. BLOWFISH_NUM_ROUNDS - 2 do
// Feistel substitution on left word
n <- S.[(l >>> 24) &&& 0xffL |> int]
n <- n + S.[0x100 ||| ((l >>> 16) &&& 0xffL |> int)]
n <- n ^^^ S.[0x200 ||| ((l >>> 8) &&& 0xffL |> int)]
n <- n + S.[0x300 ||| (l &&&0xffL |> int)]
r <- r ^^^ (n ^^^ P.[i+1])
// Feistel substitution on right word
// Feistel substitution on left word
n <- S.[(r >>> 24) &&& 0xffL |> int]
n <- n + S.[0x100 ||| ((r >>> 16) &&& 0xffL |> int)]
n <- n ^^^ S.[0x200 ||| ((r >>> 8) &&& 0xffL |> int)]
n <- n + S.[0x300 ||| (r &&& 0xffL |> int)]
l <- l ^^^ (n ^^^ P.[i+2])
block.[offset] <- r ^^^ P.[BLOWFISH_NUM_ROUNDS + 1]
block.[offset + 1] <- l
///
/// Cycically extract a word of key material.
///
/// <param name="data">The string to extract the data
/// from.</param>
/// <param name="offset">The current offset into data.</param>
/// <returns>The next work of material from data.</returns>
static let streamToWord(data:byte[], offset) =
let mutable word = 0L
for i in 0 .. 3 do
word <- (word <<< 8) ||| int64 (data.[!offset] &&& 0xffuy)
offset := (!offset + 1) % data.Length
word
///
/// Initialize the Blowfish key schedule.
///
static let initkey() =
P <- Array.copy P_ORIG
S <- Array.copy S_ORIG
///
/// key the Blowfish cipher.
///
/// <param name="key">An array containing the key.</param>
static let key k =
let lr = [|0L; 0L|]
let plen = P.Length
let offset = ref 0
for i in 0 .. plen - 1 do
P.[i] <- P.[i] ^^^ streamToWord(k, offset)
for i in 0 .. 2 .. plen - 1 do
encipher(lr, 0)
P.[i] <- lr.[0]
P.[i + 1] <- lr.[1]
for i in 0 .. 2 .. S.Length - 1 do
encipher(lr, 0)
S.[i] <- lr.[0]
S.[i + 1] <- lr.[1]
///
/// Perform the "enhanced key schedule" step described by Provos
/// and Mazieres in "A Future-Adaptable Password Scheme"
/// (http://www.openbsd.org/papers/bcrypt-paper.ps).
///
/// <param name="data">Salt information.</param>
/// <param name="key">Password information.</param>
static let ekskey(data, key) =
let lr = [|0L; 0L|]
let plen = P.Length
let slen = S.Length
let keyOffset = ref 0
for i in 0 .. plen - 1 do
P.[i] <- P.[i] ^^^ streamToWord(key, keyOffset)
let dataOffset = ref 0
for i in 0 .. 2 .. plen - 1 do
lr.[0] <- lr.[0] ^^^ streamToWord(data, dataOffset)
lr.[1] <- lr.[1] ^^^ streamToWord(data, dataOffset)
encipher(lr, 0)
P.[i] <- lr.[0]
P.[i + 1] <- lr.[1]
for i in 0 .. 2 .. slen - 1 do
lr.[0] <- lr.[0] ^^^ streamToWord(data, dataOffset)
lr.[1] <- lr.[1] ^^^ streamToWord(data, dataOffset)
encipher(lr, 0)
S.[i] <- lr.[0]
S.[i + 1] <- lr.[1]
///
/// Perform the central password hashing step in the bcrypt
/// scheme.
///
/// <param name="password">The password to hash.</param>
/// <param name="salt">The binary salt to hash with the
/// password.</param>
/// <param name="logRounds">The binary logarithm of the number of
/// rounds of hashing to apply.</param>
/// <returns>An array containing the binary hashed password.</returns>
static let cryptRaw(password, salt:byte[], logRounds) =
let cdata = Array.copy BF_CRYPT_CIPHERTEXT
let clen = cdata.Length
if (salt.Length <> BCRYPT_SALT_LEN) then
raise <| new ArgumentException("Invalid salt length.", "salt")
let logRounds = max logRounds 4 |> min 31
let rounds = 1 <<< logRounds
initkey()
ekskey(salt, password)
for i in 0 .. rounds - 1 do
key(password)
key(salt)
for i in 0 .. 63 do
for j in 0 .. (clen >>> 1) - 1 do
encipher(cdata, j <<< 1)
let ret = Array.zeroCreate (clen * 4)
let j = ref 0
let update (x : int64) =
ret.[!j] <- byte x
incr j
for i in 0 .. clen - 1 do
(cdata.[i] >>> 24) &&& 0xffL |> update
(cdata.[i] >>> 16) &&& 0xffL |> update
(cdata.[i] >>> 8) &&& 0xffL |> update
cdata.[i] &&& 0xffL |> update
ret
///
/// Hash a password using the OpenBSD bcrypt scheme.
///
/// <param name="password">The password to hash.</param>
/// <param name="salt">The salt to hash with (perhaps generated
/// using <c>BCrypt.GenerateSalt</c>).</param>
/// <returns>The hashed password.</returns>
static member HashPassword(password:string, salt:string) =
if (box password = null) then
raise <| new ArgumentNullException("password")
if (box salt = null) then
raise <| new ArgumentNullException("salt")
let mutable minor = char 0
if (salt.[0] <> '$' || salt.[1] <> '2') then
raise <| new ArgumentException("Invalid salt version")
let mutable offset = 0
if (salt.[1] <> '$') then
minor <- salt.[2]
if (minor <> 'a' || salt.[3] <> '$') then
raise <| new ArgumentException("Invalid salt revision")
offset <- 4
else
//printfn "how can we possibly get there ? %s" salt
offset <- 3
// Extract number of rounds
if (salt.[offset + 2] > '$') then
raise <| new ArgumentException("Missing salt rounds")
let rounds = Int32.Parse(salt.[offset..offset+1], NumberFormatInfo.InvariantInfo)
let passwordBytes = Encoding.UTF8.GetBytes(password + (if minor >= 'a' then string '\000' else String.Empty))
let saltBytes = decodeBase64(salt.Substring(offset + 3, 22), BCRYPT_SALT_LEN)
let hashed = cryptRaw(passwordBytes, saltBytes, rounds)
let minor_s = if (minor >= 'a') then string minor else ""
let s1 = encodeBase64(saltBytes, saltBytes.Length)
let s2 = encodeBase64(hashed, (BF_CRYPT_CIPHERTEXT.Length * 4) - 1)
sprintf "$2%s$%02d$%s%s" minor_s rounds s1 s2
///
/// Generate a salt for use with the BCrypt.HashPassword() method.
///
/// <param name="logRounds">The log2 of the number of rounds of
/// hashing to apply. The work factor therefore increases as (2 **
/// logRounds).</param>
/// <returns>An encoded salt value.</returns>
static member GenerateSalt (?logRounds) =
let logRounds = defaultArg logRounds GENSALT_DEFAULT_LOG2_ROUNDS
let randomBytes = Array.zeroCreate BCRYPT_SALT_LEN
RandomNumberGenerator.Create().GetBytes(randomBytes)
let encoded = encodeBase64(randomBytes, randomBytes.Length)
sprintf "$2a$%02d$%s" logRounds encoded
///
/// Check that a plaintext password matches a previously hashed
/// one.
///
/// <param name="plaintext">The plaintext password to verify.</param>
/// <param name="hashed">The previously hashed password.</param>
/// <returns><c>true</c> if the passwords, <c>false</c>
/// otherwise.</returns>
static member CheckPassword(plaintext, hashed) =
compare hashed (BCrypt.HashPassword(plaintext, hashed)) = 0
Example
let p = printfn "%A" let test () = let password = "this is my bloody password" let check = "this is my other bloody password" let hashed = BCrypt.HashPassword(password, BCrypt.GenerateSalt()) if (BCrypt.CheckPassword(password, hashed)) then p "'password' matches" else p "'password' does not match" if (BCrypt.CheckPassword(check, hashed)) then p "'check' matches" else p "'check' does not match"
// Copyright (c) 2007 Derek Slager
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.