All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hedera.hashgraph.sdk.PublicKeyECDSA Maven / Gradle / Ivy

There is a newer version: 2.40.0
Show newest version
/*-
 *
 * Hedera Java SDK
 *
 * Copyright (C) 2020 - 2024 Hedera Hashgraph, LLC
 *
 * 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 com.hedera.hashgraph.sdk;

import com.google.protobuf.ByteString;
import com.hedera.hashgraph.sdk.proto.SignaturePair;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.signers.ECDSASigner;

import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;

import static com.hedera.hashgraph.sdk.Crypto.calcKeccak256;

/**
 * Encapsulate the ECDSA public key.
 */
public class PublicKeyECDSA extends PublicKey {
    // Compressed 33 byte form
    private byte[] keyData;

    /**
     * Constructor.
     *
     * @param keyData                   the byte array representing the key
     */
    PublicKeyECDSA(byte[] keyData) {
        this.keyData = keyData;
    }

    /**
     * Create a key from a byte array representation.
     *
     * @param publicKey                 the byte array representing the key
     * @return                          the new key
     */
    static PublicKeyECDSA fromBytesInternal(byte[] publicKey) {
        if (publicKey.length == 33) {
            // compressed 33 byte raw form
            return new PublicKeyECDSA(publicKey);
        } else if (publicKey.length == 65) {
            // compress the 65 byte form
            return new PublicKeyECDSA(
                Key.ECDSA_SECP256K1_CURVE.getCurve().decodePoint(publicKey).getEncoded(true)
            );
        }

        // Assume a DER-encoded public key descriptor
        return fromSubjectKeyInfoInternal(SubjectPublicKeyInfo.getInstance(publicKey));
    }

    /**
     * Create a key from a subject public key info object.
     *
     * @param subjectPublicKeyInfo      the subject public key info object
     * @return                          the new public key
     */
    static PublicKeyECDSA fromSubjectKeyInfoInternal(SubjectPublicKeyInfo subjectPublicKeyInfo) {
        return fromBytesInternal(subjectPublicKeyInfo.getPublicKeyData().getBytes());
    }

    @Override
    ByteString extractSignatureFromProtobuf(SignaturePair pair) {
        return pair.getECDSA384();
    }

    @Override
    public boolean verify(byte[] message, byte[] signature) {
        var hash = calcKeccak256(message);

        ECDSASigner signer = new ECDSASigner();
        signer.init(false, new ECPublicKeyParameters(
            Key.ECDSA_SECP256K1_CURVE.getCurve().decodePoint(keyData),
            Key.ECDSA_SECP256K1_DOMAIN
        ));

        BigInteger r = new BigInteger(1, Arrays.copyOf(signature, 32));
        BigInteger s = new BigInteger(1, Arrays.copyOfRange(signature, 32, 64));

        return signer.verifySignature(hash, r, s);
    }

    @Override
    com.hedera.hashgraph.sdk.proto.Key toProtobufKey() {
        return com.hedera.hashgraph.sdk.proto.Key.newBuilder()
            .setECDSASecp256K1(ByteString.copyFrom(keyData))
            .build();
    }

    @Override
    SignaturePair toSignaturePairProtobuf(byte[] signature) {
        return SignaturePair.newBuilder()
            .setPubKeyPrefix(ByteString.copyFrom(keyData))
            .setECDSASecp256K1(ByteString.copyFrom(signature))
            .build();
    }

    @Override
    public byte[] toBytesDER() {
        try {
            return new SubjectPublicKeyInfo(
                new AlgorithmIdentifier(ID_EC_PUBLIC_KEY, ID_ECDSA_SECP256K1),
                keyData
            ).getEncoded("DER");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public byte[] toBytes() {
        return toBytesDER();
    }

    @Override
    public byte[] toBytesRaw() {
        return Arrays.copyOf(keyData, keyData.length);
    }

    @Override
    public EvmAddress toEvmAddress() {
        // Calculate the Keccak-256 hash of the uncompressed key without "04" prefix
        byte[] uncompressed = Key.ECDSA_SECP256K1_CURVE
            .getCurve().decodePoint(toBytesRaw()).getEncoded(false);
        byte[] keccakBytes = calcKeccak256(Arrays.copyOfRange(uncompressed, 1, uncompressed.length));

        // Return the last 20 bytes
        return EvmAddress.fromBytes(Arrays.copyOfRange(keccakBytes, 12, 32));
    }

    @Override
    public boolean equals( Object o) {
        if (this == o) {
            return true;
        }

        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        PublicKeyECDSA publicKey = (PublicKeyECDSA) o;
        return Arrays.equals(keyData, publicKey.keyData);
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(keyData);
    }

    @Override
    public boolean isED25519() {
        return false;
    }

    @Override
    public boolean isECDSA() {
        return true;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy