git/list[1] front-page[2] threads[3] people[4] search[5] about
wed 2026-10-07 18:15 UTC

Re: [PATCH v4] technical doc: add a design doc for hash function transition

From
JCJason Cooper <jason@lakedaemon.net>
Date
Oct 2, 2017, 19:23 UTC
Message-ID
<20171002192333.GH31762@io.lakedaemon.net>
In-Reply-To
<20170928044320.GA84719@aiede.mtv.corp.google.com>
Hi Jonathan,
On Wed, Sep 27, 2017 at 09:43:21PM -0700, Jonathan Nieder wrote:
Show 38 quoted lines
> This document describes what a transition to a new hash function for
> Git would look like.  Add it to Documentation/technical/ as the plan
> of record so that future changes can be recorded as patches.
> 
> Also-by: Brandon Williams <bmwill@google.com>
> Also-by: Jonathan Tan <jonathantanmy@google.com>
> Also-by: Stefan Beller <sbeller@google.com>
> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
> ---
> On Thu, Mar 09, 2017 at 11:14 AM, Shawn Pearce wrote:
> > On Mon, Mar 6, 2017 at 4:17 PM, Jonathan Nieder <jrnieder@gmail.com> wrote:
> 
> >> Thanks for the kind words on what had quite a few flaws still.  Here's
> >> a new draft.  I think the next version will be a patch against
> >> Documentation/technical/.
> >
> > FWIW, I like this approach.
> 
> Okay, here goes.
> 
> Instead of sharding the loose object translation tables by first byte,
> we went for a single table.  It simplifies the design and we need to
> keep the number of loose objects under control anyway.
> 
> We also included a description of the transition plan and tried to
> include a summary of what has been agreed upon so far about the choice
> of hash function.
> 
> Thanks to Junio for reviving the discussion and in particular to Dscho
> for pushing this forward and making the missing pieces clearer.
> 
> Thoughts of all kinds welcome, as always.
> 
>  Documentation/Makefile                             |   1 +
>  .../technical/hash-function-transition.txt         | 797 +++++++++++++++++++++
>  2 files changed, 798 insertions(+)
>  create mode 100644 Documentation/technical/hash-function-transition.txt
> 
...
Show 13 quoted lines
> diff --git a/Documentation/technical/hash-function-transition.txt b/Documentation/technical/hash-function-transition.txt
> new file mode 100644
> index 0000000000..417ba491d0
> --- /dev/null
> +++ b/Documentation/technical/hash-function-transition.txt
> @@ -0,0 +1,797 @@
> +Git hash function transition
> +============================
> +
> +Objective
> +---------
> +Migrate Git from SHA-1 to a stronger hash function.
> +
...
> +Goals
> +-----
> +Where NewHash is a strong 256-bit hash function to replace SHA-1 (see
> +"Selection of a New Hash", below):
Could we clarify and say "a strong hash function with 256-bit output"?
...
Show 15 quoted lines
> +Overview
> +--------
> +We introduce a new repository format extension. Repositories with this
> +extension enabled use NewHash instead of SHA-1 to name their objects.
> +This affects both object names and object content --- both the names
> +of objects and all references to other objects within an object are
> +switched to the new hash function.
> +
> +NewHash repositories cannot be read by older versions of Git.
> +
> +Alongside the packfile, a NewHash repository stores a bidirectional
> +mapping between NewHash and SHA-1 object names. The mapping is generated
> +locally and can be verified using "git fsck". Object lookups use this
> +mapping to allow naming objects using either their SHA-1 and NewHash names
> +interchangeably.

nit: Are we presuming that abbreviated hashes won't collide? Or the user needs to specify which hash type?

Show 14 quoted lines
> +Object format
> +~~~~~~~~~~~~~
> +The content as a byte sequence of a tag, commit, or tree object named
> +by sha1 and newhash differ because an object named by newhash-name refers to
> +other objects by their newhash-names and an object named by sha1-name
> +refers to other objects by their sha1-names.
> +
> +The newhash-content of an object is the same as its sha1-content, except
> +that objects referenced by the object are named using their newhash-names
> +instead of sha1-names. Because a blob object does not refer to any
> +other object, its sha1-content and newhash-content are the same.
> +
> +The format allows round-trip conversion between newhash-content and
> +sha1-content.

It would be nice here to explicitly mention deterministic hashing. Meaning that anyone who converts a commit from sha1 to newhash shall get the same newhash.

