Skip to main content

Electronic Signature Controls Specification

CODITECT Biosciences Quality Management System Platform


Document Control

Metadata

FieldValue
Document IDCODITECT-BIO-ESIG-001
Version1.0.0
StatusActive
Effective Date2026-02-16
ClassificationInternal - Restricted
OwnerChief Information Security Officer (CISO)
Review CycleAnnual (next review: 2027-02-16)
Related DocumentsE-Signature Architecture (BIO-QMS-ARCH-002)
Certificate Chain Architecture (D.1.3)
Cryptographic Standards Policy (D.1.1)
HSM Integration Architecture (D.1.2)

Approval History

RoleNameSignatureDate
Chief Information Security Officer[Pending][Digital Signature]YYYY-MM-DD
VP Quality Assurance[Pending][Digital Signature]YYYY-MM-DD
VP Engineering[Pending][Digital Signature]YYYY-MM-DD
Regulatory Affairs Director[Pending][Digital Signature]YYYY-MM-DD
Chief Technology Officer[Pending][Digital Signature]YYYY-MM-DD

Revision History

VersionDateAuthorChangesApproval Status
1.0.02026-02-16CISO OfficeInitial releaseDraft

Distribution List

  • Executive Leadership Team
  • Quality Assurance Team
  • Compliance and Regulatory Affairs
  • Engineering Leadership
  • Security Team
  • Internal Audit

1. Executive Summary

1.1 Purpose

This Electronic Signature Controls Specification establishes comprehensive technical and procedural controls for electronic signatures within the CODITECT Biosciences Quality Management System (BIO-QMS) platform. The specification ensures full compliance with FDA 21 CFR Part 11 Subpart C requirements for electronic signature implementation in regulated environments.

Regulatory Scope:

  • FDA 21 CFR Part 11 Subpart C - Electronic Signatures (§11.100, §11.200, §11.300)
  • FDA 21 CFR Part 11 §11.50 - Signature Manifestations
  • FDA 21 CFR Part 11 §11.70 - Signature/Record Linking
  • FDA Guidance - Use of Electronic Records and Electronic Signatures in Clinical Investigations (2017)
  • EU eIDAS Regulation - Electronic Identification and Trust Services (international interoperability)

1.2 Scope

This specification covers:

In Scope:

  • Electronic signature architecture and cryptographic binding mechanisms
  • Multi-factor authentication requirements per §11.200
  • Signature-record binding per §11.70
  • Signature meaning definitions and recording per §11.50
  • Sequential signature workflows and approval chains
  • Signature manifestation display requirements per §11.300
  • Identity verification per §11.100
  • Signature revocation and invalidation procedures
  • Audit trail and compliance reporting

Out of Scope:

  • General system authentication (covered by Security Architecture)
  • Data encryption at rest (covered by Cryptographic Standards Policy)
  • Network security controls (covered by Network Security Policy)
  • Physical security controls (covered by Physical Security Policy)

1.3 Key Compliance Benefits

Regulatory RequirementImplementationBenefit
§11.70 Signature/Record LinkingECDSA P-256 cryptographic binding to document hashTamper-proof signature binding
§11.100 General RequirementsUnique certificates per user via 3-tier PKINon-repudiation and identity assurance
§11.200 Signature ComponentsTwo-factor re-authentication (password + TOTP/biometric)Strong identity verification at signing
§11.50 Signature ManifestationsSigner name, timestamp, meaning displayed on all recordsRegulatory transparency
§11.300 Controls for Identification Codes/PasswordsMFA enforcement, session timeout, password complexityAccess control assurance

2. Signature Architecture Overview

2.1 Integration with PKI Certificate Chain (D.1.3)

The BIO-QMS electronic signature system is built on a three-tier Public Key Infrastructure (PKI) certificate hierarchy:

CODITECT Root CA (Offline, 20-year, ECDSA P-256)

├── Intermediate CA: Organization A (Online, 5-year, HSM-backed)
│ ├── User Signing Certificate: alice@orgA.bio (1-year, ECDSA P-256)
│ ├── User Signing Certificate: bob@orgA.bio (1-year, ECDSA P-256)
│ └── Agent Certificate: agent-wo-12345 (24-hour, ephemeral)

├── Intermediate CA: Organization B (Online, 5-year, HSM-backed)
│ └── User Signing Certificate: charlie@orgB.bio (1-year, ECDSA P-256)

└── Intermediate CA: CODITECT-Services (Online, 5-year, HSM-backed)
└── Service Certificate: esig-service.bio-qms.coditect.cloud (1-year)

Multi-Tenant Isolation:

  • Each customer organization receives a dedicated Intermediate CA
  • Cryptographic tenant isolation: Organization A signatures cannot validate against Organization B's certificate chain
  • Independent revocation: Tenant offboarding revokes only that tenant's Intermediate CA
  • Regulatory boundary: Per-organization audit trails and compliance attestation

2.2 Cryptographic Algorithm (ECDSA P-256)

Algorithm Selection Rationale:

CriterionValueJustification
AlgorithmECDSA with P-256 curve (secp256r1)NSA Suite B, NIST FIPS 186-5 approved
Hash FunctionSHA-256FIPS 180-4 approved, 128-bit collision resistance
Signature SchemeECDSA-SHA256Standard for FDA Part 11 validated systems
Key Size256-bit~128-bit security level, sufficient for 20+ year validity
Signature Size64 bytes10x smaller than RSA-2048 (512 bytes)
Performance~5ms signature generation10x faster than RSA-2048 for high-throughput workflows
Regulatory AcceptanceFDA Part 11 validated systems widely use ECDSAIndustry standard

Reference: Cryptographic Standards Policy (D.1.1) Section 3.2.6 - Digital Signature Schemes

2.3 Signature Data Model

Core Signature Entity:

interface ElectronicSignature {
// Unique identifier
id: string; // cuid, immutable
tenantId: string; // Multi-tenant partition key

// Signer identity (FDA §11.100 - unique to one individual)
signerId: string; // FK → Person.id (unique user)
signerCertificateId: string; // FK → X509Certificate.id (PKI binding)

// Signature meaning (FDA §11.50 - manifestation)
meaning: SignatureMeaning; // Enum: AUTHOR, REVIEWER, APPROVER, VERIFIER, WITNESS, REJECTOR
customMeaning: string | null; // Tenant-configurable meaning (if meaning = CUSTOM)
reason: string | null; // Optional: why signature was applied

// Cryptographic binding (FDA §11.70)
documentHash: string; // SHA-256 hash of record content at signing time
documentVersion: string; // Record version ID (immutable after signing)
signatureValue: string; // Base64-encoded ECDSA signature (64 bytes)
signatureAlgorithm: string; // "ECDSA-SHA256"

// Authentication context (FDA §11.200 - signature components)
authMethod: AuthMethod; // How identity was verified
authFactors: AuthenticationFactor[]; // Array of factors used (password, TOTP, biometric)
sessionId: string | null; // IdP session identifier
ipAddress: string | null; // Client IP for forensics
deviceFingerprint: string | null; // Browser/device fingerprint

// Timestamp (FDA §11.50 - manifestation)
signedAt: Date; // Server-generated UTC timestamp

// Signature lifecycle
status: SignatureStatus; // ACTIVE, REVOKED, EXPIRED, INVALIDATED
revokedAt: Date | null; // Timestamp of revocation
revokedBy: string | null; // User ID who revoked signature
revokedReason: string | null; // Reason for revocation

// Binding consumption (prevents signature reuse)
consumed: boolean; // True after binding to approval
consumedAt: Date | null; // When signature was consumed
consumedBy: string | null; // Approval ID that consumed signature

// Long-term validation (LTV) support
certificateChain: string[]; // [User Cert, Intermediate CA, Root CA] PEM array
ocspResponse: string | null; // OCSP response at signing time (base64)
timestampToken: string | null; // RFC 3161 timestamp authority token
}

enum SignatureMeaning {
AUTHOR = 'AUTHOR', // Created or wrote the record
REVIEWER = 'REVIEWER', // Reviewed the record for accuracy
APPROVER = 'APPROVER', // Approved the record for release
VERIFIER = 'VERIFIER', // Verified execution or completion
WITNESS = 'WITNESS', // Witnessed the signing event
REJECTOR = 'REJECTOR', // Rejected the record with documented reason
CUSTOM = 'CUSTOM' // Tenant-defined meaning (stored in customMeaning)
}

enum SignatureStatus {
ACTIVE = 'ACTIVE', // Signature valid and in use
REVOKED = 'REVOKED', // Certificate or signature explicitly revoked
EXPIRED = 'EXPIRED', // Certificate expired (but signature may still validate historically)
INVALIDATED = 'INVALIDATED' // Record modified, signature no longer valid
}

enum AuthMethod {
PASSWORD_TOTP = 'PASSWORD_TOTP', // Password + TOTP (6-digit code)
PASSWORD_BIOMETRIC = 'PASSWORD_BIOMETRIC', // Password + fingerprint/face
SMARTCARD_PIN = 'SMARTCARD_PIN', // PKI smart card + PIN
SSO_REAUTH_MFA = 'SSO_REAUTH_MFA' // SSO re-authentication with MFA
}

interface AuthenticationFactor {
type: 'PASSWORD' | 'TOTP' | 'BIOMETRIC' | 'SMARTCARD' | 'SSO';
verified: boolean;
verifiedAt: Date;
metadata?: Record<string, any>; // e.g., { biometricType: 'FINGERPRINT' }
}

2.4 Signature Lifecycle State Machine

State Descriptions:

StateDescriptionAllowed TransitionsReversible?
CreatedSignature object created, awaiting authentication→ AuthenticatingYes (timeout)
AuthenticatingUser prompted for re-authentication (MFA)→ Signed, → Created (retry)Yes
SignedCryptographic signature generated, awaiting binding→ Bound (within 5 min)No
BoundSignature bound to specific approval record→ ActiveNo
ActiveSignature in use, validating record integrity→ Invalidated, → Revoked, → ExpiredNo
InvalidatedRecord modified, signature no longer valid→ (terminal)No
RevokedCertificate or signature explicitly revoked→ (terminal)No
ExpiredCertificate validity period ended→ ArchivedNo
ArchivedLong-term validation retained for audit→ (terminal after retention)No

3. Signature Components per FDA §11.200

3.1 Two-Factor Identification Components

FDA 21 CFR Part 11 §11.200 requires electronic signatures to use at least two distinct identification components such as an identification code and password.

BIO-QMS Implementation:

3.1.1 Component 1: User Identification

Type: Knowledge factor + possession factor

Elements:

  • Unique User ID: Email address (verified via corporate email confirmation)
  • User Certificate: ECDSA P-256 certificate bound to user's identity
    • Subject: CN={FirstName} {LastName} ({email}), O={OrganizationName}, OU=BIO-QMS Users, C=US
    • Issued by: Organization's Intermediate CA
    • Validity: 1 year (annual renewal with identity re-verification)

Verification Process:

  1. User logs in with corporate email + password
  2. System validates user account exists in tenant organization
  3. System retrieves user's signing certificate from HSM-backed certificate store
  4. Certificate validity checked (not expired, not revoked via CRL/OCSP)

3.1.2 Component 2: Knowledge Factor

Type: Password meeting complexity requirements

Password Policy (FDA §11.300 Controls):

RequirementValueRationale
Minimum Length12 charactersNIST SP 800-63B recommendation
Character Complexity3 of 4: uppercase, lowercase, digits, symbolsEntropy >50 bits
Dictionary CheckReject common passwords (haveibeenpwned API)Prevent credential stuffing
Expiration90 days for standard users, 60 days for adminsFDA Part 11 best practice
Reuse PreventionLast 12 passwords cannot be reusedPrevent password cycling
Lockout Policy5 failed attempts = 15-minute lockoutBrute-force protection

Password Storage:

  • Algorithm: PBKDF2-HMAC-SHA256
  • Iterations: 600,000 (OWASP 2023 recommendation)
  • Salt: 32-byte random salt per user
  • Storage: Encrypted database column with KMS key

3.1.3 Component 3 (Optional): Biometric Factor

Type: Inherence factor (something you are)

Supported Biometrics:

  • Fingerprint: Touch ID, Windows Hello fingerprint
  • Facial Recognition: Face ID, Windows Hello facial recognition
  • Voice Recognition: (Future) Voice biometric for verbal approvals

Implementation:

  • Browser WebAuthn API for fingerprint/face authentication
  • Device-local biometric verification (not transmitted to server)
  • Server receives cryptographic attestation token (not biometric template)
  • Attestation token signed with device's secure enclave key

Opt-In Model:

  • Users opt-in to biometric authentication during onboarding
  • Biometric remains optional; password + TOTP always available as fallback
  • Regulatory users (GxP workflows) may require biometric (tenant-configurable)

3.2 Re-Authentication at Each Signing Event

FDA §11.200(a)(2) Requirement:

Electronic signatures not based on biometrics shall employ at least two distinct identification components... and shall be used only by their genuine owners; and shall be administered and executed to ensure that attempted use of an individual's electronic signature by anyone other than its genuine owner requires collaboration of two or more individuals.

BIO-QMS Enforcement:

3.2.1 Continuous Session vs. Non-Continuous Session

Session TypeRe-Authentication Required?TimeoutUse Case
Non-ContinuousYes (always)5 minutes since last signatureStandard user workflow (sign infrequently)
ContinuousNo (grace period: 15 minutes)15 minutes idle, 8 hours absolutePower users (QA reviewing 100+ documents)

Continuous Session Mode:

  • User explicitly enables "continuous signing mode" via UI toggle
  • System logs continuous mode activation (audit trail)
  • Session extends for 15 minutes after each signature (idle timeout)
  • Absolute timeout: 8 hours (user must re-authenticate regardless)
  • Terminated on: browser close, explicit logout, IP address change, 8-hour limit

Non-Continuous Session Mode (Default):

  • Re-authentication required for each signature action
  • 5-minute signature window: after authentication, signature must be executed within 5 minutes
  • Window expiration: signature object expires, user must re-authenticate

3.2.2 Authentication Context Capture

For each signature, the system captures authentication metadata:

interface SignatureAuthContext {
// Primary authentication factors
authMethod: AuthMethod; // PASSWORD_TOTP, PASSWORD_BIOMETRIC, etc.
authFactors: AuthenticationFactor[]; // Array of verified factors

// Session context
sessionType: 'CONTINUOUS' | 'NON_CONTINUOUS';
sessionId: string; // Unique session identifier
sessionStartedAt: Date; // Session creation timestamp
lastActivityAt: Date; // Last user activity before signing

// Device context
ipAddress: string; // Client IP (IPv4 or IPv6)
userAgent: string; // Browser user agent string
deviceFingerprint: string; // Canvas + WebGL + audio fingerprint hash
geolocation?: { lat: number; lon: number }; // Optional: GPS coordinates (if consent granted)

// Identity verification
idpProvider: string | null; // "Azure AD", "Okta", "Auth0", or null (local auth)
idpSessionId: string | null; // IdP session ID for SSO
mfaVerified: boolean; // True if MFA verification completed
mfaType: string | null; // "TOTP", "SMS", "BIOMETRIC", "SMARTCARD"

// Risk assessment
riskScore: number; // 0-100 (anomaly detection score)
riskFactors: string[]; // ["NEW_DEVICE", "UNUSUAL_LOCATION", "HIGH_VELOCITY"]

// Timestamp
authenticatedAt: Date; // UTC timestamp of successful authentication
}

Risk-Based Authentication:

  • High-risk signatures (e.g., final GMP batch release approval) may require additional verification
  • Risk factors: new device, unusual location, high signature velocity, after-hours access
  • Elevated risk (score >70) triggers: additional MFA challenge, supervisor notification, extended audit logging

3.3 Authentication Sequence Diagram


4. Signature-Record Binding per FDA §11.70

4.1 Cryptographic Binding Mechanism

FDA §11.70 Requirement:

Electronic signatures and handwritten signatures executed to electronic records shall be linked to their respective electronic records to ensure that the signatures cannot be excised, copied, or otherwise transferred to falsify an electronic record by ordinary means.

BIO-QMS Implementation:

4.1.1 Binding Algorithm

/**
* Cryptographic signature binding per FDA 21 CFR Part 11 §11.70
*/
interface SignatureBinding {
// Record identification
recordId: string; // Unique record identifier (e.g., "DOC-12345")
recordType: string; // "SOP", "DEVIATION", "CAPA", "WORK_ORDER", etc.
recordVersion: string; // Immutable version ID (e.g., "v3.2.1")

// Record content hash (integrity)
recordHash: string; // SHA-256(record content at signing time)
hashAlgorithm: 'SHA-256';

// Signer identity
signerId: string; // User ID (FK → Person.id)
signerCertificateSerial: string; // X.509 certificate serial number
signerCertificateDN: string; // Distinguished Name from certificate

// Signature metadata
meaning: SignatureMeaning;
signedAt: Date; // UTC timestamp

// Cryptographic signature
signature: string; // Base64(ECDSA-P256-Sign(privateKey, M))
signatureAlgorithm: 'ECDSA-SHA256';

// Long-term validation
certificateChain: string[]; // PEM-encoded [User, Intermediate, Root]
ocspResponse: string | null; // OCSP response at signing time
timestampToken: string | null; // RFC 3161 timestamp authority token
}

