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

com.swirlds.common.crypto.engine.EcdsaSecp256k1Verifier Maven / Gradle / Ivy

Go to download

Swirlds is a software platform designed to build fully-distributed applications that harness the power of the cloud without servers. Now you can develop applications with fairness in decision making, speed, trust and reliability, at a fraction of the cost of traditional server-based platforms.

There is a newer version: 0.56.6
Show newest version
/*
 * Copyright (C) 2021-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.swirlds.common.crypto.engine;

import static com.swirlds.common.utility.CommonUtils.hex;
import static com.swirlds.logging.legacy.LogMarker.TESTING_EXCEPTIONS;

import edu.umd.cs.findbugs.annotations.NonNull;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hyperledger.besu.nativelib.secp256k1.LibSecp256k1;

/**
 * Verifies signatures created with an ECDSA(secp256k1) private key.
 */
public class EcdsaSecp256k1Verifier {

    /**
     * Record for caching thread local variables to avoid allocating memory for each verification.
     */
    private record ThreadLocalCache(
            LibSecp256k1.secp256k1_ecdsa_signature signature,
            LibSecp256k1.secp256k1_pubkey publicKey,
            byte[] uncompressedPublicKeyInput) {
        public ThreadLocalCache() {
            this(
                    new LibSecp256k1.secp256k1_ecdsa_signature(),
                    new LibSecp256k1.secp256k1_pubkey(),
                    new byte[ECDSA_UNCOMPRESSED_KEY_SIZE_WITH_HEADER_BYTE]);
            // set the type header byte for uncompressed public keys, this is always the same
            uncompressedPublicKeyInput[0] = 0x04;
        }
    }

    /** Length of a Keccak256 hash */
    public static final int ECDSA_KECCAK_256_SIZE = 32;

    /** Length of an uncompressed ECDSA public key */
    public static final int ECDSA_UNCOMPRESSED_KEY_SIZE = 64;

    /** Length of an ECDSA signature */
    public static final int ECDSA_SIGNATURE_SIZE = 64;

    /** Length of an uncompressed ECDSA public key including a header byte */
    private static final int ECDSA_UNCOMPRESSED_KEY_SIZE_WITH_HEADER_BYTE = ECDSA_UNCOMPRESSED_KEY_SIZE + 1;

    /**
     * Thread local caches to avoid allocating memory for each verification. They will leak memory for each thread used
     * for verification but only just over 100 bytes to totally worth it.
     */
    private static final ThreadLocal CACHE = ThreadLocal.withInitial(ThreadLocalCache::new);

    /** Logger */
    private static final Logger logger = LogManager.getLogger(EcdsaSecp256k1Verifier.class);

    /**
     * Verifies a ECDSA(secp256k1) signature of a message is valid for a given public key.
     *
     * 

The public key must be 64 bytes where the first 32 bytes are the x-coordinate * of the public key, and the second 32 bytes are the y-coordinate of the public key. * *

The signature must be 64 bytes where the first 32 bytes are the {code r} value of * the signature and the second 32 bytes are the {@code s} value of the signature. * *

The msgHash must be 32 bytes keccak 256 hash of the message that was signed. * *

All encodings are to be unsigned big-endian. * * @param rawSig * the (r, s) signature to be verified * @param msgHash * the 32 bytes 256bit keccak hash of the message that was signed * @param pubKey * the public key to use to verify the signature * @return true if the signature is valid */ public boolean verify(@NonNull final byte[] rawSig, @NonNull final byte[] msgHash, @NonNull final byte[] pubKey) { // check message is already Keccak256 hash size if (msgHash.length != ECDSA_KECCAK_256_SIZE) { logger.warn(TESTING_EXCEPTIONS.getMarker(), () -> "Message is not Keccak256 hash size 32 bytes [ msg = %s ]" .formatted(hex(msgHash))); return false; } // check public key size if (pubKey.length != ECDSA_UNCOMPRESSED_KEY_SIZE) { logger.warn(TESTING_EXCEPTIONS.getMarker(), () -> "Public key is not %d bytes [ publicKey = %s ]" .formatted(ECDSA_UNCOMPRESSED_KEY_SIZE, hex(pubKey))); return false; } // check signature size if (rawSig.length != ECDSA_SIGNATURE_SIZE) { logger.warn(TESTING_EXCEPTIONS.getMarker(), () -> "Signature is not %d bytes [ rawSig = %s ]" .formatted(ECDSA_SIGNATURE_SIZE, hex(rawSig))); return false; } // get cached buffers so we can reuse them and avoid allocating memory for each verification final ThreadLocalCache cache = CACHE.get(); // convert signature to native format final LibSecp256k1.secp256k1_ecdsa_signature nativeSignature = cache.signature; final var signatureParseResult = LibSecp256k1.secp256k1_ecdsa_signature_parse_compact(LibSecp256k1.CONTEXT, nativeSignature, rawSig); if (signatureParseResult != 1) { logger.warn( TESTING_EXCEPTIONS.getMarker(), () -> "Failed to parse signature [ publicKey = %s, rawSig = %s ]" .formatted(hex(pubKey), hex(rawSig))); return false; } // Normalize the signature to lower-S form. This will return 1 if the signature was normalized, 0 otherwise. LibSecp256k1.secp256k1_ecdsa_signature_normalize(LibSecp256k1.CONTEXT, nativeSignature, nativeSignature); // convert public key to input format final byte[] publicKeyInput = cache.uncompressedPublicKeyInput; System.arraycopy(pubKey, 0, publicKeyInput, 1, ECDSA_UNCOMPRESSED_KEY_SIZE); // convert public key to native format final LibSecp256k1.secp256k1_pubkey nativePublicKey = cache.publicKey; final int keyParseResult = LibSecp256k1.secp256k1_ec_pubkey_parse( LibSecp256k1.CONTEXT, nativePublicKey, publicKeyInput, publicKeyInput.length); if (keyParseResult != 1) { logger.warn( TESTING_EXCEPTIONS.getMarker(), () -> "Failed to parse public key [ publicKey = %s, rawSig = %s ]" .formatted(hex(pubKey), hex(rawSig))); return false; } // verify signature final int result = LibSecp256k1.secp256k1_ecdsa_verify(LibSecp256k1.CONTEXT, nativeSignature, msgHash, nativePublicKey); return result == 1; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy