diff --git a/draft-lucas-balloon-hashing.html b/draft-lucas-balloon-hashing.html new file mode 100644 index 0000000..264ee28 --- /dev/null +++ b/draft-lucas-balloon-hashing.html @@ -0,0 +1,1904 @@ + + + + + + +Balloon Hashing + + + + + + + + + + + + + + + + + + + + + + + +
Internet-DraftBalloon HashingDecember 2023
LucasExpires 3 July 2024[Page]
+
+
+
+
Workgroup:
+
Network Working Group
+
Internet-Draft:
+
draft-lucas-balloon-hashing-latest
+
Published:
+
+ +
+
Intended Status:
+
Informational
+
Expires:
+
+
Author:
+
+
+
S. Lucas
+
Individual Contributor
+
+
+
+
+

Balloon Hashing

+
+

Abstract

+

This document describes Balloon, a memory-hard function suitable for password hashing and password-based key derivation. It has proven memory-hardness properties, is built from any standard cryptographic hash function or extendable-output function (XOF), is resistant to cache-timing attacks, and is easy to implement whilst remaining performant.

+
+
+

+About This Document +

+

This note is to be removed before publishing as an RFC.

+

+ The latest revision of this draft can be found at https://samuel-lucas6.github.io/draft-lucas-balloon-hashing/draft-lucas-balloon-hashing.html. + Status information for this document may be found at https://datatracker.ietf.org/doc/draft-lucas-balloon-hashing/.

+

Source for this draft and an issue tracker can be found at + https://github.com/samuel-lucas6/draft-lucas-balloon-hashing.

+
+
+
+

+Status of This Memo +

+

+ This Internet-Draft is submitted in full conformance with the + provisions of BCP 78 and BCP 79.

+

+ Internet-Drafts are working documents of the Internet Engineering Task + Force (IETF). Note that other groups may also distribute working + documents as Internet-Drafts. The list of current Internet-Drafts is + at https://datatracker.ietf.org/drafts/current/.

+

+ Internet-Drafts are draft documents valid for a maximum of six months + and may be updated, replaced, or obsoleted by other documents at any + time. It is inappropriate to use Internet-Drafts as reference + material or to cite them other than as "work in progress."

+

+ This Internet-Draft will expire on 3 July 2024.

+
+
+ +
+
+

+Table of Contents +

+ +
+
+
+
+

+1. Introduction +

+

Balloon [BCS16] is a memory-hard password hashing and password-based key derivation function that was published shortly after the Password Hashing Competition (PHC), which recommended Argon2 [RFC9106]. It is mentioned in a NIST Special Publication on authentication [SP800-63B] and has several advantages over prior password hashing algorithms:

+ +

Unfortunately, the paper did not fully specify the algorithm nor provide guidance on parameters. Therefore, this document aims to rectify the situation based on existing interoperable implementations, real-world password hashing guidance, and research since the paper.

+
+
+
+
+

+2. Conventions and Definitions +

+

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, +“MAY”, and “OPTIONAL” in this document are to be interpreted as +described in BCP 14 [RFC2119] [RFC8174] when, and only when, they +appear in all capitals, as shown here.

+

Throughout this document, “byte” refers to the same unit as “octet”, namely an 8-bit sequence.

+

Operations:

+ +

Constants:

+ +
+
+
+
+

+3. The Balloon Function +

+
+
+Balloon(password, salt, spaceCost, timeCost, delta)
+
+
+

The Balloon function can be divided into three steps:

+
    +
  1. +

    Expand: a large buffer is filled with pseudorandom bytes derived by repeatedly hashing the password and salt. This buffer is divided into blocks the size of the hash function output length.

    +
  2. +
  3. +

    Mix: the buffer is mixed for the number of rounds specified by the user. Each block becomes equal to the hash of the previous block, the current block, and delta other blocks ‘randomly’ chosen from the buffer based on the salt.

    +
  4. +
  5. +

    Extract: the last block of the buffer is output as the password hash/derived key.

    +
  6. +
+

Inputs:

+ +

Outputs:

+ +

Steps:

+
+
+buffer = List(spaceCost, HASH_LEN)
+counter = 0
+
+buffer[0] = Hash(LE64(counter++) || password || salt)
+for m = 1 to spaceCost - 1
+        buffer[m] = Hash(LE64(counter++) || buffer[m - 1])
+
+for t = 0 to timeCost - 1
+        for m = 0 to spaceCost - 1
+                if m == 0
+                        previous = buffer[spaceCost - 1]
+                else
+                        previous = buffer[m - 1]
+
+                buffer[m] = Hash(LE64(counter++) || previous || buffer[m])
+
+                for i = 0 to delta - 1
+                        idxBlock = Hash(LE64(t) || LE64(m) || LE64(i))
+                        idxBlock = Hash(LE64(counter++) || salt || idxBlock)
+                        other = BI(idxBlock) % spaceCost
+                        buffer[m] = Hash(LE64(counter++) || buffer[m] || buffer[INT32(other)])
+
+return buffer[spaceCost - 1]
+
+
+
+
+
+
+

+4. The Balloon-M Function +

+
+
+BalloonM(password, salt, spaceCost, timeCost, parallelism, delta)
+
+
+

A limitation of Balloon is that it lacks parallelism because the value of each block depends on the value of the previous block. Balloon-M addresses this by invoking Balloon in parallel using multiple cores, XORing the outputs, and hashing the password, salt, and XORed output concatenated together. This provides greater memory hardness without increasing the delay.

+

Inputs:

+ +

Outputs:

+ +

Steps:

+
+
+outputs = List(parallelism, HASH_LEN)
+
+parallel for i = 0 to parallelism - 1
+        newSalt = salt || LE64(i + 1)
+        outputs[i] = Balloon(password, newSalt, spaceCost, timeCost, delta)
+
+foreach output in outputs
+        for i = 0 to output.Length - 1
+                hash[i] = hash[i] ^ output[i]
+
+return Hash(password || salt || hash)
+
+
+
+
+
+
+

+5. Implementation Considerations +

+

In the context of a cryptographic library, it is RECOMMENDED to only provide users access to Balloon-M. Otherwise, users will be left wondering which variant to use and may incorrectly believe Balloon-M with parallelism = 1 is equivalent to Balloon.

