factor out revoke checking
This commit is contained in:
parent
06556c673a
commit
9842bc68ef
12 changed files with 330 additions and 269 deletions
|
@ -1,9 +1,5 @@
|
|||
package org.tcpid.opretj.testapp;
|
||||
|
||||
import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN;
|
||||
import static org.libsodium.jni.SodiumConstants.NONCE_BYTES;
|
||||
import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -17,24 +13,19 @@ import org.bitcoinj.core.AddressFormatException;
|
|||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.InsufficientMoneyException;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.Utils;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.script.ScriptBuilder;
|
||||
import org.bitcoinj.utils.Threading;
|
||||
import org.bitcoinj.wallet.SendRequest;
|
||||
import org.libsodium.jni.crypto.Box;
|
||||
import org.libsodium.jni.crypto.Hash;
|
||||
import org.libsodium.jni.keys.KeyPair;
|
||||
import org.libsodium.jni.keys.PublicKey;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tcpid.key.SigningKey;
|
||||
import org.tcpid.opretj.OPRETECParser;
|
||||
import org.tcpid.opretj.OPRETWallet;
|
||||
import org.tcpid.opretj.OPRETWalletAppKit;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.google.common.util.concurrent.Service;
|
||||
|
||||
import jline.console.ConsoleReader;
|
||||
|
@ -49,42 +40,6 @@ public class App {
|
|||
|
||||
private final static Logger logger = LoggerFactory.getLogger(App.class);
|
||||
private final static SigningKey SK = new SigningKey(HASH.sha256("TESTSEED".getBytes()));
|
||||
private final static VerifyKey VK = SK.getVerifyKey();
|
||||
private final static byte[] PKHASH = HASH.sha256(VK.toBytes());
|
||||
private final static byte[] REVOKEMSG = Bytes.concat("Revoke ".getBytes(), PKHASH);
|
||||
|
||||
public static void checkKey(final byte[] pkhash, final byte[] sig) {
|
||||
logger.warn("CHECKING REVOKE PKHASH {} - SIG {}", Utils.HEX.encode(pkhash), Utils.HEX.encode(sig));
|
||||
|
||||
if (!Arrays.equals(Arrays.copyOfRange(App.PKHASH, 0, 12), pkhash)) {
|
||||
logger.warn("Unknown PK {}", Utils.HEX.encode(pkhash));
|
||||
return;
|
||||
}
|
||||
|
||||
logger.warn("Using VerifyKey {}", VK);
|
||||
|
||||
if (VK.verify(REVOKEMSG, sig)) {
|
||||
logger.warn("REVOKED VerifyKey {}", VK);
|
||||
} else {
|
||||
logger.warn("SIGNATURE does not match!");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean cryptoSelfTest() {
|
||||
final SigningKey msk = new SigningKey();
|
||||
|
||||
final KeyPair mkpair = msk.getKeyPair();
|
||||
final KeyPair vkpair = SK.getKeyPair();
|
||||
final byte[] nonce = Arrays.copyOfRange(Sha256Hash.hash("TEST".getBytes()), 0, 8);
|
||||
final byte[] cipher = doubleEnc(mkpair, vkpair, "TEST".getBytes(), nonce);
|
||||
// System.err.println("Cipher len: " + cipher.length);
|
||||
try {
|
||||
final byte[] chk = doubleDec(msk.getVerifyKey().getPublicKey(), VK.getPublicKey(), cipher, nonce);
|
||||
return Arrays.equals(chk, "TEST".getBytes());
|
||||
} catch (final Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void displayBalance(final OPRETWalletAppKit kit, final PrintWriter out) {
|
||||
out.write("Balance: " + kit.wallet().getBalance().toFriendlyString() + "\n");
|
||||
|
@ -105,54 +60,6 @@ public class App {
|
|||
out.flush();
|
||||
}
|
||||
|
||||
private static byte[] doubleDec(final PublicKey MK, final PublicKey VK, final byte[] cipher, final byte[] nonce) {
|
||||
|
||||
final KeyPair Epair = new KeyPair(
|
||||
Arrays.copyOfRange(HASH.sha256(Bytes.concat(nonce, VK.toBytes(), MK.toBytes())), 0, SECRETKEY_BYTES));
|
||||
|
||||
final Box boxVK = new Box(VK, Epair.getPrivateKey());
|
||||
|
||||
final byte[] nonceVK = Arrays.copyOfRange(
|
||||
HASH.sha256(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), VK.toBytes())), 0, NONCE_BYTES);
|
||||
|
||||
final byte[] cipherMK = boxVK.decrypt(nonceVK, cipher);
|
||||
|
||||
final Box boxMK = new Box(MK, Epair.getPrivateKey());
|
||||
|
||||
final byte[] nonceMK = Arrays.copyOfRange(
|
||||
HASH.sha256(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MK.toBytes())), 0, NONCE_BYTES);
|
||||
|
||||
final byte[] clear = boxMK.decrypt(nonceMK, cipherMK);
|
||||
|
||||
return clear;
|
||||
}
|
||||
|
||||
private static byte[] doubleEnc(final KeyPair MKpair, final KeyPair VKpair, final byte[] clear,
|
||||
final byte[] nonce) {
|
||||
|
||||
final KeyPair Epair = new KeyPair(Arrays.copyOfRange(
|
||||
HASH.sha256(Bytes.concat(nonce, VKpair.getPublicKey().toBytes(), MKpair.getPublicKey().toBytes())), 0,
|
||||
SECRETKEY_BYTES));
|
||||
|
||||
final Box boxMK = new Box(Epair.getPublicKey(), MKpair.getPrivateKey());
|
||||
|
||||
final byte[] nonceMK = Arrays.copyOfRange(
|
||||
HASH.sha256(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MKpair.getPublicKey().toBytes())), 0,
|
||||
NONCE_BYTES);
|
||||
|
||||
final byte[] cipherMK = boxMK.encrypt(nonceMK, clear);
|
||||
|
||||
final Box boxVK = new Box(Epair.getPublicKey(), VKpair.getPrivateKey());
|
||||
|
||||
final byte[] nonceVK = Arrays.copyOfRange(
|
||||
HASH.sha256(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), VKpair.getPublicKey().toBytes())), 0,
|
||||
NONCE_BYTES);
|
||||
|
||||
final byte[] cipherVK = boxVK.encrypt(nonceVK, cipherMK);
|
||||
|
||||
return cipherVK;
|
||||
}
|
||||
|
||||
private static String executeCommand(final String command) {
|
||||
|
||||
final StringBuffer output = new StringBuffer();
|
||||
|
@ -251,14 +158,6 @@ public class App {
|
|||
|
||||
public static void main(final String[] args) throws Exception {
|
||||
|
||||
final boolean chk = cryptoSelfTest();
|
||||
if (chk) {
|
||||
System.err.println("Crypto self test: PASSED");
|
||||
} else {
|
||||
System.err.println("Crypto self test: FAILED");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
final OptionParser parser = new OptionParser();
|
||||
final OptionSpec<NetworkEnum> net = parser.accepts("net", "The network to run the examples on")
|
||||
.withRequiredArg().ofType(NetworkEnum.class).defaultsTo(NetworkEnum.TEST);
|
||||
|
@ -277,7 +176,17 @@ public class App {
|
|||
|
||||
final OPRETECParser bs = new OPRETECParser();
|
||||
|
||||
bs.addOPRETECRevokeEventListener((pkhash, sig) -> checkKey(pkhash, sig));
|
||||
final boolean chk = bs.cryptoSelfTest();
|
||||
if (chk) {
|
||||
System.err.println("Crypto self test: PASSED");
|
||||
} else {
|
||||
System.err.println("Crypto self test: FAILED");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
bs.addOPRETECRevokeEventListener((key) -> {
|
||||
System.out.println("Revoked Key: " + Utils.HEX.encode(key.toBytes()));
|
||||
});
|
||||
|
||||
long earliestTime;
|
||||
if (params.getId().equals(NetworkParameters.ID_REGTEST)) {
|
||||
|
@ -288,10 +197,7 @@ public class App {
|
|||
earliestTime = Utils.currentTimeSeconds();
|
||||
}
|
||||
|
||||
bs.addOPRET(Arrays.copyOfRange(App.PKHASH, 0, 12), earliestTime);
|
||||
// bs.addOPRET(Sha256Hash.hash("test1".getBytes()), earliestTime);
|
||||
// bs.addOPRET(Utils.HEX.decode("0f490dee643b01b06e0ea84c253a90050a3543cfb7c74319fb47b04afee5b872"),
|
||||
// earliestTime);
|
||||
bs.addVerifyKey(SK.getVerifyKey(), earliestTime);
|
||||
|
||||
final OPRETWalletAppKit kit = new OPRETWalletAppKit(params, new File("."), "opretwallet" + params.getId(), bs);
|
||||
|
||||
|
@ -375,10 +281,7 @@ public class App {
|
|||
final NetworkParameters params = wallet.getNetworkParameters();
|
||||
|
||||
final Transaction t = new Transaction(params);
|
||||
final byte[] sig = SK.sign(REVOKEMSG);
|
||||
|
||||
final Script script = new ScriptBuilder().op(OP_RETURN).data(Utils.HEX.decode("ec0f")).data(sig)
|
||||
.data(Arrays.copyOfRange(PKHASH, 0, 12)).build();
|
||||
final Script script = OPRETECParser.getRevokeScript(SK);
|
||||
t.addOutput(Coin.ZERO, script);
|
||||
final SendRequest request = SendRequest.forTx(t);
|
||||
request.ensureMinRequiredFee = true;
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
/**
|
||||
* Copyright 2013 Bruno Oliveira, and individual contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.tcpid.opretj.testapp;
|
||||
|
||||
import static org.libsodium.jni.NaCl.sodium;
|
||||
import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES;
|
||||
import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES;
|
||||
import static org.libsodium.jni.SodiumConstants.SIGNATURE_BYTES;
|
||||
|
||||
import org.libsodium.jni.crypto.Random;
|
||||
import org.libsodium.jni.crypto.Util;
|
||||
import org.libsodium.jni.encoders.Encoder;
|
||||
import org.libsodium.jni.keys.KeyPair;
|
||||
import org.libsodium.jni.keys.PrivateKey;
|
||||
|
||||
public class SigningKey {
|
||||
|
||||
private final byte[] seed;
|
||||
private final byte[] secretKey;
|
||||
|
||||
private final VerifyKey verifyKey;
|
||||
|
||||
public SigningKey() {
|
||||
this(new Random().randomBytes(SECRETKEY_BYTES));
|
||||
}
|
||||
|
||||
public SigningKey(byte[] seed) {
|
||||
Util.checkLength(seed, SECRETKEY_BYTES);
|
||||
this.seed = seed;
|
||||
this.secretKey = Util.zeros(SECRETKEY_BYTES * 2);
|
||||
final byte[] publicKey = Util.zeros(PUBLICKEY_BYTES);
|
||||
Util.isValid(sodium().crypto_sign_ed25519_seed_keypair(publicKey, secretKey, seed),
|
||||
"Failed to generate a key pair");
|
||||
|
||||
this.verifyKey = new VerifyKey(publicKey);
|
||||
}
|
||||
|
||||
public SigningKey(String seed, Encoder encoder) {
|
||||
this(encoder.decode(seed));
|
||||
}
|
||||
|
||||
public KeyPair getKeyPair() {
|
||||
final byte[] sk = Util.zeros(SECRETKEY_BYTES);
|
||||
sodium().crypto_sign_ed25519_sk_to_curve25519(sk, this.secretKey);
|
||||
return new KeyPair(sk);
|
||||
|
||||
}
|
||||
|
||||
public PrivateKey getPrivateKey() {
|
||||
final byte[] sk = Util.zeros(SECRETKEY_BYTES);
|
||||
sodium().crypto_sign_ed25519_sk_to_curve25519(sk, this.secretKey);
|
||||
return new PrivateKey(sk);
|
||||
}
|
||||
|
||||
public VerifyKey getVerifyKey() {
|
||||
return this.verifyKey;
|
||||
}
|
||||
|
||||
public byte[] sign(byte[] message) {
|
||||
byte[] signature = Util.prependZeros(SIGNATURE_BYTES, message);
|
||||
final int[] bufferLen = new int[1];
|
||||
sodium().crypto_sign_ed25519(signature, bufferLen, message, message.length, secretKey);
|
||||
signature = Util.slice(signature, 0, SIGNATURE_BYTES);
|
||||
return signature;
|
||||
}
|
||||
|
||||
public String sign(String message, Encoder encoder) {
|
||||
final byte[] signature = sign(encoder.decode(message));
|
||||
return encoder.encode(signature);
|
||||
}
|
||||
|
||||
public byte[] toBytes() {
|
||||
return seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Encoder.HEX.encode(seed);
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/**
|
||||
* Copyright 2013 Bruno Oliveira, and individual contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.tcpid.opretj.testapp;
|
||||
|
||||
import static org.libsodium.jni.NaCl.sodium;
|
||||
import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES;
|
||||
import static org.libsodium.jni.SodiumConstants.SIGNATURE_BYTES;
|
||||
|
||||
import org.libsodium.jni.crypto.Util;
|
||||
import org.libsodium.jni.encoders.Encoder;
|
||||
import org.libsodium.jni.keys.PublicKey;
|
||||
|
||||
public class VerifyKey {
|
||||
|
||||
private final byte[] key;
|
||||
|
||||
public VerifyKey(byte[] key) {
|
||||
Util.checkLength(key, PUBLICKEY_BYTES);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public VerifyKey(String key, Encoder encoder) {
|
||||
this(encoder.decode(key));
|
||||
}
|
||||
|
||||
public PublicKey getPublicKey() {
|
||||
final byte[] pk = Util.zeros(PUBLICKEY_BYTES);
|
||||
sodium().crypto_sign_ed25519_pk_to_curve25519(pk, this.key);
|
||||
return new PublicKey(pk);
|
||||
}
|
||||
|
||||
public byte[] toBytes() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Encoder.HEX.encode(key);
|
||||
}
|
||||
|
||||
public boolean verify(byte[] message, byte[] signature) {
|
||||
Util.checkLength(signature, SIGNATURE_BYTES);
|
||||
final byte[] sigAndMsg = Util.merge(signature, message);
|
||||
final byte[] buffer = Util.zeros(sigAndMsg.length);
|
||||
final int[] bufferLen = new int[1];
|
||||
|
||||
return Util.isValid(sodium().crypto_sign_ed25519_open(buffer, bufferLen, sigAndMsg, sigAndMsg.length, key),
|
||||
"signature was forged or corrupted");
|
||||
}
|
||||
|
||||
public boolean verify(String message, String signature, Encoder encoder) {
|
||||
return verify(encoder.decode(message), encoder.decode(signature));
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue