Skip to content

Commit

Permalink
add jsdoc
Browse files Browse the repository at this point in the history
  • Loading branch information
karlobencic committed Jan 7, 2025
1 parent c6f9763 commit 793ae1c
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 15 deletions.
29 changes: 29 additions & 0 deletions src/cose/Signature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,21 @@ export class Signature {
public paddingLength = 0;

private validatedTimestamp: Date | undefined;
/**
* Gets the validated timestamp or falls back to unverified timestamp
* @returns Date object representing the timestamp, or undefined if no timestamp exists
*/
public get timestamp() {
return this.validatedTimestamp ?? this.getTimestampWithoutVerification();
}

/**
* Reads a signature from JUMBF data
* @param content - The JUMBF content to parse
* @returns A new Signature instance
* @throws MalformedContentError if content is malformed
* @throws ValidationError if algorithm is unsupported
*/
public static readFromJUMBFData(content: unknown) {
const signature = new Signature();
const rawContent = content as CoseSignature;
Expand Down Expand Up @@ -99,6 +110,11 @@ export class Signature {
return signature;
}

/**
* Writes the signature data to JUMBF format
* @returns CoseSignature array containing the signature data
* @throws Error if certificate or algorithm is missing
*/
public writeJUMBFData(): CoseSignature {
if (!this.certificate) throw new Error('Signature is missing certificate');
if (!this.algorithm) throw new Error('Signature is missing algorithm');
Expand Down Expand Up @@ -131,6 +147,13 @@ export class Signature {
];
}

/**
* Signs the provided payload and optionally adds a timestamp
* @param privateKey - Private key in PKCS#8 format
* @param payload - Data to be signed
* @param timestampProvider - Optional provider for RFC3161 timestamp
* @throws Error if protected bucket, algorithm or certificate is missing
*/
public async sign(
privateKey: Uint8Array,
payload: Uint8Array,
Expand Down Expand Up @@ -290,6 +313,12 @@ export class Signature {
return undefined;
}

/**
* Validates the signature against a payload
* @param payload - The payload to validate against
* @param sourceBox - Optional JUMBF box for error context
* @returns Promise resolving to ValidationResult
*/
public async validate(payload: Uint8Array, sourceBox?: JUMBF.IBox): Promise<ValidationResult> {
if (!this.certificate || !this.rawProtectedBucket || !this.signature || !this.algorithm) {
return ValidationResult.error(ValidationStatusCode.SigningCredentialInvalid, sourceBox);
Expand Down
47 changes: 47 additions & 0 deletions src/manifest/AssertionStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ export class AssertionStore implements ManifestComponent {
public assertions: Assertion[] = [];
public sourceBox: JUMBF.SuperBox | undefined;

/**
* Reads an assertion store from a JUMBF box
* @param box - The JUMBF box to read from
* @param claim - The claim this assertion store belongs to
* @returns A new AssertionStore instance
* @throws ValidationError if the box is invalid
*/
public static read(box: JUMBF.SuperBox, claim: Claim): AssertionStore {
const assertionStore = new AssertionStore();
assertionStore.sourceBox = box;
Expand All @@ -40,6 +47,13 @@ export class AssertionStore implements ManifestComponent {
return assertionStore;
}

/**
* Reads an assertion from a JUMBF box
* @param box - The JUMBF box to read from
* @param claim - The claim this assertion belongs to
* @returns The created Assertion instance
* @throws ValidationError if the box is invalid
*/
private static readAssertion(box: JUMBF.IBox, claim: Claim): Assertion {
if (!(box instanceof JUMBF.SuperBox))
throw new ValidationError(ValidationStatusCode.AssertionMissing, box, 'Assertion is not a SuperBox');
Expand Down Expand Up @@ -82,6 +96,11 @@ export class AssertionStore implements ManifestComponent {
return assertion;
}

/**
* Generates a JUMBF box containing the assertion store
* @param claim - The claim this assertion store belongs to
* @returns The generated JUMBF box
*/
public generateJUMBFBox(claim: Claim): JUMBF.SuperBox {
const box = new JUMBF.SuperBox();
box.descriptionBox = new JUMBF.DescriptionBox();
Expand All @@ -93,20 +112,37 @@ export class AssertionStore implements ManifestComponent {
return box;
}

/**
* Gets all hard binding assertions from the store
* @returns Array of assertions that are considered hard bindings
*/
public getHardBindings() {
return this.assertions.filter(
assertion => assertion.label && AssertionLabels.hardBindings.includes(assertion.label),
);
}

/**
* Gets assertions by their label
* @param label - The label to filter by
* @returns Array of assertions matching the label
*/
public getAssertionsByLabel(label: string) {
return this.assertions.filter(assertion => assertion.label === label);
}

/**
* Gets all action assertions from the store
* @returns Array of ActionAssertion objects
*/
public getActionAssertions() {
return this.assertions.filter(assertion => assertion instanceof ActionAssertion);
}

/**
* Gets all thumbnail assertions from the store
* @returns Array of thumbnail assertions (both claim and ingredient thumbnails)
*/
public getThumbnailAssertions() {
return this.assertions.filter(
assertion =>
Expand All @@ -115,11 +151,22 @@ export class AssertionStore implements ManifestComponent {
);
}

/**
* Gets the bytes representation of the assertion store
* @param claim - The claim this assertion store belongs to
* @param rebuild - Whether to rebuild the JUMBF box before getting bytes
* @returns Uint8Array of bytes or undefined if no source box exists
*/
public getBytes(claim: Claim, rebuild = false) {
if (rebuild) this.generateJUMBFBox(claim);
return this.sourceBox?.toBuffer();
}

/**
* Gets ingredient assertions filtered by relationship type
* @param relationship - The relationship type to filter by
* @returns Array of IngredientAssertion objects matching the relationship
*/
public getIngredientsByRelationship(relationship: RelationshipType): IngredientAssertion[] {
return this.assertions.filter(
(a): a is IngredientAssertion => a instanceof IngredientAssertion && a.relationship === relationship,
Expand Down
56 changes: 56 additions & 0 deletions src/manifest/Claim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,37 @@ export class Claim implements ManifestComponent {
public claimGeneratorInfo?: string;
public versionReason?: string;

/**
* Gets the version of the claim
* @returns The claim version
*/
public get version(): ClaimVersion {
return this._version;
}

/**
* Sets the version of the claim and updates the label accordingly
* @param value - The new claim version
*/
public set version(value: ClaimVersion) {
this._version = value;
this._label = this.version === ClaimVersion.V2 ? 'c2pa.claim.v2' : 'c2pa.claim';
}

/**
* Gets the label for this claim
* @returns The claim label string
*/
public get label(): string {
return this._label;
}

/**
* Reads a claim from a JUMBF box
* @param box - The JUMBF box to read from
* @returns A new Claim instance
* @throws ValidationError if the box is invalid or has unsupported algorithm
*/
public static read(box: JUMBF.SuperBox) {
if (!box.contentBoxes.length || !(box.contentBoxes[0] instanceof JUMBF.CBORBox))
throw new ValidationError(ValidationStatusCode.ClaimCBORInvalid, box, 'Claim has invalid content box');
Expand Down Expand Up @@ -94,6 +112,11 @@ export class Claim implements ManifestComponent {
return claim;
}

/**
* Generates a JUMBF box containing the claim
* @returns The generated JUMBF box
* @throws Error if required fields are missing
*/
public generateJUMBFBox(): JUMBF.SuperBox {
if (!this.instanceID) throw new Error('Claim: missing instanceID');
if (!this.signatureRef) throw new Error('Claim: missing signature');
Expand Down Expand Up @@ -154,6 +177,11 @@ export class Claim implements ManifestComponent {
return this.sourceBox;
}

/**
* Maps a raw hash algorithm string to internal HashAlgorithm type
* @param alg - The raw hash algorithm string
* @returns The mapped HashAlgorithm or undefined if not supported
*/
public static mapHashAlgorithm(alg: raw.HashAlgorithm | undefined): HashAlgorithm | undefined {
switch (alg) {
case 'sha256':
Expand All @@ -167,6 +195,11 @@ export class Claim implements ManifestComponent {
}
}

/**
* Maps internal HashAlgorithm type to raw hash algorithm string
* @param alg - The HashAlgorithm to map
* @returns The raw hash algorithm string or undefined if not supported
*/
public static reverseMapHashAlgorithm(alg: HashAlgorithm | undefined): raw.HashAlgorithm | undefined {
switch (alg) {
case 'SHA-256':
Expand All @@ -180,6 +213,12 @@ export class Claim implements ManifestComponent {
}
}

/**
* Maps a raw HashedURI to internal HashedURI type
* @param hashedURI - The raw HashedURI to map
* @returns The mapped HashedURI
* @throws ValidationError if algorithm is unsupported
*/
public mapHashedURI(hashedURI: raw.HashedURI): HashedURI {
const algorithm = Claim.mapHashAlgorithm(hashedURI.alg) ?? this.defaultAlgorithm;
if (!algorithm) throw new ValidationError(ValidationStatusCode.AlgorithmUnsupported, hashedURI.url);
Expand All @@ -191,6 +230,11 @@ export class Claim implements ManifestComponent {
};
}

/**
* Maps internal HashedURI type to raw HashedURI
* @param hashedURI - The HashedURI to map
* @returns The raw HashedURI
*/
public reverseMapHashedURI(hashedURI: HashedURI): raw.HashedURI {
if (hashedURI.algorithm === this.defaultAlgorithm) {
// don't store the algorithm redundantly if it's the default
Expand All @@ -207,11 +251,23 @@ export class Claim implements ManifestComponent {
}
}

/**
* Gets the bytes representation of the claim
* @param claim - The claim to get bytes for
* @param rebuild - Whether to rebuild the JUMBF box before getting bytes
* @returns Uint8Array of bytes or undefined if no source box exists
*/
public getBytes(claim: Claim, rebuild = false): Uint8Array | undefined {
if (rebuild) this.generateJUMBFBox();
return (this.sourceBox?.contentBoxes[0] as JUMBF.CBORBox | undefined)?.rawContent;
}

/**
* Generates a URN for the claim based on its version
* For v1: urn:uuid:{uuid}
* For v2: urn:c2pa:{uuid}[:{generatorInfo}][:{versionReason}]
* @returns The generated URN string
*/
public getURN(): string {
const uuid = uuidv4({ random: Crypto.getRandomValues(16) });

Expand Down
Loading

0 comments on commit 793ae1c

Please sign in to comment.