Skip to content

feat: validate prism proofs for all web requests#1

Open
jns-ps wants to merge 13 commits into
mainfrom
include-prism
Open

feat: validate prism proofs for all web requests#1
jns-ps wants to merge 13 commits into
mainfrom
include-prism

Conversation

@jns-ps

@jns-ps jns-ps commented Jan 9, 2025

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

Based on the comprehensive changes across multiple files, here are the updated release notes:

  • New Features

    • Added Prism client for light node interactions
    • Introduced advanced cryptographic proof validation for Prism and Certificate Transparency logs
    • Enhanced data conversion utilities with new byte and hex manipulation functions
    • Added methods for extracting and validating certificates
  • Improvements

    • Refactored HTTP client with improved request handling
    • Updated Certificate Transparency log client with more robust type definitions
    • Streamlined serialization and hashing utilities
    • Enhanced error handling and logging for certificate validation
  • Security Enhancements

    • Implemented advanced proof validation mechanisms
    • Added SHA-256 hashing utility
    • Improved type safety for cryptographic operations
  • Performance

    • Optimized background processing with extended sleep duration
    • Simplified data conversion and serialization methods

@jns-ps jns-ps self-assigned this Jan 9, 2025
@coderabbitai

coderabbitai Bot commented Jan 9, 2025

Copy link
Copy Markdown

Walkthrough

This pull request introduces significant enhancements to the codebase, focusing on expanding cryptographic functionality, improving type definitions, and introducing new client and utility classes. The changes primarily revolve around Certificate Transparency (CT) log processing, Prism account management, and proof validation. Key additions include a new HttpClient abstract class, PrismClient, enhanced type definitions, and new utility functions for data conversion and hashing.

Changes

File Changes
src/background.ts - Updated imports for certificate handling
- Redefined run_node function with sleep duration
- Added extractCertificates, checkCertValidity, and queryLogSth methods
- Enhanced SCT processing logic
src/byte_arrays.ts - Added concatenateArrays function
- Added areArraysEqual function
src/conversion.ts - Renamed base64 conversion functions
- Added hex and bit conversion utilities
src/ct_log_client.ts - Extended HttpClient
- Updated getSignedTreeHead method signature
- Added CtSignedTreeHeadResponse interface
src/ct_log_store.ts - Added imports for CtLog and CtLogList types
src/ct_log_types.ts - Made interfaces exportable
- Modified CtSignedTreeHead structure
src/ct_parsing.ts - Removed leafHashForPreCert
- Added sthFromBytes function
src/ct_proof_validation.ts - Removed file containing proof validation functions
src/hashing.ts - Added sha256 async function
src/http_client.ts - Introduced abstract HttpClient class
- Added methods for JSON fetching
src/prism_client.ts - Added new PrismClient class
- Implemented getAccount and getCommitment methods
src/prism_serialization.ts - Added serialization functions for Prism data types
src/prism_types.ts - Introduced comprehensive Prism-related type definitions
src/proof_validation.ts - Added validatePrismProof and validateCtProof functions
src/verification_store.ts - Enhanced documentation and removed console log statements

Sequence Diagram

sequenceDiagram
    participant Background as Background Process
    participant PrismClient as Prism Client
    participant ProofValidator as Proof Validator
    participant CTLogClient as CT Log Client

    Background Process->>PrismClient: Request Account
    PrismClient-->>Background Process: Return Account Info
    Background Process->>ProofValidator: Validate Prism Proof
    ProofValidator-->>Background Process: Proof Validation Result
    Background Process->>CTLogClient: Fetch Signed Tree Head
    CTLogClient-->>Background Process: Return Tree Head
Loading

Poem

🐰 Bytes dance, proofs entwine,
Cryptographic rabbits design,
HTTP clients hop with grace,
Prism's secrets we now trace,
Code hops forward, logic divine! 🔒

Finishing Touches

  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (16)
src/http_client.ts (2)

32-32: Include HTTP status code in error messages

When throwing an error due to a failed HTTP request, including the status code in the error message can aid in debugging and provide more context about the failure.

Apply this diff to enhance the error message:

       if (!response.ok) {
-        throw new Error(`Http request failed: ${response.statusText}`);
+        throw new Error(`HTTP request failed with status ${response.status} (${response.statusText})`);
       }