/**
* Generate signature over record content
*/
async function createSignatureBinding(
recordId: string,
recordContent: string,
signerId: string,
meaning: SignatureMeaning
): Promise<SignatureBinding> {
// Step 1: Compute record hash
const recordHash = crypto
.createHash('sha256')
.update(recordContent, 'utf8')
.digest('hex');

// Step 2: Build message to sign (M)
const message = {
recordId,
recordHash,
signerId,
meaning,
timestamp: new Date().toISOString()
};
const messageJson = JSON.stringify(message);
const messageBuffer = Buffer.from(messageJson, 'utf8');

// Step 3: Sign with HSM-backed user key
const hsmClient = new HSMClient();
const signatureBytes = await hsmClient.signWithHSM(
messageBuffer,
{
projectId: 'bioqms-prod',
locationId: 'us-central1',
keyRingId: 'user-signing-keys',
keyId: `user-${signerId}`,
keyVersion: 'latest' // Use most recent key version
}
);

const signatureBase64 = signatureBytes.toString('base64');

// Step 4: Retrieve certificate chain
const certChain = await getCertificateChain(signerId);

// Step 5: Fetch OCSP response for long-term validation
const ocspResponse = await fetchOCSPResponse(certChain[0]); // User cert

// Step 6: Get timestamp token from trusted TSA (optional but recommended)
const timestampToken = await getTimestampToken(signatureBytes);

return {
recordId,
recordType: 'SOP', // Example
recordVersion: 'v1.0.0',
recordHash,
hashAlgorithm: 'SHA-256',
signerId,
signerCertificateSerial: extractCertSerial(certChain[0]),
signerCertificateDN: extractCertDN(certChain[0]),
meaning,
signedAt: new Date(),
signature: signatureBase64,
signatureAlgorithm: 'ECDSA-SHA256',
certificateChain: certChain,
ocspResponse,
timestampToken
};
}

4.1.2 Signature Verification Algorithm

/**
* Verify signature binding integrity
*/
async function verifySignatureBinding(
binding: SignatureBinding,
currentRecordContent: string
): Promise<SignatureVerificationResult> {
const errors: string[] = [];

// Check 1: Record content has not changed
const currentHash = crypto
.createHash('sha256')
.update(currentRecordContent, 'utf8')
.digest('hex');

if (currentHash !== binding.recordHash) {
errors.push(
`Record content modified: expected hash ${binding.recordHash}, ` +
`got ${currentHash}`
);
}

// Check 2: Certificate chain validation
const certChainValid = await verifyCertificateChain(
binding.certificateChain
);
if (!certChainValid.valid) {
errors.push(`Certificate chain invalid: ${certChainValid.error}`);
}

// Check 3: Certificate not revoked (CRL or OCSP check)
const revocationStatus = await checkRevocationStatus(
binding.certificateChain[0] // User certificate
);
if (revocationStatus === 'REVOKED') {
errors.push(`Certificate revoked at ${revocationStatus.revokedAt}`);
}

// Check 4: Cryptographic signature verification
const message = {
recordId: binding.recordId,
recordHash: binding.recordHash,
signerId: binding.signerId,
meaning: binding.meaning,
timestamp: binding.signedAt.toISOString()
};
const messageBuffer = Buffer.from(JSON.stringify(message), 'utf8');
const signatureBuffer = Buffer.from(binding.signature, 'base64');

const publicKey = extractPublicKeyFromCert(binding.certificateChain[0]);
const signatureValid = crypto.verify(
'sha256',
messageBuffer,
{
key: publicKey,
format: 'pem',
type: 'spki'
},
signatureBuffer
);

if (!signatureValid) {
errors.push('Cryptographic signature verification failed');
}

// Check 5: Timestamp validation (if timestamp token present)
if (binding.timestampToken) {
const timestampValid = await verifyTimestampToken(
binding.timestampToken,
signatureBuffer
);
if (!timestampValid.valid) {
errors.push(`Timestamp token invalid: ${timestampValid.error}`);
}
}

return {
valid: errors.length === 0,
errors,
verifiedAt: new Date(),
certificateValidUntil: extractCertExpiry(binding.certificateChain[0])
};
}

interface SignatureVerificationResult {
valid: boolean;
errors: string[];
verifiedAt: Date;
certificateValidUntil: Date;
}

4.2 Binding Metadata

Signature binding includes:

FieldPurposeImmutability
recordIdUnique record identifierImmutable
recordVersionVersion at time of signingImmutable (new version if record changes)
recordHashSHA-256 content hashImmutable (changes invalidate signature)
signerIdSigner's user IDImmutable
signerCertificateSerialCertificate serial number for revocation checkImmutable
timestampServer-generated UTC timestampImmutable (tamper-resistant)
meaningSignature purpose (APPROVER, REVIEWER, etc.)Immutable (bound to signature)

4.3 Immutability Enforcement

Database Constraints:

-- Signature bindings table
CREATE TABLE signature_bindings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),

-- Record binding
record_id VARCHAR(255) NOT NULL,
record_type VARCHAR(50) NOT NULL,
record_version VARCHAR(50) NOT NULL,
record_hash CHAR(64) NOT NULL, -- SHA-256 hex

-- Signer identity
signer_id UUID NOT NULL REFERENCES persons(id),
signer_certificate_serial VARCHAR(255) NOT NULL,

-- Signature
meaning signature_meaning_enum NOT NULL,
signed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
signature BYTEA NOT NULL, -- ECDSA signature bytes

-- Long-term validation
certificate_chain JSONB NOT NULL, -- Array of PEM certs
ocsp_response BYTEA,
timestamp_token BYTEA,

-- Immutability constraints
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

-- Prevent updates to critical fields
CONSTRAINT no_update_after_creation CHECK (created_at = updated_at)
);

-- Prevent any UPDATEs to signature bindings
CREATE TRIGGER prevent_signature_updates
BEFORE UPDATE ON signature_bindings
FOR EACH ROW
EXECUTE FUNCTION prevent_row_updates();

-- Audit trigger for all operations
CREATE TRIGGER audit_signature_binding_changes
AFTER INSERT OR DELETE ON signature_bindings
FOR EACH ROW
EXECUTE FUNCTION log_signature_audit_event();

Application-Level Enforcement:

// Signature binding is write-once
class SignatureBinding {
// Constructor allows setting properties
constructor(data: SignatureBindingData) {
Object.assign(this, data);
Object.freeze(this); // JavaScript object immutability
}

// No setter methods allowed
// toString, toJSON allowed for display
}

4.4 Signature Verification on Every Read

FDA §11.70 Interpretation: The signature-record link must be verified on every access to ensure integrity.

Implementation:

/**
* Middleware: Verify signature integrity on document retrieval
*/
async function verifySignaturesMiddleware(
req: Request,
res: Response,
next: NextFunction
) {
const { documentId } = req.params;

// Fetch document and all signature bindings
const document = await Document.findById(documentId);
const signatures = await SignatureBinding.find({ recordId: documentId });

// Verify each signature
const verificationResults = await Promise.all(
signatures.map(sig => verifySignatureBinding(sig, document.content))
);

// Check for any invalid signatures
const invalidSignatures = verificationResults.filter(r => !r.valid);

if (invalidSignatures.length > 0) {
// Log security event
await auditService.log({
event: 'SIGNATURE_VERIFICATION_FAILED',
documentId,
invalidCount: invalidSignatures.length,
errors: invalidSignatures.flatMap(r => r.errors)
});

// Attach verification status to response
req.signatureVerification = {
valid: false,
totalSignatures: signatures.length,
invalidSignatures: invalidSignatures.length,
errors: invalidSignatures
};
} else {
req.signatureVerification = {
valid: true,
totalSignatures: signatures.length,
invalidSignatures: 0
};
}

next();
}

Display to User:

// Frontend rendering
<DocumentHeader>
<SignatureStatus>
{signatureVerification.valid ? (
<SuccessIcon />
<span>All signatures valid ({signatureVerification.totalSignatures})</span>
) : (
<ErrorIcon />
<span>
{signatureVerification.invalidSignatures} of {signatureVerification.totalSignatures} signatures invalid
<Button onClick={showVerificationDetails}>View Details</Button>
</span>
)}
</SignatureStatus>
</DocumentHeader>

4.5 Long-Term Signature Validity (LTV)

Challenge: Signatures must remain valid beyond certificate expiration for 10+ year audit trails.

Solution: Embed validation data at signing time:

ComponentPurposeRetention
Certificate ChainProves certificate lineage to trusted rootForever (embedded in signature)
OCSP ResponseProves certificate not revoked at signing timeForever (embedded in signature)
Timestamp TokenProves signature existed at specific time (RFC 3161)Forever (embedded in signature)

Timestamp Authority (TSA) Integration:

