mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 01:40:55 +01:00
Create MaskedToString, implement it in ECPrivateKey, ExtPrivateKey, M… (#1011)
* Create MaskedToString, implement it in ECPrivateKey, ExtPrivateKey, MnemonicCode * Add MaskedToString to AesPassword, AesKey, and BIP39Seed * Add final to MaskedToString.toString() so it can't be overriden
This commit is contained in:
parent
b5d21a5a54
commit
0421076b21
@ -89,7 +89,7 @@ class ExtKeyTest extends BitcoinSUnitTest {
|
||||
val path = BIP32Path.empty
|
||||
|
||||
val masterPriv = ExtPrivateKey(LegacyMainNetPriv, Some(seedBytes), path)
|
||||
masterPriv.toString must be(
|
||||
masterPriv.toStringSensitive must be(
|
||||
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi")
|
||||
|
||||
//master public key
|
||||
@ -100,7 +100,7 @@ class ExtKeyTest extends BitcoinSUnitTest {
|
||||
//derive child
|
||||
val m0hPath = BIP32Path.fromString("m/0'")
|
||||
val m0h = masterPriv.deriveChildPrivKey(m0hPath)
|
||||
m0h.toString must be(
|
||||
m0h.toStringSensitive must be(
|
||||
"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7")
|
||||
|
||||
val m0hPub = m0h.extPublicKey
|
||||
@ -109,7 +109,7 @@ class ExtKeyTest extends BitcoinSUnitTest {
|
||||
|
||||
val m0h1Path = BIP32Path.fromString("m/0'/1")
|
||||
val m0h1 = masterPriv.deriveChildPrivKey(m0h1Path)
|
||||
m0h1.toString must be(
|
||||
m0h1.toStringSensitive must be(
|
||||
"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs")
|
||||
|
||||
val m0h1Pub = m0h1.extPublicKey
|
||||
@ -118,7 +118,7 @@ class ExtKeyTest extends BitcoinSUnitTest {
|
||||
|
||||
val m0h1P2hath = BIP32Path.fromString("m/0'/1/2'")
|
||||
val m0h12h = masterPriv.deriveChildPrivKey(m0h1P2hath)
|
||||
m0h12h.toString must be(
|
||||
m0h12h.toStringSensitive must be(
|
||||
"xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM")
|
||||
|
||||
val m0h12hPub = m0h12h.extPublicKey
|
||||
@ -127,7 +127,7 @@ class ExtKeyTest extends BitcoinSUnitTest {
|
||||
|
||||
val m0h12h2Path = BIP32Path.fromString("m/0'/1/2'/2")
|
||||
val m0h12h2 = masterPriv.deriveChildPrivKey(m0h12h2Path)
|
||||
m0h12h2.toString must be(
|
||||
m0h12h2.toStringSensitive must be(
|
||||
"xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334")
|
||||
|
||||
val m0h12h2Pub = m0h12h2.extPublicKey
|
||||
@ -136,7 +136,7 @@ class ExtKeyTest extends BitcoinSUnitTest {
|
||||
|
||||
val m0h12h21000000000Path = BIP32Path.fromString("m/0'/1/2'/2/1000000000")
|
||||
val m0h12h21000000000 = masterPriv.deriveChildPrivKey(m0h12h21000000000Path)
|
||||
m0h12h21000000000.toString must be(
|
||||
m0h12h21000000000.toStringSensitive must be(
|
||||
"xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76")
|
||||
|
||||
val m0h12h21000000000Pub = m0h12h21000000000.extPublicKey
|
||||
@ -150,7 +150,7 @@ class ExtKeyTest extends BitcoinSUnitTest {
|
||||
|
||||
val masterPriv =
|
||||
ExtPrivateKey(LegacyMainNetPriv, Some(seedBytes), BIP32Path.empty)
|
||||
masterPriv.toString must be(
|
||||
masterPriv.toStringSensitive must be(
|
||||
"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U")
|
||||
|
||||
val masterPub = masterPriv.extPublicKey
|
||||
@ -159,7 +159,7 @@ class ExtKeyTest extends BitcoinSUnitTest {
|
||||
|
||||
val m0Path = BIP32Path.fromString("m/0")
|
||||
val m0 = masterPriv.deriveChildPrivKey(m0Path)
|
||||
m0.toString must be(
|
||||
m0.toStringSensitive must be(
|
||||
"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt")
|
||||
|
||||
val m0Pub = m0.extPublicKey
|
||||
@ -169,7 +169,7 @@ class ExtKeyTest extends BitcoinSUnitTest {
|
||||
val m02147483647hPath = BIP32Path.fromString("m/0/2147483647'")
|
||||
val m02147483647h =
|
||||
masterPriv.deriveChildPrivKey(m02147483647hPath)
|
||||
m02147483647h.toString must be(
|
||||
m02147483647h.toStringSensitive must be(
|
||||
"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9")
|
||||
|
||||
val m02147483647hPub = m02147483647h.extPublicKey
|
||||
@ -178,7 +178,7 @@ class ExtKeyTest extends BitcoinSUnitTest {
|
||||
|
||||
val m02147483647h1Path = BIP32Path.fromString("m/0/2147483647'/1")
|
||||
val m02147483647h1 = masterPriv.deriveChildPrivKey(m02147483647h1Path)
|
||||
m02147483647h1.toString must be(
|
||||
m02147483647h1.toStringSensitive must be(
|
||||
"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef")
|
||||
|
||||
val m02147483647h1Pub = m02147483647h1.extPublicKey
|
||||
@ -189,7 +189,7 @@ class ExtKeyTest extends BitcoinSUnitTest {
|
||||
BIP32Path.fromString("m/0/2147483647'/1/2147483646'")
|
||||
val m02147483647h12147483646h =
|
||||
masterPriv.deriveChildPrivKey(m02147483647h12147483646hPath)
|
||||
m02147483647h12147483646h.toString must be(
|
||||
m02147483647h12147483646h.toStringSensitive must be(
|
||||
"xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc")
|
||||
|
||||
val m02147483647h12147483646hPub = m02147483647h12147483646h.extPublicKey
|
||||
@ -200,7 +200,7 @@ class ExtKeyTest extends BitcoinSUnitTest {
|
||||
BIP32Path.fromString("m/0/2147483647'/1/2147483646'/2")
|
||||
val m02147483647h12147483646h2 =
|
||||
masterPriv.deriveChildPrivKey(m02147483647h12147483646h2Path)
|
||||
m02147483647h12147483646h2.toString must be(
|
||||
m02147483647h12147483646h2.toStringSensitive must be(
|
||||
"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j")
|
||||
|
||||
val m02147483647h12147483646h2Pub = m02147483647h12147483646h2.extPublicKey
|
||||
@ -214,7 +214,7 @@ class ExtKeyTest extends BitcoinSUnitTest {
|
||||
|
||||
val masterPrivKey =
|
||||
ExtPrivateKey(LegacyMainNetPriv, Some(seedBytes), BIP32Path.empty)
|
||||
masterPrivKey.toString must be(
|
||||
masterPrivKey.toStringSensitive must be(
|
||||
"xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6")
|
||||
|
||||
val masterPubKey = masterPrivKey.extPublicKey
|
||||
@ -222,7 +222,7 @@ class ExtKeyTest extends BitcoinSUnitTest {
|
||||
"xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13")
|
||||
|
||||
val m0h = masterPrivKey.deriveChildPrivKey(BIP32Path.fromString("m/0'"))
|
||||
m0h.toString must be(
|
||||
m0h.toStringSensitive must be(
|
||||
"xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L")
|
||||
|
||||
val m0hPub = m0h.extPublicKey
|
||||
@ -241,4 +241,13 @@ class ExtKeyTest extends BitcoinSUnitTest {
|
||||
val path2 = masterPriv.extPublicKey.deriveChildPubKey(idx).get.key
|
||||
path1 must be(path2)
|
||||
}
|
||||
|
||||
it must "not serialize a ExtPrivateKey to string" in {
|
||||
val seedBytes =
|
||||
hex"4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be"
|
||||
|
||||
val masterPriv = ExtPrivateKey(LegacyMainNetPriv,
|
||||
Some(seedBytes), BIP32Path.empty)
|
||||
masterPriv.toString must be (s"Masked(ExtPrivateKeyImpl)")
|
||||
}
|
||||
}
|
||||
|
@ -254,4 +254,28 @@ class MnemonicCodeTest extends BitcoinSUnitTest {
|
||||
|
||||
testVectors.map(testTrezorVector(_))
|
||||
}
|
||||
|
||||
it must "not serialize a MnemonicCode toString" in {
|
||||
val correctSeed = Vector("phone",
|
||||
"dilemma",
|
||||
"early",
|
||||
"never",
|
||||
"test",
|
||||
"surge",
|
||||
"ecology",
|
||||
"rail",
|
||||
"medal",
|
||||
"benefit",
|
||||
"mystery",
|
||||
"toward",
|
||||
"lounge",
|
||||
"candy",
|
||||
"syrup")
|
||||
|
||||
val mnemonicCode = MnemonicCode.fromWords(correctSeed)
|
||||
|
||||
mnemonicCode.toString must be ("Masked(MnemonicCodeImpl)")
|
||||
|
||||
mnemonicCode.toStringSensitive must be (correctSeed.mkString(","))
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import scodec.bits.ByteVector
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
import org.bitcoins.core.protocol.NetworkElement
|
||||
import org.bitcoins.core.util.Factory
|
||||
import org.bitcoins.core.util.{Factory, MaskedToString}
|
||||
|
||||
/**
|
||||
* Represents a encrypted cipher text with it's accompanying
|
||||
@ -95,7 +95,7 @@ object AesSalt extends Factory[AesSalt] {
|
||||
// we enforce the non-empty password length in the companion object
|
||||
// to be able to make this extend AnyVal, and not be boxed at runtime
|
||||
final case class AesPassword private (private val value: String)
|
||||
extends AnyVal {
|
||||
extends MaskedToString {
|
||||
|
||||
/**
|
||||
* Converts this password into an AES key
|
||||
@ -127,6 +127,10 @@ final case class AesPassword private (private val value: String)
|
||||
val key = AesKey.fromSecretKey(secretKey)
|
||||
key
|
||||
}
|
||||
|
||||
override def toStringSensitive: String = {
|
||||
ByteVector.encodeUtf8(value).toString
|
||||
}
|
||||
}
|
||||
|
||||
object AesPassword {
|
||||
@ -159,7 +163,7 @@ object AesPassword {
|
||||
* and have certain length requirements.
|
||||
*/
|
||||
final case class AesKey private (bytes: ByteVector)
|
||||
extends AnyVal
|
||||
extends MaskedToString
|
||||
with NetworkElement {
|
||||
|
||||
/**
|
||||
@ -169,6 +173,9 @@ final case class AesKey private (bytes: ByteVector)
|
||||
def toSecretKey: SecretKey =
|
||||
new SecretKeySpec(bytes.toArray, "AES")
|
||||
|
||||
override def toStringSensitive: String = {
|
||||
s"AesKey(${bytes.toHex})"
|
||||
}
|
||||
}
|
||||
|
||||
object AesKey {
|
||||
|
@ -1,10 +1,10 @@
|
||||
package org.bitcoins.core.crypto
|
||||
|
||||
import org.bitcoins.core.protocol.NetworkElement
|
||||
import org.bitcoins.core.util.Factory
|
||||
import org.bitcoins.core.util.{Factory, MaskedToString}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
sealed abstract class BIP39Seed extends NetworkElement {
|
||||
sealed abstract class BIP39Seed extends NetworkElement with MaskedToString {
|
||||
require(
|
||||
bytes.length <= MAX_SEED_LENGTH_BYTES && bytes.length >= MIN_SEED_LENGTH_BYTES,
|
||||
s"Seed must be between $MIN_SEED_LENGTH_BYTES and $MAX_SEED_LENGTH_BYTES bytes, got ${bytes.length}"
|
||||
@ -16,6 +16,10 @@ sealed abstract class BIP39Seed extends NetworkElement {
|
||||
/** Generates an extended private key given a version */
|
||||
def toExtPrivateKey(keyVersion: ExtKeyPrivVersion): ExtPrivateKey =
|
||||
ExtPrivateKey.fromBIP39Seed(keyVersion, this)
|
||||
|
||||
override def toStringSensitive: String = {
|
||||
s"BIP39Seed($hex)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -32,7 +32,10 @@ sealed abstract class BaseECKey extends NetworkElement
|
||||
/**
|
||||
* Created by chris on 2/16/16.
|
||||
*/
|
||||
sealed abstract class ECPrivateKey extends BaseECKey with Sign {
|
||||
sealed abstract class ECPrivateKey
|
||||
extends BaseECKey
|
||||
with Sign
|
||||
with MaskedToString {
|
||||
|
||||
override def signFunction: ByteVector => Future[ECDigitalSignature] = {
|
||||
bytes =>
|
||||
@ -111,7 +114,7 @@ sealed abstract class ECPrivateKey extends BaseECKey with Sign {
|
||||
Base58.encode(encodedPrivKey)
|
||||
}
|
||||
|
||||
override def toString = s"ECPrivateKey($hex,$isCompressed)"
|
||||
override def toStringSensitive: String = s"ECPrivateKey($hex,$isCompressed)"
|
||||
}
|
||||
|
||||
object ECPrivateKey extends Factory[ECPrivateKey] {
|
||||
|
@ -107,11 +107,9 @@ sealed abstract class ExtKey extends NetworkElement {
|
||||
}
|
||||
|
||||
override def toString: String = {
|
||||
val checksum = CryptoUtil.doubleSHA256(bytes).bytes.take(4)
|
||||
val encoded = Base58.encode(bytes ++ checksum)
|
||||
require(Base58.decodeCheck(encoded).isSuccess)
|
||||
encoded
|
||||
ExtKey.toString(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object ExtKey extends Factory[ExtKey] {
|
||||
@ -148,6 +146,14 @@ object ExtKey extends Factory[ExtKey] {
|
||||
extKey
|
||||
}
|
||||
|
||||
def toString(extKey: ExtKey): String = {
|
||||
val bytes = extKey.bytes
|
||||
val checksum = CryptoUtil.doubleSHA256(bytes).bytes.take(4)
|
||||
val encoded = Base58.encode(bytes ++ checksum)
|
||||
require(Base58.decodeCheck(encoded).isSuccess)
|
||||
encoded
|
||||
}
|
||||
|
||||
override def fromBytes(bytes: ByteVector): ExtKey = {
|
||||
val privTry = Try(ExtPrivateKey(bytes))
|
||||
if (privTry.isSuccess) privTry.get
|
||||
@ -157,7 +163,10 @@ object ExtKey extends Factory[ExtKey] {
|
||||
}
|
||||
}
|
||||
|
||||
sealed abstract class ExtPrivateKey extends ExtKey with ExtSign {
|
||||
sealed abstract class ExtPrivateKey
|
||||
extends ExtKey
|
||||
with ExtSign
|
||||
with MaskedToString {
|
||||
import ExtKeyVersion._
|
||||
|
||||
override protected type VersionType = ExtKeyPrivVersion
|
||||
@ -224,6 +233,10 @@ sealed abstract class ExtPrivateKey extends ExtKey with ExtSign {
|
||||
case (bytes, path) =>
|
||||
deriveChildPrivKey(path).signFunction(bytes)
|
||||
}
|
||||
|
||||
override def toStringSensitive: String = {
|
||||
ExtKey.toString(this)
|
||||
}
|
||||
}
|
||||
|
||||
object ExtPrivateKey extends Factory[ExtPrivateKey] {
|
||||
|
@ -2,7 +2,7 @@ package org.bitcoins.core.crypto
|
||||
|
||||
import java.security.SecureRandom
|
||||
|
||||
import org.bitcoins.core.util.CryptoUtil
|
||||
import org.bitcoins.core.util.{CryptoUtil, MaskedToString}
|
||||
import scodec.bits.{BitVector, ByteVector}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
@ -16,7 +16,7 @@ import scala.io.Source
|
||||
* can be the root of a [[https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32]]
|
||||
* HD wallet.
|
||||
*/
|
||||
sealed abstract class MnemonicCode {
|
||||
sealed abstract class MnemonicCode extends MaskedToString {
|
||||
require(
|
||||
MnemonicCode.VALID_LENGTHS.contains(words.length), {
|
||||
val validLengths = MnemonicCode.VALID_LENGTHS.mkString(", ")
|
||||
@ -106,6 +106,10 @@ sealed abstract class MnemonicCode {
|
||||
|
||||
bits.take(codeInfo.entropyAndChecksumBits)
|
||||
}
|
||||
|
||||
override def toStringSensitive: String = {
|
||||
words.mkString(",")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,17 @@
|
||||
package org.bitcoins.core.util
|
||||
|
||||
/** Meant to provide a simple trait that
|
||||
* masks the default to string for sensitive classes */
|
||||
trait MaskedToString {
|
||||
final override def toString: String = {
|
||||
s"Masked(${getClass.getSimpleName})"
|
||||
}
|
||||
|
||||
/** Returns the real value of a sensitive string
|
||||
* This should be considered unsafe in the sense
|
||||
* that this information is sensitive and could cause
|
||||
* loss of funds if used anywhere things are persisted like logs
|
||||
*
|
||||
* */
|
||||
def toStringSensitive: String
|
||||
}
|
Loading…
Reference in New Issue
Block a user