Skip to content

Add method to push an ECDSA sig + sighash type byte on a witness#989

Merged
apoelstra merged 1 commit intorust-bitcoin:masterfrom
TheBlueMatt:2022-05-witness-push-sig
May 10, 2022
Merged

Add method to push an ECDSA sig + sighash type byte on a witness#989
apoelstra merged 1 commit intorust-bitcoin:masterfrom
TheBlueMatt:2022-05-witness-push-sig

Conversation

@TheBlueMatt
Copy link
Copy Markdown
Member

We do this all over the place in rust-lightning, and its probably
the most common thing to do with a Witness so I figured I'd
upstream the util method to do this. It also avoids an allocation
compared to the naive approach of SerializedSignature.to_vec()
with two pushes, which is nice.

We do this all over the place in rust-lightning, and its probably
the most common thing to do with a `Witness` so I figured I'd
upstream the util method to do this. It also avoids an allocation
compared to the naive approach of `SerializedSignature.to_vec()`
with two pushes, which is nice.
sig[..signature.len()].copy_from_slice(&signature);
sig[signature.len()] = hash_type as u8;
self.push(&sig[..signature.len() + 1]);
}
Copy link
Copy Markdown
Member

@tcharding tcharding May 5, 2022

Choose a reason for hiding this comment

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

Do we want to protect against a malicious caller? signature.len() returns a value that could have previously been set with signature.set_len(). A malicious call could cause this function to panic. Not sure if that matters?

One solution might be:

    pub fn push_bitcoin_signature(&mut self, signature: &ecdsa::SerializedSignature, hash_type: EcdsaSighashType) {
        // Note that a maximal length ECDSA signature is 72 bytes, plus the sighash type makes 73
        let mut sig = [0; 73];
        let len = signature.len();

        if len > 72 {
            // This can happen if a malicious caller has previously called
            // signature.set_len() with a length intentionally too big.
            return;
        }

        sig[..len].copy_from_slice(&signature);
        sig[len] = hash_type as u8;
        self.push(&sig[..=len]);
    }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Perhaps we should just add a check to set_len instead?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure what is the security model you are thinking of. If the attacker controls any code in the current process, aren't all bets off? they can just read/write random memory locations, including keys and such.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't see a set_len() anywhere in https://docs.rs/secp256k1/latest/secp256k1/ecdsa/struct.SerializedSignature.html? What am I missing?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It also looks to me like SerializedSignature cannot be coerced into having a longer length. If it were, I'd advocate for a comment describing the panic, but I'm ok with it existing.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure what is the security model you are thinking of

Sorry, I misspoke I should have said 'defensive programming' instead of 'malicious caller', my bad.

I don't see a set_len() anywhere

Its pub (crate) so doesn't show up in those docs

It also looks to me like SerializedSignature cannot be coerced into having a longer length.

Thanks for taking a look.


/// Pushes a DER-encoded ECDSA signature with a signature hash type as a new element on the
/// witness, requires an allocation.
pub fn push_bitcoin_signature(&mut self, signature: &ecdsa::SerializedSignature, hash_type: EcdsaSighashType) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

don't we want this for Schnorr too?

or maybe a generic one:

pub fn push_with_hash_type<T: AsRef<[u8]>>(&mut self, new_element: T, hash_type: u8) {

Copy link
Copy Markdown
Member Author

@TheBlueMatt TheBlueMatt May 5, 2022

Choose a reason for hiding this comment

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

I wasn't sure exactly the format for schnorr so didn't bother. I'd prefer to keep the SerializedSignature here as it makes the API more explicit, but would be happy to add one for schnorr as well. Note that making it generic would be a lot more involved as we wouldn't have a maximum length of the new_element hard-coded.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

+1 to having a totally separate method for schnorr (doesn't need to be in this PR)

Copy link
Copy Markdown
Member

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

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

ACK 0ab5eea

I also got a bit nervous seeing a hardcoded 72-byte array followed by a variable-length copy, but I think it's fine.

@TheBlueMatt
Copy link
Copy Markdown
Member Author

I also got a bit nervous seeing a hardcoded 72-byte array followed by a variable-length copy, but I think it's fine.

Heh I first checked if secp had a constant for this I could use but it also uses 72 so I just stuck with it.

Copy link
Copy Markdown
Member

@tcharding tcharding left a comment

Choose a reason for hiding this comment

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

ACK 0ab5eea

Copy link
Copy Markdown
Contributor

@dunxen dunxen left a comment

Choose a reason for hiding this comment

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

ACK 0ab5eea

@apoelstra apoelstra merged commit af1f259 into rust-bitcoin:master May 10, 2022
ChallengeDev210 pushed a commit to ChallengeDev210/rust-bitcoin that referenced this pull request Aug 1, 2022
…+ sighash type byte on a witness

0ab5eea Add method to push an ECDSA sig + sighash type byte on a witness (Matt Corallo)

Pull request description:

  We do this all over the place in rust-lightning, and its probably
  the most common thing to do with a `Witness` so I figured I'd
  upstream the util method to do this. It also avoids an allocation
  compared to the naive approach of `SerializedSignature.to_vec()`
  with two pushes, which is nice.

ACKs for top commit:
  apoelstra:
    ACK 0ab5eea
  tcharding:
    ACK 0ab5eea
  dunxen:
    ACK 0ab5eea

Tree-SHA512: d4042eb3f2bc7e6beecd2c17eaeef1fdb7d096b8889a3709924734739557031f338525b685d32ac0dc26404afefd4f2d48defd1753ea8935e54e3a82987feb8c
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.

5 participants