+

Implementations MAY hardcode delta = MIN_DELTA to avoid user confusion since other password hashing algorithms do not have this parameter. Moreover, the performance/security tradeoff is unclear from the paper.

+

Whilst the pseudocode uses a list of byte arrays for the buffer, slicing portions of a single large byte array to access/update blocks will likely be more performant.

+

Similarly, using a byte array counter instead of an integer that gets repeatedly converted to a byte array will likely aid performance.

+

With Balloon-M, the XORing of outputs can be skipped if parallelism = 1.

+

Finally, it is recommended to use an incremental hash function API rather than manually copying byte arrays to concatenate inputs as this is cleaner and may be more efficient.

+
+
+
+
+

+6. Choosing the Hash Function +

+

The choice of cryptographic hash function/XOF affects the performance and security of Balloon in two ways:

+
    +
  1. +

    For the same parameters, the attacker has an advantage if the algorithm is faster in hardware versus software. They will be able to do the computation in less time than the defender.

    +
  2. +
  3. +

    For the same delay, the defender will be forced to use smaller parameters with a slower cryptographic hash function/XOF in software. Using a faster algorithm in software means stronger parameters can be used.

    +
  4. +
+

It is RECOMMENDED to use a cryptographic hash function/XOF that is fast in software but relatively slow in hardware, such as BLAKE2b [RFC7693]. As another example, SHA-512 is preferable to SHA-256 [RFC6234]. Finally, SHA-3 [FIPS202] is NOT RECOMMENDED as it is slower in software compared to in hardware.

+
+
+
+
+

+7. Choosing the Parameters +

+

The higher the spaceCost, timeCost, and delta, the longer it takes to compute an output. If these values are too small, security is unnecessarily reduced. If they are too large, there is a risk of user frustration and denial-of-service for different types of user devices and servers. To make matters even more complicated, these parameters may need to be increased over time as hardware gets faster.

+

The following procedure can be used to choose parameters:

+
    +
  1. +

    If configurable in the implementation, set delta to 3. This value is used in the paper [BCS16].

    +
  2. +
  3. +

    If using Balloon-M, set the parallelism to 1 on a server and 4 otherwise. This assumes most user devices have at least 4 CPU cores.

    +
  4. +
  5. +

    Establish the maximum acceptable delay for the user. For example, 100-500 ms for authentication, 250-1000 ms for file encryption, and 1000-5000 ms for disk encryption. On servers, you also need to factor in the maximum number of authentication attempts per second.

    +
  6. +
  7. +

    Determine the maximum amount of memory available, taking into account different types of user devices and denial-of-service. For instance, mobile phones versus laptops/desktops.

    +
  8. +
  9. +

    Convert the MiB/GiB memory size to bytes. Then set spaceCost to bytes / HASH_LEN, which is the number of blocks.

    +
  10. +
  11. +

    Find the timeCost that brings you closest to the maximum acceptable delay or target number of authentication attempts per second by running benchmarks.

    +
  12. +
  13. +

    If timeCost is only 1, reduce spaceCost to be able to increase timeCost. Performing multiple rounds is beneficial for security [AB17].

    +
  14. +
+

Regrettably, Balloon has not yet been sufficiently investigated for generic parameter recommendations to be made. This is also difficult given how various cryptographic hash functions can be used.

+

In all cases, it is RECOMMENDED to use a 128- or 256-bit salt. Other salt lengths SHOULD NOT be used, and the salt length SHOULD NOT vary in your protocol/application. See the Section 10 section for guidance on generating the salt.

+
+
+
+
+

+8. Encoding Password Hashes +

+

To store Balloon hashes in a database as strings, the following format SHOULD be used:

+
+
+$balloon-hash$v=version$m=spaceCost,t=timeCost,p=parallelism$salt$hash
+
+
+ +

Here is an example encoded hash:

+
+
+$balloon-sha-256$v=1$m=1024,t=3,p=0$ZXhhbXBsZXNhbHQ$cWBD3/d3tEqnuI3LqxLAeKvs+snSicW1GVlnqmNEDfs
+
+
+
+
+
+
+

+9. Performing Key Derivation +

+

As Balloon produces outputs the size of the hash function output length, there is a limit on the size of derived keys. To derive larger/multiple keys, the output can be fed into a KDF, like HKDF-Expand [RFC5869], or an XOF, like SHAKE [FIPS202]. In both cases, domain separation SHOULD be specified, such as the protocol/application name and purpose of the derived key.

+

When the Balloon output is too large, it can be truncated to the required key size by taking the first portion of the output. For example, taking the first 256 bits of a 512-bit output. Alternatively, a KDF or XOF can be used, as discussed above.

+
+
+
+
+

+10. Security Considerations +

+

Balloon has been proven sequentially memory-hard in the random-oracle model and uses a password-independent memory access pattern to prevent side-channel attacks leaking information about the password [BCS16]. However, no function that uses a password-independent memory access pattern can be optimally memory-hard in the parallel setting [AB16]. In other words, Balloon is inherently weaker against parallel attacks.

+

To improve Balloon’s resistance to parallel attacks, the output can be fed into a password hashing function with a password-dependent memory access pattern, such as scrypt [RFC7914]. The cost of this approach is like increasing the timeCost of Balloon [BCS16]. However, even this does not defend against an attacker who can both a) obtain memory access pattern information and b) perform a massively parallel attack; it only protects against the two attacks separately.

+

The security properties of Balloon depend on the chosen collision-resistant hash function/XOF. For example, a 256-bit hash should provide 128-bit collision resistance and 256-bit (second) preimage resistance. Technically, only preimage resistance is required for password hashing to prevent the attacker learning information about the password from the hash. However, non-collision-resistant hash functions (e.g. MD5 [RFC6151] and SHA-1 [RFC6194]) MUST NOT be used. Such functions are cryptographically weak and unsuitable for new protocols.

+

If possible, store the password in protected memory and/or erase the password from memory once it is no longer required. Otherwise, an attacker may be able to recover the password from memory or the disk.

+

The salt MUST be unique. It SHOULD be randomly generated using a cryptographically secure pseudorandom number generator (CSPRNG). However, it MAY be deterministic and predictable if random generation is not possible. It SHOULD be at least 128 bits long and SHOULD NOT exceed 256 bits.

+

The spaceCost, timeCost, parallelism, and delta MUST be carefully chosen to avoid denial-of-service and user frustration whilst ensuring adequate protection against password cracking. Similarly, systems MUST check for overly large user-specified parameters (e.g. passwords) to prevent denial-of-service attacks.

+

Avoid using hardcoded parameters (e.g. spaceCost/timeCost) when performing password hashing; these SHOULD be stored as part of the password hash, as described in Section 8. With key derivation, hardcoded parameters are acceptable if protocol versioning is used.

+

For password hashing, it is RECOMMENDED to encrypt password hashes using an authenticated encryption with associated data (AEAD) scheme [RFC5116] before storage. This forces an attacker to compromise the key, which is stored separately from the database, as well as the database before they can begin password cracking. If the key is compromised but the database is not, it can be rotated without having to reset any passwords.

+

For key derivation, one can use a pepper (e.g. a key file) with a keyed hash function, like HMAC [RFC2104], on the password prior to calling Balloon for additional security. It is RECOMMENDED to use a 256-bit pepper.

+

If performing key derivation for password-based encryption with a non-committing AEAD scheme, be aware of partitioning oracle attacks, which can significantly speed up password guessing [LGR21]. These are relevant when a server that knows the key (an oracle) performs password-based decryption for ciphertexts you send and leaks whether decryption was successful (e.g. via an error message or timing side-channel).

+

Unlike password hashing algorithms such as bcrypt [PM99], which perform many small pseudorandom reads, Balloon is not cache-hard. Whilst there are no known publications on cache-hardness at the time of writing, it is reported to provide better GPU/ASIC resistance than memory-hardness for shorter delays (e.g. < 1000 ms). In such cases, memory bandwidth and CPU cache sizes are bigger bottlenecks than total memory. This makes cache-hard algorithms ideal for authentication scenarios but potentially less suited for key derivation.

+

Third-party analysis for Balloon can be found in [RD16], [AB17], [ABP17], and [RD17]. However, note that there are multiple versions of Balloon, and none of these papers have analysed the version used in real-world implementations.

+
+
+
+
+

+11. IANA Considerations +

+

This document has no IANA actions.

+
+
+
+

+12. References +

+
+
+

+12.1. Normative References +

+
+
[FIPS202]
+
+National Institute of Standards and Technology, "FIPS PUB 202 - SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions", , <https://doi.org/10.6028/NIST.FIPS.202>.
+
+
[RFC2119]
+
+Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/rfc/rfc2119>.
+
+
[RFC4648]
+
+Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, DOI 10.17487/RFC4648, , <https://www.rfc-editor.org/rfc/rfc4648>.
+
+
[RFC5869]
+
+Krawczyk, H. and P. Eronen, "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)", RFC 5869, DOI 10.17487/RFC5869, , <https://www.rfc-editor.org/rfc/rfc5869>.
+
+
[RFC6234]
+
+Eastlake 3rd, D. and T. Hansen, "US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)", RFC 6234, DOI 10.17487/RFC6234, , <https://www.rfc-editor.org/rfc/rfc6234>.
+
+
[RFC7693]
+
+Saarinen, M., Ed. and J. Aumasson, "The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC)", RFC 7693, DOI 10.17487/RFC7693, , <https://www.rfc-editor.org/rfc/rfc7693>.
+
+
[RFC8174]
+
+Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/rfc/rfc8174>.
+
+
+
+
+
+
+

+12.2. Informative References +

+
+
[AB16]
+
+Alwen, J. and J. Blocki, "Efficiently Computing Data-Independent Memory-Hard Functions", Advances in Cryptology – CRYPTO 2016. CRYPTO 2016. Lecture Notes in Computer Science(), vol 9815, pp. 241–271, , <https://doi.org/10.1007/978-3-662-53008-5_9>.
+
+
[AB17]
+
+Alwen, J. and J. Blocki, "Towards Practical Attacks on Argon2i and Balloon Hashing", 2017 IEEE European Symposium on Security and Privacy (EuroS&P), Paris, France, 2017, pp. 142-157, , <https://doi.org/10.1109/EuroSP.2017.47>.
+
+
[ABP17]
+
+Alwen, J., Blocki, J., and K. Pietrzak, "Depth-Robust Graphs and Their Cumulative Memory Complexity", Advances in Cryptology – EUROCRYPT 2017. EUROCRYPT 2017. Lecture Notes in Computer Science(), vol 10212, pp. 3–32, , <https://doi.org/10.1007/978-3-319-56617-7_1>.
+
+
[BCS16]
+
+Boneh, D., Corrigan-Gibbs, H., and S. Schechter, "Balloon Hashing: A Memory-Hard Function Providing Provable Protection Against Sequential Attacks", Cryptology ePrint Archive, Paper 2016/027, , <https://eprint.iacr.org/2016/027>.
+
+
[LGR21]
+
+Len, J., Grubbs, P., and T. Ristenpart, "Partitioning Oracle Attacks", 30th USENIX Security Symposium (USENIX Security 21), pp. 195–212, , <https://www.usenix.org/conference/usenixsecurity21/presentation/len>.
+
+
[PM99]
+
+Provos, N. and D. Mazières, "A Future-Adaptable Password Scheme", Proceedings of the 1999 USENIX Annual Technical Conference, , <https://www.usenix.org/legacy/publications/library/proceedings/usenix99/provos/provos.pdf>.
+
+
[RD16]
+
+Ren, L. and S. Devadas, "Proof of Space from Stacked Expanders", Theory of Cryptography. TCC 2016. Lecture Notes in Computer Science(), vol 9985, pp. 262–285, , <https://doi.org/10.1007/978-3-662-53641-4_11>.
+
+
[RD17]
+
+Ren, L. and S. Devadas, "Bandwidth Hard Functions for ASIC Resistance", Theory of Cryptography. TCC 2017. Lecture Notes in Computer Science(), vol 10677, pp. 466–492, , <https://doi.org/10.1007/978-3-319-70500-2_16>.
+
+
[RFC2104]
+
+Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-Hashing for Message Authentication", RFC 2104, DOI 10.17487/RFC2104, , <https://www.rfc-editor.org/rfc/rfc2104>.
+
+
[RFC5116]
+
+McGrew, D., "An Interface and Algorithms for Authenticated Encryption", RFC 5116, DOI 10.17487/RFC5116, , <https://www.rfc-editor.org/rfc/rfc5116>.
+
+
[RFC6151]
+
+Turner, S. and L. Chen, "Updated Security Considerations for the MD5 Message-Digest and the HMAC-MD5 Algorithms", RFC 6151, DOI 10.17487/RFC6151, , <https://www.rfc-editor.org/rfc/rfc6151>.
+
+
[RFC6194]
+
+Polk, T., Chen, L., Turner, S., and P. Hoffman, "Security Considerations for the SHA-0 and SHA-1 Message-Digest Algorithms", RFC 6194, DOI 10.17487/RFC6194, , <https://www.rfc-editor.org/rfc/rfc6194>.
+
+
[RFC7914]
+
+Percival, C. and S. Josefsson, "The scrypt Password-Based Key Derivation Function", RFC 7914, DOI 10.17487/RFC7914, , <https://www.rfc-editor.org/rfc/rfc7914>.
+
+
[RFC9106]
+
+Biryukov, A., Dinu, D., Khovratovich, D., and S. Josefsson, "Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications", RFC 9106, DOI 10.17487/RFC9106, , <https://www.rfc-editor.org/rfc/rfc9106>.
+
+
[SP800-63B]
+
+Grassi, P., Newton, E., Fenton, J., Perlner, R., Regenscheid, A., Burr, W., Richer, J., Lefkovitz, N., Danker, J., Choong, Y., Greene, K., and M. Theofanos, "NIST SP 800-63B - Digital Identity Guidelines: Authentication and Lifecycle Management", , <https://doi.org/10.6028/NIST.SP.800-63b>.
+
+
+
+
+
+
+
+

