vc: Ed25519Signature2018 verification yields "Invalid Signature" error

Hi,

I am having difficulties in actually verifying the VP I have created.

The error is simply:

Error: Invalid signature.
 at Ed25519Signature2018.verifyProof (/Users/moustachiste/work/verifiable-presentation/node_modules/jsonld-signatures/lib/suites/LinkedDataSignature.js:162:15)

So tried to dive into where the error was coming from, but I reached a low level of node-gyp (from here: https://github.com/digitalbazaar/crypto-ld/blob/master/lib/Ed25519KeyPair.js#L551) and with non human readable data so that didn’t help.

My hunch is that the suite I use to sign and to verify are somehow not identical, or wrongly used/implemented.

So here is my process:

  • I create and sign a presentation as follows:
  const presentation = vc.createPresentation({
    verifiableCredential: certs
  });

  const suite = await generateSigningSuite();

  const verifiablePresentation = await vc.signPresentation({
    presentation,
    suite,
    challenge: 'yo',
    documentLoader // custom loader resolves Blockcerts, DID document and context urls 
  });
  • I generate the suite as follows
  const keyPair = await Ed25519KeyPair.generate({
    id: DID.authentication[0],
    controller: DID.id
  });

  const suite = new Ed25519Signature2018({
    verificationMethod: keyPair.id,
    key: keyPair
  });

And this is the DID document I am using to generate the signature:

{
  "@context": "https://www.w3.org/ns/did/v1",
  "id": "did:key:z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca",
  "publicKey": [
    {
      "id": "did:key:z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca#z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca",
      "type": "Ed25519VerificationKey2018",
      "controller": "did:key:z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca",
      "publicKeyBase58": "Asrb847AmCCTmjeiQCHR5DtRaHs5KfvjXBWCNM1zh3qC"
    }
  ],
  "authentication": [
    "did:key:z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca#z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca"
  ],
  "assertionMethod": [
    "did:key:z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca#z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca"
  ],
  "capabilityDelegation": [
    "did:key:z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca#z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca"
  ],
  "capabilityInvocation": [
    "did:key:z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca#z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca"
  ],
  "keyAgreement": [
    {
      "id": "did:key:z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca#zCHKnNyBrWe3eoiaUZhgcNtNYrcP25XwfDVnCXxbedk3MT",
      "type": "X25519KeyAgreementKey2019",
      "controller": "did:key:z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca",
      "publicKeyBase58": "Hq4fc13KGJNNcXW7JC3QjCsZN7D7yF3ibtuW7WrEdoFF"
    }
  ]
}
  • To verify, this is the code I have written:
  const suite = [new Ed25519Signature2018(presentation.proof), prepareMerkleProof2019Suite(presentation.verifiableCredential[0].proof)]; // second suite to verify Blockcerts VC
  vc.verify({presentation, suite, documentLoader, challenge: 'yo'}).then((verificationStatus) => {
    return res.json(verificationStatus);
  });

And the presentation.proof, in my assumption, coming from the previous signPresentation instruction:

  "proof": {
    "type": "Ed25519Signature2018",
    "created": "2020-02-27T13:15:57Z",
    "challenge": "yo",
    "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..62FLffkSzfVJ26E2kAIrQKgp1bKii54u1KJbLxkYqtW_6hEPCL0voaxrR0hufUyYQZc8tvzJ0UgpbxvSFBODCQ",
    "proofPurpose": "authentication",
    "verificationMethod": "did:key:z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca#z6MkpL7diJMc6jgvtEVR5mFFvKSRPs8vjZB6DCR8Ccz1cGca"
  }

(you may find the full presentation example in the previous issue I opened https://github.com/digitalbazaar/vc-js/issues/48#issue-570605457 although note that the JWS is likely different since I regenerated the presentation multiple times to investigate).

It is worth noting, than contrary to the unit tests (https://github.com/digitalbazaar/vc-js/blob/master/tests/10-verify.spec.js#L184), this happens in 2 different and separate node instructions, so I do not maintain one and unique JS object for the suite, but rather rely on JSON data to “communicate between suites”. I run npm run generate:presentation and npm run verify:presentation.

Thanks for reading

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 18 (13 by maintainers)

Most upvoted comments

So that all worked, thanks @aljones15 for pointing to the right direction. @dlongley since I had the VC already verifying, it wasn’t polluting the output, so once I implemented the correct way the verification was easy.

The only thing I don’t like in my implementation (and since this is PoC work it does not matter much), is that everything is sequential and happening in the same execution. I would like to be able to separate items to be instructed step by step. However, what I have noticed is that often times, the different methods (ie: Ed25519KeyPair or Ed25519Signature2018) not only produce data, but also are classes with executable methods. Would I be able to reinstantiate the classes with the data that was initially produced?

More concrete example: This is my keyPair generated data.

{
  "passphrase": null,
  "id": "did:key:z6Mkq3L1jEDDZ5R7eT523FMLxC4k6MCpzqD7ff1CrkWpoJwM#z6Mkq3L1jEDDZ5R7eT523FMLxC4k6MCpzqD7ff1CrkWpoJwM",
  "controller": "did:key:z6Mkq3L1jEDDZ5R7eT523FMLxC4k6MCpzqD7ff1CrkWpoJwM",
  "type": "Ed25519VerificationKey2018",
  "privateKeyBase58": "3AvNk9VpgjGPwyThyWqxPmZtW5BrdWaFJNJejNWAhXWkKjPapA8qjwUF85L3kyatS1KcHqbjTSktmRD2m1ARHoco",
  "publicKeyBase58": "Bb4y8yxnDXveXxEKMgPW76WkGmvyawxkye6H2UYot69y"
}

If I don’t maintain the JS object (execute in the same runtime), am I able to re-create a keyPair object from this data, but with methods attached?

yes, use the Ed25519KeyPair.from method. This will save time in tests to as you can store the keymaterial as json. Because your key.export contains the type you could even use LDKeyPair.from using that JSON output.

https://github.com/digitalbazaar/crypto-ld/blob/892c5950b739847ae2aa2b893152a26f14f32a1d/lib/LDKeyPair.js#L89-L117

You would need to call keyToDidDoc on the result of the from though.

Actually you need to go the other way (this is WRT to having a signing key and a didKey that match):

const {keyToDidDoc} = require('did-method-key').driver();
const {Ed25519KeyPair} = require('crypto-ld');
// this produces a keypair with private key material
  // used for signing credentials / presentations
  keyPair = await Ed25519KeyPair.generate();
  // this is a did key document which only contains
  // the public key material used to verify a credential
 didDocument = keyToDidDoc(keyPair);
 // this can be assertionMethod etc.
 // if your purpose is Authentication then use authentication
 keyPair.id = didDocument.authentication[0];

yeah looking at this further I think this is your mistake: you are creating a new private key to sign the presentation and then using a didDocument’s authentication key to verify. You need to go the other way around:

  1. create a keyPair
  2. use that keyPair to create the didDocument
  3. use the keyPair to sign
  4. use the didDocument’s authentication key to verify.