Skip to content

Commit dc1eac5

Browse files
committed
WIP
1 parent eb5bdee commit dc1eac5

File tree

73 files changed

+654
-312
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+654
-312
lines changed

xrpl4j-core/src/main/java/org/xrpl/xrpl4j/crypto/keys/PrivateKey.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ public class PrivateKey implements PrivateKeyable, javax.security.auth.Destroyab
5959
*/
6060
public static final UnsignedByte SECP256K1_PREFIX = UnsignedByte.of(0x00);
6161

62+
/**
63+
* This is to distinguish ElGamal secp256k1 private keys (Generated from 32 bytes of entropy) from
64+
* secp256k1 private keys (Generated from 16 bytes of entropy) that are used for signing transactions.
65+
* Note: This is not a standard XRPL prefix. It is specific to xrpl4j.
66+
*/
6267
public static final UnsignedByte ELGAMAL_SECP256K1_PREFIX = UnsignedByte.of(0x01);
6368

6469

xrpl4j-core/src/main/java/org/xrpl/xrpl4j/crypto/keys/PublicKey.java

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public interface PublicKey {
6060
* {@link PublicKey} that can be used as the {@link Transaction#signingPublicKey()} value for multi-signed
6161
* transactions.
6262
*/
63-
PublicKey MULTI_SIGN_PUBLIC_KEY = PublicKey.builder().value(UnsignedByteArray.empty()).build();
63+
PublicKey MULTI_SIGN_PUBLIC_KEY = PublicKey.builder().value(UnsignedByteArray.empty()).keyType(KeyType.ED25519).build();
6464

6565
/**
6666
* Instantiates a new builder.
@@ -75,10 +75,11 @@ static ImmutablePublicKey.Builder builder() {
7575
* Construct a {@link PublicKey} from a base58-encoded {@link String}.
7676
*
7777
* @param base58EncodedPublicKey A base58-encoded {@link String}.
78+
* @param keyType The {@link KeyType} of the public key.
7879
*
7980
* @return A {@link PrivateKey}.
8081
*/
81-
static PublicKey fromBase58EncodedPublicKey(final String base58EncodedPublicKey) {
82+
static PublicKey fromBase58EncodedPublicKey(final String base58EncodedPublicKey, KeyType keyType) {
8283
Objects.requireNonNull(base58EncodedPublicKey);
8384

8485
if (base58EncodedPublicKey.isEmpty()) {
@@ -87,17 +88,19 @@ static PublicKey fromBase58EncodedPublicKey(final String base58EncodedPublicKey)
8788

8889
return PublicKey.builder()
8990
.value(PublicKeyCodec.getInstance().decodeAccountPublicKey(base58EncodedPublicKey))
91+
.keyType(keyType)
9092
.build();
9193
}
9294

9395
/**
9496
* Construct a {@link PrivateKey} from a base16-encoded (HEX) {@link String}.
9597
*
9698
* @param base16EncodedPublicKey A base16-encoded (HEX) {@link String}.
99+
* @param keyType The {@link KeyType} of the public key.
97100
*
98101
* @return A {@link PrivateKey}.
99102
*/
100-
static PublicKey fromBase16EncodedPublicKey(final String base16EncodedPublicKey) {
103+
static PublicKey fromBase16EncodedPublicKey(final String base16EncodedPublicKey, KeyType keyType) {
101104
Objects.requireNonNull(base16EncodedPublicKey);
102105

103106
if (base16EncodedPublicKey.isEmpty()) {
@@ -106,6 +109,7 @@ static PublicKey fromBase16EncodedPublicKey(final String base16EncodedPublicKey)
106109

107110
return PublicKey.builder()
108111
.value(UnsignedByteArray.of(BaseEncoding.base16().decode(base16EncodedPublicKey.toUpperCase())))
112+
.keyType(keyType)
109113
.build();
110114
}
111115

@@ -116,6 +120,13 @@ static PublicKey fromBase16EncodedPublicKey(final String base16EncodedPublicKey)
116120
*/
117121
UnsignedByteArray value();
118122

123+
/**
124+
* The type of this key.
125+
*
126+
* @return A {@link KeyType}.
127+
*/
128+
KeyType keyType();
129+
119130
/**
120131
* The public-key, as a base-58 encoded {@link String}.
121132
*
@@ -141,16 +152,6 @@ default String base16Value() {
141152
return this.value().hexValue();
142153
}
143154

144-
/**
145-
* The type of this key.
146-
*
147-
* @return A {@link KeyType}.
148-
*/
149-
@Derived
150-
default KeyType keyType() {
151-
return this.base16Value().startsWith("ED") ? KeyType.ED25519 : KeyType.SECP256K1;
152-
}
153-
154155
/**
155156
* Derive an XRPL address from this public key.
156157
*

xrpl4j-core/src/main/java/org/xrpl/xrpl4j/crypto/keys/Seed.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ public static KeyPair deriveKeyPair(final Seed seed) {
393393

394394
return KeyPair.builder()
395395
.privateKey(PrivateKey.fromNaturalBytes(UnsignedByteArray.of(privateKey.getEncoded()), KeyType.ED25519))
396-
.publicKey(PublicKey.fromBase16EncodedPublicKey(prefixedPublicKey.hexValue()))
396+
.publicKey(PublicKey.fromBase16EncodedPublicKey(prefixedPublicKey.hexValue(), KeyType.ED25519))
397397
.build();
398398
}
399399
}
@@ -462,7 +462,7 @@ private static KeyPair deriveKeyPair(final UnsignedByteArray seedBytes, final in
462462

463463
return KeyPair.builder()
464464
.privateKey(PrivateKey.fromPrefixedBytes(Secp256k1.toUnsignedByteArray(privateKeyInt, 33)))
465-
.publicKey(PublicKey.builder().value(publicKeyByteArray).build())
465+
.publicKey(PublicKey.builder().value(publicKeyByteArray).keyType(KeyType.SECP256K1).build())
466466
.build();
467467
}
468468

@@ -628,7 +628,7 @@ public static KeyPair deriveKeyPair(final Seed seed) {
628628

629629
return KeyPair.builder()
630630
.privateKey(PrivateKey.fromNaturalBytes(entropyBytes, KeyType.ELGAMAL_SECP256K1))
631-
.publicKey(PublicKey.builder().value(publicKeyBytes).build())
631+
.publicKey(PublicKey.builder().value(publicKeyBytes).keyType(KeyType.ELGAMAL_SECP256K1).build())
632632
.build();
633633
}
634634
}

xrpl4j-core/src/main/java/org/xrpl/xrpl4j/crypto/keys/bc/BcKeyUtils.java

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,35 @@ public static PrivateKey toPrivateKey(final Ed25519PrivateKeyParameters ed25519P
9393
}
9494

9595
/**
96-
* Convert from a {@link ECPrivateKeyParameters} to a {@link PrivateKey}.
96+
* Convert from a {@link ECPrivateKeyParameters} to a {@link PrivateKey} with SECP256K1 key type.
9797
*
9898
* @param ecPrivateKeyParameters A {@link ECPrivateKeyParameters}.
9999
*
100100
* @return A {@link PrivateKey}.
101101
*/
102102
public static PrivateKey toPrivateKey(final ECPrivateKeyParameters ecPrivateKeyParameters) {
103-
return PrivateKey.fromPrefixedBytes(
104-
// Call `UnsignedByteArray.from` to properly prefix-pad the BigInteger's bytes.
105-
Secp256k1.toUnsignedByteArray(ecPrivateKeyParameters.getD(), 33)
106-
);
103+
return toPrivateKey(ecPrivateKeyParameters, KeyType.SECP256K1);
104+
}
105+
106+
/**
107+
* Convert from a {@link ECPrivateKeyParameters} to a {@link PrivateKey}.
108+
*
109+
* @param ecPrivateKeyParameters A {@link ECPrivateKeyParameters}.
110+
* @param keyType The {@link KeyType} for the private key.
111+
*
112+
* @return A {@link PrivateKey}.
113+
*/
114+
public static PrivateKey toPrivateKey(final ECPrivateKeyParameters ecPrivateKeyParameters, KeyType keyType) {
115+
// Call `UnsignedByteArray.from` to properly prefix-pad the BigInteger's bytes.
116+
UnsignedByteArray prefixedBytes = Secp256k1.toUnsignedByteArray(ecPrivateKeyParameters.getD(), 33);
117+
118+
// If keyType is ELGAMAL_SECP256K1, replace the 0th byte with ELGAMAL_SECP256K1_PREFIX
119+
if (keyType == KeyType.ELGAMAL_SECP256K1) {
120+
prefixedBytes = UnsignedByteArray.of(PrivateKey.ELGAMAL_SECP256K1_PREFIX)
121+
.append(prefixedBytes.slice(1, 33));
122+
}
123+
124+
return PrivateKey.fromPrefixedBytes(prefixedBytes);
107125
}
108126

109127
/**
@@ -141,6 +159,7 @@ public static PublicKey toPublicKey(final Ed25519PublicKeyParameters ed25519Publ
141159

142160
return PublicKey.builder()
143161
.value(prefixedPublicKey)
162+
.keyType(ED25519)
144163
.build();
145164
}
146165

@@ -151,12 +170,13 @@ public static PublicKey toPublicKey(final Ed25519PublicKeyParameters ed25519Publ
151170
*
152171
* @return A {@link PublicKey}.
153172
*/
154-
public static PublicKey toPublicKey(final ECPublicKeyParameters ecPublicKeyParameters) {
173+
public static PublicKey toPublicKey(final ECPublicKeyParameters ecPublicKeyParameters, KeyType keyType) {
155174
Objects.requireNonNull(ecPublicKeyParameters);
156175
// The binary version of an EC PublicKey is the encoded ECPoint, compressed.
157176
final byte[] encodedPublicKey = ecPublicKeyParameters.getQ().getEncoded(true);
158177
return PublicKey.builder()
159178
.value(UnsignedByteArray.of(encodedPublicKey))
179+
.keyType(keyType)
160180
.build();
161181
}
162182

@@ -189,7 +209,11 @@ public static PublicKey toPublicKey(final PrivateKey privateKey) {
189209
} else if (privateKey.keyType() == KeyType.SECP256K1) {
190210
final ECPrivateKeyParameters ecPrivateKeyParameters = toEcPrivateKeyParams(privateKey);
191211
final ECPublicKeyParameters ecPublicKeyParameters = toPublicKey(ecPrivateKeyParameters);
192-
return toPublicKey(ecPublicKeyParameters);
212+
return toPublicKey(ecPublicKeyParameters, KeyType.SECP256K1);
213+
} else if (privateKey.keyType() == KeyType.ELGAMAL_SECP256K1){
214+
final ECPrivateKeyParameters ecPrivateKeyParameters = toEcPrivateKeyParams(privateKey);
215+
final ECPublicKeyParameters ecPublicKeyParameters = toPublicKey(ecPrivateKeyParameters);
216+
return toPublicKey(ecPublicKeyParameters, KeyType.ELGAMAL_SECP256K1);
193217
} else {
194218
throw new IllegalArgumentException("Invalid KeyType: " + privateKey.keyType());
195219
}
@@ -218,7 +242,7 @@ public static Ed25519PrivateKeyParameters toEd25519PrivateKeyParams(PrivateKey p
218242
*/
219243
public static ECPublicKeyParameters toEcPublicKeyParameters(final PublicKey publicKey) {
220244
Objects.requireNonNull(publicKey);
221-
Preconditions.checkArgument(publicKey.keyType() == KeyType.SECP256K1);
245+
Preconditions.checkArgument(publicKey.keyType() == KeyType.SECP256K1 || publicKey.keyType() == KeyType.ELGAMAL_SECP256K1);
222246

223247
ECPoint ecPoint = PARAMS.getCurve()
224248
.decodePoint(publicKey.value().toByteArray());
@@ -234,7 +258,8 @@ public static ECPublicKeyParameters toEcPublicKeyParameters(final PublicKey publ
234258
*/
235259
public static ECPrivateKeyParameters toEcPrivateKeyParams(final PrivateKey privateKey) {
236260
Objects.requireNonNull(privateKey);
237-
Preconditions.checkArgument(privateKey.keyType() == KeyType.SECP256K1, "KeyType must be SECP256K1");
261+
Preconditions.checkArgument(privateKey.keyType() == KeyType.SECP256K1 || privateKey.keyType() == KeyType.ELGAMAL_SECP256K1
262+
, "KeyType must be SECP256K1 or ELGAMAL_SECP256K1");
238263

239264
// From http://www.secg.org/sec1-v2.pdf: A PrivateKey consists of an elliptic curve secret key `d` which is an
240265
// integer in the interval [1, n − 1]. Therefore, it is safe to assume that the signum below should always be 1.

xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/PublicKeyDeserializer.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.fasterxml.jackson.core.JsonParser;
2424
import com.fasterxml.jackson.databind.DeserializationContext;
2525
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
26+
import org.xrpl.xrpl4j.codec.addresses.KeyType;
2627
import org.xrpl.xrpl4j.crypto.keys.PublicKey;
2728

2829
import java.io.IOException;
@@ -41,7 +42,8 @@ public PublicKeyDeserializer() {
4142

4243
@Override
4344
public PublicKey deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
44-
return PublicKey.fromBase16EncodedPublicKey(jsonParser.getText());
45+
KeyType keyType = !jsonParser.getText().startsWith("ED") ? KeyType.SECP256K1 : KeyType.ED25519;
46+
return PublicKey.fromBase16EncodedPublicKey(jsonParser.getText(), keyType);
4547
}
4648

4749
}

xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/Batch.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.common.annotations.Beta;
2727
import com.google.common.base.Preconditions;
2828
import org.immutables.value.Value;
29+
import org.xrpl.xrpl4j.codec.addresses.KeyType;
2930
import org.xrpl.xrpl4j.codec.addresses.UnsignedByteArray;
3031
import org.xrpl.xrpl4j.crypto.keys.PublicKey;
3132
import org.xrpl.xrpl4j.model.flags.BatchFlags;
@@ -114,7 +115,7 @@ default BatchFlags flags() {
114115
@Value.Check
115116
default void checkRawTransactions() {
116117
// TODO: Replace with constant once https://github.com/XRPLF/xrpl4j/issues/683 is merged.
117-
final PublicKey emptyPublicKey = PublicKey.builder().value(UnsignedByteArray.empty()).build();
118+
final PublicKey emptyPublicKey = PublicKey.builder().value(UnsignedByteArray.empty()).keyType(KeyType.ED25519).build();
118119
final XrpCurrencyAmount zeroFee = XrpCurrencyAmount.ofDrops(0);
119120

120121
// Check 1: Validate transaction count (must be between 2 and 8 inclusive)

0 commit comments

Comments
 (0)