packages feed

g3p-hash (empty) → 1.0.0.0

raw patch · 11 files changed

+2502/−0 lines, 11 filesdep +Streamdep +aesondep +basesetup-changed

Dependencies added: Stream, aeson, base, base16, bytestring, containers, g3p-hash, network-byte-order, phkdf, tasty, tasty-hunit, text, tuplehash-utils, vector

Files

+ ChangeLog.md view
@@ -0,0 +1,37 @@+# Revision history for g3p-hash++## Version 1.0.0.0 "Fight like a Pacifist" (2024-03-21)++Developing this project has been a long, strange trip. I've taken clues and+inspiration from so many different places, and the history of my in-the-moment+thinking on these topics is not well-preserved. But I do have a few specific+acknowledgements in mind.++First of all, I'd like to thank Lois T Clark for teaching me how to fight like+a pacifist. I'd like to thank Joe Taylor (K1JT) for the WSPR and WSJT suite of+amateur radio protocols. I'd like to thank the State of Indiana for providing me+with a world-class public education. These lessons directly inspired the goals+and methodologies of this project.++I'd like to thank Mike Dunn and Katalin Bimbo for trying to teach me relevance+logic. While I still have no formal understanding of this topic, thinking about+relevance was absolutely indispensible during this long development cycle.+It helped me see through my own dubious ideas and justifications, it helped me+modulate the goals and methodolgies of this project, and it helped me make+real progress on identifying plausibly-desirable design properties. In short,+it took me to a design that I am so much happier with than I imagined at the+outset of this project.++I'd like to thank David Doiron for introducing me to signals and the theory of+communication via optics, and Yuri Goldfeld and the Indiana Academy, especially+the classes of 1998 and 1999 for so very inadvertently helping me piece together+some of clues that lead me to this result.++I'd like to thank Guo-Qiang Zhang and George Voutsadakis for teaching me about+automata and the theory of computation, and to David Singer for introducing me+to number theory, RSA cryptography, and digital identity.++Finally, I'd like to thank the developers of HMAC, SHA256, PBKDF2, HKDF, and+bcrypt for paving the way, Steve "sc00bz" Thomas and Soatok for sharing their+valuable insights into cryptography with me, and Obsidian Systems for giving me+opportunities to develop my skills in cryptography.
+ LICENSE view
@@ -0,0 +1,202 @@++                                 Apache License+                           Version 2.0, January 2004+                        http://www.apache.org/licenses/++   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION++   1. Definitions.++      "License" shall mean the terms and conditions for use, reproduction,+      and distribution as defined by Sections 1 through 9 of this document.++      "Licensor" shall mean the copyright owner or entity authorized by+      the copyright owner that is granting the License.++      "Legal Entity" shall mean the union of the acting entity and all+      other entities that control, are controlled by, or are under common+      control with that entity. For the purposes of this definition,+      "control" means (i) the power, direct or indirect, to cause the+      direction or management of such entity, whether by contract or+      otherwise, or (ii) ownership of fifty percent (50%) or more of the+      outstanding shares, or (iii) beneficial ownership of such entity.++      "You" (or "Your") shall mean an individual or Legal Entity+      exercising permissions granted by this License.++      "Source" form shall mean the preferred form for making modifications,+      including but not limited to software source code, documentation+      source, and configuration files.++      "Object" form shall mean any form resulting from mechanical+      transformation or translation of a Source form, including but+      not limited to compiled object code, generated documentation,+      and conversions to other media types.++      "Work" shall mean the work of authorship, whether in Source or+      Object form, made available under the License, as indicated by a+      copyright notice that is included in or attached to the work+      (an example is provided in the Appendix below).++      "Derivative Works" shall mean any work, whether in Source or Object+      form, that is based on (or derived from) the Work and for which the+      editorial revisions, annotations, elaborations, or other modifications+      represent, as a whole, an original work of authorship. For the purposes+      of this License, Derivative Works shall not include works that remain+      separable from, or merely link (or bind by name) to the interfaces of,+      the Work and Derivative Works thereof.++      "Contribution" shall mean any work of authorship, including+      the original version of the Work and any modifications or additions+      to that Work or Derivative Works thereof, that is intentionally+      submitted to Licensor for inclusion in the Work by the copyright owner+      or by an individual or Legal Entity authorized to submit on behalf of+      the copyright owner. For the purposes of this definition, "submitted"+      means any form of electronic, verbal, or written communication sent+      to the Licensor or its representatives, including but not limited to+      communication on electronic mailing lists, source code control systems,+      and issue tracking systems that are managed by, or on behalf of, the+      Licensor for the purpose of discussing and improving the Work, but+      excluding communication that is conspicuously marked or otherwise+      designated in writing by the copyright owner as "Not a Contribution."++      "Contributor" shall mean Licensor and any individual or Legal Entity+      on behalf of whom a Contribution has been received by Licensor and+      subsequently incorporated within the Work.++   2. Grant of Copyright License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      copyright license to reproduce, prepare Derivative Works of,+      publicly display, publicly perform, sublicense, and distribute the+      Work and such Derivative Works in Source or Object form.++   3. Grant of Patent License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      (except as stated in this section) patent license to make, have made,+      use, offer to sell, sell, import, and otherwise transfer the Work,+      where such license applies only to those patent claims licensable+      by such Contributor that are necessarily infringed by their+      Contribution(s) alone or by combination of their Contribution(s)+      with the Work to which such Contribution(s) was submitted. If You+      institute patent litigation against any entity (including a+      cross-claim or counterclaim in a lawsuit) alleging that the Work+      or a Contribution incorporated within the Work constitutes direct+      or contributory patent infringement, then any patent licenses+      granted to You under this License for that Work shall terminate+      as of the date such litigation is filed.++   4. Redistribution. You may reproduce and distribute copies of the+      Work or Derivative Works thereof in any medium, with or without+      modifications, and in Source or Object form, provided that You+      meet the following conditions:++      (a) You must give any other recipients of the Work or+          Derivative Works a copy of this License; and++      (b) You must cause any modified files to carry prominent notices+          stating that You changed the files; and++      (c) You must retain, in the Source form of any Derivative Works+          that You distribute, all copyright, patent, trademark, and+          attribution notices from the Source form of the Work,+          excluding those notices that do not pertain to any part of+          the Derivative Works; and++      (d) If the Work includes a "NOTICE" text file as part of its+          distribution, then any Derivative Works that You distribute must+          include a readable copy of the attribution notices contained+          within such NOTICE file, excluding those notices that do not+          pertain to any part of the Derivative Works, in at least one+          of the following places: within a NOTICE text file distributed+          as part of the Derivative Works; within the Source form or+          documentation, if provided along with the Derivative Works; or,+          within a display generated by the Derivative Works, if and+          wherever such third-party notices normally appear. The contents+          of the NOTICE file are for informational purposes only and+          do not modify the License. You may add Your own attribution+          notices within Derivative Works that You distribute, alongside+          or as an addendum to the NOTICE text from the Work, provided+          that such additional attribution notices cannot be construed+          as modifying the License.++      You may add Your own copyright statement to Your modifications and+      may provide additional or different license terms and conditions+      for use, reproduction, or distribution of Your modifications, or+      for any such Derivative Works as a whole, provided Your use,+      reproduction, and distribution of the Work otherwise complies with+      the conditions stated in this License.++   5. Submission of Contributions. Unless You explicitly state otherwise,+      any Contribution intentionally submitted for inclusion in the Work+      by You to the Licensor shall be under the terms and conditions of+      this License, without any additional terms or conditions.+      Notwithstanding the above, nothing herein shall supersede or modify+      the terms of any separate license agreement you may have executed+      with Licensor regarding such Contributions.++   6. Trademarks. This License does not grant permission to use the trade+      names, trademarks, service marks, or product names of the Licensor,+      except as required for reasonable and customary use in describing the+      origin of the Work and reproducing the content of the NOTICE file.++   7. Disclaimer of Warranty. Unless required by applicable law or+      agreed to in writing, Licensor provides the Work (and each+      Contributor provides its Contributions) on an "AS IS" BASIS,+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or+      implied, including, without limitation, any warranties or conditions+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A+      PARTICULAR PURPOSE. You are solely responsible for determining the+      appropriateness of using or redistributing the Work and assume any+      risks associated with Your exercise of permissions under this License.++   8. Limitation of Liability. In no event and under no legal theory,+      whether in tort (including negligence), contract, or otherwise,+      unless required by applicable law (such as deliberate and grossly+      negligent acts) or agreed to in writing, shall any Contributor be+      liable to You for damages, including any direct, indirect, special,+      incidental, or consequential damages of any character arising as a+      result of this License or out of the use or inability to use the+      Work (including but not limited to damages for loss of goodwill,+      work stoppage, computer failure or malfunction, or any and all+      other commercial damages or losses), even if such Contributor+      has been advised of the possibility of such damages.++   9. Accepting Warranty or Additional Liability. While redistributing+      the Work or Derivative Works thereof, You may choose to offer,+      and charge a fee for, acceptance of support, warranty, indemnity,+      or other liability obligations and/or rights consistent with this+      License. However, in accepting such obligations, You may act only+      on Your own behalf and on Your sole responsibility, not on behalf+      of any other Contributor, and only if You agree to indemnify,+      defend, and hold each Contributor harmless for any liability+      incurred by, or claims asserted against, such Contributor by reason+      of your accepting any such warranty or additional liability.++   END OF TERMS AND CONDITIONS++   APPENDIX: How to apply the Apache License to your work.++      To apply the Apache License to your work, attach the following+      boilerplate notice, with the fields enclosed by brackets "[]"+      replaced with your own identifying information. (Don't include+      the brackets!)  The text should be enclosed in the appropriate+      comment syntax for the file format. We also recommend that a+      file or class name and description of purpose be included on the+      same "printed page" as the copyright notice for easier+      identification within third-party archives.++   Copyright [yyyy] [name of copyright owner]++   Licensed under the Apache License, Version 2.0 (the "License");+   you may not use this file except in compliance with the License.+   You may obtain a copy of the License at++       http://www.apache.org/licenses/LICENSE-2.0++   Unless required by applicable law or agreed to in writing, software+   distributed under the License is distributed on an "AS IS" BASIS,+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+   See the License for the specific language governing permissions and+   limitations under the License.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ csrc/bcrypt_raw.c view
@@ -0,0 +1,54 @@+/* lightly modified and aggressively stripped down version of OpenBSD's implementation of BCrypt */++#include <string.h>+#include "g3p_blf.h"+#include "bcrypt_raw.h"++#define BCRYPT_WORDS 6++void+bcrypt_raw ( const char *key, uint32_t keybytes,+             const char *salt, uint32_t saltbytes,+             char output[BCRYPT_RAW_OUTPUT_LENGTH],+             uint32_t rounds) {+  G3P_blf_ctx state;+  uint8_t ciphertext[BCRYPT_RAW_OUTPUT_LENGTH] = "OrpheanBeholderScryDoubt";+	uint32_t cdata[BCRYPT_WORDS];++	/* Setting up S-Boxes and Subkeys */+	G3P_Blowfish_initstate(&state);+	G3P_Blowfish_expandstate(&state,+	    (const uint8_t *) salt, saltbytes,+	    (const uint8_t *) key, keybytes);++	/* Written so that things work when rounds == UINT32_MAX */+	rounds++;+	do {+		G3P_Blowfish_expand0state(&state, (const uint8_t *) key, keybytes);+		G3P_Blowfish_expand0state(&state, (const uint8_t *) salt, saltbytes);+		rounds--;+	} while (rounds != 0);++	uint16_t j = 0;+	for (uint32_t i = 0; i < BCRYPT_WORDS; i++)+		cdata[i] = G3P_Blowfish_stream2word(ciphertext, 4 * BCRYPT_WORDS, &j);++	/* Now do the encryption */+	for (uint32_t k = 0; k < 64; k++)+		G3P_blf_enc(&state, cdata, BCRYPT_WORDS / 2);++	for (uint32_t i = 0; i < BCRYPT_WORDS; i++) {+		ciphertext[4 * i + 3] = cdata[i] & 0xff;+		cdata[i] = cdata[i] >> 8;+		ciphertext[4 * i + 2] = cdata[i] & 0xff;+		cdata[i] = cdata[i] >> 8;+		ciphertext[4 * i + 1] = cdata[i] & 0xff;+		cdata[i] = cdata[i] >> 8;+		ciphertext[4 * i + 0] = cdata[i] & 0xff;+	}++	memcpy(output, ciphertext, BCRYPT_RAW_OUTPUT_LENGTH);+	explicit_bzero(&state, sizeof(state));+	explicit_bzero(ciphertext, sizeof(ciphertext));+        explicit_bzero(cdata, sizeof(cdata));+}
+ csrc/blowfish.c view
@@ -0,0 +1,627 @@+/* g3p-hash global password prehash protocol, code adapted from: */++/* $OpenBSD: blowfish.c,v 1.21 2022/08/28 11:11:25 jsg Exp $ */+/*+ * Blowfish block cipher for OpenBSD+ * Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>+ * All rights reserved.+ *+ * Implementation advice by David Mazieres <dm@lcs.mit.edu>.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ * 3. The name of the author may not be used to endorse or promote products+ *    derived from this software without specific prior written permission.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++/*+ * This code is derived from section 14.3 and the given source+ * in section V of Applied Cryptography, second edition.+ * Blowfish is an unpatented fast block cipher designed by+ * Bruce Schneier.+ */++#include "g3p_blf.h"++/* Function for Feistel Networks */++#define F(s, x) ((((s)[        (((x)>>24)&0xFF)]  \+		 + (s)[0x100 + (((x)>>16)&0xFF)]) \+		 ^ (s)[0x200 + (((x)>> 8)&0xFF)]) \+		 + (s)[0x300 + ( (x)     &0xFF)])++#define BLFRND(s,p,i,j,n) (i ^= F(s,j) ^ (p)[n])++void+G3P_Blowfish_encipher(G3P_blf_ctx *c, uint32_t *xl, uint32_t *xr)+{+	uint32_t Xl;+	uint32_t Xr;+	uint32_t *s = c->S[0];+	uint32_t *p = c->P;++	Xl = *xl;+	Xr = *xr;++	Xl ^= p[0];+	BLFRND(s, p, Xr, Xl, 1); BLFRND(s, p, Xl, Xr, 2);+	BLFRND(s, p, Xr, Xl, 3); BLFRND(s, p, Xl, Xr, 4);+	BLFRND(s, p, Xr, Xl, 5); BLFRND(s, p, Xl, Xr, 6);+	BLFRND(s, p, Xr, Xl, 7); BLFRND(s, p, Xl, Xr, 8);+	BLFRND(s, p, Xr, Xl, 9); BLFRND(s, p, Xl, Xr, 10);+	BLFRND(s, p, Xr, Xl, 11); BLFRND(s, p, Xl, Xr, 12);+	BLFRND(s, p, Xr, Xl, 13); BLFRND(s, p, Xl, Xr, 14);+	BLFRND(s, p, Xr, Xl, 15); BLFRND(s, p, Xl, Xr, 16);++	*xl = Xr ^ p[17];+	*xr = Xl;+};++void+G3P_Blowfish_decipher(G3P_blf_ctx *c, uint32_t *xl, uint32_t *xr)+{+	uint32_t Xl;+	uint32_t Xr;+	uint32_t *s = c->S[0];+	uint32_t *p = c->P;++	Xl = *xl;+	Xr = *xr;++	Xl ^= p[17];+	BLFRND(s, p, Xr, Xl, 16); BLFRND(s, p, Xl, Xr, 15);+	BLFRND(s, p, Xr, Xl, 14); BLFRND(s, p, Xl, Xr, 13);+	BLFRND(s, p, Xr, Xl, 12); BLFRND(s, p, Xl, Xr, 11);+	BLFRND(s, p, Xr, Xl, 10); BLFRND(s, p, Xl, Xr, 9);+	BLFRND(s, p, Xr, Xl, 8); BLFRND(s, p, Xl, Xr, 7);+	BLFRND(s, p, Xr, Xl, 6); BLFRND(s, p, Xl, Xr, 5);+	BLFRND(s, p, Xr, Xl, 4); BLFRND(s, p, Xl, Xr, 3);+	BLFRND(s, p, Xr, Xl, 2); BLFRND(s, p, Xl, Xr, 1);++	*xl = Xr ^ p[0];+	*xr = Xl;+};++void+G3P_Blowfish_initstate(G3P_blf_ctx *c)+{+	/* P-box and S-box tables initialized with digits of Pi */++	static const G3P_blf_ctx initstate =+	{ {+		{+			0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,+			0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,+			0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,+			0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,+			0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,+			0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,+			0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,+			0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,+			0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,+			0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,+			0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,+			0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,+			0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,+			0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,+			0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,+			0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,+			0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,+			0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,+			0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,+			0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,+			0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,+			0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,+			0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,+			0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,+			0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,+			0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,+			0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,+			0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,+			0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,+			0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,+			0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,+			0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,+			0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,+			0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,+			0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,+			0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,+			0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,+			0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,+			0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,+			0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,+			0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,+			0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,+			0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,+			0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,+			0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,+			0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,+			0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,+			0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,+			0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,+			0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,+			0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,+			0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,+			0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,+			0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,+			0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,+			0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,+			0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,+			0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,+			0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,+			0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,+			0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,+			0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,+			0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,+		0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a},+		{+			0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,+			0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,+			0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,+			0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,+			0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,+			0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,+			0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,+			0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,+			0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,+			0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,+			0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,+			0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,+			0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,+			0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,+			0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,+			0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,+			0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,+			0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,+			0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,+			0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,+			0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,+			0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,+			0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,+			0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,+			0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,+			0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,+			0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,+			0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,+			0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,+			0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,+			0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,+			0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,+			0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,+			0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,+			0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,+			0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,+			0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,+			0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,+			0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,+			0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,+			0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,+			0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,+			0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,+			0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,+			0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,+			0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,+			0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,+			0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,+			0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,+			0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,+			0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,+			0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,+			0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,+			0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,+			0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,+			0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,+			0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,+			0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,+			0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,+			0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,+			0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,+			0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,+			0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,+		0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7},+		{+			0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,+			0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,+			0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,+			0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,+			0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,+			0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,+			0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,+			0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,+			0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,+			0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,+			0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,+			0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,+			0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,+			0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,+			0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,+			0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,+			0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,+			0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,+			0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,+			0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,+			0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,+			0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,+			0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,+			0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,+			0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,+			0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,+			0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,+			0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,+			0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,+			0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,+			0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,+			0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,+			0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,+			0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,+			0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,+			0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,+			0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,+			0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,+			0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,+			0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,+			0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,+			0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,+			0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,+			0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,+			0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,+			0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,+			0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,+			0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,+			0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,+			0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,+			0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,+			0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,+			0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,+			0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,+			0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,+			0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,+			0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,+			0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,+			0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,+			0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,+			0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,+			0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,+			0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,+		0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0},+		{+			0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,+			0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,+			0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,+			0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,+			0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,+			0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,+			0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,+			0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,+			0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,+			0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,+			0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,+			0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,+			0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,+			0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,+			0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,+			0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,+			0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,+			0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,+			0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,+			0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,+			0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,+			0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,+			0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,+			0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,+			0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,+			0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,+			0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,+			0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,+			0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,+			0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,+			0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,+			0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,+			0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,+			0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,+			0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,+			0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,+			0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,+			0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,+			0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,+			0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,+			0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,+			0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,+			0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,+			0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,+			0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,+			0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,+			0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,+			0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,+			0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,+			0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,+			0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,+			0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,+			0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,+			0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,+			0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,+			0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,+			0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,+			0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,+			0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,+			0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,+			0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,+			0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,+			0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,+		0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6}+	},+	{+		0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,+		0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,+		0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,+		0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,+		0x9216d5d9, 0x8979fb1b+	} };++	*c = initstate;+};++uint32_t+G3P_Blowfish_stream2word(const uint8_t *data, uint16_t databytes,+    uint16_t *current)+{+	uint8_t i;+	uint16_t j;+	uint32_t temp;++	temp = 0x00000000;+	j = *current;++	for (i = 0; i < 4; i++, j++) {+		if (j >= databytes)+			j = 0;+		temp = (temp << 8) | data[j];+	}++	*current = j;+	return temp;+};++void+G3P_Blowfish_expand0state(G3P_blf_ctx *c, const uint8_t *key, uint16_t keybytes)+{+	uint16_t i;+	uint16_t j;+	uint16_t k;+	uint32_t temp;+	uint32_t datal;+	uint32_t datar;++	j = 0;+	for (i = 0; i < G3P_BLF_N + 2; i++) {+		/* Extract 4 int8 to 1 int32 from keystream */+		temp = G3P_Blowfish_stream2word(key, keybytes, &j);+		c->P[i] = c->P[i] ^ temp;+	}++	j = 0;+	datal = 0x00000000;+	datar = 0x00000000;+	for (i = 0; i < G3P_BLF_N + 2; i += 2) {+		G3P_Blowfish_encipher(c, &datal, &datar);++		c->P[i] = datal;+		c->P[i + 1] = datar;+	}++	for (i = 0; i < 4; i++) {+		for (k = 0; k < 256; k += 2) {+			G3P_Blowfish_encipher(c, &datal, &datar);++			c->S[i][k] = datal;+			c->S[i][k + 1] = datar;+		}+	}+};+++void+G3P_Blowfish_expandstate(G3P_blf_ctx *c, const uint8_t *data, uint16_t databytes,+    const uint8_t *key, uint16_t keybytes)+{+	uint16_t i;+	uint16_t j;+	uint16_t k;+	uint32_t temp;+	uint32_t datal;+	uint32_t datar;++	j = 0;+	for (i = 0; i < G3P_BLF_N + 2; i++) {+		/* Extract 4 int8 to 1 int32 from keystream */+		temp = G3P_Blowfish_stream2word(key, keybytes, &j);+		c->P[i] = c->P[i] ^ temp;+	}++	j = 0;+	datal = 0x00000000;+	datar = 0x00000000;+	for (i = 0; i < G3P_BLF_N + 2; i += 2) {+		datal ^= G3P_Blowfish_stream2word(data, databytes, &j);+		datar ^= G3P_Blowfish_stream2word(data, databytes, &j);+		G3P_Blowfish_encipher(c, &datal, &datar);++		c->P[i] = datal;+		c->P[i + 1] = datar;+	}++	for (i = 0; i < 4; i++) {+		for (k = 0; k < 256; k += 2) {+			datal ^= G3P_Blowfish_stream2word(data, databytes, &j);+			datar ^= G3P_Blowfish_stream2word(data, databytes, &j);+			G3P_Blowfish_encipher(c, &datal, &datar);++			c->S[i][k] = datal;+			c->S[i][k + 1] = datar;+		}+	}++};++void+G3P_blf_key(G3P_blf_ctx *c, const uint8_t *k, uint16_t len)+{+	/* Initialize S-boxes and subkeys with Pi */+	G3P_Blowfish_initstate(c);++	/* Transform S-boxes and subkeys with key */+	G3P_Blowfish_expand0state(c, k, len);+};++void+G3P_blf_enc(G3P_blf_ctx *c, uint32_t *data, uint16_t blocks)+{+	uint32_t *d;+	uint16_t i;++	d = data;+	for (i = 0; i < blocks; i++) {+		G3P_Blowfish_encipher(c, d, d + 1);+		d += 2;+	}+};++void+G3P_blf_dec(G3P_blf_ctx *c, uint32_t *data, uint16_t blocks)+{+	uint32_t *d;+	uint16_t i;++	d = data;+	for (i = 0; i < blocks; i++) {+		G3P_Blowfish_decipher(c, d, d + 1);+		d += 2;+	}+};++void+G3P_blf_ecb_encrypt(G3P_blf_ctx *c, uint8_t *data, uint32_t len)+{+	uint32_t l, r;+	uint32_t i;++	for (i = 0; i < len; i += 8) {+		l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];+		r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7];+		G3P_Blowfish_encipher(c, &l, &r);+		data[0] = l >> 24 & 0xff;+		data[1] = l >> 16 & 0xff;+		data[2] = l >> 8 & 0xff;+		data[3] = l & 0xff;+		data[4] = r >> 24 & 0xff;+		data[5] = r >> 16 & 0xff;+		data[6] = r >> 8 & 0xff;+		data[7] = r & 0xff;+		data += 8;+	}+};++void+G3P_blf_ecb_decrypt(G3P_blf_ctx *c, uint8_t *data, uint32_t len)+{+	uint32_t l, r;+	uint32_t i;++	for (i = 0; i < len; i += 8) {+		l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];+		r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7];+		G3P_Blowfish_decipher(c, &l, &r);+		data[0] = l >> 24 & 0xff;+		data[1] = l >> 16 & 0xff;+		data[2] = l >> 8 & 0xff;+		data[3] = l & 0xff;+		data[4] = r >> 24 & 0xff;+		data[5] = r >> 16 & 0xff;+		data[6] = r >> 8 & 0xff;+		data[7] = r & 0xff;+		data += 8;+	}+};++void+G3P_blf_cbc_encrypt(G3P_blf_ctx *c, uint8_t *iv, uint8_t *data, uint32_t len)+{+	uint32_t l, r;+	uint32_t i, j;++	for (i = 0; i < len; i += 8) {+		for (j = 0; j < 8; j++)+			data[j] ^= iv[j];+		l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];+		r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7];+		G3P_Blowfish_encipher(c, &l, &r);+		data[0] = l >> 24 & 0xff;+		data[1] = l >> 16 & 0xff;+		data[2] = l >> 8 & 0xff;+		data[3] = l & 0xff;+		data[4] = r >> 24 & 0xff;+		data[5] = r >> 16 & 0xff;+		data[6] = r >> 8 & 0xff;+		data[7] = r & 0xff;+		iv = data;+		data += 8;+	}+};++void+G3P_blf_cbc_decrypt(G3P_blf_ctx *c, uint8_t *iva, uint8_t *data, uint32_t len)+{+	uint32_t l, r;+	uint8_t *iv;+	uint32_t i, j;++	iv = data + len - 16;+	data = data + len - 8;+	for (i = len - 8; i >= 8; i -= 8) {+		l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];+		r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7];+		G3P_Blowfish_decipher(c, &l, &r);+		data[0] = l >> 24 & 0xff;+		data[1] = l >> 16 & 0xff;+		data[2] = l >> 8 & 0xff;+		data[3] = l & 0xff;+		data[4] = r >> 24 & 0xff;+		data[5] = r >> 16 & 0xff;+		data[6] = r >> 8 & 0xff;+		data[7] = r & 0xff;+		for (j = 0; j < 8; j++)+			data[j] ^= iv[j];+		iv -= 8;+		data -= 8;+	}+	l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];+	r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7];+	G3P_Blowfish_decipher(c, &l, &r);+	data[0] = l >> 24 & 0xff;+	data[1] = l >> 16 & 0xff;+	data[2] = l >> 8 & 0xff;+	data[3] = l & 0xff;+	data[4] = r >> 24 & 0xff;+	data[5] = r >> 16 & 0xff;+	data[6] = r >> 8 & 0xff;+	data[7] = r & 0xff;+	for (j = 0; j < 8; j++)+		data[j] ^= iva[j];+};
+ g3p-hash.cabal view
@@ -0,0 +1,54 @@+name:                g3p-hash+version:             1.0.0.0+synopsis:            Global Password Prehash Protocol+description:         A password hash and key derivation function that provides+                     embedded attributions in order to support self-documenting+                     deployments.+license:             Apache-2.0+license-file:        LICENSE+author:              Leon P Smith+maintainer:          Auth Global+copyright:           Auth Global+category:            Cryptography+build-type:          Simple+extra-source-files:  ChangeLog.md+                     g3p-test-vectors.json+cabal-version:       >=1.10++library+  exposed-modules:   Crypto.G3P+                     Crypto.G3P.BCrypt++  build-depends:     base < 5+                   , bytestring+                   , network-byte-order+                   , phkdf+                   , Stream+                   , tuplehash-utils+                   , vector+  hs-source-dirs:    lib+  c-sources:         csrc/bcrypt_raw.c+                     csrc/blowfish.c+  include-dirs:      csrc+  ghc-options:       -Wall+  default-language:  Haskell2010++test-suite test+  type:              exitcode-stdio-1.0+  hs-source-dirs:    test+  main-is:           Main.hs++  other-modules:     G3P++  build-depends:     base+                   , aeson >= 2+                   , base16+                   , bytestring+                   , containers+                   , g3p-hash+                   , Stream+                   , tasty+                   , tasty-hunit+                   , text+                   , vector+  default-language:  Haskell2010
+ g3p-test-vectors.json view
@@ -0,0 +1,522 @@+[{+    "name":"prehash first light",+    "args": {+        "username":"Yuri",+        "password":"default remote access code",+        "long-tag":"Please leave the location of America's nuclear wessels after the beep.",+        "domain-tag":"1-800-CALL-SPY",+        "phkdf-rounds":1998,+        "bcrypt-rounds":7+    },+    "results":[+        {"G3Pb1":"d6ad3dd2b82b8f279b39a1c667ed247701a2a93702a37f00e867cc3bb7a3c21123b5545d90ee2e4f196fcc81b74dd8c8ad8ae867c8140b3a90ca34dd7eca01f1"},+        {"args":{"seguid":{"hex":"60473b8010e16d464314a11c2620a8ad99af49ae25474f877e57f6c27c58ca7a3538b58385eabbbbb540a350491291c870a4f12c8569485100da96f4202c3630"}},+         "G3Pb1":"c721fd9ec639fdb104afbd371d80e5253e330c4a66583dff11aef19d02fb84ec6386e5ea2a5899c9155a5dbe59cf21eda2db8a29d79a2199a287436d3c458f64"}+    ]+},{+    "name":"prehash first light, elaborated",+    "args": {+        "username":"Yuri",+        "password":"default remote access code",+        "credentials":["The Indiana Academy for Science, Mathematics, and Humanities"],+        "long-tag":"Please leave the location of America's nuclear wessels after the beep.",+        "domain-tag":"1-800-CALL-SPY",+        "seed-tags":["United States Army Counterintelligence Tip Line"],+        "role":["prankster"],+        "echo-tag":"Star Trek IV: The Voyage Home https://www.youtube.com/watch?v=MdSJFrhb-HM",+        "phkdf-rounds":1998,+        "bcrypt-rounds":7+    },+    "results":[+        {"G3Pb1":"60473b8010e16d464314a11c2620a8ad99af49ae25474f877e57f6c27c58ca7a3538b58385eabbbbb540a350491291c870a4f12c8569485100da96f4202c3630"}+    ]+},{+    "name":"prehash unicode test, inset naval story with a reference",+    "args": {+        "username":"Р-360 «Нептун»",+        "password":"Русский военный корабль, иди нахуй",+        "seed-tags":["Державне Київське конструкторське бюро «Луч»"],+        "echo-tag":"Збройні Cили України «ЗСУ»",+        "domain-tag":"Україна",+        "phkdf-rounds":2022,+        "bcrypt-rounds":8+    },+    "results":[+        {"G3Pb1":"0c06f683f093cb899b4a1e9836fc72812d22941716c05368097b912a328deec579fa21edf7aef04ec808a50bd8757cb734734cc96b00c224abe8642ce94088b3"},+        {"args":{"bcrypt-tag":"Державне Київське конструкторське бюро «Луч»"},+         "G3Pb1":"8660937862a0644410b7dd1a495ddd3dbdd05b3b43669ae2ec8dfe863f8e99d673f61c89486b7855a62936ea8684bb82e429f681abaaf571193dd1275e21481e"+        },+        {"args":{"bcrypt-tag":"«ЗСУ» Збройні Cили України «Луч» Державне Київське конструкторське бюро"},+         "G3Pb1":"d59452447147d268a81249304430d7859e8b5c135b260f9b366db37c38c10aa9c6ab83f05bc529607023a3cae5248e7e6a825e69fa4a62f4a1fede386195cb33"}+    ]+},{+    "name":"prehash unicode test, lightly extended on top",+    "args": {+        "username":"Лев Николаевич Толстой",+        "password":"Книга моя, как я и ожидал, была задержана русской цензурой, но отчасти вследствие моей репутации как писателя, отчасти потому, что она заинтересовала людей, книга эта распространилась в рукописях и литографиях в России и в переводах за границей и вызвала, с одной стороны, от людей, разделяющих мои мысли, ряд сведений о сочинениях, писанных об этом же предмете, с другой стороны, ряд критик на мысли, высказанные в самой книге.",+        "domain-tag":"Царство Божие внутри вас",+        "long-tag":"В другой брошюре, под заглавием: «Сколько нужно людей, чтобы преобразить злодейство в праведность», Адина Баллу говорит: «Один человек не должен убивать. Если он убил, он преступник, он убийца. Два, десять, сто человек, если они делают это, — они убийцы. Но государство или народ может убивать, сколько он хочет, и это не будет убийство, а хорошее, доброе дело. Только собрать побольше народа, и бойня десятков тысяч людей становится невинным делом. Но сколько именно нужно людей для этого? Вот в чем вопрос. Один не может красть, грабить, но целый народ может. Но сколько именно нужно для этого? Почему 1, 10, 100 человек не должны нарушать закона бога, а очень много могут?»",+        "tags":["Мы будем стараться распространять свои взгляды среди всех людей, к каким бы народам, исповеданиям и слоям общества они ни принадлежали. Для этой цели мы будем устраивать публичные чтения, распространять печатные объявления и брошюры, составлять общества и подавать прошения во всякие правительственные учреждения. Вообще будем стремиться всеми доступными для нас средствами к достижению коренного переворота во взглядах, чувствах и действиях нашего общества относительно греховности насилия по отношению к внешним и внутренним врагам. Принимаясь за это великое дело, мы вполне сознаем, что наша искренность может быть подвергнута жестоким испытаниям. Наша задача может навлечь на нас оскорбления, обиды, страдания и даже смерть. Нас ожидает непонимание, ложное толкование и клевета. Против нас должна подняться буря. Гордость и фарисейство, честолюбие и жестокость, правители и власти, — всё это может соединиться, чтоб уничтожить нас. Таким образом поступали с мессией, которому мы стремимся подражать по мере сил своих. Но нас не пугают эти ужасы. Мы надеемся не на людей, а на всемогущего господа. Если мы отказались от человеческого заступничества, что же может поддержать нас, как не одна вера, побеждающая мир? Мы не будем удивляться тем испытаниям, которым мы подвергнемся, а будем радоваться тому, что удостоимся разделить страдания Христа.","Вильям Ллойд Гаррисон"],+        "phkdf-rounds":1894,+        "bcrypt-rounds":66+    },+    "results":[+        {"G3Pb1":"9c08053b7e507a78b571b5b93e1326674540d7106da6408fcafeddcfcdf1ed76177ae099e711fd570d484af8e9fc548ba03d8bb545f449fdf6e86a9243d8c0f7"},+        {"args":{"seguid":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40"}},+         "G3Pb1":"7db250698fe555f6832f33189f97e14ef3c1c2dcada5807119aa7676c24f3fac8d37ecf118059f51614f4d2c7ceecb3224047abec84e0d009f43fbfe984abd28"},+        {"args":{"seguid":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f4041"}},+         "G3Pb1":"4efe93dc422d0b67e9ad5d6d4743bc3f475d805e17f2657d57d57505969d3de56622054aedf3b8fec79e2aa48a2228530b7a11c23a520cc67d70d90721f4fc83"}+    ]+},{+    "name":"xkcd account problems",+    "args":{+        "username":"Cueball",+        "password":{"hex":"7061737300776f7264"},+        "credentials":["input method not included"],+        "domain-tag":"https://xkcd.com/2700/",+        "phkdf-rounds":2022,+        "bcrypt-rounds":16+    },+    "results":{+        "G3Pb1":"c09df48d21af7c83fe124e518ec40e84abada6b176b3ee99b35701fa2c0af99053ea538a8498d30e19837e7f5b4e200d08e11995d58f1c3d5a003b4bf1bc3089"+    }+},{+    "name":"xkcd password strength #1",+    "args":{+        "username":"Anonymous Coward",+        "password":"Tr0ub4dor&3",+        "domain-tag":"https://xkcd.com/936/",+        "phkdf-rounds":2011,+        "bcrypt-rounds":5+    },+    "results":{+        "G3Pb1":"fb404c0874e90da9c9bd56e77000c6717482869c942fbac8feee4cb46c3d7e65371bf00071fd0f4a5707d3e3690acd534614a34176fcff704093a350975fa9ed"+    }+},{+    "name":"xkcd password strength #2",+    "args":{+        "username":"Randall Munroe",+        "password":"correct horse battery staple",+        "tags":[+            "Diceware","https://theworld.com/~reinhold/diceware.html","Arnold Reinhold",+            "https://www.eff.org/dice","Electronic Frontier Foundation"+        ],+        "domain-tag":"xkcd.com/936/",+        "phkdf-rounds":2011,+        "bcrypt-rounds":5+    },+    "results":[+        {"G3Pb1":"290cf026e6860f78b558cd7deb3d01442f58d3fd90742bc4759407d975b0220fb9ab36dd7f8ec48705c3f55017d5ea2ad69dbf018e25243f7787ee8ba5578c55"},+        {"args":{"phkdf-rounds":65536,+                 "bcrypt-rounds":256},+         "G3Pb1":"200210b6ad928c550b842adb12b2b621dba60f7465e456ac0440da9d8fb539e39811ad3a84d010a08cdb3d8d640da5f67f82fb78715cc6beacc149963df5ead3"},+        {"args":{"phkdf-rounds":65535,+                 "bcrypt-rounds":255},+         "G3Pb1":"872420a152eaedbd035e5aa488fa089c5ad59de5d5013dbf045dc72d9c3668e6e354b1dcbcbd6a9521fd9cf071724fabafbf914ed170c9e04becab9b823fd9a7"},+        {"args":{"phkdf-rounds":20240,+                 "bcrypt-rounds":4095},+         "G3Pb1":"650c06d2baa0a6a0e326b02328cf2ef0183cc5ad9211c56f4726e8a43d6f7c692fb1814ca57bf29bcbabc46a4c11d5b1b2af15b8631bfcd2a9bda6b71b540c8d"},+        {"args":{"phkdf-rounds":42024,+                 "bcrypt-rounds":4202},+         "G3Pb1":"7014dad47f0e7f7157d99b39a06553ce392884314bac76929f3f055bb0350b8f1920972aa1b5a2ea200d19f5d08b594cc9d891dd6913764f0bc080e332ac05e7"},+        {"args":{"phkdf-rounds":0,+                 "bcrypt-rounds":4202},+         "G3Pb1":"fdb25ce5a9a782ce6dbdf3c6e00f02c4710b1b11388eff350a4ad032a66e38c64e80fa038cb6f9db3e29f49b169f846a3ea3a9bed3f7ca4ef264403a3f4b0f35"},+        {"args":{"phkdf-rounds":42024,+                 "bcrypt-rounds":0},+         "G3Pb1":"34c1b5a3009161dabb4ef2a526dab2ed9154866b46251cae266e77c8125373af88cbb4afd03a11b526859feff12d3a4d43a375a7962011cc136d9c709c8db3fa"}+    ]+},{+    "name":"xkcd password reuse #1",+    "args":{+        "username":"Imperfect User",+        "password":"Password reuse between multiple deployments of the G3P can be safe(-ish), but adds risk and fragility, often meaningfully so. See https://xkcd.com/792/",+        "domain-tag":"Every authentication service provider inherently has offline cracking attacks against its own password database, even if PAKE is used. When you set a password, you are inherently providing that service with the means to verify your password. If a password is reused and its entropy is low enough to be cracked, access to the right authentication database can be used to bootstrap attacks on other accounts.",+        "phkdf-rounds":2010,+        "bcrypt-rounds":4+    },+    "results":{+        "G3Pb1":"f2efe2dd90f30d7509f427cbb06759e0ceaf08e51e0a33c325263a861b81c803f59374fa151c0af5904700adecf392c3bffb921d0a2b7d7c1052ef27ba9fcacc"+    }+},{+    "name":"xkcd password reuse #2",+    "args":{+        "username":"Imperfect User",+        "password":"Password reuse between multiple deployments of the G3P can be safe(-ish), but adds risk and fragility, often meaningfully so. See https://xkcd.com/792/",+        "domain-tag":"How do you know that your password is really being prehashed on your own device? Even if this does indeed happen, how do you know that it happens every single time? In the context of a webapp, it's relatively easy to covertly inject arbitrary code into selected page loads. Thus it is possible that the first 99 times you log into a webpage, you get a completely legitimate login page that properly prehashes your password in your browser. But on that 100th login, maybe you are instead presented with a webpage that steals your password in addition to its normal login functionality. It would be relatively easy to make such an attack covertly in such a way that is likely to turn surreptitious, leaving no trace that the attack ever happened. This is easiest to accomplish if the attacker is providing the webapp, or has the cooperation of those who do. However this isn't always necessary. At the start of the Arab Spring, Facebook's login page was not delivered over a TLS-authenticated channel. This oversight allowed state actors to steal user's passwords by injecting their own malicious code into Facebook's login page as it traversed Internet Service Providers inside their respective countries. Installed applications can be somewhat more resistant to selective-pageload style attacks, but more generally, malware on endpoints is a significant attack vector to be concerned about. Shoulder-surfing attacks upon password entry are another. Consider these things carefully when making decisions with security implications, such as whether or not to reuse a password and in what context(s).",+        "phkdf-rounds":2010,+        "bcrypt-rounds":4+    },+    "results":{+        "G3Pb1":"19367746312da7c3c938fc4e5264368e206f89812b84d4fa479cc7e7f66f07882d5788c1d309af46b95aa2c433186808b21fee501aa14b35ab2a9c9f5ccb0ed7"+    }+},{+    "name":"trivial inputs",+    "args":{+        "username":"",+        "password":"",+        "credentials":[],+        "seguid":"",+        "domain-tag":"",+        "long-tag":"",+        "echo-tag":"",+        "phkdf-rounds":0,+        "bcrypt-rounds":0,+        "bcrypt-tag":"",+        "bcrypt-salt-tag":"",+        "role":[],+        "seed-tags":[]+    },+    "results":{+        "G3Pb1":"2d0fb502c5e036112ca922e6bb5bd3e41acbd79a3223a42c3a1f08fd92398dfb"+    }+},{+    "name":"password padding",+    "args":{+        "username":"password padding (tests with very long inputs)",+        "password":"back references",+        "domain-tag":"your-domain-name-here.example",+        "long-tag":{"ref":"prehash first light, elaborated","len":5112},+        "phkdf-rounds":255,+        "bcrypt-rounds":15,+        "bcrypt-tag":""+    },+    "results":[+        {"G3Pb1":"1e55f1cb8f9ef582beaa176608ad33e65d56fe56898efe648a0b6993a50f4281"+        },+        {"args":{"long-tag":{"ref":"prehash first light, elaborated","len":5113}},+         "G3Pb1":"1ed3ddaea18428b04ac9b8028e6cae82ef08bdd1fa7c71337aa67e7bcd0a1c5d"+        },+        {"args":{+            "username":{+                "ref":"prehash unicode test, lightly extended on top",+                "len":3045+            }},+         "G3Pb1":"f79d27d104ce609e18609fffd72ca3b15d4b4a354dc6d2c686e6a92079654d59"+        },+        {"args":{+            "username":{+                "ref":"prehash unicode test, lightly extended on top",+                "len":3046+            }},+         "G3Pb1":"92dcd256761116151048601e171a3f9ae9da41d59fd754ee05043f139e0916b0"+        },+        {"args":{+            "username":{+                "ref":"prehash unicode test, lightly extended on top",+                "len":3045,+                "index":1+            },+            "password":{+                "ref":"prehash unicode test, inset naval story with a reference",+                "len":101,+                "index":1+            }},+         "G3Pb1":"2b71c3780105cc1379642f6ceb3e2d788ca96d7d514d8befc79c1bc403b14991"+        },+        {"args":{+            "username":{+                "ref":"prehash unicode test, lightly extended on top",+                "len":3045,+                "index":1+            },+            "password":{+                "ref":"prehash unicode test, inset naval story with a reference",+                "len":102,+                "index":1+            }},+         "G3Pb1":"a8b6f029b25f1f57e3dac00b1f20bac6f1078230dbf0dfc59c4ae2c480965c73"+        },+        {"args":{+            "long-tag":{"ref":"prehash first light","len":5049,"index":1},+            "username":{+                "ref":"prehash unicode test, lightly extended on top",+                "len":3109,+                "index":2+            },+            "password":{+                "ref":"prehash unicode test, inset naval story with a reference",+                "len":164+            }},+         "G3Pb1":"12a5118df13e45434b61d168c3c46947bb00c8f97f14675691eb97c25630c7b5"+        },+        {"args":{+            "long-tag":{"ref":"prehash first light","len":5049,"index":1},+            "username":{+                "ref":"prehash unicode test, lightly extended on top",+                "len":3109,+                "index":2+            },+            "password":{+                "ref":"prehash unicode test, inset naval story with a reference",+                "len":165+            }},+         "G3Pb1":"d833526e21357efc9cdb0fc9a650d7289ae946b5290381aecdda9bd1bdf140ea"+        },+        {"args":{"long-tag":"","bcrypt-tag":{"ref":"xkcd password strength #2","len":112}},+         "G3Pb1":"d31fa18b6bd099775b21efd7ae0f8c9d4090195be2d932ec1c462e996dfc1812"+        },+        {"args":{"long-tag":"","bcrypt-tag":{"ref":"xkcd password strength #2","len":113}},+         "G3Pb1":"7a2e399582edaad660124e2513e3b21a9096e6ec81d920e75a7f40d796fa1c5e"+        }+    ]+},{+    "name":"domain tag tests",+    "args":{+        "username":"domain tag tests",+        "password":"",+        "phkdf-rounds":0,+        "bcrypt-rounds":0+    },+    "results":[+        {"args":{"domain-tag":""},+         "G3Pb1":"76488206562b5b42828ab34e4300701a1c9719dbb886b494f940de50dd9008fc"+        },+        {"args":{"domain-tag":{"hex":"01"}},+         "G3Pb1":"2eed4085cf87decd6dcec271bab1252a3a1688f5e4f1e01a5f51c3a527176745"+        },+        {"args":{"domain-tag":{"hex":"0102"}},+         "G3Pb1":"9ad2af4b7e5ae0d0b672927069fc03384da7c8982f17ec6fdfeccdf11df96f69"+        },+        {"args":{"domain-tag":{"hex":"010203"}},+         "G3Pb1":"c62a1ef5be515385bb7d87cbf6da08685857827e1e4133dd31892d7c987e8c7e"+        },+        {"args":{"domain-tag":{"hex":"01020304"}},+         "G3Pb1":"c5d87a518feb9ae1c30bf39317e98c4ccc9f8c07d3cc78f4328648ec08a2afc3"+        },+        {"args":{"domain-tag":{"hex":"0102030405"}},+         "G3Pb1":"e4b59846812f693d4692e5d28f70fbe2e5b16bc5b44565904f377a8b8b3e1b3c"+        },+        {"args":{"domain-tag":{"hex":"010203040506"}},+         "G3Pb1":"153642e8c40383c92b534e9bf8c2c81da55aef5c22781b97d4ffeb7c90ac8574"+        },+        {"args":{"domain-tag":{"hex":"01020304050607"}},+         "G3Pb1":"1e3f7a8203531082c0b993c957d0c989f457775f8d38007bafbcc2b2607fb49b"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708"}},+         "G3Pb1":"9e7d22be88fdc4c6d6b19668bbb4640f1b2ad783e8f2b2ab8bcc8686dec097ab"+        },+        {"args":{"domain-tag":{"hex":"010203040506070809"}},+         "G3Pb1":"926ea057bf81916ce3b9094df12c76b6e51770c2969f5af15775474c05f674e1"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a"}},+         "G3Pb1":"58aa2ca337800412b99ecd98e4ebbb465a7f28b648ceb06ccca62b37d695833d"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b"}},+         "G3Pb1":"da7a85e33611e860defd768666f4f3798b5fb32ccbcf76847cd9bd0765c74a61"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c"}},+         "G3Pb1":"c23f47fedefdedb0dd68773d4ea08eb12ef1b569c9c115e135a817deb0be67b1"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d"}},+         "G3Pb1":"35a1c4f934d7d74c7747f42212d3905a6e762e8793caef26bf850815a778c224"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e"}},+         "G3Pb1":"7c2a21846426f71eecf4ef86c9dcf7cd4abadb3ba8dd507e271b8936b9946e3a"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f"}},+         "G3Pb1":"fbd455257919f6ce7f375318b2a1acc2cd0277765e79095aefa8752ade1c6fe8"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f10"}},+         "G3Pb1":"d178a9407df73a182091e48d3454d3bba28a923b19f1811f977fb488cca7306e"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f1011"}},+         "G3Pb1":"7ebb194e3b2503c5af7348cdc91209a977cb0fe1082a4a6840af192d5b4a85a9"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112"}},+         "G3Pb1":"8e9926ed8b0220303bba8fa76434477322602de88076349bf903295e27a83eda"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f10111213"}},+         "G3Pb1":"6f7dead0b80281ad271c56f36cde0c16030d46b9c66ab5426a7b4e304f177787"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f1011121314"}},+         "G3Pb1":"95819a7a4aafa7ac45c701edceb7748059d044c069ed165bd5afd4b5287f3f30"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415"}},+         "G3Pb1":"ac6a51331ec00dfb7d33a5e5c28642204b2e752bb26f67b67c6c733c344b1f52"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f10111213141516"}},+         "G3Pb1":"e58c9f5032941e77d843adf3eacc1efcefd9ee02aea0389d4bc06e15c9d0357a"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f1011121314151617"}},+         "G3Pb1":"4a1c4bbcc544a0304d0a160624c2c22498cc249792f398221cf8eb1683f8c616"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718"}},+         "G3Pb1":"99bab93d19ca43963061bf9a90eb13a64728cbd69053ce3d2f3725ff4fc7305a"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f10111213141516171819"}},+         "G3Pb1":"ebc5e55955d0ca977ba081c868316f5dfe6c7659cc352a05024d1911995d248f"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a"}},+         "G3Pb1":"7be0875e6acb133a2a4ff27aacee8ffb8bd57c21c48e711d2ee8e97c35c754e3"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b"}},+         "G3Pb1":"b155e942c56449ae076110008c244b9f6a10deddd70bfa265af7a6e12284da61"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c"}},+         "G3Pb1":"27f3ae6a201d5dbc2a0d9335e424d611b360de377e6a421822f7eae381b0e6ab"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d"}},+         "G3Pb1":"7a5a0c50c494ba373c449c023b6a543e42dfed6473ec65c1d070e89e78f85583"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e"}},+         "G3Pb1":"7200e3f36082f5389e919bf8148ba0e98cdecd1b4eb14a71a9067e583b037bdf"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"}},+         "G3Pb1":"6341748a06146f47c6bd759b615ba84d14b93de448bd773ade4e8f20ebdf6c03"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"}},+         "G3Pb1":"e8ffcab67ede795d547ffa2356daae472d6b266b520531d8d78cacff197e6db4"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021"}},+         "G3Pb1":"3c8268530283984b858331b9ecc3b54985f4760ab2b369ffa1c27924937e168e"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122"}},+         "G3Pb1":"2d0d893ddd1903dc7373206e1475d293d997dd38a9ba946283a4f206ea36d7f8"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223"}},+         "G3Pb1":"651da440e3425f1f2d69f91d5b150c572800b6613734311ea695116209455d1b"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324"}},+         "G3Pb1":"a951bb1592568a0c05bd41d4d0a9712e9f83829d3f800f1ed6bf5d567cc6ce15"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425"}},+         "G3Pb1":"0b4e8f76159bedf3b67541c08c3e11fedaed796d2e6ec3f97b1fc339e727edff"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223242526"}},+         "G3Pb1":"ff9b9a96cefcb04d42af036984daf11b0bb1d519027b32fd92c58a6a8fda2744"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627"}},+         "G3Pb1":"21112353bce77f3f1781b1ecde88cd205d2871ecbe86a80d8706ed327c0f7c67"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728"}},+         "G3Pb1":"17a536829a3c623697c003701cdf0ae16c1faaeec27c095a5edf93f576e37d2a"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223242526272829"}},+         "G3Pb1":"85c5fd1dfecb6e40f81c8066f984135dfd1def147e37a895ee585fff582a479f"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a"}},+         "G3Pb1":"6213f75b424b446a3b367c3400e1dcbd2050e2cdf8eac33745266737a1bae9ef"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b"}},+         "G3Pb1":"8998e460a5c834f5dd76cdcc18e7862d4ba7c06df9602f3066fe75c39e087fe2"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c"}},+         "G3Pb1":"4e0b9607bbdc9c82b1df64c8842d412e82bf4da73e53530d1f8bcadb402d3f8c"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d"}},+         "G3Pb1":"3111d270403f023c0342bcdc77c18eb430f27ccf6fbb3edc34845659534a6296"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e"}},+         "G3Pb1":"0c3270549af4cc19b5ee6ac4f669893c9a7b90f12dde29ba1e696813eedb8d98"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f"}},+         "G3Pb1":"45eceeae98b934fc01e064b8829b153951b53e3c094d3ece3a7e61c91df3e5f3"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30"}},+         "G3Pb1":"e65fea4a66668a6ee7a62f0a589615893eb4d6b202b27259bb107be1c31a7e1a"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031"}},+         "G3Pb1":"5045dd5daacd156ce916394f92b275039bc5ee084a495c006c783030bd860bd4"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132"}},+         "G3Pb1":"944e6ca6dbe90636f57612709d620bcea4f6670c25f329cf859a78a2a3403d63"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233"}},+         "G3Pb1":"27238f189fb2af713a17f85c8db9af5bbc650e19331cfa5851bcbe8a2dc6f94a"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334"}},+         "G3Pb1":"16661a1a66c625310478edea975716c1788a84861d5c3da4b7e5fd5f26b99fbf"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435"}},+         "G3Pb1":"d64e32c42b6bd819ba785e223a9ec2fa7fa564cbcd5831fc462cfe55849a8665"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233343536"}},+         "G3Pb1":"22279ed0604d0bb24584f26b255e50aad4e0fd3d4a9a62943d756fbef07f95ed"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637"}},+         "G3Pb1":"eb92f4bd207342ff2d2b277889e60bd45403ce158abe3c1c15d76c774c8fb267"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738"}},+         "G3Pb1":"aeb1db443177577b9e5b0e32824b21c84b848a21b77821f987b51d49a771a243"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233343536373839"}},+         "G3Pb1":"24d09f57f7d8e4186bd00a7fcd6b7c89ac631997175edc0289f641c2c84507ea"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a"}},+         "G3Pb1":"dc9ae1c446c1a196ca7829f635f3fa9537268f379cbd5eebbc2b723e4793c032"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b"}},+         "G3Pb1":"e37175ff13749abcc954da3a3001c2f93bee48da55e871e93fbc2bdab9de033a"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c"}},+         "G3Pb1":"ee37dcaf0be9e105569b328d6d0f55ba47a169c484be16c3583ca7a0cfc974b7"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d"}},+         "G3Pb1":"55babd9ed724cb1d5587aa7ec0414be1daa10b02625f07cc432d4285954a11b3"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e"}},+         "G3Pb1":"27095bf252c4692d99ee7e6057c2b0f8486c1b6a49eed72ece33477b18b04b63"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e40"}},+         "G3Pb1":"6645b3d1781caebbe51d472f56bc2269d04a234c51f191ffab03df09b57516a5"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e4041"}},+         "G3Pb1":"eb4d51310ba5e1eb684515f6a2b1478517b2ce96f3964dc4dc2c7ed905730135"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e404142"}},+         "G3Pb1":"e276e59c52481296a0c94bac8a278d216b21795066d2e196cd211c17497df7c3"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e40414243"}},+         "G3Pb1":"4604f2bbbb246fba73a5a59b7241cb55ed96375c59be780b1cb8b28fb2c92751"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e4041424344"}},+         "G3Pb1":"fbb15f395fdb8bcc9c041c56240b5ff4c309976ef40bb5561c8d7ab00b8072ef"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e404142434445"}},+         "G3Pb1":"e5fd309b5ab3e5549e8ca27a78746d5961f549afa2881e23bbf9eb5a6ad1089c"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e40414243444546"}},+         "G3Pb1":"1d82491a515e2b48272dac77434d01c645b769955e3756f0ad20e630317e2b03"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e4041424344454647"}},+         "G3Pb1":"14eb800f06fa890aa3bbd73a7a90c5cc6f04b7e49390ffafa1267982c9cf7f01"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e404142434445464748"}},+         "G3Pb1":"a90437e272efb9e725e6bee828fbcf549f5cb3e6721b23fae273d1a82721fda9"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e40414243444546474849"}},+         "G3Pb1":"e3d4058aee112881d4d421fdc7ea61228431ddf8b81470903fb6d60833c78078"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e404142434445464748494a"}},+         "G3Pb1":"bb7db09b2fe46d9ba61fb0cca6bec2a99130e24dfaddf04c2af7142c86b080e3"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e404142434445464748494a4b"}},+         "G3Pb1":"92ce36869af780f1540bad4b878d4ee3274172431e18a7a8e0a154673e7c132d"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e404142434445464748494a4b4c"}},+         "G3Pb1":"fb96277684531ea48fa7fd04ca1a1a5dcb16b93afd9b96b12665cbb60373d4ac"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e404142434445464748494a4b4c4d"}},+         "G3Pb1":"7854170c740d1d6878c81284104c40249ea9559403aaea3e33ba4a72c0ee1994"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e404142434445464748494a4b4c4d4e"}},+         "G3Pb1":"748f50f1d7834f9d6d604c8c155c5530b019b66f532039788cc06383fa5ae743"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e404142434445464748494a4b4c4d4e4f"}},+         "G3Pb1":"19eaf5992eb7d6a40cc7d1a9e7599257b65606570f43f83a3ea7ecffb53f371c"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e404142434445464748494a4b4c4d4e4f50"}},+         "G3Pb1":"8af1badbb6cfe8b76f9ab6fcfd66705cd0f8d0c4d59d4af30968b6c51d7b469a"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e404142434445464748494a4b4c4d4e4f5051"}},+         "G3Pb1":"7bfc6f034ba1ce335c4ac37646d03ded6af5c3959df59de0aed58777341783d9"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e404142434445464748494a4b4c4d4e4f505152"}},+         "G3Pb1":"db9b7aebe2a690bd64bbd14324f344918d5f6749f2bebac60d07a4540c320fda"+        },+        {"args":{"domain-tag":{"hex":"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e404142434445464748494a4b4c4d4e4f50515253"}},+         "G3Pb1":"568fb7b27fea31538e5a77d95adc58fe085e6e21f878958e040fe11358805fd2"+        }+    ]+}]
+ lib/Crypto/G3P.hs view
@@ -0,0 +1,551 @@+{-# LANGUAGE OverloadedStrings #-}++{- |++The [Global Password Prehash Protocol (G3P)](https://github.com/auth-global/self-documenting-cryptography/blob/prerelease/design-documents/g3p.md)+is a slow, attribution-armored password hash function and cryptographic key+derivation function. It supports [self-documenting deployments](https://www.cut-the-knot.org/Curriculum/Algebra/SelfDescriptive.shtml)+whose password hashes are /traceable/ or /useless/ after they have been /stolen/.+This secondary security goal seeks to use [/cryptoacoustics/](https://github.com/auth-global/self-documenting-cryptography/)+to provide [/embedded attributions/](https://joeyh.name/blog/entry/attribution_armored_code/)+that are as difficult as possible for an adversarial implementation to remove.++The G3P revisits the role of cryptographic salt, splitting the salt into the+cartesian product of the /seguid/, /username/, and /tag/ parameters. Any+parameter with "tag" as part of the name is an embedded attribution to anybody+providing the inputs to the /username/ or /password/ parameters. Tags are+themselves directly self-documenting embedded attributions, in the sense that+one cannot easily or efficiently replace the tag with anything else without+losing the ability to compute the correct hash function.++The /seguid/ corresponds to the key used for every call to HMAC-SHA256, right up+until final output expansion. In this way the G3P mimicks the construction of+HKDF, with the seguid corresponding to HKDF's /salt/ parameter. The G3P also+mimicks PBKDF2 used in an alternate mode of operation.++The seguid can be trivially replaced with a /precomputed HMAC key/, thus the+seguid is not a direct tag. However this precomputed key is a cryptographic+hash of the seguid, and for this reason the seguid is capable of serving as+an /indirect/ tag, which the Seguid Protocol is designed to utilize via+Self-Documenting Globally Unique Identifiers (seguids).++It is strongly recommend a deployment identify itself with a single 64-byte+(512-bit) seguid, and the deployment's choice of plaintext messages to be+delivered via tags. These salts can be constants across the entire deployment,+as the username is intended to be used as the final bit of salt within a+deployment.++In a traditional password hash function, the salt is a random bytestring+typically between 8 and 32 bytes long. One of its primary purposes is to+identifiy a unique hash function so that one cannot attempt to crack multiple+password hashes with a single key-stretching computation. Oftentimes this+is implemented by storing a salt per user.++However, in the context of a client-side prehash, storing a salt per user has+the potential to leak whether or not an account exists, or if a password has+changed. The G3P has the option to eliminate these complications, because+it is safe to use a plain username as the salt, in addition to the+deployment-identifying seguid and tags.++On the other hand, if one is aware of the potential issues surrounding the+implementation of a random per-user salt in a client-side hashing context, and+is willing to mitigate or live with them, then there are potential advantages+to using a random salt as the input to the G3P's @username@ parameter instead.++All parameter names are suggestive, not prescriptive. Usage is ultimately+defined by the deployment.++When somebody is guessing a username, they must also know (or guess) the+password. However, the username need not be revealed to somebody who is guessing+the password, as the raw username can always be replaced by a precomputed hash.+If this intentional feature is not desired, a deployment might choose to swap+the username and password, as these inputs are otherwise functionally identical.++The usage and interpretation(s) of any given parameter is always defined by the+deployment, and is never defined by offical G3P documentation or specifications.++The G3P always has room for more salt. It doesn't really make sense to inject+more than 256 bits of entropy into the username parameter, because when the G3P+is partially applied to a constant username, the raw input can be replaced with+a SHA256 state. This is not true of any of the tags: it doesn't matter how long+it is, the whole tag must be present for the hash computation to be correct.++Every parameter with the word _tag_ in its name exhibits this property.+Theoretically, one could specify a G3P-based hash function that requires+terabytes of salt to be hashed billions of times over. However it is unclear+what purpose such an impractical specification might serve.++This initial variant of the G3P employs a combination of PHKDF and bcrypt.+PHKDF serves as the primary cryptoacoustic component, and bcrypt serves as the+primary key-stretching component of the G3P. Both are secondarily used in the+alternate role as well, with the PHKDF adding a tiny bit of key stretching and+bcrypt providing significant additional cryptoacoustic plaintext repetitions.++1.  Every bit of every parameter matters. Every boundary between parameters+    matters. The presence and position of every null byte and every empty+    string matters. There aren't supposed to be any trivial collisions, the+    only exception being null-byte extension collisions on the seguid, which+    serves as an HMAC-SHA256 key.++2.  Except for the tweaks, any change to any parameter requires restarting the+    PHKDF key-stretching computation from somewhere in the very first call to+    HMAC.++3.  All input arguments are hardened against length-related timing side+    channels in various different ways.++    At one extreme, the username, password, and long tag have the most+    aggressive length hardening in the conventional sense, exhibiting no timing+    side channels except on multi-kilobyte inputs, after which the timing+    impacts are minimized.++    At another extreme, the domain tag exhibits severe yet predictable+    timing side channels transitioning from 19 to 20 bytes and every 64+    bytes thereafter.  However, the domain tag is otherwise free of+    timing-based side channels, so it too is hardened in its own way.++The design I converged upon employs fairly complicated data encoding+procedures. Unfortunately, this provides a fair bit of surface area for subtly+wrong implementations that work most of the time, but will return garbage on+certain lengths of inputs. I hope that this will eventually be remediated with+a more comprehensive suite of test vectors.++Note that the username, password, long-tag, and credentials vector are all+/horn-loaded inputs/ in the sense that they are consumed a constant number of+times near the beginning of the hashing protocol, and after each PHKDF+round, the hash with the least key-stretching applied is discarded.++This implies that particularly paranoid password-handling implementations can+eliminate the password from memory even before key-stretching is complete.+Additionally, assuming all the sensitive secrets are contained in horn-loaded+parameters, this implies the key-stretching computation can be relocated at+nearly any time with full credit for any key-stretching already performed.++One of the associated costs is that collisions on horn-loaded inputs can be+found over the entire G3P by "only" colliding the first call to HMAC-SHA256,+/G3Pb1 alfa/. If it were trivial to produce collisions on HMAC-SHA256, this+would very likely make collisions on the horn-loaded inputs trivial. However+such an attack would be unlikely to be able to immediately produce collisions+that vary any of the other inputs. This is because all the other inputs are+repeated elsewhere in the protocol, thus colliding /G3Pb1 alfa/ isn't enough+to collide the final output of the G3P.++This "cost" seems acceptable in the context of password-based authentication+flows, where collision resistance and second preimage resistance are not+directly relevant. What is crucially important is preimage resistance and+maximizing the cost of parallelizing multiple key-stretching computations while+minimizing the latency of a single key-stretching computation.++-}++module Crypto.G3P where++import           Control.Exception(assert)+import           Data.ByteString (ByteString)+import qualified Data.ByteString as B+import           Data.Function((&))+import           Data.Word+import           Data.Stream (Stream(..))+import           Data.Vector (Vector)+import qualified Data.Vector as V+import           Network.ByteOrder (word32)++import           Crypto.Encoding.PHKDF+                    ( add64WhileLt+                    , cycleByteString+                    , cycleByteStringWithNull+                    , usernamePadding+                    , passwordPaddingBytes+                    , credentialsPadding+                    )+import           Crypto.Encoding.SHA3.TupleHash+import           Crypto.PHKDF.Primitives+import           Crypto.PHKDF.Primitives.Assert+import           Crypto.G3P.BCrypt+++-- | These input parameters are grouped together because the envisioned use+--   for them is that they are constants (or near-constants) specified by+--   a deployment. User-supplied inputs would typically not go here.  In this+--   role, all these parameters function as salt.+--+--   The seguid parameter acts as a deployment-wide salt. Cryptographically+--   speaking, the most important thing a deployment can do is specify a+--   constant seguid.  It is highly recommended that the seguid input be a+--   genuine Self-Documenting Globally Unique Identifier attesting to the+--   parameters, purposes, and public playbook of the protocol for y'all+--   to follow to use the deployment to spec.+--+--   The remaining string parameters are all directly-documenting, embedded+--   attributions. A deployment can use these tags to encode a message into the+--   password hash function so that it must be known to whomever can compute it.+--   There are a variety of different parameters because there are different+--   lengths of messages that can be expressed for free, and there are different+--   incremental costs for exceeding that limit.+--+--   It is particularly important to include some kind of actionable message+--   in the @domainTag@, @longTag@, @bcryptTag@, and @bcryptSaltTag@+--   parameters. Specifying an empty string in any of these parameters+--   means that a significant quantity of cryptoacoustic messaging space will+--   be filled with silence.+--+--   Especially useful messages include URIs, legal names, and domain names.++data G3PInputBlock = G3PInputBlock+  { g3pInputBlock_seguid :: !ByteString+    -- ^ HMAC-SHA256 key, usable as a high-repetition indirect tag via+    --   self-documenting globally unique identifiers (seguids).+  , g3pInputBlock_domainTag :: !ByteString+    -- ^ plaintext tag with one repetition per PHKDF round. 0-19 bytes are+    --   free, 20-83 bytes cost a additional sha256 block /per PHKDF round/,+    --   with every 64 bytes thereafter incurring a similar cost.+    --+    --   Tags up to 83 or maybe even 147 bytes long might be reasonable.+    --   In the case of longer domain tags, it is strategically advantageous+    --   to ensure that the first 32 bytes are highly actionable.+    --+    --   This parameter provides [domain separation](https://csrc.nist.gov/glossary/term/domain_separation).+    --   A suggested value is a ICANN domain name controlled by the deployment.+    --   The name is also a bit of an homage to the "realm" parameter of HTTP+    --   basic authentication, which in part inspired it.+  , g3pInputBlock_longTag :: !ByteString+    -- ^ plaintext tag with 1x repetition, then cycled for roughly+    --   8 kilobytes.  Constant time on inputs up to nearly 5 kilobytes.+    --+    --   Overages incur one sha256 block per 64 bytes.+  , g3pInputBlock_tags :: !(Vector ByteString)+    -- ^ plaintext tags with 3x repetition. Constant-time on 0-63 encoded bytes,+    --   which includes the length encoding of each string. Thus 60 of those+    --   free bytes are usable if the tags vector is a single string, or less if+    --   it contains two or more strings.+    --+    --   Overages incur three sha256 blocks per 64 bytes.+    --+    --   This parameter is notable because it is the least expensive purely+    --   auxiliary input that is not horn-loaded. Thus if you want a very long+    --   salt input that provides a bit of extra collision resistance, (because+    --   the collision resistance of HMAC-SHA256 isn't enough?!) this would+    --   be a logical candidate input location to consider.+  , g3pInputBlock_phkdfRounds :: !Word32+    -- ^ How expensive will the PHKDF component be? An optimal implementation+    --   computes exactly three SHA256 blocks per round if the domain tag is+    --   19 bytes or less, plus a reasonably large but constant number of+    --   additional blocks. I recommend at least 20,000 rounds, if not 40,000.+    --   You might consider adjusting that recommendation downward in the+    --   case of domain tags that exceed 19 bytes in length: 15,000 rounds+    --   of PHKDF with a domain tag that is 83 bytes long should cost about+    --   the same number of SHA256 blocks as 20,000 rounds of PHKDF with a+    --  domain tag that is 19 bytes long.+  , g3pInputBlock_bcryptRounds :: !Word32+    -- ^ How expensive will the bcrypt component be? 4000 rounds recommended,+    --   give or take a factor of 2 or so. Each bcrypt round is approximately+    --   as time consuming as 60 PHKDF rounds. Using the recommendation, the+    --   cost should be dominated by bcrypt.+  , g3pInputBlock_bcryptTag :: !ByteString+    -- ^ Repeated once or twice per bcrypt round, plus once in PHKDF.+    --   This tag has exactly two full repetitions per bcrypt round+    --   when the tag is up to 56 bytes long. Above 56 bytes,+    --   this tag is cyclically extended to 112 bytes and then split+    --   into two strings of 56 bytes, each repeated once. Hashed+    --   once in PHKDF to avoid any truncation gotchas, and to force+    --   recomputation of PHKDF if this tag is varied.+    --+    --   0-112 bytes can be handled in a constant number of cryptographic+    --   operations.  Overages incur a cost of one SHA-256 block per+    --   64 bytes.+  } deriving (Eq, Ord, Show)++-- | The username and password are grouped together because they are normally+--   expected to be supplied by users or other observers of a deployment.+--+--   Furthermore, the credentials vector is here because it is an ideal+--   location to include other user input. For example, one could implement+--   a Two-Secret Key Derivation (2SKD) scheme analogous to 1Password's.+--+--   A deployment can also specify additional constant tags as part of the+--   credentials vector.  As the plaintext of these tags is only ever hashed+--   into the output a single time, this alongside the bcrypt tags are the+--   least expensive pay-as-you-go options for plaintext tagging.+--+--   Note that the username and password are subjected to additional length+--   hardening. The G3P operates in a constant number of SHA256 blocks so long+--   as the combined length of the username and password is less than about+--   3 KiB,  or the combined length of the username, password, and long tag is+--   less than about 8 KiB. The actual numbers are somewhat less in both cases,+--   but this is a reasonable approximation. Note that the bcrypt tags can+--   subtract up to 114 bytes from the 8 KiB total, and don't effect the 3 KiB+--   total.+--+--   In the case of all of the inputs in this record, longer values incur one+--   SHA256 block per 64 bytes.++data G3PInputArgs = G3PInputArgs+  { g3pInputArgs_username :: !ByteString+  -- ^ constant time on 0-101 bytes, or if any of the other conditions are met.+  , g3pInputArgs_password :: !ByteString+  -- ^ constant time on 0-101 bytes, or if any of the other conditions are met.+  , g3pInputArgs_credentials :: !(Vector ByteString)+  -- ^ constant time on 0-90 encoded bytes. This includes a variable-length+  -- field that encodes the bit length of each string; this field itself+  -- requires two or more bytes per string.+  } deriving (Eq, Ord, Show)++-- | These parameters are used to tweak the final output, without redoing any+--   expensive key stretching.  A possible use case is including a high entropy+--   secret in the role itself that isn't available until after a successful+--   stage of authentication.+--+--   Since these parameters are processed in a context that could conceivably be+--   performance sensitive, we don't apply any length padding or side-channel+--   hardening.  Instead we opt for maximizing free tagging space.  Thus we+--   want to avoid incurring additional SHA256 block computations, one of the+--   favorite techniques employed by the key-stretching phase of the G3P+--   to harden against timing side-channels.+--+--   A deployment could conceivably harden this expansion phase against timing+--   side channels themselves, if the were sufficiently inclined. There are+--   several techniques. For starters, a deployment could ensure that these+--   parameters themselves are constant-length.  Alternatively, a deployment+--   could specify an additional variable-length string in the role vector,+--   used to control the ending position relative to the SHA256 buffer.++newtype G3PInputRole = G3PInputRole+  { g3pInputRole_roleTags :: Vector ByteString+  -- ^ This is the least expensive parameter that will vary the secret HMAC+  --   key used to generate the final output. Very much analogous to HKDF's+  --   initial keying material (ikm) parameter. This is the recommended last+  --   call for mixing additional secrets into the output.+  } deriving (Eq, Ord, Show)++newtype G3PInputEcho = G3PInputEcho+  { g3pInputEcho_echoTag :: ByteString+  -- ^ the absolute least expensive parameter to vary, if your implementation+  --   supports it. Very much analogous to HKDF's info parameter. 0-19 bytes+  --   are free.  This incurs a cost of one SHA-256 block per output block at+  --   20 bytes and every 64 bytes thereafter.+  } deriving (Eq, Ord, Show)++-- | A plain-old-data explicit representation of the intermediate 'g3pHash'+--   computation after the 'G3PInputBlock' and 'G3PInputArgs' have been+--   processed and key stretching has been completed, but before the tweaks+--   have been applied and the final output generated.+--+--   If you ever need to serialize or persist a seed, you probably want this.+--+--   Intended to be generated by 'g3pHash_seedInit' and consumed+--   by 'g3pHash_seedFinalize'.++data G3PSeed = G3PSeed+  { g3pSeed_seguid :: !ByteString  -- ^ filled in for convenience by 'g3pHash_seedInit', but ignored by 'g3p_seedFinalize'+  , g3pSeed_seguidKey :: !HmacKey+  , g3pSeed_domainTag :: !ByteString+  , g3pSeed_secret :: !ByteString+  } deriving (Eq)++data G3PKey = G3PKey+  { g3pKey_secret :: !ByteString+  , g3pKey_secretKey :: HmacKey+  , g3pKey_domainTag :: !ByteString+  } deriving (Eq)++data G3PGen = G3PGen+  { g3pGen_secret :: !ByteString+  , g3pGen_phkdfGen :: !PhkdfGen+  }++-- | The Global Password Prehash Protocol (G3P). Note that this function is very+--   intentionally implemented in such a way that the following idiom is+--   efficient.  It performs the expensive key stretching phase only once.+--+-- @+--  let mySeed = g3pHash block args+--      myKey0 = mySeed myRole0+--      myKey1 = mySeed myRole1+--   in [ myKey0 myEcho , myKey0 altEcho, myKey1 myEcho, myKey1 altEcho ]+-- @+--+--   This expression also only performs 2 output key computations, though this+--   is very fast compared to the stretching applied to the seed. It's still+--   slower than varying only the echo tag. Thus we end up with four+--   cryptographically independent bytestreams.+--+--   In the case that you want or need to persist or serialize the intermediate+--   seed, or change the seguid or domain tag before final output expansion,+--   then the plain-old-datatype 'G3PSeed' and its companion functions+--   'g3pHash_seedInit' and 'g3pHash_seedFinalize' are needed.++g3pHash :: G3PInputBlock -> G3PInputArgs -> G3PInputRole -> G3PInputEcho -> Stream ByteString+g3pHash block args role echoTag =+    g3pHash_seedInit block args &+    g3pHash_keyInit role &+    g3pHash_finalizeGen echoTag &+    g3pGen_finalizeStream++-- | This generates a seed, which encapsulates the expensive key-stretching+--   component of 'g3pHash' into a reusable, tweakable cryptographic value.+--   This function is way slower than it's companion, 'g3pHash_seedFinalize'.+--   Broadly comparable to @HKDF-Extract@, though with key stretching built-in.++g3pHash_seedInit :: G3PInputBlock -> G3PInputArgs -> G3PSeed+g3pHash_seedInit block args =+    G3PSeed {+      g3pSeed_seguid = seguid,+      g3pSeed_seguidKey = seguidKey,+      g3pSeed_domainTag = domainTag,+      g3pSeed_secret = secret+    }+  where++    -- Explicitly unpack everything for the unused variable warnings.+    -- i.e. It's relatively easy to check that we've unpacked every+    -- field, then we can rely on unused variable warnings to ensure+    -- we have in fact made use of everything.+    domainTag = g3pInputBlock_domainTag block+    seguid = g3pInputBlock_seguid block+    longTag = g3pInputBlock_longTag block+    seedTags = g3pInputBlock_tags block+    phkdfRounds = g3pInputBlock_phkdfRounds block+    bcryptRounds = g3pInputBlock_bcryptRounds block+    bcryptTag = g3pInputBlock_bcryptTag block++    username = g3pInputArgs_username args+    password = g3pInputArgs_password args+    credentials = g3pInputArgs_credentials args++    headerAlfa = [ "G3Pb1 alfa username", username ]++    headerUsername = headerAlfa ++ [+        usernamePadding headerAlfa bcryptTag+          (domainTag <> "\x00password G3Pb1\x00")+      ]++    -- password will go here++    headerLongTag =+      [ longTag+      , B.concat+        [ "Global Password Prehash Protocol bcrypt (v1) G3Pb1"+        , leftEncode phkdfRounds+        , bareEncode bcryptRounds+        ]+      ]++    longPadding = passwordPaddingBytes bytes headerUsername headerLongTag+        longTag (domainTag <> "\x00creds G3Pb1\x00") password+      where+        bl = encodedByteLength bcryptTag+        bytes = add64WhileLt (8413 - bl) 8298++    credsPadding = credentialsPadding credentials bcryptTag+        (domainTag <> "\x00tags G3Pb1\x00")++    seguidKey = hmacKey_init seguid++    secretStream =+        phkdfCtx_initFromHmacKey seguidKey &+        phkdfCtx_addArgs headerUsername &+        phkdfCtx_assertBufferPosition' 32 &+        phkdfCtx_addArg  password &+        phkdfCtx_addArg  bcryptTag &+        phkdfCtx_addArgs headerLongTag &+        -- FIXME: fusing addArg and longPadding can save ~ 8 KiB RAM+        phkdfCtx_addArg  longPadding &+        phkdfCtx_assertBufferPosition' 32 &+        phkdfCtx_addArgs credentials &+        phkdfCtx_addArg  credsPadding &+        phkdfCtx_assertBufferPosition' 29 &+        phkdfCtx_addArgs seedTags &+        phkdfCtx_addArg (bareEncode (V.length seedTags)) &+        phkdfSlowCtx_extract+            (cycleByteStringWithNull bcryptTag)+            (word32 "go\x00\x00" + 2024) domainTag+            "G3Pb1 bravo" phkdfRounds &+        phkdfSlowCtx_assertBufferPosition' 32 &+        phkdfSlowCtx_addArgs seedTags &+        phkdfSlowCtx_finalizeStream (cycleByteStringWithNull bcryptTag)++    (Cons phkdfHash (Cons bcryptInput _)) = secretStream++    dup a = (a,a)++    (bKeyInput, bSaltInput) = B.splitAt 16 bcryptInput++    (bKeyTag, bSaltTag) =+      if B.length bcryptTag <= 56+      then dup $ cycleByteString (bcryptTag <> "\x00G3Pb1 bcrypt\00") 56+      else B.splitAt 56 $ cycleByteStringWithNull bcryptTag 112++    bKey  = bKeyTag <> bKeyInput+    bSalt = bSaltInput <> bSaltTag++    bcryptHash = assert (B.length bKey == 72 && B.length bSalt == 72) $+                 bcryptRaw bKey bSalt bcryptRounds++    headerCharlie = B.concat [+        "G3Pb1 charlie",+        phkdfHash,+        cycleByteStringWithNull bcryptTag 56,+        bcryptHash,+        cycleByteStringWithNull (domainTag <> "\x00G3Pb1 charlie\x00") 32+      ]++    secret =+        phkdfCtx_initFromHmacKey seguidKey &+        phkdfCtx_addArg headerCharlie &+        phkdfCtx_assertBufferPosition' 32 &+        phkdfCtx_addArgs seedTags &+        phkdfCtx_finalize (cycleByteStringWithNull bcryptTag) (word32 "SEED") domainTag++-- | This consumes a seed and tweaks to produce the final output stream.+-- This function is the output expansion phase of 'g3pHash'. This function+-- is way faster than it's companion 'g3pHash_seedInit'. Broadly comparable to+-- HKDF. Note that this function ignores 'g3pSeed_seguid' in favor of+-- 'g3pSeed_seguidKey'.++g3pHash_keyInit :: G3PInputRole -> G3PSeed -> G3PKey+g3pHash_keyInit roleInput seed = G3PKey+    { g3pKey_secret = secretKey+    , g3pKey_secretKey = hmacKey_init secretKey+    , g3pKey_domainTag = g3pSeed_domainTag seed+    }+  where+    -- seguid = g3pSeed_seguid seed+    seguidKey = g3pSeed_seguidKey seed+    domainTag = g3pSeed_domainTag seed+    secret = g3pSeed_secret seed++    role = g3pInputRole_roleTags roleInput++    headerDelta = B.concat [+      "G3Pb1 delta",+      secret+      ]++    secretKey =+        phkdfCtx_initFromHmacKey seguidKey &+        phkdfCtx_addArg  headerDelta &+        phkdfCtx_addArgs role &+        phkdfCtx_finalize (cycleByteStringWithNull domainTag) (word32 "KEY\x00") domainTag++g3pHash_finalizeGen :: G3PInputEcho -> G3PKey -> G3PGen+g3pHash_finalizeGen inputEcho gKey = G3PGen+    { g3pGen_secret = g3pKey_secret gKey+    , g3pGen_phkdfGen = echo+    }+  where+    secretKey = g3pKey_secretKey gKey+    domainTag = g3pKey_domainTag gKey+    echoTag = g3pInputEcho_echoTag inputEcho++    echoHeader = cycleByteString (domainTag <> "\x00G3Pb1 echo\x00") 32++    echoCtr = word32 "OUT\x00"++    echo = phkdfGen_initFromHmacKey echoHeader echoCtr echoTag secretKey++g3pGen_read :: G3PGen -> (ByteString, G3PGen)+g3pGen_read gen = let (out, next) = phkdfGen_read (g3pGen_phkdfGen gen)+                   in (out, gen { g3pGen_phkdfGen = next })++g3pGen_finalizeStream :: G3PGen -> Stream ByteString+g3pGen_finalizeStream = phkdfGen_finalizeStream . g3pGen_phkdfGen
+ lib/Crypto/G3P/BCrypt.hsc view
@@ -0,0 +1,88 @@+{-# LANGUAGE CApiFFI, ViewPatterns #-}++-- |  A very minimal binding to the core of the bcrypt algorithm, adapted from+--    OpenBSD's implementation. The Global Password Prehash Protocol version+--    G3Pb1 cannot be implemented in terms of standard bcrypt interfaces for+--    several reasons:+--+--    1.  Standard bcrypt hashes are truncated to 23 bytes.  The G3P depends+--        on all 24 output bytes.+--+--    2.  Standard bcrypt must specify a number of rounds that is a power of+--        two. The G3P allows any number of rounds between 1 and 2^32 inclusive.+--+--    3.  the G3P needs unimpeded access to the full 72 byte password input.+--        This is not doable with all bcrypt variants.+--+--    4.  Standard bcrypt limits salt length to 16 bytes.  The G3P depends on+--        72 byte salt parameters.+--+--    For this reason, this binding completely removes the code for handling+--    unix-style bcrypt hashes, which has repeatedly proven problematic. One+--    of the major design motifs of the G3P is to replace this cruft with PHKDF,+--    which is intended to be bulletproof.+--+--    Similarly, this binding cannot be directly used to process unix-style+--    bcrypt hashes, which does make testing a bit of a challenge.  However,+--    the core algorithm is unmodified, so implementing unix-style hash+--    handling in terms of this binding is very much possible.+--+--    This will be done in the test suite for this library.  Hopefully that+--    implementation will eventually migrate here, once it's production-ready,+--    so that this binding might also be used to handle standard bcrypt hashes+--    directly.++module Crypto.G3P.BCrypt+  ( bcryptRaw+  , bcryptRaw_maxInputLength+  , bcryptRaw_outputLength+  ) where++#include "bcrypt_raw.h"++import           Data.ByteString(ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Unsafe as B+import           Data.Word++import           Foreign.C.String+import           System.IO.Unsafe++foreign import capi "bcrypt_raw.h bcrypt_raw" c_bcrypt_raw :: CString -> Word32 -> CString -> Word32 -> CString -> Word32 -> IO ()++-- | Any input longer than 72 bytes will be truncated.++bcryptRaw_maxInputLength :: Int+bcryptRaw_maxInputLength = (#const BCRYPT_RAW_MAX_INPUT_LENGTH)++-- | Any output hash from 'bcryptRaw' will be exactly 24 bytes long.++bcryptRaw_outputLength :: Int+bcryptRaw_outputLength = (#const BCRYPT_RAW_OUTPUT_LENGTH)++-- | @bcryptRaw key salt rounds@ Be aware that keys and salts that are longer+--   than 72 bytes do get truncated to exactly 72 bytes. This binding will+--   return a hash that is exactly 24 bytes long.+--+--   Note the rounds parameter is one less than the number of rounds to be+--   computed. Thus if you want something equivalent to the traditional bcrypt+--   cost parameter of 12, you need to specify 4095 rounds.  This is because+--   @2^12 - 1 = 4095@.++bcryptRaw :: ByteString -> ByteString -> Word32 -> ByteString+bcryptRaw (f -> key) (f -> salt) rounds+  = unsafePerformIO $ do+      B.unsafeUseAsCString key $ \keyPtr -> do+        B.unsafeUseAsCString salt $ \saltPtr -> do+          -- using a superfluous `seq` to try to ensure that this allocates a new+          -- unique bytestring.   FIXME: there's almost certainly a better, more+          -- proper, more idiomatic solution here+          let output = B.replicate bcryptRaw_outputLength (saltPtr `seq` 0)+          B.unsafeUseAsCString output $ \outPtr -> do+            c_bcrypt_raw keyPtr (len key) saltPtr (len salt) outPtr rounds+            return output+  where+    len x = fromIntegral (min bcryptRaw_maxInputLength (B.length x))++f :: ByteString -> ByteString+f key = if B.null key then B.replicate bcryptRaw_maxInputLength 0 else key
+ test/G3P.hs view
@@ -0,0 +1,352 @@+{-# LANGUAGE OverloadedStrings, LambdaCase, RecordWildCards, ViewPatterns, ScopedTypeVariables #-}++module G3P where++import Control.Exception(try)+import Control.Applicative+import Data.Aeson(Object, Value(..), parseJSON, (.:), (.:?), withObject)+import Data.Aeson.Types(Parser)+import qualified Data.Aeson as Aeson+import Data.Aeson.Key(Key)+import qualified Data.Aeson.Key as K+import Data.Aeson.KeyMap(KeyMap)+import qualified Data.Aeson.KeyMap as KM+import Data.ByteString(ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Base16 as B+import Data.Function(fix)+import Data.Map(Map)+import qualified Data.Map as Map+import Data.Maybe(fromMaybe)+import Data.Text(Text)+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.Encoding.Base16 as T+import Data.Stream(Stream(..))+import qualified Data.Stream as S+import Data.Vector(Vector, (!))+import qualified Data.Vector as V++import Crypto.G3P+import Test.Tasty+import Test.Tasty.HUnit++type Args = KeyMap Val++data Val+   = Int !Int+   | Str !ByteString+   | Vec !(Vector ByteString)+   | Nul+   | Ref !TestId !Int+     deriving (Show)++data Result = Result+   { result_args :: !Args+   , result_hashes :: !(KeyMap ByteString)+   }++data TestVector = TestVector+   { testVector_name :: !Text+   , testVector_arguments :: !Args+   , testVector_results :: !(Vector Result)+   }++data TestId = TestId+  { testId_name :: !Text+  , testId_index :: !Int+  , testId_algorithm :: !Text+  } deriving (Eq, Ord, Show)++data SimpleTestVector = SimpleTestVector+   { simpleTestVector_id        :: !TestId+   , simpleTestVector_arguments :: !Args+   , simpleTestVector_result    :: !ByteString+   }++type TestVectors = Vector TestVector++type SimpleTestVectors = Vector SimpleTestVector++type ResultEnv = Map TestId (Either String (Stream ByteString))++blankResult :: Result+blankResult = Result+  { result_args   = KM.empty+  , result_hashes = KM.fromList [ ("G3Pb1","") ]+  }++flattenTestVectors :: TestVectors -> SimpleTestVectors+flattenTestVectors tvs =+  V.fromList $+    [ SimpleTestVector+        { simpleTestVector_id =+            TestId { testId_name = testVector_name tv+                   , testId_index = i+                   , testId_algorithm = alg+                   }+        , simpleTestVector_arguments = args+        , simpleTestVector_result = outHash+        }+    | tv <- V.toList tvs+    , (i, res) <- zip [0..]  (seedEmpty (V.toList (testVector_results tv)))+    , let args = KM.union (result_args res) (testVector_arguments tv)+    , (K.toText -> alg, outHash) <- KM.toAscList (result_hashes res)+    ]+  where+    seedEmpty xs+      | null xs = [blankResult]+      | otherwise = map addBlankResult xs+    addBlankResult x+      | null (result_hashes x) = x { result_hashes = result_hashes blankResult }+      | otherwise = x++genResultEnv :: SimpleTestVectors -> ResultEnv+genResultEnv tvs =+  -- FIXME? The resulting scoping rules in the test vector file is analogous+  -- to Haskell or scheme's letrec, whereas I really want let* here+  fix $ \resultEnv ->+    Map.fromList $+      [ (simpleTestVector_id tv, interpret tv resultEnv)+      | tv <- V.toList tvs+      ]+  where+    interpret tv resultEnv+      | alg == "G3Pb1" =+          case getG3PInputs resultEnv args of+            Just inputs -> Right (uncurry4 g3pHash inputs)+            Nothing -> Left "arguments not parsed"+      | otherwise = Left "algorithm name not recognized"+      where+        alg  = testId_algorithm $ simpleTestVector_id tv+        args = simpleTestVector_arguments tv++genSimpleTestCases :: SimpleTestVectors -> ResultEnv -> [ TestTree ]+genSimpleTestCases tvs resultEnv =+   [ testCase testName $ runTest tv resultEnv+   | tv <- V.toList tvs+   , let testId = simpleTestVector_id tv+         name = T.unpack (testId_name testId)+         idx = show (testId_index testId)+         alg = T.unpack (testId_algorithm testId)+         testName = name ++ " | " ++ idx ++ " " ++ alg+   ]++genTestCases :: TestVectors -> [ TestTree ]+genTestCases tvs = genSimpleTestCases stvs (genResultEnv stvs)+  where+    stvs = flattenTestVectors tvs++uncurry4 :: (a -> b -> c -> d -> e) -> (a,b,c,d) -> e+uncurry4 f (a,b,c,d) = f a b c d++instance Aeson.FromJSON Val where+    parseJSON val =+        (Int <$> parseJSON val) <|>+        (Str <$> parseJSONByteString val) <|>+        (Vec <$> parseJSONVectorByteString val) <|>+        (parseRef val) <|>+        (parseNul val)++instance Aeson.FromJSON Result where+    parseJSON = \case+        Object obj -> do+            mArgs <- obj .:? "args"+            args <- maybe (pure KM.empty) parseJSON mArgs+            hashes <- KM.traverse parseJSONHash (KM.delete "args" obj)+            pure (Result args hashes)+        _ -> empty++instance Aeson.FromJSON TestVector where+    parseJSON = withObject "TestVector" $ \v -> TestVector+        <$> v .: "name"+        <*> v .: "args"+        <*> parseResults v++takeBytes :: Int -> Stream ByteString -> ByteString+takeBytes n stream = B.concat (go n stream)+  where+    go n ~(Cons out outStream')+      | n <= 0 = []+      | n <= B.length out = [B.take n out]+      | otherwise = out : go (n - B.length out) outStream'++parseRef :: Value -> Parser Val+parseRef = \case+  Object obj -> do+    ref <- obj .: "ref"+    len <- obj .: "len"+    mAlg <- obj .:? "algorithm"+    mIdx <- obj .:? "index"+    let alg = fromMaybe "G3Pb1" mAlg+        idx = fromMaybe 0 mIdx+        testId = TestId ref idx alg+    return $ Ref testId len+  _ -> empty++parseNul :: Value -> Parser Val+parseNul = \case+  Null -> return Nul+  _ -> empty++parseJSONByteString :: Value -> Parser ByteString+parseJSONByteString = \case+    String txt -> pure (T.encodeUtf8 txt)+    Object obj | KM.size obj == 1 -> do+        txt <- obj .: "hex"+        case B.decodeBase16 (T.encodeUtf8 txt) of+          Left _ -> empty+          Right x -> pure x+    _ -> empty++parseJSONVectorByteString :: Value -> Parser (Vector ByteString)+parseJSONVectorByteString val =+    (V.singleton <$> parseJSONByteString val) <|>+    case val of+      Array bs -> V.generateM (V.length bs) (\i -> parseJSONByteString (bs ! i))+      _ -> empty++parseJSONHash :: Value -> Parser ByteString+parseJSONHash = \case+    String txt ->+        case B.decodeBase16 (T.encodeUtf8 txt) of+            Left _ -> empty+            Right x -> pure x+    _ -> empty++parseResults :: Object -> Parser (Vector Result)+parseResults v =+    case KM.lookup "results" v of+        Nothing -> pure V.empty+        Just v@(Object _) ->+            V.singleton <$> parseJSON v+        Just (Array v) ->+            V.generateM (V.length v) (\i -> parseJSON (v ! i))+        _ -> empty++readTestVectorsFromFile :: String -> IO (String, Either String TestVectors)+readTestVectorsFromFile fileName =+    try (Aeson.eitherDecodeFileStrict' fileName) >>= \case+        Left (err :: IOError) -> return (fileName, Left (show err))+        Right result -> return (fileName, result)++testVectorDefaultFileName :: String+testVectorDefaultFileName = "g3p-test-vectors.json"++testFile :: (String, Either String TestVectors) -> TestTree+testFile (fileName, mTestVectors) =+    case mTestVectors of+      Left err -> testCase testName $ assertFailure err+      Right tvs -> testGroup testName $ genTestCases tvs+  where+    testName = "testfile: " ++ fileName++runTest :: SimpleTestVector -> ResultEnv -> Assertion+runTest tv resultEnv =+  case Map.lookup (simpleTestVector_id tv) resultEnv of+    Nothing -> assertFailure "test result not found (this shouldn't be possible)"+    Just (Left err) -> assertFailure err+    Just (Right result) -> compareAu alg goldenOutput result+  where+    alg = T.unpack . testId_algorithm $ simpleTestVector_id tv+    goldenOutput = simpleTestVector_result tv++compareAu :: String -> ByteString -> Stream ByteString -> Assertion+compareAu name bs outStream+  | B.null bs = assertFailure ("\"" ++ name ++ "\":\"" ++ concatMap toHex (S.take 4 outStream) ++ "\"")+  | otherwise = B.encodeBase16 (takeBytes (B.length bs) outStream) @?= B.encodeBase16 bs+  where+    toHex = T.unpack . B.encodeBase16++-- FIXME? Allow computation of tweaks without recomputing seed++getG3PInputs :: ResultEnv -> KeyMap Val -> Maybe (G3PInputBlock, G3PInputArgs, G3PInputRole, G3PInputEcho)+getG3PInputs env = \case+  (getG3PBlock env -> Just (block,+   getG3PArgs env -> Just (args,+   getG3PRole env -> Just (role,+   getG3PEcho env -> Just (echo,+   args'))))) | KM.null args'+    -> Just (block, args, role, echo)+  _ -> Nothing++getG3PArgs :: ResultEnv -> KeyMap Val -> Maybe (G3PInputArgs, KeyMap Val)+getG3PArgs env = \case+  (matchKey env "username" -> (Just (Str g3pInputArgs_username),+   matchKey env "password" -> (Just (Str g3pInputArgs_password),+   matchKey env "credentials" -> (+     getByteStringVector_defaultEmpty -> Just g3pInputArgs_credentials,+   args'))))+    -> Just (G3PInputArgs {..}, args')+  _ -> Nothing++getByteStringVector_defaultEmpty :: Maybe Val -> Maybe (Vector ByteString)+getByteStringVector_defaultEmpty = \case+  Nothing -> Just V.empty+  Just Nul -> Just V.empty+  Just (Str str) -> Just (V.singleton str)+  Just (Vec vec) -> Just vec+  _ -> Nothing++getByteString_defaultEmpty :: Maybe Val -> Maybe ByteString+getByteString_defaultEmpty+  = fmap (fromMaybe B.empty) . getMaybeByteString++getByteString :: Maybe Val -> Maybe ByteString+getByteString = \case+  Just (Str str) -> Just str+  _ -> Nothing++getMaybeByteString :: Maybe Val -> Maybe (Maybe ByteString)+getMaybeByteString = \case+  Just (Str str) -> Just (Just str)+  Just Nul -> Just Nothing+  Nothing -> Just Nothing+  _ -> Nothing++getG3PBlock :: ResultEnv -> KeyMap Val -> Maybe (G3PInputBlock, KeyMap Val)+getG3PBlock env = \case+  (matchKey' env "domain-tag" -> (Just (Str g3pInputBlock_domainTag),+   matchKey env "seguid" -> (getByteString_defaultEmpty -> Just g3pInputBlock_seguid,+   matchKey env "long-tag" -> (getMaybeByteString -> Just mLongTag,+   matchKey env "tags" -> (getByteStringVector_defaultEmpty -> Just tags,+   matchKey env "seed-tags" -> (getByteStringVector_defaultEmpty -> Just seedTags,+   matchKey env "phkdf-rounds" -> (Just (Int (fromIntegral -> g3pInputBlock_phkdfRounds)),+   matchKey env "bcrypt-rounds" -> (Just (Int (fromIntegral -> g3pInputBlock_bcryptRounds)),+   matchKey env "bcrypt-tag" -> (getMaybeByteString -> Just mBcryptTag,+   matchKey env "bcrypt-salt-tag" -> (getMaybeByteString -> Just mBcryptSaltTag,+   args'))))))))))+   -> let g3pInputBlock_tags = tags <> seedTags+          g3pInputBlock_longTag = fromMaybe g3pInputBlock_domainTag mLongTag+          g3pInputBlock_bcryptTag = fromMaybe g3pInputBlock_domainTag mBcryptTag+          g3pInputBlock_bcryptSaltTag = fromMaybe g3pInputBlock_bcryptTag mBcryptSaltTag+       in Just (G3PInputBlock {..}, args')+  _ -> Nothing++getG3PRole :: ResultEnv -> KeyMap Val -> Maybe (G3PInputRole, KeyMap Val)+getG3PRole env = \case+  (matchKey env "role" -> (getByteStringVector_defaultEmpty -> Just roleTags,+   args'))+   -> Just (G3PInputRole roleTags, args')+  _ -> Nothing++getG3PEcho :: ResultEnv -> KeyMap Val -> Maybe (G3PInputEcho, KeyMap Val)+getG3PEcho env = \case+  (matchKey env "echo-tag" -> (getMaybeByteString -> Just mEchoTag,+   matchKey env "domain-tag" -> (getByteString -> Just domainTag,+   args')))+   -> let echoTag = fromMaybe domainTag mEchoTag+       in Just (G3PInputEcho echoTag, args')+  _ -> Nothing++matchKey, matchKey' :: ResultEnv -> Key -> KeyMap Val -> (Maybe Val, KeyMap Val)+matchKey env key map = (interpRefs env (KM.lookup key map), KM.delete key map)+matchKey' env key map = (interpRefs env (KM.lookup key map), map)++interpRefs :: ResultEnv -> Maybe Val -> Maybe Val+interpRefs env (Just ref@(Ref testId bytes)) =+  case Map.lookup testId env of+    Nothing -> Just ref+    Just (Left _) -> Just ref+    Just (Right echo) -> Just (Str (takeBytes bytes echo))+interpRefs _   val = val
+ test/Main.hs view
@@ -0,0 +1,13 @@+import Test.Tasty+import Data.Monoid+import qualified G3P++main = do+  let fileName = G3P.testVectorDefaultFileName+  g3pTvs <- G3P.readTestVectorsFromFile fileName+  defaultMain (tests g3pTvs)++tests :: (String, Either String G3P.TestVectors) -> TestTree+tests g3pTvs = testGroup "Test" [+    testGroup "G3Pb1" [G3P.testFile g3pTvs]+  ]