/**
* Obtain RFC 3161 timestamp token from trusted TSA
*/
async function getTimestampToken(
signatureBytes: Buffer
): Promise<string | null> {
try {
const tsaUrl = 'http://timestamp.digicert.com';

// Create timestamp request
const tsaRequest = {
version: 1,
messageImprint: {
hashAlgorithm: 'SHA-256',
hashedMessage: crypto.createHash('sha256').update(signatureBytes).digest()
},
certReq: true
};

// Submit to TSA
const response = await axios.post(tsaUrl, tsaRequest, {
headers: { 'Content-Type': 'application/timestamp-query' }
});

// Verify TSA response
const timestampToken = response.data;

// Store token (base64 encoded)
return Buffer.from(timestampToken).toString('base64');
} catch (error) {
console.error('TSA request failed:', error);
return null; // Timestamp optional but recommended
}
}

LTV Validation (Years Later):

/**
* Verify signature with long-term validation data
*/
async function verifySignatureWithLTV(
binding: SignatureBinding
): Promise<LTVVerificationResult> {
// 1. Verify certificate chain to trusted root
const chainValid = await verifyCertificateChain(binding.certificateChain);

// 2. Check OCSP response embedded at signing time
// (proves cert was valid when signature created)
const ocspValid = await verifyEmbeddedOCSP(
binding.ocspResponse,
binding.certificateChain[0],
binding.signedAt
);

// 3. Verify timestamp token
// (proves signature existed at claimed time)
const timestampValid = await verifyTimestampToken(
binding.timestampToken,
Buffer.from(binding.signature, 'base64')
);

// 4. Cryptographic signature verification
const signatureValid = verifyCryptographicSignature(binding);

return {
valid: chainValid && ocspValid && timestampValid && signatureValid,
certificateChainValid: chainValid,
ocspValid,
timestampValid,
signatureValid,
validationTimestamp: new Date()
};
}

5. Signature Meaning per FDA §11.50

5.1 Predefined Signature Meanings

FDA §11.50 requires signature manifestations to include "the meaning (such as review, approval, responsibility, or authorship) associated with the signature."

BIO-QMS Standard Meanings:

MeaningDefinitionUse Case ExamplesRegulatory Implication
AUTHORCreated or wrote the recordSOP authoring, deviation report writingRecord owner, accountable for content
REVIEWERReviewed the record for accuracy, completeness, and compliancePeer review of SOP, technical review of validation protocolTechnical accuracy verification
APPROVERApproved the record for release and implementationQA approval of SOP, management approval of CAPAAuthorization for use
VERIFIERVerified execution or completion of an activityExecution of cleaning procedure, completion of trainingEvidence of work performed
WITNESSWitnessed the signing event or activitySecond person verification, critical parameter confirmationIndependent confirmation
REJECTORRejected the record with documented reasonQA rejection of batch record, non-conformance rejectionFormal disapproval

Database Enumeration:

CREATE TYPE signature_meaning_enum AS ENUM (
'AUTHOR',
'REVIEWER',
'APPROVER',
'VERIFIER',
'WITNESS',
'REJECTOR',
'CUSTOM'
);

5.2 Custom Meaning Support

Tenant-Configurable Meanings:

Organizations may define custom signature meanings for specialized workflows:

interface CustomSignatureMeaning {
id: string;
tenantId: string;
code: string; // Unique code (e.g., "BATCH_RELEASE")
displayName: string; // "Batch Release Authorization"
description: string; // Full definition
regulatoryContext: string; // Which regulation requires this meaning
requiresJustification: boolean; // Must provide reason when signing
auditLevel: 'STANDARD' | 'ENHANCED'; // Audit logging detail
createdBy: string;
approvedBy: string; // Quality Head approval required
effectiveDate: Date;
}

Example Custom Meanings:

{
"code": "BATCH_RELEASE",
"displayName": "Batch Release Authorization",
"description": "Final approval for release of GMP batch to distribution",
"regulatoryContext": "FDA 21 CFR 211.188 - Batch production and control records",
"requiresJustification": true,
"auditLevel": "ENHANCED"
}

Approval Workflow for Custom Meanings:

  1. Request: Quality or Regulatory Affairs proposes new meaning
  2. Review: Quality Head reviews against regulatory requirements
  3. Approval: Quality Head approves (electronic signature required)
  4. Documentation: Meaning definition added to organizational SOPs
  5. Audit Trail: All uses of custom meaning logged with enhanced detail

5.3 Meaning Immutability

Constraint: Once bound to a signature, meaning cannot be changed.

Rationale: Meaning is part of the cryptographic signature payload. Changing meaning would invalidate signature.

Enforcement:

// Meaning is part of signed message
const signedMessage = {
recordId: 'DOC-12345',
recordHash: 'abc123...',
signerId: 'user-456',
meaning: 'APPROVER', // <-- Bound to signature
timestamp: '2026-02-16T10:30:00Z'
};

const signature = await signMessage(signedMessage, userPrivateKey);

// Changing meaning invalidates signature
const tamperedMessage = { ...signedMessage, meaning: 'REVIEWER' };
const valid = await verifySignature(tamperedMessage, signature, userPublicKey);
// valid === false (signature verification fails)

5.4 Display Requirements per FDA §11.50

FDA §11.50(a) Requirement:

Signed electronic records shall contain information associated with the signing that clearly indicates all of the following: (1) The printed name of the signer; (2) The date and time when the signature was executed; and (3) The meaning (such as review, approval, responsibility, or authorship) associated with the signature.

BIO-QMS Display Implementation:

5.4.1 Screen Display

// React component for signature manifestation
interface SignatureManifestationProps {
signature: ElectronicSignature;
signer: Person;
}

const SignatureManifestation: React.FC<SignatureManifestationProps> = ({
signature,
signer
}) => {
return (
<div className="signature-block">
<div className="signature-header">
<span className="meaning-badge">{signature.meaning}</span>
<VerificationIcon valid={signature.status === 'ACTIVE'} />
</div>

{/* (1) Printed name of signer */}
<div className="signer-name">
<strong>{signer.firstName} {signer.lastName}</strong>
<span className="signer-email">({signer.email})</span>
</div>

{/* (2) Date and time */}
<div className="signature-timestamp">
<CalendarIcon />
<time dateTime={signature.signedAt.toISOString()}>
{formatDateTime(signature.signedAt, signer.timezone)}
</time>
<span className="timezone-indicator">
{signer.timezone} ({getTimezoneOffset(signer.timezone)})
</span>
</div>

{/* (3) Meaning */}
<div className="signature-meaning">
<span className="meaning-label">Meaning:</span>
<span className="meaning-value">
{formatMeaning(signature.meaning)}
</span>
</div>

{/* Optional: Reason */}
{signature.reason && (
<div className="signature-reason">
<span className="reason-label">Reason:</span>
<p className="reason-text">{signature.reason}</p>
</div>
)}

{/* Certificate details (expandable) */}
<details className="certificate-details">
<summary>View Certificate Details</summary>
<dl>
<dt>Certificate Serial:</dt>
<dd>{signature.signerCertificateSerial}</dd>
<dt>Issued By:</dt>
<dd>{extractIssuerDN(signature.certificateChain[0])}</dd>
<dt>Valid Until:</dt>
<dd>{extractExpiry(signature.certificateChain[0])}</dd>
</dl>
</details>
</div>
);
};

5.4.2 Printed Copies (PDF Export)

/**
* Render signature manifestation in PDF export
*/
async function renderSignatureInPDF(
pdfDoc: PDFDocument,
signature: ElectronicSignature,
signer: Person
): Promise<void> {
const page = pdfDoc.addPage();
const { width, height } = page.getSize();
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
const fontBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold);

let yOffset = height - 50;

// Signature block border
page.drawRectangle({
x: 50,
y: yOffset - 100,
width: width - 100,
height: 90,
borderColor: rgb(0, 0, 0),
borderWidth: 1
});

// Meaning badge
page.drawText(signature.meaning, {
x: 60,
y: yOffset - 20,
size: 10,
font: fontBold,
color: rgb(0, 0, 0.8)
});

// (1) Signer name
page.drawText(`${signer.firstName} ${signer.lastName}`, {
x: 60,
y: yOffset - 40,
size: 12,
font: fontBold
});

// (2) Date and time
const dateStr = signature.signedAt.toISOString();
page.drawText(`Date/Time: ${dateStr}`, {
x: 60,
y: yOffset - 55,
size: 10,
font
});

// (3) Meaning
page.drawText(`Meaning: ${formatMeaning(signature.meaning)}`, {
x: 60,
y: yOffset - 70,
size: 10,
font
});

// Digital signature verification code
page.drawText(`Signature Hash: ${signature.signature.substring(0, 32)}...`, {
x: 60,
y: yOffset - 85,
size: 8,
font,
color: rgb(0.5, 0.5, 0.5)
});
}

5.4.3 Electronic Copies (API Responses)

/**
* API response includes signature manifestation
*/
interface DocumentResponse {
id: string;
title: string;
content: string;
version: string;

signatures: SignatureManifestation[];
}

interface SignatureManifestation {
// (1) Printed name
signerName: string;
signerEmail: string;

// (2) Date and time
signedAt: string; // ISO 8601 UTC
timezone: string;

// (3) Meaning
meaning: string;
meaningDescription: string;

// Verification
signatureValid: boolean;
certificateValid: boolean;

// Certificate details
certificate: {
serial: string;
issuer: string;
validFrom: string;
validUntil: string;
};
}

6. Sequential Signature Workflow

6.1 Approval Workflow Engine

Purpose: Enforce ordered signing requirements for multi-signature documents (e.g., SOP: Author → Reviewer → QA Approver).

6.1.1 Workflow Definition Data Model

interface ApprovalWorkflow {
id: string;
tenantId: string;
name: string; // "SOP Approval Workflow"
documentType: string; // "SOP", "DEVIATION", "CAPA"

steps: ApprovalStep[]; // Ordered array of signing steps

createdBy: string;
approvedBy: string; // Quality Head approval
effectiveDate: Date;
version: string;
}

interface ApprovalStep {
stepNumber: number; // 1, 2, 3...
role: string; // "AUTHOR", "REVIEWER", "QA_APPROVER"
meaning: SignatureMeaning; // What signature means at this step
required: boolean; // Is this step mandatory?
condition: string | null; // Optional: JSON logic for conditional steps
allowParallel: boolean; // Can execute in parallel with previous step?
timeoutHours: number | null; // Optional: step must complete within X hours
delegationAllowed: boolean; // Can signer delegate to another user?
escalationPolicy: string | null; // JSON: who to escalate to if timeout
}

Example Workflow:

{
"name": "SOP Approval Workflow",
"documentType": "SOP",
"steps": [
{
"stepNumber": 1,
"role": "AUTHOR",
"meaning": "AUTHOR",
"required": true,
"condition": null,
"allowParallel": false,
"timeoutHours": null,
"delegationAllowed": false
},
{
"stepNumber": 2,
"role": "PEER_REVIEWER",
"meaning": "REVIEWER",
"required": true,
"condition": null,
"allowParallel": false,
"timeoutHours": 72,
"delegationAllowed": true,
"escalationPolicy": "{\"escalateTo\": \"QA_MANAGER\", \"afterHours\": 96}"
},
{
"stepNumber": 3,
"role": "QA_APPROVER",
"meaning": "APPROVER",
"required": true,
"condition": null,
"allowParallel": false,
"timeoutHours": 48,
"delegationAllowed": false
}
]
}

6.1.2 Workflow State Machine

6.1.3 Step Dependencies and Parallel Execution

Sequential Steps (Default):

// Step N cannot execute until Step N-1 complete
function canExecuteStep(
workflow: ApprovalWorkflow,
stepNumber: number,
completedSteps: Set<number>
): boolean {
const step = workflow.steps.find(s => s.stepNumber === stepNumber);

if (!step) return false;

// If step allows parallel execution
if (step.allowParallel) {
// Can execute if N-2 is complete (parallel with N-1)
const prerequisiteStep = stepNumber - 2;
return prerequisiteStep < 1 || completedSteps.has(prerequisiteStep);
}

// Sequential: must wait for previous step
const previousStep = stepNumber - 1;
return previousStep < 1 || completedSteps.has(previousStep);
}

Parallel Steps Example:

{
"steps": [
{"stepNumber": 1, "role": "AUTHOR", "allowParallel": false},
{"stepNumber": 2, "role": "PEER_REVIEWER_1", "allowParallel": false},
{"stepNumber": 3, "role": "PEER_REVIEWER_2", "allowParallel": true},
{"stepNumber": 4, "role": "QA_APPROVER", "allowParallel": false}
]
}

In this example:

  • Step 2 and Step 3 (two peer reviewers) can execute in parallel
  • Step 4 (QA approver) waits for both steps 2 and 3 to complete

6.2 Delegation of Signing Authority

Use Case: Reviewer is on vacation, delegates review to backup reviewer for 5 days.

6.2.1 Delegation Model

interface SigningDelegation {
id: string;
tenantId: string;

// Delegator (original signer)
delegatorId: string; // User who delegates
delegatorRole: string; // "PEER_REVIEWER"

// Delegate (substitute signer)
delegateId: string; // User who receives delegation

// Scope constraints
workflowId: string | null; // Specific workflow or null (any workflow)
documentType: string | null; // "SOP" or null (any document type)
documentIds: string[] | null; // Specific documents or null (any document)

// Time constraints
startDate: Date;
endDate: Date;
maxDuration: number; // Maximum delegation period (hours)

// Approval
approvedBy: string | null; // Manager approval (if required)
approvalReason: string;

// Status
status: 'ACTIVE' | 'REVOKED' | 'EXPIRED';
revokedAt: Date | null;
revokedBy: string | null;
revokedReason: string | null;

// Audit
createdAt: Date;
auditTrail: DelegationAuditEvent[];
}

interface DelegationAuditEvent {
timestamp: Date;
actor: string; // Who performed action
action: 'CREATED' | 'APPROVED' | 'EXERCISED' | 'REVOKED';
details: Record<string, any>;
}

6.2.2 Delegation Constraints (FDA Compliance)

ConstraintValueRationale
Maximum Duration30 daysPrevent indefinite delegations
Approval RequiredManager or Quality HeadPrevent unauthorized delegation
Scope LimitationSpecific workflow or document typeNo blanket "sign anything" delegations
RevocationImmediate, by delegator or approverDelegator retains control
NotificationEmail to delegator on each delegate signatureVisibility into delegate actions
Audit TrailFull chain recorded (delegator → delegate → action)Compliance transparency

6.2.3 Delegation Workflow

6.2.4 Signature Manifestation for Delegated Signatures

Display:

REVIEWER
Bob Smith (bob@example.com)
Date/Time: 2026-02-20T14:30:00Z (EST -05:00)
Meaning: Reviewer

Delegated By: Alice Johnson (alice@example.com)
Delegation Period: 2026-02-15 to 2026-02-22
Delegation Approval: Carol Manager (carol@example.com)

Audit Log Entry:

{
"event": "SIGNATURE_CREATED",
"documentId": "SOP-12345",
"signerId": "bob-smith",
"signerName": "Bob Smith",
"delegatedBy": "alice-johnson",
"delegationId": "DEL-123",
"delegationApprovedBy": "carol-manager",
"meaning": "REVIEWER",
"timestamp": "2026-02-20T14:30:00Z"
}

6.3 Recall Mechanism

Use Case: Author submits SOP for review, then discovers error before QA approval.

6.3.1 Recall Rules

interface RecallRequest {
id: string;
documentId: string;
workflowInstanceId: string;
requestedBy: string; // User ID of recall requestor
reason: string; // Why recall is needed
requestedAt: Date;

// Approval required if signatures already collected
approvalRequired: boolean;
approvedBy: string | null;
approvalStatus: 'PENDING' | 'APPROVED' | 'DENIED';

// Status
status: 'PENDING' | 'APPROVED' | 'DENIED' | 'EXECUTED';
executedAt: Date | null;
}

/**
* Can document be recalled?
*/
function canRecallDocument(
workflow: ApprovalWorkflowInstance,
requestedBy: string
): RecallEligibility {
// Rule 1: Cannot recall if workflow complete
if (workflow.status === 'COMPLETED') {
return {
eligible: false,
reason: 'Workflow already completed; use Change Control process'
};
}

// Rule 2: Cannot recall if final step signature exists
const finalStep = workflow.steps[workflow.steps.length - 1];
if (finalStep.signatureId !== null) {
return {
eligible: false,
reason: 'Final approval already granted; cannot recall'
};
}

// Rule 3: Author can recall without approval if no other signatures
const otherSignatures = workflow.steps.filter(
s => s.signatureId !== null && s.signerId !== requestedBy
);
if (requestedBy === workflow.authorId && otherSignatures.length === 0) {
return {
eligible: true,
approvalRequired: false,
reason: 'Author recall before other signatures'
};
}

// Rule 4: Recall with approval if other signatures exist
if (otherSignatures.length > 0) {
return {
eligible: true,
approvalRequired: true,
approvers: ['QA_MANAGER'],
reason: 'Recall requires QA approval (existing signatures)'
};
}

return { eligible: true, approvalRequired: false };
}

6.3.2 Recall Process

Audit Trail for Recall:

{
"event": "WORKFLOW_RECALLED",
"documentId": "SOP-12345",
"workflowId": "WF-67890",
"requestedBy": "alice-johnson",
"reason": "Found calculation error in Appendix B",
"approvalRequired": true,
"approvedBy": "carol-qa-manager",
"invalidatedSignatures": [
{"signerId": "bob-smith", "meaning": "REVIEWER", "invalidatedAt": "2026-02-20T15:00:00Z"}
],
"timestamp": "2026-02-20T15:00:00Z"
}

7. Signature Revocation and Invalidation

7.1 Revocation Scenarios

ScenarioTriggerImmediate ActionLong-Term Impact
Compromised CredentialsUser reports password stolenRevoke all signatures during compromised periodRe-sign affected documents
Certificate RevocationCertificate private key exposedAdd to CRL, update OCSP responderExisting signatures may remain valid if timestamped before revocation
Record ModificationDocument content changedAutomatically invalidate all signaturesNew version created, re-approval required
Employee TerminationUser account disabledArchive signing certificate, revoke active sessionsHistorical signatures remain valid

7.2 Certificate Revocation Workflow

/**
* Revoke user signing certificate
*/
async function revokeCertificate(
certificateSerial: string,
reason: RevocationReason,
requestedBy: string,
justification: string
): Promise<RevocationResult> {
// Step 1: Validate revocation authority
const hasAuthority = await checkRevocationAuthority(requestedBy);
if (!hasAuthority) {
throw new Error('Insufficient authority to revoke certificates');
}

// Step 2: Fetch certificate details
const cert = await getCertificateBySerial(certificateSerial);

// Step 3: Add to Certificate Revocation List (CRL)
await crlService.addRevocation({
serial: certificateSerial,
reason,
revokedAt: new Date(),
revokedBy: requestedBy
});

// Step 4: Update OCSP responder
await ocspService.markRevoked(certificateSerial);

// Step 5: Identify affected signatures
const affectedSignatures = await findSignaturesByCertificate(
certificateSerial
);

// Step 6: Determine signature validity impact
const validityImpact = affectedSignatures.map(sig => {
// If signature has embedded timestamp before revocation, it remains valid
if (sig.timestampToken) {
const timestampDate = extractTimestampDate(sig.timestampToken);
return {
signatureId: sig.id,
valid: timestampDate < new Date(), // Timestamp before revocation
reason: 'Timestamped before revocation'
};
}

// No timestamp: signature invalid
return {
signatureId: sig.id,
valid: false,
reason: 'No timestamp; certificate revoked'
};
});

// Step 7: Audit log
await auditService.log({
event: 'CERTIFICATE_REVOKED',
certificateSerial,
reason,
requestedBy,
justification,
affectedSignatures: affectedSignatures.length,
invalidatedSignatures: validityImpact.filter(v => !v.valid).length
});

return {
success: true,
certificateSerial,
affectedSignatures: affectedSignatures.length,
validityImpact
};
}

enum RevocationReason {
KEY_COMPROMISE = 'KEY_COMPROMISE',
CA_COMPROMISE = 'CA_COMPROMISE',
AFFILIATION_CHANGED = 'AFFILIATION_CHANGED', // User left organization
SUPERSEDED = 'SUPERSEDED', // Certificate renewed
CESSATION_OF_OPERATION = 'CESSATION_OF_OPERATION',
CERTIFICATE_HOLD = 'CERTIFICATE_HOLD', // Temporary suspension
PRIVILEGE_WITHDRAWN = 'PRIVILEGE_WITHDRAWN' // User no longer authorized
}

7.3 Automatic Signature Invalidation on Record Modification

/**
* Database trigger: Invalidate signatures when record content changes
*/
CREATE OR REPLACE FUNCTION invalidate_signatures_on_record_change()
RETURNS TRIGGER AS $$
BEGIN
-- Check if record content actually changed
IF OLD.content IS DISTINCT FROM NEW.content THEN
-- Increment record version
NEW.version := increment_version(OLD.version);

-- Mark all active signatures as invalidated
UPDATE signature_bindings
SET status = 'INVALIDATED',
invalidated_at = NOW(),
invalidation_reason = 'Record content modified'
WHERE record_id = NEW.id
AND status = 'ACTIVE';

-- Log audit event
INSERT INTO audit_trail (event, record_id, details)
VALUES (
'SIGNATURES_INVALIDATED',
NEW.id,
jsonb_build_object(
'old_version', OLD.version,
'new_version', NEW.version,
'signatures_invalidated', (
SELECT COUNT(*) FROM signature_bindings
WHERE record_id = NEW.id AND status = 'INVALIDATED'
)
)
);
END IF;

RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_invalidate_signatures
BEFORE UPDATE ON documents
FOR EACH ROW
EXECUTE FUNCTION invalidate_signatures_on_record_change();

User Notification:

// When user attempts to modify signed document
async function beforeDocumentUpdate(
documentId: string,
userId: string
): Promise<void> {
const activeSignatures = await SignatureBinding.find({
recordId: documentId,
status: 'ACTIVE'
});

if (activeSignatures.length > 0) {
// Warn user about signature invalidation
const confirmation = await promptUser({
title: 'Warning: Active Signatures',
message: `This document has ${activeSignatures.length} active signature(s). ` +
`Modifying the content will invalidate all signatures and require re-approval. ` +
`Do you want to proceed?`,
buttons: ['Cancel', 'Create New Version']
});

if (confirmation !== 'Create New Version') {
throw new Error('Document modification cancelled by user');
}

// Log user acknowledgment
await auditService.log({
event: 'USER_ACKNOWLEDGED_SIGNATURE_INVALIDATION',
documentId,
userId,
activeSignatures: activeSignatures.length
});
}
}

7.4 Re-Signing Process After Revocation

/**
* Re-sign workflow after certificate revocation or record modification
*/
async function initiateReSigning(
documentId: string,
originalWorkflowId: string,
reason: string
): Promise<WorkflowInstance> {
// Step 1: Fetch original workflow definition
const originalWorkflow = await ApprovalWorkflow.findById(originalWorkflowId);

// Step 2: Identify which signatures need to be re-executed
const invalidatedSignatures = await SignatureBinding.find({
recordId: documentId,
status: 'INVALIDATED'
});

// Step 3: Notify original signers
const signerIds = invalidatedSignatures.map(sig => sig.signerId);
await notifySigners(signerIds, {
subject: 'Re-Approval Required',
message: `Document ${documentId} has been modified and requires your re-approval.`,
reason
});

// Step 4: Create new workflow instance
const newWorkflow = await WorkflowInstance.create({
workflowDefinitionId: originalWorkflow.id,
documentId,
status: 'PENDING',
previousWorkflowId: originalWorkflowId,
reason: `Re-signing required: ${reason}`
});

return newWorkflow;
}

8. Identity Verification per FDA §11.100

8.1 Identity Proofing Before Signature Privilege

FDA §11.100(a) Requirement:

Each electronic signature shall be unique to one individual and shall not be reused by, or reassigned to, anyone else.

FDA §11.100(b) Requirement:

Before an organization establishes, assigns, certifies, or otherwise sanctions an individual's electronic signature, the organization shall verify the identity of the individual.

BIO-QMS Identity Verification Workflow:

8.1.1 User Onboarding and Identity Verification

8.1.2 Identity Verification Ceremony Documentation

interface IdentityVerificationRecord {
id: string;
userId: string;
tenantId: string;

// Identity proofing
verificationMethod: 'GOVERNMENT_ID' | 'ORGANIZATIONAL_VERIFICATION' | 'SSO_FEDERATION';
governmentIdType: string | null; // "DRIVERS_LICENSE", "PASSPORT"
governmentIdNumber: string | null; // Last 4 digits only (PII protection)
governmentIdVerifiedBy: string | null; // Admin who verified ID

// Employment verification
employmentVerified: boolean;
hrSystemRecordId: string | null;
jobTitle: string;
department: string;
manager: string;

// Training verification
gxpTrainingCompleted: boolean;
trainingCertificateId: string | null;
trainingCompletionDate: Date | null;

// Authorization
signaturePrivilegeApprovedBy: string; // Quality Head or delegate
approvalDate: Date;
approvalReason: string;

// Certificate issuance
certificateSerial: string;
certificateIssuedAt: Date;
certificateExpiresAt: Date;

// Audit
createdAt: Date;
auditTrail: VerificationAuditEvent[];
}

8.1.3 Unique Identity Enforcement

Database Constraints:

-- Ensure one user = one identity = one signing certificate
CREATE TABLE user_signing_certificates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
user_id UUID NOT NULL REFERENCES persons(id),

certificate_serial VARCHAR(255) NOT NULL UNIQUE,
certificate_pem TEXT NOT NULL,
public_key_pem TEXT NOT NULL,

issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL,

status certificate_status_enum NOT NULL DEFAULT 'ACTIVE',
revoked_at TIMESTAMPTZ,
revoked_reason TEXT,

-- Unique constraint: one active certificate per user per tenant
CONSTRAINT one_cert_per_user UNIQUE (tenant_id, user_id, status)
WHERE (status = 'ACTIVE')
);

CREATE TYPE certificate_status_enum AS ENUM (
'ACTIVE',
'EXPIRED',
'REVOKED',
'SUPERSEDED'
);

Application-Level Enforcement:

