PHP WebShell

Текущая директория: /usr/lib/node_modules/bitgo/node_modules/@vechain/sdk-core/src/secp256k1

Просмотр файла: Secp256k1.ts

import * as nc_utils from '@noble/curves/abstract/utils';
import { HexUInt } from '../vcdm/HexUInt';
import { randomBytes as nh_randomBytes } from '@noble/hashes/utils';
import { secp256k1 as nc_secp256k1 } from '@noble/curves/secp256k1';
import {
    InvalidSecp256k1MessageHash,
    InvalidSecp256k1PrivateKey,
    InvalidSecp256k1Signature
} from '@vechain/sdk-errors';

/**
 * The Secp256k1 class provides cryptographic utilities for the
 * [SECP256K1](https://en.bitcoin.it/wiki/Secp256k1)
 * [elliptic curve](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm),
 * including compressing and inflating public keys,
 * generating private keys, and validating message hashes and private keys.
 */
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
class Secp256k1 {
    /**
     * This value is used to identify compressed public key.
     */
    private static readonly COMPRESSED_PREFIX = 2;

    /**
     * Represents the fixed length of the cryptographic signature.
     * The value is set to 65, which is the size in bytes
     * required for a 520-bit signature.
     *
     * @constant {number} SIGNATURE_LENGTH
     */
    public static readonly SIGNATURE_LENGTH = 65;

    /**
     * This value is used to identify uncompressed public key.
     */
    private static readonly UNCOMPRESS_PREFIX = 4;

    /**
     * Defines the required length for a valid hash.
     */
    private static readonly VALID_HASH_LENGTH = 32;

    /**
     * Compresses an uncompressed public key.
     *
     * @param {Uint8Array} publicKey - The uncompressed public key to be compressed.
     * @return {Uint8Array} - The compressed public key.
     *
     * @see Secp256k1.inflatePublicKey
     */
    public static compressPublicKey(publicKey: Uint8Array): Uint8Array {
        const prefix = publicKey.at(0);
        if (prefix === Secp256k1.UNCOMPRESS_PREFIX) {
            // To compress.
            const x = publicKey.slice(1, 33);
            const y = publicKey.slice(33, 65);
            const isYOdd = y[y.length - 1] & 1;
            // Prefix with 0x02 if Y coordinate is even, 0x03 if odd.
            return nc_utils.concatBytes(
                Uint8Array.of(Secp256k1.COMPRESSED_PREFIX + isYOdd),
                x
            );
        } else {
            // Compressed.
            return publicKey;
        }
    }

    /**
     * Derives the public key from a given private key.
     *
     * @param {Uint8Array} privateKey - The private key in Uint8Array format. Must be a valid 32-byte secp256k1 private key.
     * @param {boolean} [isCompressed=true] - Indicates whether the derived public key should be in compressed format.
     * @return {Uint8Array} The derived public key in Uint8Array format.
     * @throws {InvalidSecp256k1PrivateKey} Throws an error if the provided private key is not valid.
     *
     * @remarks Security auditable method, depends on
     * * [nc_secp256k1.getPublicKey](https://github.com/paulmillr/noble-secp256k1).
     */
    public static derivePublicKey(
        privateKey: Uint8Array,
        isCompressed: boolean = true
    ): Uint8Array {
        // Check if the private key is valid.
        if (Secp256k1.isValidPrivateKey(privateKey)) {
            return nc_secp256k1.getPublicKey(privateKey, isCompressed);
        }
        throw new InvalidSecp256k1PrivateKey(
            'Secp256k1.derivePublicKey',
            'Invalid private key given as input. Ensure it is a valid 32-byte secp256k1 private key.',
            undefined
        );
    }

    /**
     * Generates a new Secp256k1 private key using a secure random number generator.
     *
     * @return {Promise<Uint8Array>} A promise that resolves to a Uint8Array representing the generated private key.
     *                               This encoded private key is suitable for cryptographic operations.
     * @throws {InvalidSecp256k1PrivateKey} Throws an error if private key generation fails if a secure random number
     *                                      generator is not provided by the hosting operating system.
     *
     * @remarks Security auditable method, depends on
     * * [nc_secp256k1.utils.randomPrivateKey](https://github.com/paulmillr/noble-secp256k1).
     */
    public static async generatePrivateKey(): Promise<Uint8Array> {
        return await new Promise<Uint8Array>((resolve, reject) => {
            try {
                const privateKey = nc_secp256k1.utils.randomPrivateKey();
                resolve(privateKey); // Resolve the promise with the generated private key
            } catch (e) {
                reject(
                    new InvalidSecp256k1PrivateKey(
                        'Secp256k1.generatePrivateKey',
                        'Private key generation failed: ensure you have a secure random number generator available at runtime.',
                        undefined,
                        e
                    )
                );
            }
        });
    }

    /**
     * Inflate a compressed public key to its uncompressed form.
     *
     * @param {Uint8Array} publicKey - The compressed public key to be inflated.
     * @return {Uint8Array} - The uncompressed public key.
     *
     * @remarks Security auditable method, depends on
     * * [nc_secp256k1.ProjectivePoint.fromAffine](https://github.com/paulmillr/noble-secp256k1);
     * * [nc_secp256k1.ProjectivePoint.fromHex](https://github.com/paulmillr/noble-secp256k1);
     * * [nc_secp256k1.ProjectivePoint.toAffine](https://github.com/paulmillr/noble-secp256k1).
     *
     * @see Secp256K1.compressPublicKey
     */
    public static inflatePublicKey(publicKey: Uint8Array): Uint8Array {
        const prefix = publicKey.at(0);
        if (prefix !== Secp256k1.UNCOMPRESS_PREFIX) {
            // To inflate.
            const x = publicKey.slice(0, 33);
            const p = nc_secp256k1.ProjectivePoint.fromAffine(
                nc_secp256k1.ProjectivePoint.fromHex(
                    HexUInt.of(x).digits
                ).toAffine()
            );
            return p.toRawBytes(false);
        } else {
            // Inflated.
            return publicKey;
        }
    }

    /**
     * Checks whether the provided hash is a valid message hash.
     *
     * @param {Uint8Array} hash - The hash to be validated.
     * @return {boolean} `true` if the hash is 32 bytes long, otherwise `false`.
     */
    public static isValidMessageHash(hash: Uint8Array): boolean {
        return hash.length === Secp256k1.VALID_HASH_LENGTH;
    }

    /**
     * Checks if the provided private key is valid.
     *
     * @param {Uint8Array} privateKey - The private key to validate.
     * @return {boolean} `true` if the private key is valid, `false` otherwise.
     *
     * @remarks Security auditable method, depends on
     * * [nc_secp256k1.utils.isValidPrivateKey](https://github.com/paulmillr/noble-secp256k1).
     */
    public static isValidPrivateKey(privateKey: Uint8Array): boolean {
        return nc_secp256k1.utils.isValidPrivateKey(privateKey);
    }

    /**
     * Generates a random sequence of bytes.
     * If an error occurs during generation using
     * [nc_secp256k1](https://github.com/paulmillr/noble-secp256k1),
     * {@link {@link global.crypto} is used as fall back togenerate
     * the random sequence.
     *
     * @param {number} [bytesLength=32] - Optional. The number of random bytes to generate, 32 by default.
     * @return {Uint8Array} - A Uint8Array containing the random bytes.
     *
     * @remarks Security auditable method, depends on
     * * {@link global.crypto.getRandomValues};
     * * [nh_randomBytes](https://github.com/paulmillr/noble-hashes).
     */
    public static randomBytes(bytesLength: number = 32): Uint8Array {
        try {
            return nh_randomBytes(bytesLength);
        } catch {
            return global.crypto.getRandomValues(new Uint8Array(bytesLength));
        }
    }

    /**
     * Recovers the public key associated with the message hash from the given signature.
     *
     * @param {Uint8Array} messageHash - The 32-byte message hash to be verified.
     * @param {Uint8Array} sig - The 65-byte signature used for recovery, consisting of the compact signature and recovery byte.
     * @return {Uint8Array} The recovered public key in its raw bytes form.
     * @throws {InvalidSecp256k1MessageHash} If the provided message hash is invalid.
     * @throws {InvalidSecp256k1Signature} If the provided signature is not 65 bytes or contains an invalid recovery value.
     *
     * @remarks Security auditable method, depends on
     * * [nc_secp256k1.Signature](https://github.com/paulmillr/noble-secp256k1).
     *
     * @see Secp256k1.isValidMessageHash
     */
    public static recover(
        messageHash: Uint8Array,
        sig: Uint8Array
    ): Uint8Array {
        // Check if the message hash is valid.
        if (!Secp256k1.isValidMessageHash(messageHash)) {
            throw new InvalidSecp256k1MessageHash(
                'Secp256k1.recover',
                'Invalid message hash given as input. Ensure it is a valid 32-byte message hash.',
                { messageHash }
            );
        }
        if (sig.length !== Secp256k1.SIGNATURE_LENGTH)
            throw new InvalidSecp256k1Signature(
                'Secp256k1.recover',
                'Invalid signature given as input. Length must be exactly 65 bytes.',
                { signature: sig }
            );
        const recovery = sig[64];
        if (recovery !== 0 && recovery !== 1)
            throw new InvalidSecp256k1Signature(
                'Secp256k1.recover',
                'Invalid signature recovery value. Signature bytes at position 64 must be 0 or 1.',
                { signature: sig, recovery }
            );
        return nc_secp256k1.Signature.fromCompact(sig.slice(0, 64))
            .addRecoveryBit(recovery)
            .recoverPublicKey(messageHash)
            .toRawBytes(false);
    }

    /**
     * Signs a given message hash using the provided private key.
     *
     * @param messageHash - A 32-byte message hash that needs to be signed.
     * @param privateKey - A 32-byte private key used for signing the message hash.
     * @return The signature of the message hash consisting of the r, s, and recovery values.
     * @throws InvalidSecp256k1MessageHash if the message hash is not a valid 32-byte hash.
     * @throws InvalidSecp256k1PrivateKey if the private key is not a valid 32-byte private key.
     *
     * @remarks Security auditable method, depends on
     * * [nc_secp256k1.sign](https://github.com/paulmillr/noble-secp256k1).
     *
     * @see Secp256k1.isValidMessageHash
     * @see Secp256k1.isValidPrivateKey
     */
    public static sign(
        messageHash: Uint8Array,
        privateKey: Uint8Array
    ): Uint8Array {
        // Check if the message hash is valid.
        if (!Secp256k1.isValidMessageHash(messageHash)) {
            throw new InvalidSecp256k1MessageHash(
                'Secp256k1.sign',
                'Invalid message hash given as input. Ensure it is a valid 32-byte message hash.',
                { messageHash }
            );
        }
        // Check if the private key is valid.
        if (!Secp256k1.isValidPrivateKey(privateKey)) {
            throw new InvalidSecp256k1PrivateKey(
                'Secp256k1.sign',
                'Invalid private key given as input. Ensure it is a valid 32-byte secp256k1 private key.',
                undefined
            );
        }
        const sig = nc_secp256k1.sign(messageHash, privateKey);
        return nc_utils.concatBytes(
            nc_utils.numberToBytesBE(sig.r, 32),
            nc_utils.numberToBytesBE(sig.s, 32),
            nc_utils.numberToVarBytesBE(sig.recovery)
        );
    }
}

export { Secp256k1 };

Выполнить команду


Для локальной разработки. Не используйте в интернете!