Show 23 quoted lines
> +
> +Object storage
> +~~~~~~~~~~~~~~
> +Loose objects use zlib compression and packed objects use the packed
> +format described in Documentation/technical/pack-format.txt, just like
> +today. The content that is compressed and stored uses newhash-content
> +instead of sha1-content.
> +
> +Pack index
> +~~~~~~~~~~
> +Pack index (.idx) files use a new v3 format that supports multiple
> +hash functions. They have the following format (all integers are in
> +network byte order):
> +
> +- A header appears at the beginning and consists of the following:
> +  - The 4-byte pack index signature: '\377t0c'
> +  - 4-byte version number: 3
> +  - 4-byte length of the header section, including the signature and
> +    version number
> +  - 4-byte number of objects contained in the pack
> +  - 4-byte number of object formats in this pack index: 2
> +  - For each object format:
> +    - 4-byte format identifier (e.g., 'sha1' for SHA-1)

This seems a little rough to me. Maybe it would be better to have a 4 byte field where 0x01 = SHA-1, 0x02 = NEWHASH?

Show 18 quoted lines
> +Reading an object's sha1-content
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +The sha1-content of an object can be read by converting all newhash-names
> +its newhash-content references to sha1-names using the translation table.
> +
> +Fetch
> +~~~~~
> +Fetching from a SHA-1 based server requires translating between SHA-1
> +and NewHash based representations on the fly.
> +
> +SHA-1s named in the ref advertisement that are present on the client
> +can be translated to NewHash and looked up as local objects using the
> +translation table.
> +
> +Negotiation proceeds as today. Any "have"s generated locally are
> +converted to SHA-1 before being sent to the server, and SHA-1s
> +mentioned by the server are converted to NewHash when looking them up
> +locally.

By "converted", do you mean "looked up in the table" or "look up newhash, re-calculate sha1, send" ? I presume you mean the former, but it would be good to clarify.

Show 27 quoted lines
> +
> +After negotiation, the server sends a packfile containing the
> +requested objects. We convert the packfile to NewHash format using
> +the following steps:
> +
> +1. index-pack: inflate each object in the packfile and compute its
> +   SHA-1. Objects can contain deltas in OBJ_REF_DELTA format against
> +   objects the client has locally. These objects can be looked up
> +   using the translation table and their sha1-content read as
> +   described above to resolve the deltas.
> +2. topological sort: starting at the "want"s from the negotiation
> +   phase, walk through objects in the pack and emit a list of them,
> +   excluding blobs, in reverse topologically sorted order, with each
> +   object coming later in the list than all objects it references.
> +   (This list only contains objects reachable from the "wants". If the
> +   pack from the server contained additional extraneous objects, then
> +   they will be discarded.)
> +3. convert to newhash: open a new (newhash) packfile. Read the topologically
> +   sorted list just generated. For each object, inflate its
> +   sha1-content, convert to newhash-content, and write it to the newhash
> +   pack. Record the new sha1<->newhash mapping entry for use in the idx.
> +4. sort: reorder entries in the new pack to match the order of objects
> +   in the pack the server generated and include blobs. Write a newhash idx
> +   file
> +5. clean up: remove the SHA-1 based pack file, index, and
> +   topologically sorted list obtained from the server in steps 1
> +   and 2.

How are signed tags (against sha1 commits) to be handled? See below for further thoughts.

Show 12 quoted lines
> +Signed Tags
> +~~~~~~~~~~~
> +We add a new field "gpgsig-newhash" to the tag object format to allow
> +signing tags without relying on SHA-1. Its signed payload is the
> +newhash-content of the tag with its gpgsig-newhash field and "-----BEGIN PGP
> +SIGNATURE-----" delimited in-body signature removed.
> +
> +This means tags can be signed
> +1. using SHA-1 only, as in existing signed tag objects
> +2. using both SHA-1 and NewHash, by using gpgsig-newhash and an in-body
> +   signature.
> +3. using only NewHash, by only using the gpgsig-newhash field.
To be clear here, "gpgsig" = SHA-1, "gpgsig-SHA-256" = SHA-256?
Show 9 quoted lines
> +Caveats
> +-------
> +Invalid objects
> +~~~~~~~~~~~~~~~
> +The conversion from sha1-content to newhash-content retains any
> +brokenness in the original object (e.g., tree entry modes encoded with
> +leading 0, tree objects whose paths are not sorted correctly, and
> +commit objects without an author or committer). This is a deliberate
> +feature of the design to allow the conversion to round-trip.
Ah, so this is part of the deterministic hashing.
Show 10 quoted lines
> +Object names on the command line
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +To support the transition (see Transition plan below), this design
> +supports four different modes of operation:
> +
> + 1. ("dark launch") Treat object names input by the user as SHA-1 and
> +    convert any object names written to output to SHA-1, but store
> +    objects using NewHash.  This allows users to test the code with no
> +    visible behavior change except for performance.  This allows
> +    allows running even tests that assume the SHA-1 hash function, to
nit:  s/allows allows/allows/
Show 17 quoted lines
> +    sanity-check the behavior of the new mode.
> +
> + 2. ("early transition") Allow both SHA-1 and NewHash object names in
> +    input. Any object names written to output use SHA-1. This allows
> +    users to continue to make use of SHA-1 to communicate with peers
> +    (e.g. by email) that have not migrated yet and prepares for mode 3.
> +
> + 3. ("late transition") Allow both SHA-1 and NewHash object names in
> +    input. Any object names written to output use NewHash. In this
> +    mode, users are using a more secure object naming method by
> +    default.  The disruption is minimal as long as most of their peers
> +    are in mode 2 or mode 3.
> +
> + 4. ("post-transition") Treat object names input by the user as
> +    NewHash and write output using NewHash. This is safer than mode 3
> +    because there is less risk that input is incorrectly interpreted
> +    using the wrong hash function.
Surely we can error-out if the provided object name is ambiguous?
Show 23 quoted lines
> +Selection of a New Hash
> +-----------------------
> +In early 2005, around the time that Git was written,  Xiaoyun Wang,
> +Yiqun Lisa Yin, and Hongbo Yu announced an attack finding SHA-1
> +collisions in 2^69 operations. In August they published details.
> +Luckily, no practical demonstrations of a collision in full SHA-1 were
> +published until 10 years later, in 2017.
> +
> +The hash function NewHash to replace SHA-1 should be stronger than
> +SHA-1 was: we would like it to be trustworthy and useful in practice
> +for at least 10 years.
> +
> +Some other relevant properties:
> +
> +1. A 256-bit hash (long enough to match common security practice; not
> +   excessively long to hurt performance and disk usage).
> +
> +2. High quality implementations should be widely available (e.g. in
> +   OpenSSL).
> +
> +3. The hash function's properties should match Git's needs (e.g. Git
> +   requires collision and 2nd preimage resistance and does not require
> +   length extension resistance).

Based on recent discussion, I would add here, that the candidate hash has had sufficient review. Such that the likelihood of overnight catastrophic failure is greatly reduced. This gives git and git users time to migrate away from the now weakening hash function.

Show 6 quoted lines
> +
> +4. As a tiebreaker, the hash should be fast to compute (fortunately
> +   many contenders are faster than SHA-1).
> +
> +Some hashes under consideration are SHA-256, SHA-512/256, SHA-256x16,
> +K12, and BLAKE2bp-256.

If anyone is counting votes, I prefer either SHA-512/256 or BLAKE2bp-256. But as I've mentioned elsewhere, it's only a preference.

> +
> +Transition plan
> +---------------
...
Show 11 quoted lines
> +Once a critical mass of users have upgraded to a version of Git that
> +can verify NewHash signatures and have converted their existing
> +repositories to support verifying them, we can add support for a
> +setting to generate only NewHash signatures. This is expected to be at
> +least a year later.
> +
> +That is also a good moment to advertise the ability to convert
> +repositories to use NewHash only, stripping out all SHA-1 related
> +metadata. This improves performance by eliminating translation
> +overhead and security by avoiding the possibility of accidentally
> +relying on the safety of SHA-1.

There is a caveat here regarding old signatures. Those have value and shouldn't be lost. repos needing to prove the validity of the old sha1-only signatures should counter-hash all objects, and then counter-sign the corresponding newhash version of the original sha1-only tags.

Reviewed-by: Jason Cooper <jason@lakedaemon.net>
thx,
Jason.
Previous: Linus TorvaldsNext: Jeff King
Message 108 of 113 in “RFC: Another proposed hash function transition plan”
  1. Jonathan NiederMar 4, 2017
  2. Linus TorvaldsMar 5, 2017
  3. David LangMar 5, 2017
  4. brian m. carlsonMar 6, 2017
  5. Jeff KingMar 6, 2017
  6. Jeff KingMar 6, 2017
  7. Brandon WilliamsMar 6, 2017
  8. Junio C HamanoMar 6, 2017
  9. Jonathan TanMar 6, 2017
  10. Linus TorvaldsMar 6, 2017
  11. Brandon WilliamsMar 6, 2017
  12. Junio C HamanoMar 6, 2017
  13. Jonathan NiederMar 6, 2017
  14. RFC v3: Another proposed hash function transition planJonathan Nieder, Mar 7, 2017
  15. Mike HommeyMar 7, 2017
  16. Jeff KingMar 7, 2017
  17. Linus TorvaldsMar 7, 2017
  18. Ian JacksonMar 7, 2017
  19. Ian JacksonMar 8, 2017
  20. Johannes SchindelinMar 8, 2017
  21. Johannes SchindelinMar 8, 2017
  22. Shawn PearceMar 9, 2017
  23. Jonathan NiederMar 9, 2017
  24. Jeff KingMar 10, 2017
  25. Jonathan NiederMar 10, 2017
  26. The Keccak TeamMar 13, 2017
  27. Jonathan NiederMar 13, 2017
  28. ankostisMar 13, 2017
  29. Johannes SchindelinMar 17, 2017
  30. Use base32?Jason Hennessey, Mar 20, 2017
  31. Michael SteuerMar 20, 2017
  32. Jacob KellerMar 20, 2017
  33. Michael SteuerMar 21, 2017
  34. Which hash function to use, was Re: RFC: Another proposed hash function transition planJohannes Schindelin, Jun 15, 2017
  35. Mike HommeyJun 15, 2017
  36. Jeff KingJun 15, 2017
  37. Ævar Arnfjörð BjarmasonJun 15, 2017
  38. Brandon WilliamsJun 15, 2017
  39. Jonathan NiederJun 15, 2017
  40. Junio C HamanoJun 15, 2017
  41. Johannes SchindelinJun 15, 2017
  42. Mike HommeyJun 15, 2017
  43. Adam LangleyJun 15, 2017
  44. brian m. carlsonJun 15, 2017
  45. Ævar Arnfjörð BjarmasonJun 15, 2017
  46. brian m. carlsonJun 16, 2017
  47. Jeff KingJun 16, 2017
  48. Ævar Arnfjörð BjarmasonJun 16, 2017
  49. Johannes SchindelinJun 16, 2017
  50. Adam LangleyJun 16, 2017
  51. Jeff KingJun 16, 2017
  52. Junio C HamanoJun 16, 2017
  53. Junio C HamanoJun 16, 2017
  54. Jonathan NiederJun 16, 2017
  55. Ævar Arnfjörð BjarmasonJun 16, 2017
  56. Johannes SchindelinJun 19, 2017
  57. Junio C HamanoSep 6, 2017
  58. Junio C HamanoSep 8, 2017
  59. Jeff KingSep 8, 2017
  60. Brandon WilliamsSep 11, 2017
  61. Johannes SchindelinSep 13, 2017
  62. demerphqSep 13, 2017
  63. Jonathan NiederSep 13, 2017
  64. Junio C HamanoSep 13, 2017
  65. Stefan BellerSep 13, 2017
  66. Junio C HamanoSep 13, 2017
  67. Jonathan NiederSep 13, 2017
  68. Jonathan NiederSep 13, 2017
  69. Jonathan NiederSep 13, 2017
  70. Linus TorvaldsSep 13, 2017
  71. Junio C HamanoSep 14, 2017
  72. Junio C HamanoSep 14, 2017
  73. Johannes SchindelinSep 14, 2017
  74. Johannes SchindelinSep 14, 2017
  75. demerphqSep 14, 2017
  76. Brandon WilliamsSep 14, 2017
  77. Johannes SchindelinSep 14, 2017
  78. Jonathan NiederSep 14, 2017
  79. Johannes SchindelinSep 14, 2017
  80. Jonathan NiederSep 14, 2017
  81. Johannes SchindelinSep 14, 2017
  82. Johannes SchindelinSep 14, 2017
  83. Philip OakleySep 15, 2017
  84. Gilles Van AsscheSep 18, 2017
  85. Johannes SchindelinSep 18, 2017
  86. Jonathan NiederSep 18, 2017
  87. Gilles Van AsscheSep 19, 2017
  88. Jason CooperSep 26, 2017
  89. Johannes SchindelinSep 26, 2017
  90. technical doc: add a design doc for hash function transitionStefan Beller, Sep 26, 2017
  91. Jonathan NiederSep 26, 2017
  92. Jonathan NiederSep 26, 2017
  93. technical doc: add a design doc for hash function transitionJonathan Nieder, Sep 28, 2017
  94. Junio C HamanoSep 29, 2017
  95. Junio C HamanoSep 29, 2017
  96. Johannes SchindelinSep 29, 2017
  97. Joan DaemenSep 29, 2017
  98. Jonathan NiederSep 29, 2017
  99. Johannes SchindelinSep 29, 2017
  100. Joan DaemenSep 30, 2017
  101. Junio C HamanoOct 2, 2017
  102. Junio C HamanoOct 2, 2017
  103. Jason CooperOct 2, 2017
  104. Johannes SchindelinOct 2, 2017
  105. Jason CooperOct 2, 2017
  106. Brandon WilliamsOct 2, 2017
  107. Linus TorvaldsOct 2, 2017
  108. Jason CooperOct 2, 2017
  109. Jeff KingOct 2, 2017
  110. Jason CooperOct 2, 2017
  111. Junio C HamanoOct 3, 2017
  112. Jason CooperOct 3, 2017
  113. Junio C HamanoOct 4, 2017

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.