internal bool VerifyType(string address, PubkeyScriptType scrType, out byte[] result) { result = null; try { switch (scrType) { case PubkeyScriptType.P2PKH: byte[] decoded = b58Encoder.DecodeWithCheckSum(address); if (decoded[0] != versionByte_P2pkh_MainNet && decoded[0] != versionByte_P2pkh_TestNet && decoded[0] != versionByte_P2pkh_RegTest && decoded.Length != hashFunc.HashByteSize) { return(false); } result = decoded.SubArray(1); return(true); case PubkeyScriptType.P2SH: decoded = b58Encoder.DecodeWithCheckSum(address); if (decoded[0] != versionByte_P2sh_MainNet && decoded[0] != versionByte_P2sh_TestNet && decoded[0] != versionByte_P2sh_RegTest && decoded.Length != hashFunc.HashByteSize) { return(false); } result = decoded.SubArray(1); return(true); case PubkeyScriptType.P2WPKH: decoded = b32Encoder.Decode(address, out byte witVer, out string hrp); if (witVer != 0 || decoded.Length != hashFunc.HashByteSize) { return(false); } result = decoded; return(true); case PubkeyScriptType.P2WSH: decoded = b32Encoder.Decode(address, out witVer, out hrp); if (witVer != 0 || decoded.Length != witHashFunc.HashByteSize) { return(false); } result = decoded; return(true); default: return(false); } } catch (Exception) { return(false); } }
public string CheckPrivateKey(string key) { if (!b58End.HasValidChars(key)) { return("The given key contains invalid base-58 characters."); } if (!b58End.HasValidCheckSum(key)) { return("The given key has an invalid checksum."); } byte[] keyBa = b58End.DecodeWithCheckSum(key); if (keyBa[0] != Constants.PrivKeyFirstByte) { return($"Invalid first key byte (actual={keyBa[0]}, expected={Constants.PrivKeyFirstByte})."); } if (keyBa.Length == 33) { if (!IsPrivateKeyInRange(keyBa.SubArray(1))) { return("Invalid key integer value (outside of the range defined by secp256k1 curve)."); } return("The given key is a valid uncompressed private key."); } else if (keyBa.Length == 34) { if (keyBa[^ 1] != Constants.PrivKeyCompLastByte)
/// <summary> /// Decrypts the given base-58 encoded encrypted key using the given password bytes and first 4 bytes of hash of /// (un)compressed P2PKH address as <see cref="Scrypt"/> salt. /// </summary> /// <exception cref="ArgumentNullException"/> /// <exception cref="FormatException"/> /// <exception cref="ObjectDisposedException"/> /// <param name="encrypted">Base-58 encrypted key (it will be normalized using Unicode Normalization Form C (NFC))</param> /// <param name="password">Password to use</param> /// <param name="isCompressed">Indicates whether to use compressed or uncompressed public key to build P2PKH address</param> /// <returns>The private key</returns> public PrivateKey Decrypt(string encrypted, byte[] password, out bool isCompressed) { if (isDisposed) { throw new ObjectDisposedException(nameof(BIP0038), "Instance was disposed."); } if (string.IsNullOrWhiteSpace(encrypted)) { throw new ArgumentNullException(nameof(encrypted), "Invalid (null) encrypted key."); } if (password == null) { throw new ArgumentNullException(nameof(password), "Password can not be null."); } byte[] encryptedBytes = b58enc.DecodeWithCheckSum(encrypted); if (encryptedBytes.Length != EncodedLength) { throw new FormatException("Invalid encrypted bytes length."); } if (!((Span <byte>)encryptedBytes).Slice(0, 2).SequenceEqual(prefix)) { throw new FormatException("Invalid prefix."); } isCompressed = IsCompressed(encryptedBytes[2]); Span <byte> salt = ((Span <byte>)encryptedBytes).Slice(3, 4); byte[] dk = scrypt.GetBytes(password, salt.ToArray(), 64); byte[] decryptedResult = new byte[32]; aes.Key = dk.SubArray(32, 32); // AES key is derivedhalf2 using ICryptoTransform decryptor = aes.CreateDecryptor(); decryptor.TransformBlock(encryptedBytes, 7, 16, decryptedResult, 0); decryptor.TransformBlock(encryptedBytes, 23, 16, decryptedResult, 16); // XOR method will only work on first item's length (32 byte here) so it doesn't matter of dk.Legth is 64 PrivateKey result = new PrivateKey(XOR(decryptedResult, dk)); string address = addressMaker.GetP2pkh(result.ToPublicKey(), isCompressed, NetworkType.MainNet); Span <byte> computedHash = hash.ComputeHash(Encoding.ASCII.GetBytes(address)).SubArray(0, 4); if (!computedHash.SequenceEqual(salt)) { throw new FormatException("Wrong password (derived address hash is not the same)."); } return(result); }
public AddressType GetAddressType(string address) { if (string.IsNullOrWhiteSpace(address)) { return(AddressType.Invalid); } try { byte[] decoded = b58Encoder.DecodeWithCheckSum(address); if (decoded.Length == hashFunc.HashByteSize + 1) { if (decoded[0] == versionByte_P2pkh_MainNet || decoded[0] == versionByte_P2pkh_TestNet || decoded[0] == versionByte_P2pkh_RegTest) { return(AddressType.P2PKH); } else if (decoded[0] == versionByte_P2sh_MainNet || decoded[0] == versionByte_P2sh_TestNet || decoded[0] == versionByte_P2sh_RegTest) { return(AddressType.P2SH); } } return(AddressType.Invalid); } catch (Exception) { } try { byte[] decoded = b32Encoder.Decode(address, out byte witVer, out string hrp); if (witVer == 0 && hrp == hrp_MainNet || hrp == hrp_TestNet || hrp == hrp_RegTest) { if (decoded.Length == hashFunc.HashByteSize) { return(AddressType.P2WPKH); } else if (decoded.Length == witHashFunc.BlockByteSize) { return(AddressType.P2WSH); } } } catch (Exception) { } return(AddressType.Invalid); }
public string CheckBase58Bip38(string bip38) { if (!b58Enc.HasValidChars(bip38)) { return("The given BIP-38 string contains invalid base-58 characters."); } if (!b58Enc.IsValid(bip38)) { return("The given BIP-38 string has an invalid checksum."); } byte[] data = b58Enc.DecodeWithCheckSum(bip38); if (data.Length != ConstantsFO.Bip38ByteLen) { return("The given BIP-38 string has an invalid byte length."); } if (data[0] != 1 || (data[1] != 0x42 && data[1] != 0x43)) { return("The given BIP-38 string has invalid starting bytes."); } return("The given BIP-38 string is valid."); }
public void DecodeWithCheckSumTest(string s, byte[] expected) { byte[] actual = encoder.DecodeWithCheckSum(s); Assert.Equal(expected, actual); }