Skip to content

Commit bed419f

Browse files
author
Michał Mrożek
authored
Merge pull request #858 from input-output-hk/add-encypt-key-command
[CHORE] Add encrypt-key command to CLI
2 parents 3f1d4df + 26b2f64 commit bed419f

File tree

3 files changed

+89
-6
lines changed

3 files changed

+89
-6
lines changed

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,38 @@ Possible networks: `etc`, `eth`, `mordor`, `testnet-internal`
5050
./bin/mantis cli generate-key-pairs 5
5151
```
5252

53+
- encrypt private key (default passphrase is empty string)
54+
```
55+
./bin/mantis cli encrypt-key --passphrase=pass 00b11c32957057651d56cd83085ef3b259319057e0e887bd0fdaee657e6f75d0
56+
```
57+
58+
Command output uses the same format as keystore so it could be used ex. to setup private faucet
59+
60+
ex.
61+
```
62+
{
63+
"id":"3038d914-c4cd-43b7-9e91-3391ea443f95",
64+
"address":"c28e15ebdcbb0bdcb281d9d5084182c9c66d5d12",
65+
"version":3,
66+
"crypto":{
67+
"cipher":"aes-128-ctr",
68+
"ciphertext":"6ecdb74b2a33dc3c016b460dccc96843d9d050aea3df27a3ae5348e85b3adc3e",
69+
"cipherparams":{
70+
"iv":"096b6490fe29e42e68e2db902920cad6"
71+
},
72+
"kdf":"scrypt",
73+
"kdfparams":{
74+
"salt":"cdcc875e116e2824ab02f387210c2f4ad7fd6fa1a4fc791cc92b981e3062a23e",
75+
"n":262144,
76+
"r":8,
77+
"p":1,
78+
"dklen":32
79+
},
80+
"mac":"8388ae431198d31d57e4c17f44335c2f15959b0d08d1145234d82f0d253fa593"
81+
}
82+
}
83+
```
84+
5385
### Building the client
5486

5587
#### SBT

src/main/scala/io/iohk/ethereum/cli/CliCommands.scala

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.monovore.decline.{Command, Opts}
55
import io.iohk.ethereum.crypto
66
import io.iohk.ethereum.crypto._
77
import io.iohk.ethereum.domain.Address
8+
import io.iohk.ethereum.keystore.{EncryptedKey, EncryptedKeyJsonCodec}
89
import io.iohk.ethereum.utils.ByteStringUtils
910
import java.security.SecureRandom
1011
import io.iohk.ethereum.security.SecureRandomBuilder
@@ -16,9 +17,16 @@ object CliCommands extends SecureRandomBuilder {
1617
val generateKeyPairsCommand = "generate-key-pairs"
1718
val deriveAddressCommand = "derive-address"
1819
val generateAllocsCommand = "generate-allocs"
20+
val encryptKeyCommand = "encrypt-key"
21+
1922
val balanceOption = "balance"
2023
val keyOption = "key"
2124
val addressOption = "address"
25+
val passphraseOption = "passphrase"
26+
27+
val privateKeyArgument = "private-key"
28+
private val privateKeyOpt = Opts
29+
.argument[String](privateKeyArgument)
2230

2331
private val GeneratePrivateKeyCommand: Command[String] =
2432
Command(name = generatePrivateKeyCommand, header = "Generate private key") {
@@ -47,11 +55,7 @@ object CliCommands extends SecureRandomBuilder {
4755

4856
private val DeriveAddressFromPrivateKey: Command[String] =
4957
Command(name = deriveAddressCommand, header = "Derive address from private key") {
50-
51-
Opts
52-
.argument[String]("private-key")
53-
.map(Hex.decode)
54-
.map(privKeyToAddress)
58+
privateKeyOpt.map(Hex.decode).map(privKeyToAddress)
5559
}
5660

5761
private val GenerateAllocs: Command[String] =
@@ -74,6 +78,21 @@ object CliCommands extends SecureRandomBuilder {
7478
}
7579
}
7680

81+
private val EncryptKey: Command[String] =
82+
Command(name = encryptKeyCommand, header = "Encrypt private key") {
83+
84+
val privateKey = privateKeyOpt.map(ByteStringUtils.string2hash)
85+
86+
val passphrase = Opts
87+
.option[String](long = passphraseOption, short = "p", help = "Passphrase")
88+
.withDefault("")
89+
90+
(privateKey, passphrase).mapN { (privateKey, passphrase) =>
91+
val encKey = EncryptedKey(privateKey, passphrase, secureRandom)
92+
EncryptedKeyJsonCodec.toJson(encKey)
93+
}
94+
}
95+
7796
private def allocs(addresses: List[String], balance: BigInt): String =
7897
s""""alloc": ${addresses
7998
.map(address => s"""$address: { "balance": $balance }""")
@@ -87,6 +106,12 @@ object CliCommands extends SecureRandomBuilder {
87106
}
88107

89108
val api: Command[String] = Command.apply(name = "cli", header = "Mantis CLI") {
90-
Opts.subcommands(GeneratePrivateKeyCommand, DeriveAddressFromPrivateKey, GenerateAllocs, GenerateKeyPairs)
109+
Opts.subcommands(
110+
GeneratePrivateKeyCommand,
111+
DeriveAddressFromPrivateKey,
112+
GenerateAllocs,
113+
GenerateKeyPairs,
114+
EncryptKey
115+
)
91116
}
92117
}

src/test/scala/io/iohk/ethereum/cli/CliCommandsSpec.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.iohk.ethereum.cli
22

3+
import io.iohk.ethereum.keystore.EncryptedKeyJsonCodec
4+
import io.iohk.ethereum.utils.ByteStringUtils
35
import org.scalatest.EitherValues
46
import org.scalatest.flatspec.AnyFlatSpec
57
import org.scalatest.matchers.should.Matchers
@@ -92,6 +94,30 @@ class CliCommandsSpec extends AnyFlatSpec with Matchers with EitherValues {
9294
val stringSplit = result.right.get.split("\\n\\n")
9395
stringSplit.length shouldEqual numOfKeysAsInt
9496
}
97+
98+
behavior of encryptKeyCommand
99+
it should "encrypt private key (without passphrase)" in {
100+
val json = api.parse(Seq(encryptKeyCommand, privateKey)).value
101+
102+
val decrypted = (for {
103+
encrypted <- EncryptedKeyJsonCodec.fromJson(json)
104+
decrypted <- encrypted.decrypt("")
105+
} yield decrypted).value
106+
107+
ByteStringUtils.hash2string(decrypted) shouldBe privateKey
108+
}
109+
110+
it should "encrypt private key (with passphrase)" in {
111+
val pass = "pass"
112+
val json = api.parse(Seq(encryptKeyCommand, argument(passphraseOption, Some(pass)), privateKey)).value
113+
114+
val decrypted = (for {
115+
encrypted <- EncryptedKeyJsonCodec.fromJson(json)
116+
decrypted <- encrypted.decrypt(pass)
117+
} yield decrypted).value
118+
119+
ByteStringUtils.hash2string(decrypted) shouldBe privateKey
120+
}
95121
}
96122

97123
object Fixture {

0 commit comments

Comments
 (0)