<p>Script with the solution (node JS):</p>
<pre><code>const {COSESign1, Label} = require(&quot;./extra_modules/@emurgo/cardano-message-signing-nodejs&quot;);
const {Address, Ed25519Signature, PublicKey, BaseAddress, StakeCredential, RewardAddress} = require('@emurgo/cardano-serialization-lib-nodejs');
const {Buffer} = require('buffer');

class SignedData
{
    constructor(signed)
    {
        let message = COSESign1.from_bytes(Buffer.from(Buffer.from(signed, 'hex'), 'hex'));
        let headermap = message.headers().protected().deserialized_headers();
        this.headers = {
            algorithmId: headermap.algorithm_id().as_int().as_i32(),
            address: Address.from_bytes(headermap.header(Label.new_text('address')).as_bytes()),
            publicKey: PublicKey.from_bytes(headermap.key_id())
        };
        this.payload = message.payload();
        this.signature = Ed25519Signature.from_bytes(message.signature());
        this.data = message.signed_data().to_bytes();
    }

    verify(address, payload)
    {
        if (!this.verifyPayload(payload)) {
            throw new Error('Payload does not match');
        }
        if (!this.verifyAddress(address)) {
            throw new Error('Could not verify because of address mismatch');
        }
        return this.headers.publicKey.verify(this.data, this.signature);
    };

    verifyPayload(payload)
    {
        return Buffer.from(this.payload, 'hex').compare(Buffer.from(payload, 'hex'));
    }

    verifyAddress(address)
    {
        const checkAddress = Address.from_bech32(address);
        if (this.headers.address.to_bech32() !== checkAddress.to_bech32()) {
            console.log('FASLE1');
            return false;
        }
        // check if BaseAddress
        try {
            const baseAddress = BaseAddress.from_address(this.headers.address);
            //reconstruct address
            const paymentKeyHash = this.headers.publicKey.hash();
            const stakeKeyHash = baseAddress.stake_cred().to_keyhash();
            const reconstructedAddress = BaseAddress.new(
                checkAddress.network_id(),
                StakeCredential.from_keyhash(paymentKeyHash),
                StakeCredential.from_keyhash(stakeKeyHash)
            );
            return checkAddress.to_bech32() === reconstructedAddress.to_address().to_bech32();
        } catch (e) {
        }

        try {
            const stakeKeyHash = this.headers.address.hash();
            const reconstructedAddress = RewardAddress.new(
                checkAddress.network_id(),
                StakeCredential.from_keyhash(stakeKeyHash)
            );
            return checkAddress.to_bech32() === reconstructedAddress.to_address().to_bech32();
        } catch (e) {
        }
        return false;
    };
}
let signed = &quot;845869a30127045820674d11e432450118d70ea78673d5e31d5cc1aec63de0ff6284784876544be3406761646472657373583901d2eb831c6cad4aba700eb35f86966fbeff19d077954430e32ce65e8da79a3abe84f4ce817fad066acc1435be2ffc6bd7dce2ec1cc6cca6cba166686173686564f44568656c6c6f5840a3b5acd99df5f3b5e4449c5a116078e9c0fcfc126a4d4e2f6a9565f40b0c77474cafd89845e768fae3f6eec0df4575fcfe7094672c8c02169d744b415c617609&quot;;
let signedData = new SignedData(signed);
let address = 'addr1q8fwhqcudjk54wnsp6e4lp5kd7l07xwsw725gv8r9nn9ard8ngatap85e6qhltgxdtxpgdd79l7xh47uutkpe3kv5m9smpyc34';
let payload = 'hello';
console.log(signedData.verify(address, payload));
</code></pre>

CC BY-SA 4.0
2022-07-24T18:37:29.443Z
I had some trouble implementing this - outlined the issues I found in a post linked below, incase anyone is able to provide some insight, please. https://cardano.stackexchange.com/questions/8708/missing-key-id-on-signature-from-signdata
zing
CC BY-SA 4.0
2023-05-06T17:39:46.957Z
@zing your link is dead?
Displee
CC BY-SA 4.0
2021-08-29T14:58:26.847Z
2021-08-29T15:05:36.893Z
2021-08-29T15:05:36.893Z
Jeroen
<p>You will need to send your verification key from Nami to the backend during the handshake in order to validate the signature.</p>
<pre><code>cardano.signData(address: BaseAddress|RewardAddress, payload: string) : CoseSign1
</code></pre>
<blockquote>
<p>The returned CoseSign1 object contains the payload, signature and the following protected headers:</p>
<p>key_id =&gt; PublicKey,
address =&gt; BaseAddress | RewardAddress
algorithm_id =&gt; EdDSA(0) (the algorithm used for Cardano addresses).</p>
</blockquote>
<p>Source: <a href="https://github.com/Berry-Pool/nami-wallet#cardanosigndataaddress-payload" rel="nofollow noreferrer">Nami Readme</a></p>
<p>When you run the cardano.SignData method, you are returned an object that includes the public key for the private key that you used to sign the payload. Thus, when you send the signed object to the backend, the backend has everything it needs in the header to validate that transaction. To avoid hacking, you should also ensure that the public key is constant throughout communication with the same entity or in the same session.</p>
<p>The output is a hex-encoded byte string adhering to the CBOR format standard. <a href="http://cbor.me" rel="nofollow noreferrer">Here</a> is a link to a playground that will let you convert between the CBOR that is the output (e.g., &quot;CoseSign1&quot; from the readme) and something readable. Here is an example output (don't forget to check all of the boxes to get the full result).</p>
<pre><code>[&lt;&lt; {1: -8, 4: h'ABF29B244C2E1C4DD3CA2F8DB27D87C6B73DA1A87D08389101D5D1ECEBDD0079', &quot;address&quot;: h'E1DC0C1BD97FCD0ED6905EBA692C1DDEFF27DA43D59C5A1F2EA766442E'} &gt;&gt;, {&quot;hashed&quot;: false}, 'this is a test\n', h'1F19958A76C7414F311F5176A52A544A4BE9D59FC5F3870F41B00D8B8C52C9B819953D10AE3F23C0D906A138D37D6C70DDADE77DB94D7054D730618106F18108']
</code></pre>
<p>It is a JSON array with two parts: the first is the header, and the second is the contents. According to <a href="https://github.com/cardano-foundation/CIPs/blob/master/CIP-0008/CIP-0008.md" rel="nofollow noreferrer">CIP-0008</a>, the block is expected to contain the following.</p>
<pre><code>COSE_Sign1 = [
    Headers,
    payload : bstr / nil,
    signature : bstr
]
</code></pre>
<p>My guess is that the pub key is mapped to 4: <code>ABF29B244C2E1C4DD3CA2F8DB27D87C6B73DA1A87D08389101D5D1ECEBDD0079</code></p>
<p>Hope this helps!</p>

CC BY-SA 4.0
2021-08-28T18:28:05.847Z
I had noticed that indeed, and I have tried the past 2h to extract that info, but as far as I found this is a cbor encoded message and I just cannot extract it. i see the data i tried to sign in it though (Buffer.from_bytes(data, 'hex').to_string('ascii') :) : Xi#'X gMd2EW'sUc\A.F=`bxHvTKc@gaddressX9Rkl-J:p3_o>PwD0c,f^
':>tN-jL5>/|kW\blFL&K!fhashedtKhello worldX@CW1f4\FcWB0IrK
 _\#wMYl{I;__#
56._TV68'Vu 
DWf0)
Jeroen
CC BY-SA 4.0
2021-08-28T20:16:13.670Z
I completed the explanation =^). Please see edits. Basically, it's just in CBOR, and you will have to convert it. I worked an example. Let me know if you need anything else!
Noah Jones
CC BY-SA 4.0
2021-08-29T14:59:58.260Z
I added a node script with a working solution using the emurgo serialization and message signing libraries. Thanks!
Jeroen
CC BY-SA 4.0
2021-08-29T18:00:09.110Z
Nice well done!! Any time =^)
Noah Jones
CC BY-SA 4.0
2021-08-28T16:16:16.947Z
2021-08-28T20:15:55.010Z
2021-08-28T20:15:55.010Z
Noah Jones
<p>I've created <a href="https://github.com/pyropy/web3-cardano-token" rel="nofollow noreferrer">javascript package</a> that could help you with this. The examples in the README should show you how to sign data on frontend and validate the signature on the backend.</p>

CC BY-SA 4.0
2021-11-29T11:16:57.197Z
2021-11-29T11:16:57.197Z
pyropy
<p>WARNING</p>
<p>The provided solution in the question is a great start, however</p>
<pre><code> verifyPayload(payload)
{
    return Buffer.from(this.payload, 'hex').compare(Buffer.from(payload, 'hex'));
}
</code></pre>
<p>Does not do a correct comparison since in the example the payload to check is not hexencoded but rather a string, so use this instead</p>
<pre><code>    verifyPayload(payload)
{
    let hexMessage=Buffer.from(this.payload, 'hex');
    let stringMessage=hexMessage.toString();
    return stringMessage===payload;
}
</code></pre>

CC BY-SA 4.0
2022-01-18T22:53:11.057Z
It is a bad idea to refer to other solutions.  They may get edited and then your answer becomes weird.  Better to comment on them directly when you have enough reputation.  It is always better to focus on making your answer entirely about the question.  Take the [tour] and look at [answer] for great advice.
gRebel
CC BY-SA 4.0
2022-01-16T14:32:30.650Z
2022-01-16T14:32:30.650Z
Sandro Schaier
<p>I would like to suggest a modification in the verifyPayload function.</p>
<p><code>verifyPayload(payload) { return Buffer.from(this.payload, 'hex').equals(Buffer.from(payload, 'utf8')); }</code></p>

CC BY-SA 4.0
2021-10-27T06:41:39.410Z
2021-10-27T06:41:39.410Z
Manik Jain
<p>Given that this question - and all of the answers so far - are from mid 2021, I don't think the solutions are up to date. <a href="https://github.com/cardano-foundation/CIPs/pull/148" rel="nofollow noreferrer">CIP-0030</a> brought a change to signData, which forced me to update the approach I use to verify wallets with my backend. I actually still use the CIP-0008 approach for Nami because of a bug on their end, but it is technically deprecated. <a href="https://cardano.stackexchange.com/a/4814/965">This post</a> explains what I do now.</p>

CC BY-SA 4.0
2022-05-04T23:27:36.960Z
2022-05-04T23:27:36.960Z
Matt Halloran
<p>I am playing around with the nami cardano wallet. I am able to link the wallet in the frontend and now want to link it to the backend.</p>
<p>First action the frontend sends an auth request with the cardano address to the backend. The backend returns a nonce to the frontend. The front end then lets nami wallet sign this nonce and sends the signature back to the backend.</p>
<p>In this step, the backend should validate if the signature is valid.</p>
<p>Now, how can I let the backend validate this signature?</p>
<p>This is the information I have access to:</p>
<ul>
<li>balance</li>
<li>change address</li>
<li>network id</li>
<li>reward address</li>
<li>unused addresses</li>
<li>used addresses</li>
</ul>
<p>Can I use this info to validate the signature? Or do I need to access the blockchain from the backend code?</p>
<p>(I will most likely be adding blockfrost api later, so nami-wallet + blockfrost will be how I access the blockchain. Unless there are other libraries available for the backend (most likely node.js), nami+blockfrost will be how I access the blockchain.</p>
<p>I am also using @emurgo/cardano-serialization-lib-browser - maybe here something is possible?</p>
<p>Based on response below I made a quick node script:</p>
<pre><code>const {COSESign1, Label} = require(&quot;./extra_modules/@emurgo/cardano-message-signing-nodejs&quot;);
const {Address} = require(&quot;@emurgo/cardano-serialization-lib-nodejs&quot;);
const {Buffer} = require('buffer');
let signedRaw = &quot;845869a30127045820674d11e432450118d70ea78673d5e31d5cc1aec63de0ff6284784876544be3406761646472657373583901d2eb831c6cad4aba700eb35f86966fbeff19d077954430e32ce65e8da79a3abe84f4ce817fad066acc1435be2ffc6bd7dce2ec1cc6cca6cba166686173686564f44568656c6c6f5840a3b5acd99df5f3b5e4449c5a116078e9c0fcfc126a4d4e2f6a9565f40b0c77474cafd89845e768fae3f6eec0df4575fcfe7094672c8c02169d744b415c617609&quot;;
let message = COSESign1.from_bytes(Buffer.from(signedRaw, 'hex'));
let headers = message.headers().protected().deserialized_headers();
let address = Address.from_bytes(headers.header(Label.new_text('address')).as_bytes());
let publicKey = headers.key_id();
let signature = message.signature();
</code></pre>
<p>OK, finally got a working solution, sharing a node script that works for the next person with the same problem:</p>
<pre><code>const {COSESign1, Label} = require(&quot;./extra_modules/@emurgo/cardano-message-signing-nodejs&quot;);
const {Address, Ed25519Signature, PublicKey, BaseAddress, StakeCredential, RewardAddress} = require('@emurgo/cardano-serialization-lib-nodejs');
const {Buffer} = require('buffer');

class SignedData
{
    constructor(signed)
    {
        let message = COSESign1.from_bytes(Buffer.from(Buffer.from(signed, 'hex'), 'hex'));
        let headermap = message.headers().protected().deserialized_headers();
        this.headers = {
            algorithmId: headermap.algorithm_id().as_int().as_i32(),
            address: Address.from_bytes(headermap.header(Label.new_text('address')).as_bytes()),
            publicKey: PublicKey.from_bytes(headermap.key_id())
        };
        this.payload = message.payload();
        this.signature = Ed25519Signature.from_bytes(message.signature());
        this.data = message.signed_data().to_bytes();
    }

    verify(address, payload)
    {
        if (!this.verifyPayload(payload)) {
            throw new Error('Payload does not match');
        }
        if (!this.verifyAddress(address)) {
            throw new Error('Could not verify because of address mismatch');
        }
        return this.headers.publicKey.verify(this.data, this.signature);
    };

    verifyPayload(payload)
    {
        return Buffer.from(this.payload, 'hex').compare(Buffer.from(payload, 'hex'));
    }

    verifyAddress(address)
    {
        const checkAddress = Address.from_bech32(address);
        if (this.headers.address.to_bech32() !== checkAddress.to_bech32()) {
            console.log('FASLE1');
            return false;
        }
        // check if BaseAddress
        try {
            const baseAddress = BaseAddress.from_address(this.headers.address);
            //reconstruct address
            const paymentKeyHash = this.headers.publicKey.hash();
            const stakeKeyHash = baseAddress.stake_cred().to_keyhash();
            const reconstructedAddress = BaseAddress.new(
                checkAddress.network_id(),
                StakeCredential.from_keyhash(paymentKeyHash),
                StakeCredential.from_keyhash(stakeKeyHash)
            );
            return checkAddress.to_bech32() === reconstructedAddress.to_address().to_bech32();
        } catch (e) {
        }

        try {
            const stakeKeyHash = this.headers.address.hash();
            const reconstructedAddress = RewardAddress.new(
                checkAddress.network_id(),
                StakeCredential.from_keyhash(stakeKeyHash)
            );
            return checkAddress.to_bech32() === reconstructedAddress.to_address().to_bech32();
        } catch (e) {
        }
        return false;
    };
}
let signed = &quot;845869a30127045820674d11e432450118d70ea78673d5e31d5cc1aec63de0ff6284784876544be3406761646472657373583901d2eb831c6cad4aba700eb35f86966fbeff19d077954430e32ce65e8da79a3abe84f4ce817fad066acc1435be2ffc6bd7dce2ec1cc6cca6cba166686173686564f44568656c6c6f5840a3b5acd99df5f3b5e4449c5a116078e9c0fcfc126a4d4e2f6a9565f40b0c77474cafd89845e768fae3f6eec0df4575fcfe7094672c8c02169d744b415c617609&quot;;
let signedData = new SignedData(signed);
let address = 'addr1q8fwhqcudjk54wnsp6e4lp5kd7l07xwsw725gv8r9nn9ard8ngatap85e6qhltgxdtxpgdd79l7xh47uutkpe3kv5m9smpyc34';
let payload = 'hello';
console.log(signedData.verify(address, payload));
</code></pre>

<blockchain>
How to verify data signed with nami wallet in a node backend?
# How to verify data signed with nami wallet in a node backend?

(Asked by: username_0 on 2021-08-28)

I am playing around with the nami cardano wallet. I am able to link the wallet in the frontend and now want to link it to the backend.
First action the frontend sends an auth request with the cardano address to the backend. The backend returns a nonce to the frontend. The front end then lets nami wallet sign this nonce and sends the signature back to the backend.
In this step, the backend should validate if the signature is valid.
Now, how can I let the backend validate this signature?
This is the information I have access to:

balance
change address
network id
reward address
unused addresses
used addresses

Can I use this info to validate the signature? Or do I need to access the blockchain from the backend code?
(I will most likely be adding blockfrost api later, so nami-wallet + blockfrost will be how I access the blockchain. Unless there are other libraries available for the backend (most likely node.js), nami+blockfrost will be how I access the blockchain.
I am also using @emurgo/cardano-serialization-lib-browser - maybe here something is possible?
Based on response below I made a quick node script:
const {COSESign1, Label} = require("./extra_modules/@emurgo/cardano-message-signing-nodejs");
const {Address} = require("@emurgo/cardano-serialization-lib-nodejs");
const {Buffer} = require('buffer');
let signedRaw = "845869a30127045820674d11e432450118d70ea78673d5e31d5cc1aec63de0ff6284784876544be3406761646472657373583901d2eb831c6cad4aba700eb35f86966fbeff19d077954430e32ce65e8da79a3abe84f4ce817fad066acc1435be2ffc6bd7dce2ec1cc6cca6cba166686173686564f44568656c6c6f5840a3b5acd99df5f3b5e4449c5a116078e9c0fcfc126a4d4e2f6a9565f40b0c77474cafd89845e768fae3f6eec0df4575fcfe7094672c8c02169d744b415c617609";
let message = COSESign1.from_bytes(Buffer.from(signedRaw, 'hex'));
let headers = message.headers().protected().deserialized_headers();
let address = Address.from_bytes(headers.header(Label.new_text('address')).as_bytes());
let publicKey = headers.key_id();
let signature = message.signature();

OK, finally got a working solution, sharing a node script that works for the next person with the same problem:
const {COSESign1, Label} = require("./extra_modules/@emurgo/cardano-message-signing-nodejs");
const {Address, Ed25519Signature, PublicKey, BaseAddress, StakeCredential, RewardAddress} = require('@emurgo/cardano-serialization-lib-nodejs');
const {Buffer} = require('buffer');

class SignedData
{
    constructor(signed)
    {
        let message = COSESign1.from_bytes(Buffer.from(Buffer.from(signed, 'hex'), 'hex'));
        let headermap = message.headers().protected().deserialized_headers();
        this.headers = {
            algorithmId: headermap.algorithm_id().as_int().as_i32(),
            address: Address.from_bytes(headermap.header(Label.new_text('address')).as_bytes()),
            publicKey: PublicKey.from_bytes(headermap.key_id())
        };
        this.payload = message.payload();
        this.signature = Ed25519Signature.from_bytes(message.signature());
        this.data = message.signed_data().to_bytes();
    }

    verify(address, payload)
    {
        if (!this.verifyPayload(payload)) {
            throw new Error('Payload does not match');
        }
        if (!this.verifyAddress(address)) {
            throw new Error('Could not verify because of address mismatch');
        }
        return this.headers.publicKey.verify(this.data, this.signature);
    };

    verifyPayload(payload)
    {
        return Buffer.from(this.payload, 'hex').compare(Buffer.from(payload, 'hex'));
    }

    verifyAddress(address)
    {
        const checkAddress = Address.from_bech32(address);
        if (this.headers.address.to_bech32() !== checkAddress.to_bech32()) {
            console.log('FASLE1');
            return false;
        }
        // check if BaseAddress
        try {
            const baseAddress = BaseAddress.from_address(this.headers.address);
            //reconstruct address
            const paymentKeyHash = this.headers.publicKey.hash();
            const stakeKeyHash = baseAddress.stake_cred().to_keyhash();
            const reconstructedAddress = BaseAddress.new(
                checkAddress.network_id(),
                StakeCredential.from_keyhash(paymentKeyHash),
                StakeCredential.from_keyhash(stakeKeyHash)
            );
            return checkAddress.to_bech32() === reconstructedAddress.to_address().to_bech32();
        } catch (e) {
        }

        try {
            const stakeKeyHash = this.headers.address.hash();
            const reconstructedAddress = RewardAddress.new(
                checkAddress.network_id(),
                StakeCredential.from_keyhash(stakeKeyHash)
            );
            return checkAddress.to_bech32() === reconstructedAddress.to_address().to_bech32();
        } catch (e) {
        }
        return false;
    };
}
let signed = "845869a30127045820674d11e432450118d70ea78673d5e31d5cc1aec63de0ff6284784876544be3406761646472657373583901d2eb831c6cad4aba700eb35f86966fbeff19d077954430e32ce65e8da79a3abe84f4ce817fad066acc1435be2ffc6bd7dce2ec1cc6cca6cba166686173686564f44568656c6c6f5840a3b5acd99df5f3b5e4449c5a116078e9c0fcfc126a4d4e2f6a9565f40b0c77474cafd89845e768fae3f6eec0df4575fcfe7094672c8c02169d744b415c617609";
let signedData = new SignedData(signed);
let address = 'addr1q8fwhqcudjk54wnsp6e4lp5kd7l07xwsw725gv8r9nn9ard8ngatap85e6qhltgxdtxpgdd79l7xh47uutkpe3kv5m9smpyc34';
let payload = 'hello';
console.log(signedData.verify(address, payload));

**Tags:** blockchain

---

## 6 answers

### Answer by username_0 on 2021-08-29. Score: 6 | Accepted answer

Script with the solution (node JS):
const {COSESign1, Label} = require("./extra_modules/@emurgo/cardano-message-signing-nodejs");
const {Address, Ed25519Signature, PublicKey, BaseAddress, StakeCredential, RewardAddress} = require('@emurgo/cardano-serialization-lib-nodejs');
const {Buffer} = require('buffer');

class SignedData
{
    constructor(signed)
    {
        let message = COSESign1.from_bytes(Buffer.from(Buffer.from(signed, 'hex'), 'hex'));
        let headermap = message.headers().protected().deserialized_headers();
        this.headers = {
            algorithmId: headermap.algorithm_id().as_int().as_i32(),
            address: Address.from_bytes(headermap.header(Label.new_text('address')).as_bytes()),
            publicKey: PublicKey.from_bytes(headermap.key_id())
        };
        this.payload = message.payload();
        this.signature = Ed25519Signature.from_bytes(message.signature());
        this.data = message.signed_data().to_bytes();
    }

    verify(address, payload)
    {
        if (!this.verifyPayload(payload)) {
            throw new Error('Payload does not match');
        }
        if (!this.verifyAddress(address)) {
            throw new Error('Could not verify because of address mismatch');
        }
        return this.headers.publicKey.verify(this.data, this.signature);
    };

    verifyPayload(payload)
    {
        return Buffer.from(this.payload, 'hex').compare(Buffer.from(payload, 'hex'));
    }

    verifyAddress(address)
    {
        const checkAddress = Address.from_bech32(address);
        if (this.headers.address.to_bech32() !== checkAddress.to_bech32()) {
            console.log('FASLE1');
            return false;
        }
        // check if BaseAddress
        try {
            const baseAddress = BaseAddress.from_address(this.headers.address);
            //reconstruct address
            const paymentKeyHash = this.headers.publicKey.hash();
            const stakeKeyHash = baseAddress.stake_cred().to_keyhash();
            const reconstructedAddress = BaseAddress.new(
                checkAddress.network_id(),
                StakeCredential.from_keyhash(paymentKeyHash),
                StakeCredential.from_keyhash(stakeKeyHash)
            );
            return checkAddress.to_bech32() === reconstructedAddress.to_address().to_bech32();
        } catch (e) {
        }

        try {
            const stakeKeyHash = this.headers.address.hash();
            const reconstructedAddress = RewardAddress.new(
                checkAddress.network_id(),
                StakeCredential.from_keyhash(stakeKeyHash)
            );
            return checkAddress.to_bech32() === reconstructedAddress.to_address().to_bech32();
        } catch (e) {
        }
        return false;
    };
}
let signed = "845869a30127045820674d11e432450118d70ea78673d5e31d5cc1aec63de0ff6284784876544be3406761646472657373583901d2eb831c6cad4aba700eb35f86966fbeff19d077954430e32ce65e8da79a3abe84f4ce817fad066acc1435be2ffc6bd7dce2ec1cc6cca6cba166686173686564f44568656c6c6f5840a3b5acd99df5f3b5e4449c5a116078e9c0fcfc126a4d4e2f6a9565f40b0c77474cafd89845e768fae3f6eec0df4575fcfe7094672c8c02169d744b415c617609";
let signedData = new SignedData(signed);
let address = 'addr1q8fwhqcudjk54wnsp6e4lp5kd7l07xwsw725gv8r9nn9ard8ngatap85e6qhltgxdtxpgdd79l7xh47uutkpe3kv5m9smpyc34';
let payload = 'hello';
console.log(signedData.verify(address, payload));

> **Comments:**
> **username_1**: I had some trouble implementing this - outlined the issues I found in a post linked below, incase anyone is able to provide some insight, please. https://cardano.stackexchange.com/questions/8708/missing-key-id-on-signature-from-signdata  
> **username_2**: @zing your link is dead?  

---

### Answer by username_3 on 2021-08-28. Score: 5

You will need to send your verification key from Nami to the backend during the handshake in order to validate the signature.
cardano.signData(address: BaseAddress|RewardAddress, payload: string) : CoseSign1


The returned CoseSign1 object contains the payload, signature and the following protected headers:
key_id => PublicKey,
address => BaseAddress | RewardAddress
algorithm_id => EdDSA(0) (the algorithm used for Cardano addresses).

Source: Nami Readme
When you run the cardano.SignData method, you are returned an object that includes the public key for the private key that you used to sign the payload. Thus, when you send the signed object to the backend, the backend has everything it needs in the header to validate that transaction. To avoid hacking, you should also ensure that the public key is constant throughout communication with the same entity or in the same session.
The output is a hex-encoded byte string adhering to the CBOR format standard. Here is a link to a playground that will let you convert between the CBOR that is the output (e.g., "CoseSign1" from the readme) and something readable. Here is an example output (don't forget to check all of the boxes to get the full result).
[<< {1: -8, 4: h'ABF29B244C2E1C4DD3CA2F8DB27D87C6B73DA1A87D08389101D5D1ECEBDD0079', "address": h'E1DC0C1BD97FCD0ED6905EBA692C1DDEFF27DA43D59C5A1F2EA766442E'} >>, {"hashed": false}, 'this is a test\n', h'1F19958A76C7414F311F5176A52A544A4BE9D59FC5F3870F41B00D8B8C52C9B819953D10AE3F23C0D906A138D37D6C70DDADE77DB94D7054D730618106F18108']

It is a JSON array with two parts: the first is the header, and the second is the contents. According to CIP-0008, the block is expected to contain the following.
COSE_Sign1 = [
    Headers,
    payload : bstr / nil,
    signature : bstr
]

My guess is that the pub key is mapped to 4: ABF29B244C2E1C4DD3CA2F8DB27D87C6B73DA1A87D08389101D5D1ECEBDD0079
Hope this helps!

> **Comments:**
> **username_0**: I had noticed that indeed, and I have tried the past 2h to extract that info, but as far as I found this is a cbor encoded message and I just cannot extract it. i see the data i tried to sign in it though (Buffer.from_bytes(data, 'hex').to_string('ascii') :) : Xi#'X gMd2EW'sUc\A.F=`bxHvTKc@gaddressX9Rkl-J:p3_o>PwD0c,f^ ':>tN-jL5>/|kW\blFL&K!fhashedtKhello worldX@CW1f4\FcWB0IrK  _\#wMYl{I;__# 56._TV68'Vu  DWf0)  
> **username_3**: I completed the explanation =^). Please see edits. Basically, it's just in CBOR, and you will have to convert it. I worked an example. Let me know if you need anything else!  
> **username_0**: I added a node script with a working solution using the emurgo serialization and message signing libraries. Thanks!  
> **username_3**: Nice well done!! Any time =^)  

---

### Answer by username_4 on 2021-11-29. Score: 2

I've created [javascript package](https://github.com/pyropy/web3-cardano-token) that could help you with this. The examples in the README should show you how to sign data on frontend and validate the signature on the backend.


---

### Answer by username_5 on 2022-01-16. Score: 2

WARNING
The provided solution in the question is a great start, however
 verifyPayload(payload)
{
    return Buffer.from(this.payload, 'hex').compare(Buffer.from(payload, 'hex'));
}

Does not do a correct comparison since in the example the payload to check is not hexencoded but rather a string, so use this instead
    verifyPayload(payload)
{
    let hexMessage=Buffer.from(this.payload, 'hex');
    let stringMessage=hexMessage.toString();
    return stringMessage===payload;
}

> **Comments:**
> **username_6**: It is a bad idea to refer to other solutions.  They may get edited and then your answer becomes weird.  Better to comment on them directly when you have enough reputation.  It is always better to focus on making your answer entirely about the question.  Take the [tour] and look at [answer] for great advice.  

---

### Answer by username_7 on 2021-10-27. Score: 0

I would like to suggest a modification in the verifyPayload function.

`verifyPayload(payload) { return Buffer.from(this.payload, 'hex').equals(Buffer.from(payload, 'utf8')); }`


---

### Answer by username_8 on 2022-05-04. Score: 0

Given that this question - and all of the answers so far - are from mid 2021, I don't think the solutions are up to date. [CIP-0030](https://github.com/cardano-foundation/CIPs/pull/148) brought a change to signData, which forced me to update the approach I use to verify wallets with my backend. I actually still use the CIP-0008 approach for Nami because of a bug on their end, but it is technically deprecated. [This post](https://cardano.stackexchange.com/a/4814/965) explains what I do now.


---

