# How to create a verifiable signature

**URL:** <https://forum.flow.com/t/how-to-create-a-verifiable-signature/1552>\
**Category:** 🏄🏻‍♀️ Cadence\
**Created:** [March 23, 2021, 2:02pm UTC](https://forum.flow.com/t/how-to-create-a-verifiable-signature/1552 "2021-03-23T14:02:35Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![avcd](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.flow.com/avcd/32/790_2.png) [@avcd](https://forum.flow.com/u/avcd)\
**Post date:** [March 23, 2021, 2:02pm UTC](https://forum.flow.com/t/how-to-create-a-verifiable-signature/1552/1 "2021-03-23T14:02:35Z")

</div>

I would like to create an ECDSA\_P256 signature in offchain and verify it in Cadence.  
So I have tried the following code using Node.js, but the result fails (`false` returns).

Can you please tell me what is wrong with this code and how I can sign it correctly in JavaScript (or Golang)? Am I missing something?

**js** :

```auto
const { SHA3 } = require('sha3');
const EC = require('elliptic').ec;
const ec = new EC('p256');

function signWithKey(privateKey, msg) {
  const key = ec.keyFromPrivate(Buffer.from(privateKey, 'hex'));
  console.log('publicKey:', key.getPublic('hex').replace(/^04/, ''))
  const sig = key.sign(hashMsg(msg));
  const n = 32;
  const r = sig.r.toArrayLike(Buffer, 'be', n);
  const s = sig.s.toArrayLike(Buffer, 'be', n);
  return Buffer.concat([r, s]).toString('hex');
};

function hashMsg(msg) {
  const sha = new SHA3(256);
  sha.update(Buffer.from(msg, 'hex'));
  return sha.digest();
};

const privateKey = '9a5080ac4d1323609357a29a755254f770c3e9dcb5e2becdbe8f13f4edd688f5'
const message = '666f6f'; // 'foo'
const signature = signWithKey(privateKey, message);
console.log('signature:', signature);

```

**Cadence script** :

```auto
import Crypto

pub fun main(): Bool {
    let keyList = Crypto.KeyList()
    let publicKeyA = Crypto.PublicKey(
        publicKey: "8653bdf116189ef4963a6250ba9f1d25daaa028e39569547976a545ca5ff72d31b6bd54955ce51ce579c583a8e10aef12d4b53037660b2fe2b12df0876789ece".decodeHex(),
        signatureAlgorithm: Crypto.ECDSA_P256
    )
    keyList.add(publicKeyA, hashAlgorithm: Crypto.SHA3_256, weight: 1.0)
    let signatureSet = [
        Crypto.KeyListSignature(
            keyIndex: 0,
            signature: "d82b75754be1593c0ab4344e62f83b43df7ef854c198181de547e3b4ba320ae2f97160445b6364a6557a1768064c3e45e1c17a9ccfb22183b2135eaf7c989b19".decodeHex()
        )
    ]
    // "foo", encoded as UTF-8, in hex representation
    let signedData = "666f6f".decodeHex()
    let isValid = keyList.isValid(signatureSet: signatureSet, signedData: signedData)
    return isValid
}

```

\*Note that this key pair was generated by the `flow keys generate` command.

---

<div class="post-metadata">

**Author:** ![avcd](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.flow.com/avcd/32/790_2.png) [@avcd](https://forum.flow.com/u/avcd)\
**Post date:** [June 11, 2021, 2:10am UTC](https://forum.flow.com/t/how-to-create-a-verifiable-signature/1552/2 "2021-06-11T02:10:12Z")

</div>

By looking at the Go SDK signature example code, I found that the message needed a 32-byte tag.  
I created a JS SDK version of this and verified that signing and its verification in Cadence correctly.

> <https://github.com/avcdsld/cadence-signature-verification/blob/ef7d73/index.ts>

**ts** :

```auto
import * as fs from 'fs';
import * as path from 'path';
import * as fcl from '@onflow/fcl';
import * as t from '@onflow/types';
import { ec as EC } from 'elliptic';
import { SHA3 } from 'sha3';

const ec: EC = new EC('p256'); // or 'secp256k1'

const toBytesWithTag = (str: string) => {
  // Tag: '464c4f572d56302e302d75736572000000000000000000000000000000000000'
  // ref: https://github.com/onflow/flow-go-sdk/blob/9bb50d/sign.go
  const tagBytes = Buffer.alloc(32);
  Buffer.from('FLOW-V0.0-user').copy(tagBytes);
  const strBytes = Buffer.from(str);
  return Buffer.concat([tagBytes, strBytes]);
}

const hashMsg = (msg: string) => {
  const sha = new SHA3(256);
  return sha.update(toBytesWithTag(msg)).digest();
};

const sign = (privKey: string, msg: string) => {
  const key = ec.keyFromPrivate(Buffer.from(privKey, 'hex'));
  const sig = key.sign(hashMsg(msg));
  const n = 32;
  const r = sig.r.toArrayLike(Buffer, 'be', n);
  const s = sig.s.toArrayLike(Buffer, 'be', n);
  return Buffer.concat([r, s]).toString('hex');
};

const toHexStr = (str: string): string => {
  return Buffer.from(str).toString('hex');
}

const verifySig = async (pubKey: string, msg: string, sig: string) => {
  fcl.config().put('accessNode.api', 'http://127.0.0.1:8080');
  const script = fs.readFileSync(path.join(__dirname, './scripts/verify_sig.cdc'), 'utf8');
  const response = await fcl.send([
    fcl.script`${script}`,
    fcl.args([
      fcl.arg([pubKey], t.Array(t.String)),
      fcl.arg(['1.0'], t.Array(t.UFix64)),
      fcl.arg([sig], t.Array(t.String)),
      fcl.arg(toHexStr(msg), t.String),
    ])
  ]);
  return await fcl.decode(response);
}

const main = async () => {
  const pubKey = '7be160dcfc5b4e9044b473fc479c4c5528d71fa2bbd9dc4f740b4a80c749830ed2ee0445f0fa707b4ea888313953b90de8b47fa388302c4268f5fdbc6329754f';
  const privKey = '9a3259d7c18fd98ccf51356a48df5b63d7d544153db49079c46d152ea9739539';
  const msg = 'test message';
  const sig = sign(privKey, msg);
  const isValid = await verifySig(pubKey, msg, sig);
  console.log({ pubKey, privKey, msg, sig, isValid });
};

main().catch(e => console.error(e));

```

**Cadence script** :

```auto
import Crypto

pub fun main(rawPublicKeys: [String], weights: [UFix64], signatures: [String], signedData: String): Bool {
  let keyList = Crypto.KeyList()
  var i = 0
  for rawPublicKey in rawPublicKeys {
    keyList.add(
      PublicKey(
        publicKey: rawPublicKey.decodeHex(),
        signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 // or SignatureAlgorithm.ECDSA_Secp256k1
      ),
      hashAlgorithm: HashAlgorithm.SHA3_256,
      weight: weights[i],
    )
    i = i + 1
  }

  let signatureSet: [Crypto.KeyListSignature] = []
  var j = 0
  for signature in signatures {
    signatureSet.append(
      Crypto.KeyListSignature(
        keyIndex: j,
        signature: signature.decodeHex()
      )
    )
    j = j + 1
  }

  return keyList.isValid(
    signatureSet: signatureSet,
    signedData: signedData.decodeHex(),
  )
}

```

I hope this information is useful to someone.

---

<div class="post-metadata">

**Author:** ![bastian](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.flow.com/bastian/32/183_2.png) [@bastian](https://forum.flow.com/u/bastian)\
**Post date:** [June 11, 2021, 4:18pm UTC](https://forum.flow.com/t/how-to-create-a-verifiable-signature/1552/3 "2021-06-11T16:18:46Z")

</div>

@avcd Thank you so much for figuring this out! Someone is working on the JVM SDK and we tried to figure out why the verification fails. This is very useful! We should document this better

---

<div class="post-metadata">

**Author:** ![briandilley](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.flow.com/briandilley/32/300_2.png) [@briandilley](https://forum.flow.com/u/briandilley)\
**Post date:** [June 11, 2021, 5:27pm UTC](https://forum.flow.com/t/how-to-create-a-verifiable-signature/1552/4 "2021-06-11T17:27:08Z")

</div>

Still having some trouble with this on the JVM SDK side. Running this test:

> <https://github.com/the-nft-company/flow-jvm-sdk/blob/domainTags/src/test/kotlin/org/onflow/sdk/ScriptTest.kt#L118>

“False” is all that is ever returned.

---

<div class="post-metadata">

**Author:** ![avcd](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.flow.com/avcd/32/790_2.png) [@avcd](https://forum.flow.com/u/avcd)\
**Post date:** [June 29, 2021, 3:57pm UTC](https://forum.flow.com/t/how-to-create-a-verifiable-signature/1552/5 "2021-06-29T15:57:46Z")

</div>

There is a destructive change in Cadence and I have modified the sample code.

```auto
return keyList.isValid(...)

```

↓

```auto
return keyList.verify(...)

```

> <https://github.com/avcdsld/cadence-signature-verification/blob/main/scripts/verify_sig.cdc>

---

<div class="post-metadata">

**Author:** ![pnjn10](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.flow.com/pnjn10/32/1632_2.png) [@pnjn10](https://forum.flow.com/u/pnjn10)\
**Post date:** [September 16, 2024, 8:20pm UTC](https://forum.flow.com/t/how-to-create-a-verifiable-signature/1552/6 "2024-09-16T20:20:11Z")

</div>

Not working for me
