Message Signing specification

Private keys can be used to sign arbitrary data. If you have the public key, you can verify the data was signed by the owner of the private key. This is how transaction signing works internally but its utility is not limited to transactions. This document tries to set a standard for how to represent and verify signed messages for Cardano.

8 Likes

Would love if someone could add graphics to this CIP - it’s technically very fleshed out but might be a bit hard to wrap one’s head around if anyone is able to step up!
Currently live in the CIP repository here.

1 Like

Hi @SebastienGllmt,

Just came across to this CIP-0008 what what @Frederic posted above and was wondering the encryption part of it.

I would say, and I think that almost all the cryptographers would agree, that you should never use an Ed25519 verify key for ECDH but only X25519 public keys, for several reason I do not want to point out.

Though, fortunately, an Ed25519 verify key can be easily converted to an X25519 public key for encryption. The other way is not straight forward as you would get two valid Ed25519 verify keys.

Also, the first 32-byte of a Cardano’s extended key is a valid X25519 private key, so there is some easy change could be made for that specification to be securely valid. Though, you should consults /w some IOG’s cryptograpers relating to that encryption part. I am still digesting the other parts.

Also, a good idea could be to standardise the bech32 hrps for the keys too.
for example I just generated this table now.

Keypairs Main HRP Alt HRP(s) Key length Purpose Comment
ed25519 ed25519_sk ed25519_skpk 64 Ed25519 signatures
ed25519_pk 32 Ed25519 signatures Can be converted to x25519_pk for ECDH
Extended Ed25519 ed25519e_sk ed25519_esk 64 Ed25519 signatures Can be converted to x25519_sk for ECDH
ed25519_pk 32 Ed25519 signatures
Bip32-Ed25519 ed25519bip32_sk ed25519bip32_skcc ed25519bip32_eskcc xprv (not recommended) 96 Ed25519 signatures Can be converted to x25519_sk for ECDH
ed25519bip32_pk ed25519bip32_pkcc xpub (not recommended) 64 Ed25519 signatures Can be converted to x25519_pk for ECDH
X25519 x25519_sk 32 ECDH The left part of an ed25519e_sk or ed2551bip32_sk is a valid X255119 private key.
x25519_pk 32 ECDH

I gave it a second thought and this below (As some API like presentation for the publicKey, rawKey, encryptionKey, signignKey and verifyKey) would make much more sense, as there are only two type of keys. The Ed25519 (for EdDSA i.e. Signatures signing/verify keys) and the X25519 (for ECDH encryption keys) and the Extended and the Bip32-Ed25519 keys are theoretically same keys.

But, this could be discussed further, but I doubt it would make any progress imo.

KeyPair key verifyKey encryptionKey publicKey rawKey Comment
ed25519 ed25519_sk ed25519_pk N/A verifyKey ed25519_sk This is the 32-byte long seed
ed25519_skpk ed25519_pk N/A verifyKey ed25519_sk rawKey is the 32-byte long seed
ed25519_pk ed25519_pk N/A verifyKey ed25519_pk rawKey is the key itself
Extended Ed25519 ed25519_esk ed25519_pk x25519_sk verifyKey ed25519_esk rawKey is the key itself The encryption key is the first 32-byte long bytes
ed25519_pk ed25519_pk x25519_pk verifyKey ed25519_pk rawKey is the key itself
Bip32-Ed25519 ed25519_eskcc ed25519_pkcc x25519_sk verifyKey ed25519_esk rawKey a valid extended key
ed25519_pkcc ed25519_pkcc x25519_pk verifyKey ed25519_pk rawKeys is a valid Ed25519 signing key
X25519 x25519_sk N/A x25519_pk encryptionKey x25519_sk rawKey is the key itself
x25519_pk N/A x25519_pk encryptionKey x25519_pk rawKey is the key itself
1 Like

Hi @_ilap and @SebastienGllmt ,

I found this CIP very interesting as it has a lot that I was thinking to use in a similar context.

I am working on a blockchain authentication mechanism, basically a wallet friendly auth mechanism where a wallet is used to signup/login on a system and use the wallet public key as the equivalent of an email.

The approach I was thinking to use is very similar to the CIP-0022 used to verify a SPO. In fact I have a PoC of both approaches in java here: blockchain-auth-mech-dev/SigningService.java at master · speedwing/blockchain-auth-mech-dev · GitHub

The scenarios that I am depicting, and that are already partially discussed in the CIP are the following:

  1. user wants to stay anonymous, so they create a new wallet, that they will leave empty or with very little funds
  2. user wants to anonumously and safely demonstrate they own (or owned) a certain amount of ada
  3. user wants to anonimously demonstrate they own or have owned a certain token

An authenticating server would issue a challenge (substantially random text[1]) that needs to be signed within a certain amount of time to avoid spoofing/hacking.

Along with the signed message, the user seeking authentication, needs to provide their public key (used to verify signed message against original message), and an address (here I am a bit outside of my comfort zone, as I’m not sure how, the authenticating server, can verify the address belongs to the range of addresse owned by the public key).

What do you guys think? Are there major drawbacks to this idea? Do you think I could make a CIP out of it?

Additional info:
[1] the random text may contain for example teh domain of the authenticating server + timestamp, text should be valid for just a handful of seconds. Some issue here could be reply attack, or spoofing.

1 Like

This signature service would help to verify a stake address as well. Something I am looking into currently CIP Proof Ownership for Stake Address

1 Like

Hi Sebastien

Thanks for this needed CIP. I would find it extremely helpful if there were a couple of examples for the signing and verification process of arbitrary using the emugo/message-signign library (browser/wasm).
Basically picking up from the output of the CIP030’s wallet.signData() API method.

const signedData = JSON.parse("{\"signature\":\"844fa20127676164647265737343ad0d01a166686173686564f44f7b22736f6d65223a2264617461227d584087c626eb8320b86ea2146afc845ebba12917ffb0b2b9e7eaed005d43b783a9d2de2c5d7fccc1a88ad6310d9adb50a8e3ae14ad8dd442877d5c413f6cb9b2a306\",\"key\":\"a4010103272006215820b079c83b0bd317ff0bd7c7cf1e08859a70f8806c16e314373f922a8fe7739115\"}")
const coseKey = messageSigning.COSEKey.from_bytes(Buffer.from(signedData.key, 'hex'));
// how does one verify the message from here on?

Not sure where to put this, in the CIP-30 section or in this CIP-8.
CIP-30 is using CIP-8 as the underlying layer of the COSE_Sign1 / COSE_Key format. The COSE_Sign1 specification allows all kind of different labels in the unprotected Headers.
Currently i only saw the usage of the label { “hashed”: true/false } in there.

Having the COSE_Sign1 signature always requires the COSE_Key to do the verification. The COSE_Key is completely unprotected.

So i was wondering, can we put the content of the COSE_Key (labels 1,3,-1,-2) directly into the unprotected headers of COSE_Sign1 to have all information present in just one signature?