Back to skill

Security audit

MedCrypt: End-to-End Encryption for Medical Messaging

Security checks for vulnerabilities and agentic risk

Overview

This package appears to be a medical encryption demo, but its security and compliance promises are stronger than what the code actually provides.

Review carefully before installing or using with real patient data. This may be acceptable as a cryptography demo, but it should not be treated as production medical messaging, regulatory compliance tooling, durable audit logging, or reliable emergency recovery without redesign and external review.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
medcrypt.py:95
Finding
Patient Identifier Exposed Through Reversible Base64 Encoding<![CDATA[ ## Vulnerability Details **File Location**: `medcrypt.py`, lines 95–125 **Vulnerability Type**: Sensitive medical metadata exposure **Risk Level**: High ### Vulnerable Code ```python def encrypt_message(plaintext: str, key: SecureKey, patient_id: str) -> str: """Encrypt plaintext → MedCrypt wire format. Wire format: [MEDCRYPT:v1:<patient_id>:<nonce_b64>:<ct_b64>:<tag_b64>] - patient_id is base64-encoded to avoid ':' delimiter collisions - nonce: 96-bit CSPRNG (os.urandom) - AAD: raw patient_id bytes (bound to ciphertext via GCM tag) """ if not patient_id: raise ValueError("patient_id must not be empty") nonce = os.urandom(12) # 96-bit CSPRNG nonce aesgcm = AESGCM(key.raw) aad = patient_id.encode("utf-8") ct_with_tag = aesgcm.encrypt(nonce, plaintext.encode("utf-8"), aad) # AESGCM appends 16-byte tag ciphertext = ct_with_tag[:-16] tag = ct_with_tag[-16:] # Base64-encode patient_id to avoid ':' in wire format pid_b64 = base64.b64encode(aad).decode("ascii") return ( f"[{WIRE_PREFIX}:{WIRE_VERSION}:{pid_b64}" f":{base64.b64encode(nonce).decode('ascii')}" f":{base64.b64encode(ciphertext).decode('ascii')}" f":{base64.b64encode(tag).decode('ascii')}]" ) ``` ### Technical Analysis The clinical message body is protected with AES-256-GCM, but `patient_id` is placed directly in the wire envelope using Base64. Base64 only converts binary data into a transport-safe textual representation; it does not provide confidentiality. The patient identifier is also used as authenticated additional data. AES-GCM authenticates AAD but does not encrypt it. Including the Base64 value in the transmitted envelope consequently allows anyone who can observe or obtain the message to recover the identifier without possessing the encryption key. This design exposes medical metadata and enables messages belonging to the same patient to be correlated. It conflicts w ...[truncated 1634 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not include the real patient identifier in the public wire envelope. 2. Encrypt the patient identifier together with the clinical message: ```python payload = json.dumps({ "patient_id": patient_id, "message": plaintext, }).encode("utf-8") ct_with_tag = aesgcm.encrypt(nonce, payload, protocol_aad) ``` 3. If routing requires an external identifier, use a random, unlinkable conversation token that contains no patient information. 4. Rotate or scope routing tokens to prevent long-term correlation. 5. Use only non-sensitive protocol metadata, such as the protocol name and version, as public AAD. 6. Explicitly document all metadata that remains visible to Telegram, WhatsApp, group members, backups, notification services, and other intermediaries. 7. Add tests verifying that neither the patient identifier nor its common encodings appear in the generated wire message. 8. Revise compliance claims until a complete privacy, key-management, access-control, retention, and platform metadata assessment has been performed. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
medcrypt.py:270
Finding
Incorrect GF(256) Inversion Breaks Emergency Key Reconstruction<![CDATA[ ## Vulnerability Details **File Location**: `medcrypt.py`, lines 270–278 **Vulnerability Type**: Defective cryptographic implementation **Risk Level**: High ### Vulnerable Code ```python def _gf256_inv(a: int) -> int: if a == 0: raise ValueError("Cannot invert zero in GF(256)") # Fermat's little theorem: a^(254) = a^(-1) in GF(2^8) result = a for _ in range(6): result = _gf256_mul(result, result) result = _gf256_mul(result, a) return result ``` ### Technical Analysis The comment correctly states that a nonzero GF(256) element can be inverted by raising it to the power 254. The implementation does not calculate that exponent. Starting with `result = a`, every loop iteration squares the current result and then multiplies it by `a`. If the current exponent is `e`, the next exponent is `2e + 1`. After six iterations, the sequence is: ```text 1 → 3 → 7 → 15 → 31 → 63 → 127 ``` The function therefore returns `a^127`, not `a^254`. `recover_key_from_shares()` uses this defective result when computing denominators in Lagrange interpolation. Consequently, valid Shamir shares can produce an incorrect reconstructed key. The defect undermines the documented 2-of-3 emergency break-glass functionality. This is a custom cryptographic implementation without the field-level validation required to establish correctness. ### Attack Path 1. A 32-byte master key is divided into three shares using `create_emergency_shares()`. 2. Direct access to the master key is subsequently lost or intentionally removed. 3. During an emergency, two authorized custodians submit valid shares to `recover_key_from_shares()`. 4. Recovery invokes `_gf256_inv()` while calculating the Lagrange basis. 5. The defective inverse produces incorrect interpolation coefficients. 6. The reconstructed byte sequence differs from the original master key. 7. Data protected by the original key remains unavailable during the emergency. This defect does not r ...[truncated 794 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the custom Shamir implementation with a mature, reviewed secret-sharing library suitable for production cryptographic use. 2. If retaining custom field arithmetic, implement a verified exponentiation routine and calculate `a^254` explicitly: ```python def _gf256_pow(a: int, exponent: int) -> int: result = 1 base = a while exponent: if exponent & 1: result = _gf256_mul(result, base) base = _gf256_mul(base, base) exponent >>= 1 return result def _gf256_inv(a: int) -> int: if a == 0: raise ValueError("Cannot invert zero in GF(256)") return _gf256_pow(a, 254) ``` 3. Add an exhaustive field test for all nonzero byte values: ```python for value in range(1, 256): assert _gf256_mul(value, _gf256_inv(value)) == 1 ``` 4. Test randomized 32-byte keys across every valid threshold combination and verify byte-for-byte reconstruction. 5. Add negative tests for duplicate share coordinates, zero coordinates, malformed share lengths, insufficient shares, and inconsistent shares. 6. Authenticate shares and include version, threshold, share index, and key identifier metadata so corrupted or mixed share sets fail clearly. 7. Do not destroy the original key or rely on this emergency mechanism until recovery has been independently tested using an offline recovery drill. 8. Subject the complete key-recovery design to external cryptographic review before using it for medical records. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (9)

Intent-Code Divergence

High
Confidence
98% confidence
Finding
The documentation advertises a QR-code/PBKDF2 shared-secret key exchange, but the implementation reportedly derives a key from a hardcoded password instead. In a medical-messaging context, this can cause operators and users to rely on a much weaker and materially different trust model than promised, leading to predictable key compromise and exposure of protected health information.

Intent-Code Divergence

High
Confidence
97% confidence
Finding
The documentation promises a 2-of-3 multisig break-glass emergency capability, but no such recovery or emergency-access path exists. In healthcare workflows, this can directly affect safety and compliance because responders may assume emergency access is available when it is not, or administrators may rely on nonexistent controls during incidents.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The threat-model section claims that compromised servers, stolen devices, unauthorized group members, and legal subpoenas are all mitigated, while the code reportedly only performs local encryption. These broad, unsupported assurances are dangerous in a medical-messaging skill because they can cause deployment in high-risk scenarios without the device security, access control, key custody, endpoint hardening, and governance controls actually required.

Credential Access

High
Category
Privilege Escalation
Content
# ─── Key Rotation ───

class KeyRing:
    """Monthly key rotation with deterministic salt per period.

    Salt is derived from HMAC(secret, month) — not raw SHA256 of secret —
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# ─── Key Rotation ───

class KeyRing:
    """Monthly key rotation with deterministic salt per period.

    Salt is derived from HMAC(secret, month) — not raw SHA256 of secret —
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The skill claims monthly key rotation with 7-day backward compatibility, but the code reportedly has no rotation or legacy-key support. This creates a false sense of cryptographic hygiene and extends exposure if a key is compromised, especially where users expect regulated handling of sensitive medical data.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The module advertises an 'encrypted audit log', but the implementation only keeps encrypted entries in an in-memory Python list. That can mislead integrators into believing they have durable, tamper-evident auditability for patient-physician events when the data is actually lost on process exit and can be modified at runtime.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The 'append-only audit trail' claim is contradicted by exposing `self.entries` as a mutable public list, which any caller can clear, reorder, replace, or forge. In a medical messaging context, this weakens accountability and incident investigation because activity records are not tamper resistant.

Natural-Language Policy Violations

Low
Confidence
79% confidence
Finding
This code includes natural-language clinical content entirely in Spanish, which can imply a fixed language/locale behavior. Under the policy rule, locale-specific language should either be optional for the user or clearly documented as a justified regional constraint.

Static analysis

No suspicious patterns detected.