+Appendix A. Test Vectors +

+
+
+

+A.1. Balloon-SHA-256 +

+
+
+

+A.1.1. Test Vector 1 +

+
+
+password: 70617373776f7264
+
+salt: 73616c74
+
+spaceCost: 1
+
+timeCost: 1
+
+delta: 3
+
+hash: eefda4a8a75b461fa389c1dcfaf3e9dfacbc26f81f22e6f280d15cc18c417545
+
+
+
+
+
+
+

+A.1.2. Test Vector 2 +

+
+
+password: 68756e7465723432
+
+salt: 6578616d706c6573616c74
+
+spaceCost: 1024
+
+timeCost: 3
+
+delta: 3
+
+hash: 716043dff777b44aa7b88dcbab12c078abecfac9d289c5b5195967aa63440dfb
+
+
+
+
+
+
+

+A.1.3. Test Vector 3 +

+
+
+password: 70617373776f7264
+
+salt:
+
+spaceCost: 3
+
+timeCost: 3
+
+delta: 3
+
+hash: 20aa99d7fe3f4df4bd98c655c5480ec98b143107a331fd491deda885c4d6a6cc
+
+
+
+
+
+
+
+
+

+A.2. Balloon-M-SHA-256 +

+
+
+

+A.2.1. Test Vector 1 +

+
+
+password: 70617373776f7264
+
+salt: 73616c74
+
+spaceCost: 1
+
+timeCost: 1
+
+parallelism: 1
+
+delta: 3
+
+hash: 97a11df9382a788c781929831d409d3599e0b67ab452ef834718114efdcd1c6d
+
+
+
+
+
+
+

+A.2.2. Test Vector 2 +

+
+
+password: 70617373776f7264
+
+salt: 73616c74
+
+spaceCost: 1
+
+timeCost: 1
+
+parallelism: 16
+
+delta: 3
+
+hash: a67b383bb88a282aef595d98697f90820adf64582a4b3627c76b7da3d8bae915
+
+
+
+
+
+
+

+A.2.3. Test Vector 3 +

+
+
+password: 68756e7465723432
+
+salt: 6578616d706c6573616c74
+
+spaceCost: 1024
+
+timeCost: 3
+
+parallelism: 4
+
+delta: 3
+
+hash: 1832bd8e5cbeba1cb174a13838095e7e66508e9bf04c40178990adbc8ba9eb6f
+
+
+
+
+
+
+

+A.2.4. Test Vector 4 +

+
+
+password:
+
+salt: 73616c74
+
+spaceCost: 3
+
+timeCost: 3
+
+parallelism: 2
+
+delta: 3
+
+hash: f8767fe04059cef67b4427cda99bf8bcdd983959dbd399a5e63ea04523716c23
+
+
+
+
+
+
+
+
+
+
+

+Acknowledgments +

+

Balloon was designed by Dan Boneh, Henry Corrigan-Gibbs, and Stuart Schechter.

+

Thank you to the Rust Crypto contributors for making Balloon implementations interoperable.

+
+
+
+
+

+Author's Address +

