Skip to content

Conversation

@edgarsj
Copy link
Owner

@edgarsj edgarsj commented Jan 3, 2026

Summary

  • Add granular validation status (VALID/INVALID/INDETERMINATE/UNSUPPORTED) to verification results
  • Fix C14N 1.1 canonicalization bug that broke signatures with compact XML
  • Add Safari/WebKit and Firefox to browser test suite

Changes

Multi-state validation

VerificationResult now includes:

  • status: "VALID" | "INVALID" | "INDETERMINATE" | "UNSUPPORTED"
  • statusMessage: Human-readable explanation
  • limitations: Platform constraints (e.g., RSA >4096 in Safari)

This enables better UX - apps can show "cannot verify in this browser" instead of "invalid signature" for platform limitations.

C14N 1.1 bug fix

Fixed canonicalization incorrectly adding newlines to compact XML. This caused signature verification to fail for files signed with tools that produce compact XML.

Cross-browser testing

Added Playwright for Safari/WebKit and Firefox testing. All 16 sample files now validate across Chrome, Safari, and Firefox.

@greptile-apps
Copy link

greptile-apps bot commented Jan 3, 2026

Greptile Summary

This PR introduces two major improvements to signature verification: multi-state validation status and a critical C14N 1.1 canonicalization bug fix.

Multi-State Validation

  • Replaced boolean isValid with granular status field: VALID, INVALID, INDETERMINATE, and UNSUPPORTED
  • Added platform limitation detection for RSA keys >4096 bits in Safari/WebKit, returning UNSUPPORTED instead of failing as INVALID
  • Implemented getRSAModulusLength() to parse RSA key size from SPKI format via ASN.1 DER structure
  • Added ValidationLimitation interface with machine-readable codes and human-readable descriptions
  • Enhanced RSA key import logic to try original key first, then fallback to padding fix (handles browser differences)

C14N 1.1 Canonicalization Fix

  • Fixed critical bug where C14N 1.1 was incorrectly adding newlines to compact XML elements
  • C14N 1.1 spec difference from C14N 1.0 is xml:id normalization, NOT whitespace formatting
  • Changed c14n11 method implementation from conditional newline injection to preserving original whitespace
  • This fixes signature verification failures for files signed with Microsoft Office 365/Azure and other tools producing compact XML

Cross-Browser Testing

  • Added Playwright for Safari/WebKit and Firefox testing via @web/test-runner-playwright
  • All 16 sample files now validate across Chrome, Safari, and Firefox
  • Test suite updated to accept INDETERMINATE and UNSUPPORTED statuses as non-failing states

Test Coverage

  • Updated canonicalization tests to reflect whitespace preservation behavior
  • Added new test case for preserving existing newlines vs compact XML
  • Modified sample validation tests to handle granular status properly

The implementation is well-structured with comprehensive test coverage and thorough documentation updates.

Confidence Score: 5/5

  • This PR is safe to merge with high confidence - fixes critical bug and adds useful validation granularity
  • The changes are well-implemented with comprehensive test coverage. The C14N 1.1 fix resolves a real bug affecting Microsoft Office signatures. The multi-state validation adds valuable UX improvements without breaking backward compatibility (isValid field preserved). Cross-browser testing validates the RSA key size detection works correctly across platforms.
  • No files require special attention

Important Files Changed

Filename Overview
src/core/verification.ts Added granular validation status (VALID/INVALID/INDETERMINATE/UNSUPPORTED) with RSA key size detection for Safari/WebKit platform limitations
src/core/canonicalization/XMLCanonicalizer.ts Fixed C14N 1.1 to preserve original whitespace instead of adding newlines, resolving compact XML signature verification failures
tests-browser/canonicalizer.spec.ts Updated tests to reflect C14N 1.1 whitespace preservation behavior and added test for newline preservation
tests-browser/validatesamples.spec.ts Updated validation logic to accept INDETERMINATE and UNSUPPORTED statuses, only failing on INVALID

Sequence Diagram

sequenceDiagram
    participant User
    participant verifySignature
    participant verifySignedInfo
    participant getRSAModulusLength
    participant isRSAKeySizeSupported
    participant CryptoSubtle
    
    User->>verifySignature: Call with signature & files
    verifySignature->>verifySignedInfo: Verify XML signature
    verifySignedInfo->>getRSAModulusLength: Extract modulus length from key
    getRSAModulusLength-->>verifySignedInfo: Return bits (e.g., 8192)
    verifySignedInfo->>isRSAKeySizeSupported: Check platform support
    isRSAKeySizeSupported-->>verifySignedInfo: false (Safari >4096)
    verifySignedInfo-->>verifySignature: unsupportedPlatform: true
    verifySignature->>verifySignature: Set status = "UNSUPPORTED"
    verifySignature->>verifySignature: Add limitation with code & platform
    verifySignature-->>User: VerificationResult with UNSUPPORTED status
    
    Note over User,CryptoSubtle: C14N 1.1 Fix Flow
    User->>verifySignature: Verify signature with compact XML
    verifySignature->>verifySignedInfo: Canonicalize SignedInfo
    verifySignedInfo->>XMLCanonicalizer: canonicalize(node, "c14n11")
    Note over XMLCanonicalizer: Preserves original whitespace<br/>Does NOT add newlines
    XMLCanonicalizer-->>verifySignedInfo: Compact XML unchanged
    verifySignedInfo->>CryptoSubtle: verify(signature, canonicalized data)
    CryptoSubtle-->>verifySignedInfo: true
    verifySignedInfo-->>verifySignature: isValid: true
    verifySignature->>verifySignature: Set status = "VALID"
    verifySignature-->>User: VerificationResult with VALID status
Loading

@edgarsj edgarsj merged commit 6fce9b9 into main Jan 4, 2026
3 checks passed
@edgarsj edgarsj deleted the granularity branch January 4, 2026 12:47
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.

2 participants