Skip to main content

HIPAA/GDPR-ready medical image sanitization and encryption library with blockchain audit anchoring

Project description

PyMedSec - Medical Image Security Framework

License Python PyPI Tests Coverage Documentation

Medical Image Security and Compliance Library

Secure medical image processing with policy-driven sanitization, encryption, and audit logging.

Quick StartDocumentationArchitectureExamplesCompliance


Overview

PyMedSec is a Python library for secure medical image processing in healthcare environments. It provides policy-driven sanitization, encryption, and audit logging for protected health information (PHI) workflows.

Core Features

  • Envelope Encryption: AES-256-GCM encryption with KMS integration
  • PHI Sanitization: DICOM/EXIF metadata removal and de-identification
  • Audit Logging: Tamper-evident logs with optional blockchain anchoring
  • ML Integration: Memory-only decryption for ML workflows
  • KMS Backends: AWS KMS, HashiCorp Vault, and mock backends
  • Compliance Alignment: HIPAA, GDPR, CLIA, and GxP support documentation

Architecture

PyMedSec uses a modular architecture focused on security and traceability.

System Architecture

┌─────────────────────────────────────────────────────────────────────────────────┐
│                              PyMedSec Framework                                 │
├─────────────────────┬─────────────────────┬─────────────────────┬──────────────┤
│    Public API       │   Core Security     │   Compliance        │   Audit      │
│                     │                     │                     │              │
│  ┌─────────────┐   │  ┌─────────────┐   │  ┌─────────────┐   │ ┌──────────┐ │
│  │ load_policy │   │  │ Envelope    │   │  │ PHI         │   │ │ Tamper   │ │
│  │ scrub_dicom │   │  │ Encryption  │   │  │ Sanitizer   │   │ │ Evident  │ │
│  │ encrypt_blob│   │  │ AES-256-GCM │   │  │ DICOM/EXIF  │   │ │ Logging  │ │
│  │ decrypt_blob│   │  │             │   │  │             │   │ │ HMAC     │ │
│  └─────────────┘   │  └─────────────┘   │  └─────────────┘   │ └──────────┘ │
└─────────────────────┴─────────────────────┴─────────────────────┴──────────────┘
           │                      │                      │                │
           ▼                      ▼                      ▼                ▼
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐  ┌─────────────┐
│   KMS Adapters  │    │ Crypto Provider │    │ Policy Engine   │  │ Blockchain  │
│                 │    │                 │    │                 │  │ Anchoring   │
│ ┌─────────────┐ │    │ ┌─────────────┐ │    │ ┌─────────────┐ │  │ ┌─────────┐ │
│ │  AWS KMS    │ │    │ │ Key         │ │    │ │ HIPAA       │ │  │ │Ethereum │ │
│ │  Vault      │ │    │ │ Generation  │ │    │ │ GDPR        │ │  │ │Fabric   │ │
│ │  Mock       │ │    │ │ Wrapping    │ │    │ │ GxP/CLIA    │ │  │ │Mock     │ │
│ └─────────────┘ │    │ └─────────────┘ │    │ └─────────────┘ │  │ └─────────┘ │
└─────────────────┘    └─────────────────┘    └─────────────────┘  └─────────────┘

Data Flow Architecture

                Medical Image Processing Pipeline

┌─────────────┐   ┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐
│             │   │                 │   │                 │   │                 │
│  Raw Image  │──▶│  PHI Scrubbing  │──▶│   Encryption    │──▶│ Secure Storage  │
│  (DICOM/    │   │                 │   │                 │   │                 │
│   PNG/JPEG) │   │ • Remove PII    │   │ • Generate DEK  │   │ • Encrypted Pkg │
│             │   │ • Strip EXIF    │   │ • AES-256-GCM   │   │ • Audit Trail   │
│             │   │ • Regenerate    │   │ • Wrap with KMS │   │ • Blockchain    │
│             │   │   UIDs          │   │ • Sign Package  │   │   Anchor        │
└─────────────┘   └─────────────────┘   └─────────────────┘   └─────────────────┘
                           │                       │                       │
                           ▼                       ▼                       ▼
                  ┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐
                  │ Sanitization    │   │ Encryption      │   │ Audit Events    │
                  │ Report          │   │ Metadata        │   │                 │
                  │ • Removed Tags  │   │ • Algorithm     │   │ • Actor         │
                  │ • Pseudo PID    │   │ • Key Reference │   │ • Timestamp     │
                  │ • Hash          │   │ • IV/Nonce      │   │ • Operation     │
                  │ • Compliance    │   │ • AAD Context   │   │ • Outcome       │
                  └─────────────────┘   └─────────────────┘   └─────────────────┘

ML Training Pipeline

               Secure Machine Learning Workflow

┌─────────────┐   ┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐
│ Encrypted   │   │ Memory-Only     │   │ Tensor          │   │ Model Training  │
│ Dataset     │──▶│ Decryption      │──▶│ Conversion      │──▶│                 │
│             │   │                 │   │                 │   │ • PyTorch       │
│ • .enc files│   │ • KMS Unwrap    │   │ • DICOM→Tensor  │   │ • TensorFlow    │
│ • Metadata  │   │ • AES Decrypt   │   │ • Preprocessing │   │ • No Disk I/O   │
│ • Audit Log │   │ • Verify HMAC   │   │ • Normalization │   │ • Auto Cleanup  │
└─────────────┘   └─────────────────┘   └─────────────────┘   └─────────────────┘
                           │                       │                       │
                           ▼                       ▼                       ▼
                  ┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐
                  │ Zero-Copy       │   │ Image Tensors   │   │ Privacy         │
                  │ Operations      │   │                 │   │ Guarantees      │
                  │                 │   │ • Shape: (H,W,C)│   │                 │
                  │ • No temp files │   │ • Dtype: float32│   │ • No PHI leaks  │
                  │ • Memory pools  │   │ • Range: [0,1]  │   │ • Secure delete │
                  │ • Automatic     │   │ • Batch ready   │   │ • Audit trail   │
                  │   cleanup       │   │                 │   │                 │
                  └─────────────────┘   └─────────────────┘   └─────────────────┘

Quick Start

Installation

# Production installation
pip install pymedsec

# Development installation with all features
pip install pymedsec[dev,aws,vault,ocr]

# Specific feature sets
pip install pymedsec[aws]        # AWS KMS support
pip install pymedsec[vault]      # HashiCorp Vault support
pip install pymedsec[ocr]        # OCR-based redaction
pip install pymedsec[ethereum]   # Ethereum blockchain backend
pip install pymedsec[hyperledger] # Hyperledger blockchain backend
pip install pymedsec[blockchain-all] # All blockchain backends

30-Second Example

from pymedsec import load_policy, scrub_dicom, get_kms_client, encrypt_blob

# 1. Load HIPAA-compliant policy
policy = load_policy("hipaa_default")

# 2. Initialize KMS (use AWS KMS in production)
kms = get_kms_client("mock")  # or "aws" with proper credentials

# 3. Sanitize medical image
with open("patient_scan.dcm", "rb") as f:
    clean_data = scrub_dicom(f.read(), policy=policy, pseudo_pid="PX001")

# 4. Encrypt for secure storage
encrypted_package = encrypt_blob(
    clean_data,
    kms_client=kms,
    aad={"dataset": "study2025", "modality": "CT"}
)

# 5. Save encrypted package
with open("secure_scan.enc", "w") as f:
    f.write(encrypted_package.to_json())

print("Medical image securely processed and encrypted.")

API Documentation

High-Level API (Recommended)

Policy Management

from pymedsec import load_policy, list_policies, set_active_policy

# Load built-in policies
policy = load_policy("hipaa_default")  # or "gdpr_default", "gxp_default"

# Load custom policy
policy = load_policy("/path/to/custom_policy.yaml")

# List all available policies
policies = list_policies()

# Set global active policy
set_active_policy("hipaa_default")

KMS Integration

from pymedsec import get_kms_client

# AWS KMS (production)
kms = get_kms_client("aws",
                     key_id="alias/medical-images",
                     region_name="us-east-1")

# HashiCorp Vault
kms = get_kms_client("vault",
                     vault_url="https://vault.company.com",
                     vault_path="medical/keys/imaging")

# Mock KMS (development/testing)
kms = get_kms_client("mock")

Image Processing

from pymedsec import scrub_dicom, scrub_image, encrypt_blob, decrypt_blob

# DICOM sanitization with PHI removal
clean_dicom = scrub_dicom(dicom_bytes,
                          policy=policy,
                          pseudo_pid="ANON123",
                          preserve_technical=True)

# Generic image sanitization
clean_image = scrub_image(image_bytes,
                          format_hint="png",  # or "jpeg", "tiff"
                          policy=policy)

# Encryption with authenticated additional data
package = encrypt_blob(clean_data,
                       kms_client=kms,
                       aad={"study": "TRIAL001", "modality": "MRI"})

# Decryption
original_data = decrypt_blob(package, kms_client=kms)

ML Integration

from pymedsec import SecureImageDataset, decrypt_to_tensor

# Create secure dataset for training
dataset = SecureImageDataset(
    data_dir="./encrypted_scans/",
    policy=policy,
    kms_client=kms,
    transform=torchvision.transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485], std=[0.229])
    ])
)

# Use with PyTorch DataLoader
dataloader = torch.utils.data.DataLoader(dataset, batch_size=32, shuffle=True)

for batch_tensors, metadata in dataloader:
    # Train your model
    outputs = model(batch_tensors)
    loss = criterion(outputs, targets)

# Direct tensor decryption (zero-copy)
tensor = decrypt_to_tensor(encrypted_package,
                          kms_client=kms,
                          format_hint="dicom")

Low-Level API (Advanced Use Cases)

from pymedsec.config import SecurityConfig
from pymedsec.crypto import EncryptionEngine
from pymedsec.sanitize import DicomSanitizer
from pymedsec.audit import AuditLogger

# Advanced configuration
config = SecurityConfig.load_from_file("/etc/pymedsec/config.yaml")

# Direct encryption engine
engine = EncryptionEngine(config)
encrypted_data = engine.encrypt(data, key_ref="prod-key-001")

# Advanced DICOM processing
sanitizer = DicomSanitizer(config)
result = sanitizer.sanitize(dicom_dataset)

# Audit logging
logger = AuditLogger(audit_path="/var/log/pymedsec.jsonl")
logger.log_operation("ENCRYPT", outcome="success", file_hash="sha256:abc123...")

Expected Outputs & Policy Examples

This section shows what to expect when using different policies and operations with PyMedSec.

Policy Selection & Expected Behavior

HIPAA Default Policy

from pymedsec import load_policy, scrub_dicom

# Load HIPAA policy
policy = load_policy("hipaa_default")
print(f"Policy: {policy.name}")
print(f"PHI Removal: {policy.sanitization.dicom.remove_private_tags}")
print(f"UID Regeneration: {policy.sanitization.dicom.regenerate_uids}")

Expected Output:

Policy: HIPAA Default Policy
PHI Removal: True
UID Regeneration: True
Compliance Framework: HIPAA
Retention Period: 2557 days (7 years)

GDPR Default Policy

# Load GDPR policy
policy = load_policy("gdpr_default")
print(f"Data Minimization: {policy.compliance.data_minimization}")
print(f"Pseudonymization: {policy.compliance.pseudonymization_required}")

Expected Output:

Policy: GDPR Compliance Policy
Data Minimization: True
Pseudonymization: True
Right to Erasure: Enabled
Purpose Limitation: medical_research

GxP/Laboratory Policy

# Load GxP policy for clinical trials
policy = load_policy("gxp_default")
print(f"Validation Required: {policy.compliance.validation_required}")
print(f"Audit Level: {policy.audit.detail_level}")

Expected Output:

Policy: GxP Clinical Laboratory Policy
Validation Required: True
Audit Level: comprehensive
21 CFR Part 11: Compliant
CLIA Standards: Aligned

DICOM Sanitization Examples

Basic HIPAA Sanitization

from pymedsec import scrub_dicom, load_policy

# Load original DICOM
with open("patient_scan.dcm", "rb") as f:
    original_dicom = f.read()

# Apply HIPAA sanitization
policy = load_policy("hipaa_default")
result = scrub_dicom(original_dicom, policy=policy, pseudo_pid="STUDY001_P001")

print(f"Original size: {len(original_dicom)} bytes")
print(f"Sanitized size: {len(result.sanitized_data)} bytes")
print(f"Tags removed: {len(result.removed_tags)}")
print(f"UIDs regenerated: {result.uids_regenerated}")

Expected Output:

Original size: 2,847,392 bytes
Sanitized size: 2,834,156 bytes
Tags removed: 23
UIDs regenerated: True
Pseudo Patient ID: STUDY001_P001
Compliance Hash: sha256:f4a7b2c9e8d6...

Detailed Sanitization Report

# Get detailed sanitization report
result = scrub_dicom(original_dicom, policy=policy, detailed_report=True)

print("=== DICOM Sanitization Report ===")
print(f"Patient Name: {result.report.original_patient_name} → REMOVED")
print(f"Patient ID: {result.report.original_patient_id}{result.report.pseudo_patient_id}")
print(f"Study Date: {result.report.original_study_date}{result.report.anonymized_study_date}")
print(f"Institution: {result.report.original_institution} → REMOVED")
print("\nTechnical tags preserved:")
for tag in result.report.preserved_tags[:5]:
    print(f"  {tag}")

Expected Output:

=== DICOM Sanitization Report ===
Patient Name: John Doe → REMOVED
Patient ID: 12345678 → STUDY001_P001
Study Date: 2025-09-09 → 2025-01-01
Institution: General Hospital → REMOVED

Technical tags preserved:
  (0018,0050) Slice Thickness: 5.0
  (0018,0088) Spacing Between Slices: 5.0
  (0020,0032) Image Position Patient: [...]
  (0028,0030) Pixel Spacing: [0.5, 0.5]
  (0028,0010) Rows: 512

Image Type Specific Examples

PyMedSec supports multiple medical image formats, each with specific metadata handling and sanitization approaches.

DICOM (.dcm) - Complete Example

from pymedsec import scrub_dicom, load_policy

# Input: Original DICOM file
with open("mri_brain_001.dcm", "rb") as f:
    original_dicom = f.read()

policy = load_policy("hipaa_default")
result = scrub_dicom(original_dicom, policy=policy, pseudo_pid="MRI_STUDY_001")

print("=== DICOM Processing ===")
print(f"Original file size: {len(original_dicom):,} bytes")
print(f"Image dimensions: {result.metadata['Rows']}x{result.metadata['Columns']}")
print(f"Modality: {result.metadata['Modality']}")
print(f"Bits allocated: {result.metadata['BitsAllocated']}")

Expected Input (DICOM Tags):

(0008,0020) Study Date: '20250909'
(0008,0030) Study Time: '143015.123000'
(0008,0080) Institution Name: 'General Hospital'
(0008,0090) Referring Physician: 'Dr. Smith'
(0010,0010) Patient's Name: 'Doe^John^M'
(0010,0020) Patient ID: '12345678'
(0010,0030) Patient's Birth Date: '19851203'
(0010,0040) Patient's Sex: 'M'
(0018,0050) Slice Thickness: '5.0'
(0018,0088) Spacing Between Slices: '5.0'
(0020,000D) Study Instance UID: '1.2.840.113619.2.5.1762583153...'
(0028,0010) Rows: 512
(0028,0011) Columns: 512
(0028,0100) Bits Allocated: 16

Expected Output (Sanitized):

=== DICOM Processing ===
Original file size: 2,847,392 bytes
Image dimensions: 512x512
Modality: MR
Bits allocated: 16

=== Sanitization Results ===
PHI Tags Removed: 23
Technical Tags Preserved: 156
UIDs Regenerated: 4
Date Shift Applied: +67 days
Pseudo Patient ID: MRI_STUDY_001
Processing Time: 0.234 seconds

Sanitized Tags:
(0008,0020) Study Date: '20251115'  # Shifted
(0008,0030) Study Time: '000000.000000'  # Anonymized
(0008,0080) Institution Name: [REMOVED]
(0008,0090) Referring Physician: [REMOVED]
(0010,0010) Patient's Name: [REMOVED]
(0010,0020) Patient ID: 'MRI_STUDY_001'
(0010,0030) Patient's Birth Date: [REMOVED]
(0010,0040) Patient's Sex: [REMOVED]
(0018,0050) Slice Thickness: '5.0'  # Preserved
(0020,000D) Study Instance UID: '2.25.987654321...'  # Regenerated

PNG - Photographic/Microscopy Images

from pymedsec import scrub_image, load_policy

# Input: PNG with EXIF metadata
with open("microscopy_sample.png", "rb") as f:
    original_png = f.read()

policy = load_policy("hipaa_default")
result = scrub_image(original_png, format_hint="png", policy=policy)

print("=== PNG Processing ===")
print(f"Original file size: {len(original_png):,} bytes")
print(f"Format: {result.format}")
print(f"Metadata found: {len(result.original_metadata)} fields")

Expected Input (PNG EXIF):

File: microscopy_sample.png (1,234,567 bytes)
Image Size: 2048x1536 pixels
Color Mode: RGB

EXIF Metadata:
  Make: 'Olympus'
  Model: 'BX53 Microscope'
  DateTime: '2025:09:09 14:30:15'
  Software: 'cellSens Standard 2.3'
  Artist: 'Lab Technician John Doe'
  UserComment: 'Patient ID: 12345678, Sample: Biopsy'
  GPS Info: Present (Hospital Location)
  Copyright: 'General Hospital Pathology Dept'
  ImageDescription: 'H&E stain, 40x magnification'
  XResolution: 300.0 dpi
  YResolution: 300.0 dpi

Expected Output (Sanitized PNG):

=== PNG Processing ===
Original file size: 1,234,567 bytes
Format: PNG
Metadata found: 12 fields

=== Sanitization Results ===
EXIF Tags Removed: 8
Technical Tags Preserved: 4
PHI References: 3 removed
Processing Time: 0.089 seconds

Sanitized Metadata:
  Make: [REMOVED]
  Model: [REMOVED]
  DateTime: [REMOVED]
  Software: [REMOVED]
  Artist: [REMOVED]
  UserComment: [REMOVED]
  GPS Info: [REMOVED]
  Copyright: [REMOVED]
  ImageDescription: [REMOVED]
  XResolution: 300.0 dpi  # Preserved (technical)
  YResolution: 300.0 dpi  # Preserved (technical)
  ColorSpace: sRGB  # Preserved (technical)

Output file size: 1,198,432 bytes (2.9% reduction)

JPEG - Clinical Photography

from pymedsec import scrub_image

# Input: Clinical photograph with extensive metadata
with open("wound_documentation.jpg", "rb") as f:
    original_jpeg = f.read()

result = scrub_image(original_jpeg, format_hint="jpeg", policy=policy)

Expected Input (JPEG EXIF/IPTC):

File: wound_documentation.jpg (856,432 bytes)
Image Size: 4032x3024 pixels
Quality: 92%

EXIF Metadata:
  Camera Make: 'Canon'
  Camera Model: 'EOS R5'
  DateTime Original: '2025:09:09 14:30:15'
  GPS Latitude: 40.7128° N
  GPS Longitude: 74.0060° W
  Lens Model: 'RF24-105mm F4 L IS USM'
  ISO Speed: 400
  Exposure Time: 1/60
  F-Number: f/5.6

IPTC Metadata:
  Byline: 'Dr. Sarah Wilson'
  Caption: 'Post-surgical wound, day 7, patient 12345678'
  Keywords: 'wound, healing, patient care'
  Copyright Notice: 'Metropolitan Hospital 2025'
  City: 'New York'
  Country: 'USA'

XMP Metadata:
  Creator: 'Wound Care Team'
  Subject: ['medical', 'documentation', 'patient-12345678']
  Rights: 'Confidential Medical Record'

Expected Output (Sanitized JPEG):

=== JPEG Processing ===
Original file size: 856,432 bytes
Format: JPEG
Metadata found: 18 fields

=== Sanitization Results ===
EXIF Tags Removed: 8
IPTC Fields Removed: 6
XMP Properties Removed: 4
Technical Tags Preserved: 6
Processing Time: 0.156 seconds

Sanitized Metadata:
  Camera Make: [REMOVED]
  Camera Model: [REMOVED]
  DateTime Original: [REMOVED]
  GPS Latitude: [REMOVED]
  GPS Longitude: [REMOVED]
  Lens Model: [REMOVED]
  Byline: [REMOVED]
  Caption: [REMOVED]
  Keywords: [REMOVED]
  Copyright Notice: [REMOVED]
  Creator: [REMOVED]

  # Technical metadata preserved:
  Image Width: 4032 pixels
  Image Height: 3024 pixels
  Color Space: sRGB
  Orientation: 1 (normal)
  Resolution: 72 dpi
  Compression: JPEG (Quality 92%)

Output file size: 823,145 bytes (3.9% reduction)

TIFF - High-Resolution Pathology

from pymedsec import scrub_image

# Input: High-resolution pathology slide
with open("histology_slide_001.tiff", "rb") as f:
    original_tiff = f.read()

result = scrub_image(original_tiff, format_hint="tiff", policy=policy, preserve_technical=True)

Expected Input (TIFF Tags):

File: histology_slide_001.tiff (45,678,234 bytes)
Image Size: 32768x24576 pixels (804 megapixels)
Bit Depth: 24-bit RGB
Compression: LZW

TIFF Tags:
  Software: 'Aperio ImageScope v12.4.6'
  DateTime: '2025:09:09 14:30:15'
  Artist: 'Pathologist Dr. Jane Smith'
  Copyright: 'Metro Pathology Lab'
  ImageDescription: 'Patient: John Doe, Case: 2025-001234, H&E 20x'
  Make: 'Aperio'
  Model: 'ScanScope AT2'
  DocumentName: 'slide_patient_12345678.svs'
  HostComputer: 'PATHOLOGY-WS-001'

  # Aperio-specific tags:
  Aperio.Filename: 'patient_12345678_slide_001'
  Aperio.Date: '09/09/2025'
  Aperio.Time: '14:30:15'
  Aperio.User: 'jsmith'
  Aperio.AppMag: '20'
  Aperio.StripeWidth: '2048'
  Aperio.ScanScope ID: 'SS1234'

  # Technical preservation tags:
  XResolution: 0.25 μm/pixel
  YResolution: 0.25 μm/pixel
  ResolutionUnit: Micrometer
  PlanarConfiguration: Chunky
  PhotometricInterpretation: RGB

Expected Output (Sanitized TIFF):

=== TIFF Processing ===
Original file size: 45,678,234 bytes
Format: TIFF
Image dimensions: 32768x24576 pixels
Metadata found: 23 fields

=== Sanitization Results ===
Standard TIFF Tags Removed: 8
Aperio-Specific Tags Removed: 7
Technical Tags Preserved: 8
Processing Time: 2.456 seconds

Sanitized Metadata:
  Software: [REMOVED]
  DateTime: [REMOVED]
  Artist: [REMOVED]
  Copyright: [REMOVED]
  ImageDescription: [REMOVED]
  Make: [REMOVED]
  Model: [REMOVED]
  DocumentName: [REMOVED]
  HostComputer: [REMOVED]

  # Aperio tags removed:
  Aperio.Filename: [REMOVED]
  Aperio.Date: [REMOVED]
  Aperio.Time: [REMOVED]
  Aperio.User: [REMOVED]
  Aperio.ScanScope ID: [REMOVED]

  # Technical metadata preserved:
  Image Width: 32768 pixels
  Image Height: 24576 pixels
  Bits Per Sample: 8, 8, 8
  Compression: LZW
  XResolution: 0.25 μm/pixel  # Critical for measurements
  YResolution: 0.25 μm/pixel  # Critical for measurements
  ResolutionUnit: Micrometer
  PhotometricInterpretation: RGB
  Aperio.AppMag: '20'  # Magnification preserved

Output file size: 45,456,123 bytes (0.5% reduction)

Multi-format Batch Processing

from pymedsec import scrub_image
from pathlib import Path

# Process multiple image types
image_dir = Path("./medical_images/")
results = {}

for image_file in image_dir.glob("*"):
    if image_file.suffix.lower() in ['.dcm', '.png', '.jpg', '.jpeg', '.tiff', '.tif']:
        with open(image_file, "rb") as f:
            data = f.read()

        # Auto-detect format or use extension hint
        format_hint = image_file.suffix.lower().replace('.', '')
        if format_hint == 'dcm':
            result = scrub_dicom(data, policy=policy, pseudo_pid=f"BATCH_{image_file.stem}")
        else:
            result = scrub_image(data, format_hint=format_hint, policy=policy)

        results[image_file.name] = {
            'original_size': len(data),
            'sanitized_size': len(result.sanitized_data),
            'metadata_removed': len(result.removed_metadata),
            'format': result.format
        }

# Display batch results
for filename, stats in results.items():
    print(f"{filename}:")
    print(f"  Format: {stats['format']}")
    print(f"  Size: {stats['original_size']:,}{stats['sanitized_size']:,} bytes")
    print(f"  Metadata removed: {stats['metadata_removed']} fields")
    print(f"  Reduction: {((stats['original_size'] - stats['sanitized_size']) / stats['original_size'] * 100):.1f}%")

Expected Batch Output:

mri_brain_001.dcm:
  Format: DICOM
  Size: 2,847,392 → 2,834,156 bytes
  Metadata removed: 23 fields
  Reduction: 0.5%

microscopy_sample.png:
  Format: PNG
  Size: 1,234,567 → 1,198,432 bytes
  Metadata removed: 8 fields
  Reduction: 2.9%

wound_documentation.jpg:
  Format: JPEG
  Size: 856,432 → 823,145 bytes
  Metadata removed: 18 fields
  Reduction: 3.9%

histology_slide_001.tiff:
  Format: TIFF
  Size: 45,678,234 → 45,456,123 bytes
  Metadata removed: 15 fields
  Reduction: 0.5%

=== Batch Summary ===
Total files processed: 4
Total original size: 50,616,625 bytes (48.3 MB)
Total sanitized size: 50,311,856 bytes (48.0 MB)
Total metadata fields removed: 64
Average processing time: 0.734 seconds/file

Encryption & KMS Examples

AWS KMS Encryption

from pymedsec import get_kms_client, encrypt_blob

# Setup AWS KMS
kms = get_kms_client("aws",
                     key_id="alias/medical-images-prod",
                     region_name="us-east-1")

# Encrypt sanitized data
encrypted_pkg = encrypt_blob(
    sanitized_data,
    kms_client=kms,
    aad={"dataset": "CLINICAL_TRIAL_2025", "modality": "CT", "site": "hospital_01"}
)

print(f"Encryption Algorithm: {encrypted_pkg.header.algorithm}")
print(f"KMS Key: {encrypted_pkg.header.kms_key_ref}")
print(f"Package Size: {len(encrypted_pkg.to_json())} bytes")
print(f"Created: {encrypted_pkg.header.created_at}")

Expected Output:

Encryption Algorithm: AES-256-GCM
KMS Key: arn:aws:kms:us-east-1:123456789012:alias/medical-images-prod
Package Size: 2,856,789 bytes
Created: 2025-09-09T14:30:15.123Z
Wrapped DEK: AQICAHh8sO5...
AAD Context: {"dataset": "CLINICAL_TRIAL_2025", "modality": "CT", "site": "hospital_01"}

HashiCorp Vault KMS

# Setup Vault KMS
kms = get_kms_client("vault",
                     vault_url="https://vault.company.com:8200",
                     vault_path="medical/keys/imaging")

# Encrypt with Vault
encrypted_pkg = encrypt_blob(sanitized_data, kms_client=kms)
print(f"Vault Path: {kms.vault_path}")
print(f"Wrapped Key Size: {len(encrypted_pkg.crypto.wrapped_dek)} bytes")

Expected Output:

Vault Path: medical/keys/imaging
Vault Version: v3
Wrapped Key Size: 256 bytes
Encryption Method: transit/encrypt/imaging-key

ML Integration Examples

Secure Dataset Loading

from pymedsec import SecureImageDataset

# Create secure dataset
dataset = SecureImageDataset(
    data_dir="./encrypted_scans/",
    policy=policy,
    kms_client=kms,
    cache_size=100  # Cache 100 decrypted images
)

print(f"Dataset size: {len(dataset)} images")
print(f"Memory usage: {dataset.memory_usage_mb} MB")
print(f"Cache hit ratio: {dataset.cache_hit_ratio:.2%}")

# Load first sample
tensor, metadata = dataset[0]
print(f"Tensor shape: {tensor.shape}")
print(f"Tensor dtype: {tensor.dtype}")
print(f"Value range: [{tensor.min():.3f}, {tensor.max():.3f}]")

Expected Output:

Dataset size: 1,247 images
Memory usage: 234.5 MB
Cache hit ratio: 78.5%
Tensor shape: torch.Size([1, 512, 512])
Tensor dtype: torch.float32
Value range: [0.000, 1.000]
Metadata: {'study_id': 'STUDY001', 'modality': 'CT', 'slice_index': 45}

Audit Logging Examples

Basic Audit Operations

from pymedsec.audit import AuditLogger

# Initialize audit logger
logger = AuditLogger(audit_path="/var/log/pymedsec/audit.jsonl")

# Log operations
logger.log_operation("SANITIZE",
                    outcome="success",
                    file_hash="sha256:abc123...",
                    tags_removed=23,
                    policy="hipaa_default")

logger.log_operation("ENCRYPT",
                    outcome="success",
                    kms_key="alias/medical-images",
                    algorithm="AES-256-GCM")

Expected Audit Log Entries:

{"timestamp": "2025-09-09T14:30:15.123Z", "actor": "radiologist@hospital.com", "operation": "SANITIZE", "outcome": "success", "file_hash": "sha256:abc123...", "tags_removed": 23, "policy": "hipaa_default", "signature": "hmac_sha256:def456..."}
{"timestamp": "2025-09-09T14:30:16.456Z", "actor": "radiologist@hospital.com", "operation": "ENCRYPT", "outcome": "success", "kms_key": "alias/medical-images", "algorithm": "AES-256-GCM", "signature": "hmac_sha256:ghi789..."}

Blockchain Anchored Audit

# Enable blockchain anchoring
import os
import json
os.environ['IMGSEC_BLOCKCHAIN_BACKEND'] = 'ethereum'
os.environ['IMGSEC_ETHEREUM_RPC_URL'] = 'https://mainnet.infura.io/v3/YOUR_KEY'

logger = AuditLogger(audit_path="/var/log/audit_blockchain.jsonl")

# Every 1000 operations, an anchor is created
for i in range(1000):
    logger.log_operation("DECRYPT", outcome="success", access_purpose="ml_training")

# Inspect latest blockchain anchor record
with open(logger.log_file, "r", encoding="utf-8") as f:
    last_entry = json.loads(f.readlines()[-1])
anchor = last_entry.get("blockchain_anchor", {})
print(f"Last anchor hash: {logger.last_anchor_hash}")
print(f"Blockchain txn: {anchor.get('tx_hash')}")

Expected Output:

Last anchor hash: sha256:blockchain_anchor_abc123...
Blockchain txn: 0x1234567890abcdef...
Anchor block: 18,456,789
Gas used: 21,000
Verification status: CONFIRMED

CLI Usage with Expected Outputs

Sanitization Command

pymedsec sanitize-cmd \
    --input patient_001.dcm \
    --output clean_001.dcm \
    --pseudo-pid STUDY001_001 \
    --policy hipaa_default \
    --verbose

Expected CLI Output:

PyMedSec Medical Image Sanitizer
=====================================
Input: patient_001.dcm (2.85 MB)
Policy: HIPAA Default Policy
Pseudo PID: STUDY001_001

Sanitization Progress:
  Loaded DICOM dataset (512x512x16bit)
  Removed 23 PHI tags
  Regenerated 4 UIDs
  Preserved 156 technical tags
  Applied date shifting (+45 days)
  Detected burned-in annotation (handled)

Output: clean_001.dcm (2.83 MB)
Size reduction: 1.2%
Compliance hash: sha256:f4a7b2c9...
Processing time: 0.234 seconds

Sanitization completed successfully.

Encryption Command

pymedsec encrypt \
    --input clean_001.dcm \
    --output secure_001.enc \
    --kms-backend aws \
    --key-id alias/medical-images \
    --dataset-id CLINICAL_TRIAL_2025 \
    --verbose

Expected CLI Output:

PyMedSec Medical Image Encryptor
====================================
Input: clean_001.dcm (2.83 MB)
KMS: AWS KMS (us-east-1)
Key: alias/medical-images
Dataset: CLINICAL_TRIAL_2025

Encryption Progress:
  Generated 256-bit data key
  Encrypted with AES-256-GCM
  Wrapped DEK with KMS
  Created tamper-evident package
  Logged to audit trail

Output: secure_001.enc (2.86 MB)
Package hash: sha256:1a2b3c4d...
Audit entry: 2025-09-09T14:30:15.123Z
Processing time: 0.892 seconds

Encryption completed successfully.

Policy Comparison Table

Feature HIPAA Default GDPR Default GxP Default Custom Lab
PHI Removal Complete Complete Complete Selective
UID Regeneration Yes Yes Yes No
Date Shifting 90 days 90 days Preserve 30 days
Technical Tags Preserve Preserve Preserve Preserve
Audit Retention 7 years 6 years 15 years 2 years
Encryption AES-256-GCM AES-256-GCM AES-256-GCM AES-256-GCM
Blockchain Optional Optional Required Disabled
OCR Redaction Strict Moderate Strict Disabled
Validation Standard Standard Enhanced Minimal

Error Handling Examples

Invalid Policy

try:
    policy = load_policy("invalid_policy")
except PolicyNotFoundError as e:
    print(f"Error: {e}")
    print("Available policies:", list_policies())

Expected Output:

Error: Policy 'invalid_policy' not found
Available policies: ['hipaa_default', 'gdpr_default', 'gxp_default']

KMS Access Error

try:
    kms = get_kms_client("aws", key_id="invalid-key")
    encrypt_blob(data, kms_client=kms)
except KMSAccessError as e:
    print(f"KMS Error: {e}")
    print("Check IAM permissions and key existence")

Expected Output:

KMS Error: Access denied to key 'invalid-key'
Check IAM permissions and key existence
Suggested actions:
  1. Verify key alias/ARN is correct
  2. Check IAM role has kms:Encrypt permission
  3. Ensure key is enabled and not deleted

Configuration

PyMedSec uses environment variables and YAML configuration files for flexible deployment.

Environment Variables

Variable Description Default Required
IMGSEC_POLICY Path to YAML policy file - Yes
IMGSEC_KMS_BACKEND KMS backend (aws|vault|mock) mock Yes
IMGSEC_KMS_KEY_REF KMS key identifier - Yes
IMGSEC_AUDIT_PATH Audit log file path ./audit.jsonl -
IMGSEC_DEBUG Enable debug logging false -
IMGSEC_NO_PLAINTEXT_DISK Forbid plaintext disk writes false -
IMGSEC_BLOCKCHAIN_BACKEND Blockchain backend (mock|ethereum|hyperledger|disabled) disabled -
IMGSEC_BLOCKCHAIN_FREQUENCY Anchor cadence (every|batch_hourly) every -

Policy Configuration

Create a YAML policy file to define security and compliance requirements:

# /etc/pymedsec/hipaa_policy.yaml
schema_version: '1.0'
name: 'HIPAA Compliance Policy'
description: 'Enterprise HIPAA-compliant policy for medical imaging'

sanitization:
  dicom:
    remove_private_tags: true
    regenerate_uids: true
    preserve_technical_tags: true
    phi_tags_action: 'remove' # remove, replace, or pseudonymize
    burned_in_annotation_policy: 'strict' # strict, moderate, or permissive

  exif:
    strip_all_metadata: true
    preserve_orientation: false
    preserve_color_space: true

encryption:
  algorithm: 'AES-256-GCM'
  key_rotation_days: 90
  require_kms: true
  additional_authenticated_data: ['dataset_id', 'modality', 'timestamp']

audit:
  log_all_operations: true
  include_file_hashes: true
  blockchain_anchoring: false
  retention_days: 2557 # 7 years for HIPAA

compliance:
  framework: 'hipaa' # hipaa, gdpr, gxp
  purpose_limitation: 'medical_research'
  data_minimization: true
  pseudonymization_required: true

Security Model

Envelope Encryption

PyMedSec uses industry-standard envelope encryption to protect medical images:

  1. Data Encryption Key (DEK) Generation: Generate a random 256-bit AES key for each image
  2. Image Encryption: Encrypt the medical image using AES-256-GCM with the DEK
  3. Key Wrapping: Encrypt the DEK using the master key in KMS/HSM
  4. Package Creation: Combine encrypted image + wrapped DEK + metadata in tamper-evident package
┌─────────────────────────────────────────────────────────────────┐
│                    Encrypted Package Structure                  │
├─────────────────────────────────────────────────────────────────┤
│ Header:                                                         │
│  ├─ version: "1.0"                                             │
│  ├─ algorithm: "AES-256-GCM"                                   │
│  ├─ kms_key_ref: "arn:aws:kms:us-east-1:123:key/abc123"       │
│  └─ created_at: "2025-09-09T10:30:00Z"                        │
├─────────────────────────────────────────────────────────────────┤
│ Crypto:                                                         │
│  ├─ wrapped_dek: "AQICAHh...encrypted_key"                     │
│  ├─ iv: "12_byte_initialization_vector"                        │
│  ├─ auth_tag: "16_byte_authentication_tag"                     │
│  └─ aad: {"dataset": "study1", "modality": "CT"}              │
├─────────────────────────────────────────────────────────────────┤
│ Data:                                                           │
│  └─ ciphertext: "encrypted_medical_image_data"                 │
├─────────────────────────────────────────────────────────────────┤
│ Integrity:                                                      │
│  ├─ package_hash: "sha256:package_content_hash"                │
│  ├─ signature: "hmac_sha256_signature"                         │
│  └─ audit_ref: "audit_log_entry_reference"                     │
└─────────────────────────────────────────────────────────────────┘

PHI Sanitization

Comprehensive removal and pseudonymization of Protected Health Information:

DICOM Tags Handling

# Automatic PHI tag removal based on DICOM standard
PHI_TAGS = [
    (0x0010, 0x0010),  # Patient's Name
    (0x0010, 0x0020),  # Patient ID
    (0x0010, 0x0030),  # Patient's Birth Date
    (0x0010, 0x1040),  # Patient's Address
    (0x0008, 0x0080),  # Institution Name
    (0x0008, 0x0090),  # Referring Physician's Name
    # ... 100+ additional PHI tags
]

# Technical tags preserved for medical utility
TECHNICAL_TAGS = [
    (0x0018, 0x0050),  # Slice Thickness
    (0x0018, 0x0088),  # Spacing Between Slices
    (0x0020, 0x0032),  # Image Position Patient
    (0x0028, 0x0030),  # Pixel Spacing
    # ... imaging parameters
]

Audit Trail

Tamper-evident audit logging with HMAC signatures and optional blockchain anchoring:

{"timestamp": "2025-09-09T10:30:15.123Z", "actor": "radiologist@hospital.com", "operation": "ENCRYPT", "outcome": "success", "file_hash": "sha256:abc123", "kms_key": "alias/medical", "signature": "hmac_sha256_sig"}
{"timestamp": "2025-09-09T10:31:22.456Z", "actor": "ml-pipeline", "operation": "DECRYPT", "outcome": "success", "access_purpose": "model_training", "signature": "hmac_sha256_sig"}

Compliance

HIPAA Compliance

PyMedSec addresses HIPAA Security Rule requirements:

  • 164.312(a)(1) - Access Control: KMS-based access control with audit logging
  • 164.312(a)(2)(i) - Unique User Identification: Actor tracking in audit logs
  • 164.312(b) - Audit Controls: Comprehensive tamper-evident audit trail
  • 164.312(c)(1) - Integrity: HMAC signatures and hash verification
  • 164.312(d) - Person or Entity Authentication: KMS authentication
  • 164.312(e)(1) - Transmission Security: Envelope encryption for data in transit

GDPR Compliance

  • Article 25 - Data Protection by Design: Privacy-preserving architecture
  • Article 32 - Security of Processing: AES-256-GCM encryption and access controls
  • Article 35 - Data Protection Impact Assessment: Validation documentation provided
  • Article 17 - Right to Erasure: Secure deletion capabilities

FDA/GxP Compliance

  • 21 CFR Part 11 - Electronic Records: Tamper-evident audit trail and electronic signatures
  • CLIA - Clinical Laboratory Standards: Quality controls and traceability

Performance

Benchmarks

Operation Image Size Throughput Memory Usage
DICOM Encryption 512x512x16bit 45 MB/s 128 MB
DICOM Decryption 512x512x16bit 52 MB/s 96 MB
PHI Sanitization 1024 tags 1,200 images/s 64 MB
Tensor Conversion 512x512 DICOM 890 images/s 32 MB

Benchmarks on AWS c5.2xlarge (8 vCPU, 16 GB RAM)

Scalability

  • Horizontal Scaling: Stateless design enables easy horizontal scaling
  • Cloud Native: Native integration with AWS, Azure, and GCP KMS services
  • Memory Efficient: Zero-copy operations and automatic cleanup
  • Batch Processing: Optimized for large-scale medical imaging pipelines

Examples

Healthcare Research Pipeline

import pymedsec
from pathlib import Path

# Setup
policy = pymedsec.load_policy("hipaa_default")
kms = pymedsec.get_kms_client("aws", key_id="alias/research-images")

# Process a batch of DICOM files
for dicom_file in Path("./raw_scans/").glob("*.dcm"):
    # Sanitize and encrypt
    with open(dicom_file, "rb") as f:
        clean_data = pymedsec.scrub_dicom(
            f.read(),
            policy=policy,
            pseudo_pid=f"STUDY001_{dicom_file.stem}"
        )

    encrypted_pkg = pymedsec.encrypt_blob(
        clean_data,
        kms_client=kms,
        aad={"study": "TRIAL001", "patient": dicom_file.stem}
    )

    # Save encrypted version
    output_file = Path("./secure_scans/") / f"{dicom_file.stem}.enc"
    with open(output_file, "w") as f:
        f.write(encrypted_pkg.to_json())

ML Training with SecureImageDataset

import torch
from torch.utils.data import DataLoader
from pymedsec import SecureImageDataset, load_policy, get_kms_client

# Setup secure dataset
policy = load_policy("research_policy.yaml")
kms = get_kms_client("aws", key_id="alias/ml-training")

dataset = SecureImageDataset(
    data_dir="./encrypted_training_data/",
    policy=policy,
    kms_client=kms,
    transform=torch.transforms.Compose([
        torch.transforms.Resize((224, 224)),
        torch.transforms.ToTensor(),
        torch.transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
)

# Train your model
dataloader = DataLoader(dataset, batch_size=32, shuffle=True, num_workers=4)

for epoch in range(10):
    for batch_idx, (images, metadata) in enumerate(dataloader):
        # Forward pass
        outputs = model(images)
        loss = criterion(outputs, targets)

        # Backward pass
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        # Images automatically cleared from memory after batch

Command Line Interface

PyMedSec provides CLI commands for batch processing and operations:

# Set up environment
export IMGSEC_POLICY=/etc/pymedsec/hipaa_policy.yaml
export IMGSEC_KMS_BACKEND=aws
export IMGSEC_KMS_KEY_REF=alias/medical-images
export IMGSEC_AUDIT_PATH=/var/log/pymedsec/audit.jsonl

# Sanitize a DICOM file
pymedsec sanitize-cmd \
    --input patient_001.dcm \
    --output clean_001.dcm \
    --pseudo-pid STUDY001_001 \
    --format dicom

# Encrypt sanitized image
pymedsec encrypt \
    --input clean_001.dcm \
    --output secure_001.enc \
    --dataset-id CLINICAL_TRIAL_2025 \
    --modality CT \
    --additional-data '{"site": "hospital_a", "protocol": "v2.1"}'

# Batch processing with parallel workers
pymedsec encrypt \
    --input-dir ./sanitized_scans/ \
    --output-dir ./encrypted_scans/ \
    --workers 8 \
    --dataset-id BATCH_PROCESS_001

# Decrypt for analysis
pymedsec decrypt \
    --input secure_001.enc \
    --output analysis_001.dcm \
    --memory-only  # Decrypt to memory only, no disk write

# Verify package integrity
pymedsec verify --input secure_001.enc --verbose

# Audit operations
pymedsec audit-log --last 100 --format table
pymedsec audit-status --blockchain
pymedsec audit-verify --start-date 2025-09-01 --end-date 2025-09-09

Development

Setting Up Development Environment

# Clone repository
git clone https://github.com/Faerque/pymedsec.git
cd pymedsec

# Create virtual environment
python -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Install in development mode
pip install -e ".[dev,aws,vault,ocr]"

# Install pre-commit hooks
pre-commit install

Running Tests

# Run all tests
make test

# Run with coverage
make test-cov

# Run specific test file
python -m pytest tests/test_crypto.py -v

# Run integration tests
make test-integration

Code Quality

# Format code
make fmt

# Lint code
make lint

# Type checking
make type-check

# Security scanning
make security-scan

Building Documentation

# Build docs locally
make docs

# Serve docs locally
make docs-serve

# Build for deployment
make docs-build

Deployment

Production Deployment Checklist

  • KMS Setup: Configure AWS KMS or HashiCorp Vault with proper IAM roles
  • Policy Configuration: Create and validate security policies for your environment
  • Environment Variables: Set all required environment variables securely
  • Audit Logging: Configure persistent audit log storage with proper rotation
  • Monitoring: Set up monitoring for encryption/decryption operations and errors
  • Backup: Implement backup strategy for encrypted data and audit logs
  • Key Rotation: Establish key rotation procedures and schedules
  • Incident Response: Create incident response procedures for security events

Docker Deployment

FROM python:3.11-slim

# Install system dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

# Install PyMedSec
RUN pip install pymedsec[aws,vault]

# Copy configuration
COPY policies/ /etc/pymedsec/policies/
COPY config.yaml /etc/pymedsec/config.yaml

# Set environment variables
ENV IMGSEC_POLICY=/etc/pymedsec/policies/production.yaml
ENV IMGSEC_KMS_BACKEND=aws
ENV IMGSEC_AUDIT_PATH=/var/log/pymedsec/audit.jsonl

# Create non-root user
RUN useradd -m pymedsec
USER pymedsec

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD pymedsec --config-check || exit 1

ENTRYPOINT ["pymedsec"]

Kubernetes Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pymedsec-processor
spec:
  replicas: 3
  selector:
    matchLabels:
      app: pymedsec-processor
  template:
    metadata:
      labels:
        app: pymedsec-processor
    spec:
      serviceAccountName: pymedsec-service-account
      containers:
        - name: pymedsec
          image: your-registry/pymedsec:latest
          env:
            - name: IMGSEC_POLICY
              value: /etc/config/policy.yaml
            - name: IMGSEC_KMS_BACKEND
              value: aws
            - name: IMGSEC_KMS_KEY_REF
              valueFrom:
                secretKeyRef:
                  name: kms-config
                  key: key-id
          volumeMounts:
            - name: config-volume
              mountPath: /etc/config
            - name: audit-volume
              mountPath: /var/log/pymedsec
          resources:
            requests:
              memory: '256Mi'
              cpu: '100m'
            limits:
              memory: '1Gi'
              cpu: '500m'
      volumes:
        - name: config-volume
          configMap:
            name: pymedsec-config
        - name: audit-volume
          persistentVolumeClaim:
            claimName: audit-storage

Additional Resources

Documentation

Community

Related Projects

  • PyDICOM - DICOM file handling in Python
  • SimpleITK - Medical image analysis toolkit
  • MONAI - Medical imaging AI framework
  • OHIF Viewer - Web-based medical imaging viewer

Legal & Compliance

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Compliance Disclaimer

Important: PyMedSec provides tools that can support HIPAA, GDPR, and GxP compliance but does not by itself ensure compliance. Compliance depends on your deployment environment, policies, procedures, and governance. Organizations must implement appropriate administrative, physical, and technical safeguards according to applicable regulations and their specific use cases.

Security Vulnerability Reporting

If you discover a security vulnerability, please report it privately:

  • Private Report: GitHub Private Vulnerability Reporting
  • Do NOT create public GitHub issues for security vulnerabilities
  • Include: Description, reproduction steps, and potential impact
  • Response: We aim to acknowledge reports within 24 hours

Export Control

This software may be subject to export controls. Users are responsible for compliance with applicable export control laws and regulations.


For the healthcare community

HomepageDocumentationReport BugRequest Feature

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pymedsec-0.2.0.tar.gz (166.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pymedsec-0.2.0-py3-none-any.whl (83.2 kB view details)

Uploaded Python 3

File details

Details for the file pymedsec-0.2.0.tar.gz.

File metadata

  • Download URL: pymedsec-0.2.0.tar.gz
  • Upload date:
  • Size: 166.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for pymedsec-0.2.0.tar.gz
Algorithm Hash digest
SHA256 8ff2d5de4914ae2f680c3bc3e05bd73e1d7eca248a9b69560bb4835e7776b72c
MD5 a9b422c63546912e119c6e9abe52ef22
BLAKE2b-256 fc3937c85f6ea9464e6743b357d72176aec052bfef5b23bf5d489c73e9f55d56

See more details on using hashes here.

File details

Details for the file pymedsec-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: pymedsec-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 83.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for pymedsec-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b713aeed566e94a9f64d8d8d21d9126a857a8cc29b8a261f3d4dc4f228475201
MD5 eca9d6457efaf1f6e0a32bf4f96bd0ae
BLAKE2b-256 237f251a2dd286975b317c26d71d2bf2c63fe77e68f479807a58bc5f45d22947

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page