Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.arkecosystem.crypto.exceptions;

public class InvalidProofOfPossessionException extends IllegalArgumentException {

public InvalidProofOfPossessionException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
public class ValidatorRegistrationBuilder
extends AbstractTransactionBuilder<ValidatorRegistrationBuilder> {

public ValidatorRegistrationBuilder validatorPassphrase(String passphrase) {
ProofOfPossession.Result pop = ProofOfPossession.fromMnemonic(passphrase);
public ValidatorRegistrationBuilder validatorProof(
String passphrase, String registrantAddress) {
ProofOfPossession.Result pop =
ProofOfPossession.fromMnemonic(passphrase, registrantAddress);
this.transaction.validatorPublicKey = Hex.encode(pop.pk);
this.transaction.validatorProof = Hex.encode(pop.pop);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@

public class ValidatorUpdateBuilder extends AbstractTransactionBuilder<ValidatorUpdateBuilder> {

public ValidatorUpdateBuilder validatorPassphrase(String passphrase) {
ProofOfPossession.Result pop = ProofOfPossession.fromMnemonic(passphrase);
public ValidatorUpdateBuilder validatorProof(String passphrase, String registrantAddress) {
ProofOfPossession.Result pop =
ProofOfPossession.fromMnemonic(passphrase, registrantAddress);
this.transaction.validatorPublicKey = Hex.encode(pop.pk);
this.transaction.validatorProof = Hex.encode(pop.pop);

Expand All @@ -26,5 +27,4 @@ protected AbstractTransaction getTransactionInstance() {
protected ValidatorUpdateBuilder instance() {
return this;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@

import java.nio.charset.StandardCharsets;
import java.text.Normalizer;
import org.arkecosystem.crypto.configuration.Network;
import org.arkecosystem.crypto.encoding.Hex;
import org.arkecosystem.crypto.exceptions.InvalidProofOfPossessionException;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
import org.web3j.abi.TypeEncoder;
import org.web3j.abi.datatypes.DynamicBytes;
import org.web3j.abi.datatypes.generated.Uint256;
import supranational.blst.P1;
import supranational.blst.P2;
import supranational.blst.SecretKey;

public class ProofOfPossession {

private static final String POP_DST = "BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
private static final String POP_DST = "MAINSAIL_BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";

public static final class Result {
public final byte[] pk;
Expand All @@ -32,16 +37,33 @@ public static String deriveBlsPublicKey(String passphrase) {
return Hex.encode(new P1(deriveChildSk(passphrase)).compress());
}

public static Result buildProofOfPossession(byte[] secretKeyBytes) {
public static Result buildProofOfPossession(byte[] secretKeyBytes, String registrantAddress) {
if (!Address.validate(registrantAddress)) {
throw new InvalidProofOfPossessionException(
"registrantAddress must be a valid address. Got " + registrantAddress + ".");
}

SecretKey sk = new SecretKey();
sk.from_bendian(secretKeyBytes);

// 1. Derive the compressed public key (48-byte G1).
byte[] pk = new P1(sk).compress();
P2 sig = new P2().hash_to(pk, POP_DST).sign_with(sk);

// 2. Hash chainId ‖ registrantAddress ‖ pk to a G2 point under POP_DST, binding
// the proof to the registrant and chain.
String packedChainId = TypeEncoder.encodePacked(new Uint256(Network.get().chainId()));
String packedAddress =
TypeEncoder.encodePacked(new org.web3j.abi.datatypes.Address(registrantAddress));
String packedPk = TypeEncoder.encodePacked(new DynamicBytes(pk));
byte[] message = Hex.decode(packedChainId + packedAddress + packedPk);

// 3. Sign the hashed G2 point with the secret key.
P2 sig = new P2().hash_to(message, POP_DST).sign_with(sk);
return new Result(pk, sig.compress());
}

public static Result fromMnemonic(String passphrase) {
return buildProofOfPossession(deriveBlsPrivateKey(passphrase));
public static Result fromMnemonic(String passphrase, String registrantAddress) {
return buildProofOfPossession(deriveBlsPrivateKey(passphrase), registrantAddress);
}

private static SecretKey deriveChildSk(String passphrase) {
Expand All @@ -62,6 +84,4 @@ private static byte[] passphraseToSeed(String passphrase) {
gen.init(pass, salt, 2048);
return ((KeyParameter) gen.generateDerivedParameters(512)).getKey();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import org.arkecosystem.crypto.encoding.Hex;
import org.arkecosystem.crypto.enums.AbiFunction;
import org.arkecosystem.crypto.enums.ContractAbiType;
import org.arkecosystem.crypto.utils.ProofOfPossession;

public final class TransactionEncoder {

Expand Down Expand Up @@ -41,8 +40,9 @@ public static String usernameResignation() {
Collections.emptyList());
}

public static String validatorRegistration(String passphrase) {
ProofOfPossession.Result pop = ProofOfPossession.fromMnemonic(passphrase);
public static String validatorRegistration(String passphrase, String registrantAddress) {
ProofOfPossession.Result pop =
ProofOfPossession.fromMnemonic(passphrase, registrantAddress);
return encode(
ContractAbiType.CONSENSUS,
AbiFunction.VALIDATOR_REGISTRATION,
Expand All @@ -56,8 +56,9 @@ public static String validatorResignation() {
Collections.emptyList());
}

public static String validatorUpdate(String passphrase) {
ProofOfPossession.Result pop = ProofOfPossession.fromMnemonic(passphrase);
public static String validatorUpdate(String passphrase, String registrantAddress) {
ProofOfPossession.Result pop =
ProofOfPossession.fromMnemonic(passphrase, registrantAddress);
return encode(
ContractAbiType.CONSENSUS,
AbiFunction.UPDATE_VALIDATOR,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.Map;
import org.arkecosystem.crypto.AbstractTest;
import org.arkecosystem.crypto.encoding.Hex;
import org.arkecosystem.crypto.identities.Address;
import org.junit.jupiter.api.Test;

public class ValidatorRegistrationBuilderTest extends AbstractTest {
Expand All @@ -25,7 +26,8 @@ public void it_should_sign_it_with_a_passphrase() throws Exception {
.nonce(Long.parseLong(data.get("nonce").toString()))
.network(((Number) data.get("network")).intValue())
.gasLimit(((Number) data.get("gasLimit")).longValue())
.validatorPassphrase(VALIDATOR_PASSPHRASE)
.validatorProof(
VALIDATOR_PASSPHRASE, Address.fromPassphrase(this.passphrase))
.recipientAddress((String) data.get("recipientAddress"))
.sign(this.passphrase);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.Map;
import org.arkecosystem.crypto.AbstractTest;
import org.arkecosystem.crypto.encoding.Hex;
import org.arkecosystem.crypto.identities.Address;
import org.junit.jupiter.api.Test;

public class ValidatorUpdateBuilderTest extends AbstractTest {
Expand All @@ -25,7 +26,8 @@ public void it_should_sign_it_with_a_passphrase() throws Exception {
.nonce(Long.parseLong(data.get("nonce").toString()))
.network(((Number) data.get("network")).intValue())
.gasLimit(((Number) data.get("gasLimit")).longValue())
.validatorPassphrase(VALIDATOR_PASSPHRASE)
.validatorProof(
VALIDATOR_PASSPHRASE, Address.fromPassphrase(this.passphrase))
.recipientAddress((String) data.get("recipientAddress"))
.sign(this.passphrase);

Expand Down
Loading