mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-24 23:08:31 +01:00
Adding function to detect if a signature is strictly der encoded according to BIP66
This commit is contained in:
parent
7d8699e822
commit
215cdddf20
4 changed files with 115 additions and 3 deletions
|
@ -82,6 +82,90 @@ trait DERSignatureUtil extends BitcoinSLogger {
|
|||
(r.getPositiveValue, s.getPositiveValue)
|
||||
} else throw new RuntimeException("The given sequence of bytes was not a DER signature: " + BitcoinSUtil.encodeHex(bytes))
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions implements the strict der encoding rules that were created in BIP66
|
||||
* https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
|
||||
* @param signature the signature to check if they are strictly der encoded
|
||||
* @return boolean indicating whether the signature was der encoded or not
|
||||
*/
|
||||
def isStrictDEREncoding(signature : ECDigitalSignature) : Boolean = isStrictDEREncoding(signature.bytes)
|
||||
|
||||
|
||||
/**
|
||||
* This functions implements the strict der encoding rules that were created in BIP66
|
||||
* https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
|
||||
* @param bytes the bytes to check if they are strictly der encoded
|
||||
* @return boolean indicating whether the bytes were der encoded or not
|
||||
*/
|
||||
def isStrictDEREncoding(bytes : Seq[Byte]) : Boolean = {
|
||||
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash]
|
||||
// * total-length: 1-byte length descriptor of everything that follows,
|
||||
// excluding the sighash byte.
|
||||
// * R-length: 1-byte length descriptor of the R value that follows.
|
||||
// * R: arbitrary-length big-endian encoded R value. It must use the shortest
|
||||
// possible encoding for a positive integers (which means no null bytes at
|
||||
// the start, except a single one when the next byte has its highest bit set).
|
||||
// * S-length: 1-byte length descriptor of the S value that follows.
|
||||
// * S: arbitrary-length big-endian encoded S value. The same rules apply.
|
||||
// * sighash: 1-byte value indicating what data is hashed (not part of the DER
|
||||
// signature)
|
||||
|
||||
//check if the bytes are ATLEAST der encoded
|
||||
val isDerEncoded = isDEREncoded(bytes)
|
||||
if (!isDerEncoded) return false
|
||||
|
||||
|
||||
if (bytes.size < 9) return false
|
||||
if (bytes.size > 73) return false
|
||||
|
||||
// A signature is of type 0x30 (compound)
|
||||
if (bytes.head != 0x30) return false
|
||||
|
||||
// Make sure the length covers the entire signature.
|
||||
if (bytes(1) != bytes.size - 3) return false
|
||||
|
||||
val rSize = bytes(3)
|
||||
|
||||
// Make sure the length of the S element is still inside the signature.
|
||||
if (5 + rSize >= bytes.size) return false
|
||||
|
||||
// Extract the length of the S element.
|
||||
val sSize = bytes(5 + rSize)
|
||||
|
||||
// Verify that the length of the signature matches the sum of the length
|
||||
// of the elements.
|
||||
if ((rSize + sSize + 7) != bytes.size) return false
|
||||
|
||||
// Check whether the R element is an integer.
|
||||
if (bytes(2) != 0x02) return false
|
||||
|
||||
// Zero-length integers are not allowed for R.
|
||||
if (rSize == 0) return false
|
||||
|
||||
// Negative numbers are not allowed for R.
|
||||
if ((bytes(4) & 0x80) != 0) return false
|
||||
|
||||
// Null bytes at the start of R are not allowed, unless R would
|
||||
// otherwise be interpreted as a negative number.
|
||||
if (rSize > 1 && (bytes(4) == 0x00) && !((bytes(5) & 0x80) != 0 )) return false
|
||||
|
||||
// Check whether the S element is an integer.
|
||||
if (bytes(rSize + 4) != 0x02) return false
|
||||
|
||||
// Zero-length integers are not allowed for S.
|
||||
if (rSize == 0) return false
|
||||
|
||||
// Negative numbers are not allowed for S.
|
||||
if ((bytes(rSize + 6) & 0x80) != 0) return false
|
||||
|
||||
// Null bytes at the start of S are not allowed, unless S would otherwise be
|
||||
// interpreted as a negative number.
|
||||
if (sSize > 1 && (bytes(rSize + 6) == 0x00) && !((bytes(rSize + 7) & 0x80) != 0)) return false
|
||||
|
||||
//if we made it to this point without returning false this must be a valid strictly encoded der sig
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
object DERSignatureUtil extends DERSignatureUtil
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package org.scalacoin.script.crypto
|
||||
|
||||
import org.scalacoin.crypto.{TransactionSignatureChecker, ECFactory, TransactionSignatureSerializer}
|
||||
import org.scalacoin.crypto.{DERSignatureUtil, TransactionSignatureChecker, ECFactory, TransactionSignatureSerializer}
|
||||
import org.scalacoin.protocol.script._
|
||||
import org.scalacoin.protocol.transaction.Transaction
|
||||
import org.scalacoin.script.control.{ControlOperationsInterpreter, OP_VERIFY}
|
||||
import org.scalacoin.script.flag.ScriptVerifyDerSig
|
||||
import org.scalacoin.script.{ScriptProgramFactory, ScriptProgramImpl, ScriptProgram}
|
||||
import org.scalacoin.script.constant._
|
||||
import org.scalacoin.util.{BitcoinSLogger, BitcoinSUtil, CryptoUtil}
|
||||
|
@ -94,7 +95,13 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
throw new RuntimeException("We do not know how to evaluate a nonstandard or EmptyScriptSignature for an OP_CHECKSIG operation")
|
||||
}
|
||||
|
||||
|
||||
//TODO: Check if strict dersig flag
|
||||
if (program.flags.contains(ScriptVerifyDerSig)) {
|
||||
//this means all of the signatures must encoded according to BIP66 strict dersig
|
||||
//https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
|
||||
require(DERSignatureUtil.isDEREncoded(signature), "Since the ScriptVerifyDerSig flag is set the signature being checked must be a strict dersig signature as per BIP 66\n" +
|
||||
"Sig: " + signature.hex)
|
||||
}
|
||||
|
||||
val restOfStack = program.stack.tail.tail
|
||||
val hashType = (signature.bytes.size == 0) match {
|
||||
|
@ -157,6 +164,8 @@ trait CryptoInterpreter extends ControlOperationsInterpreter with BitcoinSLogger
|
|||
def opCheckMultiSig(program : ScriptProgram) : ScriptProgram = {
|
||||
require(program.script.headOption.isDefined && program.script.head == OP_CHECKMULTISIG, "Script top must be OP_CHECKMULTISIG")
|
||||
require(program.stack.size > 2, "Stack must contain at least 3 items for OP_CHECKMULTISIG")
|
||||
|
||||
//TODO: Check if strict dersig flag
|
||||
//head should be n for m/n
|
||||
val nPossibleSignatures : Int = program.stack.head match {
|
||||
case s : ScriptNumber => s.num.toInt
|
||||
|
|
|
@ -2,15 +2,21 @@ package org.scalacoin.script.flag
|
|||
|
||||
/**
|
||||
* Created by chris on 3/23/16.
|
||||
* Trait used to create a script flag used to evalaute scripts in a
|
||||
* Trait used to create a script flag used to evaluate scripts in a
|
||||
* certain way
|
||||
*/
|
||||
trait ScriptFlagFactory {
|
||||
|
||||
/**
|
||||
* All the script flags found inside of bitcoin core
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.h#L31
|
||||
* @return
|
||||
*/
|
||||
private def flags = Seq(ScriptVerifyNone, ScriptVerifyP2SH, ScriptVerifyStrictEnc,
|
||||
ScriptVerifyDerSig, ScriptVerifyLowS, ScriptVerifySigPushOnly, ScriptVerifyMinimalData,
|
||||
ScriptVerifyNullDummy, ScriptVerifyDiscourageUpgradableNOPs, ScriptVerifyCleanStack,
|
||||
ScriptVerifyCheckLocktimeVerify, ScriptVerifyCheckSequenceVerify)
|
||||
|
||||
/**
|
||||
* Takes in a string and tries to match it with a script flag
|
||||
* @param str the string to try and match with a script flag
|
||||
|
|
|
@ -41,5 +41,18 @@ class DERSignatureUtilTest extends FlatSpec with MustMatchers {
|
|||
r must be (BitcoinSUtil.hexToBigInt("0a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa1"))
|
||||
s must be (BitcoinSUtil.hexToBigInt("1fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b7420"))
|
||||
}
|
||||
|
||||
it must "say that a signature taken from a p2sh transaction is a valid stirctly DER encoded signature" in {
|
||||
DERSignatureUtil.isStrictDEREncoding(p2shSignature) must be (true)
|
||||
}
|
||||
|
||||
it must "say that signature taken from a p2pkh transaction is a valid strictly DER encoded signature" in {
|
||||
DERSignatureUtil.isStrictDEREncoding(p2pkhSignature) must be (true)
|
||||
}
|
||||
|
||||
it must "say that a signature taken from a p2pk transaction is a valid strictly DER encoded signature" in {
|
||||
|
||||
DERSignatureUtil.isStrictDEREncoding(p2pkSignature) must be (true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue