vacuumlabs/cardano-hw-cli

Signing feature to use policy.hwsfile for Token-Metadata-Registry

Opened this issue · 5 comments

With the next release it will be possible to use minting policies also with signing keys on the hw-ledger. This is awesome and a great step in security, but we have a missing link here now to sign metadata content for the token-registry.

To create the right json file for the token-registry we use the Offchain-Metadata-Tools (https://github.com/input-output-hk/offchain-metadata-tools), especially the Token-Metadata-Creator (https://github.com/input-output-hk/offchain-metadata-tools/tree/master/token-metadata-creator). This tool generates standard JSON files with various entries and values. Each entry is signed by the secret key(s) of the policy. Now with the secret key represented via the .hwsfile, this is no longer possible. So we need a simple signing method within hw-cli to generate the needed output. Preprocessing the hash to sign and putting it all together into a new JSON can be handle via external tools.

A typically signed entry in such a token-registry.json would look like:

  "subject": "34250edd1e9836f5378702fbf9416b709bc140e04f668cc3552085184154414441636f696e",

...

  "name": {
    "sequenceNumber": 14,
    "value": "ATADA Coin",
    "signatures": [
      {
        "signature": "8b01ec8330c708a3972dc4ffbe7541d9c0aa4726309c43cd81fd440f70af890d2ceab7c82a64e215fc6cfbb168f6ba61a3bcb2404b9a5b47386f178f8378680b",
        "publicKey": "1287e9ce9e00a603d250b557146aa0581fc4edf277a244ce39d3b2f2ced5072f"
      }
    ]
  },
 "ticker": {
    "sequenceNumber": 14,
    "value": "ATADA",
    "signatures": [
      {
        "signature": "8bc85b2426bbf51b9c5fc6bf4dd96836b15f2644a3f6e2d15e3c526c5d46f30888fea96c5b300a635fe2a331a562cad39872ce12127bcaa80f8d38782d15fd0b",
        "publicKey": "1287e9ce9e00a603d250b557146aa0581fc4edf277a244ce39d3b2f2ced5072f"
      }
    ]
  }

...

The "publicKey" is easy, thats already available with the policy.vkey. But for the "signature" we need some blake2b_256 with .hwsfile signing magic, as far as i know the content of "subject" (thats the combined hexstring of the policyid and assetname), "sequenceNumber", "fieldName" (name, description, url, ticker, logo, policy, decimals) and the "value" is combined and signed.

https://github.com/input-output-hk/offchain-metadata-tools/blob/79145b3a7d91aa2fecf91bbe5ee96176671d8fd8/token-metadata-creator/src/Cardano/Metadata/Types.hs#L530

https://github.com/input-output-hk/offchain-metadata-tools/blob/79145b3a7d91aa2fecf91bbe5ee96176671d8fd8/token-metadata-creator/app/token-metadata-creator.hs#L144

The Token-Metadata-Registry (https://github.com/cardano-foundation/cardano-token-registry) is the offchain query service where the wallets can check the metadata of an asset, to display it with the right name,description, logo and decimals in the wallet.

I tried the tool and honestly this seems like something out of scope of cardano-hw-cli and also the apps for Ledger and Trezor. Currently if I am not mistaken we have a way to create signatures for transactions, but this seems like a completely different call and a signature that would need to get computed.
So the scope of this issue goes beyond just cardano-hw-cli. @janmazak maybe will have more insight, but I believe this is currently something we are not planning to work on as of now.

I tried the tool and honestly this seems like something out of scope of cardano-hw-cli and also the apps for Ledger and Trezor. Currently if I am not mistaken we have a way to create signatures for transactions, but this seems like a completely different call and a signature that would need to get computed. So the scope of this issue goes beyond just cardano-hw-cli. @janmazak maybe will have more insight, but I believe this is currently something we are not planning to work on as of now.

I will try to get a better documentation about how to sign such a Hash-String. The thing is, now when we are able to let the people generate there Assets/NTFs/FTs with a hardware based policy key, this is awesome. But it would not be possible for them to register there Assets/NFTs/FTs in the registry like other ones. I don't think it would be super complicated to do the signing, the tool is a really tiny one. I'll try to get more infos.

The problematic part here could be the message size. AFAIK 370KB is maximum for a token registry entry (perhaps logo can take lots of space), and Ledger receives chunks of data of about 200 bytes, so you might need hundreds to thousands of those, and at least on laptops where I work the usb connection is not stable for long enough to transfer that many chunks. Also, if we are to show the data being signed to the human user, we would need to sign much much less.

So in the worst case changes would need to be made to the token registry signatures. I don't undestand the details of what is signed; generally, not whole documents are signed, just their hashes --- is it true in this case, too?

@janmazak yes you're right, there is only a signing of the hashes. first there is the hash calculation of the four fields/values concated together, than a hash "blake2b_256" is made out of it. this hash is than signed with the secret key.

hash(
  hash(CBOR(subject)) + 
  hash(CBOR(name)) + 
  hash(CBOR(value)) + 
  hash(CBOR(sequenceNumber))
)

so it would be enough to just pass over that final hash of each entry (name, description, url, decimals, ticker, logo) and just sign this. not the whole thing at once. combining it all together in the end can be handled via the calling bash script.

super nice solution would be to have it like:
cardano-hw-cli tokenregistry sign --subject "02f68378e37af4545d027d0a9fa5581ac682897a3fc1f6d8f936ed2b4154414441" --entry-name "description" --entry-value "This is the desc. of the Token" --entry-sequencenumber 1 --hw-signing-file hw-mint.hwsfile [--mainnet [NETWORK]] [--testnet-magic [NETWORK]]
and it returns the signature hexstring.

but it would also be ok to just have it like:
cardano-hw-cli tokenregistry sign --entry-hash "46777fba65095d33b...79c3f8e968" --hw-signing-file hw-mint.hwsfile [--mainnet [NETWORK]] [--testnet-magic [NETWORK]]

and users have to do the hash calculation on there own before the hw-cli call.

@gabrielKerekes @janmazak @KubqoA

Hey, did some testing and i put together (with the help of Sun Qingsheng) a super simple signing demo js file in my repo. Its actually pretty easy to do so. There would just be the need to send over only the finalHash to be signed with the policy key on the hw-ledger. Now that we can generate the witnesses with hw-based-policy keys, this would be the final piece for the token registry.

The integration into hw-cli could be made pretty simple to just provide the parts of an entry that is needed (subject, propertyName, propertyValue, sequenceNumber) for the signing and the output can be the signed hex string.
Or another possibility (the nice one) would be to give the unsigned json file as a parameter, and you cycle thru each entry and do an automatic signing via the hw-wallet.

The little demo includes a signed json and an unsigned one.

https://github.com/gitmachtl/cardano-related-stuff/tree/master/token_metadata_sign_demo

The signing is basically:

Most everything is encoded as CBOR, then hashed with Blake2b_256.

  1. The first step is to grab a series of hashes

    1. hash subject

      • encode string as CBOR
      • hash with Blake2b_256 algorithm
    2. hash well-known property name

      • encode string as CBOR
      • hash with Blake2b_256 algorithm
    3. hash well-known property value (different properties have different representations before hashing)

      • policy:

        • encode string as CBOR
        • hash with Blake2b_256 algorithm
      • decimals

        • encode int as CBOR
        • hash with Blake2b_256 algorithm
      • name

        • encode string as CBOR
        • hash with Blake2b_256 algorithm
      • description

        • encode string as CBOR
        • hash with Blake2b_256 algorithm
      • logo

        • encode bytes as CBOR
        • hash with Blake2b_256 algorithm
      • url

        • encode string as CBOR
        • hash with Blake2b_256 algorithm
      • ticker

        • encode string as CBOR
        • hash with Blake2b_256 algorithm
    4. hash sequence number

      • encode word (unsigned int) as CBOR
      • hash with Blake2b_256 algorithm
  2. Form an attestation digest:

    • concatenate, in order:

      • [ bytes of subject hash (i)
        , bytes of property name hash (ii)
        , bytes of property value hash (iii)
        , bytes of sequence number hash (iv)
        ]
  3. Sign the attestation digest with your private key.

  4. Create JSON object:

  { "publicKey" = public key of private key used in last step, encoded as base16
  , "signature" = signed attestation digest created in last step, encoded as base16
  }
  1. Add this JSON object to the list of signatures

The output of the demo js looks like:

$ node token_metadata_sign_demo.js

---

    Entry: url
    Value: https://stakepool.at
   Target: 8d4244be28d7cd0f4f7e31cf54350356cfd1e7dc83a29ee43f8d4f69696f30bb6d5ef5f5a5032e2369b92e3572318960467f674ecc01fad1158eb2c958f52107
Signature: 8d4244be28d7cd0f4f7e31cf54350356cfd1e7dc83a29ee43f8d4f69696f30bb6d5ef5f5a5032e2369b92e3572318960467f674ecc01fad1158eb2c958f52107
publicKey: ca739639a1f79ec17ba57ff9d675f86e5a33af6697e90d405691f26c088bcf75


---

    Entry: name
    Value: token registry signing test
   Target: e09f9a0dcbbc6d60f016df8c70f84f44295cd7296d96546022e4612993221d0e2024509bac90834ba6ee605022b2ff93c32ffb5ee0e93f86818a94c61476b807
Signature: e09f9a0dcbbc6d60f016df8c70f84f44295cd7296d96546022e4612993221d0e2024509bac90834ba6ee605022b2ff93c32ffb5ee0e93f86818a94c61476b807
publicKey: ca739639a1f79ec17ba57ff9d675f86e5a33af6697e90d405691f26c088bcf75


---

    Entry: ticker
    Value: TOKEN
   Target: 4ef86936745189936379009da21a5a5b2b17571aa4a153e17d76f0ceb0166afee9a417ac0e18f8b4504c4270637c3ece9cd3335150976b61395d3a4a8626e305
Signature: 4ef86936745189936379009da21a5a5b2b17571aa4a153e17d76f0ceb0166afee9a417ac0e18f8b4504c4270637c3ece9cd3335150976b61395d3a4a8626e305
publicKey: ca739639a1f79ec17ba57ff9d675f86e5a33af6697e90d405691f26c088bcf75


---

    Entry: decimals
    Value: 6
   Target: a2b7b25067fb868f59e6faf9b4f1e210eb557f2c3fab6bab2d111702ad7af17faf6bf28ba3880ab2ce4833f3f98028e32adad4d99d2338bb0f7b10e863838a0f
Signature: a2b7b25067fb868f59e6faf9b4f1e210eb557f2c3fab6bab2d111702ad7af17faf6bf28ba3880ab2ce4833f3f98028e32adad4d99d2338bb0f7b10e863838a0f
publicKey: ca739639a1f79ec17ba57ff9d675f86e5a33af6697e90d405691f26c088bcf75


---

    Entry: logo
    Value: {base64}
   Target: df66df3994301fe774d0ff0b838f3df1a19025a8e7b5d2bffafa3be0cc3d7df10ccde371ca74200df2dde3d1fbebbe5902c141afa346a9a6652f3a2e8fba2607
Signature: df66df3994301fe774d0ff0b838f3df1a19025a8e7b5d2bffafa3be0cc3d7df10ccde371ca74200df2dde3d1fbebbe5902c141afa346a9a6652f3a2e8fba2607
publicKey: ca739639a1f79ec17ba57ff9d675f86e5a33af6697e90d405691f26c088bcf75


---

    Entry: description
    Value: This is a test for the token metadata registry signing
   Target: 9dc407bcb1ed7a75e0e3f53a435390112f6501774caae9ed1f02725bad00773a619f8c10f1d3d1d189458565bf45d3f580505a4ca5752f0e73649892615f3503
Signature: 9dc407bcb1ed7a75e0e3f53a435390112f6501774caae9ed1f02725bad00773a619f8c10f1d3d1d189458565bf45d3f580505a4ca5752f0e73649892615f3503
publicKey: ca739639a1f79ec17ba57ff9d675f86e5a33af6697e90d405691f26c088bcf75