53-53: Include HTTP status code in error messages

Similarly, update the error message in the postJson method for consistency.

Apply this diff:

       if (!response.ok) {
-        throw new Error(`Http request failed: ${response.statusText}`);
+        throw new Error(`HTTP request failed with status ${response.status} (${response.statusText})`);
       }
src/proof_validation.ts (2)

22-24: Correct the function documentation for validatePrismProof

The comment incorrectly states that validatePrismProof validates a Merkle proof from a CT log server. It should specify that it validates a Merkle proof from a Prism node.

Apply this diff to correct the comment:

 /**
- * Validates a Merkle proof from a CT log server
+ * Validates a Merkle proof from a Prism node
  */

34-34: Address the TODO: Calculate Prism leaf hash in the browser extension

There's a TODO comment indicating the need to calculate the Prism leaf hash. Implementing this functionality is essential for the proof validation process.

Do you want me to help implement the leaf hash calculation? I can provide code to complete this task or open a GitHub issue to track it.

src/background.ts (2)

23-25: Remove unused variable i and clean up run_node function

The variable i is incremented but not used in the run_node function. This is unnecessary and may lead to confusion. Consider removing it to clean up the code.

Apply this diff to remove the unused variable:

-  let i = 0;
   while (true) {
     await sleep(60000);
-    i += 1;
     console.log(`Node is still running ...`);
   }

90-98: Use strict equality operator and improve error message formatting

Using !== instead of != ensures strict comparison and prevents unexpected type coercion. Also, using template literals enhances the readability of the error message.

Apply this diff:

-if (accountRes.account.signed_data.length != 1) {
+if (accountRes.account.signed_data.length !== 1) {
   console.error(
-    "Incorrect number of prism entries (",
-    accountRes.account.signed_data.length,
-    ") for Log",
-    b64LogId,
-  );
+    `Incorrect number of prism entries (${accountRes.account.signed_data.length}) for Log ${b64LogId}`
   );
   return;
 }
src/prism_client.ts (3)

14-19: Implement error handling for network requests in getAccount

The getAccount method assumes that the network request will always succeed. To make the code robust, add error handling to catch network errors or unexpected responses.

Apply this diff to include error handling:

   async getAccount(accountId: string): Promise<PrismAccountResponse> {
     const body = {
       id: accountId,
     };
-    return await this.postJson("/get-account", body);
+    try {
+      return await this.postJson("/get-account", body);
+    } catch (error) {
+      console.error(`Error fetching account ${accountId}:`, error);
+      throw error;
+    }
   }

21-23: Implement error handling for network requests in getCommitment

Similar to getAccount, the getCommitment method should handle potential network errors to prevent unhandled exceptions.

Apply this diff to include error handling:

   async getCommitment(): Promise<PrismCommitmentResponse> {
-    return await this.fetchJson("/get-current-commitment");
+    try {
+      return await this.fetchJson("/get-current-commitment");
+    } catch (error) {
+      console.error("Error fetching commitment:", error);
+      throw error;
+    }
   }

4-11: Define comprehensive interfaces for API responses

Ensure that the interfaces PrismAccountResponse and PrismCommitmentResponse accurately represent all possible API responses, including error cases. This will improve type safety and code clarity.

Consider updating the interfaces to include potential error fields:

 interface PrismAccountResponse {
   account?: PrismAccount;
   proof: PrismProof;
+  error?: string;
 }
 
 interface PrismCommitmentResponse {
   commitment: string;
+  error?: string;
 }
src/ct_log_types.ts (1)

38-42: Consider typing the tiled_logs array

The any[] type for tiled_logs reduces type safety. Consider defining a proper interface for tiled logs or document why the type is intentionally loose.

src/byte_arrays.ts (1)

14-25: Consider optimizing array concatenation for large arrays

The current implementation creates a new array and copies data multiple times. For large arrays or frequent operations, consider using a more efficient approach.

 export function concatenateArrays(...arrays: Uint8Array[]): Uint8Array {
+  // Early return for single array or empty input
+  if (arrays.length === 0) return new Uint8Array(0);
+  if (arrays.length === 1) return arrays[0];
+
   const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);
   const result = new Uint8Array(totalLength);
   let offset = 0;

   for (const arr of arrays) {
     result.set(arr, offset);
     offset += arr.length;
   }

   return result;
 }
src/prism_types.ts (1)

61-65: Add RSA algorithm support for broader compatibility

The current PrismCryptoAlgorithm enum only supports elliptic curve algorithms. Consider adding RSA support for broader compatibility with existing systems.

src/prism_serialization.ts (1)

72-74: Add explicit null check for service_challenge

The optional chaining operator is not sufficient for runtime type safety. Add an explicit null check to prevent potential undefined behavior.

   const serviceChallengeBytes = account.service_challenge
-    ? serviceChallengeToBincode(account.service_challenge)
+    ? (account.service_challenge === null
+      ? new Uint8Array([0])
+      : serviceChallengeToBincode(account.service_challenge))
     : new Uint8Array([0]);
src/ct_parsing.ts (1)

Line range hint 44-122: Add JSDoc documentation for the function.

The implementation correctly follows RFC 6962 for pre-certificate entries. Consider adding documentation to explain the function's purpose and return value format.

Add this documentation:

/**
 * Generates the MerkleTreeLeaf bytes for a pre-certificate as specified in RFC 6962.
 * 
 * @param certDer - The DER-encoded pre-certificate
 * @param issuerDer - The DER-encoded issuer certificate
 * @param sct_time - The timestamp for the SCT
 * @param sct_extensions - The SCT extensions
 * @returns A Uint8Array containing the MerkleTreeLeaf bytes with 0x00 prefix
 */
src/conversion.ts (2)

36-70: Consider using TypedArray.from for better performance.

The implementation is correct but could be optimized.

Consider this alternative implementation:

 export function hexToBytes(hexString: string): Uint8Array {
   const normalizedHex = hexString.replace(/^0x/i, "").toUpperCase();
   
   if (!/^[0-9A-F]*$/.test(normalizedHex)) {
     throw new Error("Invalid hexadecimal string");
   }
   
   const paddedHex =
     normalizedHex.length % 2 ? "0" + normalizedHex : normalizedHex;
   
-  const bytes = new Uint8Array(paddedHex.length / 2);
-  
-  for (let i = 0; i < paddedHex.length; i += 2) {
-    bytes[i / 2] = parseInt(paddedHex.substr(i, 2), 16);
-  }
-  
-  return bytes;
+  return Uint8Array.from(paddedHex.match(/.{2}/g) || [], byte => parseInt(byte, 16));
 }

93-115: Consider using a lookup table for better performance.

While the implementation is correct, bit operations in a loop can be slower than using a lookup table.

Consider this optimization:

 export function bytesToBits(uint8Array: Uint8Array): Uint8Array {
+  const BITS_LOOKUP = new Uint8Array(256 * 8);
+  for (let i = 0; i < 256; i++) {
+    for (let j = 0; j < 8; j++) {
+      BITS_LOOKUP[i * 8 + j] = (i >> (7 - j)) & 1;
+    }
+  }
+
   const bits = new Uint8Array(uint8Array.length * 8);
   for (let i = 0; i < uint8Array.length; i++) {
-    const byte = uint8Array[i];
-    for (let j = 0; j < 8; j++) {
-      bits[i * 8 + j] = (byte >> (7 - j)) & 1;
-    }
+    bits.set(BITS_LOOKUP.subarray(uint8Array[i] * 8, uint8Array[i] * 8 + 8), i * 8);
   }
   return bits;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f7d5734 and 9c53f19.

📒 Files selected for processing (14)
  • src/background.ts (3 hunks)
  • src/byte_arrays.ts (1 hunks)
  • src/conversion.ts (2 hunks)
  • src/ct_log_client.ts (1 hunks)
  • src/ct_log_store.ts (1 hunks)
  • src/ct_log_types.ts (2 hunks)
  • src/ct_parsing.ts (4 hunks)
  • src/ct_proof_validation.ts (0 hunks)
  • src/hashing.ts (1 hunks)
  • src/http_client.ts (1 hunks)
  • src/prism_client.ts (1 hunks)
  • src/prism_serialization.ts (1 hunks)
  • src/prism_types.ts (1 hunks)
  • src/proof_validation.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/ct_proof_validation.ts
✅ Files skipped from review due to trivial changes (1)
  • src/ct_log_store.ts
🔇 Additional comments (8)
src/background.ts (2)

86-87: Avoid logging sensitive information

Logging b64LogId may expose sensitive information. Ensure that logs do not contain any sensitive data to adhere to security best practices.

[security]

Apply this diff to modify the log message:

-    console.error("CT Log", b64LogId, "not found in prism");
+    console.error("CT Log not found in prism");

100-100: Address the TODO regarding signed_data.key validation

There's a TODO comment questioning whether signed_data.key needs to be checked. It's important to resolve TODOs before merging to ensure code completeness.

Would you like assistance in implementing the check for signed_data.key or creating a GitHub issue to track this task?

src/hashing.ts (1)

1-3: Ensure crypto.subtle.digest is supported in all target environments

The crypto.subtle.digest method may not be available in all browsers or environments, particularly in older versions or certain browser extension contexts. Verify that this method is supported in all intended environments, or provide a fallback implementation.

Consider adding a feature detection mechanism or polyfill to handle environments where crypto.subtle is not available.

src/ct_log_types.ts (1)

61-66: ⚠️ Potential issue

Verify alignment with RFC 6962 for CtSignedTreeHead changes

The modifications to CtSignedTreeHead interface deviate from the standard Certificate Transparency format defined in RFC 6962. The tree_head_signature is a crucial field for verifying the authenticity of the tree head.

Consider reverting to RFC 6962 compliant field names or document why we're deviating:

 export interface CtSignedTreeHead {
   version: number;
   signatureType: number;
   timestamp: number;
   treeSize: number;
   rootHash: Uint8Array;
+  treeHeadSignature: string; // Required for RFC 6962 compliance
 }
src/ct_parsing.ts (1)

6-13: LGTM! Well-organized imports and constants.

The new imports and constants are properly structured for Certificate Transparency processing.

src/conversion.ts (3)

Line range hint 1-19: LGTM! Well-documented base64 decoder.

The function is properly renamed and documented with clear examples.


21-34: LGTM! Well-documented base64 encoder.

The function is properly renamed and documented with clear examples.


72-91: LGTM! Well-implemented hex encoder.

The function correctly handles all cases with proper padding and optional prefix.

Comment thread src/ct_log_client.ts
}

async getSignedTreeHead(): Promise<CtSignedTreeHead> {
export class CTLogClient extends HttpClient {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add constructor to pass baseUrl to HttpClient

The CTLogClient class extends HttpClient but does not define a constructor to pass the baseUrl to the HttpClient's constructor. Since HttpClient requires a baseUrl parameter, this will lead to a runtime error due to baseUrl being undefined.

Apply this diff to add the constructor:

 export class CTLogClient extends HttpClient {
+  constructor(baseUrl: string | URL) {
+    super(baseUrl);
+  }

   async getSignedTreeHead(): Promise<CtSignedTreeHeadResponse> {
     return await this.fetchJson("/ct/v1/get-sth", {});
   }

Comment thread src/proof_validation.ts Outdated
Comment on lines +43 to +65
console.log("Validating prism proof for", key);
console.log("key:", bytesToHex(keyHash));
console.log("val:", bytesToHex(valueHash));
console.log("leaf:", bytesToHex(leafHash));
console.log("expected:", expectedRootHashHex);

const bits = bytesToBits(keyHash)
.reverse()
.slice(256 - proof.siblings.length);
const sibling_hashes = proof.siblings.map(hexToBytes);

const calculatedRootHash = await calculateRootHash(
leafHash,
bits,
sibling_hashes,
PRISM_INTERNAL_DOMAIN_SEPARATOR,
);

console.log(
"Comparing hashes",
bytesToHex(calculatedRootHash),
bytesToHex(expectedRootHash),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid logging sensitive data in production

The console.log statements are outputting potentially sensitive information such as hashes and keys used in proof validation. Logging such data can lead to security risks.

Consider removing these log statements or ensuring they are only included in a development or debug mode.

Apply this diff to remove or conditionally include the logs:

         console.log("Validating prism proof for", key);
         console.log("key:", bytesToHex(keyHash));
         console.log("val:", bytesToHex(valueHash));
         console.log("leaf:", bytesToHex(leafHash));
         console.log("expected:", expectedRootHashHex);
+        // Remove or guard these logs with a debug flag if necessary.

...

         console.log(
           "Comparing hashes",
           bytesToHex(calculatedRootHash),
           bytesToHex(expectedRootHash),
         );
+        // Remove or guard these logs with a debug flag if necessary.

Also applies to: 95-101

Comment thread src/background.ts Outdated
Comment on lines +83 to +85
accountRes.account === undefined ||
accountRes.proof.leaf === undefined
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Safely access nested properties to prevent runtime errors

When checking accountRes.proof.leaf, if accountRes.proof is undefined, accessing accountRes.proof.leaf will cause a runtime error. Use optional chaining (?.) to safely access nested properties.

Apply this diff to prevent potential runtime errors:

 if (
   accountRes.account === undefined ||
-  accountRes.proof.leaf === undefined
+  accountRes.proof?.leaf === undefined
 ) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
accountRes.account === undefined ||
accountRes.proof.leaf === undefined
) {
accountRes.account === undefined ||
accountRes.proof?.leaf === undefined
) {

Comment thread src/background.ts Outdated

run_node();

const prism = new PrismClient("http://127.0.0.1:50524");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid hardcoding the Prism client URL

Hardcoding the URL "http://127.0.0.1:50524" reduces flexibility and makes it difficult to configure the application for different environments. Consider externalizing this URL using a configuration file, environment variable, or browser storage.

Here's a suggestion to use an environment variable:

-const prism = new PrismClient("http://127.0.0.1:50524");
+const PRISM_CLIENT_URL = process.env.PRISM_CLIENT_URL || "http://127.0.0.1:50524";
+const prism = new PrismClient(PRISM_CLIENT_URL);

Alternatively, you could use browser extension storage or a configuration object.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const prism = new PrismClient("http://127.0.0.1:50524");
const PRISM_CLIENT_URL = process.env.PRISM_CLIENT_URL || "http://127.0.0.1:50524";
const prism = new PrismClient(PRISM_CLIENT_URL);

Comment thread src/background.ts Outdated
Comment on lines +117 to +124
if (!isPrismProofValid) {
await domainVerificationStore.reportLogVerification(
domain,
log.description,
isPrismProofValid,
);
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Continue processing SCTs after failed proof validation

Currently, if isPrismProofValid is false, the function returns early, which stops the processing of remaining SCTs. Consider using continue instead of return to ensure that all SCTs are processed.

Apply this diff to continue processing:

         if (!isPrismProofValid) {
             await domainVerificationStore.reportLogVerification(
                 domain,
                 log.description,
                 isPrismProofValid,
             );
-            return;
+            continue;
         }

Committable suggestion skipped: line range outside the PR's diff.

Comment thread src/byte_arrays.ts
Comment on lines +40 to +45
export function areArraysEqual(arr1: Uint8Array, arr2: Uint8Array): boolean {
if (arr1.length !== arr2.length) {
return false;
}
return arr1.every((value, index) => value === arr2[index]);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Consider using constant-time comparison for cryptographic operations

The current array comparison using every might be vulnerable to timing attacks if used for comparing sensitive cryptographic data.

 export function areArraysEqual(arr1: Uint8Array, arr2: Uint8Array): boolean {
   if (arr1.length !== arr2.length) {
     return false;
   }
-  return arr1.every((value, index) => value === arr2[index]);
+  // Constant-time comparison
+  let result = 0;
+  for (let i = 0; i < arr1.length; i++) {
+    result |= arr1[i] ^ arr2[i];
+  }
+  return result === 0;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function areArraysEqual(arr1: Uint8Array, arr2: Uint8Array): boolean {
if (arr1.length !== arr2.length) {
return false;
}
return arr1.every((value, index) => value === arr2[index]);
}
export function areArraysEqual(arr1: Uint8Array, arr2: Uint8Array): boolean {
if (arr1.length !== arr2.length) {
return false;
}
// Constant-time comparison
let result = 0;
for (let i = 0; i < arr1.length; i++) {
result |= arr1[i] ^ arr2[i];
}
return result === 0;
}

Comment thread src/prism_types.ts
Comment on lines +16 to +18
key: PrismCryptoPayload;
data: string; //base64 encoded bytes
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider using Uint8Array for cryptographic data instead of base64 strings

Storing cryptographic data as base64 strings in memory increases the attack surface. Consider using Uint8Array directly and only converting to base64 when necessary for transport.

Comment on lines +11 to +16
export function num64ToBincodeLe(num: number): Uint8Array {
const buffer = new ArrayBuffer(8); // 8 bytes for 64-bit integer
const view = new DataView(buffer);
view.setBigUint64(0, BigInt(num), true);
return new Uint8Array(buffer);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add bounds checking for num64ToBincodeLe

The function should validate that the input number is within safe bounds for BigInt conversion to prevent potential overflow issues.

 export function num64ToBincodeLe(num: number): Uint8Array {
+  if (!Number.isSafeInteger(num) || num < 0) {
+    throw new Error(`Invalid number for 64-bit conversion: ${num}`);
+  }
   const buffer = new ArrayBuffer(8);
   const view = new DataView(buffer);
   view.setBigUint64(0, BigInt(num), true);
   return new Uint8Array(buffer);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function num64ToBincodeLe(num: number): Uint8Array {
const buffer = new ArrayBuffer(8); // 8 bytes for 64-bit integer
const view = new DataView(buffer);
view.setBigUint64(0, BigInt(num), true);
return new Uint8Array(buffer);
}
export function num64ToBincodeLe(num: number): Uint8Array {
if (!Number.isSafeInteger(num) || num < 0) {
throw new Error(`Invalid number for 64-bit conversion: ${num}`);
}
const buffer = new ArrayBuffer(8); // 8 bytes for 64-bit integer
const view = new DataView(buffer);
view.setBigUint64(0, BigInt(num), true);
return new Uint8Array(buffer);
}

Comment thread src/ct_parsing.ts
Comment on lines +27 to +54
export function sthFromBytes(bytes: Uint8Array): CtSignedTreeHead {
const view = new DataView(bytes.buffer, 0, bytes.length);
const version = view.getUint8(0);
const signatureType = view.getUint8(1);
const timestamp = Number(view.getBigUint64(2));
const treeSize = Number(view.getBigUint64(10));
const rootHash = bytes.slice(18, 50);

return {
version,
signatureType,
timestamp,
treeSize,
rootHash,
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add bounds checking for input array.

The function correctly parses STH bytes, but should validate the input length to prevent buffer overruns.

Add this validation at the start of the function:

 export function sthFromBytes(bytes: Uint8Array): CtSignedTreeHead {
+  if (bytes.length < 50) {
+    throw new Error('Invalid STH bytes: insufficient length');
+  }
   const view = new DataView(bytes.buffer, 0, bytes.length);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function sthFromBytes(bytes: Uint8Array): CtSignedTreeHead {
const view = new DataView(bytes.buffer, 0, bytes.length);
const version = view.getUint8(0);
const signatureType = view.getUint8(1);
const timestamp = Number(view.getBigUint64(2));
const treeSize = Number(view.getBigUint64(10));
const rootHash = bytes.slice(18, 50);
return {
version,
signatureType,
timestamp,
treeSize,
rootHash,
};
}
export function sthFromBytes(bytes: Uint8Array): CtSignedTreeHead {
if (bytes.length < 50) {
throw new Error('Invalid STH bytes: insufficient length');
}
const view = new DataView(bytes.buffer, 0, bytes.length);
const version = view.getUint8(0);
const signatureType = view.getUint8(1);
const timestamp = Number(view.getBigUint64(2));
const treeSize = Number(view.getBigUint64(10));
const rootHash = bytes.slice(18, 50);
return {
version,
signatureType,
timestamp,
treeSize,
rootHash,
};
}

Comment thread src/conversion.ts
Comment on lines +117 to +135
/**
* Converts a number to its binary representation as an array of bits.
* The number is treated as a 64-bit value and converted to 64 bits.
*
* @param num - The number to convert to bits
* @returns A Uint8Array where each element is a single bit (0 or 1)
* @example
* ```typescript
* const bits = numberToBits(5);
* // bits represents the 64-bit binary form of 5
* ```
*/
export function numberToBits(num: number): Uint8Array {
const bytes = new Uint8Array(8);
for (let i = 0; i < 8; i++) {
bytes[i] = (num >> (56 - i * 8)) & 0xff;
}
return bytesToBits(bytes);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add input validation for number range.

The function should validate that the input number is within the safe integer range.

Add this validation:

 export function numberToBits(num: number): Uint8Array {
+  if (!Number.isSafeInteger(num) || num < 0) {
+    throw new Error('Input must be a non-negative safe integer');
+  }
   const bytes = new Uint8Array(8);
   for (let i = 0; i < 8; i++) {
     bytes[i] = (num >> (56 - i * 8)) & 0xff;
   }
   return bytesToBits(bytes);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Converts a number to its binary representation as an array of bits.
* The number is treated as a 64-bit value and converted to 64 bits.
*
* @param num - The number to convert to bits
* @returns A Uint8Array where each element is a single bit (0 or 1)
* @example
* ```typescript
* const bits = numberToBits(5);
* // bits represents the 64-bit binary form of 5
* ```
*/
export function numberToBits(num: number): Uint8Array {
const bytes = new Uint8Array(8);
for (let i = 0; i < 8; i++) {
bytes[i] = (num >> (56 - i * 8)) & 0xff;
}
return bytesToBits(bytes);
}
/**
* Converts a number to its binary representation as an array of bits.
* The number is treated as a 64-bit value and converted to 64 bits.
*
* @param num - The number to convert to bits
* @returns A Uint8Array where each element is a single bit (0 or 1)
* @example

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (6)
src/proof_validation.ts (2)

87-88: 🛠️ Refactor suggestion

Avoid logging sensitive error information

Logging the error object may expose sensitive information. Consider logging error messages without including the error details or ensure that sensitive data is not exposed when logging.

Apply this diff to adjust the logging:

-        console.error("Error validating Merkle proof:", error);
+        console.error("Error validating Merkle proof.");

122-123: 🛠️ Refactor suggestion

Avoid logging sensitive error information

Logging the error object may expose sensitive information. Consider logging error messages without including the error details or ensure that sensitive data is not exposed when logging.

Apply this diff to adjust the logging:

-        console.error("Error validating Merkle proof:", error);
+        console.error("Error validating Merkle proof.");
src/ct_log_client.ts (1)

19-19: ⚠️ Potential issue

Add constructor to pass baseUrl to HttpClient

The CTLogClient class extends HttpClient but does not define a constructor to pass the baseUrl to the HttpClient's constructor. Since HttpClient requires a baseUrl parameter, this will lead to a runtime error due to baseUrl being undefined.

src/ct_parsing.ts (1)

39-54: ⚠️ Potential issue

Add bounds checking for input array.

The function correctly parses STH bytes, but should validate the input length to prevent buffer overruns.

src/background.ts (2)

105-105: 🛠️ Refactor suggestion

Avoid hardcoding the Prism client URL

Hardcoding the URL reduces flexibility and makes it difficult to configure the application for different environments.


175-175: ⚠️ Potential issue

Use optional chaining for nested properties

When checking accountRes.proof.leaf, use optional chaining to prevent potential runtime errors.

🧹 Nitpick comments (5)
src/proof_validation.ts (1)

51-90: Consider providing more detailed error handling

While the current implementation catches errors during validation, providing more specific error messages or introducing custom error types can improve debuggability without exposing sensitive information.

src/prism_client.ts (1)

25-38: Consider adding request body type.

The implementation looks good, but consider adding a type for the request body to improve type safety.

+interface GetAccountRequest {
+  id: string;
+}

 async getAccount(accountId: string): Promise<PrismAccountResponse> {
-  const body = {
+  const body: GetAccountRequest = {
     id: accountId,
   };
   return await this.postJson("/get-account", body);
 }
src/ct_parsing.ts (1)

Line range hint 67-145: Consider breaking down the complex byte manipulation.

The function is well-documented but contains complex byte manipulation logic. Consider extracting the byte manipulation into smaller, focused helper functions for better maintainability.

For example:

function writeTimestamp(view: DataView, offset: number, timestamp: BigInt): number {
  view.setBigUint64(offset, timestamp);
  return offset + 8;
}

function writeIssuerKeyHash(bytes: Uint8Array, offset: number, hash: Uint8Array): number {
  bytes.set(hash, offset);
  return offset + hash.length;
}
src/background.ts (2)

187-187: Track TODO for light node integration.

The comment indicates a pending task to query commitment from light node instead of REST.

Would you like me to create a GitHub issue to track this TODO item?


121-128: Improve node simulation logging.

The current implementation only logs a basic message. Consider adding more informative logging:

-    console.log(`Node is still running ...`);
+    console.log(`Prism light node running (iteration ${i}). Waiting for implementation...`);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c53f19 and 62e2bde.

📒 Files selected for processing (10)
  • src/background.ts (3 hunks)
  • src/ct_log_client.ts (2 hunks)
  • src/ct_log_store.ts (1 hunks)
  • src/ct_parsing.ts (4 hunks)
  • src/ct_proof_validation.ts (0 hunks)
  • src/hashing.ts (1 hunks)
  • src/http_client.ts (1 hunks)
  • src/prism_client.ts (1 hunks)
  • src/proof_validation.ts (1 hunks)
  • src/verification_store.ts (3 hunks)
💤 Files with no reviewable changes (1)
  • src/ct_proof_validation.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/ct_log_store.ts
  • src/http_client.ts
🧰 Additional context used
🪛 Biome (1.9.4)
src/verification_store.ts

[error] 40-40: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)

🔇 Additional comments (10)
src/verification_store.ts (1)

63-63: Ensure proper promise handling in asynchronous operations

The reportLogVerification method returns a Promise<void>, but the operation provided to enqueue does not explicitly return a value. Consider ensuring that any asynchronous operations within the method are properly handled to prevent unhandled promise rejections.

src/hashing.ts (1)

1-19: Well-implemented SHA-256 hashing function

The sha256 function is correctly implemented using the Web Crypto API. The use of async/await enhances readability, and the detailed JSDoc comments improve maintainability.

src/prism_client.ts (3)

8-11: LGTM! Well-structured response type.

The interface is well-defined with appropriate optional/required fields.


17-19: LGTM! Clear and focused interface.

The interface is simple and well-documented.


46-48: LGTM! Simple and focused implementation.

The method is well-documented and handles the endpoint correctly.

src/ct_log_client.ts (4)

8-13: LGTM! Well-structured response type.

The interface includes all necessary fields for a Signed Tree Head response.


25-25: LGTM! Simple and focused implementation.

The method correctly fetches the Signed Tree Head.


Line range hint 29-46: LGTM! Well-documented and type-safe implementation.

The method handles parameters correctly and includes comprehensive documentation.


Line range hint 47-62: LGTM! Clear implementation with good error documentation.

The method is well-documented and handles parameters appropriately.

src/background.ts (1)

84-85: 🛠️ Refactor suggestion

Consider continuing validation after error.

The early return after an error prevents validation of remaining SCTs. Consider using continue instead to process all SCTs.

-        return;
+        continue;

Likely invalid or redundant comment.

Comment thread src/verification_store.ts
* @returns Promise resolving to the operation result
*/
private enqueue<T>(operation: () => Promise<T> | T): Promise<T> {
return (this.queue = this.queue.then(() => operation()));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid assignment within return statement

Assigning this.queue within the return statement can be confusing and may reduce code readability. It's better to separate the assignment from the return statement for clarity.

Apply this diff to separate the assignment:

 private enqueue<T>(operation: () => Promise<T> | T): Promise<T> {
-  return (this.queue = this.queue.then(() => operation()));
+  this.queue = this.queue.then(() => operation());
+  return this.queue;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (this.queue = this.queue.then(() => operation()));
private enqueue<T>(operation: () => Promise<T> | T): Promise<T> {
this.queue = this.queue.then(() => operation());
return this.queue;
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 40-40: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant