Skip to content

Make poly1305 support incremental computation + modernize #27993

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 17, 2023

Conversation

sipa
Copy link
Member

@sipa sipa commented Jun 28, 2023

Our current Poly1305 code (src/crypto/poly1305.*) only supports computing the entire tag in one go (the poly1305_auth function takes a key and message, and outputs the tag). However, the RFC8439 authenticated encryption (as used in BIP324, see #27634) scheme makes use of Poly1305 in a way where the message consists of 3 different pieces:

  • The additionally authenticated data (AAD), padded to 16 bytes.
  • The ciphertext, padded to 16 bytes.
  • The length of the AAD and the length of the ciphertext, together another 16 bytes.

Implementing RFC8439 using the existing poly1305_auth function requires creating a temporary copy with all these pieces of data concatenated just for the purpose of computing the tag (the approach used in #25361).

This PR replaces the poly1305 code with new code from https://github.com/floodyberry/poly1305-donna (with minor adjustments to make it match our coding style and use our utility functions, documented in the commit) which supports incremental operation, and then adds a C++ wrapper interface using std::byte Spans around it, and adds tests that incremental and all-at-once computation match.

@DrahtBot
Copy link
Contributor

DrahtBot commented Jun 28, 2023

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK theStack, stratospher, achow101

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

No conflicts as of last run.

@sipa sipa mentioned this pull request Jun 27, 2023
43 tasks
@sipa sipa force-pushed the 202306_poly1305 branch from ae4f35b to 5ebfa8a Compare June 29, 2023 15:40
@sipa sipa force-pushed the 202306_poly1305 branch from 5ebfa8a to 72ba7f1 Compare June 30, 2023 18:07
@sipa sipa force-pushed the 202306_poly1305 branch 4 times, most recently from 96ad961 to c9209ef Compare July 11, 2023 02:45
Copy link
Contributor

@theStack theStack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK

Verified that the poly1305 implementation introduced in 4abc846 matches poly1305-donna (used https://github.com/openbsd/src/blob/master/sys/crypto/poly1305.c as a reference, which is again based on Andrew Moon's implementation with minor adaptions). Still planning to verify the test vectors with another implementation, likely PyCryptodome.

@sipa sipa force-pushed the 202306_poly1305 branch from c9209ef to 3dfa837 Compare July 11, 2023 14:41
This code is taken from poly1305-donna-32.h, poly1305-donna.h, poly1305-donna.c
from https://github.com/floodyberry/poly1305-donna, commit
e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781, with the following modifications:

* Coding style (braces around one-line indented if/for loops).
* Rename unsigned long (long) to uint32_t and uint64_t.
* Rename poly1305_block_size to POLY1305_BLOCK_SIZE.
* Adding noexcept to functions.
* Merging poly1305_state_internal_t and poly1305_context types.
* Merging code from multiple files.
* Place all imported code in the poly1305_donna namespace.
@sipa sipa force-pushed the 202306_poly1305 branch from 3dfa837 to d6a097c Compare July 12, 2023 18:50
Copy link
Contributor

@theStack theStack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall. Verified the introduced test vectors (40de8ce) with the pyca/cryptography library, which uses OpenSSL in the background (tried with PyCryptodome first, but that didn't allow use of raw Poly1305 without a cipher algorithm). As expected, everything passes with this alternative implementation as well.

Python code
#!/usr/bin/env python3
from cryptography.hazmat.primitives.poly1305 import Poly1305

def test_poly1305(msg, key, tag):
    p = Poly1305(bytes.fromhex(key))
    p.update(bytes.fromhex(msg))
    assert p.finalize() == bytes.fromhex(tag)

test_poly1305("8e993b9f48681273c29650ba32fc76ce48332ea7164d96a4476fb8c531a1186a" +
              "c0dfc17c98dce87b4da7f011ec48c97271d2c20f9b928fe2270d6fb863d51738" +
              "b48eeee314a7cc8ab932164548e526ae90224368517acfeabd6bb3732bc0e9da" +
              "99832b61ca01b6de56244a9e88d5f9b37973f622a43d14a6599b1f654cb45a74" +
              "e355a5",
              "eea6a7251c1e72916d11c2cb214d3c252539121d8e234e652d651fa4c8cff880",
              "f3ffc7703f9400e52a7dfb4b3d3305d9")

total_ctx = Poly1305(bytes.fromhex("01020304050607fffefdfcfbfaf9ffffffffffffffffffffffffffff00000000"))
for i in range(256):
    key = bytes([i]*32)
    msg = bytes([i]*i)
    p = Poly1305(key)
    p.update(msg)
    tag = p.finalize()
    total_ctx.update(tag)
assert total_ctx.finalize() == bytes.fromhex("64afe2e8d6ad7bbdd287f97c44623d39")

test_poly1305("000000000000000000000094000000000000b07c4300000000002c002600d500" +
              "00000000000000000000000000bc58000000000000000000c9000000dd000000" +
              "00000000000000d34c000000000000000000000000f9009100000000000000c2" +
              "4b0000e900000000000000000000000000000000000e00000027000074000000" +
              "0000000003000000000000f1000000000000dce2000000000000003900000000" +
              "0000000000000000000000000000000000000000000000520000000000000000" +
              "000000000000000000000000009500000000000000000000000000cf00826700" +
              "000000a900000000000000000000000000000000000000000079000000000000" +
              "0000de0000004c000000000033000000000000000000000000002800aa000000" +
              "00003300860000e000000000",
              "6e543496db3cf677592989891ab021f58390feb84fb419fbc7bb516a60bfa302",
              "7ea80968354d40d9d790b45310caf7f3")
test_poly1305("0000005900000000c40000002f00000000000000000000000000000029690000" +
              "0000e8000037000000000000000000000000000b000000000000000000000000" +
              "000000000000000000000000001800006e0000000000a4000000000000000000" +
              "00000000000000004d00000000000000b0000000000000000000005a00000000" +
              "0000000000b7c300000000000000540000000000000000000000000a00000000" +
              "00005b0000000000000000000000000000000000002d00e70000000000000000" +
              "000000000000003400006800d700000000000000000000360000000000000000" +
              "00eb000000000000000000000000000000000000000000000000000028000000" +
              "37000000000000000000000000000000000000000000000000000000008f0000" +
              "000000000000000000000000",
              "f0b659a4f3143d8a1e1dacb9a409fe7e7cd501dfb58b16a2623046c5d337922a",
              "0e410fa9d7a40ac582e77546be9a72bb")

Left some nits for removing the need for Make{Writable}ByteSpan calls. I think they can also be applied to the TestPoly1305 function.

@sipa sipa force-pushed the 202306_poly1305 branch from d6a097c to 95f3c62 Compare July 13, 2023 02:41
sipa added 2 commits July 12, 2023 22:43
This also removes the old poly1305_auth interface, as it no longer serves any
function. The new Poly1305 class based interface is more modern and safe.
@sipa sipa force-pushed the 202306_poly1305 branch from 95f3c62 to 4e5c933 Compare July 13, 2023 02:44
Copy link
Contributor

@theStack theStack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 4e5c933

@sipa
Copy link
Member Author

sipa commented Jul 13, 2023

@MarcoFalke You may be interested in the Span<std::byte> usage here.

Copy link
Member

@maflcko maflcko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, happy to leave comments, but I think it may be better to fix them in a follow-up, unless you really want to here.

Copy link
Contributor

@stratospher stratospher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tested ACK 4e5c933.

cross checked that this is consistent with poly1305-donna implementation. benchmarks look better compared to previous poly1305_auth code too. (also found this blogpost which explains poly1305's design interesting!)

noticed this tiny difference in poly1305-donna's vs RFC 8439 r - though it might also be some trick for later computation i guess.

  • r before clamping: 85:d6:be:78:57:55:6d:33:7f:44:52:fe:42:d5:06:a8
  • Clamped r as a number: 806d5400e52447c036d555408bed685
  • chopping r into 5 pieces for r[0], r[1], r[2], r[3], r[4]
  • can be written as: 806d5, 400e524, 47c036, d555408, bed685
  • r in floodyberry's : 806d5, 1003949, 47c036, 3555502, bed685
  • where 400e524 = 1003949 + "two zero bits" and d555408 = 3555502 + "two zero bits"

@sipa
Copy link
Member Author

sipa commented Jul 17, 2023

@stratospher There are two approaches for poly1305 implementations out there; one where the key (and the accumulated hash value) are represented using 32-bit limbs, and one where it's represented using 26-bit limbs (so in one r = sum(r[i] * 2^(32*i)) while in the other r = sum(r[i] * 2^(26*i))). Poly1305 is designed to make 32-bit limb designs efficient (the masking out of the bits guarantee some overflows are avoided, as explained in the blogpost you link). However, in all my benchmarks the 26-bit limb design (as is used in the poly1305-donna code) is faster. That's kind of a pity, because it means the masking is pointless (and actually reduces the security of the scheme by a few bits), yet necessary to remain compatible with the poly1305 spec.

@sipa
Copy link
Member Author

sipa commented Jul 17, 2023

@MarcoFalke Thanks for the comments; I think I'm going to create a separate PR with some further code refactoring related to this as well, outside of the critical path of #25361.

@achow101
Copy link
Member

ACK 4e5c933

Verified that this was consistent with poly1305-donna.

@achow101 achow101 merged commit 306157a into bitcoin:master Jul 17, 2023
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jul 19, 2023
…modernize

4e5c933 Switch all callers from poly1305_auth to Poly1305 class (Pieter Wuille)
8871f7d tests: add more Poly1305 test vectors (Pieter Wuille)
40e6c5b crypto: add Poly1305 class with std::byte Span interface (Pieter Wuille)
50269b3 crypto: switch poly1305 to incremental implementation (Pieter Wuille)

Pull request description:

  Our current Poly1305 code (src/crypto/poly1305.*) only supports computing the entire tag in one go (the `poly1305_auth` function takes a key and message, and outputs the tag). However, the RFC8439 authenticated encryption (as used in BIP324, see bitcoin#27634) scheme makes use of Poly1305 in a way where the message consists of 3 different pieces:
  * The additionally authenticated data (AAD), padded to 16 bytes.
  * The ciphertext, padded to 16 bytes.
  * The length of the AAD and the length of the ciphertext, together another 16 bytes.

  Implementing RFC8439 using the existing `poly1305_auth` function requires creating a temporary copy with all these pieces of data concatenated just for the purpose of computing the tag (the approach used in bitcoin#25361).

  This PR replaces the poly1305 code with new code from https://github.com/floodyberry/poly1305-donna (with minor adjustments to make it match our coding style and use our utility functions, documented in the commit) which supports incremental operation, and then adds a C++ wrapper interface using std::byte Spans around it, and adds tests that incremental and all-at-once computation match.

ACKs for top commit:
  achow101:
    ACK 4e5c933
  theStack:
    ACK 4e5c933
  stratospher:
    tested ACK 4e5c933.

Tree-SHA512: df6e9a2a4a38a480f9e4360d3e3def5311673a727a4a85b008a084cf6843b260dc82cec7c73e1cecaaccbf10f3521a0ae7dba388b65d0b086770f7fbc5223e2a
fanquake added a commit that referenced this pull request Aug 10, 2023
1c7582e tests: add decryption test to bip324_tests (Pieter Wuille)
990f0f8 Add BIP324Cipher, encapsulating key agreement, derivation, and stream/AEAD ciphers (Pieter Wuille)
c91cedf crypto: support split plaintext in ChaCha20Poly1305 Encrypt/Decrypt (Pieter Wuille)
af2b44c bench: add benchmark for FSChaCha20Poly1305 (Pieter Wuille)
aa8cee9 crypto: add FSChaCha20Poly1305, rekeying wrapper around ChaCha20Poly1305 (Pieter Wuille)
0fee267 crypto: add FSChaCha20, a rekeying wrapper around ChaCha20 (Pieter Wuille)
9ff0768 crypto: add the ChaCha20Poly1305 AEAD as specified in RFC8439 (Pieter Wuille)
9fd085a crypto: remove outdated variant of ChaCha20Poly1305 AEAD (Pieter Wuille)

Pull request description:

  Depends on #27985 and #27993, based on and partially replaces #25361, part of #27634. Draft while dependencies are not merged.

  This adds implementations of:
  * The ChaCha20Poly1305 AEAD from [RFC8439 section 2.8](https://datatracker.ietf.org/doc/html/rfc8439#section-2.8), including test vectors.
  * The FSChaCha20 stream cipher as specified in [BIP324](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki#rekeying-wrappers-fschacha20poly1305-and-fschacha20), a rekeying wrapper around ChaCha20.
  * The FSChaCha20Poly1305 AEAD as specified in [BIP324](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki#rekeying-wrappers-fschacha20poly1305-and-fschacha20), a rekeying wrapper around ChaCha20Poly1305.
  * A BIP324Cipher class that encapsulates key agreement, key derivation, and stream ciphers and AEADs for [BIP324 packet encoding](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki#overall-packet-encryption-and-decryption-pseudocode).

  The ChaCha20Poly1305 and FSChaCha20Poly1305 implementations are new, taking advance of the improvements in #27993.

ACKs for top commit:
  jamesob:
    reACK 1c7582e
  theStack:
    ACK 1c7582e
  stratospher:
    tested ACK 1c7582e.

Tree-SHA512: 06728b4b95b21c5b732ed08faf40e94d0583f9d86ff4db3b92dd519dcd9fbfa0f310bc66ef1e59c9e49dd844ba8c5ac06e2001762a804fb5aa97027816045a46
fanquake added a commit to bitcoin-core/gui that referenced this pull request Aug 18, 2023
…tion & follow-ups

57cc136 crypto: make ChaCha20::SetKey wipe buffer (Pieter Wuille)
da0ec62 tests: miscellaneous hex / std::byte improvements (Pieter Wuille)
bdcbc85 fuzz: support std::byte in Consume{Fixed,Variable}LengthByteVector (Pieter Wuille)
7d1cd93 crypto: require key on ChaCha20 initialization (Pieter Wuille)
44c1176 random: simplify FastRandomContext::randbytes using fillrand (Pieter Wuille)
3da636e crypto: refactor ChaCha20 classes to use Span<std::byte> interface (Pieter Wuille)

Pull request description:

  This modernizes the ChaCha20 and ChaCha20Aligned interfaces to be `Span<std::byte>` based, and other improvements.

  * Modifies all functions and constructors of `ChaCha20` and `ChaCha20Aligned` to be `Span<std::byte>` based (aligning them with `FSChaCha20`, `AEADChaCha20Poly1305`, and `FSChaCha20Poly1305`)
  * Remove default constructors, to make sure all call sites provide a key (suggested in bitcoin/bitcoin#26153 (comment))
  * Wipe key material on rekey for security (suggested in bitcoin/bitcoin#26153 (comment))
  * Use `HexStr` on byte vectors in tests (suggested in bitcoin/bitcoin#27993 (comment))
  * Support `std::byte` vectors in `ConsumeRandomLengthByteVector` and `ConsumeFixedLengthByteVector`, and use it (suggested in bitcoin/bitcoin#27993 (comment))
  * And a few more.

  While related, I don't see this as a necessary for BIP324.

ACKs for top commit:
  stratospher:
    ACK 57cc136.
  theStack:
    re-ACK 57cc136

Tree-SHA512: 361da4ff003c8465a32eeac0983a8a6f047dbbf5b400168b409c8e3234e79d577fc854e0764389446585da3e12b964c94dd67fc0c9c1d1d092cec296121e05d4
Frank-GER pushed a commit to syscoin/syscoin that referenced this pull request Sep 8, 2023
…ollow-ups

57cc136 crypto: make ChaCha20::SetKey wipe buffer (Pieter Wuille)
da0ec62 tests: miscellaneous hex / std::byte improvements (Pieter Wuille)
bdcbc85 fuzz: support std::byte in Consume{Fixed,Variable}LengthByteVector (Pieter Wuille)
7d1cd93 crypto: require key on ChaCha20 initialization (Pieter Wuille)
44c1176 random: simplify FastRandomContext::randbytes using fillrand (Pieter Wuille)
3da636e crypto: refactor ChaCha20 classes to use Span<std::byte> interface (Pieter Wuille)

Pull request description:

  This modernizes the ChaCha20 and ChaCha20Aligned interfaces to be `Span<std::byte>` based, and other improvements.

  * Modifies all functions and constructors of `ChaCha20` and `ChaCha20Aligned` to be `Span<std::byte>` based (aligning them with `FSChaCha20`, `AEADChaCha20Poly1305`, and `FSChaCha20Poly1305`)
  * Remove default constructors, to make sure all call sites provide a key (suggested in bitcoin#26153 (comment))
  * Wipe key material on rekey for security (suggested in bitcoin#26153 (comment))
  * Use `HexStr` on byte vectors in tests (suggested in bitcoin#27993 (comment))
  * Support `std::byte` vectors in `ConsumeRandomLengthByteVector` and `ConsumeFixedLengthByteVector`, and use it (suggested in bitcoin#27993 (comment))
  * And a few more.

  While related, I don't see this as a necessary for BIP324.

ACKs for top commit:
  stratospher:
    ACK 57cc136.
  theStack:
    re-ACK 57cc136

Tree-SHA512: 361da4ff003c8465a32eeac0983a8a6f047dbbf5b400168b409c8e3234e79d577fc854e0764389446585da3e12b964c94dd67fc0c9c1d1d092cec296121e05d4
kwvg added a commit to kwvg/dash that referenced this pull request Feb 19, 2024
kwvg added a commit to kwvg/dash that referenced this pull request Feb 20, 2024
kwvg added a commit to kwvg/dash that referenced this pull request Feb 23, 2024
kwvg added a commit to kwvg/dash that referenced this pull request Feb 24, 2024
kwvg added a commit to kwvg/dash that referenced this pull request Feb 27, 2024
kwvg added a commit to kwvg/dash that referenced this pull request Feb 29, 2024
kwvg added a commit to kwvg/dash that referenced this pull request Mar 5, 2024
PastaPastaPasta added a commit to dashpay/dash that referenced this pull request Mar 6, 2024
, bitcoin#28267, bitcoin#28374, bitcoin#28100 (p2p primitives)

b60c493 merge bitcoin#28100: more Span<std::byte> modernization & follow-ups (Kittywhiskers Van Gogh)
c2aa01c merge bitcoin#28374: python cryptography required for BIP 324 functional tests (Kittywhiskers Van Gogh)
7c5edf7 merge bitcoin#28267: BIP324 ciphersuite follow-up (Kittywhiskers Van Gogh)
1b1924e merge bitcoin#28008: BIP324 ciphersuite (Kittywhiskers Van Gogh)
ff54219 merge bitcoin#27993: Make poly1305 support incremental computation + modernize (Kittywhiskers Van Gogh)
d7482eb merge bitcoin#27985: Add support for RFC8439 variant of ChaCha20 (Kittywhiskers Van Gogh)

Pull request description:

  ## Additional Information

  * Dependent on #5900

  * Dependent on #5901

  * Without modifications, tests introduced in [bitcoin#28008](bitcoin#28008) will fail due to salt comprising of a fixed string (`bitcoin_v2_shared_secret`) and network bytes ([source](https://github.com/sipa/bitcoin/blob/1c7582ead6e1119899922041c1af2b4169b0bc74/src/bip324.cpp#L39-L40)). Bitcoin uses [`{0xf9, 0xbe, 0xb4, 0xd9}`](https://github.com/sipa/bitcoin/blob/1c7582ead6e1119899922041c1af2b4169b0bc74/src/kernel/chainparams.cpp#L114-L117) for mainnet while Dash uses [`{0xbf, 0x0c, 0x6b, 0xbd}`](https://github.com/dashpay/dash/blob/37f43e4e56de0d510110a7f790df34ea77748dc9/src/chainparams.cpp#L238-L241).
    * The replacement parameters are generated by:
      * Cloning https://github.com/bitcoin/bips (as of this writing, at bitcoin/bips@b3701fa)
      * Editing `bip-0324/reference.py`
        * Changing `NETWORK_MAGIC` to Dash's network magic
      * Running `gen_test_vectors.py` to get a new `packet_encoding_test_vectors.csv`
      * Using [this python script](https://github.com/bitcoin/bitcoin/blob/1c7582ead6e1119899922041c1af2b4169b0bc74/src/test/bip324_tests.cpp#L174-L196) mentioned in a comment in `src/test/bip324_tests.cpp`, generate the values that will be used to replace the ones in `bip324_tests.cpp` (it will print to `stdout` so it's recommended to pipe it to a file)
      * Paste the new values over the old ones

  ## Breaking Changes

  None. Changes are restricted to BIP324 cryptosuite, tests and associated logic.

  ## Checklist:
    _Go over all the following points, and put an `x` in all the boxes that apply._
  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas **(note: N/A)**
  - [x] I have added or updated relevant unit/integration/functional/e2e tests **(note: N/A)**
  - [x] I have made corresponding changes to the documentation **(note: N/A)**
  - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

Top commit has no ACKs.

Tree-SHA512: bb056de8588026adae63e78d56878274ff3934a439e2eae81606fa9c0a37dab432a129315bb9c1b754400b5c883bf460eea3a0c857a3be0816c8fbf55c479843
@bitcoin bitcoin locked and limited conversation to collaborators Jul 17, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants