Saltar a contenido

05 - Seguridad del Cliente de MedTime

Identificador: TECH-SEC-CLIENT-001 Version: 1.0.0 Fecha: 2025-12-07 Ultima Revision: Creacion inicial - IT-02 Autor: SecurityDrone (MTS-DRN-SEC-001) + SpecQueen Refs Funcional: MTS-AUTH-001, MTS-PRI-001 Refs Investigacion: INV-001 (Cifrado E2E), INV-008 (Cifrado de Perfil) Estado: Borrador



1. Principios de Seguridad del Cliente

1.1. Zero-Knowledge Architecture

PRINCIPIO ZERO-KNOWLEDGE:
+------------------------------------------------------------------+
|  El servidor MedTime NUNCA tiene acceso al contenido              |
|  de los datos de salud (PHI) del paciente.                         |
|                                                                    |
|  - TODO el cifrado/descifrado ocurre en el CLIENTE                 |
|  - El servidor almacena BLOBS opacos (indescifrable)               |
|  - Solo el paciente tiene la clave de descifrado                   |
|  - En caso de brecha del servidor: datos inutiles                  |
+------------------------------------------------------------------+

1.1.1. Implicaciones Arquitectonicas

Responsabilidad Ubicacion Servidor Puede Ver
Generacion de claves Cliente NO
Cifrado de datos PHI Cliente NO (solo blob)
Descifrado de datos Cliente NO
Almacenamiento de master_key Cliente (Keychain/Keystore) NO
Calculo de blind indexes Cliente Solo el hash
Validacion de tokens Servidor Metadata de sesion

1.1.2. Diagrama de Flujo Zero-Knowledge

sequenceDiagram
    participant U as Usuario
    participant C as Cliente (App)
    participant K as Keychain/Keystore
    participant S as Servidor

    Note over C,S: El servidor NUNCA ve datos PHI en claro

    U->>C: Ingresa datos medicos
    C->>K: Solicita master_key
    K->>K: Verifica biometria/PIN
    K->>C: master_key
    C->>C: Cifrar(datos, master_key) = blob E2E
    C->>S: POST /sync {blob_cifrado}
    S->>S: Almacena blob opaco
    Note over S: Servidor ve: 0x7f8a9b2c...<br/>NO puede descifrar

1.2. Defense in Depth

MedTime implementa multiples capas de seguridad para que la falla de una capa no comprometa todo el sistema.

flowchart TB
    subgraph L5["CAPA 5: SEGURIDAD DE APLICACION"]
        L5A["Autenticacion biometrica/PIN"]
        L5B["Session timeout configurable"]
        L5C["Screen protection (blur)"]
        L5D["Jailbreak/Root detection"]
    end

    subgraph L4["CAPA 4: CIFRADO E2E (APLICACION)"]
        L4A["AES-256-GCM por usuario"]
        L4B["Clave derivada de master_key"]
        L4C["Servidor NO puede descifrar"]
    end

    subgraph L3["CAPA 3: ALMACENAMIENTO SEGURO"]
        L3A["iOS Keychain (Secure Enclave)"]
        L3B["Android Keystore (TEE/StrongBox)"]
        L3C["Datos sensibles SOLO aqui"]
    end

    subgraph L2["CAPA 2: CIFRADO DE DISCO (OS)"]
        L2A["iOS Data Protection"]
        L2B["Android Full Disk Encryption"]
        L2C["Transparente para la app"]
    end

    subgraph L1["CAPA 1: SEGURIDAD DE TRANSPORTE"]
        L1A["TLS 1.3 obligatorio"]
        L1B["Certificate Pinning"]
        L1C["HSTS"]
    end

    L5 --> L4 --> L3 --> L2 --> L1

    style L5 fill:#e8f5e9,stroke:#2e7d32
    style L4 fill:#e3f2fd,stroke:#1565c0
    style L3 fill:#fff3e0,stroke:#e65100
    style L2 fill:#fce4ec,stroke:#c62828
    style L1 fill:#f3e5f5,stroke:#7b1fa2

1.3. Principio de Minimo Privilegio

MINIMO PRIVILEGIO EN CLIENTE:
+------------------------------------------------------------------+

1. PERMISOS DEL SISTEMA OPERATIVO
   - SOLO solicitar permisos necesarios
   - Notificaciones: Requerido para alertas
   - Camara: Solo si usa OCR (Pro/Perfect)
   - Biometria: Para autenticacion local
   - NO solicitar: Ubicacion, Contactos, Microfono

2. ACCESO A DATOS
   - Componentes de UI solo ven datos necesarios
   - Use Cases reciben entidades, no DTOs completos
   - Logs NUNCA contienen PHI

3. ACCESO A CLAVES
   - master_key solo accesible por CryptoService
   - derived_keys tienen scope especifico
   - Claves se liberan de memoria despues de uso

+------------------------------------------------------------------+

1.4. Security by Design

Principio Implementacion en MedTime
Fail Secure Si falla autenticacion, acceso denegado
Secure Defaults Cifrado E2E por defecto (no opt-in)
Complete Mediation Cada request verifica autorizacion
Open Design Algoritmos estandar (AES, Argon2id)
Separation of Privilege Biometria + PIN para operaciones criticas
Least Common Mechanism Componentes aislados, interfaces minimas
Psychological Acceptability UX amigable sin sacrificar seguridad

2. Gestion de Claves Criptograficas

2.1. Jerarquia de Claves

JERARQUIA DE CLAVES DE MEDTIME:
+------------------------------------------------------------------+

                    +------------------+
                    | Recovery Key     |  <-- Usuario guarda offline
                    | (24 palabras     |      (papel/caja fuerte)
                    |  BIP39)          |
                    +--------+---------+
                             |
                             | Puede regenerar
                             v
+----------------+  +------------------+  +----------------+
| PIN/Password   |->| Master Key       |<-| Device Key     |
| (Usuario)      |  | (256 bits)       |  | (Secure Enclave)|
+----------------+  +--------+---------+  +----------------+
                             |
            +----------------+----------------+
            |                |                |
            v                v                v
    +-------+------+ +-------+------+ +-------+------+
    | Data         | | Sync         | | Share        |
    | Encryption   | | Key          | | Key          |
    | Key (DEK)    | |              | |              |
    +-------+------+ +-------+------+ +-------+------+
            |                |                |
            v                v                v
    [Cifrado de     [Cifrado para   [Cifrado para
     datos PHI]      sincronizar]    compartir]

+------------------------------------------------------------------+

2.2. Master Key

La Master Key es la clave raiz de la que se derivan todas las demas claves de cifrado del usuario.

2.2.1. Especificacion Tecnica

Parametro Valor Justificacion
Tamano 256 bits Seguridad maxima AES
Generacion CSPRNG del sistema iOS: SecRandomCopyBytes, Android: SecureRandom
Derivacion Argon2id Resistente a GPU y side-channel
Almacenamiento Keychain/Keystore Hardware-backed cuando disponible

2.2.2. Parametros de Argon2id (NIST SP 800-63B compliant)

ARGON2ID PARAMETERS:
+------------------------------------------------------------------+
| Parametro      | Valor           | Notas                          |
|----------------|-----------------|--------------------------------|
| Memory         | 64 MiB          | Compromiso movil/seguridad     |
| Iterations     | 3               | Factor tiempo                  |
| Parallelism    | 4               | Threads concurrentes           |
| Salt           | 128 bits        | Aleatorio por usuario          |
| Output Length  | 256 bits        | Para AES-256                   |
+------------------------------------------------------------------+

NOTA iOS: Valores > 64 MiB causan warnings en iOS AutoFill
(Referencia: Bitwarden KDF documentation)

2.2.3. Flujo de Generacion de Master Key

flowchart TD
    A[Usuario crea cuenta] --> B[Generar salt aleatorio]
    B --> C[Usuario ingresa password]
    C --> D[Argon2id: password + salt]
    D --> E[master_key = 256 bits]
    E --> F{Secure Enclave disponible?}
    F -->|Si| G[Proteger con Device Key]
    F -->|No| H[Proteger con PIN hash]
    G --> I[Almacenar en Keychain]
    H --> I
    I --> J[Master Key lista]

2.3. Data Encryption Key (DEK)

La DEK se deriva de la Master Key y se usa para cifrar datos PHI especificos.

2.3.1. Derivacion de DEK

DEK DERIVATION (HKDF-SHA256):
+------------------------------------------------------------------+

Input Key Material (IKM):  master_key
Salt:                      Constante por dominio (ej: "medtime-phi-v1")
Info:                      Identificador de dominio + version
Output Length:             256 bits

Dominios de DEK:
- "medtime-phi-medications-v1"    -> DEK para medicamentos
- "medtime-phi-doses-v1"          -> DEK para dosis tomadas
- "medtime-phi-profile-v1"        -> DEK para perfil medico
- "medtime-phi-prescriptions-v1"  -> DEK para recetas
- "medtime-phi-notes-v1"          -> DEK para notas personales

+------------------------------------------------------------------+

2.3.2. Pseudocodigo de Derivacion

# Pseudocodigo - NO usar en produccion sin revision de seguridad
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes

def derive_dek(master_key: bytes, domain: str, version: int = 1) -> bytes:
    """
    Deriva una Data Encryption Key especifica para un dominio.

    Cada dominio tiene su propia clave para aislar el impacto
    si una clave es comprometida.
    """
    info = f"medtime-phi-{domain}-v{version}".encode('utf-8')
    salt = b"medtime-dek-salt-v1"  # Constante por version

    hkdf = HKDF(
        algorithm=hashes.SHA256(),
        length=32,  # 256 bits
        salt=salt,
        info=info
    )

    return hkdf.derive(master_key)

2.4. Recovery Key

La Recovery Key permite recuperar acceso a los datos si el usuario pierde su dispositivo o contrasena.

2.4.1. Especificacion BIP39

RECOVERY KEY SPECIFICATION:
+------------------------------------------------------------------+

Formato:        BIP39 Mnemonic (24 palabras)
Entropia:       256 bits
Wordlist:       English (2048 palabras)
Checksum:       8 bits (SHA-256)
Total:          264 bits encoded -> 24 palabras

Ejemplo (NO USAR):
"abandon ability able about above absent absorb abstract absurd abuse
 access accident account accuse achieve acid acoustic acquire across
 act action actor actress actual"

+------------------------------------------------------------------+

2.4.2. Generacion de Recovery Key

flowchart TD
    A[Usuario activa E2E] --> B[Generar 256 bits entropia]
    B --> C[Calcular checksum SHA-256]
    C --> D[Concatenar entropia + checksum]
    D --> E[Dividir en grupos de 11 bits]
    E --> F[Mapear a palabras BIP39]
    F --> G[Mostrar 24 palabras al usuario]
    G --> H[OBLIGAR confirmacion]
    H --> I{Usuario confirmo?}
    I -->|No| J[Bloquear hasta confirmar]
    I -->|Si| K[Almacenar hash de Recovery Key]
    K --> L[Recovery Key activa]

2.4.3. Verificacion de Backup

VERIFICACION OBLIGATORIA:
+------------------------------------------------------------------+

El usuario DEBE confirmar que guardo la Recovery Key:

1. Mostrar las 24 palabras (una pantalla, sin scroll)
2. Requerir que el usuario confirme haberlas escrito
3. Pedir 3 palabras aleatorias para verificar:
   - "Ingresa la palabra #3: ____"
   - "Ingresa la palabra #11: ____"
   - "Ingresa la palabra #18: ____"
4. Si falla, repetir desde paso 1
5. Mostrar advertencia legal:

   "ADVERTENCIA: Si pierdes tu Recovery Key Y tu password,
    tus datos seran IRRECUPERABLES. MedTime no puede ayudarte."

6. Requerir aceptacion explicita

+------------------------------------------------------------------+

2.5. Key Wrapping

