/cose

A COSE SDK for TypeScript

Primary LanguageTypeScriptApache License 2.0Apache-2.0

cose

CI Branches Functions Lines Statements Jest coverage

Usage

🔥 This package is not stable or suitable for production use 🚧

npm install '@transmute/cose'
import * as cose from "@transmute/cose";
const cose = require("@transmute/cose");

SCITT Receipts

const issuerSecretKeyJwk = await cose.key.generate<cose.SecretKeyJwk>(
  "ES256",
  "application/jwk+json"
);
const issuerPublicKeyJwk = await cose.key.publicFromPrivate<cose.PublicKeyJwk>(
  issuerSecretKeyJwk
);

const notarySecretKeyJwk = await cose.key.generate<cose.SecretKeyJwk>(
  "ES256",
  "application/jwk+json"
);
const notaryPublicKeyJwk = await cose.key.publicFromPrivate<cose.PublicKeyJwk>(
  notarySecretKeyJwk
);

const issuer = cose.detached.signer({
  remote: cose.crypto.signer({
    privateKeyJwk: issuerSecretKeyJwk,
  }),
});
const notary = cose.detached.signer({
  remote: cose.crypto.signer({
    privateKeyJwk: notarySecretKeyJwk,
  }),
});
const content = fs.readFileSync("./examples/image.png");
const signatureForImage = await issuer.sign({
  protectedHeader: cose.ProtectedHeader([
    [cose.Protected.Alg, cose.Signature.ES256], // signing algorithm ES256
    [cose.Protected.ContentType, "image/png"], // content type image/png
    [cose.Protected.Kid, issuerPublicKeyJwk.kid], // issuer key identifier
  ]),
  payload: content,
});
const transparencyLogContainingImageSignatures = [
  await cose.receipt.leaf(signatureForImage),
];
const receiptForImageSignature = await cose.receipt.inclusion.issue({
  protectedHeader: cose.ProtectedHeader([
    [cose.Protected.Alg, cose.Signature.ES256],
    [
      cose.Protected.VerifiableDataStructure,
      cose.VerifiableDataStructures["RFC9162-Binary-Merkle-Tree"],
    ],
    [cose.Protected.Kid, notaryPublicKeyJwk.kid],
  ]),
  entry: 0,
  entries: transparencyLogContainingImageSignatures,
  signer: notary,
});
const transparentSignature = await cose.receipt.add(
  signatureForImage,
  receiptForImageSignature
);
const resolve = async (
  coseSign1: cose.CoseSign1Bytes
): Promise<cose.PublicKeyJwk> => {
  const { tag, value } = cose.cbor.decodeFirstSync(coseSign1);
  if (tag !== cose.COSE_Sign1) {
    throw new Error("Only tagged cose sign 1 are supported");
  }
  const [protectedHeaderBytes] = value;
  const protectedHeaderMap = cose.cbor.decodeFirstSync(protectedHeaderBytes);
  const kid = protectedHeaderMap.get(cose.Protected.Kid);
  if (kid === issuerPublicKeyJwk.kid) {
    return issuerPublicKeyJwk;
  }
  if (kid === notaryPublicKeyJwk.kid) {
    return notaryPublicKeyJwk;
  }
  throw new Error("No verification key found in trust store.");
};
const verifier = await cose.receipt.verifier({
  resolve,
});
const verified = await verifier.verify({
  coseSign1: transparentSignature,
  payload: content,
});

HPKE

const message = "💀 My lungs taste the air of Time Blown past falling sands ⌛";
const plaintext = new TextEncoder().encode(message);
const encryptionKeys = {
  keys: [
    {
      kid: "meriadoc.brandybuck@buckland.example",
      alg: "HPKE-Base-P256-SHA256-AES128GCM",
      kty: "EC",
      crv: "P-256",
      x: "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
      y: "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
    },
  ],
};
const decryptionKeys = {
  keys: [
    {
      kid: "meriadoc.brandybuck@buckland.example",
      alg: "HPKE-Base-P256-SHA256-AES128GCM",
      kty: "EC",
      crv: "P-256",
      x: "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
      y: "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
      d: "r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8",
    },
  ],
};
const ciphertext = await cose.encrypt.direct({
  protectedHeader: ProtectedHeader([
    [Protected.Alg, Direct["HPKE-Base-P256-SHA256-AES128GCM"]],
  ]),
  plaintext,
  recipients: encryptionKeys,
});
const decrypted = await cose.decrypt.direct({
  ciphertext,
  recipients: decryptionKeys,
});

COSE RFCs

COSE Drafts

SCITT Drafts

Develop

npm i
npm t
npm run lint
npm run build