/**
* Prevent certificate reuse or reassignment
*/
async function issueCertificate(
userId: string,
tenantId: string
): Promise<Certificate> {
// Check: user already has active certificate
const existingCert = await UserSigningCertificate.findOne({
userId,
tenantId,
status: 'ACTIVE'
});

if (existingCert) {
throw new Error(
`User already has active signing certificate (serial: ${existingCert.serial}). ` +
`Revoke existing certificate before issuing new one.`
);
}

// Check: certificate serial not already issued (no reuse)
const serialExists = await UserSigningCertificate.findOne({
certificateSerial: generateCertificateSerial()
});

if (serialExists) {
// Extremely unlikely with proper serial generation, but safety check
throw new Error('Certificate serial collision detected; regenerate serial');
}

// Proceed with issuance...
}

8.2 Identity Lifecycle Management

Lifecycle Events:

EventTriggerActionApproval Required
OnboardingNew hire with signing authorityIdentity verification ceremonyQuality Head
ActivationVerification completeIssue signing certificateQuality Head
RenewalCertificate nearing expiry (90 days)Re-verify identity, issue new certificateQuality Head
SuspensionPolicy violation, investigationTemporarily disable certificateManager
ReinstatementInvestigation clearedRe-enable certificateQuality Head
RevocationTermination, key compromiseRevoke certificate, add to CRLCISO or Quality Head
Archival7 years post-revocationMove to cold storageAutomated

9. Signature Audit and Compliance

9.1 Signature Event Logging

Every signature operation generates audit events:

EventCaptured DataRetentionRegulatory Reference
SIGNATURE_CREATEDUser ID, document ID, meaning, auth method, IP, timestamp10 yearsFDA §11.10(e)
SIGNATURE_CONSUMEDSignature ID, approval ID, workflow step10 yearsFDA §11.10(e)
SIGNATURE_VERIFIEDVerification result, timestamp, verified by1 yearSOC 2 CC6.1
SIGNATURE_INVALIDATEDReason, document version change, timestamp10 yearsFDA §11.10(e)
CERTIFICATE_ISSUEDUser ID, certificate serial, issued by, timestamp10 yearsFDA §11.100
CERTIFICATE_REVOKEDCertificate serial, reason, revoked by, timestamp10 yearsFDA §11.100
DELEGATION_CREATEDDelegator, delegate, scope, approved by10 yearsFDA §11.200
DELEGATION_EXERCISEDDelegation ID, signature ID, timestamp10 yearsFDA §11.200

Audit Log Schema:

CREATE TABLE signature_audit_trail (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),

-- Event classification
event_type signature_event_enum NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),

-- Actors
performed_by UUID NOT NULL REFERENCES persons(id),
performed_by_name VARCHAR(255) NOT NULL, -- Denormalized for reporting
on_behalf_of UUID REFERENCES persons(id), -- For delegation

-- Target entities
signature_id UUID REFERENCES signature_bindings(id),
document_id VARCHAR(255),
certificate_serial VARCHAR(255),

-- Context
ip_address INET,
user_agent TEXT,
session_id VARCHAR(255),

-- Event-specific data (JSON)
event_details JSONB NOT NULL,

-- Audit chain integrity (HMAC)
previous_entry_hash CHAR(64) NOT NULL, -- SHA-256 hash of previous entry
current_entry_hash CHAR(64) NOT NULL, -- SHA-256 hash of this entry
chain_hmac BYTEA NOT NULL, -- HMAC-SHA256 with KMS key

-- Partition key for performance
event_date DATE NOT NULL GENERATED ALWAYS AS (event_timestamp::DATE) STORED,

-- Prevent updates or deletions
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT immutable_audit_entry CHECK (created_at = event_timestamp)
);

-- Partition by month for performance
CREATE INDEX idx_signature_audit_by_month
ON signature_audit_trail (tenant_id, event_date DESC, event_type);

CREATE INDEX idx_signature_audit_by_user
ON signature_audit_trail (tenant_id, performed_by, event_timestamp DESC);

-- Prevent updates
CREATE TRIGGER prevent_audit_updates
BEFORE UPDATE ON signature_audit_trail
FOR EACH ROW
EXECUTE FUNCTION prevent_row_updates();

9.2 Compliance Reporting

9.2.1 Signature Statistics Dashboard

/**
* Generate monthly signature compliance report
*/
interface SignatureComplianceReport {
tenantId: string;
reportPeriod: {
startDate: Date;
endDate: Date;
};

// Signature volume
totalSignatures: number;
signaturesByMeaning: Record<SignatureMeaning, number>;
signaturesByUser: Array<{userId: string; userName: string; count: number}>;

// Authentication methods
authMethodBreakdown: Record<AuthMethod, number>;
mfaComplianceRate: number; // Percentage with MFA

// Certificate health
activeCertificates: number;
expiringCertificates: number; // Expiring within 90 days
revokedCertificates: number;

// Verification health
totalVerifications: number;
failedVerifications: number;
verificationFailureRate: number;

// Workflow completion
completedWorkflows: number;
averageWorkflowDuration: number; // Hours
escalatedWorkflows: number;

// Compliance metrics
fda11_70_compliance: number; // % signatures with valid binding
fda11_200_compliance: number; // % signatures with 2FA
fda11_50_compliance: number; // % signatures with manifestation

// Issues detected
issues: ComplianceIssue[];
}

interface ComplianceIssue {
severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
type: string;
description: string;
affectedRecords: string[];
recommendedAction: string;
}

9.2.2 Expired/Expiring Signatures Alert

/**
* Daily cron job: Check for expiring certificates
*/
async function checkExpiringCertificates(): Promise<void> {
const now = new Date();
const ninetyDaysFromNow = addDays(now, 90);

const expiringCerts = await UserSigningCertificate.find({
status: 'ACTIVE',
expiresAt: { $lte: ninetyDaysFromNow, $gte: now }
});

for (const cert of expiringCerts) {
const daysUntilExpiry = differenceInDays(cert.expiresAt, now);

// Alert thresholds: 90, 60, 30, 14, 7, 1 days
if ([90, 60, 30, 14, 7, 1].includes(daysUntilExpiry)) {
await sendCertificateExpiryNotification({
userId: cert.userId,
certificateSerial: cert.certificateSerial,
expiresAt: cert.expiresAt,
daysRemaining: daysUntilExpiry
});
}
}
}

9.2.3 Failed Verification Alerts

/**
* Real-time alert: Signature verification failure
*/
async function onSignatureVerificationFailure(
signatureId: string,
documentId: string,
errors: string[]
): Promise<void> {
// Log security event
await auditService.log({
event: 'SIGNATURE_VERIFICATION_FAILED',
signatureId,
documentId,
errors,
severity: 'HIGH'
});

// Alert Security and Quality teams
await alertService.send({
to: ['security@bioqms.com', 'quality@bioqms.com'],
subject: 'ALERT: Signature Verification Failure',
body: `
Signature verification failed for document ${documentId}.

Signature ID: ${signatureId}
Errors: ${errors.join(', ')}

This may indicate:
- Record tampering
- Certificate revocation
- Cryptographic integrity failure

Immediate investigation required.
`,
priority: 'HIGH'
});

// Quarantine document (prevent further access until investigation)
await Document.update(documentId, {
quarantined: true,
quarantineReason: 'Signature verification failure',
quarantinedAt: new Date()
});
}

9.3 FDA Inspection Readiness

15-Minute Retrieval Requirement:

/**
* Generate complete signature history for FDA inspection
*/
async function generateInspectionPackage(
documentId: string
): Promise<InspectionPackage> {
const startTime = Date.now();

// Fetch document
const document = await Document.findById(documentId);

// Fetch all signature bindings (current and historical)
const signatures = await SignatureBinding.find({
recordId: documentId
}).sort({ signedAt: 1 });

// Fetch all audit trail entries
const auditEntries = await SignatureAuditTrail.find({
documentId
}).sort({ eventTimestamp: 1 });

// Fetch all workflow instances
const workflows = await ApprovalWorkflowInstance.find({
documentId
});

// Verify all signatures
const verificationResults = await Promise.all(
signatures.map(sig => verifySignatureBinding(sig, document.content))
);

// Generate PDF report
const pdfReport = await generatePDFInspectionReport({
document,
signatures,
auditEntries,
workflows,
verificationResults
});

const elapsedTime = Date.now() - startTime;

return {
documentId,
documentTitle: document.title,
documentVersion: document.version,
signatures: signatures.length,
auditEntries: auditEntries.length,
workflows: workflows.length,
verificationResults,
pdfReport,
generatedAt: new Date(),
generationTimeMs: elapsedTime,
complianceStatus: verificationResults.every(v => v.valid) ? 'COMPLIANT' : 'NON_COMPLIANT'
};
}

10. Cross-Reference to Part 11 Compliance Matrix

10.1 Part 11 Subpart C Mapping

CitationRequirementImplementationStatusEvidenceTest Reference
§11.50 Signature ManifestationsSigned records contain signer name, date/time, meaningSignatureManifestation component displays all three fields on screen, PDF, API✅ Readysrc/components/SignatureManifestation.tsx
src/services/pdf/renderSignature.ts
E2E-SIG-001
E2E-PDF-001
§11.70 Signature/Record LinkingSignatures linked to records, cannot be excised/copied/transferredECDSA-SHA256 cryptographic binding to document hash, verification on every read✅ Readysrc/services/signature/binding.ts
src/middleware/verifySignatures.ts
UNIT-SIG-001
INT-SIG-001
§11.100(a) General Requirements - UniquenessEach signature unique to one individual, not reused/reassignedOne certificate per user, unique constraint in database✅ ReadyDatabase constraint
V012__user_signing_certificates.sql
DB-CONST-001
§11.100(b) General Requirements - Identity VerificationVerify identity before establishing signatureIdentity verification ceremony, Quality Head approval✅ Readysrc/services/identity/verification.ts
docs/procedures/identity-verification-SOP.md
PROC-001
§11.200(a)(1) Signature Components - Biometrics ExemptUse at least two distinct ID components (ID + password)Multi-factor authentication (password + TOTP or biometric)✅ Readysrc/services/auth/mfa.ts
src/services/signature/authContext.ts
UNIT-AUTH-001
E2E-MFA-001
§11.200(a)(2) Signature Components - Re-AuthenticationGenuine owner only, require collaboration for misuseRe-authentication at each signing event, 5-min window✅ Readysrc/services/auth/reauth.ts
src/middleware/signatureGate.ts
E2E-REAUTH-001
§11.200(a)(3) Signature Components - Session ControlExecuted during single period of controlled system accessContinuous vs non-continuous session mode, timeout enforcement✅ Readysrc/services/auth/sessionManagement.tsUNIT-SESSION-001
§11.300 Controls for ID Codes/PasswordsPeriodic checks, loss management, revocation, testingPassword complexity, expiration, MFA enrollment, certificate revocation✅ Readysrc/services/auth/passwordPolicy.ts
src/services/ca/revocation.ts
UNIT-PWD-001
INT-REVOKE-001

10.2 Gap Analysis Summary

RequirementCurrent StatusCompletion %EvidenceRemaining WorkTarget Date
§11.50✅ Complete100%Signature manifestation displayed on all interfacesNone
§11.70✅ Complete100%Cryptographic binding implemented, verification on readNone
§11.100✅ Complete100%Identity verification SOP, unique certificatesNone
§11.200✅ Complete100%MFA, re-authentication, session controlNone
§11.300✅ Complete100%Password policy, certificate lifecycleNone

Overall Compliance: 100% (5/5 requirements complete)

Critical Path: None remaining (D.2.3 complete)


11. Appendices

Appendix A: Signature Meaning Definitions

MeaningFull DefinitionRegulatory ContextExample Use Cases
AUTHORThe individual who created, wrote, or originated the record content. The author is accountable for the technical accuracy and completeness of the record.FDA Part 11 §11.50 manifestation requirement- SOP authorship
- Deviation report creation
- Protocol writing
REVIEWERThe individual who reviewed the record for technical accuracy, completeness, and compliance with applicable regulations and procedures. The reviewer provides independent verification of record quality.FDA Part 11 §11.50 manifestation requirement;
FDA 21 CFR 211.188 (batch record review)
- Peer review of SOPs
- Technical review of validation protocols
- Cross-check of batch records
APPROVERThe individual with authority to approve the record for release, implementation, or use within the organization. Approval signifies authorization and acceptance of accountability.FDA Part 11 §11.50 manifestation requirement;
FDA 21 CFR 211.186 (master production records)
- QA approval of SOPs
- Management approval of CAPAs
- Batch release approval
VERIFIERThe individual who verified the execution or completion of an activity described in the record. Verification confirms that the activity was performed as specified.FDA 21 CFR 211.188 (batch production records)- Verification of cleaning completion
- Confirmation of training completion
- Equipment qualification verification
WITNESSThe individual who witnessed the signing event or activity execution. Witnessing provides independent confirmation for critical operations.FDA 21 CFR 211 (Subpart D) - critical parameter verification- Second person verification
- Critical parameter confirmation
- Batch sampling witness
REJECTORThe individual who formally rejected the record with documented justification. Rejection prevents record release or implementation.FDA 21 CFR 211.192 (deviation investigation)- QA rejection of non-conforming batch
- Rejection of inadequate deviation investigation
- Non-approval of out-of-specification results

Appendix B: Authentication Method Matrix

Auth MethodComponent 1Component 2Component 3 (Optional)Security LevelUse Case
PASSWORD_TOTPUser ID (email)PasswordTOTP 6-digit codeHighStandard users, regulatory workflows
PASSWORD_BIOMETRICUser ID (email)PasswordFingerprint or Face IDHighMobile users, high-frequency signing
SMARTCARD_PINSmart card (PKI)PINVery HighGxP critical operations, auditor access
SSO_REAUTH_MFASSO providerPassword (via SSO)SSO-enforced MFAHighEnterprise SSO deployments

Appendix C: Certificate Profile Examples

User Signing Certificate

Certificate:
Data:
Version: 3 (0x2)
Serial Number:
5a:3f:8e:2c:1d:9b:4f:7a:e3:c5:d8:1f:6b:9a:2e:4c
Signature Algorithm: ecdsa-with-SHA256
Issuer: CN=OrganizationA BIO-QMS Intermediate CA, O=OrganizationA, OU=BIO-QMS, C=US
Validity
Not Before: Feb 16 00:00:00 2026 GMT
Not After : Feb 16 23:59:59 2027 GMT
Subject: CN=Alice Johnson (alice@orgA.bio), O=OrganizationA, OU=BIO-QMS Users, C=US
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:8d:2f:a3:5e:7b:9c:1d:4f:e2:8a:6b:3c:9f:d1:
e5:7a:2b:4c:8e:9f:3d:6a:1c:5e:8b:7d:4f:a2:9e:
3b:5c:1d:8f:6e:2a:9b:4d:7c:3e:5f:1a:8d:2c:6b:
9e:4f:7a:3d:1e:5c:8b:2f:9d:4e:6a:1c:7b:3f:8e:
2d:5a:9c:1f:6b
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Non Repudiation
X509v3 Extended Key Usage:
E-mail Protection
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Subject Alternative Name:
email:alice@orgA.bio
X509v3 Authority Key Identifier:
keyid:9F:3E:7D:2A:5C:1B:8E:4F:6A:9D:3C:5E:8B:2F:1D:7A
X509v3 CRL Distribution Points:
Full Name:
URI:http://crl.bio-qms.coditect.cloud/orgA/user-certs.crl
Authority Information Access:
OCSP - URI:http://ocsp.bio-qms.coditect.cloud/orgA
CA Issuers - URI:http://certs.bio-qms.coditect.cloud/orgA/intermediate-ca.cer
Signature Algorithm: ecdsa-with-SHA256
30:45:02:21:00:e4:b3:c9:f8:a2:d7:e1:b5:c6:a9:f3:d8:b2:
...

Appendix D: Regulatory References

  1. FDA 21 CFR Part 11 - Electronic Records; Electronic Signatures

  2. FDA Guidance: Use of Electronic Records and Electronic Signatures in Clinical Investigations

  3. EU eIDAS Regulation (Regulation EU No 910/2014)

  4. NIST SP 800-63B - Digital Identity Guidelines: Authentication and Lifecycle Management

  5. NIST FIPS 186-5 - Digital Signature Standard (DSS)

DocumentLocationPurpose
E-Signature Architecturedocs/architecture/17-e-signature-architecture.mdTwo-phase signature flow, workflow engine design
Certificate Chain Architecturedocs/compliance/certificate-chain-architecture.md3-tier PKI, certificate profiles, CRL/OCSP
Cryptographic Standards Policydocs/compliance/crypto-standards-policy.mdAlgorithm selection, key sizes, TLS configuration
HSM Integration Architecturedocs/compliance/hsm-integration-architecture.mdKey hierarchy, HSM access control, rotation
Audit Trail Specificationdocs/compliance/audit-trail-specification.mdAudit log integrity chain, retention, reporting
Identity Verification SOPdocs/procedures/identity-verification-SOP.mdStep-by-step identity proofing ceremony
Certificate Lifecycle SOPdocs/procedures/certificate-lifecycle-SOP.mdIssuance, renewal, revocation procedures

Appendix F: Change Log

VersionDateAuthorChangesApproval
1.0.02026-02-16CISO OfficeInitial releaseDraft

Document ID: CODITECT-BIO-ESIG-001 Version: 1.0.0 Classification: Internal - Restricted Next Review Date: 2027-02-16 Policy Owner: Chief Information Security Officer Document Location: docs/compliance/electronic-signature-controls.md Approval Status: Draft (pending executive signature)

Confidentiality Notice: This document contains proprietary information and is intended solely for authorized personnel of CODITECT Biosciences. Unauthorized distribution is prohibited.


END OF DOCUMENT