+
+
Samuel Lucas
+
Individual Contributor
+ +
+
+
+ + + diff --git a/draft-lucas-balloon-hashing.txt b/draft-lucas-balloon-hashing.txt new file mode 100644 index 0000000..b19c843 --- /dev/null +++ b/draft-lucas-balloon-hashing.txt @@ -0,0 +1,766 @@ + + + + +Network Working Group S. Lucas +Internet-Draft Individual Contributor +Intended status: Informational 31 December 2023 +Expires: 3 July 2024 + + + Balloon Hashing + draft-lucas-balloon-hashing-latest + +Abstract + + This document describes Balloon, a memory-hard function suitable for + password hashing and password-based key derivation. It has proven + memory-hardness properties, is built from any standard cryptographic + hash function or extendable-output function (XOF), is resistant to + cache-timing attacks, and is easy to implement whilst remaining + performant. + +About This Document + + This note is to be removed before publishing as an RFC. + + The latest revision of this draft can be found at https://samuel- + lucas6.github.io/draft-lucas-balloon-hashing/draft-lucas-balloon- + hashing.html. Status information for this document may be found at + https://datatracker.ietf.org/doc/draft-lucas-balloon-hashing/. + + Source for this draft and an issue tracker can be found at + https://github.com/samuel-lucas6/draft-lucas-balloon-hashing. + +Status of This Memo + + This Internet-Draft is submitted in full conformance with the + provisions of BCP 78 and BCP 79. + + Internet-Drafts are working documents of the Internet Engineering + Task Force (IETF). Note that other groups may also distribute + working documents as Internet-Drafts. The list of current Internet- + Drafts is at https://datatracker.ietf.org/drafts/current/. + + Internet-Drafts are draft documents valid for a maximum of six months + and may be updated, replaced, or obsoleted by other documents at any + time. It is inappropriate to use Internet-Drafts as reference + material or to cite them other than as "work in progress." + + This Internet-Draft will expire on 3 July 2024. + +Copyright Notice + + Copyright (c) 2023 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents (https://trustee.ietf.org/ + license-info) in effect on the date of publication of this document. + Please review these documents carefully, as they describe your rights + and restrictions with respect to this document. + +Table of Contents + + 1. Introduction + 2. Conventions and Definitions + 3. The Balloon Function + 4. The Balloon-M Function + 5. Implementation Considerations + 6. Choosing the Hash Function + 7. Choosing the Parameters + 8. Encoding Password Hashes + 9. Performing Key Derivation + 10. Security Considerations + 11. IANA Considerations + 12. References + 12.1. Normative References + 12.2. Informative References + Appendix A. Test Vectors + A.1. Balloon-SHA-256 + A.1.1. Test Vector 1 + A.1.2. Test Vector 2 + A.1.3. Test Vector 3 + A.2. Balloon-M-SHA-256 + A.2.1. Test Vector 1 + A.2.2. Test Vector 2 + A.2.3. Test Vector 3 + A.2.4. Test Vector 4 + Acknowledgments + Author's Address + +1. Introduction + + Balloon [BCS16] is a memory-hard password hashing and password-based + key derivation function that was published shortly after the Password + Hashing Competition (PHC), which recommended Argon2 [RFC9106]. It is + mentioned in a NIST Special Publication on authentication [SP800-63B] + and has several advantages over prior password hashing algorithms: + + * It has proven memory-hardness properties, making it resistant + against sequential GPU/ASIC attacks. An adversary trying to save + space pays a large penalty in computation time. + + * It can be instantiated with any cryptographic hash function or + XOF, making it a mode of operation for these existing algorithms. + + * It uses a password-independent memory access pattern, making it + resistant to cache-timing attacks. This property is especially + relevant in cloud computing environments where multiple users can + share the same physical machine. + + * It is easy to implement whilst offering acceptable performance. + + Unfortunately, the paper did not fully specify the algorithm nor + provide guidance on parameters. Therefore, this document aims to + rectify the situation based on existing interoperable + implementations, real-world password hashing guidance, and research + since the paper. + +2. Conventions and Definitions + + The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, + “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and + “OPTIONAL” in this document are to be interpreted as described in + BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all + capitals, as shown here. + + Throughout this document, “byte” refers to the same unit as “octet”, + namely an 8-bit sequence. + + Operations: + + * x++: incrementing the integer x by 1 after it has been used in a + function. + + * a ^ b: the bitwise XOR of a and b. + + * a % b: the remainder when dividing a by b. + + * a || b: the concatenation of a and b. + + * a[i]: index ‘i’ of byte array/list ‘a’. + + * a.Length: the length of a in bytes. + + * a.Slice(i, l): the copy of l bytes from byte array a, starting at + index i. + + * List(i, l): the creation of a new list containing i byte arrays, + each with length l. + + * Hash(a): collision-resistant hashing of the byte array a. + + * BI(a): the conversion of byte array a into an unsigned, little- + endian BigInteger. + + * INT32(x): the casting of BigInteger x into a signed 32-bit + integer. + + * LE64(x): the little-endian encoding of unsigned 64-bit integer x. + + Constants: + + * HASH_LEN: the output length of the hash function in bytes. For an + XOF, this is the minimum output length to obtain the maximum + advertised security level. For example, a 256-bit output for an + XOF targeting 128-bit security. + + * MIN_SPACECOST: the minimum space cost, which is 1. + + * MIN_TIMECOST: the minimum time cost, which is 1. + + * MIN_PARALLELISM: the minimum parallelism, which is 1. + + * MIN_DELTA: the minimum delta, which is 3. + +3. The Balloon Function + + Balloon(password, salt, spaceCost, timeCost, delta) + + The Balloon function can be divided into three steps: + + 1. Expand: a large buffer is filled with pseudorandom bytes derived + by repeatedly hashing the password and salt. This buffer is + divided into blocks the size of the hash function output length. + + 2. Mix: the buffer is mixed for the number of rounds specified by + the user. Each block becomes equal to the hash of the previous + block, the current block, and delta other blocks ‘randomly’ + chosen from the buffer based on the salt. + + 3. Extract: the last block of the buffer is output as the password + hash/derived key. + + Inputs: + + * password: the password to be hashed. + + * salt: the unique salt. + + * spaceCost: the memory size in blocks, which MUST be at least + MIN_SPACECOST. A block is the size of the hash function output + length in bytes. + + * timeCost: the number of rounds, which MUST be at least + MIN_TIMECOST. + + * delta: the number of dependencies per block (a security + parameter), which MUST be at least MIN_DELTA. + + Outputs: + + * The password hash/derived key the size of the hash function output + length. + + Steps: + +buffer = List(spaceCost, HASH_LEN) +counter = 0 + +buffer[0] = Hash(LE64(counter++) || password || salt) +for m = 1 to spaceCost - 1 + buffer[m] = Hash(LE64(counter++) || buffer[m - 1]) + +for t = 0 to timeCost - 1 + for m = 0 to spaceCost - 1 + if m == 0 + previous = buffer[spaceCost - 1] + else + previous = buffer[m - 1] + + buffer[m] = Hash(LE64(counter++) || previous || buffer[m]) + + for i = 0 to delta - 1 + idxBlock = Hash(LE64(t) || LE64(m) || LE64(i)) + idxBlock = Hash(LE64(counter++) || salt || idxBlock) + other = BI(idxBlock) % spaceCost + buffer[m] = Hash(LE64(counter++) || buffer[m] || buffer[INT32(other)]) + +return buffer[spaceCost - 1] + +4. The Balloon-M Function + + BalloonM(password, salt, spaceCost, timeCost, parallelism, delta) + + A limitation of Balloon is that it lacks parallelism because the + value of each block depends on the value of the previous block. + Balloon-M addresses this by invoking Balloon in parallel using + multiple cores, XORing the outputs, and hashing the password, salt, + and XORed output concatenated together. This provides greater memory + hardness without increasing the delay. + + Inputs: + + * password: the password to be hashed. + + * salt: the unique salt. + + * spaceCost: the memory size in blocks, which MUST be at least + MIN_SPACECOST. A block is the size of the hash function output + length in bytes. + + * timeCost: the number of rounds, which MUST be at least + MIN_TIMECOST. + + * parallelism: the number of CPU cores/Balloon calls in parallel, + which MUST be at least MIN_PARALLELISM. + + * delta: the number of dependencies per block (a security + parameter), which MUST be at least MIN_DELTA. + + Outputs: + + * The password hash/derived key the size of the hash function output + length. + + Steps: + +outputs = List(parallelism, HASH_LEN) + +parallel for i = 0 to parallelism - 1 + newSalt = salt || LE64(i + 1) + outputs[i] = Balloon(password, newSalt, spaceCost, timeCost, delta) + +foreach output in outputs + for i = 0 to output.Length - 1 + hash[i] = hash[i] ^ output[i] + +return Hash(password || salt || hash) + +5. Implementation Considerations + + In the context of a cryptographic library, it is RECOMMENDED to only + provide users access to Balloon-M. Otherwise, users will be left + wondering which variant to use and may incorrectly believe Balloon-M + with parallelism = 1 is equivalent to Balloon. + + Implementations MAY hardcode delta = MIN_DELTA to avoid user + confusion since other password hashing algorithms do not have this + parameter. Moreover, the performance/security tradeoff is unclear + from the paper. + + Whilst the pseudocode uses a list of byte arrays for the buffer, + slicing portions of a single large byte array to access/update blocks + will likely be more performant. + + Similarly, using a byte array counter instead of an integer that gets + repeatedly converted to a byte array will likely aid performance. + + With Balloon-M, the XORing of outputs can be skipped if parallelism = + 1. + + Finally, it is recommended to use an incremental hash function API + rather than manually copying byte arrays to concatenate inputs as + this is cleaner and may be more efficient. + +6. Choosing the Hash Function + + The choice of cryptographic hash function/XOF affects the performance + and security of Balloon in two ways: + + 1. For the same parameters, the attacker has an advantage if the + algorithm is faster in hardware versus software. They will be + able to do the computation in less time than the defender. + + 2. For the same delay, the defender will be forced to use smaller + parameters with a slower cryptographic hash function/XOF in + software. Using a faster algorithm in software means stronger + parameters can be used. + + It is RECOMMENDED to use a cryptographic hash function/XOF that is + fast in software but relatively slow in hardware, such as BLAKE2b + [RFC7693]. As another example, SHA-512 is preferable to SHA-256 + [RFC6234]. Finally, SHA-3 [FIPS202] is NOT RECOMMENDED as it is + slower in software compared to in hardware. + +7. Choosing the Parameters + + The higher the spaceCost, timeCost, and delta, the longer it takes to + compute an output. If these values are too small, security is + unnecessarily reduced. If they are too large, there is a risk of + user frustration and denial-of-service for different types of user + devices and servers. To make matters even more complicated, these + parameters may need to be increased over time as hardware gets + faster. + + The following procedure can be used to choose parameters: + + 1. If configurable in the implementation, set delta to 3. This + value is used in the paper [BCS16]. + + 2. If using Balloon-M, set the parallelism to 1 on a server and 4 + otherwise. This assumes most user devices have at least 4 CPU + cores. + + 3. Establish the maximum acceptable delay for the user. For + example, 100-500 ms for authentication, 250-1000 ms for file + encryption, and 1000-5000 ms for disk encryption. On servers, + you also need to factor in the maximum number of authentication + attempts per second. + + 4. Determine the maximum amount of memory available, taking into + account different types of user devices and denial-of-service. + For instance, mobile phones versus laptops/desktops. + + 5. Convert the MiB/GiB memory size to bytes. Then set spaceCost to + bytes / HASH_LEN, which is the number of blocks. + + 6. Find the timeCost that brings you closest to the maximum + acceptable delay or target number of authentication attempts per + second by running benchmarks. + + 7. If timeCost is only 1, reduce spaceCost to be able to increase + timeCost. Performing multiple rounds is beneficial for security + [AB17]. + + Regrettably, Balloon has not yet been sufficiently investigated for + generic parameter recommendations to be made. This is also difficult + given how various cryptographic hash functions can be used. + + In all cases, it is RECOMMENDED to use a 128- or 256-bit salt. Other + salt lengths SHOULD NOT be used, and the salt length SHOULD NOT vary + in your protocol/application. See the Section 10 section for + guidance on generating the salt. + +8. Encoding Password Hashes + + To store Balloon hashes in a database as strings, the following + format SHOULD be used: + + $balloon-hash$v=version$m=spaceCost,t=timeCost,p=parallelism$salt$hash + + * balloon-hash: where hash is the proper, correctly punctuated name + of the hash function in lowercase. For example, sha-256, not + sha256. Another example is sha3-256, not sha3256 or sha-3-256. + Finally, sha-512-256 for SHA-512 truncated to 256 bits, and sha- + 512/256 for the SHA-512/256 algorithm. + + * v=version: this is version 1 of Balloon. If the design is + modified, the version will be incremented. + + * m=spaceCost: the memory size in blocks, not KiB. + + * t=timeCost: the number of rounds. + + * p=parallelism: 0 if Balloon and 1+ for Balloon-M. + + * salt: the salt encoded in Base64 with no padding [RFC4648]. + + * hash: the full/untruncated Balloon output encoded in Base64 with + no padding [RFC4648]. + + Here is an example encoded hash: + +$balloon-sha-256$v=1$m=1024,t=3,p=0$ZXhhbXBsZXNhbHQ$cWBD3/d3tEqnuI3LqxLAeKvs+snSicW1GVlnqmNEDfs + +9. Performing Key Derivation + + As Balloon produces outputs the size of the hash function output + length, there is a limit on the size of derived keys. To derive + larger/multiple keys, the output can be fed into a KDF, like HKDF- + Expand [RFC5869], or an XOF, like SHAKE [FIPS202]. In both cases, + domain separation SHOULD be specified, such as the protocol/ + application name and purpose of the derived key. + + When the Balloon output is too large, it can be truncated to the + required key size by taking the first portion of the output. For + example, taking the first 256 bits of a 512-bit output. + Alternatively, a KDF or XOF can be used, as discussed above. + +10. Security Considerations + + Balloon has been proven sequentially memory-hard in the random-oracle + model and uses a password-independent memory access pattern to + prevent side-channel attacks leaking information about the password + [BCS16]. However, no function that uses a password-independent + memory access pattern can be optimally memory-hard in the parallel + setting [AB16]. In other words, Balloon is inherently weaker against + parallel attacks. + + To improve Balloon’s resistance to parallel attacks, the output can + be fed into a password hashing function with a password-dependent + memory access pattern, such as scrypt [RFC7914]. The cost of this + approach is like increasing the timeCost of Balloon [BCS16]. + However, even this does not defend against an attacker who can both + a) obtain memory access pattern information and b) perform a + massively parallel attack; it only protects against the two attacks + separately. + + The security properties of Balloon depend on the chosen collision- + resistant hash function/XOF. For example, a 256-bit hash should + provide 128-bit collision resistance and 256-bit (second) preimage + resistance. Technically, only preimage resistance is required for + password hashing to prevent the attacker learning information about + the password from the hash. However, non-collision-resistant hash + functions (e.g. MD5 [RFC6151] and SHA-1 [RFC6194]) MUST NOT be used. + Such functions are cryptographically weak and unsuitable for new + protocols. + + If possible, store the password in protected memory and/or erase the + password from memory once it is no longer required. Otherwise, an + attacker may be able to recover the password from memory or the disk. + + The salt MUST be unique. It SHOULD be randomly generated using a + cryptographically secure pseudorandom number generator (CSPRNG). + However, it MAY be deterministic and predictable if random generation + is not possible. It SHOULD be at least 128 bits long and SHOULD NOT + exceed 256 bits. + + The spaceCost, timeCost, parallelism, and delta MUST be carefully + chosen to avoid denial-of-service and user frustration whilst + ensuring adequate protection against password cracking. Similarly, + systems MUST check for overly large user-specified parameters (e.g. + passwords) to prevent denial-of-service attacks. + + Avoid using hardcoded parameters (e.g. spaceCost/timeCost) when + performing password hashing; these SHOULD be stored as part of the + password hash, as described in Section 8. With key derivation, + hardcoded parameters are acceptable if protocol versioning is used. + + For password hashing, it is RECOMMENDED to encrypt password hashes + using an authenticated encryption with associated data (AEAD) scheme + [RFC5116] before storage. This forces an attacker to compromise the + key, which is stored separately from the database, as well as the + database before they can begin password cracking. If the key is + compromised but the database is not, it can be rotated without having + to reset any passwords. + + For key derivation, one can use a pepper (e.g. a key file) with a + keyed hash function, like HMAC [RFC2104], on the password prior to + calling Balloon for additional security. It is RECOMMENDED to use a + 256-bit pepper. + + If performing key derivation for password-based encryption with a + non-committing AEAD scheme, be aware of partitioning oracle attacks, + which can significantly speed up password guessing [LGR21]. These + are relevant when a server that knows the key (an oracle) performs + password-based decryption for ciphertexts you send and leaks whether + decryption was successful (e.g. via an error message or timing side- + channel). + + Unlike password hashing algorithms such as bcrypt [PM99], which + perform many small pseudorandom reads, Balloon is not cache-hard. + Whilst there are no known publications on cache-hardness at the time + of writing, it is reported to provide better GPU/ASIC resistance than + memory-hardness for shorter delays (e.g. < 1000 ms). In such cases, + memory bandwidth and CPU cache sizes are bigger bottlenecks than + total memory. This makes cache-hard algorithms ideal for + authentication scenarios but potentially less suited for key + derivation. + + Third-party analysis for Balloon can be found in [RD16], [AB17], + [ABP17], and [RD17]. However, note that there are multiple versions + of Balloon, and none of these papers have analysed the version used + in real-world implementations. + +11. IANA Considerations + + This document has no IANA actions. + +12. References + +12.1. Normative References + + [FIPS202] National Institute of Standards and Technology, "FIPS PUB + 202 - SHA-3 Standard: Permutation-Based Hash and + Extendable-Output Functions", 2015, + . + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, + DOI 10.17487/RFC2119, March 1997, + . + + [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data + Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006, + . + + [RFC5869] Krawczyk, H. and P. Eronen, "HMAC-based Extract-and-Expand + Key Derivation Function (HKDF)", RFC 5869, + DOI 10.17487/RFC5869, May 2010, + . + + [RFC6234] Eastlake 3rd, D. and T. Hansen, "US Secure Hash Algorithms + (SHA and SHA-based HMAC and HKDF)", RFC 6234, + DOI 10.17487/RFC6234, May 2011, + . + + [RFC7693] Saarinen, M., Ed. and J. Aumasson, "The BLAKE2 + Cryptographic Hash and Message Authentication Code (MAC)", + RFC 7693, DOI 10.17487/RFC7693, November 2015, + . + + [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC + 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, + May 2017, . + +12.2. Informative References + + [AB16] Alwen, J. and J. Blocki, "Efficiently Computing Data- + Independent Memory-Hard Functions", Advances in Cryptology + – CRYPTO 2016. CRYPTO 2016. Lecture Notes in Computer + Science(), vol 9815, pp. 241–271, 2016, + . + + [AB17] Alwen, J. and J. Blocki, "Towards Practical Attacks on + Argon2i and Balloon Hashing", 2017 IEEE European Symposium + on Security and Privacy (EuroS&P), Paris, France, 2017, + pp. 142-157, 2017, + . + + [ABP17] Alwen, J., Blocki, J., and K. Pietrzak, "Depth-Robust + Graphs and Their Cumulative Memory Complexity", Advances + in Cryptology – EUROCRYPT 2017. EUROCRYPT 2017. Lecture + Notes in Computer Science(), vol 10212, pp. 3–32, 2017, + . + + [BCS16] Boneh, D., Corrigan-Gibbs, H., and S. Schechter, "Balloon + Hashing: A Memory-Hard Function Providing Provable + Protection Against Sequential Attacks", Cryptology ePrint + Archive, Paper 2016/027, 2016, + . + + [LGR21] Len, J., Grubbs, P., and T. Ristenpart, "Partitioning + Oracle Attacks", 30th USENIX Security Symposium (USENIX + Security 21), pp. 195–212, 2021, + . + + [PM99] Provos, N. and D. Mazières, "A Future-Adaptable Password + Scheme", Proceedings of the 1999 USENIX Annual Technical + Conference, 1999, . + + [RD16] Ren, L. and S. Devadas, "Proof of Space from Stacked + Expanders", Theory of Cryptography. TCC 2016. Lecture + Notes in Computer Science(), vol 9985, pp. 262–285, 2016, + . + + [RD17] Ren, L. and S. Devadas, "Bandwidth Hard Functions for ASIC + Resistance", Theory of Cryptography. TCC 2017. Lecture + Notes in Computer Science(), vol 10677, pp. 466–492, 2017, + . + + [RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed- + Hashing for Message Authentication", RFC 2104, + DOI 10.17487/RFC2104, February 1997, + . + + [RFC5116] McGrew, D., "An Interface and Algorithms for Authenticated + Encryption", RFC 5116, DOI 10.17487/RFC5116, January 2008, + . + + [RFC6151] Turner, S. and L. Chen, "Updated Security Considerations + for the MD5 Message-Digest and the HMAC-MD5 Algorithms", + RFC 6151, DOI 10.17487/RFC6151, March 2011, + . + + [RFC6194] Polk, T., Chen, L., Turner, S., and P. Hoffman, "Security + Considerations for the SHA-0 and SHA-1 Message-Digest + Algorithms", RFC 6194, DOI 10.17487/RFC6194, March 2011, + . + + [RFC7914] Percival, C. and S. Josefsson, "The scrypt Password-Based + Key Derivation Function", RFC 7914, DOI 10.17487/RFC7914, + August 2016, . + + [RFC9106] Biryukov, A., Dinu, D., Khovratovich, D., and S. + Josefsson, "Argon2 Memory-Hard Function for Password + Hashing and Proof-of-Work Applications", RFC 9106, + DOI 10.17487/RFC9106, September 2021, + . + + [SP800-63B] + Grassi, P., Newton, E., Fenton, J., Perlner, R., + Regenscheid, A., Burr, W., Richer, J., Lefkovitz, N., + Danker, J., Choong, Y., Greene, K., and M. Theofanos, + "NIST SP 800-63B - Digital Identity Guidelines: + Authentication and Lifecycle Management", 2017, + . + +Appendix A. Test Vectors + +A.1. Balloon-SHA-256 + +A.1.1. Test Vector 1 + + password: 70617373776f7264 + + salt: 73616c74 + + spaceCost: 1 + + timeCost: 1 + + delta: 3 + + hash: eefda4a8a75b461fa389c1dcfaf3e9dfacbc26f81f22e6f280d15cc18c417545 + +A.1.2. Test Vector 2 + + password: 68756e7465723432 + + salt: 6578616d706c6573616c74 + + spaceCost: 1024 + + timeCost: 3 + + delta: 3 + + hash: 716043dff777b44aa7b88dcbab12c078abecfac9d289c5b5195967aa63440dfb + +A.1.3. Test Vector 3 + + password: 70617373776f7264 + + salt: + + spaceCost: 3 + + timeCost: 3 + + delta: 3 + + hash: 20aa99d7fe3f4df4bd98c655c5480ec98b143107a331fd491deda885c4d6a6cc + +A.2. Balloon-M-SHA-256 + +A.2.1. Test Vector 1 + + password: 70617373776f7264 + + salt: 73616c74 + + spaceCost: 1 + + timeCost: 1 + + parallelism: 1 + + delta: 3 + + hash: 97a11df9382a788c781929831d409d3599e0b67ab452ef834718114efdcd1c6d + +A.2.2. Test Vector 2 + + password: 70617373776f7264 + + salt: 73616c74 + + spaceCost: 1 + + timeCost: 1 + + parallelism: 16 + + delta: 3 + + hash: a67b383bb88a282aef595d98697f90820adf64582a4b3627c76b7da3d8bae915 + +A.2.3. Test Vector 3 + + password: 68756e7465723432 + + salt: 6578616d706c6573616c74 + + spaceCost: 1024 + + timeCost: 3 + + parallelism: 4 + + delta: 3 + + hash: 1832bd8e5cbeba1cb174a13838095e7e66508e9bf04c40178990adbc8ba9eb6f + +A.2.4. Test Vector 4 + + password: + + salt: 73616c74 + + spaceCost: 3 + + timeCost: 3 + + parallelism: 2 + + delta: 3 + + hash: f8767fe04059cef67b4427cda99bf8bcdd983959dbd399a5e63ea04523716c23 + +Acknowledgments + + Balloon was designed by Dan Boneh, Henry Corrigan-Gibbs, and Stuart + Schechter. + + Thank you to the Rust Crypto contributors for making Balloon + implementations interoperable. + +Author's Address + + Samuel Lucas + Individual Contributor + Email: ietf.tree495@simplelogin.fr diff --git a/index.html b/index.html index e69de29..ae74bf0 100644 --- a/index.html +++ b/index.html @@ -0,0 +1,48 @@ + + + + samuel-lucas6/draft-lucas-balloon-hashing main preview + + + + +

Editor's drafts for main branch of samuel-lucas6/draft-lucas-balloon-hashing

+

View saved issues, or the latest GitHub issues and pull requests in the repo.

+ + + + + + + + +
Balloon Hashingplain textdatatrackerdiff with last submission
+ + +