diff --git a/src/main/java/com/casper/sdk/model/clvalue/CLValuePublicKey.java b/src/main/java/com/casper/sdk/model/clvalue/CLValuePublicKey.java index 6741669c8..53a7d6785 100644 --- a/src/main/java/com/casper/sdk/model/clvalue/CLValuePublicKey.java +++ b/src/main/java/com/casper/sdk/model/clvalue/CLValuePublicKey.java @@ -8,7 +8,6 @@ import dev.oak3.sbs4j.DeserializerBuffer; import dev.oak3.sbs4j.SerializerBuffer; import dev.oak3.sbs4j.exception.ValueSerializationException; -import dev.oak3.sbs4j.util.ByteUtils; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; @@ -54,7 +53,7 @@ protected void serializeValue(final SerializerBuffer ser) throws ValueSerializat @Override public void deserializeCustom(final DeserializerBuffer deser) throws Exception { - this.setValue(PublicKey.fromTaggedHexString(ByteUtils.encodeHexString(deser.readByteArray(33)))); + this.setValue(PublicKey.deserialize(deser)); } @Override diff --git a/src/main/java/com/casper/sdk/model/key/AlgorithmTag.java b/src/main/java/com/casper/sdk/model/key/AlgorithmTag.java index d822fe6b8..d37ee591d 100644 --- a/src/main/java/com/casper/sdk/model/key/AlgorithmTag.java +++ b/src/main/java/com/casper/sdk/model/key/AlgorithmTag.java @@ -18,9 +18,11 @@ @Getter @AllArgsConstructor(access = AccessLevel.PRIVATE) public enum AlgorithmTag implements Tag { - SECP256K1((byte) 0x02), ED25519((byte) 0x01); + SECP256K1((byte) 0x02, 33), ED25519((byte) 0x01, 32); private final byte byteTag; + /** The number of bytes for a key excluding the tag byte */ + private final int length; public static AlgorithmTag getByTag(byte byteTag) throws NoSuchAlgorithmException { for (AlgorithmTag a : values()) { @@ -29,4 +31,5 @@ public static AlgorithmTag getByTag(byte byteTag) throws NoSuchAlgorithmExceptio } throw new NoSuchAlgorithmException(); } + } diff --git a/src/main/java/com/casper/sdk/model/key/PublicKey.java b/src/main/java/com/casper/sdk/model/key/PublicKey.java index 2412a2a99..21fc7cb80 100644 --- a/src/main/java/com/casper/sdk/model/key/PublicKey.java +++ b/src/main/java/com/casper/sdk/model/key/PublicKey.java @@ -8,11 +8,14 @@ import com.syntifi.crypto.key.Ed25519PublicKey; import com.syntifi.crypto.key.Secp256k1PublicKey; import com.syntifi.crypto.key.hash.Blake2b; +import dev.oak3.sbs4j.DeserializerBuffer; +import dev.oak3.sbs4j.exception.ValueDeserializationException; import dev.oak3.sbs4j.util.ByteUtils; import lombok.NoArgsConstructor; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.util.Arrays; @@ -29,30 +32,37 @@ public class PublicKey extends AbstractSerializedKeyTaggedHex { public static PublicKey fromTaggedHexString(String hex) throws NoSuchAlgorithmException, IllegalArgumentException { - byte[] bytes = ByteUtils.parseHexString(hex); - return PublicKey.fromBytes(bytes); + return PublicKey.fromBytes(ByteUtils.parseHexString(hex)); } - public static PublicKey fromBytes(byte[] bytes) throws NoSuchAlgorithmException { - PublicKey object = new PublicKey(); + public static PublicKey fromBytes(final byte[] bytes) throws NoSuchAlgorithmException { + final PublicKey object = new PublicKey(); object.setTag(AlgorithmTag.getByTag(bytes[0])); object.setKey(Arrays.copyOfRange(bytes, 1, bytes.length)); - return object; } - public static PublicKey fromAbstractPublicKey(AbstractPublicKey key) { - PublicKey object = new PublicKey(); - object.setTag((key instanceof Secp256k1PublicKey) - ? AlgorithmTag.SECP256K1 - : AlgorithmTag.ED25519); + public static PublicKey fromAbstractPublicKey(final AbstractPublicKey key) { + final PublicKey object = new PublicKey(); + object.setTag((key instanceof Secp256k1PublicKey) ? AlgorithmTag.SECP256K1 : AlgorithmTag.ED25519); object.setKey(key.getKey()); return object; } - public String generateAccountHash(boolean includePrefix) throws IOException { - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - byteArrayOutputStream.write(getTag().toString().toLowerCase().getBytes("UTF-8")); + public static PublicKey deserialize(final DeserializerBuffer deser) throws ValueDeserializationException, NoSuchAlgorithmException { + // Obtain algorithm tag + final AlgorithmTag tag = AlgorithmTag.getByTag(deser.readU8()); + final PublicKey publicKey = new PublicKey(); + publicKey.setTag(tag); + // Read the required number of bytes for the algorithm length + publicKey.setKey(deser.readByteArray(tag.getLength())); + return publicKey; + } + + + public String generateAccountHash(final boolean includePrefix) throws IOException { + final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + byteArrayOutputStream.write(getTag().toString().toLowerCase().getBytes(StandardCharsets.UTF_8)); byteArrayOutputStream.write(0); byteArrayOutputStream.write(getKey()); @@ -60,8 +70,8 @@ public String generateAccountHash(boolean includePrefix) throws IOException { } @JsonCreator - public void createPublicKey(String key) throws NoSuchAlgorithmException, IllegalArgumentException { - PublicKey obj = PublicKey.fromTaggedHexString(key); + public void createPublicKey(final String key) throws NoSuchAlgorithmException, IllegalArgumentException { + final PublicKey obj = PublicKey.fromTaggedHexString(key); this.setTag(obj.getTag()); this.setKey(obj.getKey()); } diff --git a/src/test/java/com/casper/sdk/model/clvalue/CLValueTests.java b/src/test/java/com/casper/sdk/model/clvalue/CLValueTests.java index 7d5562ac9..eb19b52e2 100644 --- a/src/test/java/com/casper/sdk/model/clvalue/CLValueTests.java +++ b/src/test/java/com/casper/sdk/model/clvalue/CLValueTests.java @@ -2,9 +2,14 @@ import com.casper.sdk.exception.DynamicInstanceException; import com.casper.sdk.exception.NoSuchTypeException; +import com.casper.sdk.helper.CasperKeyHelper; import com.casper.sdk.model.clvalue.cltype.*; import com.casper.sdk.model.clvalue.serde.Target; import com.casper.sdk.model.deploy.NamedArg; +import com.casper.sdk.model.key.PublicKey; +import com.syntifi.crypto.key.AbstractPublicKey; +import com.syntifi.crypto.key.Ed25519PrivateKey; +import com.syntifi.crypto.key.Secp256k1PrivateKey; import com.syntifi.crypto.key.encdec.Hex; import dev.oak3.sbs4j.DeserializerBuffer; import dev.oak3.sbs4j.SerializerBuffer; @@ -323,4 +328,40 @@ void nestedListSerialization() throws Exception { assertThat(clValueList.getBytes(), is(outerList.getBytes())); } + + @Test + void secp256k1PublicKeySerialization() throws Exception { + + final Secp256k1PrivateKey secp256k1PrivateKey = CasperKeyHelper.createRandomSecp256k1Key(); + final AbstractPublicKey abstractPublicKey = secp256k1PrivateKey.derivePublicKey(); + final PublicKey publicKey = PublicKey.fromAbstractPublicKey(abstractPublicKey); + + final CLValuePublicKey clValuePublicKey = new CLValuePublicKey(publicKey); + final SerializerBuffer ser = new SerializerBuffer(); + clValuePublicKey.serialize(ser, Target.BYTE); + + final byte[] bytes = ser.toByteArray(); + assertThat(bytes.length, is(39)); + + final CLValuePublicKey deserialized = (CLValuePublicKey) clValuePublicKey.deserialize(new DeserializerBuffer(bytes), Target.BYTE); + assertThat(deserialized.getBytes(), is(clValuePublicKey.getBytes())); + } + + @Test + void Ed25519PublicKeySerialization() throws Exception { + Ed25519PrivateKey randomEd25519Key = CasperKeyHelper.createRandomEd25519Key(); + final AbstractPublicKey abstractPublicKey = randomEd25519Key.derivePublicKey(); + final PublicKey publicKey = PublicKey.fromAbstractPublicKey(abstractPublicKey); + + final CLValuePublicKey clValuePublicKey = new CLValuePublicKey(publicKey); + final SerializerBuffer ser = new SerializerBuffer(); + clValuePublicKey.serialize(ser, Target.BYTE); + + final byte[] bytes = ser.toByteArray(); + assertThat(bytes.length, is(38)); + + final CLValuePublicKey deserialized = (CLValuePublicKey) clValuePublicKey.deserialize(new DeserializerBuffer(bytes), Target.BYTE); + assertThat(deserialized.getBytes(), is(clValuePublicKey.getBytes())); + } + }