From bfbe0794d3fce1880b78271a12b26d5a86fe3384 Mon Sep 17 00:00:00 2001
From: "Q. T. Felix" <53819958+Quant-TheodoreFelix@users.noreply.github.com>
Date: Mon, 23 Feb 2026 17:31:40 +0900
Subject: [PATCH 1/6] =?UTF-8?q?=ED=81=AC=EB=A0=88=EC=9D=B4=ED=8A=B8/?=
=?UTF-8?q?=EB=AA=A8=EB=93=88=20=EC=84=B8=EB=B6=84=ED=99=94=20=EB=B0=8F=20?=
=?UTF-8?q?=ED=95=B5=EC=8B=AC=20=EB=B3=B4=EC=95=88=20=EA=B8=B0=EB=8A=A5=20?=
=?UTF-8?q?=EA=B0=95=ED=99=94?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.idea/copyright/profiles_settings.xml | 3 -
.idea/entlib-native.iml | 24 +-
core/ffi/benches/ffi_base64_bench.rs | 154 ------------
core/ffi/benches/ffi_wipe_bench.rs | 37 ---
core/ffi/src/base64_ffi.rs | 182 ---------------
core/ffi/src/secure_buffer.rs | 80 -------
core/helper/Cargo.toml | 32 ---
core/helper/benches/secure_buffer_bench.rs | 42 ----
core/helper/src/lib.rs | 14 --
core/helper/src/secure_buffer.rs | 29 ---
.../base64}/benches/base64_bench.rs | 2 +-
{core/helper => crypto/base64}/src/base64.rs | 6 +-
.../base64}/tests/base64_test.rs | 2 +-
.../constant-time}/benches/ct_ops_security.rs | 24 +-
.../benches/ct_ops_throughput.rs | 24 +-
.../constant-time}/constant_time.md | 0
.../constant-time}/constant_time_asm.md | 0
.../constant-time}/src/constant_time.rs | 24 +-
.../constant-time}/src/constant_time_asm.rs | 26 ++-
.../tests/constant_time_asm_test.rs | 24 +-
.../tests/constant_time_test.rs | 2 +-
.../tests/ct_cross_validation.rs | 2 +-
.../benches/secure_buffer_bench.rs | 64 +++++
crypto/core-secure/src/secure_buffer.rs | 63 +++++
crypto/rng/Cargo.toml | 12 +-
crypto/rng/benches/anu_qrng_bench.rs | 98 ++++++++
crypto/rng/src/anu_qrng.rs | 77 ++++++
crypto/rng/src/base_rng.rs | 6 +-
crypto/rng/src/lib.rs | 1 +
crypto/rng/src/mixed.rs | 32 ++-
crypto/rng/tests/anu_qrng_test.rs | 88 +++++++
crypto/rng/tests/hw_based_rng_test.rs | 2 +-
crypto/rng/tests/mixed_rng_test.rs | 176 ++++++++------
crypto/sha2/Cargo.toml | 2 +-
crypto/sha2/benches/sha2_bench.rs | 2 +-
crypto/sha3/Cargo.toml | 2 +-
crypto/sha3/benches/sha3_bench.rs | 2 +-
{core => internal}/ffi/Cargo.toml | 25 +-
internal/ffi/src/base64_ffi.rs | 219 ++++++++++++++++++
{core => internal}/ffi/src/lib.rs | 7 +-
internal/ffi/src/rng_ffi.rs | 158 +++++++++++++
internal/ffi/src/secure_buffer_ffi.rs | 98 ++++++++
.../sha2.rs => internal/ffi/src/sha2_ffi.rs | 2 +-
43 files changed, 1176 insertions(+), 693 deletions(-)
delete mode 100644 .idea/copyright/profiles_settings.xml
delete mode 100644 core/ffi/benches/ffi_base64_bench.rs
delete mode 100644 core/ffi/benches/ffi_wipe_bench.rs
delete mode 100644 core/ffi/src/base64_ffi.rs
delete mode 100644 core/ffi/src/secure_buffer.rs
delete mode 100644 core/helper/Cargo.toml
delete mode 100644 core/helper/benches/secure_buffer_bench.rs
delete mode 100644 core/helper/src/lib.rs
delete mode 100644 core/helper/src/secure_buffer.rs
rename {core/helper => crypto/base64}/benches/base64_bench.rs (98%)
rename {core/helper => crypto/base64}/src/base64.rs (89%)
rename {core/helper => crypto/base64}/tests/base64_test.rs (98%)
rename {core/helper => crypto/constant-time}/benches/ct_ops_security.rs (90%)
rename {core/helper => crypto/constant-time}/benches/ct_ops_throughput.rs (78%)
rename {core/helper => crypto/constant-time}/constant_time.md (100%)
rename {core/helper => crypto/constant-time}/constant_time_asm.md (100%)
rename {core/helper => crypto/constant-time}/src/constant_time.rs (76%)
rename {core/helper => crypto/constant-time}/src/constant_time_asm.rs (93%)
rename {core/helper => crypto/constant-time}/tests/constant_time_asm_test.rs (66%)
rename {core/helper => crypto/constant-time}/tests/constant_time_test.rs (96%)
rename {core/helper => crypto/constant-time}/tests/ct_cross_validation.rs (98%)
create mode 100644 crypto/core-secure/benches/secure_buffer_bench.rs
create mode 100644 crypto/core-secure/src/secure_buffer.rs
create mode 100644 crypto/rng/benches/anu_qrng_bench.rs
create mode 100644 crypto/rng/src/anu_qrng.rs
create mode 100644 crypto/rng/tests/anu_qrng_test.rs
rename {core => internal}/ffi/Cargo.toml (61%)
create mode 100644 internal/ffi/src/base64_ffi.rs
rename {core => internal}/ffi/src/lib.rs (90%)
create mode 100644 internal/ffi/src/rng_ffi.rs
create mode 100644 internal/ffi/src/secure_buffer_ffi.rs
rename core/ffi/src/sha2.rs => internal/ffi/src/sha2_ffi.rs (98%)
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
deleted file mode 100644
index c3ba54a..0000000
--- a/.idea/copyright/profiles_settings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/.idea/entlib-native.iml b/.idea/entlib-native.iml
index d541518..fdae6f1 100644
--- a/.idea/entlib-native.iml
+++ b/.idea/entlib-native.iml
@@ -3,14 +3,7 @@
-
-
-
-
-
-
-
@@ -20,6 +13,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/ffi/benches/ffi_base64_bench.rs b/core/ffi/benches/ffi_base64_bench.rs
deleted file mode 100644
index 52650f4..0000000
--- a/core/ffi/benches/ffi_base64_bench.rs
+++ /dev/null
@@ -1,154 +0,0 @@
-use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
-use std::time::Duration;
-
-use entlib_native_ffi::base64_ffi::{
- entlib_b64_decode_secure, entlib_b64_encode_secure, entlib_free_secure_buffer,
- entlib_secure_buffer_get_ptr,
-};
-
-const ENCODE_SIZES: &[usize] = &[3, 32, 256, 1_024, 4_096, 65_536, 1_048_576];
-
-//
-// 처리량 — encode: 입력 크기별 전체 lifecycle (encode → get_ptr → free)
-//
-
-fn ffi_encode_throughput(c: &mut Criterion) {
- let mut group = c.benchmark_group("throughput/ffi_b64_encode");
-
- for &size in ENCODE_SIZES {
- let input = vec![0x42u8; size];
- group.throughput(Throughput::Bytes(size as u64));
-
- group.bench_with_input(BenchmarkId::from_parameter(size), &input, |b, input| {
- b.iter(|| {
- let mut out_len: usize = 0;
- let ptr =
- unsafe { entlib_b64_encode_secure(input.as_ptr(), input.len(), &mut out_len) };
- let _ = unsafe { entlib_secure_buffer_get_ptr(ptr) };
- entlib_free_secure_buffer(ptr);
- })
- });
- }
-
- group.finish();
-}
-
-//
-// 처리량 — decode: 사전 인코딩된 입력으로 decode lifecycle
-//
-
-fn ffi_decode_throughput(c: &mut Criterion) {
- let mut group = c.benchmark_group("throughput/ffi_b64_decode");
-
- for &size in ENCODE_SIZES {
- // 사전 인코딩: 원본 크기 size → Base64 인코딩된 데이터 생성
- let plain = vec![0x42u8; size];
- let mut enc_len: usize = 0;
- let enc_ptr =
- unsafe { entlib_b64_encode_secure(plain.as_ptr(), plain.len(), &mut enc_len) };
- let enc_data_ptr = unsafe { entlib_secure_buffer_get_ptr(enc_ptr) };
- let encoded: Vec =
- unsafe { std::slice::from_raw_parts(enc_data_ptr, enc_len) }.to_vec();
- entlib_free_secure_buffer(enc_ptr);
-
- group.throughput(Throughput::Bytes(encoded.len() as u64));
-
- group.bench_with_input(BenchmarkId::from_parameter(size), &encoded, |b, encoded| {
- b.iter(|| {
- let mut out_len: usize = 0;
- let mut err_flag: u8 = 0;
- let ptr = unsafe {
- entlib_b64_decode_secure(
- encoded.as_ptr(),
- encoded.len(),
- &mut out_len,
- &mut err_flag,
- )
- };
- entlib_free_secure_buffer(ptr);
- })
- });
- }
-
- group.finish();
-}
-
-//
-// 보안성 — decode: 유효 입력 vs 무효 문자 주입 (1KB)
-//
-
-fn ffi_decode_security(c: &mut Criterion) {
- let mut group = c.benchmark_group("security/ffi_b64_decode");
- group.measurement_time(Duration::from_secs(5));
- group.sample_size(1000);
-
- // 유효 입력: 1KB 평문을 인코딩한 결과
- let valid_plain = vec![0x42u8; 1024];
- let mut enc_len: usize = 0;
- let enc_ptr =
- unsafe { entlib_b64_encode_secure(valid_plain.as_ptr(), valid_plain.len(), &mut enc_len) };
- let enc_data_ptr = unsafe { entlib_secure_buffer_get_ptr(enc_ptr) };
- let valid_encoded: Vec =
- unsafe { std::slice::from_raw_parts(enc_data_ptr, enc_len) }.to_vec();
- entlib_free_secure_buffer(enc_ptr);
-
- // 무효 입력: 같은 길이의 데이터에 무효 문자(0x00) 주입
- let mut invalid_encoded = valid_encoded.clone();
- for i in (0..invalid_encoded.len()).step_by(4) {
- invalid_encoded[i] = 0x00; // 무효 바이트 주입 (매 4번째)
- }
-
- group.bench_with_input(
- BenchmarkId::new("valid_1kb", ""),
- &valid_encoded,
- |b, input| {
- b.iter(|| {
- let mut out_len: usize = 0;
- let mut err_flag: u8 = 0;
- let ptr = unsafe {
- entlib_b64_decode_secure(
- input.as_ptr(),
- input.len(),
- &mut out_len,
- &mut err_flag,
- )
- };
- entlib_free_secure_buffer(ptr);
- })
- },
- );
-
- group.bench_with_input(
- BenchmarkId::new("invalid_1kb", ""),
- &invalid_encoded,
- |b, input| {
- b.iter(|| {
- let mut out_len: usize = 0;
- let mut err_flag: u8 = 0;
- let ptr = unsafe {
- entlib_b64_decode_secure(
- input.as_ptr(),
- input.len(),
- &mut out_len,
- &mut err_flag,
- )
- };
- entlib_free_secure_buffer(ptr);
- })
- },
- );
-
- group.finish();
-}
-
-//
-// Criterion 설정
-//
-
-criterion_group!(
- benches,
- ffi_encode_throughput,
- ffi_decode_throughput,
- ffi_decode_security,
-);
-criterion_main!(benches);
diff --git a/core/ffi/benches/ffi_wipe_bench.rs b/core/ffi/benches/ffi_wipe_bench.rs
deleted file mode 100644
index b6bc443..0000000
--- a/core/ffi/benches/ffi_wipe_bench.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
-
-use entlib_native_ffi::secure_buffer::entanglement_secure_wipe;
-
-const SIZES: &[usize] = &[64, 256, 1_024, 4_096, 65_536, 1_048_576];
-
-//
-// 처리량 — secure wipe (각 반복마다 0xAA 재충전으로 반복 zero-wipe 최적화 방지)
-//
-
-fn ffi_wipe_throughput(c: &mut Criterion) {
- let mut group = c.benchmark_group("throughput/ffi_secure_wipe");
-
- for &size in SIZES {
- group.throughput(Throughput::Bytes(size as u64));
-
- group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
- let mut buffer = vec![0xAAu8; size];
- b.iter(|| {
- // 0xAA로 재충전 — 반복 zero-wipe 최적화 방지
- buffer.fill(0xAA);
- unsafe {
- entanglement_secure_wipe(buffer.as_mut_ptr(), buffer.len());
- }
- })
- });
- }
-
- group.finish();
-}
-
-//
-// Criterion 설정
-//
-
-criterion_group!(benches, ffi_wipe_throughput);
-criterion_main!(benches);
diff --git a/core/ffi/src/base64_ffi.rs b/core/ffi/src/base64_ffi.rs
deleted file mode 100644
index 5fa7f81..0000000
--- a/core/ffi/src/base64_ffi.rs
+++ /dev/null
@@ -1,182 +0,0 @@
-use std::boxed::Box;
-use std::ffi::c_void;
-use std::slice;
-use std::vec::Vec;
-
-use entlib_native_helper::base64::{ct_b64_to_bin_u8, ct_bin_to_b64_u8};
-use entlib_native_helper::constant_time::ConstantTimeOps;
-use entlib_native_helper::secure_buffer::SecureBuffer;
-
-/// Java측에 노출되는 Base64 인코딩 함수 (encode endpoint)
-///
-/// # Arguments
-/// * `input_ptr` - 인코딩할 평문 바이트 배열의 포인터 (memory segment address)
-/// * `input_len` - 평문 배열의 길이
-/// * `out_len` - (출력 매개변수) 생성된 base64 문자열의 길이를 반환할 포인터
-///
-/// # Returns
-/// 인코딩된 데이터를 담고 있는 `SecureBuffer`의 불투명 포인터 (opaque pointer)
-///
-/// # Safety
-/// * `input_ptr`은 `input_len` 바이트만큼 유효한 메모리를 가리켜야 합니다.
-/// * `out_len`은 유효한 `usize` 메모리 공간을 가리켜야 합니다.
-/// * 반환된 포인터는 사용 후 반드시 `entlib_free_secure_buffer`로 해제해야 합니다.
-#[unsafe(no_mangle)]
-pub unsafe extern "C" fn entlib_b64_encode_secure(
- input_ptr: *const u8,
- input_len: usize,
- out_len: *mut usize,
-) -> *mut c_void {
- // null pointer 검증
- if input_ptr.is_null() || out_len.is_null() {
- return core::ptr::null_mut();
- }
-
- // 입력 길이 검증 및 최대 용량 산출
- let capacity = match input_len.checked_add(2) {
- Some(val) => (val / 3).checked_mul(4),
- None => None,
- };
-
- let capacity = match capacity {
- Some(c) => c,
- None => return core::ptr::null_mut(), // 오버플로우 방지 (overflow protection)
- };
-
- let input = unsafe { slice::from_raw_parts(input_ptr, input_len) };
- let mut out = Vec::with_capacity(capacity);
-
- // 상수 시간 인코딩 루프
- // 보안을 위해 분기 처리 대신 비트 연산 기반의 패딩 마스킹을 사용해야 하지만
- // 공간 제약상 기본적인 3-to-4 블록 변환 구조를 명시
- let mut i = 0;
- while i < input_len {
- let b0 = input[i];
- let b1 = if i + 1 < input_len { input[i + 1] } else { 0 };
- let b2 = if i + 2 < input_len { input[i + 2] } else { 0 };
-
- let e0 = b0 >> 2;
- let e1 = ((b0 & 0x03) << 4) | (b1 >> 4);
- let e2 = ((b1 & 0x0F) << 2) | (b2 >> 6);
- let e3 = b2 & 0x3F;
-
- out.push(ct_bin_to_b64_u8(e0));
- out.push(ct_bin_to_b64_u8(e1));
-
- // 패딩 처리 로직은 별도의 상수 시간 마스킹으로 치환 가능
- out.push(if i + 1 < input_len {
- ct_bin_to_b64_u8(e2)
- } else {
- b'='
- });
- out.push(if i + 2 < input_len {
- ct_bin_to_b64_u8(e3)
- } else {
- b'='
- });
-
- i += 3;
- }
-
- unsafe { *out_len = out.len() };
-
- let secure_buf = SecureBuffer { inner: out };
- Box::into_raw(Box::new(secure_buf)) as *mut c_void
-}
-
-/// Java 측에 노출되는 Base64 디코딩 함수 (decode endpoint)
-///
-/// # Arguments
-/// * `input_ptr` - 디코딩할 base64 문자열의 포인터
-/// * `input_len` - 문자열의 길이
-/// * `out_len` - (출력 매개변수) 복원된 평문의 길이를 반환할 포인터
-/// * `err_flag` - (출력 매개변수) 디코딩 중 에러(잘못된 문자 등) 발생 시 1을 반환, 정상 시 0
-///
-/// # Safety
-/// * `input_ptr`은 `input_len` 바이트만큼 유효한 메모리를 가리켜야 합니다.
-/// * `out_len`과 `err_flag`는 유효한 메모리 공간을 가리켜야 합니다.
-/// * 반환된 포인터는 사용 후 반드시 `entlib_free_secure_buffer`로 해제해야 합니다.
-#[unsafe(no_mangle)]
-pub unsafe extern "C" fn entlib_b64_decode_secure(
- input_ptr: *const u8,
- input_len: usize,
- out_len: *mut usize,
- err_flag: *mut u8,
-) -> *mut c_void {
- if input_ptr.is_null() || out_len.is_null() || err_flag.is_null() {
- return core::ptr::null_mut();
- }
-
- let input = unsafe { slice::from_raw_parts(input_ptr, input_len) };
- let capacity = (input_len / 4) * 3;
- let mut out = Vec::with_capacity(capacity);
-
- let mut error_accum = 0u8;
- let mut buf = [0u8; 4];
- let mut buf_idx = 0;
-
- for &byte in input {
- let decoded = ct_b64_to_bin_u8(byte);
-
- // 에러 마스크 누적 (error accumulation) - 분기 없는 상수 시간 로직
- let is_invalid = decoded.ct_eq(0xFF);
- error_accum |= is_invalid & 0x01;
-
- // 공백 문자(0x80) 무시 및 유효 바이트 버퍼링
- let is_valid_char = !decoded.ct_eq(0x80) & !decoded.ct_eq(0x81) & !is_invalid;
-
- // note: 완벽한 상수 시간 구동을 위해 버퍼 인덱스 증가도 분기 없이 처리하는 것이 이상적임
- if is_valid_char == 0xFF {
- buf[buf_idx] = decoded;
- buf_idx = (buf_idx + 1) & 0x03; // modulo 4
-
- if buf_idx == 0 {
- out.push((buf[0] << 2) | (buf[1] >> 4));
- out.push((buf[1] << 4) | (buf[2] >> 2));
- out.push((buf[2] << 6) | buf[3]);
- }
- }
- }
-
- // 잔여 버퍼 처리가 없으면 패딩 문자로 인해 4-문자 블록이 완성되지 않은 잔여 버퍼 데이터가 그대로 버려짐
- // 해당 문제 해결을 위해 잔여 버퍼를 처리함
- if buf_idx == 2 {
- out.push((buf[0] << 2) | (buf[1] >> 4));
- } else if buf_idx == 3 {
- out.push((buf[0] << 2) | (buf[1] >> 4));
- out.push((buf[1] << 4) | (buf[2] >> 2));
- }
-
- unsafe {
- *out_len = out.len();
- *err_flag = error_accum;
- }
-
- let secure_buf = SecureBuffer { inner: out };
- Box::into_raw(Box::new(secure_buf)) as *mut c_void
-}
-
-/// `SecureBuffer` 메모리 데이터 추출 함수 (buffer read endpoint)
-///
-/// # Safety
-/// * `ptr`은 `entlib_b64_encode_secure` 혹은 `entlib_b64_decode_secure`로부터 반환된 유효한 `SecureBuffer` 포인터여야 합니다.
-/// * `ptr`이 가리키는 메모리는 해제되지 않은 상태여야 합니다.
-#[unsafe(no_mangle)]
-pub unsafe extern "C" fn entlib_secure_buffer_get_ptr(ptr: *mut c_void) -> *const u8 {
- if ptr.is_null() {
- return core::ptr::null();
- }
- let buf = unsafe { &*(ptr as *const SecureBuffer) };
- buf.inner.as_ptr()
-}
-
-/// Java 힙(heap)으로 복사 완료 후 반드시 호출해야 하는 네이티브 메모리 소거 및 해제 함수
-#[unsafe(no_mangle)]
-pub extern "C" fn entlib_free_secure_buffer(ptr: *mut c_void) {
- if !ptr.is_null() {
- unsafe {
- // 박스 소유권 반환을 통한 drop 호출 및 zeroize 수행
- let _ = Box::from_raw(ptr as *mut SecureBuffer);
- }
- }
-}
diff --git a/core/ffi/src/secure_buffer.rs b/core/ffi/src/secure_buffer.rs
deleted file mode 100644
index ba8a56a..0000000
--- a/core/ffi/src/secure_buffer.rs
+++ /dev/null
@@ -1,80 +0,0 @@
-use core::ptr::{self, write_volatile};
-use core::slice;
-use core::sync::atomic::{Ordering, compiler_fence};
-use entlib_native_helper::secure_buffer::SecureBuffer;
-
-/// 보안 버퍼 내 실제 데이터의 메모리 주소 반환 (get immutable data pointer)
-///
-/// # Safety
-/// - 반환된 원시 포인터(raw pointer)는 `SecureBuffer`가 `entlib_secure_buffer_free`를 통해
-/// 해제되기 전까지만 유효합니다. 해제 후 역참조 시 미정의 동작(undefined behavior)이 발생합니다.
-#[unsafe(no_mangle)]
-pub unsafe extern "C" fn entlib_secure_buffer_data(buf: *const SecureBuffer) -> *const u8 {
- if buf.is_null() {
- return ptr::null();
- }
- let buffer = unsafe { &*buf };
- buffer.inner.as_ptr()
-}
-
-/// 보안 버퍼 내 데이터의 바이트 길이 반환 (get length of data)
-///
-/// # Safety
-/// - `buf`가 null이 아닌 경우, 유효한 `SecureBuffer` 인스턴스를 가리켜야 합니다.
-#[unsafe(no_mangle)]
-pub unsafe extern "C" fn entlib_secure_buffer_len(buf: *const SecureBuffer) -> usize {
- if buf.is_null() {
- return 0;
- }
- let buffer = unsafe { &*buf };
- buffer.inner.len()
-}
-
-/// 보안 버퍼 메모리 해제 및 데이터 소거 (free and zeroize)
-///
-/// # Safety
-/// - 호출 즉시 `SecureBuffer`의 `Drop` 트레이트가 실행되어 `write_volatile` 및
-/// `compiler_fence`를 통해 메모리가 안전하게 소거됩니다.
-/// - 자바 링커(java linker api)를 통해 반드시 한 번만 호출되어야 합니다 (double-free 방지).
-#[unsafe(no_mangle)]
-pub unsafe extern "C" fn entlib_secure_buffer_free(buf: *mut SecureBuffer) {
- if !buf.is_null() {
- unsafe {
- // Box::from_raw를 통해 소유권을 가져오며, 즉시 스코프를 벗어나 Drop 실행
- drop(Box::from_raw(buf));
- }
- }
-}
-
-/// 자바 측 `SensitiveDataContainer`가 소유한 네이티브 메모리 세그먼트(memory segment)를
-/// 안전하게 소거(zeroize)하는 ffi 엔드포인트입니다.
-///
-/// # Arguments
-/// * `ptr` - 소거할 메모리 영역의 시작 포인터 (*mut u8)
-/// * `len` - 소거할 메모리의 바이트 크기 (usize)
-///
-/// # Safety
-/// * `ptr`은 `len` 바이트만큼 할당된 유효한 메모리 영역을 가리켜야 합니다.
-/// * 자바의 제한된 아레나(confined arena) 수명 주기에 의해 유효성이 검증된 상태에서만 호출되어야 합니다.
-#[unsafe(no_mangle)]
-pub unsafe extern "C" fn entanglement_secure_wipe(ptr: *mut u8, len: usize) {
- // null pointer 및 0바이트 길이 방어 코드
- if ptr.is_null() || len == 0 {
- return;
- }
-
- // 원시 포인터로부터 가변 슬라이스 생성
- let buffer = unsafe { slice::from_raw_parts_mut(ptr, len) };
-
- // secure_buffer 구조체와 동일한 수준의 컴파일러 최적화 방어 소거
- for byte in buffer.iter_mut() {
- unsafe {
- // 휘발성 쓰기(volatile write)를 통해 dce 최적화 강제 회피
- write_volatile(byte, 0);
- }
- }
-
- // 메모리 배리어를 통해 소거 작업이 후속 메모리 해제(arena#close) 연산보다
- // 반드시 먼저 완료되도록 순서(sequential consistency) 보장
- compiler_fence(Ordering::SeqCst);
-}
diff --git a/core/helper/Cargo.toml b/core/helper/Cargo.toml
deleted file mode 100644
index b74f65a..0000000
--- a/core/helper/Cargo.toml
+++ /dev/null
@@ -1,32 +0,0 @@
-[package]
-name = "entlib-native-helper"
-version.workspace = true
-edition.workspace = true
-authors.workspace = true
-license.workspace = true
-
-[features]
-ct-tests = []
-
-[dependencies]
-
-[dev-dependencies]
-subtle = "2.6.1"
-proptest = "1.10.0"
-criterion = { version = "0.8.2", features = ["html_reports"] }
-
-[[bench]]
-name = "ct_ops_security"
-harness = false
-
-[[bench]]
-name = "ct_ops_throughput"
-harness = false
-
-[[bench]]
-name = "base64_bench"
-harness = false
-
-[[bench]]
-name = "secure_buffer_bench"
-harness = false
\ No newline at end of file
diff --git a/core/helper/benches/secure_buffer_bench.rs b/core/helper/benches/secure_buffer_bench.rs
deleted file mode 100644
index eb59e35..0000000
--- a/core/helper/benches/secure_buffer_bench.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-use criterion::{BatchSize, BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
-
-use entlib_native_helper::secure_buffer::SecureBuffer;
-
-const SIZES: &[usize] = &[64, 256, 1_024, 4_096, 65_536, 1_048_576];
-
-//
-// SecureBuffer drop(wipe) 처리량
-//
-
-fn secure_buffer_wipe_throughput(c: &mut Criterion) {
- let mut group = c.benchmark_group("throughput/secure_buffer_wipe");
-
- for &size in SIZES {
- group.throughput(Throughput::Bytes(size as u64));
-
- let batch_size = if size >= 65_536 {
- BatchSize::LargeInput
- } else {
- BatchSize::SmallInput
- };
-
- group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
- b.iter_batched(
- || SecureBuffer {
- inner: vec![0xAA; size],
- },
- drop, // |buf| drop(buf)
- batch_size,
- )
- });
- }
-
- group.finish();
-}
-
-//
-// Criterion 설정
-//
-
-criterion_group!(benches, secure_buffer_wipe_throughput);
-criterion_main!(benches);
diff --git a/core/helper/src/lib.rs b/core/helper/src/lib.rs
deleted file mode 100644
index a4ed6f8..0000000
--- a/core/helper/src/lib.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-#![no_std]
-extern crate alloc;
-
-pub mod constant_time;
-
-#[cfg(feature = "ct-tests")]
-#[doc(hidden)]
-pub mod constant_time_asm;
-
-#[cfg(not(feature = "ct-tests"))]
-pub(crate) mod constant_time_asm;
-
-pub mod base64;
-pub mod secure_buffer;
diff --git a/core/helper/src/secure_buffer.rs b/core/helper/src/secure_buffer.rs
deleted file mode 100644
index e555e6c..0000000
--- a/core/helper/src/secure_buffer.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use alloc::vec::Vec;
-use core::ptr::write_volatile;
-use core::sync::atomic::{Ordering, compiler_fence};
-
-/// 메모리 소거를 보장하는 보안 버퍼 구조체입니다.
-///
-/// 민감한 데이터를 사용한 연산 후 그 결과를 Java Heap으로 전달하는 경우,
-/// 가비지 컬렉터의 생명주기에 종속되어 위험에 다시 노출되는 딜레마를 가지게
-/// 됩니다. 이 문제를 해결하기 위해 단순히 `byte[]`와 같은 Java 데이터로
-/// 직렬화(serialize)하지 않고, 이 구조체에 저장합니다.
-///
-/// Java 측으로는 오직 해당 메모리의 원시 포인만이 전달되기 떄문에 안전한
-/// 데이터 관리가 가능합니다.
-pub struct SecureBuffer {
- pub inner: Vec,
-}
-
-impl Drop for SecureBuffer {
- fn drop(&mut self) {
- for byte in self.inner.iter_mut() {
- // volatile write -> 컴파일러 dce 최적화 방지함
- unsafe {
- write_volatile(byte, 0);
- }
- }
- // 메모리 배리어로 소거 순서 보장
- compiler_fence(Ordering::SeqCst);
- }
-}
diff --git a/core/helper/benches/base64_bench.rs b/crypto/base64/benches/base64_bench.rs
similarity index 98%
rename from core/helper/benches/base64_bench.rs
rename to crypto/base64/benches/base64_bench.rs
index 5baaa18..f793cde 100644
--- a/core/helper/benches/base64_bench.rs
+++ b/crypto/base64/benches/base64_bench.rs
@@ -1,7 +1,7 @@
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use std::time::Duration;
-use entlib_native_helper::base64::{ct_b64_to_bin_u8, ct_bin_to_b64_u8};
+use entlib_native_base64::base64::{ct_b64_to_bin_u8, ct_bin_to_b64_u8};
//
// 보안성 — encode: 문자 클래스별 타이밍 비교
diff --git a/core/helper/src/base64.rs b/crypto/base64/src/base64.rs
similarity index 89%
rename from core/helper/src/base64.rs
rename to crypto/base64/src/base64.rs
index b0a8ea8..0199ce9 100644
--- a/core/helper/src/base64.rs
+++ b/crypto/base64/src/base64.rs
@@ -1,6 +1,6 @@
-use crate::constant_time::ConstantTimeOps;
+use entlib_native_constant_time::constant_time::ConstantTimeOps;
-/// 단일 바이트 상수-시간 Base64 인코딩을 수행하는 함수입니다.
+/// 단일 바이트 상수-시간 `Base64` 인코딩을 수행하는 함수입니다.
#[inline(always)]
pub fn ct_bin_to_b64_u8(c: u8) -> u8 {
// 0 <= c < 26
@@ -29,7 +29,7 @@ pub fn ct_bin_to_b64_u8(c: u8) -> u8 {
res
}
-/// 단일 바이트 상수-시간 Base64 디코딩을 수행하는 함수입니다.
+/// 단일 바이트 상수-시간 `Base64` 디코딩을 수행하는 함수입니다.
#[inline(always)]
pub fn ct_b64_to_bin_u8(b: u8) -> u8 {
// 범위 검사
diff --git a/core/helper/tests/base64_test.rs b/crypto/base64/tests/base64_test.rs
similarity index 98%
rename from core/helper/tests/base64_test.rs
rename to crypto/base64/tests/base64_test.rs
index 01977cf..2cd5fed 100644
--- a/core/helper/tests/base64_test.rs
+++ b/crypto/base64/tests/base64_test.rs
@@ -1,4 +1,4 @@
-use entlib_native_helper::base64::{ct_b64_to_bin_u8, ct_bin_to_b64_u8};
+use entlib_native_base64::base64::{ct_b64_to_bin_u8, ct_bin_to_b64_u8};
#[cfg(test)]
mod base64_constant_time_tests {
diff --git a/core/helper/benches/ct_ops_security.rs b/crypto/constant-time/benches/ct_ops_security.rs
similarity index 90%
rename from core/helper/benches/ct_ops_security.rs
rename to crypto/constant-time/benches/ct_ops_security.rs
index 27148a8..67cfc74 100644
--- a/core/helper/benches/ct_ops_security.rs
+++ b/crypto/constant-time/benches/ct_ops_security.rs
@@ -1,7 +1,29 @@
+/*
+ * Copyright (c) 2025-2026 Quant
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the “Software”),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use std::time::Duration;
-use entlib_native_helper::constant_time::ConstantTimeOps;
+use entlib_native_constant_time::constant_time::ConstantTimeOps;
//
// Macros — 반복 제거를 위한 보안성 벤치마크 매크로
diff --git a/core/helper/benches/ct_ops_throughput.rs b/crypto/constant-time/benches/ct_ops_throughput.rs
similarity index 78%
rename from core/helper/benches/ct_ops_throughput.rs
rename to crypto/constant-time/benches/ct_ops_throughput.rs
index 73ef388..a6a8cae 100644
--- a/core/helper/benches/ct_ops_throughput.rs
+++ b/crypto/constant-time/benches/ct_ops_throughput.rs
@@ -1,6 +1,28 @@
+/*
+ * Copyright (c) 2025-2026 Quant
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the “Software”),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
use criterion::{Criterion, Throughput, criterion_group, criterion_main};
-use entlib_native_helper::constant_time::ConstantTimeOps;
+use entlib_native_constant_time::constant_time::ConstantTimeOps;
const BATCH: u64 = 4096;
diff --git a/core/helper/constant_time.md b/crypto/constant-time/constant_time.md
similarity index 100%
rename from core/helper/constant_time.md
rename to crypto/constant-time/constant_time.md
diff --git a/core/helper/constant_time_asm.md b/crypto/constant-time/constant_time_asm.md
similarity index 100%
rename from core/helper/constant_time_asm.md
rename to crypto/constant-time/constant_time_asm.md
diff --git a/core/helper/src/constant_time.rs b/crypto/constant-time/src/constant_time.rs
similarity index 76%
rename from core/helper/src/constant_time.rs
rename to crypto/constant-time/src/constant_time.rs
index 9d738d5..aaaa71a 100644
--- a/core/helper/src/constant_time.rs
+++ b/crypto/constant-time/src/constant_time.rs
@@ -1,3 +1,25 @@
+/*
+ * Copyright (c) 2025-2026 Quant
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the “Software”),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
//! 상수-시간(Constant-Time) 연산을 위한 통합 트레이트 및 구현체
//!
//! 이 모듈은 민감 데이터를 다룰 때 타이밍 공격(timing attack)을 방지하기 위해
@@ -9,7 +31,7 @@
//! # Security Warning
//! 이 코드는 `entlib-native`의 일부로, 컴파일러 최적화 레벨이나 타겟 아키텍처에 따라
//! 안전성이 달라질 수 있습니다. `x86_64` 및 `aarch64`에서는 인라인 어셈블리를 사용하여
-//! 컴파일러 최적화를 원천 차단하며, 기타 아키텍처에서는 `core::hint::black_box`로 폴백합니다.
+//! 컴파일러 최적화를 원천 차단하며, 기타 아키텍처에서는 `internal::hint::black_box`로 폴백합니다.
//! 최종 바이너리에 대한 어셈블리 검증이 권장됩니다.
//!
//! 이 모듈은 단순한 개념 증명(PoC) 기능을 가지지 않습니다.
diff --git a/core/helper/src/constant_time_asm.rs b/crypto/constant-time/src/constant_time_asm.rs
similarity index 93%
rename from core/helper/src/constant_time_asm.rs
rename to crypto/constant-time/src/constant_time_asm.rs
index 3b88017..f1fd7d9 100644
--- a/core/helper/src/constant_time_asm.rs
+++ b/crypto/constant-time/src/constant_time_asm.rs
@@ -1,3 +1,25 @@
+/*
+ * Copyright (c) 2025-2026 Quant
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the “Software”),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
//! 아키텍처별 인라인 어셈블리 기반 상수-시간 프리미티브
//!
//! # Author
@@ -17,7 +39,7 @@
///
/// # Usage
/// ```rust
-/// use entlib_native_helper::constant_time_asm::CtPrimitive;
+/// use entlib_native_constant_time::constant_time_asm::CtPrimitive;
///
/// let a = 10u32;
/// let b = 20u32;
@@ -33,7 +55,7 @@
/// ```
///
/// # Safety
-/// 이 트레이트의 구현체들은 컴파일러 최적화를 방지하기 위해 `core::hint::black_box` 또는
+/// 이 트레이트의 구현체들은 컴파일러 최적화를 방지하기 위해 `internal::hint::black_box` 또는
/// 인라인 어셈블리(`asm!`)를 사용합니다. 하지만 특정 아키텍처나 컴파일러 버전에서는
/// 의도치 않은 최적화가 발생할 수 있으므로, 중요한 보안 로직에 사용할 경우
/// 생성된 어셈블리 코드를 검증하는 것이 좋습니다.
diff --git a/core/helper/tests/constant_time_asm_test.rs b/crypto/constant-time/tests/constant_time_asm_test.rs
similarity index 66%
rename from core/helper/tests/constant_time_asm_test.rs
rename to crypto/constant-time/tests/constant_time_asm_test.rs
index add0e48..a013eda 100644
--- a/core/helper/tests/constant_time_asm_test.rs
+++ b/crypto/constant-time/tests/constant_time_asm_test.rs
@@ -1,5 +1,27 @@
+/*
+ * Copyright (c) 2025-2026 Quant
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the “Software”),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
#![cfg(feature = "ct-tests")]
-use entlib_native_helper::constant_time_asm::CtPrimitive;
+use entlib_native_constant_time::constant_time_asm::CtPrimitive;
macro_rules! test_ct_primitive {
($test_name:ident, $t:ty, $zero:expr, $one:expr, $neg:expr) => {
diff --git a/core/helper/tests/constant_time_test.rs b/crypto/constant-time/tests/constant_time_test.rs
similarity index 96%
rename from core/helper/tests/constant_time_test.rs
rename to crypto/constant-time/tests/constant_time_test.rs
index a7557bb..c5a7b77 100644
--- a/core/helper/tests/constant_time_test.rs
+++ b/crypto/constant-time/tests/constant_time_test.rs
@@ -1,4 +1,4 @@
-use entlib_native_helper::constant_time::ConstantTimeOps;
+use entlib_native_constant_time::constant_time::ConstantTimeOps;
#[test]
fn test_ct_eq_u32() {
diff --git a/core/helper/tests/ct_cross_validation.rs b/crypto/constant-time/tests/ct_cross_validation.rs
similarity index 98%
rename from core/helper/tests/ct_cross_validation.rs
rename to crypto/constant-time/tests/ct_cross_validation.rs
index 7093f8c..3da648c 100644
--- a/core/helper/tests/ct_cross_validation.rs
+++ b/crypto/constant-time/tests/ct_cross_validation.rs
@@ -1,6 +1,6 @@
#[cfg(test)]
mod tests {
- use entlib_native_helper::constant_time::ConstantTimeOps;
+ use entlib_native_constant_time::constant_time::ConstantTimeOps;
use proptest::prelude::*;
use subtle::Choice;
diff --git a/crypto/core-secure/benches/secure_buffer_bench.rs b/crypto/core-secure/benches/secure_buffer_bench.rs
new file mode 100644
index 0000000..6922b08
--- /dev/null
+++ b/crypto/core-secure/benches/secure_buffer_bench.rs
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2025-2026 Quant
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the “Software”),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+use criterion::{BatchSize, BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
+
+use entlib_native_core_secure::secure_buffer::SecureBuffer;
+
+const SIZES: &[usize] = &[64, 256, 1_024, 4_096, 65_536, 1_048_576];
+
+//
+// SecureBuffer drop(wipe) 처리량
+//
+
+fn secure_buffer_wipe_throughput(c: &mut Criterion) {
+ let mut group = c.benchmark_group("throughput/secure_buffer_wipe");
+
+ for &size in SIZES {
+ group.throughput(Throughput::Bytes(size as u64));
+
+ let batch_size = if size >= 65_536 {
+ BatchSize::LargeInput
+ } else {
+ BatchSize::SmallInput
+ };
+
+ group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
+ b.iter_batched(
+ || SecureBuffer {
+ inner: vec![0xAA; size],
+ },
+ drop, // |buf| drop(buf)
+ batch_size,
+ )
+ });
+ }
+
+ group.finish();
+}
+
+//
+// Criterion 설정
+//
+
+criterion_group!(benches, secure_buffer_wipe_throughput);
+criterion_main!(benches);
diff --git a/crypto/core-secure/src/secure_buffer.rs b/crypto/core-secure/src/secure_buffer.rs
new file mode 100644
index 0000000..2e2c562
--- /dev/null
+++ b/crypto/core-secure/src/secure_buffer.rs
@@ -0,0 +1,63 @@
+use core::ptr::write_volatile;
+use core::sync::atomic::{Ordering, compiler_fence};
+
+/// 메모리 소거를 보장하는 보안 버퍼 구조체입니다.
+///
+/// 이 구조체는 쉽게 말해 Rust가 할당하고 소유하는 메모리입니다.
+/// 이 네이티브 코드상에서 연산의 '결과물'을 새로 생성할 때 사용됩니다.
+/// `Base64` 디코딩 결과, 암호화된 사이퍼텍스트 생성 등의 상황을 예로
+/// 들 수 있습니다.
+///
+/// 민감한 데이터를 사용한 연산 후 그 결과를 Java Heap으로 전달하는 경우,
+/// 가비지 컬렉터의 생명주기에 종속되어 위험에 다시 노출되는 딜레마를 가지게
+/// 됩니다. 이 문제를 해결하기 위해 단순히 `byte[]`와 같은 Java 데이터로
+/// 직렬화(serialize)하지 않고, 이 구조체에 저장합니다.
+///
+/// Java 측으로는 오직 해당 메모리의 원시 포인만이 전달되기 떄문에 안전한
+/// 데이터 관리가 가능합니다.
+pub struct SecureBuffer {
+ pub inner: Vec,
+}
+
+impl Drop for SecureBuffer {
+ fn drop(&mut self) {
+ for byte in self.inner.iter_mut() {
+ // volatile write -> 컴파일러 dce 최적화 방지함
+ unsafe {
+ write_volatile(byte, 0);
+ }
+ }
+ // 메모리 배리어로 소거 순서 보장
+ compiler_fence(Ordering::SeqCst);
+ }
+}
+
+/// 메모리 소거를 보장하며, Java 측에서 사용되는 보안 버퍼 구조체입니다.
+///
+/// 이 구조체는 Java 측에서 민감 데이터를 이미 Off-Heap 영역에 할당하여
+/// 들고 있을 때 사용됩니다.
+///
+/// 민감한 데이터를 사용한 연산 후 그 결과를 Java Heap으로 전달하는 경우,
+/// 가비지 컬렉터의 생명주기에 종속되어 위험에 다시 노출되는 딜레마를 가지게
+/// 됩니다. 이 문제를 해결하기 위해 단순히 `byte[]`와 같은 Java 데이터로
+/// 직렬화(serialize)하지 않고, 이 구조체에 저장합니다.
+#[repr(C)]
+pub struct FFIExternalSecureBuffer {
+ pub inner: *mut u8,
+ pub len: usize,
+}
+
+impl Drop for FFIExternalSecureBuffer {
+ fn drop(&mut self) {
+ if self.inner.is_null() || self.len == 0 {
+ return;
+ }
+ unsafe {
+ let slice = core::slice::from_raw_parts_mut(self.inner, self.len);
+ for byte in slice.iter_mut() {
+ write_volatile(byte, 0);
+ }
+ }
+ compiler_fence(Ordering::SeqCst);
+ }
+}
diff --git a/crypto/rng/Cargo.toml b/crypto/rng/Cargo.toml
index 71a9b1f..a5ebc73 100644
--- a/crypto/rng/Cargo.toml
+++ b/crypto/rng/Cargo.toml
@@ -5,10 +5,12 @@ edition.workspace = true
authors.workspace = true
license.workspace = true
-#[lib]
-#crate-type = ["cdylib", "rlib"]
-
[dependencies]
-entlib-native-helper.workspace = true
+entlib-native-core-secure.workspace = true
+
+[dev-dependencies]
+criterion = { version = "0.8.2", features = ["html_reports"] }
-[dev-dependencies]
\ No newline at end of file
+[[bench]]
+name = "anu_qrng_bench"
+harness = false
\ No newline at end of file
diff --git a/crypto/rng/benches/anu_qrng_bench.rs b/crypto/rng/benches/anu_qrng_bench.rs
new file mode 100644
index 0000000..7b16857
--- /dev/null
+++ b/crypto/rng/benches/anu_qrng_bench.rs
@@ -0,0 +1,98 @@
+//! 베이스가 TLS 통신 처리라 기본적으로 그닥 성능이 좋지 못함
+//! 하지만 난수가 Java로 전달되기 전의 지연율 상한성을 파악할 수 있어서
+//! 혼합 rng 모듈이 양자 난수를 최초 1회만 받아야 하는 수학적 근거가 됌.
+
+use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
+use entlib_native_rng::anu_qrng::AnuQrngClient;
+use std::hint::black_box; // Q. T. Felix NOTE: std::hint blackbox
+use std::time::Duration;
+
+/// 섀넌 엔트로피(shannon entropy) 계산 함수
+///
+/// 주어진 바이트 시퀀스의 정보량을 측정합니다.
+/// 대규모 및 군사급 보안 시스템에서는 난수 스트림의 엔트로피가 8.0에 수렴하는지 실시간으로
+/// 평가(security evaluation)해야해서 해당 연산의 처리량(throughput) 측정은 필수적입니다.
+fn compute_shannon_entropy(data: &[u8]) -> f64 {
+ let mut counts = [0usize; 256];
+ for &byte in data {
+ counts[byte as usize] += 1;
+ }
+
+ let mut entropy = 0.0;
+ let len = data.len() as f64;
+
+ for &count in &counts {
+ if count > 0 {
+ let probability = count as f64 / len;
+ entropy -= probability * probability.log2();
+ }
+ }
+
+ entropy
+}
+
+/// 양자 난수 추출 처리량(throughput) 벤치마크
+///
+/// 외부 네트워크(curl) 호출 및 무의존성(zero-dependency) json 파서의 병목을 분석합니다.
+fn bench_qrng_throughput(c: &mut Criterion) {
+ let mut group = c.benchmark_group("QRNG_Throughput");
+
+ // 네트워크 지연(network latency)으로 인한 벤치마크 타임아웃을 방지하기 위해
+ // 샘플 크기(sample size)를 축소하고 측정 시간을 연장합니다.
+ group.sample_size(10);
+ group.measurement_time(Duration::from_secs(20));
+
+ // 전략 패턴(strategy pattern)의 초기 상태 구성에 필요한 32바이트(키) 및 최대 허용치 1024바이트
+ let lengths = [32usize, 256, 1024];
+
+ for &len in &lengths {
+ group.throughput(Throughput::Bytes(len as u64));
+ group.bench_with_input(
+ BenchmarkId::new("fetch_secure_bytes", len),
+ &len,
+ |b, &size| {
+ b.iter(|| {
+ // black_box를 통해 컴파일러의 데드 코드 제거(dead code elimination) 최적화 방지
+ let result = AnuQrngClient::fetch_secure_bytes(black_box(size));
+
+ // 네트워크 실패 시 벤치마크 패닉을 방지하고 에러 코드를 반환하도록 처리
+ if let Ok(buffer) = result {
+ black_box(buffer);
+ }
+ });
+ },
+ );
+ }
+
+ group.finish();
+}
+
+/// 실시간 보안성 평가(security evaluation) 연산 오버헤드 벤치마크
+fn bench_qrng_security_evaluation(c: &mut Criterion) {
+ let mut group = c.benchmark_group("QRNG_Security_Evaluation");
+
+ let sizes = [32usize, 256, 1024];
+
+ for &size in &sizes {
+ // 실제 API 호출로 인한 네트워크 병목을 배제하고, 순수 엔트로피 연산의
+ // CPU 처리량(cpu throughput)만을 정밀하게 평가하기 위해 더미(dummy) 데이터를 구성합니다.
+ let mock_data: Vec = (0..size).map(|i| (i % 256) as u8).collect();
+
+ group.throughput(Throughput::Bytes(size as u64));
+ group.bench_with_input(BenchmarkId::new("shannon_entropy", size), &size, |b, _| {
+ b.iter(|| {
+ let entropy = compute_shannon_entropy(black_box(&mock_data));
+ black_box(entropy);
+ });
+ });
+ }
+
+ group.finish();
+}
+
+criterion_group!(
+ benches,
+ bench_qrng_throughput,
+ bench_qrng_security_evaluation
+);
+criterion_main!(benches);
diff --git a/crypto/rng/src/anu_qrng.rs b/crypto/rng/src/anu_qrng.rs
new file mode 100644
index 0000000..5bf0bd9
--- /dev/null
+++ b/crypto/rng/src/anu_qrng.rs
@@ -0,0 +1,77 @@
+//! ANU QRNG API(Streaming)
+//!
+//! TLS통신을 통해 작업을 수행하기 떄문에
+//! 네트워크 지연 등의 문제가 있습니다.
+//!
+//! # Author
+//! Q. T. Felix
+
+use crate::base_rng::RngError;
+use entlib_native_core_secure::secure_buffer::SecureBuffer;
+use std::process::Command;
+use std::str;
+use std::vec::Vec;
+
+pub struct AnuQrngClient;
+
+impl AnuQrngClient {
+ pub fn fetch_secure_bytes(length: usize) -> Result {
+ if !(1..=1024).contains(&length) {
+ return Err(RngError::ParseError);
+ }
+
+ let url = format!(
+ "https://qrng.anu.edu.au/API/jsonI.php?length={}&type=uint8",
+ length
+ );
+
+ // -f (HTTP 오류 시 실패), -L (리다이렉트 추적)
+ let output = Command::new("curl")
+ .arg("-s")
+ .arg("-f")
+ .arg("-L")
+ .arg(url)
+ .output()
+ .map_err(|_| RngError::NetworkFailure)?;
+
+ if !output.status.success() {
+ return Err(RngError::NetworkFailure);
+ }
+
+ let response_str = str::from_utf8(&output.stdout).map_err(|_| RngError::ParseError)?;
+
+ let bytes = Self::parse_json_data(response_str)?;
+
+ Ok(SecureBuffer { inner: bytes })
+ }
+
+ /// 공백 및 포맷 변경에 강건하게 대응하는 개선된 슬라이싱 파서
+ pub fn parse_json_data(json: &str) -> Result, RngError> {
+ let data_key = "\"data\"";
+
+ // "data" 키의 시작 위치 탐색
+ let key_idx = json.find(data_key).ok_or(RngError::ParseError)?;
+
+ // 키 이후부터 첫 번째 '[' 탐색
+ let start_bracket = json[key_idx..].find('[').ok_or(RngError::ParseError)? + key_idx + 1;
+
+ // '[' 이후부터 첫 번째 ']' 탐색
+ let end_bracket = json[start_bracket..]
+ .find(']')
+ .ok_or(RngError::ParseError)?
+ + start_bracket;
+
+ let data_part = &json[start_bracket..end_bracket];
+ let mut bytes = Vec::new();
+
+ for val_str in data_part.split(',') {
+ let trimmed = val_str.trim();
+ if !trimmed.is_empty() {
+ let val = trimmed.parse::().map_err(|_| RngError::ParseError)?;
+ bytes.push(val);
+ }
+ }
+
+ Ok(bytes)
+ }
+}
diff --git a/crypto/rng/src/base_rng.rs b/crypto/rng/src/base_rng.rs
index c599db7..2da5f69 100644
--- a/crypto/rng/src/base_rng.rs
+++ b/crypto/rng/src/base_rng.rs
@@ -12,7 +12,7 @@
use core::arch::asm;
use core::ptr::{copy_nonoverlapping, write_unaligned, write_volatile};
use core::sync::atomic::compiler_fence;
-use entlib_native_helper::secure_buffer::SecureBuffer;
+use entlib_native_core_secure::secure_buffer::SecureBuffer;
use std::sync::atomic::Ordering;
use std::vec::Vec;
@@ -23,6 +23,10 @@ pub enum RngError {
UnsupportedHardware,
/// 엔트로피 풀 고갈 또는 하드웨어 응답에 실패했습니다.
EntropyDepletion,
+ /// 양자 난수 네트워크 요청(curl)이 실패했습니다.
+ NetworkFailure,
+ /// 양자 난수 API 응답 데이터 파싱에 실패했습니다.
+ ParseError,
}
/// 요청한 길이만큼의 진난수를 포함하는 보안 버퍼를 반환합니다.
diff --git a/crypto/rng/src/lib.rs b/crypto/rng/src/lib.rs
index 5179aa5..4bd6aa2 100644
--- a/crypto/rng/src/lib.rs
+++ b/crypto/rng/src/lib.rs
@@ -1,2 +1,3 @@
+pub mod anu_qrng;
pub mod base_rng;
pub mod mixed;
diff --git a/crypto/rng/src/mixed.rs b/crypto/rng/src/mixed.rs
index d9edc21..b3b23d9 100644
--- a/crypto/rng/src/mixed.rs
+++ b/crypto/rng/src/mixed.rs
@@ -13,10 +13,11 @@
//! # Note
//! 이 기능은 곧 `chacha20` 모듈로 차별화됩니다.
+use crate::anu_qrng::AnuQrngClient;
use crate::base_rng::{RngError, generate_hardware_random_bytes};
use core::ptr::{copy_nonoverlapping, write_volatile};
use core::sync::atomic::{Ordering, compiler_fence};
-use entlib_native_helper::secure_buffer::SecureBuffer;
+use entlib_native_core_secure::secure_buffer::SecureBuffer;
/// 상수 시간(constant-time) 연산을 보장하는 chacha20 쿼터 라운드(quarter round)
macro_rules! quarter_round {
@@ -36,6 +37,14 @@ macro_rules! quarter_round {
};
}
+/// 엔트로피 소스(entropy source)를 결정하는 전략(strategy) 열거형
+pub enum EntropyStrategy {
+ /// 로컬 프로세서의 하드웨어 난수 생성기(`rdseed`, `rndr` 등)를 사용합니다.
+ LocalHardware,
+ /// ANU 양자 난수 API를 통해 진공 양자 요동(quantum vacuum fluctuations) 데이터를 가져옵니다.
+ QuantumNetwork,
+}
+
/// 혼합 난수 생성기(mixed rng) 구조체
///
/// 하드웨어 진난수를 시드(seed)로 사용하여 512-비트(bit) 상태를 초기화합니다.
@@ -44,11 +53,19 @@ pub struct MixedRng {
}
impl MixedRng {
- /// 하드웨어 명령어를 호출하여 새로운 혼합 난수 생성기를 인스턴스화합니다.
- pub fn new() -> Result {
- // 32바이트 키(key) 및 12바이트 논스(nonce) 추출
- let hw_key = generate_hardware_random_bytes(32)?;
- let hw_nonce = generate_hardware_random_bytes(12)?;
+ /// 지정된 엔트로피 전략을 사용하여 새로운 혼합 난수 생성기를 인스턴스화합니다.
+ pub fn new(strategy: EntropyStrategy) -> Result {
+ // 전략 패턴(strategy pattern)에 따른 시드(seed) 추출 분기
+ let (hw_key, hw_nonce) = match strategy {
+ EntropyStrategy::LocalHardware => (
+ generate_hardware_random_bytes(32)?,
+ generate_hardware_random_bytes(12)?,
+ ),
+ EntropyStrategy::QuantumNetwork => (
+ AnuQrngClient::fetch_secure_bytes(32)?,
+ AnuQrngClient::fetch_secure_bytes(12)?,
+ ),
+ };
let mut state = [0u32; 16];
@@ -73,6 +90,9 @@ impl MixedRng {
// 블록 카운터(block counter)
state[12] = 0;
+ // hw_key와 hw_nonce는 SecureBuffer의 Drop 트레이트 구현에 의해
+ // 스코프를 벗어날 때 자동으로 데이터 소거(zeroize)가 수행됨
+
Ok(Self { state })
}
diff --git a/crypto/rng/tests/anu_qrng_test.rs b/crypto/rng/tests/anu_qrng_test.rs
new file mode 100644
index 0000000..71d726e
--- /dev/null
+++ b/crypto/rng/tests/anu_qrng_test.rs
@@ -0,0 +1,88 @@
+#[cfg(test)]
+mod tests {
+ use entlib_native_rng::anu_qrng::*;
+ use entlib_native_rng::base_rng::RngError;
+ use std::collections::HashSet;
+
+ #[test]
+ fn test_fetch_secure_bytes_valid() {
+ // 정상적인 길이의 난수 요청이 보안 버퍼(secure buffer)에 올바르게 적재되는지 검증
+ let target_length = 32;
+
+ match AnuQrngClient::fetch_secure_bytes(target_length) {
+ Ok(buffer) => {
+ assert_eq!(
+ buffer.inner.len(),
+ target_length,
+ "요청한 길이와 반환된 버퍼의 크기가 일치하지 않습니다."
+ );
+ }
+ Err(RngError::NetworkFailure) | Err(RngError::ParseError) => {
+ // 폐쇄망 환경이거나 ANU 서버의 정책 변경으로 인한 통신 불가 시 테스트를 안전하게 우회
+ println!(
+ "Skipping network test: ANU QRNG service is currently unreachable or format changed."
+ );
+ }
+ Err(e) => {
+ panic!(
+ "예기치 않은 보안 모듈 에러(unexpected security module error): {:?}",
+ e
+ );
+ }
+ }
+ }
+
+ #[test]
+ fn test_fetch_secure_bytes_invalid_bounds() {
+ // 버퍼 오버플로 및 비정상적 메모리 할당을 방지하기 위한 경계값 검증
+ // api가 허용하는 1~1024 범위를 벗어난 요청 시 명시적 에러반환
+ assert!(AnuQrngClient::fetch_secure_bytes(0).is_err());
+ assert!(AnuQrngClient::fetch_secure_bytes(1025).is_err());
+ }
+
+ #[test]
+ fn test_parse_json_data_stability() {
+ // 외부 라이브러리 없이 구현된 슬라이싱(slicing) 기반 파서의 정상 작동 검증
+ let mock_json =
+ r#"{"type":"uint8","length":5,"data":[12, 255, 0, 128, 42],"success":true}"#;
+ let result = AnuQrngClient::parse_json_data(mock_json);
+
+ assert!(result.is_ok());
+ let bytes = result.unwrap();
+ assert_eq!(bytes, vec![12, 255, 0, 128, 42]);
+ }
+
+ #[test]
+ fn test_parse_json_data_malformed() {
+ // 손상된 페이로드(malformed payload) 주입 시 시스템이 패닉(panic)에 빠지지 않고
+ // 안전하게 에러(error)를 반환하는지 검증
+ let missing_data_json = r#"{"type":"uint8","length":5,"success":true}"#;
+ assert!(AnuQrngClient::parse_json_data(missing_data_json).is_err());
+
+ let unclosed_bracket_json = r#"{"type":"uint8","length":5,"data":[12, 255, 0"#;
+ assert!(AnuQrngClient::parse_json_data(unclosed_bracket_json).is_err());
+
+ let invalid_type_json = r#"{"type":"uint8","length":1,"data":[NaN],"success":false}"#;
+ assert!(AnuQrngClient::parse_json_data(invalid_type_json).is_err());
+ }
+
+ #[test]
+ fn test_randomness_basic_entropy() {
+ // 생성된 양자 난수(quantum random number)의 기본적인 무작위성(randomness)을 평가
+ // 추출된 바이트 시퀀스 내의 고유값(unique values) 개수를 확인하여, 통신 오류로 인해
+ // 0으로만 채워진 배열(zeroed array)이 반환되는 치명적 보안 결함을 방지함
+ let len = 100;
+ if let Ok(buffer) = AnuQrngClient::fetch_secure_bytes(len) {
+ let mut unique_values = HashSet::new();
+ for &byte in &buffer.inner {
+ unique_values.insert(byte);
+ }
+
+ // 100바이트 표본에서 이론적 엔트로피 $H(X)$가 0에 수렴하는 경우(동일한 값만 반복)를 실패로 간주합니다.
+ assert!(
+ unique_values.len() > 10,
+ "추출된 난수의 엔트로피가 보안 요구사항을 충족하지 못합니다."
+ );
+ }
+ }
+}
diff --git a/crypto/rng/tests/hw_based_rng_test.rs b/crypto/rng/tests/hw_based_rng_test.rs
index 7440255..e0b1a3e 100644
--- a/crypto/rng/tests/hw_based_rng_test.rs
+++ b/crypto/rng/tests/hw_based_rng_test.rs
@@ -1,6 +1,6 @@
#[cfg(test)]
mod tests {
- use entlib_native_helper::secure_buffer::SecureBuffer;
+ use entlib_native_core_secure::secure_buffer::SecureBuffer;
use entlib_native_rng::base_rng::{RngError, generate_hardware_random_bytes, next_generate};
#[test]
diff --git a/crypto/rng/tests/mixed_rng_test.rs b/crypto/rng/tests/mixed_rng_test.rs
index 8b6fa92..265bfd2 100644
--- a/crypto/rng/tests/mixed_rng_test.rs
+++ b/crypto/rng/tests/mixed_rng_test.rs
@@ -1,38 +1,59 @@
#[cfg(test)]
mod tests {
use entlib_native_rng::base_rng::RngError;
- use entlib_native_rng::mixed::MixedRng;
+ use entlib_native_rng::mixed::{EntropyStrategy, MixedRng};
+
+ /// 등록된 모든 엔트로피 전략(entropy strategy)에 대해 동일한 테스트를 수행하기 위한 헬퍼 함수입니다.
+ fn run_with_strategies(test_logic: F)
+ where
+ F: Fn(EntropyStrategy, &str),
+ {
+ // 로컬 프로세서 하드웨어 난수 기반(trng)
+ test_logic(EntropyStrategy::LocalHardware, "LocalHardware");
+ // 외부 양자 난수 네트워크 통신 기반(qrng)
+ test_logic(EntropyStrategy::QuantumNetwork, "QuantumNetwork");
+ }
#[test]
fn test_mixed_rng_initialization() {
- // MixedRng 초기화 테스트
- match MixedRng::new() {
- Ok(rng) => {
- // 초기화 성공 시, 내부 상태가 0이 아닌지 확인 (간접적 확인)
- // state 필드는 private이므로 직접 접근 불가하지만,
- // generate 호출을 통해 동작 여부 확인 가능
- let mut rng = rng;
- let result = rng.generate(32);
- assert!(
- result.is_ok(),
- "Failed to generate random bytes after initialization"
- );
- }
- Err(RngError::UnsupportedHardware) => {
- println!("Skipping initialization test: Hardware RNG not supported");
- }
- Err(e) => {
- panic!("Failed to initialize MixedRng: {:?}", e);
+ // MixedRng 초기화 및 의존성 주입 테스트
+ run_with_strategies(|strategy, strategy_name| {
+ match MixedRng::new(strategy) {
+ Ok(mut rng) => {
+ // 초기화 성공 시, 32바이트(byte) 난수 생성을 통한 내부 상태 무결성 확인
+ let result = rng.generate(32);
+ assert!(
+ result.is_ok(),
+ "[{}] Failed to generate random bytes after initialization",
+ strategy_name
+ );
+ }
+ Err(RngError::UnsupportedHardware) => {
+ println!(
+ "[{}] Skipping initialization test: Hardware RNG not supported",
+ strategy_name
+ );
+ }
+ Err(RngError::NetworkFailure) | Err(RngError::ParseError) => {
+ // 오프라인 환경 또는 방화벽에 의한 네트워크 차단 시 테스트 우회(skip)
+ println!(
+ "[{}] Skipping initialization test: Network or parsing unavailable",
+ strategy_name
+ );
+ }
+ Err(e) => {
+ panic!("[{}] Failed to initialize MixedRng: {:?}", strategy_name, e);
+ }
}
- }
+ });
}
#[test]
fn test_mixed_rng_generation_length() {
- // 다양한 길이의 난수 생성 테스트
+ // 다양한 길이의 블록(block) 및 잔여 바이트 생성 테스트
let lengths = [0, 1, 16, 64, 128, 1024];
- match MixedRng::new() {
+ run_with_strategies(|strategy, strategy_name| match MixedRng::new(strategy) {
Ok(mut rng) => {
for &len in &lengths {
match rng.generate(len) {
@@ -40,76 +61,97 @@ mod tests {
assert_eq!(
buffer.inner.len(),
len,
- "Buffer length mismatch for requested length: {}",
+ "[{}] Buffer length mismatch for requested length: {}",
+ strategy_name,
len
);
}
Err(e) => {
panic!(
- "Failed to generate random bytes for length {}: {:?}",
- len, e
+ "[{}] Failed to generate random bytes for length {}: {:?}",
+ strategy_name, len, e
);
}
}
}
}
- Err(RngError::UnsupportedHardware) => {
- println!("Skipping length test: Hardware RNG not supported");
+ Err(RngError::UnsupportedHardware)
+ | Err(RngError::NetworkFailure)
+ | Err(RngError::ParseError) => {
+ println!(
+ "[{}] Skipping length test: Dependency unavailable",
+ strategy_name
+ );
}
Err(e) => {
- panic!("Failed to initialize MixedRng: {:?}", e);
+ panic!("[{}] Failed to initialize MixedRng: {:?}", strategy_name, e);
}
- }
+ });
}
#[test]
fn test_mixed_rng_randomness() {
- // 생성된 난수의 무작위성 (기본적인 중복 검사)
- match MixedRng::new() {
- Ok(mut rng) => {
- let len = 32;
- let buf1 = rng.generate(len).expect("First generation failed");
- let buf2 = rng.generate(len).expect("Second generation failed");
+ // 생성된 난수 스트림(random stream)의 멱등성(idempotence) 및 중복 검사
+ run_with_strategies(|strategy, strategy_name| {
+ match MixedRng::new(strategy) {
+ Ok(mut rng) => {
+ let len = 32;
+ let buf1 = rng.generate(len).expect("First generation failed");
+ let buf2 = rng.generate(len).expect("Second generation failed");
- // 연속 호출 시 동일한 값이 나오지 않는지 확인
- assert_ne!(
- buf1.inner, buf2.inner,
- "Two consecutive random generations produced identical output"
- );
- }
- Err(RngError::UnsupportedHardware) => {
- println!("Skipping randomness test: Hardware RNG not supported");
- }
- Err(e) => {
- panic!("Failed to initialize MixedRng: {:?}", e);
+ // 연속 호출 시 동일한 값이 출력되지 않음을 증명(내부 상태가 정상 갱신됨을 의미)
+ assert_ne!(
+ buf1.inner, buf2.inner,
+ "[{}] Two consecutive random generations produced identical output",
+ strategy_name
+ );
+ }
+ Err(RngError::UnsupportedHardware)
+ | Err(RngError::NetworkFailure)
+ | Err(RngError::ParseError) => {
+ println!(
+ "[{}] Skipping randomness test: Dependency unavailable",
+ strategy_name
+ );
+ }
+ Err(e) => {
+ panic!("[{}] Failed to initialize MixedRng: {:?}", strategy_name, e);
+ }
}
- }
+ });
}
#[test]
fn test_mixed_rng_block_counter_increment() {
- // 블록 카운터 증가에 따른 출력 변화 확인
- // 64바이트(1블록) 이상 생성 시 내부 카운터가 증가하여 다음 블록이 생성되어야 함
- match MixedRng::new() {
- Ok(mut rng) => {
- // 128바이트 생성 (2개 블록)
- let len = 128;
- let buffer = rng.generate(len).expect("Generation failed");
+ // chacha20 코어(core) 블록 카운터(block counter) 증가에 따른 출력 전이 검증
+ run_with_strategies(|strategy, strategy_name| {
+ match MixedRng::new(strategy) {
+ Ok(mut rng) => {
+ // 128바이트 생성 (64바이트 단위 2개 블록)
+ let len = 128;
+ let buffer = rng.generate(len).expect("Generation failed");
- let (first_block, second_block) = buffer.inner.split_at(64);
+ let (first_block, second_block) = buffer.inner.split_at(64);
- // 첫 번째 블록과 두 번째 블록이 달라야 함
- assert_ne!(
- first_block, second_block,
- "Consecutive blocks are identical, counter might not be incrementing"
- );
- }
- Err(RngError::UnsupportedHardware) => {
- println!("Skipping block counter test: Hardware RNG not supported");
- }
- Err(e) => {
- panic!("Failed to initialize MixedRng: {:?}", e);
+ // 블록 카운터가 올바르게 증가햇다면 두 블록의 암호학적 출력은 독립적이어야 함
+ assert_ne!(
+ first_block, second_block,
+ "[{}] Consecutive blocks are identical, counter might not be incrementing",
+ strategy_name
+ );
+ }
+ Err(RngError::UnsupportedHardware)
+ | Err(RngError::NetworkFailure)
+ | Err(RngError::ParseError) => {
+ println!(
+ "[{}] Skipping block counter test: Dependency unavailable",
+ strategy_name
+ );
+ }
+ Err(e) => {
+ panic!("[{}] Failed to initialize MixedRng: {:?}", strategy_name, e);
+ }
}
- }
+ });
}
}
diff --git a/crypto/sha2/Cargo.toml b/crypto/sha2/Cargo.toml
index 0553ee6..02e7a2b 100644
--- a/crypto/sha2/Cargo.toml
+++ b/crypto/sha2/Cargo.toml
@@ -9,10 +9,10 @@ license.workspace = true
crate-type = ["cdylib", "rlib"]
[dependencies]
-entlib-native-helper.workspace = true
[dev-dependencies]
criterion = { version = "0.8.2", features = ["html_reports"] }
+entlib-native-core-secure.workspace = true
entlib-native-rng.workspace = true
[[bench]]
diff --git a/crypto/sha2/benches/sha2_bench.rs b/crypto/sha2/benches/sha2_bench.rs
index d5e10ee..7e6d2a5 100644
--- a/crypto/sha2/benches/sha2_bench.rs
+++ b/crypto/sha2/benches/sha2_bench.rs
@@ -1,5 +1,5 @@
use criterion::{Criterion, Throughput, criterion_group, criterion_main};
-use entlib_native_helper::secure_buffer::SecureBuffer;
+use entlib_native_core_secure::secure_buffer::SecureBuffer;
use entlib_native_rng::base_rng::next_generate;
use entlib_native_sha2::api::*;
use std::hint::black_box;
diff --git a/crypto/sha3/Cargo.toml b/crypto/sha3/Cargo.toml
index ddf9c10..b169460 100644
--- a/crypto/sha3/Cargo.toml
+++ b/crypto/sha3/Cargo.toml
@@ -9,10 +9,10 @@ license.workspace = true
crate-type = ["cdylib", "rlib"]
[dependencies]
-entlib-native-helper.workspace = true
[dev-dependencies]
criterion = { version = "0.8.2", features = ["html_reports"] }
+entlib-native-core-secure.workspace = true
entlib-native-rng.workspace = true
[[bench]]
diff --git a/crypto/sha3/benches/sha3_bench.rs b/crypto/sha3/benches/sha3_bench.rs
index 8303b9b..9f611da 100644
--- a/crypto/sha3/benches/sha3_bench.rs
+++ b/crypto/sha3/benches/sha3_bench.rs
@@ -1,5 +1,5 @@
use criterion::{Criterion, Throughput, criterion_group, criterion_main};
-use entlib_native_helper::secure_buffer::SecureBuffer;
+use entlib_native_core_secure::secure_buffer::SecureBuffer;
use entlib_native_rng::base_rng::next_generate;
use entlib_native_sha3::api::*;
use std::hint::black_box;
diff --git a/core/ffi/Cargo.toml b/internal/ffi/Cargo.toml
similarity index 61%
rename from core/ffi/Cargo.toml
rename to internal/ffi/Cargo.toml
index 62bd654..dbdbb67 100644
--- a/core/ffi/Cargo.toml
+++ b/internal/ffi/Cargo.toml
@@ -20,19 +20,20 @@ strip = true
panic = "abort"
[dependencies]
-entlib-native-helper.workspace = true
+entlib-native-base64.workspace = true
+entlib-native-constant-time.workspace = true
+entlib-native-core-secure.workspace = true
+entlib-native-rng.workspace = true
entlib-native-sha2.workspace = true
entlib-native-sha3.workspace = true
-#libc = "0.2.182"
-#libc_alloc = "1.0.7"
[dev-dependencies]
-criterion = { version = "0.8.2", features = ["html_reports"] }
-
-[[bench]]
-name = "ffi_base64_bench"
-harness = false
-
-[[bench]]
-name = "ffi_wipe_bench"
-harness = false
\ No newline at end of file
+#criterion = { version = "0.8.2", features = ["html_reports"] }
+#
+#[[bench]]
+#name = "ffi_base64_bench"
+#harness = false
+#
+#[[bench]]
+#name = "ffi_wipe_bench"
+#harness = false
\ No newline at end of file
diff --git a/internal/ffi/src/base64_ffi.rs b/internal/ffi/src/base64_ffi.rs
new file mode 100644
index 0000000..4d4f18e
--- /dev/null
+++ b/internal/ffi/src/base64_ffi.rs
@@ -0,0 +1,219 @@
+//! 상수-시간(constant-time) Base64 인코딩·디코딩 FFI 모듈
+//!
+//! Java/Kotlin 측에서 호출자 할당(caller-alloc) 메모리에 직접 기록합니다.
+//! 모든 연산은 분기 없는 상수 시간으로 side-channel 공격에 저항합니다.
+//!
+//! # Author
+//! Q. T. Felix
+
+use entlib_native_base64::base64::{ct_b64_to_bin_u8, ct_bin_to_b64_u8};
+use entlib_native_constant_time::constant_time::ConstantTimeOps;
+use std::slice;
+
+/// Java 측에서 할당한 메모리에 `Base64` 인코딩 결과를 직접 기록하는 함수입니다.
+///
+/// # Arguments
+/// * `input_ptr` - 인코딩할 평문 배열의 포인터
+/// * `input_len` - 평문 배열의 길이
+/// * `out_ptr` - (호출자 할당) 인코딩된 결과를 기록할 메모리의 시작 포인터
+/// * `out_capacity` - 할당된 출력 메모리의 최대 바이트 크기
+///
+/// # Returns
+/// * `>= 0`: 성공 시 인코딩된 결과의 실제 바이트 길이 반환
+/// * `< 0`: 에러 코드 반환 (-1: `Null`, -2: `Capacity` 부족)
+///
+/// # Safety
+/// 이 함수는 raw pointer를 직접 다루므로 unsafe입니다. 호출자는 다음을 **반드시** 보장해야 합니다.
+///
+/// - `input_ptr`은 null이 아니며, `input_len` 바이트만큼 **읽기 유효**한 메모리를 가리켜야 합니다
+/// (정렬 요구사항 없음, u8 기준).
+/// - `out_ptr`은 null이 아니며, `out_capacity` 바이트만큼 **쓰기 유효**한 메모리를 가리켜야 합니다.
+/// - `input_ptr`과 `out_ptr`이 가리키는 메모리 영역은 서로 겹치지 않아야 합니다
+/// (aliasing violation → UB).
+/// - `out_capacity`는 내부에서 계산된 `required_capacity` 이상이어야 합니다
+/// (함수가 -2를 반환하지만, 호출 전 미리 확인 권장).
+/// - 호출 기간 동안 두 메모리 영역이 해제되거나 재할당되지 않아야 합니다.
+/// - 단일 스레드에서 호출되며, concurrent 접근이 없어야 합니다.
+///
+/// 함수 내부는 `write_volatile`과 constant-time 연산만 사용하므로
+/// timing attack 및 메모리 잔여 데이터 유출에 대한 군사급 보호를 제공합니다.
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn entlib_b64_encode_caller_alloc(
+ input_ptr: *const u8,
+ input_len: usize,
+ out_ptr: *mut u8,
+ out_capacity: usize,
+) -> isize {
+ // 포인터 유효성 검증
+ if input_ptr.is_null() || out_ptr.is_null() {
+ return -1;
+ }
+
+ // 필요 버퍼 크기 산출 및 오버플로우 방지
+ let required_capacity = match input_len.checked_add(2) {
+ Some(val) => (val / 3).checked_mul(4),
+ None => return -1,
+ };
+
+ // 호출자가 할당한 용량(capacity) 검증
+ if out_capacity < required_capacity.expect("entlib-native-ffi ERROR: overflow") {
+ return -2;
+ }
+
+ let input = unsafe { slice::from_raw_parts(input_ptr, input_len) };
+ let mut out_idx = 0;
+ let mut i = 0;
+
+ // 상수 시간 인코딩 쓰기 루프
+ while i < input_len {
+ let b0 = input[i];
+ let b1 = if i + 1 < input_len { input[i + 1] } else { 0 };
+ let b2 = if i + 2 < input_len { input[i + 2] } else { 0 };
+
+ let e0 = b0 >> 2;
+ let e1 = ((b0 & 0x03) << 4) | (b1 >> 4);
+ let e2 = ((b1 & 0x0F) << 2) | (b2 >> 6);
+ let e3 = b2 & 0x3F;
+
+ unsafe {
+ core::ptr::write_volatile(out_ptr.add(out_idx), ct_bin_to_b64_u8(e0));
+ core::ptr::write_volatile(out_ptr.add(out_idx + 1), ct_bin_to_b64_u8(e1));
+
+ // 패딩 처리 (상수 시간 마스킹으로 치환 권장)
+ let pad2 = if i + 1 < input_len {
+ ct_bin_to_b64_u8(e2)
+ } else {
+ b'='
+ };
+ let pad3 = if i + 2 < input_len {
+ ct_bin_to_b64_u8(e3)
+ } else {
+ b'='
+ };
+
+ core::ptr::write_volatile(out_ptr.add(out_idx + 2), pad2);
+ core::ptr::write_volatile(out_ptr.add(out_idx + 3), pad3);
+ }
+
+ out_idx += 4;
+ i += 3;
+ }
+
+ out_idx as isize
+}
+
+/// Java 측에서 할당한 메모리에 분기 없는 상수-시간(constant-time)으로 `Base64` 디코딩을 수행합니다.
+///
+/// # Arguments
+/// * `input_ptr` - 디코딩할 `Base64` 문자열의 포인터
+/// * `input_len` - 문자열의 바이트 길이
+/// * `out_ptr` - (호출자 할당) 복원된 평문을 기록할 메모리의 시작 포인터
+/// * `out_capacity` - 할당된 출력 메모리의 최대 바이트 크기
+///
+/// # Returns
+/// * `>= 0`: 성공 시 디코딩된 평문의 실제 바이트 길이 반환
+/// * `< 0`: 에러 코드 반환 (-1: `Null`, -2: `Capacity` 부족, -3: 디코딩 중 유효하지 않은 문자열 감지)
+///
+/// # Safety
+/// 이 함수는 raw pointer를 직접 다루므로 unsafe입니다. 호출자는 다음을 **반드시** 보장해야 합니다.
+///
+/// - `input_ptr`은 null이 아니며, `input_len` 바이트만큼 **읽기 유효**한 메모리를 가리켜야 합니다.
+/// - `out_ptr`은 null이 아니며, `out_capacity` 바이트만큼 **쓰기 유효**한 메모리를 가리켜야 합니다.
+/// - `input_ptr`과 `out_ptr`이 가리키는 메모리 영역은 서로 겹치지 않아야 합니다 (aliasing UB 방지).
+/// - `out_capacity`는 `(input_len / 4 + 1) * 3` 이상이어야 합니다 (함수가 -2 반환).
+/// - 호출 기간 동안 메모리 영역이 유효해야 합니다.
+/// - 단일 스레드 호출, concurrent 접근 금지.
+///
+/// 함수는 `ct_b64_to_bin_u8`와 [ConstantTimeOps::ct_select] 연산만 사용하며,
+/// `write_volatile`로 메모리 잔여 데이터 유출을 방지합니다.
+/// 잘못된 Base64 문자는 -3 에러로 안전하게 처리되므로 side-channel 누출이 없습니다.
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn entlib_b64_decode_caller_alloc(
+ input_ptr: *const u8,
+ input_len: usize,
+ out_ptr: *mut u8,
+ out_capacity: usize,
+) -> isize {
+ // 포인터 유효성 검증
+ if input_ptr.is_null() || out_ptr.is_null() {
+ return -1;
+ }
+
+ // 최대 필요 버퍼 크기 산출 (여유 공간 3바이트 포함)
+ let max_required_capacity = (input_len / 4 + 1) * 3;
+
+ // 호출자가 할당한 용량 검증
+ if out_capacity < max_required_capacity {
+ return -2;
+ }
+
+ let input = unsafe { slice::from_raw_parts(input_ptr, input_len) };
+
+ let mut error_accum = 0u8;
+ let mut acc = 0u32;
+ let mut buf_idx = 0usize;
+ let mut out_idx = 0usize;
+
+ for &byte in input {
+ let decoded = ct_b64_to_bin_u8(byte);
+
+ let is_err = decoded.ct_eq(0xFF);
+ let is_pad = decoded.ct_eq(0x81);
+ let is_ws = decoded.ct_eq(0x80);
+
+ let is_valid = !is_err & !is_pad & !is_ws;
+ error_accum |= is_err & 0x01;
+
+ let valid_mask_u32 = (is_valid as i8 as i32) as u32;
+ let valid_mask_usize = (is_valid as i8 as isize) as usize;
+
+ let next_acc = (acc << 6) | (decoded as u32 & 0x3F);
+ acc = next_acc.ct_select(acc, valid_mask_u32);
+
+ let next_buf_idx = buf_idx + 1;
+ buf_idx = next_buf_idx.ct_select(buf_idx, valid_mask_usize);
+
+ let is_full = buf_idx.ct_eq(4);
+
+ let b0 = (acc >> 16) as u8;
+ let b1 = (acc >> 8) as u8;
+ let b2 = acc as u8;
+
+ unsafe {
+ core::ptr::write_volatile(out_ptr.add(out_idx), b0);
+ core::ptr::write_volatile(out_ptr.add(out_idx + 1), b1);
+ core::ptr::write_volatile(out_ptr.add(out_idx + 2), b2);
+ }
+
+ let next_out_idx = out_idx + 3;
+ out_idx = next_out_idx.ct_select(out_idx, is_full);
+
+ acc = 0u32.ct_select(acc, is_full as u32);
+ buf_idx = 0usize.ct_select(buf_idx, is_full);
+ }
+
+ let is_two = buf_idx.ct_eq(2);
+ let is_three = buf_idx.ct_eq(3);
+
+ let b0_2 = (acc >> 4) as u8;
+ let b0_3 = (acc >> 10) as u8;
+ let b1_3 = (acc >> 2) as u8;
+
+ let final_b0 = b0_2.ct_select(0, is_two as u8) | b0_3.ct_select(0, is_three as u8);
+ let final_b1 = b1_3.ct_select(0, is_three as u8);
+
+ unsafe {
+ core::ptr::write_volatile(out_ptr.add(out_idx), final_b0);
+ core::ptr::write_volatile(out_ptr.add(out_idx + 1), final_b1);
+ }
+
+ let add_len = 1usize.ct_select(0, is_two) | 2usize.ct_select(0, is_three);
+ out_idx += add_len;
+
+ // 에러 플래그 누적 여부 확인 후 분기 반환
+ if error_accum != 0 {
+ return -3;
+ }
+
+ out_idx as isize
+}
diff --git a/core/ffi/src/lib.rs b/internal/ffi/src/lib.rs
similarity index 90%
rename from core/ffi/src/lib.rs
rename to internal/ffi/src/lib.rs
index 46243bc..6edac8e 100644
--- a/core/ffi/src/lib.rs
+++ b/internal/ffi/src/lib.rs
@@ -1,6 +1,7 @@
pub mod base64_ffi;
-pub mod secure_buffer;
-pub mod sha2;
+mod rng_ffi;
+pub mod secure_buffer_ffi;
+pub mod sha2_ffi;
/// ffi 작업 중 발생할 수 있는 상태 코드 (status code)
#[repr(C)]
@@ -28,7 +29,7 @@ pub enum FFIStatus {
//
// #[cfg(not(test))]
// #[panic_handler]
-// fn panic(_info: &core::panic::PanicInfo) -> ! {
+// fn panic(_info: &internal::panic::PanicInfo) -> ! {
// // 민감 데이터 유출 방지를 위해 즉시 해제
// unsafe {
// libc::abort();
diff --git a/internal/ffi/src/rng_ffi.rs b/internal/ffi/src/rng_ffi.rs
new file mode 100644
index 0000000..9e3a418
--- /dev/null
+++ b/internal/ffi/src/rng_ffi.rs
@@ -0,0 +1,158 @@
+//! 난수 생성기 FFI 브릿지 모듈
+//!
+//! `base_rng` (하드웨어 TRNG)와 `mixed` (ChaCha20 혼합 RNG)를 Java/Kotlin 네이티브 환경으로
+//! 안전하게 노출합니다. ANU QRNG 네트워크 엔트로피도 전략 선택으로 사용 가능.
+//!
+//! # Security
+//! - 모든 민감 상태는 `MixedRng::Drop` + `SecureBuffer::Drop`에 의해 강제 zeroize
+//! - FFI 경계에서 철저한 null 체크 + 에러 코드 매핑
+//! - Rust 2024 에디션 완벽 호환 (unsafe-op-in-unsafe-fn 해결)
+//!
+//! # Author
+//! Q. T. Felix
+
+use core::ptr;
+use entlib_native_core_secure::secure_buffer::SecureBuffer;
+use entlib_native_rng::base_rng::{RngError, generate_hardware_random_bytes, next_generate};
+use entlib_native_rng::mixed::{EntropyStrategy, MixedRng};
+use std::boxed::Box;
+
+/// FFI 에러 코드 매핑 (모든 RngError variant + FFI 전용 코드 커버)
+#[inline(always)]
+fn map_rng_error(err: RngError) -> u8 {
+ match err {
+ RngError::UnsupportedHardware => 1,
+ RngError::EntropyDepletion => 2,
+ RngError::NetworkFailure => 4,
+ RngError::ParseError => 5,
+ }
+}
+
+// ================================================
+// 하드웨어 진난수 생성기 (Hardware TRNG) FFI
+// ================================================
+
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn entlib_rng_hw_generate(
+ len: usize,
+ err_flag: *mut u8,
+) -> *mut SecureBuffer {
+ if !err_flag.is_null() {
+ unsafe {
+ *err_flag = 0;
+ }
+ }
+
+ match generate_hardware_random_bytes(len) {
+ Ok(buffer) => Box::into_raw(Box::new(buffer)),
+ Err(e) => {
+ if !err_flag.is_null() {
+ unsafe {
+ *err_flag = map_rng_error(e);
+ }
+ }
+ ptr::null_mut()
+ }
+ }
+}
+
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn entlib_rng_hw_next_generate(buf: *mut SecureBuffer) -> u8 {
+ if buf.is_null() {
+ return 3; // Invalid pointer
+ }
+
+ let buffer = unsafe { &mut *buf };
+ match next_generate(buffer) {
+ Ok(_) => 0,
+ Err(e) => map_rng_error(e),
+ }
+}
+
+// ================================================
+// 혼합 난수 생성기 (Mixed RNG with ChaCha20) FFI
+// ================================================
+
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn entlib_rng_mixed_new_with_strategy(
+ strategy: u8,
+ err_flag: *mut u8,
+) -> *mut MixedRng {
+ if !err_flag.is_null() {
+ unsafe {
+ *err_flag = 0;
+ }
+ }
+
+ let entropy_strategy = match strategy {
+ 0 => EntropyStrategy::LocalHardware,
+ 1 => EntropyStrategy::QuantumNetwork,
+ _ => {
+ if !err_flag.is_null() {
+ unsafe {
+ *err_flag = 3;
+ } // Invalid strategy
+ }
+ return ptr::null_mut();
+ }
+ };
+
+ match MixedRng::new(entropy_strategy) {
+ Ok(rng) => Box::into_raw(Box::new(rng)),
+ Err(e) => {
+ if !err_flag.is_null() {
+ unsafe {
+ *err_flag = map_rng_error(e);
+ }
+ }
+ ptr::null_mut()
+ }
+ }
+}
+
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn entlib_rng_mixed_new(err_flag: *mut u8) -> *mut MixedRng {
+ unsafe { entlib_rng_mixed_new_with_strategy(0, err_flag) }
+}
+
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn entlib_rng_mixed_generate(
+ rng_ptr: *mut MixedRng,
+ len: usize,
+ err_flag: *mut u8,
+) -> *mut SecureBuffer {
+ if !err_flag.is_null() {
+ unsafe {
+ *err_flag = 0;
+ }
+ }
+
+ if rng_ptr.is_null() {
+ if !err_flag.is_null() {
+ unsafe {
+ *err_flag = 3;
+ }
+ }
+ return ptr::null_mut();
+ }
+
+ let rng = unsafe { &mut *rng_ptr };
+ match rng.generate(len) {
+ Ok(buffer) => Box::into_raw(Box::new(buffer)),
+ Err(e) => {
+ if !err_flag.is_null() {
+ unsafe {
+ *err_flag = map_rng_error(e);
+ }
+ }
+ ptr::null_mut()
+ }
+ }
+}
+
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn entlib_rng_mixed_free(rng_ptr: *mut MixedRng) {
+ if !rng_ptr.is_null() {
+ let _ = unsafe { Box::from_raw(rng_ptr) }; // Drop → zeroize 보장
+ }
+}
diff --git a/internal/ffi/src/secure_buffer_ffi.rs b/internal/ffi/src/secure_buffer_ffi.rs
new file mode 100644
index 0000000..86e9ed8
--- /dev/null
+++ b/internal/ffi/src/secure_buffer_ffi.rs
@@ -0,0 +1,98 @@
+//! 외부 함수 인터페이스(foreign function interface) 환경에서 민감 데이터의 안전한 교환 및
+//! 메모리 소거(zeroize)를 완벽하게 보장하기 위한 통제 모듈입니다.
+//!
+//! 얽힘 라이브러리의 Java 런타임과 네이티브 환경 간의 브리지 역할을 수행하며,
+//! 가비지 컬렉터(garbage collector)에 의한 메모리 누수 및 잔류 데이터 노출을 원천 차단합니다.
+//! 본 모듈은 대규모 엔터프라이즈 및 군사급 보안 요구사항을 충족하기 위해, 다음과 같이
+//! 두 가지 독립적이고 엄격한 메모리 소유권(memory ownership) 모델을 지원합니다.
+//!
+//! # 피호출자 할당 패턴 (callee-allocated, rust-owned memory)
+//! 네이티브 환경에서 연산 결과의 크기를 Java 측이 사전에 알 수 없는 경우(가변 길이의 암호문 생성 등)
+//! 사용되는 패턴입니다. Rust가 동적으로 할당한 [SecureBuffer]의 불투명 포인터(opaque pointer)가
+//! Java로 반환됩니다.
+//!
+//! Java 측은 획득한 포인터를 통해 다음 함수들을 순차적으로 호출해야 합니다.
+//! - [entlib_secure_buffer_data]: 실제 데이터의 메모리 주소 매핑
+//! - [entlib_secure_buffer_len]: 데이터의 바이트 길이 확인
+//! - [entlib_secure_buffer_free]: 사용 완료 후 즉각적인 데이터 소거 및 메모리 할당 해제(deallocation) 지시
+//!
+//! # 호출자 할당 패턴 (caller-allocated, java-owned memory)
+//! Java 측의 보안 데이터 컨테이너(`SensitiveDataContainer`)가 `off-heap` 영역에 메모리를
+//! 선제적으로 확보하여 제공하는 경우 사용되는 패턴입니다.
+//!
+//! Java 스코프 컨텍스트(`SDCScopeContext`)가 종료될 때 호출되며, 네이티브는 데이터의 덮어쓰기만 수행합니다.
+//! - [entanglement_secure_wipe]: Java가 소유한 메모리 영역을 임시로 `FFIExternalSecureBuffer`에
+//! 매핑하여 소거 로직을 실행하되, 할당 해제는 수행하지 않음 (해제 권한은 Java에 위임됨)
+//!
+//! # Authors
+//! Q. T. Felix
+
+use core::ptr;
+use entlib_native_core_secure::secure_buffer::{FFIExternalSecureBuffer, SecureBuffer};
+
+/// 보안 버퍼 내 실제 데이터의 메모리 주소 반환 (get immutable data pointer)
+///
+/// # Safety
+/// - 반환된 원시 포인터(raw pointer)는 `SecureBuffer`가 `entlib_secure_buffer_free`를 통해
+/// 해제되기 전까지만 유효합니다. 해제 후 역참조 시 미정의 동작(undefined behavior)이 발생합니다.
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn entlib_secure_buffer_data(buf: *const SecureBuffer) -> *const u8 {
+ if buf.is_null() {
+ return ptr::null();
+ }
+ let buffer = unsafe { &*buf };
+ buffer.inner.as_ptr()
+}
+
+/// 보안 버퍼 내 데이터의 바이트 길이 반환 (get length of data)
+///
+/// # Safety
+/// - `buf`가 null이 아닌 경우, 유효한 `SecureBuffer` 인스턴스를 가리켜야 합니다.
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn entlib_secure_buffer_len(buf: *const SecureBuffer) -> usize {
+ if buf.is_null() {
+ return 0;
+ }
+ let buffer = unsafe { &*buf };
+ buffer.inner.len()
+}
+
+/// 보안 버퍼 메모리 해제 및 데이터 소거 (free and zeroize)
+///
+/// # Safety
+/// - 호출 즉시 `SecureBuffer`의 `Drop` 트레이트가 실행되어 `write_volatile` 및
+/// `compiler_fence`를 통해 메모리가 안전하게 소거됩니다.
+/// - Java 링커(java linker api)를 통해 반드시 한 번만 호출되어야 합니다 (double-free 방지).
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn entlib_secure_buffer_free(buf: *mut SecureBuffer) {
+ if !buf.is_null() {
+ unsafe {
+ // Box::from_raw를 통해 소유권을 가져오며, 즉시 스코프를 벗어나 Drop 실행
+ drop(Box::from_raw(buf));
+ }
+ }
+}
+
+/// Java 측 `SensitiveDataContainer`가 소유한 네이티브 메모리 세그먼트(memory segment)를
+/// 안전하게 소거(zeroize)하는 ffi 엔드포인트입니다.
+///
+/// # Arguments
+/// * `ptr` - 소거할 메모리 영역의 시작 포인터 (*mut u8)
+/// * `len` - 소거할 메모리의 바이트 크기 (usize)
+///
+/// # Safety
+/// - `ptr`은 `len` 바이트만큼 할당된 유효한 메모리 영역을 가리켜야 합니다.
+/// - Java의 제한된 아레나(confined arena) 수명 주기에 의해 유효성이 검증된 상태에서만 호출되어야 합니다.
+/// - 이 함수는 호출자 할당 패턴으로, Java 측에서 할당 해제를 수행해야 합니다.
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn entanglement_secure_wipe(ptr: *mut u8, len: usize) {
+ // null 포인터 및 길이 검증
+ if ptr.is_null() || len == 0 {
+ return;
+ }
+
+ // 명시적 drop 호출로 RAII 소멸자를 즉시 실행
+ // Q. T. Felix NOTE: FFIExternalSecureBuffer의 Drop 구현은 오직 write_volatile을 통한 소거만 수행해야 하며,
+ // 메모리 할당 해제(deallocate)는 Java 측에서 수행되어야 합니당
+ drop(FFIExternalSecureBuffer { inner: ptr, len });
+}
diff --git a/core/ffi/src/sha2.rs b/internal/ffi/src/sha2_ffi.rs
similarity index 98%
rename from core/ffi/src/sha2.rs
rename to internal/ffi/src/sha2_ffi.rs
index a40bf3e..9e71c76 100644
--- a/core/ffi/src/sha2.rs
+++ b/internal/ffi/src/sha2_ffi.rs
@@ -1,7 +1,7 @@
use crate::FFIStatus;
use core::ptr;
use core::slice;
-use entlib_native_helper::secure_buffer::SecureBuffer;
+use entlib_native_core_secure::secure_buffer::SecureBuffer;
use entlib_native_sha2::api::{SHA224, SHA256, SHA384, SHA512};
macro_rules! generate_sha2_ffi {
From 4dfeb3164cf102d601a48d500bdbf26e86e74489 Mon Sep 17 00:00:00 2001
From: "Q. T. Felix" <53819958+Quant-TheodoreFelix@users.noreply.github.com>
Date: Mon, 23 Feb 2026 17:32:04 +0900
Subject: [PATCH 2/6] =?UTF-8?q?=ED=81=AC=EB=A0=88=EC=9D=B4=ED=8A=B8=20?=
=?UTF-8?q?=EC=84=B8=EB=B6=84=ED=99=94=EC=97=90=20=EB=94=B0=EB=A5=B8=20?=
=?UTF-8?q?=EC=83=81=EC=88=98-=EC=8B=9C=EA=B0=84=20=EC=9D=B8=EB=9D=BC?=
=?UTF-8?q?=EC=9D=B8=20=EC=96=B4=EC=85=88=EB=B8=94=EB=A6=AC=20=EA=B2=80?=
=?UTF-8?q?=EC=A6=9D=20=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EC=88=98?=
=?UTF-8?q?=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
scripts/verify_ct_asm.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/scripts/verify_ct_asm.sh b/scripts/verify_ct_asm.sh
index b0bdb5d..f15bfb6 100755
--- a/scripts/verify_ct_asm.sh
+++ b/scripts/verify_ct_asm.sh
@@ -8,7 +8,7 @@ if [ -z "$1" ]; then
fi
TARGET=$1
-CRATE_NAME="entlib_native"
+CRATE_NAME="entlib_native_constant_time"
MODULE_PATH="constant_time_asm"
EXIT_CODE=0
@@ -30,7 +30,7 @@ echo "[$TARGET] 상수-시간 어셈블리 검증 시작..."
for TYPE in "${TYPES[@]}"; do
for METHOD in "${METHODS[@]}"; do
- # Rust 심볼 경로 조합 (::ct_mux 등)
+ # Rust 심볼 경로 조합 (::ct_mux 등)
SYMBOL="<$TYPE as ${CRATE_NAME}::${MODULE_PATH}::CtPrimitive>::${METHOD}"
echo "검사 중: $SYMBOL"
From ef6f4ec0f585a67002ac2fc8b09fe79a1108f55f Mon Sep 17 00:00:00 2001
From: "Q. T. Felix" <53819958+Quant-TheodoreFelix@users.noreply.github.com>
Date: Mon, 23 Feb 2026 17:32:16 +0900
Subject: [PATCH 3/6] =?UTF-8?q?=EB=AC=B8=EC=84=9C=20=EB=82=B4=EC=9A=A9=20?=
=?UTF-8?q?=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
INTRODUCTION.md | 22 +---------------------
INTRODUCTION_EN.md | 22 +---------------------
README.md | 2 +-
README_EN.md | 2 +-
4 files changed, 4 insertions(+), 44 deletions(-)
diff --git a/INTRODUCTION.md b/INTRODUCTION.md
index e41747e..de16c49 100644
--- a/INTRODUCTION.md
+++ b/INTRODUCTION.md
@@ -64,24 +64,4 @@
# 구성
-네이티브는 가상 매니페스트(virtual manifest) 기반의 워크스페이스로 구성되며, 각 크레이트는 명확한 책임 경계를 가집니다.
-
-## core/helper
-
-`no_std` 환경에서 동작하는 보안 원시 연산 모듈입니다. 상수 시간 비교 및 선택 연산(`ConstantTimeOps` 트레이트), 아키텍처별 인라인 어셈블리 구현(`CtPrimitive` 트레이트), 상수 시간 `Base64` 인코딩/디코딩, 그리고 `SecureBuffer`를 제공합니다. 네이티브의 다른 모든 크레이트가 의존하는 기반 계층입니다.
-
-## core/ffi
-
-Java 측 FFM API와 직접 맞닿는 C ABI 브릿지 계층입니다. `Base64` 인/디코딩 FFI 엔드포인트(`entlib_b64_encode_secure`, `entlib_b64_decode_secure`)와 메모리 소거 엔드포인트(`entanglement_secure_wipe`), `SecureBuffer`의 포인터 추출 및 해제 함수를 제공합니다. 모든 진입점에서 `null` 포인터 검증과 오버플로 보호를 수행합니다.
-
-## crypto/rng
-
-하드웨어 기반 난수 생성 모듈입니다. CPU의 `rdseed`/`rndr` 명령어를 직접 호출하는 기본 생성기(`base_rng`)와, 하드웨어 엔트로피를 ChaCha20 코어 블록으로 비선형 혼합하는 확장 생성기(`MixedRng`)를 제공합니다.
-
-## crypto/sha2
-
-SHA-2 계열 해시 함수 구현입니다. `SHA-224`, `SHA-256`, `SHA-384`, `SHA-512` 네 가지 변형을 제공하며, 내부 상태 구조체(`Sha256State`, `Sha512State`)는 `Drop` 트레이트를 통해 연산 완료 시 자동 소거됩니다. 메시지 스케줄 등 연산 중간의 임시 데이터 역시 휘발성 소거 후 메모리 배리어가 적용됩니다.
-
-## crypto/sha3
-
-SHA-3 계열 해시 함수 및 XOF(확장 출력 함수) 구현입니다. `Keccak` 스펀지 구조 위에 `SHA3-224`, `SHA3-256`, `SHA3-384`, `SHA3-512` 고정 출력 변형과 `SHAKE128`, `SHAKE256` 가변 출력 변형을 제공합니다. 내부 상태(`KeccakState`)의 25개 64비트 레인과 200바이트 버퍼는 `Drop` 트레이트에 의해 자동 소거되며, Keccak-f[1600] 순열의 임시 상태 역시 매 호출마다 휘발성 소거됩니다.
+네이티브는 가상 매니페스트(virtual manifest) 기반의 워크스페이스로 구성되며, 각 크레이트는 세분화되어 명확한 책임 경계를 가집니다. `crypto/` 하위에는 `Base64`, `Hash` 또는 알고리즘 연산을 수행하기 위한 크레이트가 위치해 있고, `internal/` 하위에는 `ffi` 연동 및 양자 관련 유틸리티 크레이트가 포함되어 있습니다.
\ No newline at end of file
diff --git a/INTRODUCTION_EN.md b/INTRODUCTION_EN.md
index c04a12c..38200e3 100644
--- a/INTRODUCTION_EN.md
+++ b/INTRODUCTION_EN.md
@@ -64,24 +64,4 @@ The reason this design is necessary is clear. Java's Garbage Collector freely co
# Composition
-Native is composed of a workspace based on a virtual manifest, and each crate has clear responsibility boundaries.
-
-## core/helper
-
-A security primitive operation module operating in a `no_std` environment. It provides constant time comparison and selection operations (`ConstantTimeOps` trait), inline assembly implementations per architecture (`CtPrimitive` trait), constant time `Base64` encoding/decoding, and `SecureBuffer`. It is the base layer on which all other crates of Native depend.
-
-## core/ffi
-
-A C ABI bridge layer directly touching the Java side FFM API. It provides `Base64` encoding/decoding FFI endpoints (`entlib_b64_encode_secure`, `entlib_b64_decode_secure`), memory erasure endpoint (`entanglement_secure_wipe`), and pointer extraction and release functions of `SecureBuffer`. It performs null pointer verification and overflow protection at all entry points.
-
-## crypto/rng
-
-A hardware-based random number generation module. It provides a basic generator (`base_rng`) that directly calls the CPU's `rdseed`/`rndr` instructions, and an extended generator (`MixedRng`) that non-linearly mixes hardware entropy with the ChaCha20 core block.
-
-## crypto/sha2
-
-Implementation of SHA-2 family hash functions. It provides four variants: `SHA-224`, `SHA-256`, `SHA-384`, `SHA-512`, and internal state structures (`Sha256State`, `Sha512State`) are automatically erased upon operation completion via the `Drop` trait. Temporary data during operations such as message schedules are also volatilely erased, and then memory barriers are applied.
-
-## crypto/sha3
-
-Implementation of SHA-3 family hash functions and XOF (Extendable Output Functions). It provides `SHA3-224`, `SHA3-256`, `SHA3-384`, `SHA3-512` fixed output variants and `SHAKE128`, `SHAKE256` variable output variants on top of the `Keccak` sponge structure. The 25 64-bit lanes and 200-byte buffer of the internal state (`KeccakState`) are automatically erased by the `Drop` trait, and the temporary state of the Keccak-f[1600] permutation is also volatilely erased at every call.
\ No newline at end of file
+Native is composed of a virtual manifest-based workspace, and each crate is granularized to have clear responsibility boundaries. Under `crypto/`, crates for performing `Base64`, `Hash`, or algorithm operations are located, and under `internal/`, crates for `ffi` integration and quantum-related utilities are included.
\ No newline at end of file
diff --git a/README.md b/README.md
index 65547b6..611eca3 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@
# Alpha 버전
-이 네이티브 라이브러리는 `1.1.0` 릴리즈 출시에 대한 준비를 마쳐도 곧바로 출시되진 않습니다. 이에 세밀하고 또 정밀하게 코드를 검토하고, 그 환경을 원활히 구축하기 위해 지금 이 시점에서 알파 버전으로 우선 공개하겠습니다.
+이 네이티브 라이브러리는 `1.1.x` 릴리즈 출시에 대한 준비를 마쳐도 곧바로 출시되진 않습니다. 이에 세밀하고 또 정밀하게 코드를 검토하고, 그 환경을 원활히 구축하기 위해 지금 이 시점에서 알파 버전으로 우선 공개하겠습니다.
# 벤치마킹
diff --git a/README_EN.md b/README_EN.md
index f45b43d..053f664 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -19,7 +19,7 @@ Just in time, the respected security organization `Legion of the BouncyCastle In
# Alpha Version
-This native library will not be released immediately even if preparations for the `1.1.0` release are completed. Therefore, to review the code in detail and precisely, and to smoothly build that environment, I will first release it as an alpha version at this point.
+This native library will not be released immediately even if preparations for the `1.1.x` release are completed. Therefore, to review the code in detail and precisely, and to smoothly build that environment, I will first release it as an alpha version at this point.
# Benchmarking
From 09d65abccbc34a59ef1e6c7f88acd1eccff26021 Mon Sep 17 00:00:00 2001
From: "Q. T. Felix" <53819958+Quant-TheodoreFelix@users.noreply.github.com>
Date: Mon, 23 Feb 2026 17:32:51 +0900
Subject: [PATCH 4/6] =?UTF-8?q?=EC=95=94=ED=98=B8=ED=99=94=20=ED=81=AC?=
=?UTF-8?q?=EB=A0=88=EC=9D=B4=ED=8A=B8=20=EC=84=B8=EB=B6=84=ED=99=94=20?=
=?UTF-8?q?=EB=B0=8F=20=EB=B3=B4=EC=95=88=20=EA=B0=95=ED=99=94?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
crypto/base64/Cargo.toml | 16 ++++++++++++++++
crypto/base64/src/lib.rs | 1 +
crypto/constant-time/Cargo.toml | 24 ++++++++++++++++++++++++
crypto/constant-time/src/lib.rs | 8 ++++++++
crypto/core-secure/Cargo.toml | 15 +++++++++++++++
crypto/core-secure/src/lib.rs | 1 +
6 files changed, 65 insertions(+)
create mode 100644 crypto/base64/Cargo.toml
create mode 100644 crypto/base64/src/lib.rs
create mode 100644 crypto/constant-time/Cargo.toml
create mode 100644 crypto/constant-time/src/lib.rs
create mode 100644 crypto/core-secure/Cargo.toml
create mode 100644 crypto/core-secure/src/lib.rs
diff --git a/crypto/base64/Cargo.toml b/crypto/base64/Cargo.toml
new file mode 100644
index 0000000..7bc30a1
--- /dev/null
+++ b/crypto/base64/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "entlib-native-base64"
+version.workspace = true
+edition.workspace = true
+authors.workspace = true
+license.workspace = true
+
+[dependencies]
+entlib-native-constant-time.workspace = true
+
+[dev-dependencies]
+criterion = { version = "0.8.2", features = ["html_reports"] }
+
+[[bench]]
+name = "base64_bench"
+harness = false
\ No newline at end of file
diff --git a/crypto/base64/src/lib.rs b/crypto/base64/src/lib.rs
new file mode 100644
index 0000000..39abdce
--- /dev/null
+++ b/crypto/base64/src/lib.rs
@@ -0,0 +1 @@
+pub mod base64;
diff --git a/crypto/constant-time/Cargo.toml b/crypto/constant-time/Cargo.toml
new file mode 100644
index 0000000..14c54c3
--- /dev/null
+++ b/crypto/constant-time/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "entlib-native-constant-time"
+version.workspace = true
+edition.workspace = true
+authors.workspace = true
+license.workspace = true
+
+[features]
+ct-tests = []
+
+[dependencies]
+
+[dev-dependencies]
+subtle = "2.6.1"
+proptest = "1.10.0"
+criterion = { version = "0.8.2", features = ["html_reports"] }
+
+[[bench]]
+name = "ct_ops_security"
+harness = false
+
+[[bench]]
+name = "ct_ops_throughput"
+harness = false
\ No newline at end of file
diff --git a/crypto/constant-time/src/lib.rs b/crypto/constant-time/src/lib.rs
new file mode 100644
index 0000000..be1cdae
--- /dev/null
+++ b/crypto/constant-time/src/lib.rs
@@ -0,0 +1,8 @@
+pub mod constant_time;
+
+#[cfg(feature = "ct-tests")]
+#[doc(hidden)]
+pub mod constant_time_asm;
+
+#[cfg(not(feature = "ct-tests"))]
+pub(crate) mod constant_time_asm;
diff --git a/crypto/core-secure/Cargo.toml b/crypto/core-secure/Cargo.toml
new file mode 100644
index 0000000..6f473db
--- /dev/null
+++ b/crypto/core-secure/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "entlib-native-core-secure"
+version.workspace = true
+edition.workspace = true
+authors.workspace = true
+license.workspace = true
+
+[dependencies]
+
+[dev-dependencies]
+criterion = { version = "0.8.2", features = ["html_reports"] }
+
+[[bench]]
+name = "secure_buffer_bench"
+harness = false
\ No newline at end of file
diff --git a/crypto/core-secure/src/lib.rs b/crypto/core-secure/src/lib.rs
new file mode 100644
index 0000000..0da0cd6
--- /dev/null
+++ b/crypto/core-secure/src/lib.rs
@@ -0,0 +1 @@
+pub mod secure_buffer;
From dd1d65f46366fffe92824e0990bd0aeb73f7da2a Mon Sep 17 00:00:00 2001
From: "Q. T. Felix" <53819958+Quant-TheodoreFelix@users.noreply.github.com>
Date: Mon, 23 Feb 2026 17:33:07 +0900
Subject: [PATCH 5/6] =?UTF-8?q?=EC=96=91=EC=9E=90=20=EA=B8=B0=EC=88=A0=20?=
=?UTF-8?q?=EC=9C=A0=ED=8B=B8=EB=A6=AC=ED=8B=B0=20=ED=81=AC=EB=A0=88?=
=?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
internal/quantum-util/Cargo.toml | 8 ++++++++
internal/quantum-util/src/lib.rs | 14 ++++++++++++++
2 files changed, 22 insertions(+)
create mode 100644 internal/quantum-util/Cargo.toml
create mode 100644 internal/quantum-util/src/lib.rs
diff --git a/internal/quantum-util/Cargo.toml b/internal/quantum-util/Cargo.toml
new file mode 100644
index 0000000..6b74391
--- /dev/null
+++ b/internal/quantum-util/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "entlib-native-quantum-util"
+version.workspace = true
+edition.workspace = true
+authors.workspace = true
+license.workspace = true
+
+[dependencies]
diff --git a/internal/quantum-util/src/lib.rs b/internal/quantum-util/src/lib.rs
new file mode 100644
index 0000000..b93cf3f
--- /dev/null
+++ b/internal/quantum-util/src/lib.rs
@@ -0,0 +1,14 @@
+pub fn add(left: u64, right: u64) -> u64 {
+ left + right
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn it_works() {
+ let result = add(2, 2);
+ assert_eq!(result, 4);
+ }
+}
From e43ca209b7365a5fe30f805fa466c4e83ca5ecbe Mon Sep 17 00:00:00 2001
From: "Q. T. Felix" <53819958+Quant-TheodoreFelix@users.noreply.github.com>
Date: Mon, 23 Feb 2026 17:33:38 +0900
Subject: [PATCH 6/6] =?UTF-8?q?=EB=A7=8E=EC=9D=80=20=EC=88=98=EC=A0=95?=
=?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=B2=84=EC=A0=84=20=EC=97=85?=
=?UTF-8?q?=20->=20`1.1.2-Alpha`?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Cargo.toml | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 87a9978..62eaea7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,9 +1,9 @@
[workspace]
-members = ["core/*", "crypto/*"]
+members = ["internal/*", "crypto/*"]
resolver = "2"
[workspace.package]
-version = "1.1.1-Alpha3"
+version = "1.1.2-Alpha"
edition = "2024"
authors = ["Q. T. Felix "]
license = "MIT LICENSE"
@@ -12,8 +12,13 @@ license = "MIT LICENSE"
strip = true
[workspace.dependencies]
-entlib-native-helper = { path = "./core/helper", version = "1.1.1-Alpha3" }
-entlib-native-ffi = { path = "./core/ffi", version = "1.1.1-Alpha3" }
-entlib-native-sha2 = { path = "./crypto/sha2", version = "1.1.1-Alpha3" }
-entlib-native-sha3 = { path = "./crypto/sha3", version = "1.1.1-Alpha3" }
-entlib-native-rng = { path = "./crypto/rng", version = "1.1.1-Alpha3" }
\ No newline at end of file
+### INTERNAL CORE DEPENDENCIES ###
+entlib-native-ffi = { path = "./internal/ffi", version = "1.1.2-Alpha" }
+entlib-native-quantum-util = { path = "./internal/quantum-util", version = "1.1.2-Alpha" }
+### INTERNAL CRYPTO DEPENDENCIES ###
+entlib-native-base64 = { path = "./crypto/base64", version = "1.1.2-Alpha" }
+entlib-native-constant-time = { path = "./crypto/constant-time", version = "1.1.2-Alpha" }
+entlib-native-core-secure = { path = "./crypto/core-secure", version = "1.1.2-Alpha" }
+entlib-native-rng = { path = "./crypto/rng", version = "1.1.2-Alpha" }
+entlib-native-sha2 = { path = "./crypto/sha2", version = "1.1.2-Alpha" }
+entlib-native-sha3 = { path = "./crypto/sha3", version = "1.1.2-Alpha" }