Skip to content

Instantly share code, notes, and snippets.

@scalahub
Created December 25, 2017 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save scalahub/929bc2eed2bf4d1d06e10a5eeb0ed568 to your computer and use it in GitHub Desktop.
Save scalahub/929bc2eed2bf4d1d06e10a5eeb0ed568 to your computer and use it in GitHub Desktop.
package sh.ecc
import Util._
import sh.util.Hex
import sh.btc.BitcoinUtil._
abstract class AbstractPrvKey(protected [sh] val key:BigInt, val compressed:Boolean) {
if (key > n) throw new Exception(s"Private key must be < $n")
val pubKey:PubKey = key * G // G is generator of EC
pubKey.isCompressed = compressed
protected val keyHex = key.toHex
protected lazy val keyBytes = key.toBytes
val pubKeyHex = pubKey.pubKeyHex
@deprecated("Should not be exposed", "22 Dec 2017")
def getWIF:String = getBase58FromBytes( // from BitcoinUtil
(if (isMainNet) "80" else "ef")+keyHex + (if (compressed) "01" else "")
)
val sigHashAllBytes = getFixedIntBytes(0x01, 4) // 1 implies SIGHASH_ALL // https://en.bitcoin.it/wiki/OP_CHECKSIG
// retuens (k, r) in a deterministic way from hash. Requires that hash is already input as SHA256(msg)
// Follows: https://tools.ietf.org/html/rfc6979
private def getDeterministicKRFromHash(hash:Array[Byte]) = {
if (hash.size != 32) throw new Exception("Hash must be 32 bytes")
val h1 = getBits(hash) // a
var V = Array.fill(32)(0x01.toByte) // b
var K = Array.fill(32)(0x00.toByte) // c
K = HMAC(K)(V ++ Array(0x00.toByte) ++ intToOctets(key) ++ bitsToOctets(h1)) // d
V = HMAC(K)(V) // e
K = HMAC(K)(V ++ Array(0x01.toByte) ++ intToOctets(key) ++ bitsToOctets(h1)) // f
V = HMAC(K)(V) // g
var optKR:Option[(BigInt, BigInt)] = None
while(optKR.isEmpty) { // h
var T = Array[Byte]() // h.1
while(T.size < 32) T = T ++ HMAC(K)(V) // h.2
val k = bitsToInt(getBits(T)) // h.3
if (k < n && k > 0) {
val r = (k * G).x mod n
if (r != 0) optKR = Some((k, r))else {
K = HMAC(K)(V ++ Array(0x00.toByte))
V = HMAC(K)(V)
}
}
}
optKR.get
}
/*
https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki#DER_encoding_reference
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)
example:
3046022100E755B8C887AF3C97822875908B1BD4566ECAC5BEE9A2BF736C19A4E4BE74F5F8022100A18B52AE9FBE0DE525F6FA58B68D5AC74308886AAC1AA0AB4A7EC55087C85C0C
*/
private def encodeSigDER(r:BigInt, s:BigInt) = {
val rBytes = r.toByteArray
val rLen = rBytes.size // 2 chars = 1 byte
val sBytes = s.toByteArray
val sLen = sBytes.size // 2
val tLen = 2 + rLen + 2 + sLen
Array(0x30.toByte, tLen.toByte, 0x02.toByte, rLen.toByte) ++ rBytes ++
Array(0x02.toByte, sLen.toByte) ++ sBytes
}
def sign(msg:String):String = Hex.encodeBytes(signHash(sha256Bytes2Bytes(msg.getBytes)))
protected def signHash(hash:Array[Byte]) = {
if (hash.size != 32) throw new Exception("Hash must be 32 bytes")
val (k, r) = getDeterministicKRFromHash(hash)
// println("Deterministic => "+k.toHex) // for testing implementation
val h = BigInt(Hex.encodeBytes(hash), 16) // mod n
val s = ((h + key * r) * (k.modInverse(n))) mod n
/*
*https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#Low_S_values_in_signatures
Low S values in signatures
The value S in signatures must be between 0x1 and 0x7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 5D576E73 57A4501D DFE92F46 681B20A0 (inclusive). If S is too high, simply replace it by S' = 0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141 - S.
*/
encodeSigDER(r, if (s > sMax) n - s else s)
}
override def toString = "PrvKey [Hidden] with publicKey: "+pubKey.pubKeyHex
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment