2

I am using google cloud key management service to generate and manage keys. I have generated the HSM key for Asymmetric signing using Elliptic Curve secp256k1 - SHA256 Digest. The public key is something as below -

{
  pem: '-----BEGIN PUBLIC KEY-----\n' +
    'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n' +
    'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n' +
    '-----END PUBLIC KEY-----\n',
  algorithm: 'EC_SIGN_SECP256K1_SHA256',
  pemCrc32c: { value: '12345678' },
  name: 'PATH-TO-KEY-ON-KMS/cryptoKeyVersions/1',
  protectionLevel: 'HSM'
}

I am looking to derive Ethereum address from this so that I can fund the wallet and perform signing. For the same I have written a function as below -

const deriveEthAddress = async () => {
    const publicKey = await getPublicKey(); // this returns same key as show above snippet
    const address = keccak256(publicKey.pem);
    const hexAddress = address.toString('hex');
    return '0x' + hexAddress.toString('hex').substring(hexAddress.length - 40, hexAddress.length)
}

This function gives me ethereum checksum verified address, but not sure is it the correct way to do this. Is this solution correct or needs improvement?

1 Answer 1

0

Google provides public keys encoded in DER format.

To derive an Ethereum Address, one must first obtain the Ethereum public key.

The asn1js library serves as an efficient mechanism for decoding and interpreting the DER-encoded public key data.

It is important to note, as per section 2.2 of RFC 5480, that the initial byte 0x04 signifies an uncompressed key. By removing this initial byte, one can acquire a standard Ethereum public key.

Here's how to decrypt the DER-encoded public key:

import * as asn1js from "asn1js";

decryptPublickeyDerEncoding(input: Buffer) : Buffer {
    /**
     * Before calculating the Ethereum address, we need to get the raw value of the public key.
     * the input returns a DER-encoded X.509 public key
     * asSubjectPublickeyInfo (SPKI), as defined in RFC 5280. 
     * Use an ASN1 library that allows us to define this as a schema as `OBJECT IDENTIFIER `
     * https://www.rfc-editor.org/rfc/rfc5480#section-2
     */
    const schema = new asn1js.Sequence({ value: [
        new asn1js.Sequence({ value: [new asn1js.ObjectIdentifier()] }),
        new asn1js.BitString({ name: "objectIdentifier" }),
    ]});
    const parsed = asn1js.verifySchema(input, schema);
    if (!parsed.verified) {
        throw new Error(`Publickey: failed to parse. ${parsed.result.error}`);
    }
    const objectIdentifier: ArrayBuffer = parsed.result.objectIdentifier.valueBlock.valueHex;

    /**
     * According to section 2.2 of RFC 5480, the first byte, 0x04 indicates that this is an uncompressed key.
     * We need to remove this byte for the public key to be correct. Once we delete the first byte, we get the
     * raw public key that can be used to calculate our Ethereum address.
     */
    const publickey = objectIdentifier.slice(1); // remove 0x04

    /**
     * Returns the wallet's public key buffer
     */
    return Buffer.from(publickey);
}

With the Ethereum public key in hand, you can now compute the Ethereum Address:

publickeyToAddress(input: Buffer) {
    const hash = Buffer.from(keccak256(input), 'hex');
    const address = "0x" + hash.slice(-20).toString('hex');
    return address;    
}

And there you have it, your Ethereum Address!

For a deeper understanding, check this article : https://jonathanokz.medium.com/secure-an-ethereum-wallet-with-a-kms-provider-2914bd1e4341

Additionally, this npm repository provides a ready-to-use solution : https://github.com/JonathanOkz/web3-kms-signer

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.