Files
BE_IBL/application/libraries/Ibl_encryptor.php
sas.fajri c410d7bbd9 FHM31052601IBL - implementasi enkripsi PII pasien dan data medis (UU PDP)
- Tambah .env loader di index.php untuk IBL_ENCRYPT_KEY dan IBL_ENCRYPT_SEARCH_KEY
- Library Ibl_encryptor: AES-256-GCM encrypt/decrypt + trigram blind index untuk partial search
- SQL migration: tambah kolom _enc dan _bidx di 16 tabel (m_patient, m_patientaddress, hasil lab, log)
- Script backup_pdp_tables.sh: backup tabel terdampak sebelum migrasi
- Script migrate_encrypt_patient.php: enkripsi batch 178K data PII pasien
- Script migrate_encrypt_results.php: enkripsi data medis hasil lab dan log
- Patient.php: search via trigram blind index, add_new/edit enkripsi sebelum save

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 14:07:42 +07:00

88 lines
3.0 KiB
PHP

<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Ibl_encryptor
{
private $key;
private $search_key;
private $cipher = 'aes-256-gcm';
private $tag_length = 16;
public function __construct()
{
$passphrase = $_ENV['IBL_ENCRYPT_KEY'] ?? '';
$passphrase_s = $_ENV['IBL_ENCRYPT_SEARCH_KEY'] ?? '';
if ($passphrase === '' || $passphrase_s === '') {
log_message('error', 'Ibl_encryptor: IBL_ENCRYPT_KEY atau IBL_ENCRYPT_SEARCH_KEY kosong di .env');
}
// Derive 32-byte key dari passphrase via SHA-256
$this->key = hash('sha256', $passphrase, true);
$this->search_key = hash('sha256', $passphrase_s, true);
}
// Enkripsi plaintext → base64(iv[12] + tag[16] + ciphertext)
public function encrypt($plaintext)
{
if ($plaintext === null || $plaintext === '') return null;
$iv = random_bytes(12);
$tag = '';
$ct = openssl_encrypt((string)$plaintext, $this->cipher, $this->key, OPENSSL_RAW_DATA, $iv, $tag, '', $this->tag_length);
if ($ct === false) return null;
return base64_encode($iv . $tag . $ct);
}
// Dekripsi base64(iv + tag + ciphertext) → plaintext
public function decrypt($ciphertext)
{
if ($ciphertext === null || $ciphertext === '') return null;
$raw = base64_decode($ciphertext);
if (strlen($raw) < 12 + $this->tag_length) return null;
$iv = substr($raw, 0, 12);
$tag = substr($raw, 12, $this->tag_length);
$ct = substr($raw, 12 + $this->tag_length);
$pt = openssl_decrypt($ct, $this->cipher, $this->key, OPENSSL_RAW_DATA, $iv, $tag);
return $pt === false ? null : $pt;
}
// Hasilkan JSON array trigram token untuk kolom _bidx (partial search)
public function search_bidx($value)
{
if ($value === null || $value === '') return null;
$norm = mb_strtolower(trim((string)$value), 'UTF-8');
$len = mb_strlen($norm, 'UTF-8');
$tokens = [];
if ($len <= 3) {
$tokens[] = $this->_token($norm);
} else {
for ($i = 0; $i <= $len - 3; $i++) {
$tokens[] = $this->_token(mb_substr($norm, $i, 3, 'UTF-8'));
}
}
return json_encode(array_values(array_unique($tokens)));
}
// Hasilkan array trigram token dari query string (untuk WHERE JSON_CONTAINS)
public function query_tokens($value)
{
if ($value === null || $value === '') return [];
$norm = mb_strtolower(trim((string)$value), 'UTF-8');
$len = mb_strlen($norm, 'UTF-8');
$tokens = [];
if ($len <= 3) {
$tokens[] = $this->_token($norm);
} else {
for ($i = 0; $i <= $len - 3; $i++) {
$tokens[] = $this->_token(mb_substr($norm, $i, 3, 'UTF-8'));
}
}
return array_values(array_unique($tokens));
}
private function _token($str)
{
return hash_hmac('sha256', $str, $this->search_key);
}
}