Las claves se protegen ("wrapping") antes de almacenarse.

KEY WRAPPING STRATEGY:
+------------------------------------------------------------------+

NIVEL 1: Master Key Wrapping
+----------------------------------+
| Wrapped with:                     |
| - iOS: Secure Enclave key         |
| - Android: Hardware-backed key    |
|                                   |
| Si no hay hardware:               |
| - Derivar wrapping key de PIN     |
| - AES-256-KWP (RFC 5649)          |
+----------------------------------+

NIVEL 2: DEK Wrapping
+----------------------------------+
| DEKs derivadas en runtime         |
| (No se almacenan directamente)    |
| Master Key -> HKDF -> DEK         |
+----------------------------------+

NIVEL 3: Sync Key Wrapping
+----------------------------------+
| sync_key cifrada con master_key   |
| Se sincroniza al servidor         |
| Permite multi-dispositivo         |
+----------------------------------+

+------------------------------------------------------------------+

2.5.1. Diagrama de Key Wrapping

flowchart LR
    subgraph SECURE_HARDWARE["Hardware Seguro"]
        SE["Secure Enclave Key<br/>(iOS)"]
        SB["StrongBox Key<br/>(Android)"]
    end

    subgraph KEYCHAIN["Keychain/Keystore"]
        MK["Master Key<br/>(wrapped)"]
        RKH["Recovery Key Hash"]
        SK["Sync Key<br/>(wrapped)"]
    end

    subgraph RUNTIME["Runtime (Memoria)"]
        MK_PLAIN["Master Key<br/>(unwrapped)"]
        DEK["DEK<br/>(derivada)"]
    end

    SE --> MK
    SB --> MK
    MK --> MK_PLAIN
    MK_PLAIN --> DEK

2.6. Rotacion de Claves

POLITICA DE ROTACION DE CLAVES:
+------------------------------------------------------------------+

| Clave           | Frecuencia        | Trigger                     |
|-----------------|-------------------|----------------------------|
| Master Key      | Solo si comprometida | Usuario solicita reset   |
| DEKs            | Con Master Key    | Automatico                  |
| Sync Key        | Anual             | Automatico (en sync)        |
| Session Keys    | Por sesion        | Login/Logout                |

PROCESO DE ROTACION DE MASTER KEY:
1. Generar nueva master_key
2. Descifrar todos los datos con old_master_key
3. Re-cifrar con new_master_key
4. Actualizar blobs en servidor (atomico)
5. Invalidar old_master_key
6. Generar nueva Recovery Key
7. OBLIGAR usuario a guardar nueva Recovery Key

NOTA: Rotacion es operacion critica. Implementar con transacciones atomicas.

+------------------------------------------------------------------+

3. Cifrado End-to-End (E2E)

3.1. Algoritmos Criptograficos

ESPECIFICACION CRIPTOGRAFICA MEDTIME:
+------------------------------------------------------------------+

CIFRADO SIMETRICO:
+----------------------------------+
| Algoritmo:     AES-256-GCM       |
| Modo:          Galois/Counter    |
| Tamano clave:  256 bits          |
| Tamano nonce:  96 bits (12 bytes)|
| Tamano tag:    128 bits          |
| Padding:       No requerido (GCM)|
+----------------------------------+

KEY DERIVATION:
+----------------------------------+
| Algoritmo:     Argon2id          |
| Alternativa:   PBKDF2-SHA256     |
| (si Argon2 no disponible)        |
| Iteraciones PBKDF2: 600,000+     |
+----------------------------------+

HASHING:
+----------------------------------+
| Integridad:    SHA-256           |
| Blind Index:   HMAC-SHA256       |
| Password:      Argon2id          |
+----------------------------------+

CUMPLIMIENTO:
- NIST SP 800-132 (Key Derivation)
- NIST SP 800-38D (GCM Mode)
- FIPS 140-2 (AES)
- HIPAA Safe Harbor

+------------------------------------------------------------------+

3.1.1. Comparativa de Algoritmos

Algoritmo Uso en MedTime Alternativa Razon
AES-256-GCM Cifrado datos ChaCha20-Poly1305 Hardware acceleration
Argon2id KDF PBKDF2-SHA256 Resistencia GPU, side-channel
HMAC-SHA256 Blind Index - Estandar, eficiente
SHA-256 Integridad - NIST approved

3.2. Flujo de Cifrado

FLUJO DETALLADO DE CIFRADO:
+------------------------------------------------------------------+

ENTRADA:
- plaintext: Datos en claro (ej: JSON de medicamento)
- entity_id: UUID de la entidad
- entity_type: Tipo de entidad ("medication")

PROCESO:

1. SERIALIZAR
   json_bytes = utf8_encode(JSON.stringify(plaintext))

1.5. APLICAR PADDING (Prevencion de Metadata Attack)
   // Padding a multiplo de 1KB para ocultar tamano real
   padded_bytes = pad_to_block(json_bytes, block_size=1024)
   // Esto previene que atacante infiera cantidad de items por tamano de blob

2. OBTENER CLAVE
   master_key = Keychain.get("master_key")
   dek = HKDF(master_key, salt="medtime-dek-salt", info=entity_type)

3. GENERAR NONCE
   nonce = SecureRandom.generate(96 bits)
   // CRITICO: Nonce DEBE ser unico por operacion

4. PREPARAR AAD (Additional Authenticated Data)
   aad = utf8_encode(entity_id + "|" + entity_type + "|" + version)

5. CIFRAR
   (ciphertext, tag) = AES-256-GCM.encrypt(
       key: dek,
       nonce: nonce,
       plaintext: padded_bytes,  // Usar bytes con padding
       aad: aad
   )

6. EMPAQUETAR
   encrypted_blob = {
       "version": "1.0",
       "nonce": base64(nonce),
       "ciphertext": base64(ciphertext),
       "tag": base64(tag),
       "aad_hash": sha256(aad)  // Para verificacion
   }

7. CALCULAR HASH DE INTEGRIDAD
   blob_hash = SHA-256(serialize(encrypted_blob))

SALIDA:
- encrypted_blob: Blob cifrado (opaco para servidor)
- blob_hash: Hash para verificar integridad

+------------------------------------------------------------------+

3.2.1. Diagrama de Flujo de Cifrado

sequenceDiagram
    participant D as Datos PHI
    participant C as CryptoService
    participant K as Keychain
    participant S as SyncService

    D->>C: Cifrar(medication_data)
    C->>K: Obtener master_key
    K->>K: Verificar autenticacion
    K->>C: master_key
    C->>C: dek = HKDF(master_key, domain)
    C->>C: nonce = SecureRandom(96 bits)
    C->>C: aad = entity_id|type|version
    C->>C: (ciphertext, tag) = AES-GCM(dek, nonce, data, aad)
    C->>C: blob = pack(nonce, ciphertext, tag)
    C->>C: hash = SHA-256(blob)
    C->>S: {encrypted_blob, blob_hash}
    Note over S: Servidor almacena<br/>bytes opacos

3.3. Flujo de Descifrado

FLUJO DETALLADO DE DESCIFRADO:
+------------------------------------------------------------------+

ENTRADA:
- encrypted_blob: Blob cifrado del servidor
- blob_hash: Hash para verificar integridad
- entity_id: UUID de la entidad (para reconstruir AAD)
- entity_type: Tipo de entidad

PROCESO:

1. VERIFICAR INTEGRIDAD
   calculated_hash = SHA-256(serialize(encrypted_blob))
   IF calculated_hash != blob_hash:
       THROW IntegrityError("Blob corrupted or tampered")

2. DESEMPAQUETAR
   nonce = base64_decode(encrypted_blob.nonce)
   ciphertext = base64_decode(encrypted_blob.ciphertext)
   tag = base64_decode(encrypted_blob.tag)
   version = encrypted_blob.version

3. OBTENER CLAVE
   master_key = Keychain.get("master_key")
   dek = HKDF(master_key, salt="medtime-dek-salt", info=entity_type)

4. RECONSTRUIR AAD
   aad = utf8_encode(entity_id + "|" + entity_type + "|" + version)
   IF sha256(aad) != encrypted_blob.aad_hash:
       THROW IntegrityError("AAD mismatch")

5. DESCIFRAR
   plaintext_bytes = AES-256-GCM.decrypt(
       key: dek,
       nonce: nonce,
       ciphertext: ciphertext,
       tag: tag,
       aad: aad
   )
   // Si tag no valida, GCM lanza excepcion automaticamente

6. DESERIALIZAR
   plaintext = JSON.parse(utf8_decode(plaintext_bytes))

SALIDA:
- plaintext: Datos en claro

MANEJO DE ERRORES:
- IntegrityError: Blob corrupto o manipulado -> Reportar, no usar
- AuthenticationError: Tag GCM invalido -> Posible ataque, alertar
- DecryptionError: Clave incorrecta -> Verificar autenticacion

+------------------------------------------------------------------+

3.4. Estructura de Datos Cifrados

{
  "version": "1.0",
  "algorithm": "AES-256-GCM",
  "kdf": "argon2id",
  "nonce": "base64_encoded_96bit_nonce",
  "ciphertext": "base64_encoded_encrypted_data",
  "tag": "base64_encoded_128bit_auth_tag",
  "aad_hash": "sha256_of_additional_authenticated_data",
  "metadata": {
    "created_at": "2025-12-07T10:00:00Z",
    "entity_type": "medication",
    "key_version": 1
  }
}

3.4.1. Campos Obligatorios

Campo Descripcion Tamano
version Version del formato de cifrado String
nonce Valor unico por operacion 96 bits (12 bytes)
ciphertext Datos cifrados Variable
tag Tag de autenticacion GCM 128 bits (16 bytes)
aad_hash Hash de datos autenticados 256 bits (32 bytes)

3.5. Manejo de Nonces

MANEJO SEGURO DE NONCES:
+------------------------------------------------------------------+

REGLA CRITICA:
+----------------------------------+
| NUNCA reutilizar un nonce con    |
| la misma clave. Reutilizacion    |
| permite ataques de recuperacion. |
+----------------------------------+

ESTRATEGIA DE GENERACION:
1. Usar CSPRNG del sistema operativo
   - iOS: SecRandomCopyBytes
   - Android: SecureRandom
2. 96 bits = 2^96 combinaciones
3. Probabilidad de colision negligible para uso normal

PROTECCION ADICIONAL:
- Cada entidad tiene su propia DEK (derivada de master_key + dominio)
- Incluso si nonce se repitiera, diferentes DEKs

LIMITES SEGUROS (NIST SP 800-38D):
- Con 96-bit nonce: max 2^32 invocaciones por clave
- MedTime: max ~1000 sincronizaciones/dia por usuario
- Margen de seguridad: millones de anos

VERIFICACION EN TESTS:
- Unit test: verificar que nonces nunca se repiten en 10,000 llamadas
- Integration test: verificar generacion distribuida

+------------------------------------------------------------------+

3.6. Datos Adicionales Autenticados (AAD)

ADDITIONAL AUTHENTICATED DATA (AAD):
+------------------------------------------------------------------+

AAD permite autenticar datos que no se cifran pero deben ser
verificados como parte del mensaje.

ESTRUCTURA AAD EN MEDTIME:
aad = entity_id | "|" | entity_type | "|" | version

EJEMPLO:
entity_id:   "550e8400-e29b-41d4-a716-446655440000"
entity_type: "medication"
version:     "1.0"
aad:         "550e8400-e29b-41d4-a716-446655440000|medication|1.0"

PROPOSITO:
1. Vincular ciphertext a entidad especifica
2. Prevenir ataques de replay (mover blob a otra entidad)
3. Detectar modificacion de metadata

VERIFICACION:
- Al descifrar, reconstruir AAD y comparar hash
- Si AAD no coincide, blob fue manipulado o movido

+------------------------------------------------------------------+

3.7. Padding de Blobs (Prevencion de Metadata Attack)

PROBLEMA DE METADATA LEAK:
+------------------------------------------------------------------+
| Aunque el contenido este cifrado, el TAMANO del blob puede        |
| revelar informacion:                                               |
| - Blob grande = muchos medicamentos = condicion cronica           |
| - Cambio de tamano = agregado/eliminado medicamento               |
| - Correlacion temporal = patrones de comportamiento               |
+------------------------------------------------------------------+

SOLUCION: PADDING A TAMANO FIJO
+------------------------------------------------------------------+
| Aplicar padding para que todos los blobs tengan tamano            |
| multiplo de un bloque (1KB), ocultando el tamano real.            |
+------------------------------------------------------------------+

IMPLEMENTACION:
+------------------------------------------------------------------+

// Swift (iOS)
func padToBlock(_ data: Data, blockSize: Int = 1024) -> Data {
    let paddingNeeded = blockSize - (data.count % blockSize)
    // Usar PKCS#7-like padding (ultimo byte = cantidad de padding)
    let paddingByte = UInt8(paddingNeeded)
    let padding = Data(repeating: paddingByte, count: paddingNeeded)
    return data + padding
}

func removePadding(_ data: Data) -> Data {
    guard let lastByte = data.last else { return data }
    let paddingLength = Int(lastByte)
    guard paddingLength > 0, paddingLength <= 1024 else { return data }
    return data.prefix(data.count - paddingLength)
}

// Kotlin (Android)
fun padToBlock(data: ByteArray, blockSize: Int = 1024): ByteArray {
    val paddingNeeded = blockSize - (data.size % blockSize)
    val padding = ByteArray(paddingNeeded) { paddingNeeded.toByte() }
    return data + padding
}

fun removePadding(data: ByteArray): ByteArray {
    val paddingLength = data.last().toInt() and 0xFF
    if (paddingLength <= 0 || paddingLength > 1024) return data
    return data.copyOfRange(0, data.size - paddingLength)
}

+------------------------------------------------------------------+

FLUJO CON PADDING:
1. Serializar datos a JSON bytes
2. Aplicar padding a multiplo de 1KB
3. Cifrar con AES-256-GCM
4. Servidor almacena blob de tamano uniforme

AL DESCIFRAR:
1. Descifrar con AES-256-GCM
2. Remover padding (leer ultimo byte para saber cuantos)
3. Deserializar JSON

ENTIDADES QUE REQUIEREN PADDING:
- cli_medications (lista de medicamentos)
- cli_scheduled_alerts (lista de IDs de medicamentos)
- cli_emergency_contacts (lista de contactos)
- Cualquier entidad con listas de tamano variable

+------------------------------------------------------------------+

4. Blind Index para Busqueda Segura

4.1. Proposito del Blind Index

PROBLEMA:
+------------------------------------------------------------------+
| Si el email esta cifrado E2E, el servidor no puede:              |
| - Buscar usuarios por email para login                            |
| - Verificar unicidad de email                                     |
| - Enviar emails de recuperacion (sin saber el email)              |
+------------------------------------------------------------------+

SOLUCION: BLIND INDEX
+------------------------------------------------------------------+
| Hash determinista que permite buscar sin revelar el valor         |
| original. El servidor puede buscar pero no puede leer.            |
+------------------------------------------------------------------+

4.1.1. Analogia

Es como una huella digital:
- Puedes verificar si dos huellas son iguales
- Pero no puedes reconstruir la persona desde la huella

4.2. Implementacion Tecnica

BLIND INDEX SPECIFICATION:
+------------------------------------------------------------------+

ALGORITMO:      HMAC-SHA256
CLAVE:          global_salt (almacenada en HSM del servidor)
INPUT:          Valor normalizado (lowercase, trim)
OUTPUT:         256 bits, truncados a 128 bits para storage

NORMALIZACION:
- Email: lowercase, trim, remove dots in local part (gmail)
- Telefono: solo digitos, sin espacios ni guiones
- Nombre: lowercase, trim, normalizar unicode (NFD)

TRUNCAMIENTO:
- HMAC-SHA256 produce 256 bits
- Almacenar solo primeros 128 bits (32 hex chars)
- Reduce storage sin perder seguridad practica

+------------------------------------------------------------------+

4.2.1. Pseudocodigo

# Pseudocodigo - NO usar en produccion sin revision de seguridad
import hmac
import hashlib

def create_blind_index(value: str, global_salt: bytes, field_type: str) -> str:
    """
    Crea un blind index para busqueda segura.

    Args:
        value: Valor original (email, telefono, etc.)
        global_salt: Salt global (DEBE estar en HSM)
        field_type: Tipo de campo para salt adicional

    Returns:
        Hex string del blind index (32 caracteres)
    """
    # Normalizar segun tipo
    if field_type == "email":
        normalized = normalize_email(value)
    elif field_type == "phone":
        normalized = normalize_phone(value)
    else:
        normalized = value.lower().strip()

    # Agregar salt especifico del campo
    input_data = f"{field_type}:{normalized}".encode('utf-8')

    # HMAC con global_salt
    mac = hmac.new(
        key=global_salt,
        msg=input_data,
        digestmod=hashlib.sha256
    )

    # Truncar a 128 bits (32 hex chars)
    return mac.hexdigest()[:32]


def normalize_email(email: str) -> str:
    """Normaliza email para blind index."""
    email = email.lower().strip()
    # Remover dots en parte local (Gmail ignora dots)
    local, domain = email.split('@')
    if domain in ['gmail.com', 'googlemail.com']:
        local = local.replace('.', '')
    return f"{local}@{domain}"


def normalize_phone(phone: str) -> str:
    """Normaliza telefono a solo digitos."""
    return ''.join(c for c in phone if c.isdigit())

4.3. Casos de Uso

Caso de Uso Campo Blind Index Usado
Login por email email email_blind_index
Verificar unicidad email email_blind_index
SMS para MFA telefono phone_blind_index
Buscar paciente (medico) nombre name_blind_index

4.3.1. Flujo de Login con Blind Index

sequenceDiagram
    participant U as Usuario
    participant C as Cliente
    participant S as Servidor

    U->>C: Ingresar email: "Juan@Example.com"
    C->>C: normalize("Juan@Example.com") = "juan@example.com"
    C->>C: blind_idx = HMAC(global_salt, "email:juan@example.com")
    C->>S: POST /auth/login {blind_index: "7f8a9b2c..."}
    S->>S: SELECT * FROM users WHERE email_blind_index = "7f8a9b2c..."
    S->>C: {user_id, encrypted_challenge}
    Note over S: Servidor encontro usuario<br/>pero NO sabe email real

4.4. Limitaciones y Mitigaciones

LIMITACIONES DE BLIND INDEX:
+------------------------------------------------------------------+

LIMITACION 1: Ataques de Diccionario
- Atacante con global_salt puede probar emails comunes
- MITIGACION:
  * global_salt en HSM (acceso auditado)
  * Rate limiting en busquedas
  * Honeypots con blind indexes falsos

LIMITACION 2: Colisiones
- Dos valores diferentes podrian producir mismo blind index
- MITIGACION:
  * 128 bits tiene colision negligible para millones de usuarios
  * Verificacion adicional en cliente post-match

LIMITACION 3: Busquedas Parciales
- No permite "LIKE '%ejemplo%'" solo match exacto
- MITIGACION:
  * Busquedas complejas se hacen client-side
  * Para catalogos, usar datos no cifrados

PROTECCIONES ADICIONALES:
+----------------------------------+
| - global_salt en HSM             |
| - Rotacion anual de global_salt  |
| - Audit log de cada busqueda     |
| - Rate limit: 10 busquedas/min   |
| - Monitoreo de patrones anomalos |
+----------------------------------+

+------------------------------------------------------------------+

4.5. Estrategia de Rotacion de global_salt (DV2-P3: SEC-BAJO-002)

ROTACION DUAL DE BLIND INDEX KEYS:
+------------------------------------------------------------------+

PROBLEMA:
- Rotar global_salt invalida TODOS los blind indexes existentes
- Re-indexar toda la BD en produccion es operacion de alto riesgo
- Durante rotacion, busquedas pueden fallar

SOLUCION: DUAL KEY SYSTEM
+------------------------------------------------------------------+

FASE 1: ESCRITURA DUAL (Preparacion)
┌─────────────────────────────────────────────────────────────────┐
│ Servidor almacena:                                               │
│   - global_salt_v1 (actual)                                      │
│   - global_salt_v2 (nuevo)                                       │
│                                                                   │
│ Para cada campo buscable:                                        │
│   - email_blind_index_v1 = HMAC(global_salt_v1, email)          │
│   - email_blind_index_v2 = HMAC(global_salt_v2, email)          │
│                                                                   │
│ Cliente recibe AMBAS keys en /auth/login response                │
│ Cliente calcula y envia AMBOS blind indexes en writes            │
│                                                                   │
│ Busquedas usan: WHERE (email_blind_index_v1 = X OR              │
│                        email_blind_index_v2 = Y)                 │
│                                                                   │
│ Duracion: 30 dias (permitir que todos los usuarios activos      │
│           escriban ambos indexes)                                 │
└─────────────────────────────────────────────────────────────────┘

FASE 2: MIGRACION BATCH (Background)
┌─────────────────────────────────────────────────────────────────┐
│ Job batch en servidor:                                           │
│   FOR cada usuario en BD:                                        │
│     IF email_blind_index_v2 IS NULL:                             │
│       # Usuario inactivo, recalcular server-side                 │
│       # IMPOSIBLE - servidor no tiene valor claro               │
│       # SOLUCION: Marcar para re-index en proximo login         │
│       set_flag(user_id, "needs_reindex_v2")                      │
│                                                                   │
│ Durante login de usuario con flag:                               │
│   Cliente recibe global_salt_v2                                  │
│   Cliente calcula blind_index_v2 localmente                      │
│   Cliente envia v2 al servidor                                   │
│   Servidor actualiza email_blind_index_v2                        │
│   Servidor limpia flag                                           │
│                                                                   │
│ Metrica: % de usuarios con v2 populated                          │
│ Goal: 95% coverage antes de FASE 3                               │
└─────────────────────────────────────────────────────────────────┘

FASE 3: CUTOVER (Finalizacion)
┌─────────────────────────────────────────────────────────────────┐
│ Cuando 95%+ usuarios tienen v2:                                  │
│   1. Cambiar busquedas a solo v2                                 │
│      WHERE email_blind_index_v2 = Y                              │
│                                                                   │
│   2. Cliente solo recibe global_salt_v2                          │
│   3. Cliente solo envia v2 en writes                             │
│                                                                   │
│   4. DROP column email_blind_index_v1 (30 dias despues)         │
│   5. Renombrar v2 -> v1 para proximo ciclo                       │
│   6. Archivar global_salt_v1 viejo (NUNCA eliminar)             │
│                                                                   │
│ Rollback plan: Revertir a busqueda dual si issues               │
└─────────────────────────────────────────────────────────────────┘

CALENDARIO DE ROTACION:
+------------------------------------------------------------------+
| Frecuencia:    ANUAL (cada 365 dias)                             |
| Trigger:       Automatico via cron job + manual override         |
| Notificacion:  30 dias antes a Security Team                     |
| Duracion:      90 dias total (30 + 30 + 30)                      |
| Rollback:      Posible hasta FASE 3 completada                   |
+------------------------------------------------------------------+

EJEMPLO DE SCHEMA:
```sql

CREATE TABLE users (
    user_id UUID PRIMARY KEY,

    -- Email fields
    email_encrypted BYTEA NOT NULL,
    email_blind_index_v1 CHAR(32),  -- HMAC truncado a 128 bits
    email_blind_index_v2 CHAR(32),  -- Null hasta rotacion

    -- Phone fields
    phone_encrypted BYTEA,
    phone_blind_index_v1 CHAR(32),
    phone_blind_index_v2 CHAR(32),

    -- Control
    needs_reindex_v2 BOOLEAN DEFAULT FALSE,
    last_login_at TIMESTAMPTZ
);

CREATE INDEX idx_email_v1 ON users(email_blind_index_v1);
CREATE INDEX idx_email_v2 ON users(email_blind_index_v2);

```text

EJEMPLO DE BUSQUEDA DURANTE ROTACION:
```python

def find_user_by_email(email: str) -> Optional[User]:
    # Cliente calcula ambos blind indexes
    blind_v1 = hmac_sha256(global_salt_v1, f"email:{email.lower()}")
    blind_v2 = hmac_sha256(global_salt_v2, f"email:{email.lower()}")

    # Busqueda dual
    query = """
        SELECT * FROM users
        WHERE email_blind_index_v1 = %s
           OR email_blind_index_v2 = %s
        LIMIT 1
    """
    return db.query_one(query, [blind_v1[:32], blind_v2[:32]])
```mermaid

MONITOREO DE ROTACION:
+------------------------------------------------------------------+
| Metrica                        | Threshold  | Accion             |
|--------------------------------|------------|--------------------|
| % usuarios con v2              | < 80% @ D60| Extender FASE 2    |
| Busquedas fallidas             | > 0.1%     | Rollback a dual    |
| Latencia de busqueda           | > +50ms    | Optimizar indexes  |
| Errores de HMAC calculation    | > 0%       | Investigar         |
+------------------------------------------------------------------+

SEGURIDAD:
+------------------------------------------------------------------+
| - Ambos salts SIEMPRE en HSM                                     |
| - Acceso a salts solo via API autenticada                        |
| - Audit log de CADA acceso a global_salt_v1 y v2                 |
| - Cliente NUNCA almacena global_salt (solo en memoria)           |
| - Rotacion NO requiere re-cifrado de datos (solo blind indexes)  |
+------------------------------------------------------------------+

5. Almacenamiento Seguro por Plataforma

5.1. iOS - Keychain Services

iOS KEYCHAIN CONFIGURATION:
+------------------------------------------------------------------+

CLASE DE PROTECCION:
+----------------------------------+
| kSecAttrAccessibleWhenUnlockedThisDeviceOnly                      |
|                                                                    |
| - Datos accesibles solo cuando dispositivo desbloqueado           |
| - Datos NO se incluyen en backup de iCloud                         |
| - Datos eliminados si se borra el dispositivo                      |
+----------------------------------+

SECURE ENCLAVE (si disponible):
+----------------------------------+
| - iPhone 5S+ / iPad Air+         |
| - Claves criptograficas NUNCA    |
|   salen del Secure Enclave       |
| - Operaciones de cifrado dentro  |
|   del hardware                   |
+----------------------------------+

BIOMETRIC PROTECTION:
+----------------------------------+
| kSecAccessControlBiometryCurrentSet                               |
|                                                                    |
| - Requiere autenticacion biometrica para acceder                  |
| - Invalidado si cambia huella/Face ID                             |
+----------------------------------+

+------------------------------------------------------------------+

5.1.1. Estructura de Keychain Items

// Pseudocodigo Swift - Referencia
let query: [String: Any] = [
    kSecClass as String: kSecClassGenericPassword,
    kSecAttrService as String: "com.medtime.crypto",
    kSecAttrAccount as String: "master_key",
    kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
    kSecAttrAccessControl as String: accessControl,
    kSecUseAuthenticationContext as String: authContext,
    kSecValueData as String: masterKeyData
]

5.1.2. Items en Keychain

Item Descripcion Proteccion
master_key Clave maestra cifrada Biometria + SE
recovery_key_hash Hash de Recovery Key Device Only
sync_key Clave de sincronizacion Biometria
session_token Token de sesion actual Device Only

5.2. Android - Keystore System

ANDROID KEYSTORE CONFIGURATION:
+------------------------------------------------------------------+

HARDWARE-BACKED KEYSTORE:
+----------------------------------+
| - Android 9+ con TEE             |
| - Pixel 3+ con Titan M           |
| - Samsung con Knox               |
| - Claves NUNCA exportables       |
+----------------------------------+

STRONGBOX (si disponible):
+----------------------------------+
| setIsStrongBoxBacked(true)       |
|                                  |
| - Chip dedicado (similar a SE)   |
| - Resistente a ataques fisicos   |
| - Pixel 3+, Galaxy S10+          |
+----------------------------------+

BIOMETRIC BINDING:
+----------------------------------+
| setUserAuthenticationRequired(true)                               |
| setUserAuthenticationValidityDurationSeconds(-1)                  |
| setInvalidatedByBiometricEnrollment(true)                        |
|                                                                    |
| - Requiere autenticacion biometrica cada vez                      |
| - Invalidado si cambia huella/face                                |
+----------------------------------+

+------------------------------------------------------------------+

5.2.1. Generacion de Clave en Keystore

// Pseudocodigo Kotlin - Referencia
val keyGenerator = KeyGenerator.getInstance(
    KeyProperties.KEY_ALGORITHM_AES,
    "AndroidKeyStore"
)

val parameterSpec = KeyGenParameterSpec.Builder(
    "medtime_master_key",
    KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).apply {
    setBlockModes(KeyProperties.BLOCK_MODE_GCM)
    setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
    setKeySize(256)
    setUserAuthenticationRequired(true)
    setUserAuthenticationValidityDurationSeconds(-1)
    setInvalidatedByBiometricEnrollment(true)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        setIsStrongBoxBacked(true)
    }
}.build()

keyGenerator.init(parameterSpec)
keyGenerator.generateKey()

5.3. Comparativa de Seguridad

Caracteristica iOS Keychain + SE Android Keystore + StrongBox
Hardware isolation Si (Secure Enclave) Si (TEE/StrongBox)
Key never leaves hardware Si Si
Biometric binding Si Si
Tamper resistance Muy alta Alta (varia por OEM)
Backup protection No backup por defecto Depende configuracion
API consistency Muy alta Varia por Android version

5.4. Fallback y Compatibilidad

FALLBACK STRATEGY:
+------------------------------------------------------------------+

iOS:
+----------------------------------+
| Secure Enclave disponible?       |
|   Si -> Usar SE para master_key  |
|   No -> (no hay iPhone sin SE)   |
|                                  |
| Biometria disponible?            |
|   Si -> Proteger con Face/Touch  |
|   No -> Proteger con passcode    |
+----------------------------------+

Android:
+----------------------------------+
| StrongBox disponible?            |
|   Si -> setIsStrongBoxBacked(true)|
|   No -> Usar TEE (software-backed)|
|                                  |
| Biometria disponible?            |
|   Si -> setUserAuthenticationRequired(true)|
|   No -> Usar PIN/Pattern         |
|                                  |
| Android < 6.0?                   |
|   MedTime no soporta < Android 8 |
+----------------------------------+

MINIMOS REQUERIDOS:
- iOS 15+: Secure Enclave garantizado
- Android 8+: Keystore con TEE disponible

+------------------------------------------------------------------+

6. Batch Encryption Strategy

6.1. Contexto

El cifrado de multiples entidades durante sync inicial o bulk operations requiere una estrategia que:

  • Evite bloquear el thread principal (UI responsiva)
  • Proporcione feedback de progreso al usuario
  • Maneje errores parciales sin perder datos ya procesados

6.2. Configuracion

batch_encryption:
  batch_size: 10                    # Entidades por batch
  parallel_operations: 4            # Operaciones concurrentes dentro del batch
  ui_yield_ms: 16                   # Yield entre batches (~60fps)
  retry_failed: true                # Reintentar items fallidos
  max_retries_per_item: 3           # Maximo reintentos por entidad

6.3. Patron de Implementacion

BATCH ENCRYPTION FLOW:
+------------------------------------------------------------------+

                    INITIAL SYNC (250 entities)
                              |
                              v
              +-------------------------------+
              |     Dividir en batches        |
              |     (25 batches x 10 items)   |
              +-------------------------------+
                              |
                              v
         +--------------------------------------------+
         |           PROCESO POR BATCH                |
         |  +--------------------------------------+  |
         |  |  Batch N (10 entidades)              |  |
         |  |                                      |  |
         |  |  [E1][E2][E3][E4] <- 4 en paralelo   |  |
         |  |  [E5][E6][E7][E8] <- 4 en paralelo   |  |
         |  |  [E9][E10]        <- 2 en paralelo   |  |
         |  +--------------------------------------+  |
         |                    |                       |
         |                    v                       |
         |  +--------------------------------------+  |
         |  |  Progress Callback                   |  |
         |  |  - encrypted: N * 10                 |  |
         |  |  - total: 250                        |  |
         |  |  - percentage: (N*10/250)*100        |  |
         |  +--------------------------------------+  |
         |                    |                       |
         |                    v                       |
         |  +--------------------------------------+  |
         |  |  UI Yield (16ms)                     |  |
         |  |  - Actualizar progress bar           |  |
         |  |  - Procesar eventos UI               |  |
         |  +--------------------------------------+  |
         +--------------------------------------------+
                              |
                              v
              +-------------------------------+
              |     Siguiente batch           |
              |     (loop hasta completar)    |
              +-------------------------------+

+------------------------------------------------------------------+

6.4. Implementacion iOS (Swift)

actor BatchEncryptor {
    private let cryptoManager: CryptoManager
    private let batchSize = 10
    private let parallelism = 4

    func encryptBatch<T: Encryptable>(
        entities: [T],
        progress: @escaping (Int, Int) -> Void
    ) async throws -> [EncryptedBlob] {
        var results: [EncryptedBlob] = []
        results.reserveCapacity(entities.count)

        for (batchIndex, batch) in entities.chunked(into: batchSize).enumerated() {
            // Procesar batch en paralelo (limitado a 4)
            let batchResults = try await withThrowingTaskGroup(of: EncryptedBlob.self) { group in
                for entity in batch {
                    group.addTask {
                        try await self.cryptoManager.encrypt(entity)
                    }
                }

                var batchEncrypted: [EncryptedBlob] = []
                for try await result in group {
                    batchEncrypted.append(result)
                }
                return batchEncrypted
            }

            results.append(contentsOf: batchResults)

            // Progress callback
            let encrypted = (batchIndex + 1) * batchSize
            progress(min(encrypted, entities.count), entities.count)

            // UI yield
            try await Task.sleep(nanoseconds: 16_000_000) // 16ms
        }

        return results
    }
}

6.5. Implementacion Android (Kotlin)

class BatchEncryptor(
    private val cryptoManager: CryptoManager,
    private val dispatcher: CoroutineDispatcher = Dispatchers.Default
) {
    companion object {
        private const val BATCH_SIZE = 10
        private const val PARALLELISM = 4
        private const val UI_YIELD_MS = 16L
    }

    suspend fun <T : Encryptable> encryptBatch(
        entities: List<T>,
        onProgress: (encrypted: Int, total: Int) -> Unit
    ): List<EncryptedBlob> = withContext(dispatcher) {
        val results = mutableListOf<EncryptedBlob>()
        val semaphore = Semaphore(PARALLELISM)

        entities.chunked(BATCH_SIZE).forEachIndexed { batchIndex, batch ->
            val batchResults = batch.map { entity ->
                async {
                    semaphore.withPermit {
                        cryptoManager.encrypt(entity)
                    }
                }
            }.awaitAll()

            results.addAll(batchResults)

            // Progress callback (en Main thread)
            withContext(Dispatchers.Main) {
                val encrypted = minOf((batchIndex + 1) * BATCH_SIZE, entities.size)
                onProgress(encrypted, entities.size)
            }

            // UI yield
            delay(UI_YIELD_MS)
        }

        results
    }
}

6.6. Manejo de Errores

error_handling:
  partial_failure:
    behavior: "Continuar con resto del batch"
    failed_items: "Agregar a retry queue"

  retry_strategy:
    max_attempts: 3
    backoff: exponential
    base_delay_ms: 100

  complete_failure:
    behavior: "Rollback transaccion"
    user_notification: "Error de sincronizacion, reintentar?"

  progress_on_error:
    show_failed_count: true
    allow_skip: false  # No permitir skip de items fallidos

6.7. Metricas de Performance

Escenario Entidades Tiempo Esperado Notas
Sync inicial tipico 50 < 3s Usuario nuevo
Sync inicial maximo 250 < 15s Multi-perfil
Sync incremental 5-10 < 500ms Tipico diario
Bulk import 100 < 6s Importar recetas

7. Autenticacion Local

7.1. Biometria

CONFIGURACION BIOMETRICA:
+------------------------------------------------------------------+

iOS - LOCAL AUTHENTICATION:
+----------------------------------+
| LAContext con:                   |
| - .biometryCurrentSet           |
| - evaluatePolicy(.deviceOwner   |
|   Authentication)               |
|                                  |
| Fallback: Passcode del sistema   |
+----------------------------------+

Android - BIOMETRIC PROMPT:
+----------------------------------+
| BiometricPrompt con:             |
| - BIOMETRIC_STRONG              |
| - setNegativeButtonText para    |
|   fallback a PIN                |
|                                  |
| Clases soportadas:               |
| - Class 3 (Strong)              |
| - Fingerprint, Face (3D)        |
+----------------------------------+

+------------------------------------------------------------------+

7.1.1. Flujo de Autenticacion Biometrica

flowchart TD
    A[Usuario abre app] --> B{Sesion activa?}
    B -->|No| C[Mostrar pantalla de login]
    C --> D[Solicitar biometria]
    D --> E{Biometria exitosa?}
    E -->|Si| F[Desbloquear Keychain]
    E -->|No| G{Intentos < 3?}
    G -->|Si| D
    G -->|No| H[Fallback a PIN]
    H --> I{PIN correcto?}
    I -->|Si| F
    I -->|No| J{Intentos < 5?}
    J -->|Si| H
    J -->|No| K[Bloquear app 30min]
    F --> L[Obtener master_key]
    L --> M[App desbloqueada]

7.2. PIN/Passcode

PIN SECURITY:
+------------------------------------------------------------------+

REQUISITOS MINIMOS:
+----------------------------------+
| - 4 digitos minimo (configurable)|
| - 6 digitos recomendado          |
| - Alfanumerico opcional          |
| - NO patrones simples:           |
|   * 1234, 0000, 1111             |
|   * Fecha nacimiento             |
|   * Ultimos 4 digitos telefono   |
+----------------------------------+

ALMACENAMIENTO:
+----------------------------------+
| pin_hash = Argon2id(             |
|   pin + device_specific_salt,    |
|   memory=64MiB,                  |
|   iterations=3                   |
| )                                |
|                                  |
| Almacenado en Keychain/Keystore  |
+----------------------------------+

PROTECCION ANTI-BRUTE-FORCE:
+----------------------------------+
| Intentos | Delay                 |
|----------|----------------------|
| 1-3      | Sin delay            |
| 4-5      | 30 segundos          |
| 6-7      | 1 minuto             |
| 8-9      | 5 minutos            |
| 10       | 30 minutos           |
| 11+      | Requiere Recovery Key|
+----------------------------------+

+------------------------------------------------------------------+

7.3. Flujo de Autenticacion Local

sequenceDiagram
    participant U as Usuario
    participant A as App
    participant B as BiometricPrompt
    participant K as Keychain/Keystore

    U->>A: Abrir app
    A->>A: Verificar estado de sesion

    alt Sesion expirada
        A->>B: Solicitar biometria
        B->>U: Mostrar prompt biometrico
        U->>B: Autenticar (huella/face)
        B->>K: Desbloquear acceso
        K->>A: master_key accesible
        A->>A: Derivar session_key
        A->>U: App desbloqueada
    else Sesion activa
        A->>U: Acceso directo
    end

7.4. Proteccion contra Ataques

PROTECCIONES DE AUTENTICACION LOCAL:
+------------------------------------------------------------------+

ANTI-BRUTE-FORCE:
- Backoff exponencial despues de 3 intentos
- Lockout temporal despues de 10 intentos
- Wipe opcional despues de 15 intentos (configurable)

ANTI-REPLAY:
- Tokens de sesion tienen timestamp
- Nonce en cada request de autenticacion
- Session binding a device_id

ANTI-TAMPERING:
- Checksum de codigo en runtime (iOS/Android)
- Deteccion de debugging
- Deteccion de instrumentacion (Frida, Xposed)

SCREEN PROTECTION:
- Blur/Cover cuando app va a background
- Prevenir screenshots en pantallas sensibles
- Secure text entry para PIN

+------------------------------------------------------------------+

7.5. Session Management Local

SESSION MANAGEMENT:
+------------------------------------------------------------------+

TIPOS DE SESION:
+----------------------------------+
| 1. App Session                  |
|    - Inicia: Login exitoso      |
|    - Termina: App killed o      |
|               background > timeout|
|                                 |
| 2. Background Session           |
|    - Inicia: App a background   |
|    - Termina: Timeout o foreground|
|    - Timeout: 5 min (config)    |
|                                 |
| 3. Secure Operation Session     |
|    - Inicia: Operacion sensible |
|    - Termina: Operacion completa|
|    - Requiere: Biometria cada vez|
+----------------------------------+

TIMEOUT CONFIGURABLES:
+----------------------------------+
| Setting           | Default     |
|-------------------|-------------|
| background_timeout| 5 minutos   |
| session_max_age   | 24 horas    |
| pin_entry_timeout | 30 segundos |
| biometric_prompt  | 60 segundos |
+----------------------------------+

OPERACIONES QUE REQUIEREN RE-AUTENTICACION:
- Cambiar password/PIN
- Exportar datos
- Compartir con nuevo receptor
- Ver Recovery Key
- Eliminar cuenta

+------------------------------------------------------------------+

8. Proteccion de Datos en Reposo

8.1. Base de Datos Local

DATABASE ENCRYPTION:
+------------------------------------------------------------------+

iOS - CORE DATA:
+----------------------------------+
| Opcion 1: File Protection        |
| - NSFileProtectionComplete       |
| - Cifrado transparente por iOS   |
| - Datos inaccesibles si locked   |
|                                  |
| Opcion 2: SQLCipher (opcional)   |
| - Cifrado a nivel de SQLite      |
| - Clave derivada de master_key   |
| - Proteccion adicional           |
+----------------------------------+

Android - ROOM + SQLCipher:
+----------------------------------+
| SQLCipher for Android            |
| - AES-256-CBC                    |
| - Clave derivada de master_key   |
| - Integrado con Room             |
|                                  |
| Alternativa: EncryptedFile       |
| - Jetpack Security library       |
+----------------------------------+

RECOMENDACION MEDTIME:
- iOS: File Protection (suficiente dado Secure Enclave)
- Android: SQLCipher (TEE menos uniforme)

+------------------------------------------------------------------+

8.1.1. Estructura de Almacenamiento

/app_sandbox/
+------------------------------------------------------------------+
|
+-- databases/
|   +-- medtime.db              # SQLite (cifrado por OS/SQLCipher)
|   +-- medtime.db-wal          # Write-ahead log
|   +-- medtime.db-shm          # Shared memory
|
+-- documents/
|   +-- prescriptions/          # Imagenes de recetas (cifradas E2E)
|   |   +-- rx_uuid1.enc        # Blob AES-256-GCM
|   |   +-- rx_uuid2.enc
|   +-- exports/                # Backups temporales (cifrados)
|       +-- backup_date.enc
|
+-- caches/
|   +-- images/                 # Cache de imagenes (efimero)
|   +-- catalogs/               # Cache de catalogos (publicos)
|
+-- Library/                    # iOS
|   +-- Preferences/            # UserDefaults (NO para PHI)
|
+-- shared_prefs/               # Android
    +-- app_preferences.xml     # NO para PHI
|
+------------------------------------------------------------------+

8.2. Archivos y Cache

FILE ENCRYPTION POLICY:
+------------------------------------------------------------------+

ARCHIVOS PHI (recetas, fotos medicas):
+----------------------------------+
| - Cifrar con AES-256-GCM        |
| - Clave: DEK especifica de media|
| - Almacenar en documents/       |
| - Extension: .enc               |
| - Metadata en DB (nombre, tipo) |
+----------------------------------+

ARCHIVOS TEMPORALES:
+----------------------------------+
| - Usar directorio tmp/ del OS   |
| - Eliminar inmediatamente       |
|   despues de uso                |
| - NUNCA almacenar PHI en tmp    |
+----------------------------------+

CACHE:
+----------------------------------+
| Permitido en cache:              |
| - Imagenes de UI (no PHI)       |
| - Catalogos de medicamentos     |
| - Assets estaticos              |
|                                 |
| PROHIBIDO en cache:             |
| - Cualquier dato PHI            |
| - Tokens de sesion              |
| - Claves criptograficas         |
+----------------------------------+

+------------------------------------------------------------------+

8.3. Clasificacion de Datos

CLASIFICACION DE ALMACENAMIENTO:
+------------------------------------------------------------------+

| Dato                  | Ubicacion          | Cifrado           |
|-----------------------|--------------------|-------------------|
| master_key            | Keychain/Keystore  | Hardware (SE/TEE) |
| session_token         | Keychain/Keystore  | Hardware          |
| pin_hash              | Keychain/Keystore  | Hardware          |
| recovery_key_hash     | Keychain/Keystore  | Hardware          |
| datos_phi (DB)        | SQLite             | E2E + Disk        |
| imagenes_recetas      | Documents/         | E2E + Disk        |
| preferencias_no_phi   | UserDefaults/SP    | Disk (OS)         |
| catalogos_publicos    | Cache/             | Sin cifrar        |
| logs_anonimos         | Logs/              | Disk (OS)         |

REGLA GENERAL:
- PHI -> Cifrado E2E + Disk encryption
- Claves -> Hardware security
- Publico -> Disk encryption del OS es suficiente

+------------------------------------------------------------------+

8.4. Borrado Seguro

SECURE DELETE POLICY:
+------------------------------------------------------------------+

AL CERRAR SESION:
+----------------------------------+
| - Limpiar session_token         |
| - Limpiar caches de datos PHI   |
| - Mantener: master_key (wrapped)|
| - Mantener: preferencias        |
+----------------------------------+

AL ELIMINAR CUENTA:
+----------------------------------+
| 1. Solicitar confirmacion 2x    |
| 2. Requerir Recovery Key        |
| 3. Enviar DELETE al servidor    |
| 4. Eliminar todos los archivos: |
|    - Keychain items             |
|    - Base de datos              |
|    - Archivos de documentos     |
|    - Caches                     |
| 5. Sobrescribir con zeros       |
|    (donde sea posible)          |
| 6. Reinstalar app desde store   |
|    para borrado completo        |
+----------------------------------+

iOS SECURE DELETE:
- SecItemDelete para Keychain
- FileManager.removeItem
- NSFileManager.replaceItemAtURL

Android SECURE DELETE:
- keystore.deleteEntry()
- context.deleteDatabase()
- File.delete() + sanitize

+------------------------------------------------------------------+

9. Proteccion de Datos en Transito

9.1. TLS 1.3

TLS CONFIGURATION:
+------------------------------------------------------------------+

REQUISITOS MINIMOS:
+----------------------------------+
| Protocolo:    TLS 1.3 obligatorio|
| Fallback:     TLS 1.2 (temporal) |
| NO soportado: TLS 1.1, 1.0, SSL  |
+----------------------------------+

CIPHER SUITES (orden de preferencia):
+----------------------------------+
| 1. TLS_AES_256_GCM_SHA384        |
| 2. TLS_CHACHA20_POLY1305_SHA256  |
| 3. TLS_AES_128_GCM_SHA256        |
+----------------------------------+

KEY EXCHANGE:
+----------------------------------+
| - X25519 (preferido)             |
| - secp256r1 (P-256)              |
| - secp384r1 (P-384)              |
+----------------------------------+

HEADERS DE SEGURIDAD:
+----------------------------------+
| Strict-Transport-Security:       |
|   max-age=31536000;              |
|   includeSubDomains              |
|                                  |
| X-Content-Type-Options: nosniff  |
| X-Frame-Options: DENY            |
+----------------------------------+

+------------------------------------------------------------------+

9.2. Certificate Pinning

CERTIFICATE PINNING:
+------------------------------------------------------------------+

ESTRATEGIA:
+----------------------------------+
| Pin Type:     SPKI (Subject Public Key Info)                      |
| Algoritmo:    SHA-256                                             |
| Pins:         2 minimo (primary + backup)                         |
| Rotacion:     Anual, con overlap de 30 dias                       |
+----------------------------------+

iOS - NETWORK SECURITY:
+----------------------------------+
| Info.plist:                      |
| <key>NSAppTransportSecurity</key>|
| <dict>                           |
|   <key>NSPinnedDomains</key>     |
|   <dict>                         |
|     <key>api.medtime.com</key>   |
|     ...                          |
|   </dict>                        |
| </dict>                          |
+----------------------------------+

Android - NETWORK SECURITY CONFIG:
+----------------------------------+
| res/xml/network_security_config: |
| <network-security-config>        |
|   <domain-config>                |
|     <domain>api.medtime.com</domain>|
|     <pin-set>                    |
|       <pin digest="SHA-256">     |
|         base64_encoded_hash      |
|       </pin>                     |
|     </pin-set>                   |
|   </domain-config>               |
| </network-security-config>       |
+----------------------------------+

DOMINIOS A PINEAR:
- api.medtime.com (API principal)
- auth.medtime.com (autenticacion)
- sync.medtime.com (sincronizacion)

+------------------------------------------------------------------+

9.2.1. SPKI Pins por Dominio (DV2-REMEDIACION)

IMPORTANTE: Los pins a continuacion son PLACEHOLDERS. Generar pins reales antes de produccion usando:

openssl s_client -connect domain:443 | openssl x509 -pubkey | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

Certificate Pins (PLACEHOLDERS - GENERAR ANTES DE PROD):

  api.medtime.app:
    description: "API principal"
    primary_pin: "sha256/AAAA_PLACEHOLDER_GENERATE_BEFORE_PRODUCTION_AAAA"
    backup_pin: "sha256/BBBB_PLACEHOLDER_BACKUP_CA_ROOT_BBBB"
    expiry_date: "2026-01-01"
    note: "Primary = leaf cert, Backup = CA intermediate"

  sync.medtime.app:
    description: "Sincronizacion de blobs"
    primary_pin: "sha256/CCCC_PLACEHOLDER_GENERATE_BEFORE_PRODUCTION_CCCC"
    backup_pin: "sha256/DDDD_PLACEHOLDER_BACKUP_CA_ROOT_DDDD"
    expiry_date: "2026-01-01"

  auth.medtime.app:
    description: "Autenticacion Firebase"
    primary_pin: "sha256/EEEE_PLACEHOLDER_GENERATE_BEFORE_PRODUCTION_EEEE"
    backup_pin: "sha256/FFFF_PLACEHOLDER_BACKUP_CA_ROOT_FFFF"
    expiry_date: "2026-01-01"
    note: "Puede requerir pins de Firebase/Google"

Pin Validation:
  on_failure: BLOCK_REQUEST
  fallback: none  # No fallback, seguridad primero
  user_message: "Connection security error. Please try again later."
  log_event: security_pin_failure (sin PHI)

Pin Update Mechanism:
  method: Remote Config (Firebase)
  update_check: On app start
  grace_period: 24h  # Usar pins cached si remote falla
  emergency_bypass: false  # NUNCA bypass pins

Rotation Schedule:
  frequency: Annual
  overlap_period: 30 days  # Ambos pins validos durante transicion
  notification: 60 days antes de expiracion
  responsible: Security Team

9.2.2. Implementacion iOS

// Info.plist - NSAppTransportSecurity
/*
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSPinnedDomains</key>
    <dict>
        <key>api.medtime.app</key>
        <dict>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSPinnedLeafIdentities</key>
            <array>
                <dict>
                    <key>SPKI-SHA256-BASE64</key>
                    <string>AAAA_PLACEHOLDER_PRIMARY</string>
                </dict>
                <dict>
                    <key>SPKI-SHA256-BASE64</key>
                    <string>BBBB_PLACEHOLDER_BACKUP</string>
                </dict>
            </array>
        </dict>
        <!-- Repetir para sync.medtime.app, auth.medtime.app -->
    </dict>
</dict>
*/

// TrustKit alternative for dynamic pins
class CertificatePinningManager {
    static let shared = CertificatePinningManager()

    func configurePinning() {
        let config: [String: Any] = [
            kTSKSwizzleNetworkDelegates: true,
            kTSKPinnedDomains: [
                "api.medtime.app": [
                    kTSKEnforcePinning: true,
                    kTSKPublicKeyHashes: [
                        "AAAA_PLACEHOLDER_PRIMARY",
                        "BBBB_PLACEHOLDER_BACKUP"
                    ]
                ]
            ]
        ]
        TrustKit.initSharedInstance(withConfiguration: config)
    }
}

9.2.3. Implementacion Android

<!-- res/xml/network_security_config.xml -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">api.medtime.app</domain>
        <pin-set expiration="2026-01-01">
            <pin digest="SHA-256">AAAA_PLACEHOLDER_PRIMARY</pin>
            <pin digest="SHA-256">BBBB_PLACEHOLDER_BACKUP</pin>
        </pin-set>
    </domain-config>

    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">sync.medtime.app</domain>
        <pin-set expiration="2026-01-01">
            <pin digest="SHA-256">CCCC_PLACEHOLDER_PRIMARY</pin>
            <pin digest="SHA-256">DDDD_PLACEHOLDER_BACKUP</pin>
        </pin-set>
    </domain-config>

    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">auth.medtime.app</domain>
        <pin-set expiration="2026-01-01">
            <pin digest="SHA-256">EEEE_PLACEHOLDER_PRIMARY</pin>
            <pin digest="SHA-256">FFFF_PLACEHOLDER_BACKUP</pin>
        </pin-set>
    </domain-config>
</network-security-config>
// OkHttp CertificatePinner (alternative for dynamic pins)
val certificatePinner = CertificatePinner.Builder()
    .add("api.medtime.app", "sha256/AAAA_PLACEHOLDER_PRIMARY")
    .add("api.medtime.app", "sha256/BBBB_PLACEHOLDER_BACKUP")
    .add("sync.medtime.app", "sha256/CCCC_PLACEHOLDER_PRIMARY")
    .add("sync.medtime.app", "sha256/DDDD_PLACEHOLDER_BACKUP")
    .build()

val okHttpClient = OkHttpClient.Builder()
    .certificatePinner(certificatePinner)
    .build()

9.2.4. Procedimiento de Rotacion de Certificate Pins (DV2-P2)

PROCEDIMIENTO DE ROTACION DE CERTIFICATE PINS:
+------------------------------------------------------------------+

TIMELINE DE ROTACION:
+----------------------------------+
| T-60 dias | Generar nuevos certificados                          |
| T-45 dias | Agregar nuevo pin como backup en app update          |
| T-30 dias | Notificar usuarios de actualizacion requerida        |
| T-15 dias | Activar nuevo certificado en servidor                |
| T-0       | Nuevo pin es primario, viejo es backup               |
| T+30 dias | Remover pin viejo de la app                          |
+----------------------------------+

FASE 1: PREPARACION (T-60 a T-45)
1. Generar nuevo certificado:
   $ openssl req -new -x509 -days 365 -key server.key -out new_cert.pem

2. Extraer SPKI pin del nuevo certificado:
   $ openssl x509 -in new_cert.pem -pubkey -noout | \
     openssl pkey -pubin -outform der | \
     openssl dgst -sha256 -binary | \
     openssl enc -base64

3. Documentar nuevo pin en este documento
4. Crear PR para agregar pin a apps

FASE 2: TRANSICION (T-45 a T-15)
1. Release app con nuevo pin como BACKUP:
   - iOS: Agregar a NSPinnedLeafIdentities array
   - Android: Agregar a <pin-set>
   - TrustKit/OkHttp: Agregar a lista de pins

2. Monitorear adopcion:
   - Target: >90% usuarios en version con nuevo pin
   - Alertar si adopcion < 70% a T-20

3. Opcional: Push notification recordando actualizar app

FASE 3: ACTIVACION (T-15 a T-0)
1. Instalar nuevo certificado en servidores:
   - api.medtime.app
   - sync.medtime.app
   - auth.medtime.app

2. Verificar conectividad con ambos pins
3. Monitorear errores de conexion
4. Rollback plan: revertir a certificado viejo si >1% errores

FASE 4: LIMPIEZA (T+0 a T+30)
1. Nuevo certificado es primario
2. Certificado viejo sigue valido (backup durante transicion)
3. Release app removiendo pin viejo
4. Actualizar documentacion con nuevas fechas

+------------------------------------------------------------------+

Checklist de Rotacion:

Paso Responsable Verificacion Fecha
[ ] Generar nuevo certificado DevOps SHA256 pin documentado
[ ] PR con nuevo pin (backup) Mobile Dev Tests pasan
[ ] Release iOS Mobile Dev App Store aprobado
[ ] Release Android Mobile Dev Play Store aprobado
[ ] Verificar adopcion >90% DevOps Analytics dashboard
[ ] Instalar cert en servers DevOps Health checks OK
[ ] Monitorear 48h DevOps Error rate <0.1%
[ ] PR removiendo pin viejo Mobile Dev Tests pasan
[ ] Documentar rotacion Security Este doc actualizado

Comando para Generar Pin de Certificado Existente:

# Para certificado en archivo
openssl x509 -in cert.pem -pubkey -noout | \
  openssl pkey -pubin -outform der | \
  openssl dgst -sha256 -binary | \
  openssl enc -base64

# Para certificado de servidor remoto
openssl s_client -connect api.medtime.app:443 2>/dev/null | \
  openssl x509 -pubkey -noout | \
  openssl pkey -pubin -outform der | \
  openssl dgst -sha256 -binary | \
  openssl enc -base64

Registro de Rotaciones:

Fecha Dominio Pin Viejo (ultimos 8 chars) Pin Nuevo (ultimos 8 chars) Responsable
2025-01-01 api.medtime.app (inicial) AAAA...AAAA -
proxima - - - -

9.3. Doble Cifrado (TLS + E2E)

DOBLE CIFRADO EN TRANSITO:
+------------------------------------------------------------------+

CAPA 1: TLS 1.3 (Transporte)
+----------------------------------+
| - Protege contra MITM           |
| - Cifrado punto a punto         |
| - Servidor VE datos dentro      |
+----------------------------------+
           |
           v
CAPA 2: E2E (Aplicacion)
+----------------------------------+
| - Datos ya cifrados antes de TLS|
| - Servidor NO puede descifrar   |
| - Solo usuario tiene clave      |
+----------------------------------+

FLUJO:
1. Cliente cifra datos PHI con AES-256-GCM (E2E)
2. Cliente envia blob E2E por TLS 1.3
3. TLS descifrado en servidor
4. Servidor ve: blob E2E (opaco)
5. Servidor almacena blob
6. Respuesta por TLS 1.3

BENEFICIO:
- Si TLS comprometido: atacante ve blobs opacos
- Si servidor comprometido: atacante ve blobs opacos
- Solo cliente con master_key puede descifrar

+------------------------------------------------------------------+

9.4. Proteccion contra MITM

PROTECCIONES MITM:
+------------------------------------------------------------------+

1. CERTIFICATE PINNING
   - Previene certificados fraudulentos
   - Detecta proxies corporativos

2. CERTIFICATE TRANSPARENCY
   - Verificar CT logs
   - Detectar certificados emitidos sin autorizacion

3. DETECCION DE PROXY
   - Warning si proxy detectado
   - Opcion de continuar (para debug)
   - Bloquear en produccion (configurable)

4. DEVICE ATTESTATION
   - iOS: App Attest
   - Android: Play Integrity API
   - Verificar que app es legitima

5. RESPONSE VALIDATION
   - Verificar firma de respuestas
   - Detectar respuestas modificadas

COMPORTAMIENTO EN DETECCION:
+----------------------------------+
| Proxy/MITM detectado:            |
| 1. Log evento de seguridad       |
| 2. Mostrar warning al usuario    |
| 3. Bloquear operaciones PHI      |
| 4. Permitir continuar (debug)    |
| 5. Reportar a servidor (metadata)|
+----------------------------------+

+------------------------------------------------------------------+

10. Proteccion contra Ataques Comunes

10.1. OWASP Mobile Top 10

OWASP MOBILE TOP 10 (2024) - ESTADO MEDTIME:
+------------------------------------------------------------------+

| #  | Vulnerabilidad                    | Estado    | Mitigacion   |
|----|-----------------------------------|-----------|--------------|
| M1 | Improper Platform Usage           | MITIGADO  | Ver 9.2      |
| M2 | Insecure Data Storage             | MITIGADO  | Seccion 7    |
| M3 | Insecure Communication            | MITIGADO  | Seccion 8    |
| M4 | Insecure Authentication           | MITIGADO  | Seccion 6    |
| M5 | Insufficient Cryptography         | MITIGADO  | Seccion 3    |
| M6 | Insecure Authorization            | MITIGADO  | Ver 9.2      |
| M7 | Client Code Quality               | MITIGADO  | Ver 9.2      |
| M8 | Code Tampering                    | MITIGADO  | Seccion 9.4  |
| M9 | Reverse Engineering               | PARCIAL   | Ver 9.2      |
| M10| Extraneous Functionality          | MITIGADO  | Ver 9.2      |

+------------------------------------------------------------------+

10.2. Protecciones Implementadas

PROTECCIONES POR CATEGORIA:
+------------------------------------------------------------------+

M1 - IMPROPER PLATFORM USAGE:
+----------------------------------+
| - Usar APIs de seguridad del OS |
| - No reinventar Keychain/Keystore|
| - Seguir guidelines de Apple/Google|
| - Permisos minimos necesarios   |
+----------------------------------+

M6 - INSECURE AUTHORIZATION:
+----------------------------------+
| - Verificar permisos en cada request|
| - No confiar en datos del cliente|
| - Server-side authorization     |
| - Role-based access control     |
+----------------------------------+

M7 - CLIENT CODE QUALITY:
+----------------------------------+
| - Static analysis (SonarQube)   |
| - Dependency scanning           |
| - Code review obligatorio       |
| - Unit tests de crypto          |
+----------------------------------+

M9 - REVERSE ENGINEERING:
+----------------------------------+
| - Obfuscacion de codigo         |
| - iOS: Bitcode enabled          |
| - Android: ProGuard/R8          |
| - Claves NUNCA hardcodeadas     |
| - APIs keys en build secrets    |
+----------------------------------+

M10 - EXTRANEOUS FUNCTIONALITY:
+----------------------------------+
| - No debug code en produccion   |
| - No test endpoints en prod     |
| - Logs sanitizados              |
| - Build variants separados      |
+----------------------------------+

+------------------------------------------------------------------+

10.3. Deteccion de Jailbreak/Root

JAILBREAK/ROOT DETECTION:
+------------------------------------------------------------------+

iOS - DETECCION DE JAILBREAK:
+----------------------------------+
| Checks:                          |
| 1. Existencia de Cydia/Sileo    |
| 2. Paths sospechosos:           |
|    /Applications/Cydia.app      |
|    /bin/bash                    |
|    /usr/sbin/sshd               |
| 3. Sandbox escape test          |
| 4. Fork() allowed (no en iOS)   |
| 5. URL schemes de jailbreak     |
+----------------------------------+

Android - DETECCION DE ROOT:
+----------------------------------+
| Checks:                          |
| 1. su binary present            |
| 2. Superuser.apk installed      |
| 3. Build tags (test-keys)       |
| 4. System paths writable        |
| 5. Magisk detection             |
+----------------------------------+

COMPORTAMIENTO EN DETECCION:
+----------------------------------+
| Opcion 1 (Default): WARNING      |
| - Mostrar advertencia           |
| - Permitir continuar            |
| - Log evento                    |
|                                  |
| Opcion 2 (Strict): BLOCK         |
| - Bloquear acceso a PHI         |
| - Solo permitir lectura         |
| - Requerir modo seguro          |
|                                  |
| Opcion 3 (Enterprise): DENY      |
| - No permitir uso de app        |
| - Para deployments corporativos |
+----------------------------------+

NOTA: Ningun check es 100% efectivo. Defense in depth es clave.

+------------------------------------------------------------------+

10.4. Anti-Tampering

ANTI-TAMPERING MEASURES:
+------------------------------------------------------------------+

INTEGRIDAD DE CODIGO:
+----------------------------------+
| iOS:                             |
| - Code signing obligatorio       |
| - App Attest para validar        |
| - Checksum de binarios criticos  |
|                                  |
| Android:                         |
| - APK signing (v2/v3)            |
| - Play Integrity API             |
| - SafetyNet Attestation          |
+----------------------------------+

DETECCION DE DEBUGGING:
+----------------------------------+
| iOS:                             |
| - sysctl() para detectar debugger|
| - ptrace(PT_DENY_ATTACH)         |
|                                  |
| Android:                         |
| - Debug.isDebuggerConnected()    |
| - /proc/self/status (TracerPid)  |
+----------------------------------+

DETECCION DE INSTRUMENTACION:
+----------------------------------+
| Detectar:                        |
| - Frida                          |
| - Xposed Framework               |
| - Substrate                      |
| - Objection                      |
|                                  |
| Metodos:                         |
| - Memory scanning                |
| - Process name checks            |
| - Library injection detection    |
+----------------------------------+

RESPUESTA A TAMPERING:
+----------------------------------+
| 1. Log evento (sin bloquear)    |
| 2. Deshabilitar features PHI     |
| 3. Notificar al usuario          |
| 4. Reportar a servidor (metadata)|
| 5. NO crashear (evitar bypass)   |
+----------------------------------+

+------------------------------------------------------------------+

11. Logging y Auditoria de Seguridad

11.1. Eventos de Seguridad

EVENTOS DE SEGURIDAD A LOGGEAR:
+------------------------------------------------------------------+

AUTENTICACION:
+----------------------------------+
| - Login exitoso                 |
| - Login fallido (sin password)  |
| - Logout                        |
| - Session timeout               |
| - Biometria fallida             |
| - PIN incorrecto (count)        |
| - Recovery Key usado            |
+----------------------------------+

CRIPTOGRAFIA:
+----------------------------------+
| - Master key generada           |
| - Key rotation ejecutada        |
| - Descifrado fallido            |
| - Integridad blob fallida       |
+----------------------------------+

SEGURIDAD:
+----------------------------------+
| - Jailbreak/Root detectado      |
| - Debugger detectado            |
| - Tampering detectado           |
| - Proxy/MITM detectado          |
| - Certificate pinning fallo     |
+----------------------------------+

DATOS:
+----------------------------------+
| - Exportacion de datos          |
| - Compartir con cuidador        |
| - Eliminacion de cuenta         |
| - Backup creado                 |
+----------------------------------+

+------------------------------------------------------------------+

11.2. Sanitizacion de Logs

LOG SANITIZATION:
+------------------------------------------------------------------+

DATOS QUE NUNCA SE LOGGEAN:
+----------------------------------+
| - Passwords/PINs                |
| - Claves criptograficas         |
| - Recovery Keys                 |
| - Tokens de sesion completos    |
| - Datos PHI (medicamentos, etc) |
| - Nombres de pacientes          |
| - Emails completos              |
| - Telefonos                     |
+----------------------------------+

DATOS QUE SE SANITIZAN:
+----------------------------------+
| Original              | Logged    |
|-----------------------|-----------|
| juan@ejemplo.com      | j***@e*** |
| 5551234567            | 555***7   |
| Metformina 500mg      | [REDACTED]|
| token_abc123xyz       | tok_***z  |
| uuid-550e8400-e29b... | uuid_***b |
+----------------------------------+

IMPLEMENTACION:
# Pseudocodigo - Sanitizacion de logs
def sanitize_for_log(value: str, field_type: str) -> str:
    """Sanitiza un valor para logging seguro."""
    if field_type == "email":
        local, domain = value.split('@')
        return f"{local[0]}***@{domain[0]}***"
    elif field_type == "phone":
        return f"{value[:3]}***{value[-1]}"
    elif field_type == "token":
        return f"tok_***{value[-1]}"
    elif field_type == "phi":
        return "[REDACTED]"
    else:
        if len(value) > 4:
            return f"{value[:2]}***{value[-1]}"
        return "***"

11.3. Almacenamiento de Audit Trail

AUDIT TRAIL LOCAL:
+------------------------------------------------------------------+

ESTRUCTURA:
+----------------------------------+
| AuditEvent {                     |
|   id: UUID                       |
|   timestamp: ISO8601             |
|   event_type: String             |
|   category: ENUM                 |
|   details: Map<String, Any>      |
|   device_id: String (hashed)     |
|   session_id: String (partial)   |
| }                                |
+----------------------------------+

RETENCION:
+----------------------------------+
| - Local: 30 dias                |
| - Servidor (Pro/Perfect): 1 ano |
| - Rotacion automatica           |
+----------------------------------+

SINCRONIZACION:
+----------------------------------+
| - Events se cifran E2E          |
| - Servidor ve blobs opacos      |
| - Solo usuario puede leer       |
|   su propio audit trail         |
+----------------------------------+

EJEMPLO LOG ENTRY:
{
  "id": "evt_550e8400",
  "timestamp": "2025-12-07T10:30:00Z",
  "event_type": "login_success",
  "category": "authentication",
  "details": {
    "auth_method": "biometric",
    "device_type": "iphone"
  },
  "device_id": "dev_7f8a9b***",
  "session_id": "ses_***c3d"
}

+------------------------------------------------------------------+

12. Recuperacion de Desastres

12.1. Escenarios de Perdida de Acceso

ESCENARIOS DE PERDIDA:
+------------------------------------------------------------------+

ESCENARIO 1: Olvido de Password
+----------------------------------+
| Tiene Recovery Key?              |
|   SI -> Restaurar con RK        |
|   NO -> Datos perdidos          |
+----------------------------------+

ESCENARIO 2: Dispositivo Perdido/Robado
+----------------------------------+
| Tiene otro dispositivo?          |
|   SI -> Sync desde servidor      |
|   NO -> Usar Recovery Key        |
|         en nuevo dispositivo    |
+----------------------------------+

ESCENARIO 3: Recovery Key Perdida
+----------------------------------+
| Tiene acceso a dispositivo?      |
|   SI -> Generar nueva RK        |
|   NO -> Datos perdidos          |
|         (si tambien perdio pass)|
+----------------------------------+

ESCENARIO 4: Todo Perdido
+----------------------------------+
| Password + Recovery Key + Device |
| perdidos:                        |
|   -> DATOS IRRECUPERABLES       |
|   -> Advertencia durante setup  |
+----------------------------------+

+------------------------------------------------------------------+

12.2. Procedimientos de Recuperacion

flowchart TD
    A[Usuario no puede acceder] --> B{Tiene dispositivo<br/>con sesion activa?}
    B -->|Si| C[Abrir app en dispositivo]
    C --> D[Generar nueva Recovery Key]
    D --> E[Cambiar password si necesario]
    E --> F[Acceso restaurado]

    B -->|No| G{Tiene Recovery Key<br/>guardada?}
    G -->|Si| H[Instalar app en nuevo device]
    H --> I[Ingresar Recovery Key]
    I --> J[Crear nuevo password]
    J --> K[Sync desde servidor]
    K --> F

    G -->|No| L{Recuerda password?}
    L -->|Si| M[Instalar app en nuevo device]
    M --> N[Login con password]
    N --> K

    L -->|No| O[DATOS PERDIDOS<br/>Contactar soporte]
    O --> P[Crear cuenta nueva]

12.3. Backup Cifrado

BACKUP STRATEGY:
+------------------------------------------------------------------+

BACKUP AUTOMATICO (Pro/Perfect):
+----------------------------------+
| - Sincronizacion continua       |
| - Datos cifrados E2E            |
| - Servidor almacena blobs       |
| - Recuperable con Recovery Key  |
+----------------------------------+

BACKUP MANUAL (Free/Pro/Perfect):
+----------------------------------+
| - Exportar archivo .medtime     |
| - Cifrado con AES-256-GCM       |
| - Clave derivada de password    |
| - Usuario guarda archivo        |
+----------------------------------+

FORMATO DE BACKUP:
+----------------------------------+
| {                                |
|   "version": "1.0",              |
|   "created_at": "ISO8601",       |
|   "encryption": {                |
|     "algorithm": "AES-256-GCM",  |
|     "kdf": "argon2id",           |
|     "kdf_params": {...}          |
|   },                             |
|   "data": "base64_encrypted..."  |
| }                                |
+----------------------------------+

RESTORE PROCESS:
1. Usuario proporciona archivo .medtime
2. Usuario ingresa password de backup
3. App deriva clave con Argon2id
4. App descifra contenido
5. App importa datos a DB local
6. App sincroniza con servidor (si aplica)

+------------------------------------------------------------------+

13. Cumplimiento Regulatorio

13.1. HIPAA Security Rule

HIPAA SECURITY RULE COMPLIANCE:
+------------------------------------------------------------------+

TECHNICAL SAFEGUARDS:
+----------------------------------+
| Requirement         | MedTime    |
|---------------------|------------|
| Access Control      | Biometria + PIN, RBAC |
| Audit Controls      | Seccion 10 |
| Integrity           | AES-GCM tags |
| Transmission Security| TLS 1.3 + E2E |
| Encryption          | AES-256-GCM |
+----------------------------------+

ADDRESSABLE SPECIFICATIONS:
+----------------------------------+
| Automatic Logoff    | Session timeout |
| Unique User ID      | UUID por usuario |
| Emergency Access    | Recovery Key |
| Encryption at Rest  | Keychain + E2E |
| Encryption in Transit| TLS 1.3 |
+----------------------------------+

SAFE HARBOR:
+----------------------------------+
| MedTime califica para "safe harbor" bajo HIPAA                    |
| porque datos PHI estan cifrados con AES-256.                      |
| En caso de brecha del servidor, datos son indescifrables.         |
+----------------------------------+

+------------------------------------------------------------------+

13.2. LGPD (Brasil)

LGPD COMPLIANCE:
+------------------------------------------------------------------+

PRINCIPIOS (Art. 6):
+----------------------------------+
| Principio           | MedTime    |
|---------------------|------------|
| Finalidad           | Solo gestion de medicacion |
| Adequacao           | Datos minimos necesarios |
| Necessidade         | Minimo privilegio |
| Livre Acesso        | Usuario ve todos sus datos |
| Qualidade           | Datos editables por usuario |
| Transparencia       | Aviso de privacidad claro |
| Seguranca           | Cifrado E2E |
| Prevencao           | Security by design |
+----------------------------------+

DERECHOS DEL TITULAR (Art. 18):
+----------------------------------+
| Derecho              | Implementacion |
|----------------------|----------------|
| Acceso               | Export de datos |
| Correccion           | Edicion en app |
| Eliminacion          | Delete account |
| Portabilidad         | Export JSON/PDF |
| Revocacion consent   | Settings > Privacy |
+----------------------------------+

DATOS SENSIBLES (Art. 11):
+----------------------------------+
| Datos de salud requieren consentimiento explicito.                |
| MedTime obtiene consentimiento durante onboarding.                |
+----------------------------------+

+------------------------------------------------------------------+

13.3. FDA 21 CFR Part 11

FDA 21 CFR PART 11 COMPLIANCE:
+------------------------------------------------------------------+

ELECTRONIC RECORDS:
+----------------------------------+
| Requirement              | MedTime |
|--------------------------|---------|
| System validation        | Testing strategy |
| Accurate copies          | Export function |
| Record retention         | Audit trail |
| Limiting system access   | Auth + RBAC |
| Operational system checks| Integrity verification |
| Device checks            | Jailbreak detection |
| Authority checks         | Permission system |
+----------------------------------+

ELECTRONIC SIGNATURES:
+----------------------------------+
| Requirement              | MedTime |
|--------------------------|---------|
| Unique to individual     | Biometria + UUID |
| Not reusable             | One-time tokens |
| Administered by authorized| Admin controls |
| Independent verification | MFA required |
+----------------------------------+

AUDIT TRAIL:
+----------------------------------+
| All PHI modifications are logged with:                            |
| - Timestamp                                                       |
| - User ID                                                         |
| - Action type                                                     |
| - Previous value hash (not value itself - E2E)                   |
| - Device ID                                                       |
+----------------------------------+

NOTA: FDA 21 CFR Part 11 aplica si MedTime se usa para
clinical trials o como regulated medical device.
Para uso personal, es best practice pero no obligatorio.

+------------------------------------------------------------------+

14. Testing de Seguridad

14.1. Pruebas Requeridas

SECURITY TESTING MATRIX:
+------------------------------------------------------------------+

STATIC ANALYSIS:
+----------------------------------+
| Tool          | Target           |
|---------------|------------------|
| SonarQube     | Code quality     |
| Snyk          | Dependencies     |
| Semgrep       | Security patterns|
| MobSF         | Mobile security  |
+----------------------------------+

DYNAMIC ANALYSIS:
+----------------------------------+
| Tool          | Purpose          |
|---------------|------------------|
| OWASP ZAP     | API testing      |
| Burp Suite    | Penetration      |
| Frida         | Runtime analysis |
| Objection     | Mobile testing   |
+----------------------------------+

CRYPTOGRAPHIC TESTING:
+----------------------------------+
| Test                     | Pass Criteria          |
|--------------------------|------------------------|
| Nonce uniqueness         | 0 collisions in 10K    |
| Key derivation time      | > 100ms (brute-force)  |
| Entropy quality          | NIST SP 800-90B        |
| Ciphertext randomness    | NIST statistical tests |
| Padding consistency      | Same size for same tier|
| Padding removal          | 100% data integrity    |
+----------------------------------+

PADDING UNIT TESTS (DV2-P3: SEC-BAJO-001):
+----------------------------------+
| Test Case                       | Platform  | Required |
|---------------------------------|-----------|----------|
| padToBlock() añade padding      | iOS       | Si       |
| padToBlock() multiplo de 1KB    | iOS       | Si       |
| removePadding() recupera datos  | iOS       | Si       |
| Padding PKCS#7-like valido      | iOS       | Si       |
| padToBlock() añade padding      | Android   | Si       |
| padToBlock() multiplo de 1KB    | Android   | Si       |
| removePadding() recupera datos  | Android   | Si       |
| Padding PKCS#7-like valido      | Android   | Si       |
| Edge case: data size = 1KB      | Ambos     | Si       |
| Edge case: data size = 0 bytes  | Ambos     | Si       |
| Edge case: data size = 1KB - 1  | Ambos     | Si       |
| Roundtrip: pad -> unpad         | Ambos     | Si       |
| Verificar metadata hiding       | Ambos     | Si       |
+----------------------------------+

EJEMPLO DE UNIT TEST - iOS:
```swift

func testPaddingConsistency() {
    let data1 = "Short".data(using: .utf8)!
    let data2 = "Medium length data".data(using: .utf8)!

    let padded1 = padToBlock(data1, blockSize: 1024)
    let padded2 = padToBlock(data2, blockSize: 1024)

    // Ambos deben tener el mismo tamaño (1KB)
    XCTAssertEqual(padded1.count, 1024)
    XCTAssertEqual(padded2.count, 1024)
}

func testPaddingRoundtrip() {
    let original = "Test data for encryption".data(using: .utf8)!
    let padded = padToBlock(original, blockSize: 1024)
    let unpadded = removePadding(padded)

    // Debe recuperar datos originales exactos
    XCTAssertEqual(original, unpadded)
}

```text

EJEMPLO DE UNIT TEST - Android:
```kotlin

@Test
fun testPaddingConsistency() {
    val data1 = "Short".toByteArray()
    val data2 = "Medium length data".toByteArray()

    val padded1 = padToBlock(data1, blockSize = 1024)
    val padded2 = padToBlock(data2, blockSize = 1024)

    // Ambos deben tener el mismo tamaño (1KB)
    assertEquals(1024, padded1.size)
    assertEquals(1024, padded2.size)
}

@Test
fun testPaddingRoundtrip() {
    val original = "Test data for encryption".toByteArray()
    val padded = padToBlock(original, blockSize = 1024)
    val unpadded = removePadding(padded)

    // Debe recuperar datos originales exactos
    assertArrayEquals(original, unpadded)
}

```mermaid
+----------------------------------+

PENETRATION TESTING:
+----------------------------------+
| Frecuencia: Anual + cada major release                           |
| Scope: App mobile + APIs + Infraestructura                       |
| Proveedor: Tercero independiente                                 |
+----------------------------------+

+------------------------------------------------------------------+

14.2. Checklist de Seguridad por Release

RELEASE SECURITY CHECKLIST:
+------------------------------------------------------------------+

PRE-RELEASE:
[ ] 1. Static analysis ejecutado (0 critical, 0 high)
[ ] 2. Dependency scan (0 known vulnerabilities)
[ ] 3. Code review de cambios crypto completado
[ ] 4. Unit tests de crypto passing (100%)
[ ] 5. Integration tests de auth passing
[ ] 6. No secrets hardcodeados
[ ] 7. Debug code removido
[ ] 8. Logs sanitizados (no PHI)
[ ] 9. Certificate pins actualizados
[ ] 10. Jailbreak/root detection funcional

POST-RELEASE:
[ ] 11. Smoke test en produccion
[ ] 12. Monitoreo de errores de crypto
[ ] 13. Monitoreo de auth failures
[ ] 14. Review de crash reports
[ ] 15. Verificar certificate pinning en red

QUARTERLY:
[ ] Penetration test (si major release)
[ ] Review de access controls
[ ] Rotacion de server secrets
[ ] Review de audit logs

ANNUAL:
[ ] Penetration test completo
[ ] Review de arquitectura de seguridad
[ ] Actualizacion de threat model
[ ] Renovacion de certificados
[ ] Rotacion de global_salt (blind index)

+------------------------------------------------------------------+

15. Referencias

15.1. Documentos MedTime

15.2. Estandares NIST

15.3. OWASP

15.4. Plataformas

15.5. Regulatorios

15.6. Algoritmos


Documento generado por SecurityDrone (MTS-DRN-SEC-001) + SpecQueen - IT-02 "Zero-Knowledge no es una feature, es una promesa. Tus datos seran asimilados... en un bunker criptografico que ni nosotros podemos abrir."