Invalid public key (JS Error): secp256k1 key generated in Rust fails to load while creating DelegationChain in Javascript

I have rust code outside canister running using axum server.
Which has generated secp256k1 key pair.
I want to pass its public key on the javascript frontend to create delegated identity.
In the first step, I’m trying to create DelegationChain in javascript by using Signed Delegation generated on Rust server.
However it fails to load public key.

Rust code looks like this:

// client_pem is created using secp256k1 SecretKey
// similar to shown here : https://github.com/dfinity/sdk/blob/95d2e707d06e0087fb5bf2b187d5014b3b4e908f/src/dfx-core/src/identity/identity_manager.rs#L804

    let client_identity =
        Secp256k1Identity::from_pem(client_pem.private_key.as_bytes()).unwrap();

    let delegation = Delegation {
        pubkey: client_identity.public_key.as_bytes().to_vec(),
        expiration,
        targets: None,
    };

    let signature = client_identity.sign_delegation(&delegation).unwrap();

    let signed_delegation = SignedDelegation {
        delegation,
        signature: signature.signature.unwrap(),
    };

    let client_pubkey: Vec<u8> = client_identity.public_key().unwrap();

Rust API returns signed_delegation & client_pubkey to JS frontend.

Javascript code looks like this :

let jsonnableDelegationChain = { publicKey: client_pubkey, delegations: [signed_delegation ] };
const delegationChain = DelegationChain.fromJSON(jsonnableDelegationChain);


while executing jsonnableDelegationChain , it is giving JS error,
Invalid public key.

What is the accepted format for public key in JS (agent-js) to create JsonnableDelegationChain ?

1 Like

It’s a hex-encoded string of the raw public key:

  /**
   * Serialize this key to JSON-serializable object.
   * @returns {JsonableSecp256k1Identity}
   */
  public toJSON(): JsonableSecp256k1Identity {
    return [toHexString(this._publicKey.toRaw()), toHexString(this._privateKey)];
  }
1 Like

Thanks @kpeacock for your help.

I found below implementation in buffer.ts which I used further in code.

  function toHexString(bytes) {
    return new Uint8Array(bytes).reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
  }

I have access to public_key() or to_sec1_der() methods for ic_agent::Identity objects in Rust. which returns Vec from rust. to_sec1_der() must be der encoded key, while public_key may not. Please correct my understanding. I used to_sec1_der() below. However using public_key() (which has different u8 array) also gives same error.

the values looks like these:

JsonnableDelegationChain Object after parsing on frontend looks like this:

{
  "publicKey": "03f11d149ad7d721244a5aff21289504a4100367c07839bc80454707313fa6d913",
  "delegations": [
    {
      "delegation": {
        "pubkey": "03f11d149ad7d721244a5aff21289504a4100367c07839bc80454707313fa6d913",
        "expiration": "1702623454339632600"
      },
      "signature": "42440b724d10a39efc556ecbf40727fe015ed1096f130c81a18bdbdde372b5d27bd80c011aec1f1ce9337598037524b545ca15f5705a969b03c22c7b17b43fe9"
    }
  ]
}

When i use it in :

const delegationChain = DelegationChain.fromJSON(jsonnableDelegationChain);

It fails with error:

Length too long (> 4 bytes)
encodeLenBytes der.ts:21

Am I getting public key in right format from Rust?

1 Like

Yes, it sounds like you are getting the der-encoded public key instead of the raw key.

Once you have a Secp256k1PublicKey instance, you can access the raw key via Secp256k1PublicKey.rawKey or Secp256k1PublicKey.toRaw().

toRaw has better backwards compatibility in the library, in case you are using older versions of agent-js

1 Like