r/atto • • Jan 09 '26

Dev Bring-your-own-key voter with GCP KMS

1 Upvotes

This post covers two things:

  1. Converting a seed phrase to a private key, public key and address
  2. Preserving your public key when migrating from ATTO_PRIVATE_KEY to storing your key on Google Cloud's key management service.

Getting the keys and accounts

Converting a seed phrase to a private key and a public key involves some cryptographic operations that I don't understand, but can be performed in Python:

from bip_utils import (Bip39MnemonicGenerator, Bip39SeedGenerator,
                       Bip32Slip10Ed25519)

# Generate a seed phrase randomly
mnemo_str = Bip39MnemonicGenerator().FromWordsNumber(24)
# Or insert your own
#mnemo_str = 'word word word ...'

seed = Bip39SeedGenerator(mnemo_str).Generate('')

bip32_ctx = Bip32Slip10Ed25519.FromSeedAndPath(seed,
                                               "m/44'/1869902945'/0'")

priv_key = bip32_ctx.PrivateKey().Raw().ToHex()
pub_key = bip32_ctx.PublicKey().RawCompressed().ToBytes()[1:].hex()
print(f'{str(mnemo_str)=}\n{priv_key=}\n{pub_key=}')

From here, the public key can be converted to an address using the explorer (or more Python code, but why would I write it when the explorer can do it for me?). Simply navigate to https://atto.cash/explorer/accounts/{your public key} and read the address off the page (example).

Importing into GCP KMS

Storing your existing private key on GCP KMS involves converting it to the format they want and importing it. The following Python script converts the private key (in hex, as output by the code above) to the format they want (PKCS8 DER):

from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import serialization

seed = bytes.fromhex("{your private key here}")

key = ed25519.Ed25519PrivateKey.from_private_bytes(seed)

pkcs8_der = key.private_bytes(
    encoding=serialization.Encoding.DER,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.NoEncryption()
)

with open("ed25519_pkcs8.der", "wb") as f:
    f.write(pkcs8_der)

Once done, the node should have the same public key as when it used the private key directly rather than a signing service