-
Notifications
You must be signed in to change notification settings - Fork 16
feat: expanded signer definition API #92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,7 +13,9 @@ import { getBlobForAsset } from 'test/utils.js'; | |
| import { Settings } from './settings.js'; | ||
| import { createC2pa } from './c2pa.js'; | ||
| import wasmSrc from '@contentauth/c2pa-web/resources/c2pa.wasm?url'; | ||
| import { createTestSigner } from 'test/testSigner.js'; | ||
|
|
||
| import C_with_CAWG_data_thumbnail from 'test/assets/C_with_CAWG_data_thumbnail.jpg'; | ||
| import C_JPG from 'test/assets/C.jpg'; | ||
|
|
||
| describe('builder', () => { | ||
|
|
@@ -181,7 +183,6 @@ describe('builder', () => { | |
| instance_id: '' | ||
| }; | ||
|
|
||
| // Create builder with generateC2paArchive enabled | ||
| const builder = await c2pa.builder.fromDefinition(manifestDefinition); | ||
|
|
||
| const blob = await getBlobForAsset(C_JPG); | ||
|
|
@@ -360,6 +361,57 @@ describe('builder', () => { | |
| expect(definition.ingredients).toHaveLength(1); | ||
| expect(definition.ingredients?.[0]).toMatchObject(ingredient); | ||
| }); | ||
|
|
||
| describe('sign', () => { | ||
| test.only('should sign and return bytes representing the signed asset', async ({ | ||
| c2pa | ||
| }) => { | ||
| const manifestDefinition: ManifestDefinition = { | ||
| claim_generator_info: [ | ||
| { | ||
| name: 'c2pa-web-test', | ||
| version: '1.0.0' | ||
| } | ||
| ], | ||
| title: 'Test_Manifest', | ||
| format: 'image/jpeg', | ||
| assertions: [], | ||
| ingredients: [], | ||
| instance_id: '' | ||
| }; | ||
|
|
||
| const builder = await c2pa.builder.fromDefinition(manifestDefinition); | ||
|
|
||
| const blob = await getBlobForAsset(C_with_CAWG_data_thumbnail); | ||
|
|
||
| const testSigner = await createTestSigner(); | ||
|
|
||
| const signedBytes = await builder.sign( | ||
| testSigner, | ||
| 'image/jpeg', | ||
| blob | ||
| ); | ||
|
|
||
| const reader = await c2pa.reader.fromBlob( | ||
| 'image/jpeg', | ||
| new Blob([signedBytes]) | ||
| ); | ||
|
|
||
| const activeManifest = await reader!.activeManifest(); | ||
|
|
||
| expect(activeManifest.claim_generator_info).toMatchObject( | ||
| manifestDefinition.claim_generator_info! | ||
| ); | ||
|
|
||
| expect(activeManifest.signature_info).toMatchObject({ | ||
| alg: 'Ed25519', | ||
| cert_serial_number: | ||
| '638838410810235485828984295321338730070538954823', | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is this coming from? |
||
| common_name: 'C2PA Signer', | ||
| issuer: 'C2PA Test Signing Cert' | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,76 @@ | ||||||
| /** | ||||||
| * Copyright 2026 Adobe | ||||||
| * All Rights Reserved. | ||||||
| * | ||||||
| * NOTICE: Adobe permits you to use, modify, and distribute this file in | ||||||
| * accordance with the terms of the Adobe license agreement accompanying | ||||||
| * it. | ||||||
| */ | ||||||
|
|
||||||
| import type { Signer } from '../src/lib/signer.js'; | ||||||
|
|
||||||
| import ed25519_pub from './trust/ed25519.pub?raw'; | ||||||
| import ed25519_pem from './trust/ed25519.pem?raw'; | ||||||
|
|
||||||
| /** | ||||||
| * Creates a signer suitable for testing from an ed25519 private key / certificate pair. | ||||||
| */ | ||||||
| export async function createTestSigner(): Promise<Signer> { | ||||||
| const certBytes = extractDerBytes(ed25519_pub); | ||||||
|
|
||||||
| const privateKeyBytes = extractDerBytes(ed25519_pem); | ||||||
|
|
||||||
| const privateKey = await crypto.subtle.importKey( | ||||||
| 'pkcs8', | ||||||
| privateKeyBytes, | ||||||
| { | ||||||
| name: 'Ed25519' | ||||||
| }, | ||||||
| false, | ||||||
| ['sign'] | ||||||
| ); | ||||||
|
|
||||||
| return { | ||||||
| async sign(data) { | ||||||
| const signature = await crypto.subtle.sign( | ||||||
| { | ||||||
| name: 'Ed25519' | ||||||
| }, | ||||||
| privateKey, | ||||||
| data | ||||||
| ); | ||||||
|
|
||||||
| return new Uint8Array(signature); | ||||||
| }, | ||||||
| async reserveSize() { | ||||||
| return 1000; | ||||||
| }, | ||||||
| alg: 'ed25519', | ||||||
| certs: [new Uint8Array(certBytes)], | ||||||
| directCoseHandling: false | ||||||
| }; | ||||||
| } | ||||||
|
|
||||||
| function extractDerBytes(str: string): ArrayBuffer { | ||||||
| const pemHeader = '-----BEGIN PRIVATE KEY-----'; | ||||||
| const pemFooter = '-----END PRIVATE KEY-----'; | ||||||
| const pemContents = str.substring( | ||||||
| pemHeader.length, | ||||||
| str.length - pemFooter.length - 1 | ||||||
| ); | ||||||
| // base64 decode the string to get the binary data | ||||||
| const binaryDerString = window.atob(pemContents); | ||||||
| // convert from a binary string to an ArrayBuffer | ||||||
| const binaryDer = str2ab(binaryDerString); | ||||||
|
|
||||||
| return binaryDer; | ||||||
| } | ||||||
|
|
||||||
| function str2ab(str: string): ArrayBuffer { | ||||||
| const buf = new ArrayBuffer(str.length); | ||||||
| const bufView = new Uint8Array(buf); | ||||||
| for (let i = 0, strLen = str.length; i < strLen; i++) { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: can we simplify?
Suggested change
|
||||||
| bufView[i] = str.charCodeAt(i); | ||||||
| } | ||||||
| return buf; | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| -----BEGIN PRIVATE KEY----- | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feels weird to commit the private key, but I guess we don't have many other choices. 🤔 |
||
| MC4CAQAwBQYDK2VwBCIEIL2+9INLPNSLH3STzKQJ3Wen9R6uPbIYOIKA2574YQ4O | ||
| -----END PRIVATE KEY----- | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| -----BEGIN CERTIFICATE----- | ||
| MIICSDCCAfqgAwIBAgIUb+aBTX1CsjJ1iuMJ9kRudz/7qEcwBQYDK2VwMIGMMQsw | ||
| CQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNvbWV3aGVyZTEnMCUG | ||
| A1UECgweQzJQQSBUZXN0IEludGVybWVkaWF0ZSBSb290IENBMRkwFwYDVQQLDBBG | ||
| T1IgVEVTVElOR19PTkxZMRgwFgYDVQQDDA9JbnRlcm1lZGlhdGUgQ0EwHhcNMjIw | ||
| NjEwMTg0NjQxWhcNMzAwODI2MTg0NjQxWjCBgDELMAkGA1UEBhMCVVMxCzAJBgNV | ||
| BAgMAkNBMRIwEAYDVQQHDAlTb21ld2hlcmUxHzAdBgNVBAoMFkMyUEEgVGVzdCBT | ||
| aWduaW5nIENlcnQxGTAXBgNVBAsMEEZPUiBURVNUSU5HX09OTFkxFDASBgNVBAMM | ||
| C0MyUEEgU2lnbmVyMCowBQYDK2VwAyEAMp5+0e83nNgQhdhBW8Rshkjy90sa1A9J | ||
| IzkItcDqCuKjeDB2MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUH | ||
| AwQwDgYDVR0PAQH/BAQDAgbAMB0GA1UdDgQWBBTuLrYRqW4wu6yjIK1/iW8ud7dm | ||
| kTAfBgNVHSMEGDAWgBRXTAfC/JxQvRlk/bCbdPMDbsSfqTAFBgMrZXADQQB2R6vb | ||
| I+X8CTRC54j3NTvsUj454G1/bdzbiHVgl3n+ShOAJ85FJigE7Eoav7SeXeVnNjc8 | ||
| QZ1UrJGwgBBEP84G | ||
| -----END CERTIFICATE----- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