verifiable credentials

2025-12-10 0 535

@transmute/verifiablecredentials

Questions? Contact Transmute

? Experimental implementation of Verifiable Credentials Data Model v2.0

  • CR-vcdatamodel-2.0-20240201

  • WD-vc-jose-cose-20240126

  • WD-vc-bitstring-status-list-20240107

  • CG-ED-traceability-vocab

Usage

Requires node 18 or higher.

nvm use 18
npm i @transmute/verifiable-credentials@latest --save
import * as transmute from \"@transmute/verifiable-credentials\";

Generating Keys

const privateKey = await transmute.key.generate({
  alg,
  type: \"application/jwk+json\",
});
// console.log(new TextDecoder().decode(privateKey))
// {
//   \"kid\": \"xSgm4GQOT_ZyYFApew0GnRvPWt70omVJV9XVB5tsmN8\",
//   \"alg\": \"ES256\",
//   \"kty\": \"EC\",
//   \"crv\": \"P-256\",
//   \"x\": \"XRkZngz2KSCrLdXKGCRNyDzBgsovioZIqMWnF42nmdg\",
//   \"y\": \"H2t6Xxdg8p8Cqn2-hsuWnXYj0192He4zTZghAxNXllo\",
//   ...
// }
const publicKey = await transmute.key.publicFromPrivate({
  type: \"application/jwk+json\",
  content: privateKey,
});
// console.log(new TextDecoder().decode(publicKey))
// {
//   \"kid\": \"xSgm4GQOT_ZyYFApew0GnRvPWt70omVJV9XVB5tsmN8\",
//   \"alg\": \"ES256\",
//   \"kty\": \"EC\",
//   \"crv\": \"P-256\",
//   \"x\": \"XRkZngz2KSCrLdXKGCRNyDzBgsovioZIqMWnF42nmdg\",
//   \"y\": \"H2t6Xxdg8p8Cqn2-hsuWnXYj0192He4zTZghAxNXllo\",
// }

Issuing Credentials

const alg = `ES256`;
const statusListSize = 131072;
const revocationIndex = 94567;
const suspensionIndex = 23452;

const issuer = `did:example:123`;
const baseURL = `https://vend*or.**example/api`;

const issuerSigner = {
  sign: async (bytes: Uint8Array) => {
    const jws = await new jose.CompactSign(bytes)
      .setProtectedHeader({ kid: `${issuer}#key-42`, alg })
      .sign(
        await transmute.key.importKeyLike({
          type: \"application/jwk+json\",
          content: privateKey,
        })
      );
    return transmute.text.encoder.encode(jws);
  },
};
const issued = await transmute
  .issuer({
    alg,
    type: \"application/vc-ld+jwt\",
    signer: issuerSigner,
  })
  .issue({
    claimset: transmute.text.encoder.encode(`
\"@context\":
  - https://www.***w3.org/ns/credentials/v2
  - https://www.***w3.org/ns/credentials/examples/v2

id: ${baseURL}/credentials/3732
type:
  - VerifiableCredential
  - ExampleDegreeCredential
issuer:
  id: ${issuer}
  name: \"Example University\"
validFrom: ${moment().toISOString()}
credentialSchema:
  id: ${baseURL}/schemas/product-passport
  type: JsonSchema
credentialStatus:
  - id: ${baseURL}/credentials/status/3#${revocationIndex}
    type: BitstringStatusListEntry
    statusPurpose: revocation
    statusListIndex: \"${revocationIndex}\"
    statusListCredential: \"${baseURL}/credentials/status/3\"
  - id: ${baseURL}/credentials/status/4#${suspensionIndex}
    type: BitstringStatusListEntry
    statusPurpose: suspension
    statusListIndex: \"${suspensionIndex}\"
    statusListCredential: \"${baseURL}/credentials/status/4\"
credentialSubject:
  id: did:example:ebfeb1f712ebc6f1c276e12ec21
  degree:
    type: ExampleBachelorDegree
    subtype: Bachelor of Science and Arts
`),
  });
// console.log(new TextDecoder().decode(issued))
// eyJraWQiOiJkaWQ6ZXhhbXBsZToxMjMja2V5LTQyIiwiYWxnIjoiRVMyNTYifQ.eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvbnMvY3JlZGVudGlhbHMvdjIiLCJodHRwczovL3ZlbmRvci5leGFtcGxlL2FwaS9jb250ZXh0L3YyIl0sImlkIjoiaHR0cHM6Ly92ZW5kb3IuZXhhbXBsZS9hcGkvY3JlZGVudGlhbHMvMzczMiIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJFeGFtcGxlRGVncmVlQ3JlZGVudGlhbCJdLCJpc3N1ZXIiOnsiaWQiOiJkaWQ6ZXhhbXBsZToxMjMiLCJuYW1lIjoiRXhhbXBsZSBVbml2ZXJzaXR5In0sInZhbGlkRnJvbSI6IjIwMjQtMDQtMjRUMjI6MjM6MDIuODU2WiIsImNyZWRlbnRpYWxTY2hlbWEiOnsiaWQiOiJodHRwczovL3ZlbmRvci5leGFtcGxlL2FwaS9zY2hlbWFzL3Byb2R1Y3QtcGFzc3BvcnQiLCJ0eXBlIjoiSnNvblNjaGVtYSJ9LCJjcmVkZW50aWFsU3RhdHVzIjpbeyJpZCI6Imh0dHBzOi8vdmVuZG9yLmV4YW1wbGUvYXBpL2NyZWRlbnRpYWxzL3N0YXR1cy8zIzk0NTY3IiwidHlwZSI6IkJpdHN0cmluZ1N0YXR1c0xpc3RFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwic3RhdHVzTGlzdEluZGV4IjoiOTQ1NjciLCJzdGF0dXNMaXN0Q3JlZGVudGlhbCI6Imh0dHBzOi8vdmVuZG9yLmV4YW1wbGUvYXBpL2NyZWRlbnRpYWxzL3N0YXR1cy8zIn0seyJpZCI6Imh0dHBzOi8vdmVuZG9yLmV4YW1wbGUvYXBpL2NyZWRlbnRpYWxzL3N0YXR1cy80IzIzNDUyIiwidHlwZSI6IkJpdHN0cmluZ1N0YXR1c0xpc3RFbnRyeSIsInN0YXR1c1B1cnBvc2UiOiJzdXNwZW5zaW9uIiwic3RhdHVzTGlzdEluZGV4IjoiMjM0NTIiLCJzdGF0dXNMaXN0Q3JlZGVudGlhbCI6Imh0dHBzOi8vdmVuZG9yLmV4YW1wbGUvYXBpL2NyZWRlbnRpYWxzL3N0YXR1cy80In1dLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSIsImRlZ3JlZSI6eyJ0eXBlIjoiRXhhbXBsZUJhY2hlbG9yRGVncmVlIiwic3VidHlwZSI6IkJhY2hlbG9yIG9mIFNjaWVuY2UgYW5kIEFydHMifX19.xHjfiUwx61qmoVMGLrHT8FI-ZYUHXQy4B6oF0Cb5EOTYYPXdwjW9sa1l5aa008xvsFvrcNats9TywmN2nNKz6A

Validating Credentials

const validated = await transmute
  .validator({
    resolver: {
      resolve: async ({ id, type, content }) => {
        // Resolve external resources according to verifier policy
        // In this case, we return inline exampes...
        if (id === `${baseURL}/schemas/product-passport`) {
          return {
            type: `application/schema+json`,
            content: transmute.text.encoder.encode(`
{
  \"$id\": \"${baseURL}/schemas/product-passport\",
  \"$schema\": \"https://json*-s*ch*ema.org/draft/2020-12/schema\",
  \"title\": \"Example JSON Schema\",
  \"description\": \"This is a test schema\",
  \"type\": \"object\",
  \"properties\": {
    \"credentialSubject\": {
      \"type\": \"object\",
      \"properties\": {
        \"id\": {
          \"type\": \"string\"
        }
      }
    }
  }
}
              `),
          };
        }
        if (id === `${baseURL}/credentials/status/3`) {
          return {
            type: `application/vc-ld+jwt`,
            content: await transmute
              .issuer({
                alg: \"ES384\",
                type: \"application/vc-ld+jwt\",
                signer: issuerSigner,
              })
              .issue({
                claimset: transmute.text.encoder.encode(
                  `
\"@context\":
  - https://www.***w3.org/ns/credentials/v2
id: ${baseURL}/status/3#list
type:
  - VerifiableCredential
  - BitstringStatusListCredential
issuer:
  id: ${issuer}
validFrom: ${moment().toISOString()}
credentialSubject:
  id: ${baseURL}/status/3#list#list
  type: BitstringStatusList
  statusPurpose: revocation
  encodedList: ${await transmute.status
    .bs(statusListSize)
    .set(revocationIndex, false)
    .encode()}
`.trim()
                ),
              }),
          };
        }
        if (id === `${baseURL}/credentials/status/4`) {
          return {
            type: `application/vc-ld+jwt`,
            content: await transmute
              .issuer({
                alg: \"ES384\",
                type: \"application/vc-ld+jwt\",
                signer: issuerSigner,
              })
              .issue({
                claimset: transmute.text.encoder.encode(
                  `
\"@context\":
  - https://www.***w3.org/ns/credentials/v2
id: ${baseURL}/status/4#list
type:
  - VerifiableCredential
  - BitstringStatusListCredential
issuer:
  id: ${issuer}
validFrom: ${moment().toISOString()}
credentialSubject:
  id: ${baseURL}/status/4#list#list
  type: BitstringStatusList
  statusPurpose: suspension
  encodedList: ${await transmute.status
    .bs(statusListSize)
    .set(suspensionIndex, false)
    .encode()}
`.trim()
                ),
              }),
          };
        }
        if (content != undefined && type === `application/vc-ld+jwt`) {
          const { kid } = jose.decodeProtectedHeader(
            transmute.text.decoder.decode(content)
          );
          // lookup public key by kid on a trusted resolver
          if (kid === `did:example:123#key-42`) {
            return {
              type: \"application/jwk+json\",
              content: publicKey,
            };
          }
        }
        throw new Error(\"Resolver option not supported.\");
      },
    },
  })
  .validate({
    type: \"application/vc-ld+jwt\",
    content: issued,
  });

// expect(validated.valid).toBe(true)
// expect(validated.schema[`${baseURL}/schemas/product-passport`].valid).toBe(true)
// expect(validated.status[`${baseURL}/credentials/status/3#${revocationIndex}`].valid).toBe(false)
// expect(validated.status[`${baseURL}/credentials/status/4#${suspensionIndex}`].valid).toBe(false)

Issuing Presentations

const presentation = await transmute
  .holder({
    alg,
    type: \"application/vp-ld+jwt\",
  })
  .issue({
    signer: issuerSigner,
    presentation: {
      \"@context\": [\"https://www.***w3.org/ns/credentials/v2\"],
      type: [\"VerifiablePresentation\"],
      holder: `${baseURL}/holders/565049`,
      // this part is built from disclosures without key binding below.
      // \"verifiableCredential\": [{
      //   \"@context\": \"https://www.***w3.org/ns/credentials/v2\",
      //   \"id\": \"data:application/vc-ld+sd-jwt;QzVjV...RMjU\",
      //   \"type\": \"EnvelopedVerifiableCredential\"
      // }]
    },
    disclosures: [
      {
        type: `application/vc-ld+jwt`,
        credential: issued,
      },
    ],
  });

Validating Presentations

const validation = await transmute
  .validator({
    resolver: {
      resolve: async ({ type, content }) => {
        // Resolve external resources according to verifier policy
        // In this case, we return inline exampes...
        if (content != undefined && type === `application/vp-ld+jwt`) {
          const { kid } = jose.decodeProtectedHeader(
            transmute.text.decoder.decode(content)
          );
          // lookup public key on a resolver
          if (kid === `did:example:123#key-42`) {
            return {
              type: \"application/jwk+json\",
              content: publicKey,
            };
          }
        }
        throw new Error(\"Resolver option not supported.\");
      },
    },
  })
  .validate<transmute.TraceablePresentationValidationResult>({
    type: `application/vp-ld+jwt`,
    content: presentation,
  });
// {
//   \"valid\": true,
//   \"content\": {
//     \"@context\": [
//       \"https://www.***w3.org/ns/credentials/v2\"
//     ],
//     \"type\": [
//       \"VerifiablePresentation\"
//     ],
//     \"holder\": \"https://*vendo**r.example/api/holders/565049\",
//     \"verifiableCredential\": [
//       {
//         \"@context\": \"https://www.***w3.org/ns/credentials/v2\",
//         \"id\": \"data:application/vc-ld+jwt;eyJraWQiOiJkaWQ6ZX...

Develop

npm i
npm t
npm run lint
npm run build

下载源码

通过命令行克隆项目:

git clone https://github.com/transmute-industries/verifiable-credentials.git

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 verifiable credentials https://www.zuozi.net/33318.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务