Giter Site home page Giter Site logo

c2pa-node's Introduction

C2PA Node.js

The c2pa-node repository implements a Node.js API that can:

WARNING: This is an early prerelease version of this library. There may be bugs and unimplemented features, and the API is subject to change.

Installation

Prerequisites

You must install:

If you need to manage multiple versions of Node on your machine, use a tool such as nvm.

Installing for use in a client app

Using npm:

$ npm install c2pa-node

Using Yarn:

$ yarn add c2pa-node

Using pnpm:

$ pnpm add c2pa-node

This downloads the latest Rust SDK and builds it as a binding locally (which is why Rust must be locally installed).

Note: In the future, CAI may create binary releases so you won't need to install Rust on your machine.

Building custom binaries

For platforms or architectures that do not have Rust tooling installed, you may need to build custom binaries. To pre-build a binary, install the Rust toolchain and then run the following commands on the target system or VM:

$ cd c2pa-node
$ pnpm install
$ pnpm build:rust

Then, you can copy the binary to a place that is accessible by your application (in this example, it is /path/to/my/application/resources) and set the path to the c2pa.node module via the C2PA_LIBRARY_PATH environment variable:

# In your application that uses c2pa-node
$ cd /path/to/my/application
$ mkdir resources
$ cp /path/to/c2pa-node/generated/c2pa.node resources/c2pa.node
$ export C2PA_LIBRARY_PATH=resources/c2pa.node
$ npm install c2pa-node
$ npm start

Important: C2PA_LIBRARY_PATH must be set while both installing or adding c2pa-node to your app to avoid building the Rust code. It must also be set while running your app so that it loads the bindings from the correct location.

Installing for project contributions

If you want to contribute to this project, install the project with npm. In the project directory, run:

# Switch to the supported version of Node.js for building
$ nvm use
# Install pnpm
$ npm install -g pnpm
# Install dependencies
$ pnpm install
# Build the SDK
$ pnpm run build

Usage

Creating a c2pa object

Instantiate a c2pa object by using createC2pa():

import { createC2pa } from 'c2pa-node';

const c2pa = createC2pa();

Reading a manifest

Use the c2pa.read() function to read a manifest; for example:

import { createC2pa } from 'c2pa-node';
import { readFile } from 'node:fs/promises';

const c2pa = createC2pa();

async function read(path, mimeType) {
  const buffer = await readFile(path);
  const result = await c2pa.read({ buffer });

  if (result) {
    const { active_manifest, manifests, validation_status } = result;
    console.log(active_manifest);
  } else {
    console.log('No claim found');
  }
}

read('my-c2pa-file.jpg', 'image/jpeg');

Creating a manifest

To create a manifest, pass the claim information to the ManifestBuilder object constructor; for example:

import { ManifestBuilder } from 'c2pa-node';

const manifest = new ManifestBuilder({
  claim_generator: 'my-app/1.0.0',
  format: 'image/jpeg',
  title: 'node_test_local_signer.jpg',
  assertions: [
    {
      label: 'c2pa.actions',
      data: {
        actions: [
          {
            action: 'c2pa.created',
          },
        ],
      },
    },
    {
      label: 'com.custom.my-assertion',
      data: {
        description: 'My custom test assertion',
        version: '1.0.0',
      },
    },
  ],
});

Adding an ingredient

Use c2pa.createIngredient() to load ingredient data for inclusion into a manifest. You can store the ingredient data on the backend and load it at signing time if necessary (for example if the original ingredient is no longer available); for example:

// Create the ingredient asset from a buffer
const ingredientAssetFromBuffer = {
  buffer: await readFile('my-ingredient.jpg'),
  mimeType: 'image/jpeg',
};
// Or load from a file
const ingredientAssetFromFile = {
  path: resolve('my-ingredient.jpg'),
};

// Create the ingredient
const ingredient = await c2pa.createIngredient({
  asset: ingredientAssetFromBuffer,
  title: 'ingredient.jpg',
});
// Add it to the manifest
manifest.addIngredient(ingredient);

Signing a manifest

Use the c2pa.sign() method to sign an ingredient, either locally if you have a signing certificate and key available, or by using a remote signing API.

Signing buffers

If you have an asset file's data loaded into memory, you can sign the the asset using a buffer.

NOTE: Signing using a buffer is currently supported only for image/jpeg and image/png data. For all other file types, use the file-based approach below.

import { readFile } from 'node:fs/promises';
import { createC2pa, createTestSigner } from 'c2pa-node';

async function sign(asset, manifest) {
  const buffer = await readFile('to-be-signed.jpg');
  const asset: Asset = { buffer, mimeType: 'image/jpeg' };
  const signer = createTestSigner();
  const c2pa = createC2pa({
    signer,
  });

  const { signedAsset, signedManifest } = await c2pa.sign({
    asset,
    manifest,
  });
}

sign(asset, manifest);

Signing files

To avoid loading the entire asset into memory (or for file types other than JPEG and PNG that don't support in-memory signing), pass in the file path to the asset file to sign it; for example:

import { resolve } from 'node:path';
import { createC2pa, createTestSigner } from 'c2pa-node';

async function sign(asset, manifest) {
  const asset = {
    path: resolve('to-be-signed.jpg'),
  };
  const outputPath = resolve('signed.jpg');
  const signer = createTestSigner();
  const c2pa = createC2pa({
    signer,
  });

  const { signedAsset, signedManifest } = await c2pa.sign({
    manifest,
    asset,
    options: {
      outputPath,
    },
  });
}

sign(asset, manifest);

Local signing

If you have a signing certificate and key, you can sign locally using a local signer; for example:

import { readFile } from 'node:fs/promises';
import { SigningAlgorithm } from 'c2pa-node';

async function createLocalSigner() {
  const [certificate, privateKey] = await Promise.all([
    readFile('tests/fixtures/certs/es256.pem'),
    readFile('tests/fixtures/certs/es256.pub'),
  ]);

  return {
    type: 'local',
    certificate,
    privateKey,
    algorithm: SigningAlgorithm.ES256,
    tsaUrl: 'http://timestamp.digicert.com',
  };
}

async function sign(asset, manifest) {
  const buffer = await readFile('to-be-signed.jpg');
  const asset: Asset = { buffer, mimeType: 'image/jpeg' };
  const signer = await createLocalSigner();
  const c2pa = createC2pa({
    signer,
  });

  const { signedAsset, signedManifest } = await c2pa.sign({
    asset,
    manifest,
  });
}

sign(asset, manifest);

Remote signing

If you have access to a web service that performs signing, you can use it to sign remotely; for example:

import { readFile } from 'node:fs/promises';
import { fetch, Headers } from 'node-fetch';
import { createC2pa, SigningAlgorithm } from 'c2pa-node';

function createRemoteSigner() {
  return {
    type: 'remote',
    async reserveSize() {
      const url = `https://my.signing.service/box-size`;
      const res = await fetch(url);
      const data = (await res.json()) as { boxSize: number };
      return data.boxSize;
    },
    async sign({ reserveSize, toBeSigned }) {
      const url = `https://my.signing.service/sign?boxSize=${reserveSize}`;
      const res = await fetch(url, {
        method: 'POST',
        headers: new Headers({
          'Content-Type': 'application/octet-stream',
        }),
        body: toBeSigned,
      });
      return res.buffer();
    },
  };
}

async function sign(asset, manifest) {
  const buffer = await readFile('to-be-signed.jpg');
  const asset: Asset = { buffer, mimeType: 'image/jpeg' };
  const signer = createRemoteSigner();
  const c2pa = createC2pa({
    signer,
  });

  const { signedAsset, signedManifest } = await c2pa.sign({
    asset,
    manifest,
  });
}

sign(asset, manifest);

API documentation

For the API documentation, see the /docs/ directory.

Testing

After installation, run the test suite by entering this command:

$ pnpm test

c2pa-node's People

Contributors

dkozma avatar github-actions[bot] avatar emensch avatar crandmck avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.