Îõ³h*NvIH£      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰ Š ‹ Œ  Ž   ‘ ’ “ ” • – — ˜ ™ š › œ  ž Ÿ   ¡ ¢ 0.1.0.0 (c) 2024 Auth GlobalApache2 Safe-Inferred"(,phkdfïExtends a PHKDF end-of-message tag in order to ensure the last SHA-256 block contains something interesting.°Tags less than 160 bits (20 bytes) long are appended directly, without extension, as the final portion of the message. Thus this function is the identity on short inputs.¨After extension, tags that are at least 20 bytes long should be thought of as a bitstring with a single null bit appended at the end to make it a full bytestring.Tags 160 bits or longer are first extended, iff necessary, to a full bytestring by adding a single "1" bit followed by zero to six "0" bits.÷The bytestring is then extended by 0-63 bytes as needed to make the overall length equivalent to 19 (mod 64). The first byte of the extension is a null byte, then followed by the bytestring, then starting again at the null byte as needed.êThe length of this extension takes up the first 6 bits of the last byte, followed by a "0" bit denoting the tag is a bytestring, or a "1" denoting that the tag is a proper bitstring whose length is not an exact multiple of 8.ÚThe final bit is reserved for SHA-256's end-of-message padding, which will set it to 1.phkdfThis function robustly undoes ò, thus "proving" that all collisions on PHKDF's tag are cryptographically non-trivial, even after extension.*This is a "proof" in the sense that if 'trimExtendedTag (extendTag x) == Just x is true for all bytestrings x«, then all collisions are non-trivial, but we haven't presented a full deductive proof of this property. It is part of the test suite, tested by quickcheck fuzzing.ëThe rest of PHKDF and the G3P's syntax follows this as an iron rule of syntax design. I've not literally written a program to parse out the original arguments, but I've ensured that it is straightforward to do so in principle.´In the case of variable-length PHKDF, starting from some known buffer position (usually either 0 or 32), first there are zero or more bitstring arguments encoded via TupleHash syntax. Since TupleHash's length encoding cannot start with a null byte, a single null byte is used to signal the end of these input arguments. Then 0-63 end padding bytes are generated in order to bring the buffer position equivalent to 32 (mod 64), then 4 bytes of counter, then the extended version of PHKDF's end-of-message tag, then finally SHA256's end padding.úThis is easy to robustly undo, as I've started to demonstrate in this subroutine. This leads to a simple categorical/combinatorial style proof that all collisions over PHKDF's input arguments and domain tag are cryptographically non-trivial.phkdfadd64WhileLt b c is equivalent to #while (b < c) { b += 64 }; return bphkdfEquivalent to Ë, except with trace debugging. This should never be used in production. phkdf8Partition a bytestring into chunks of up to a given sizephkdfæPartition a cyclically extended bytestring into chunks of a given size, starting at a given offset.ÛNote that repetitions of the original string get a single null byte placed between them.phkdfDesired chunk sizephkdf!String to be cyclically extended.phkdfStarting offsetphkdfInfinite stream of chunks    (c) 2024 Auth GlobalApache2 Safe-Inferred"(åphkdfadd64WhileLt b c is equivalent to #while (b < c) { b += 64 }; return b    Safe-Inferred(ä%:£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚ  Safe-Inferred(äÀ#£ÛÜݤÞßà¥áâã¦äåæçèéêëìíîïðñòóôõö÷ø(c) 2024 Auth GlobalApache2 Safe-InferredH&')(*+!"#$%, &')(*+!"#$%, (c) 2024 Auth GlobalApache2 Safe-Inferred±-./0-./0(c) 2024 Auth GlobalApache2 Safe-Inferred"ݲ1phkdfinitialize an empty  phkdfStream# context from a plaintext HMAC key.2phkdfinitialize an empty  phkdfStream2 context from a plaintext or precomputed HMAC key.3phkdfinitialize an empty  phkdfStreamÄ context from a plaintext, precomputed, or buffer-prefixed HMAC key.4phkdfinitialize an empty  phkdfStream% context from a precomputed HMAC key.5phkdfinitialize an empty  phkdfStream) context from a buffer-prefixed HMAC key.:phkdfinitialize a new empty  phkdfStreamé context from the HMAC key originally supplied to the context, discarding all arguments already added.;phkdf…initialize a new empty HMAC context from the key originally supplied to the PHKDF context, discarding all arguments already added.<phkdf'append a single string onto the end of  phkdfStream's list of arguments.=phkdf,append zero or more strings onto the end of  phkdfStream's list of arguments.@phkdf close out a  phkdfStreamþ context using the first mode of operation, examining only the first output block and discarding the rest of the stream.AphkdfTurn a & into a incomplete call to hmacï, with the option of adding additional data to the end of the message that need not be TupleHash encoded.Bphkdf"improperly" close out a & as if it were a call to hmac instead of  phkdfStream+, though with a TupleHash message encoding.Cphkdf close out a  phkdfStream% context with a given counter and tagQphkdf close out a  phkdfStream context with a call to phkdfSlowExtract!, providing the counter, tag, fnName0, and number of rounds to compute. Note that fnNameê is truncated to a length of 25-29 bytes long, depending upon the number of rounds specified. Thus the fnName4 is primarily intended to be a protocol constant.RphkdfAdd a tweak to a call to phkdfSlowExtract.Sphkdf%Add zero or more tweaks to a call to phkdfSlowExtract.Tphkdffinalize a call to phkdfSlowExtract<, discarding all but the first block of the output streamUphkdffinalize a call to phkdfSlowExtract,&)124536789;:<=>?@BACD!QRSTUEFHIGJKLMONP,&124536789);:<=>?@BACD!QRSTUEFHIGJKLMONP(c) 2024 Auth GlobalApache2 Safe-InferredM ]^a_`bVWXYZ[\ ]^a_`bVWXYZ[\(c) 2024 Auth GlobalApache2 Safe-Inferredªcdcd(c) 2024 Auth GlobalApache2 Safe-Inferred"Ý'ephkdfinitialize an empty  phkdfStream# context from a plaintext HMAC key.fphkdfinitialize an empty  phkdfStream2 context from a plaintext or precomputed HMAC key.gphkdfinitialize an empty  phkdfStreamÄ context from a plaintext, precomputed, or buffer-prefixed HMAC key.hphkdfinitialize an empty  phkdfStream% context from a precomputed HMAC key.iphkdfinitialize an empty  phkdfStream) context from a buffer-prefixed HMAC key.jphkdfÛRetrieve the HmacKeyPlain that the phkdfCtx was originally initialized with, if possiblekphkdfÜRetrieve the HmacKeyHashed that the phkdfCtx was originally initialized with, if possiblelphkdfÒRetrieve the HmacKeyPrefixed that the phkdfCtx was originally initialized with.mphkdf×Retrieve the HmacKey that the phkdfCtx was originally initialized with, if possible.nphkdfinitialize a new empty  phkdfStreamé context from the HMAC key originally supplied to the context, discarding all arguments already added.ophkdf…initialize a new empty HMAC context from the key originally supplied to the PHKDF context, discarding all arguments already added.pphkdf'append a single string onto the end of  phkdfStream's list of arguments.qphkdf,append zero or more strings onto the end of  phkdfStream's list of arguments.tphkdf close out a  phkdfStreamþ context using the first mode of operation, examining only the first output block and discarding the rest of the stream.uphkdfTurn a ] into a incomplete call to hmacï, with the option of adding additional data to the end of the message that need not be TupleHash encoded.vphkdfTurn a ] into a £Ü by adding a null byte followed by 0-63 bytes as needed to get to a SHA256 block boundarywphkdf"improperly" close out a ] as if it were a call to hmac instead of  phkdfStream+, though with a TupleHash message encoding.xphkdf close out a  phkdfStream% context with a given counter and tagyphkdfÀHow long would the end padding be if the PhkdfCtx was finalized?zphkdf'How long would the block padding be if v is called?tphkdfÊend-of-message padding, output length must be equal to the number providedphkdfcounterphkdftagvphkdfÐblock synchronization padding, ouput length must be equal to the number provided{phkdfÊend-of-message padding, output length must be equal to the number providedphkdfcounterphkdftag+]a_efhigjklmonpqrstwuvx{yzVX|}€~‚ƒ„†‡…ˆ+]efhigjklmaonpqrstwuvx{_yzV|}€~‚ƒ„X†‡…ˆ (c) 2024 Auth GlobalApache2 Safe-Inferred"H܉phkdf=A plain-old-data explicit representation of the intermediate   computation after the ˜ and “‰ 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 ¡. and then consumed without modification by ¢.phkdfý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  + 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 specify an additional variable-length string in the role vector, used to control its relative ending position inside the SHA256 buffer.“phkdfThe 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 is the least expensive pay-as-you-go option for plaintext tagging.†The credentials vector is constant time on 0-63 encoded bytes, incurring one additional SHA256 block every 64 bytes thereafter. This includes a variable-length field that encodes the bit length of each string; this field itself requires 2 or more bytes.ªThe username and password are constant time as long as their encoded lengths add up to less than roughly 3 kilobytes, or the username, password, and domain tag add up to less than roughly 8 kilobytes. The actual numbers are somewhat less in both cases, but this is a good approximation.•phkdfúThe name of this parameter is suggestive, but this parameter is functionally identical to a second password. The only difference is the fact that a password can be cracked without knowledge of the plaintext username. By contrast, the password acts as a plaintext tag if one provides the username: guessing the username implies plaintext knowledge of the password.˜phkdfÑ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.ó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.õIn more concrete cryptographic terms, the seguid parameter is the constant HMAC key used by the protocol right up until the final output exansion. This design is closely modelled on the HKDF construction. As such, adding null bytes onto the ends of seguids that are less than 64 bytes long should be the only source of trivial collisions in the entire protocol.õThe remaining parameter strings are all directly-documenting plaintext tags. A deployment can use these tags to encode a message into the password hash function so that it must be known to whomever is hashing a password of their choice.ÆFinally, the rounds parameter determines the latency of the function. At least 250,000 rounds are recommended if PHKDF is used as the sole key stretching component of a password hash database.ÜUnfortunately PHKDF is inexpensively parallelized, so large investments here aren't a good expenditure of a user's latency budget. This is why the G3P integrates bcrypt, and cuts the suggested rounds down to 20,000For comparison, n3 rounds of PHKDF is approximately equivalent to (1.5 + dtl)*n + c rounds of PBKDF2, where dtlÎ is related to the domain tag length, and c is a bit larger than 130 or so.Here, dtlŒ is 0 when the domain tag is between 0 and 19 bytes long, 0.5 when the domain tag is between 20 and 83 bytes long, and an additional 0.5 for every 64 bytes thereafter. Thus these functions exhibit extreme timing side channels on the length of the domain tag.äBy contrast, the long tag is hardened against timing side channels up to a bit less than 5 kilobytes in length. However, an extremely long tag does reduce the headroom provided to masking the length of the username and password fields, however the minimum headroom allocated to the username and password fields is a bit less than 3 kilobytes./As an alternate tagging location, consider the —Ý vector, which can be used as an inexpensive, pay-as-you-go plaintext tagging location.$If the total encoded byte length of  is between 0-63 bytes, then these hash protocols operate in a constant number of SHA256 blocks. Every additional 64 bytes incurs the computation of two or three additional SHA256 blocks, because these tags are hashed into the result two times in the case of  $, and three times in the case of Ÿ (and g3pHash).šphkdføHMAC-SHA256 key, usable as a high-repetition indirect tag via self-documenting globally unique identifiers (seguids).›phkdf·plaintext tag with one repetition per round. 0-19 bytes are free, 20-83 bytes cost a additional sha256 block per round, with every 64 bytes thereafter incurring a similar cost.œphkdfüplaintext tag with 1x repetition, then cycled for roughly 8 kilobytes. Constant time on inputs up to nearly 5 kilobytes.phkdf"plaintext tag with 2x repetition ( ) or 3x repetition (ŸØ). Constant-time on 0-63 encoded bytes, which includes the length encoding of each string. Thus 60 of those bytes are usable if the tags vector is a single string, or less if it contains two or more strings.žphkdfhow expensive will this hash function be? An optimal implementation computes exactly three SHA256 blocks per round if the domain tag is 19 bytes or less. It is not recommended that phkdf be used as the primary key-stretching component of a deployment, but if it is used this way, we recommend at least 250,000 rounds. This can be adjusted downward in the case of domain tags longer than 19 bytes.Ÿphkdf3A non-tweakable, complete password prehash protocol phkdfÙA tweakable, complete prehash protocol. Note that this function is very intentionally implemented in such a way that the following idiom is efficient, and only performs the expensive key stretching phase once: Ø let mySeed = phkdfPass block args in [ mySeed tweak1, mySeed tweak2, mySeed tweak3 ] øHowever in the case that you want or need to persist or serialize the intermediate seed, then the plain-old-datatype ‰ and its companion functions ¡ and ¢& are likely to be more appropriate.¡phkdfÔThis generates a seed, which encapsulates the expensive key-stretching component of  ã into a reusable, tweakable cryptographic value. This function is way slower than it's companion, ¢Ë. Broadly comparable to HKDF-Extract, though with key stretching built-in.¢phkdfôThis consumes a seed and tweaks to produce the final output stream. This function is the output expansion phase of  4. This function is way faster than it's companion ¡&. Broadly comparable to HKDF-Expand.‰ŽŒ‹Š’‘“—–•”˜žœ›š™Ÿ ¡¢˜žœ›š™“—–•”’‘‰ŽŒ‹ŠŸ ¡¢ù   !"#$%&'(()*+,-../01223456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`(()*+,-22a3468:<=>?@ABCDEFGHIJKLbMNcdOPQRSTUVWXYeZ[ f f g h i j k k l m n n o p q r r s t u v w x y z { | } ~  €  ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ  Ž   ‘ ’ “ ” • – — ˜ ™ š › œ  ž Ÿ   ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ± ² ³ | ´ µ } ¶ · ~ ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í ÎÏ$phkdf-0.1.0.0-HhmyRSSXptvA4zqp60xn1iCrypto.PHKDF.PrimitivesCrypto.Encoding.PHKDFCrypto.Encoding.PHKDF.V1Crypto.PHKDF.Primitives.SubtleCrypto.PHKDF.Primitives.AssertCrypto.PHKDF.SubtleCrypto.PHKDF.Assert Crypto.PHKDFCrypto.PHKDF.V1.CookbookphkdfCrypto.PHKDF.HMACCrypto.PHKDF.HMAC.Subtle%sha256-0.1.0.2-BtfTb451cqRKdC6SfPoEmf!Crypto.Sha256.Hmac.ImplementationHmacKeyCrypto.Sha256.HmachmacKeyextendTagToList extendTagtrimExtendedTag add64WhileLt add64WhileLt'dropBstakeBstakeBs'takeB' assertTakeB' nullBufferchunkify chunkifyCyclecycleByteStringToListcycleByteStringWithNullToListcycleByteStringcycleByteStringWithNull trimExtTagusernamePaddingpasswordPaddingBytespasswordPaddingcredentialsPaddingPhkdfGenphkdfGen_hmacKeyLikephkdfGen_extTagphkdfGen_counterphkdfGen_statephkdfGen_initCtx PhkdfSlowCtxphkdfSlowCtx_phkdfCtxphkdfSlowCtx_counterphkdfSlowCtx_tagPhkdfCtxphkdfCtx_statephkdfCtx_hmacKeyLikephkdfCtx_byteLenphkdfCtx_unsafeFeedphkdfSlowCtx_liftphkdfCtx_assertBufferPosition'"phkdfSlowCtx_assertBufferPosition'phkdfCtx_assertBufferPosition!phkdfSlowCtx_assertBufferPositionphkdfCtx phkdfCtx_initphkdfCtx_initLikephkdfCtx_initHashedphkdfCtx_initPrefixedphkdfCtx_hmacKeyPlainphkdfCtx_hmacKeyHashedphkdfCtx_hmacKeyPrefixedphkdfCtx_hmacKeyphkdfCtx_resetphkdfCtx_toResetHmacCtxphkdfCtx_feedArgphkdfCtx_feedArgsphkdfCtx_feedArgsByphkdfCtx_feedArgConcatphkdfCtx_finalizephkdfCtx_toHmacCtxphkdfCtx_finalizeHmacphkdfCtx_toStreamphkdfCtx_toGenphkdfGen phkdfGen_initphkdfGen_initLikephkdfGen_initHashedphkdfGen_initPrefixedphkdfGen_hmacKeyPlainphkdfGen_hmacKeyHashedphkdfGen_hmacKeyPrefixedphkdfGen_hmacKey phkdfGen_peek phkdfGen_readphkdfGen_toStreamphkdfSlowCtx_extractphkdfSlowCtx_feedArgphkdfSlowCtx_feedArgsphkdfSlowCtx_finalizephkdfSlowCtx_toStreamphkdfCtx_byteCountphkdfCtx_toHmacKeyPrefixedphkdfCtx_endPaddingLengthphkdfCtx_blockPaddingLength phkdfGen_head PhkdfSeedphkdfSeed_seguidphkdfSeed_seguidKeyphkdfSeed_domainTagphkdfSeed_secretPhkdfInputTweakphkdfInputTweak_rolephkdfInputTweak_echoTagPhkdfInputArgsphkdfInputArgs_usernamephkdfInputArgs_passwordphkdfInputArgs_credentialsPhkdfInputBlockphkdfInputBlock_seguidphkdfInputBlock_domainTagphkdfInputBlock_longTagphkdfInputBlock_tagsphkdfInputBlock_rounds phkdfSimple phkdfPassphkdfPass_seedInitphkdfPass_seedFinalizeHmacKeyPrefixed HmacKeyHashedHmacCtx HmacKeyLike HmacKeyPlainhmacKey_toHashedhmacKeyLike_toPrefixedhmacKeyPrefixed_initHashedhmacKeyPrefixed_blockCounthmacKey_toPlainhmacKey_forgetPlain hmacKeyLikehmacKeyLike_inithmacKeyLike_initHashedhmacKeyLike_initPrefixedhmacKeyLike_toPlainhmacKeyLike_toHashedhmacKeyLike_toKeyhmacKeyLike_runhmacKeyLike_byteCounthmacKeyLike_blockCounthmacKeyLike_bufferLengthhmacKey_hashed hmacKey_run hmacKeyHashedhmacKeyHashed_toKeyhmacKeyHashed_runhmacKeyHashed_runWithhmacKeyPrefixedhmacKeyPrefixed_inithmacKeyPrefixed_initLikehmacKeyPrefixed_toHashedhmacKeyPrefixed_feedhmacKeyPrefixed_feedshmacKeyPrefixed_feedsWithhmacKeyPrefixed_runhmacKeyPrefixed_byteCounthmacKeyPrefixed_bufferLengthhmachmac'hmacCtx hmacCtx_inithmacCtx_initWithhmacCtx_update hmacCtx_feedhmacCtx_updates hmacCtx_feedshmacCtx_finalizehmacCtx_finalizeBitshmacCtx_finalize_toByteString!hmacCtx_finalizeBits_toByteStringhmacCtx_finalizeBytes"hmacCtx_finalizeBytes_toByteStringhmacCtx_byteCounthmacCtx_blockCounthmacCtx_bufferLengthhmacKeyPrefixed_opadhmacKeyPrefixed_ipadCtxhmacKeyHashed_opadhmacKeyHashed_ipad hmacCtx_opadhmacCtx_ipadCtxHmacKeyLike_PlainHmacKeyLike_HashedHmacKeyLike_Prefixed HmacKey_PlainHmacKey_Hashed hmacKey_ipadhmacKey_ipadCtx hmacKey_opadhmacKey_opadCtxhmacKeyLike_ipadCtxhmacKeyLike_opadhmacKeyLike_opadCtxhmacKeyLike_runIpadCtxhmacKeyLike_runOpadCtxhmacKeyHashed_ipadCtxhmacKeyHashed_runIpadCtxhmacKeyHashed_opadCtxhmacKeyHashed_runOpadCtxhmacKeyPrefixed_runIpadCtxhmacKeyPrefixed_runOpadCtxhmacKeyPrefixed_opadCtx