Skip to content
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

BIP-146 using lower S at start #6

Merged
merged 2 commits into from
Aug 20, 2024

Conversation

Kirillvs
Copy link

@Kirillvs Kirillvs commented Aug 6, 2024

Hello!

Problem

As you know, due to BIP-146 https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#user-content-LOW_S, we should use only a lower S

And there is a check in the code (if lengthS == 33 {...}) that converts higher S to lower S, but there are a few problems with it

  • first one, this check is indirect, because, if I understand correctly, 31, 32 and 33 bytes are correct lengths for S part of signature
  • second one and the most important, sometimes it leads to the incorrect signature

About second problem: sometimes, with some circumstances, after this check and calculating lower S, encoded lower S has length 31 bytes (that is valid for S too), but encodeBigInt function in this case unconditionally adds a leading zero byte, to make length equal 32 bytes, and if before adding zero byte, the s signature had leading byte less than 0x7F byte (that means that we have zero leading bit and is being considered as a positive), it leads to -26: mandatory-script-verify-flag-failed (Non-canonical DER signature) in bitcoin

This check fails https://github.com/bitcoin/bitcoin/blob/80ad135a5e54e8a065fee5ef36e57034679111ab/src/script/interpreter.cpp#L159

    // Null bytes at the start of S are not allowed, unless S would otherwise be
    // interpreted as a negative number.
    if (lenS > 1 && (sig[lenR + 6] == 0x00) && !(sig[lenR + 7] & 0x80)) return false;

Solution

So the solution in this MR is to explicitly check if we have a high S at the start, and if so, we calculating the low S at this point, and after that we don't need to have check of if lengthS == 33 {...}

@mrtnetwork
Copy link
Owner

Hello!

Problem

As you know, due to BIP-146 https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#user-content-LOW_S, we should use only a lower S

And there is a check in the code (if lengthS == 33 {...}) that converts higher S to lower S, but there are a few problems with it

  • first one, this check is indirect, because, if I understand correctly, 31, 32 and 33 bytes are correct lengths for S part of signature
  • second one and the most important, sometimes it leads to the incorrect signature

About second problem: sometimes, with some circumstances, after this check and calculating lower S, encoded lower S has length 31 bytes (that is valid for S too), but encodeBigInt function in this case unconditionally adds a leading zero byte, to make length equal 32 bytes, and if before adding zero byte, the s signature had leading byte less than 0x7F byte (that means that we have zero leading bit and is being considered as a positive), it leads to -26: mandatory-script-verify-flag-failed (Non-canonical DER signature) in bitcoin

This check fails https://github.com/bitcoin/bitcoin/blob/80ad135a5e54e8a065fee5ef36e57034679111ab/src/script/interpreter.cpp#L159

    // Null bytes at the start of S are not allowed, unless S would otherwise be
    // interpreted as a negative number.
    if (lenS > 1 && (sig[lenR + 6] == 0x00) && !(sig[lenR + 7] & 0x80)) return false;

Solution

So the solution in this MR is to explicitly check if we have a high S at the start, and if so, we calculating the low S at this point, and after that we don't need to have check of if lengthS == 33 {...}

Thank you for the detailed explanation! I'll take a closer look in the next few days.

@mrtnetwork
Copy link
Owner

Hello!

Problem

As you know, due to BIP-146 https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#user-content-LOW_S, we should use only a lower S

And there is a check in the code (if lengthS == 33 {...}) that converts higher S to lower S, but there are a few problems with it

  • first one, this check is indirect, because, if I understand correctly, 31, 32 and 33 bytes are correct lengths for S part of signature
  • second one and the most important, sometimes it leads to the incorrect signature

About second problem: sometimes, with some circumstances, after this check and calculating lower S, encoded lower S has length 31 bytes (that is valid for S too), but encodeBigInt function in this case unconditionally adds a leading zero byte, to make length equal 32 bytes, and if before adding zero byte, the s signature had leading byte less than 0x7F byte (that means that we have zero leading bit and is being considered as a positive), it leads to -26: mandatory-script-verify-flag-failed (Non-canonical DER signature) in bitcoin

This check fails https://github.com/bitcoin/bitcoin/blob/80ad135a5e54e8a065fee5ef36e57034679111ab/src/script/interpreter.cpp#L159

    // Null bytes at the start of S are not allowed, unless S would otherwise be
    // interpreted as a negative number.
    if (lenS > 1 && (sig[lenR + 6] == 0x00) && !(sig[lenR + 7] & 0x80)) return false;

Solution

So the solution in this MR is to explicitly check if we have a high S at the start, and if so, we calculating the low S at this point, and after that we don't need to have check of if lengthS == 33 {...}

Thanks for that, I've reviewed everything, and it all looks correct.

@mrtnetwork mrtnetwork merged commit 27052ae into mrtnetwork:main Aug 20, 2024
1 check passed
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants