/// <summary> /// Initializes a new instance of <see cref="TransactionVerifier"/> using given parameters. /// </summary> /// <exception cref="ArgumentNullException"/> /// <param name="isMempool">Indicates whether this instance is used by the memory pool or block</param> /// <param name="utxoDatabase">UTXO database</param> /// <param name="memoryPool">Memory pool</param> /// <param name="consensus">Consensus rules</param> public TransactionVerifier(bool isMempool, IUtxoDatabase utxoDatabase, IMemoryPool memoryPool, IConsensus consensus) { if (utxoDatabase is null) { throw new ArgumentNullException(nameof(utxoDatabase), "UTXO database can not be null."); } if (memoryPool is null) { throw new ArgumentNullException(nameof(memoryPool), "Memory pool can not be null."); } if (consensus is null) { throw new ArgumentNullException(nameof(consensus), "Consensus rules can not be null."); } this.isMempool = isMempool; utxoDb = utxoDatabase; mempool = memoryPool; this.consensus = consensus; calc = new EllipticCurveCalculator(); scrSer = new ScriptSerializer(); hash160 = new Ripemd160Sha256(); sha256 = new Sha256(); }
/// <summary> /// Signs the given message and returns the <see cref="Signature"/> result with its recovery ID set to /// appropriate value according to the given <see cref="AddressType"/>. /// </summary> /// <exception cref="ArgumentException"/> /// <exception cref="ArgumentNullException"/> /// <param name="key">Private key to use</param> /// <param name="message">UTF-8 encoded message to sign (null and white space will be rejected)</param> /// <param name="addrType">Type of the corresponding address (will affect the recovery ID)</param> /// <param name="ignoreSegwit"> /// [Default value = false] /// If true and address type is a SegWit address, sets the additional recovery ID value to 31. /// This is useful to return a recovery ID that can be verified with popular Bitcoin implementations such as Electrum. /// </param> /// <returns>Signature with the recovery ID set to appropriate value</returns> public Signature Sign(PrivateKey key, string message, AddressType addrType, bool ignoreSegwit = false) { if (key is null) { throw new ArgumentNullException(nameof(key), "Private key can not be null."); } if (string.IsNullOrWhiteSpace(message)) { throw new ArgumentNullException(nameof(message), "Signing an empty message is not allowed in this implementation."); } byte[] toSign = GetBytesToSign(message); byte recId = addrType switch { AddressType.P2PKH_Uncompressed => 27, AddressType.P2PKH_Compressed => 31, AddressType.P2SH_P2WPKH => 35, AddressType.P2WPKH => 39, _ => throw new ArgumentException("Invalid address type.") }; if (ignoreSegwit && (addrType == AddressType.P2SH_P2WPKH || addrType == AddressType.P2WPKH)) { recId = 31; } var calc = new EllipticCurveCalculator(); var sig = calc.Sign(toSign, key.ToBytes()); sig.RecoveryId += recId; return(sig); }
public MessageSignatureService(IReport rep) { calc = new EllipticCurveCalculator(); addressBuilder = new Address(); inputService = new InputService(); report = rep; }
/// <summary> /// Verifies the given signature against the message and for the given address. /// </summary> /// <exception cref="ArgumentException"/> /// <exception cref="ArgumentNullException"/> /// <exception cref="FormatException"/> /// <param name="message">UTF-8 encoded message to sign</param> /// <param name="address">Address of the key used to create the given signature</param> /// <param name="signature">Fixed length (65 byte) signature with a starting recovery ID encoded using Base-64</param> /// <param name="ignoreSegwit"> /// [Default value = false] /// If true and address type is a SegWit address, accepts both correct recovery ID (as defined by this BIP) /// and the incorrect one. /// This is useful to verify signature produced by popular Bitcoin implementations such as Electrum. /// </param> /// <returns>True if the verification succeeds; otherwise false.</returns> public bool Verify(string message, string address, string signature, bool ignoreSegwit = false) { byte[] toSign = GetBytesToSign(message); byte[] sigBa = Convert.FromBase64String(signature); if (!Signature.TryReadWithRecId(sigBa, out Signature sig, out string error)) { throw new FormatException(error); } Address addr = new Address(); AddressType addrType = AddressType.P2PKH_Compressed; if (sig.RecoveryId < 27 || sig.RecoveryId > 43) { return(false); } else if (sig.RecoveryId >= 27 && sig.RecoveryId < 35) { if (!addr.VerifyType(address, Blockchain.Scripts.PubkeyScriptType.P2PKH, out _)) { // Special case where this BIP is not used to create the signature if (ignoreSegwit && addr.VerifyType(address, Blockchain.Scripts.PubkeyScriptType.P2SH, out _)) { addrType = AddressType.P2SH_P2WPKH; } else if (ignoreSegwit && addr.VerifyType(address, Blockchain.Scripts.PubkeyScriptType.P2WPKH, out _)) { addrType = AddressType.P2WPKH; } else { return(false); } } else { addrType = sig.RecoveryId < 31 ? AddressType.P2PKH_Uncompressed : AddressType.P2PKH_Compressed; } } else if (sig.RecoveryId >= 35 && sig.RecoveryId < 39) { if (!addr.VerifyType(address, Blockchain.Scripts.PubkeyScriptType.P2SH, out _)) { return(false); } addrType = AddressType.P2SH_P2WPKH; } else if (sig.RecoveryId >= 39 && sig.RecoveryId < 43) { if (!addr.VerifyType(address, Blockchain.Scripts.PubkeyScriptType.P2WPKH, out _)) { return(false); } addrType = AddressType.P2WPKH; } EllipticCurveCalculator calc = new EllipticCurveCalculator(); if (!calc.TryRecoverPublicKeys(toSign, sig, out EllipticCurvePoint[] points))
public PublicKey() { addrMaker = new Address(); IECurveFp curve = new SecP256k1(); calc = new EllipticCurveCalculator(curve); byteSize = (int)Math.Ceiling((double)curve.SizeInBits / 8); }
public PublicKey[] GetPublicKeys(BIP0032Path path, uint count, uint startIndex = 0, uint step = 1) { if (isDisposed) { throw new ObjectDisposedException(nameof(BIP0032)); } if (path is null) { throw new ArgumentNullException(nameof(path), "Path can not be null!"); } if (ExtendedKeyDepth == byte.MaxValue || ExtendedKeyDepth + path.Indexes.Length + 1 > byte.MaxValue) { throw new ArgumentOutOfRangeException(nameof(ExtendedKeyDepth), "Can not get children since " + "depth will be bigger than 1 byte"); } PublicKey[] result = new PublicKey[count]; if (!(PrvKey is null)) { PrivateKey[] tempPK = GetPrivateKeys(path, count, startIndex, step); for (int j = 0; j < tempPK.Length; j++) { result[j] = tempPK[j].ToPublicKey(); tempPK[j].Dispose(); } return(result); } // If we are here it means PrivateKey was null bool anyHardened = path.Indexes.Any(x => IsHardendedIndex(x)); if (anyHardened || IsHardendedIndex(startIndex)) { throw new ArgumentException(); } // Two quick fixes: if (count == 0) { return(null); } if (step < 1) { step = 1; } // First start deriving the extended keys for each index BigInteger prevPrvInt = PrvKey.ToBigInt(); byte[] prevPubBa = PubKey.ToByteArray(true); EllipticCurvePoint prevPubPoint = PubKey.ToPoint(); byte[] tempCC = ChainCode; byte[] tempLeft; EllipticCurveCalculator calc = new EllipticCurveCalculator(); foreach (var index in path.Indexes) { // There is no hardened indexes thanks to first check FastStream stream = new FastStream(33 + 4); stream.Write(prevPubBa); stream.WriteBigEndian(index); HmacAndSplitData(stream.ToByteArray(), tempCC, out tempLeft, out tempCC); BigInteger kTemp = tempLeft.ToBigInt(true, true); // Note that we throw an exception here if the values were invalid (highly unlikely) // because it is the extended keys, we can't skip anything here. if (kTemp == 0 || kTemp >= N) { throw new ArgumentException(); } EllipticCurvePoint pt = calc.AddChecked(calc.MultiplyByG(kTemp), prevPubPoint); PublicKey tempPub = new PublicKey(pt); prevPubPoint = tempPub.ToPoint(); prevPubBa = tempPub.ToByteArray(true); } // Then derive the actual keys int i = 0; uint childIndex = startIndex; while (i < count) { // There is no hardened indexes thanks to first check FastStream stream = new FastStream(33 + 4); stream.Write(prevPubBa); stream.WriteBigEndian(childIndex); HmacAndSplitData(stream.ToByteArray(), tempCC, out tempLeft, out _); BigInteger kTemp = tempLeft.ToBigInt(true, true); // Note: we don't throw any exceptions here. We simply ignore invalid values (highly unlikely) // and move on to the next index. The returned value will always be filled with expected number of items. if (kTemp == 0 || kTemp >= N) { continue; } EllipticCurvePoint pt = calc.AddChecked(calc.MultiplyByG(kTemp), prevPubPoint); PublicKey temp = new PublicKey(pt); result[i++] = temp; childIndex += step; } return(result); }
private unsafe bool LoopComp(string key, int missingCount, char missingChar, byte[] expectedHash) { int[] missingIndexes = new int[missingCount]; byte[] ba = new byte[32]; for (int i = 0, j = 0; i < ba.Length; i++) { int hi, lo; if (key[i * 2] == missingChar) { hi = 0; missingIndexes[j++] = i * 2; } else { hi = key[i * 2] - 65; hi = hi + 10 + ((hi >> 31) & 7); } if (key[i * 2 + 1] == '*') { lo = 0; missingIndexes[j++] = i * 2 + 1; } else { lo = key[i * 2 + 1] - 65; lo = lo + 10 + ((lo >> 31) & 7) & 0x0f; } ba[i] = (byte)(lo | hi << 4); } var cartesian = CartesianProduct.Create(Enumerable.Repeat(Enumerable.Range(0, 16), missingCount)); EllipticCurveCalculator calc = new EllipticCurveCalculator(); BigInteger smallVal = new BigInteger(ba, true, true); EllipticCurvePoint smallPub = calc.MultiplyByG(smallVal); Ripemd160Sha256 hash = new Ripemd160Sha256(); Parallel.ForEach(cartesian, (item, loopState) => { Span <byte> temp = new byte[32]; int mis = 0; foreach (int keyItem in item) { int misIndex = missingIndexes[mis]; if (misIndex % 2 == 0) { temp[misIndex / 2] |= (byte)(keyItem << 4); } else { temp[misIndex / 2] |= (byte)keyItem; } mis++; } BigInteger tempVal = new BigInteger(temp, true, true); EllipticCurvePoint tempPub = calc.MultiplyByG(tempVal); EllipticCurvePoint pub = calc.AddChecked(tempPub, smallPub); byte[] toHash = new byte[33]; toHash[0] = pub.Y.IsEven ? (byte)2 : (byte)3; byte[] xBytes = pub.X.ToByteArray(true, true); Buffer.BlockCopy(xBytes, 0, toHash, 33 - xBytes.Length, xBytes.Length); ReadOnlySpan <byte> actual = hash.ComputeHash(toHash); if (actual.SequenceEqual(expectedHash)) { char[] origHex = key.ToCharArray(); int index = 0; foreach (var keyItem in item) { origHex[missingIndexes[index++]] = GetHex(keyItem); } AddQueue($"Found a key: {new string(origHex)}"); loopState.Break(); } }); AddQueue("Failed to find any key."); return(false); }
public MnemonicSevice(IReport rep) { report = rep; inputService = new InputService(); calc = new EllipticCurveCalculator(); }
public MessageSignatureService(Report rep) : base(rep) { calc = new EllipticCurveCalculator(new SecP256k1()); addressBuilder = new Address(); inputService = new InputService(); }