Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
kroxylicious-pqc-filter — Un plugin filtro Kroxylicious che fornisce crittografia trasparente post-quantum (ML-KEM + AES-256-GCM) a livello di record per Apache Kafka, senza richiedere alcuna modifica al codice client. | Kitploit
Strumenti/GitHubGitHub/oscerd/kroxylicious-pqc-filter
Sicurezza dell'Infrastruttura CloudStrumenti di Crittografia/DecrittografiaCrittografiaUtilità e Framework
GitHuboscerd/kroxylicious-pqc-filter

kroxylicious-pqc-filter

Un plugin filtro Kroxylicious che fornisce crittografia trasparente post-quantum (ML-KEM + AES-256-GCM) a livello di record per Apache Kafka, senza richiedere alcuna modifica al codice client.

Vedi Repository
13 mesi faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

Kroxylicious Filtro di Crittografia dei Record PQC

Un plugin filtro per Kroxylicious che fornisce una trasparente crittografia a livello di record con Crittografia Post-Quantum (PQC) per Apache Kafka utilizzando ML-KEM (FIPS 203) key encapsulation con crittografia simmetrica AES-256-GCM.

I produttori e i consumatori Kafka richiedono zero modifiche al codice. Il proxy Kroxylicious intercetta il traffico e crittografa in Produce / decrittografa in Fetch automaticamente.``` Producer ──plaintext──> Kroxylicious ──encrypted──> Kafka Broker Consumer <──plaintext── Kroxylicious <──encrypted── Kafka Broker

root@kitploit:~
## Perché PQC per Kafka?

Gli algoritmi classici di stabilimento delle chiavi (RSA, ECDH) sono vulnerabili ai
futuri computer quantistici. Se le chiavi di cifratura per singolo record vengono stabilite utilizzando un
KEM classico, un avversario quantistico potrebbe recuperare quelle chiavi dalle
incapsulazioni memorizzate insieme al ciphertext sul broker.

Questo plugin utilizza ML-KEM (FIPS 203) per l'incapsulamento delle chiavi resistente ai computer quantistici,
garantendo che i dati a riposo sul broker Kafka non possano essere decifrati nemmeno da
un avversario dotato di un computer quantistico crittograficamente rilevante.

**Nota:** Questo filtro protegge **i dati a riposo sul broker**, non il canale TLS
in transito. Vedere [THREAT_MODEL.md](https://github.com/oscerd/kroxylicious-pqc-filter/blob/HEAD/THREAT_MODEL.md) per un'analisi
completa di ciò che viene difeso e ciò che non lo è.

| Standard | Algoritmo | Scopo in questo plugin |
|----------|-----------|------------------------|
| FIPS 203 | ML-KEM (Kyber) | Incapsulamento delle chiavi - stabilisce in modo sicuro una chiave AES per messaggio |
| N/D | AES-256-GCM | Cifratura autenticata simmetrica del payload del record |
| N/D | X25519 ECDH | Accordo di chiave classico per difesa in profondità in modalità ibrida |

## Caratteristiche

- **Cifratura/decifratura trasparente** - nessuna modifica lato client richiesta
- **Set di parametri ML-KEM-512, ML-KEM-768 (predefinito), ML-KEM-1024**
- **Modalità ibrida** (predefinita) - combina ML-KEM + X25519 ECDH così che entrambi debbano essere compromessi
- **Cifratura per singolo record** - ogni record ottiene una nuova incapsulazione KEM + IV casuale
- **Filtro per argomento** - pattern regex selezionano quali argomenti cifrare
- **Rilevamento manomissioni** - la cifratura autenticata AES-GCM rifiuta ciphertext modificati
- **Sicurezza semantica** - plaintext identici producono ciphertext diversi (IND-CCA2)
- **Generazione automatica delle chiavi** - genera e salva le chiavi ML-KEM al primo avvio se assenti
- **Header `x-pqc-encrypted`** - contrassegna i record cifrati per la consapevolezza a valle
- **Provider di chiavi componibili** - la SPI `KeyProvider` supporta backend filesystem (predefinito) e HashiCorp Vault

## Prerequisiti

| Requisito | Versione |
|-------------|---------|
| JDK | 17+ (21+ consigliato) |
| Maven | 3.8+ |
| Kroxylicious | 0.19.0 |
| Apache Kafka | 3.9.x |

## Avvio rapido

### 1. Compilare il plugin```bash
git clone <this-repo>
cd kroxylicious-pqc-filter
mvn clean package -DskipTests

Il JAR shaded in target/kroxylicious-pqc-filter-1.0.0-SNAPSHOT.jar include Bouncy Castle così può essere inserito in Kroxylicious senza dipendenze extra.

Per includere il supporto per il key provider HashiCorp Vault, compila con il profilo vault:```bash mvn clean package -Pvault -DskipTests

root@kitploit:~
Questo include `spring-vault-core` e `VaultKeyProvider` nel JAR.

### 2. Genera le chiavi ML-KEM```bash
java -cp target/kroxylicious-pqc-filter-1.0.0-SNAPSHOT.jar \
  io.kroxylicious.filter.pqc.PqcKeyGeneratorCli \
  ML_KEM_768 \
  /etc/kroxylicious/pqc/

I'm unable to produce a translation because no source text was included in this request. The INPUT: section is empty, so there is no content to translate.``` Generating ML-KEM-768 key pair... Public key: /etc/kroxylicious/pqc/pqc-public.der Size: 1206 bytes Format: X.509 Private key: /etc/kroxylicious/pqc/pqc-private.der Size: 2498 bytes Format: PKCS#8

root@kitploit:~
In alternativa, ometti i percorsi delle chiavi nella configurazione e il filtro genererà le chiavi automaticamente al primo avvio.

### 3. Configura Kroxylicious

Aggiungi il filtro alla configurazione YAML del proxy Kroxylicious:```yaml
filterDefinitions:
  - name: pqc-encryption
    type: PqcRecordEncryptionFilterFactory
    config:
      kemAlgorithm: ML_KEM_768
      hybridMode: true
      publicKeyPath: /etc/kroxylicious/pqc/pqc-public.der
      privateKeyPath: /etc/kroxylicious/pqc/pqc-private.der
      topicPatterns:
        - "sensitive-.*"
        - "pii-.*"

defaultFilters:
  - pqc-encryption

4. Distribuzione

Posiziona il JAR in una directory accessibile a Kroxylicious e aggiungilo al classpath tramite la variabile d'ambiente KROXYLICIOUS_CLASSPATH:```bash export KROXYLICIOUS_CLASSPATH="/opt/kroxylicious/plugins/*"

root@kitploit:~
Quando usi Docker, impostalo nell'ambiente del container:```yaml
environment:
  KROXYLICIOUS_CLASSPATH: /opt/kroxylicious/plugins/*

Then start the proxy. Producers and consumers connect to the proxy port instead of the broker directly.

Configuration Reference

Key Providers

Filesystem (keyProviderType: filesystem, default): Loads ML-KEM keys from DER files on disk. If the files do not exist, generates a fresh key pair and saves them. Requires publicKeyPath and privateKeyPath.

HashiCorp Vault (keyProviderType: vault, requires -Pvault build): Fetches ML-KEM keys from a Vault KV v2 secrets engine. Keys are stored as base64-encoded DER in publicKey and privateKey fields. Vault secret versions map to key IDs for key rotation support.

Vault keyProviderConfig properties:

Example Vault configuration:```yaml filterDefinitions:

  • name: pqc-encryption type: PqcRecordEncryptionFilterFactory config: kemAlgorithm: ML_KEM_768 hybridMode: false keyProviderType: vault keyProviderConfig: vaultAddress: http://vault:8200 vaultToken: my-token secretPath: kroxylicious/pqc topicPatterns: - "sensitive-.*"
root@kitploit:~
### Set di parametri ML-KEM

| Algorithm | Security Level | Public Key | Private Key | Ciphertext Overhead | Use Case |
|-----------|---------------|------------|-------------|--------------------:|----------|
| ML-KEM-512 | 128-bit | 822 B | 1,730 B | ~854 B | Leggero, IoT |
| ML-KEM-768 | 192-bit | 1,206 B | 2,498 B | ~1,174 B | **Default consigliato** |
| ML-KEM-1024 | 256-bit | 1,590 B | 3,266 B | ~1,654 B | Dati classificati / di lunga durata |

### Modalità di cifratura

**Solo PQC** (`hybridMode: false`):
Utilizza esclusivamente ML-KEM. La chiave AES-256 viene derivata dal segreto condiviso ML-KEM
tramite `SHA-256(0x01 || "kroxylicious-pqc-v1" || secret)`.

**Ibrido** (`hybridMode: true`, predefinito):
Combina ML-KEM + X25519. La chiave AES-256 viene derivata da entrambi i segreti tramite
`SHA-256(0x02 || "kroxylicious-pqc-hybrid-v1" || pqcSecret || x25519Secret)`.
Ciò garantisce la sicurezza anche se un algoritmo viene compromesso.

## Formato della busta cifrata

Ogni valore di record cifrato viene sostituito con una busta binaria:```
PQC-only (version 0x01):
+--------+-----------+--------------------+--------+---------------------------+
| 1 byte | 2 bytes   | N bytes            | 12 B   | remaining                 |
| 0x01   | encap len | ML-KEM encapsulat. | AES IV | AES-GCM ciphertext + tag  |
+--------+-----------+--------------------+--------+---------------------------+

Hybrid (version 0x02):
+--------+-----------+--------------------+---------+--------+-----------------+
| 1 byte | 2 bytes   | N bytes            | 32 B    | 12 B   | remaining       |
| 0x02   | encap len | ML-KEM encapsulat. | X25519  | AES IV | AES-GCM ct+tag |
|        |           |                    | eph pub |        |                 |
+--------+-----------+--------------------+---------+--------+-----------------+

Il byte di versione consente al decriptatore di determinare la modalità senza configurazione.

Architettura

Struttura dei moduli```

kroxylicious-pqc-filter/ src/main/java/io/kroxylicious/filter/pqc/ PqcRecordEncryptionFilterFactory.java # FilterFactory entry point (@Plugin) PqcRecordEncryptionFilter.java # ProduceRequestFilter + FetchResponseFilter PqcKeyGeneratorCli.java # CLI key generation utility config/ PqcEncryptionConfig.java # Jackson-deserialized configuration POJO crypto/ KeyProvider.java # SPI interface for pluggable key backends FileSystemKeyProvider.java # Default: loads/generates keys from disk PqcCryptoEngine.java # ML-KEM encapsulation + AES-256-GCM PqcKeyManager.java # Resolves KeyProvider via ServiceLoader src/main/java-vault/ # (vault profile only) .../crypto/ VaultKeyProvider.java # HashiCorp Vault KV v2 key provider src/main/resources/ META-INF/services/ io.kroxylicious.proxy.filter.FilterFactory # ServiceLoader: filter registration io.kroxylicious.filter.pqc.crypto.KeyProvider # ServiceLoader: key provider src/main/resources-vault/ # (vault profile only) META-INF/services/ io.kroxylicious.filter.pqc.crypto.KeyProvider # Registers both FS + Vault providers src/test/java/... # Unit tests (49 tests) src/test/java-vault/... # Vault provider tests (17 tests) examples/ standalone/ # Runs without Kafka (crypto engine demo) docker/ # Docker Compose: Kafka + Vault + Kroxylicious

root@kitploit:~
### Data Flow

#### What is stored in Vault

Vault KV v2 holds the ML-KEM key pair at `secret/<secretPath>` (e.g., `secret/kroxylicious/pqc`):

| Field | Content | Format |
|-------|---------|--------|
| `publicKey` | ML-KEM public key (used for encapsulation) | Base64-encoded X.509 DER |
| `privateKey` | ML-KEM private key (used for decapsulation) | Base64-encoded PKCS#8 DER |

Each Vault secret version acts as a key ID, enabling key rotation. New versions
encrypt new records; old versions can still decrypt records encrypted with them.

#### Startup flow```
┌─────────────────────────────────────────────────────────────────────┐
│                           STARTUP                                   │
│                                                                     │
│  1. Vault starts (dev mode, port 8200)                              │
│         │                                                           │
│  2. vault-init container:                                           │
│         │  • Reads pre-generated ML-KEM DER key files from disk     │
│         │  • Base64-encodes them                                    │
│         │  • POST /v1/secret/data/kroxylicious/pqc                  │
│         │  • Stores publicKey + privateKey in Vault KV v2           │
│         │  • Exits                                                  │
│         │                                                           │
│  3. Kafka starts (KRaft mode, port 9092)                            │
│         │                                                           │
│  4. Kroxylicious starts (port 9192):                                │
│         │                                                           │
│         │  FilterFactory.initialize()                                │
│         │    → PqcKeyManager resolves VaultKeyProvider               │
│         │      via ServiceLoader (matches keyProviderType: "vault")  │
│         │    → VaultKeyProvider.configure()                          │
│         │      connects to Vault (token/approle/kubernetes auth)     │
│         │    → Fetches ML-KEM key pair from Vault KV v2              │
│         │      (GET /v1/secret/data/kroxylicious/pqc)                │
│         │    → Decodes: base64 → DER bytes → Java Key objects        │
│         │    → PqcCryptoEngine initialized with key pair             │
│         ▼                                                           │
│       Proxy ready — Vault is NOT contacted again per-message         │
└─────────────────────────────────────────────────────────────────────┘

Flusso di produzione (crittografia)```

root@kitploit:~
                     Kroxylicious (:9192)
                    ┌───────────────────────────────────────────┐

Producer ──plaintext──> │ PqcRecordEncryptionFilter │ │ .onProduceRequest() │ │ │ │ For each record in matching topic: │ │ 1. ML-KEM encapsulate (public key) │ │ → fresh shared secret │ │ → encapsulation blob │ │ 2. SHA-256(shared secret) → AES-256 key │ │ 3. AES-GCM encrypt record value │ │ 4. Build binary envelope: │ │ [ver|encap_len|encap|IV|ciphertext] │ │ 5. Add x-pqc-encrypted header │ └────────────────┬──────────────────────────┘ │ ▼ Kafka Broker (:9092) stores encrypted blob

root@kitploit:~
#### Flusso di recupero (decrittazione)```
                                  Kafka Broker (:9092)
                                  returns encrypted blob
                                         │
                                         ▼
                        ┌────────────────┴──────────────────────────┐
                        │ PqcRecordEncryptionFilter                 │
                        │   .onFetchResponse()                      │
                        │                                           │
                        │ For each record with x-pqc-encrypted:     │
                        │   1. Parse envelope → extract encap +     │
                        │      ciphertext                           │
                        │   2. ML-KEM decapsulate (private key)     │
                        │      → recover shared secret              │
                        │   3. SHA-256(shared secret) → AES-256 key │
                        │   4. AES-GCM decrypt → plaintext          │
                        │   5. Remove x-pqc-encrypted header        │
Consumer <──plaintext── │                                           │
                        └───────────────────────────────────────────┘
                         Kroxylicious (:9192)

Decisioni progettuali chiave

  • Vault viene contattato solo all'avvio, non per ogni messaggio. Nessuna latenza per singolo record.
  • Se Vault si interrompe dopo l'avvio, crittografia/decrittografia continuano senza problemi.
  • Ogni record riceve una nuova incapsulazione ML-KEM (nuovo segreto condiviso + IV casuale), garantendo sicurezza semantica (IND-CCA2) anche se la stessa coppia di chiavi viene riusata.
  • La rotazione delle chiavi viene eseguita scrivendo una nuova versione del segreto in Vault e riavviando il proxy.

Ciclo di vita del filtro```

Startup: FilterFactory.initialize() -> PqcKeyManager loads/generates ML-KEM key pair -> PqcCryptoEngine created with key pair -> Topic patterns compiled -> SharedPqcContext returned

Per connection: FilterFactory.createFilter() -> New PqcRecordEncryptionFilter instance -> Shares the same PqcCryptoEngine (thread-safe)

Produce request: onProduceRequest() -> For each topic matching topicPatterns: -> For each partition: -> For each record: -> ML-KEM encapsulate (fresh shared secret + encapsulation) -> Derive AES-256 key from shared secret -> AES-GCM encrypt the record value -> Replace value with encrypted envelope -> Add x-pqc-encrypted header -> Forward to broker

Fetch response: onFetchResponse() -> For each topic matching topicPatterns: -> For each partition: -> For each record with x-pqc-encrypted header: -> Read version + encapsulation from envelope -> ML-KEM decapsulate (recover shared secret) -> Derive AES-256 key -> AES-GCM decrypt -> Replace value with plaintext -> Remove x-pqc-encrypted header -> Forward to client

root@kitploit:~
### Classi chiave

**`PqcRecordEncryptionFilterFactory`** implementa `FilterFactory<PqcEncryptionConfig, SharedPqcContext>`.
È annotata con `@Plugin(configType = PqcEncryptionConfig.class)`.
Registrata tramite `META-INF/services/io.kroxylicious.proxy.filter.FilterFactory`.
Viene chiamata una volta all'avvio (`initialize`) e una volta per connessione client (`createFilter`).

**`PqcRecordEncryptionFilter`** implementa `ProduceRequestFilter` e `FetchResponseFilter`.
Intercetta `onProduceRequest` per cifrare e `onFetchResponse` per decifrare.
 Una istanza per connessione; non è necessaria alcuna sincronizzazione (modello di thread di Kroxylicious).

**`PqcCryptoEngine`** esegue tutte le operazioni crittografiche.
Senza stato, ad eccezione del materiale delle chiavi e di `SecureRandom`.
`encrypt()` restituisce un involucro auto-descrittivo; `decrypt()` lo analizza.
Registra i provider Bouncy Castle (`BC`, `BCPQC`) in un inizializzatore statico.

**`PqcKeyManager`** risolve un `KeyProvider` tramite `ServiceLoader`, effettuando la corrispondenza in base a
`keyProviderType`. Delega tutte le operazioni sulle chiavi al provider risolto.

**`KeyProvider`** è l'interfaccia SPI per backend di archiviazione delle chiavi componibili.
Le implementazioni vengono scoperte tramite `META-INF/services`. Provider integrati:
`FileSystemKeyProvider` (predefinito) e `VaultKeyProvider` (con `-Pvault`).

**`PqcEncryptionConfig`** è un POJO annotato con Jackson.
Deserializzato dal blocco `config:` nello YAML del proxy Kroxylicious.
Supporta `keyProviderType` e `keyProviderConfig` per la selezione del backend.

## Compilazione e Test```bash
# Compile
mvn compile

# Run unit tests (38 core tests)
mvn test

# Package (creates shaded JAR with Bouncy Castle bundled)
mvn package

# Build with Vault support (55 tests: 38 core + 17 vault)
mvn package -Pvault

# Install to local Maven repository
mvn install

Copertura dei test

Totale: 55 test (38 core + 17 vault)

Dipendenze

Runtime

Test

JUnit 5.11.4, Mockito 5.15.2, AssertJ 3.27.3, Jackson Databind 2.18.3.

Prestazioni

Misurato sulla demo standalone (JVM a regime, 1,000 iterazioni, messaggi da 1 KB):

AlgoritmoCifraturaDecifratura
ML-KEM-512~7,700 msg/s (0.13 ms/msg)~7,800 msg/s (0.13 ms/msg)
ML-KEM-768

L'overhead per messaggio è inferiore al millisecondo. Per i tipici carichi di lavoro Kafka (messaggi nell'intervallo KB-MB), il costo della cifratura è trascurabile rispetto all'I/O di rete e di disco.

Considerazioni sulla sicurezza

Per un modello di minaccia completo che copra ciò da cui questo filtro difende e ciò da cui non difende, vedere THREAT_MODEL.md.

  • Protezione delle chiavi (filesystem): Il file della chiave privata deve essere leggibile solo dal processo Kroxylicious. Utilizzare i permessi del filesystem (chmod 600).
  • Protezione delle chiavi (Vault): Utilizzare le policy di Vault per limitare l'accesso al percorso del secret. Preferire l'autenticazione approle o kubernetes ai token statici in produzione. Non committare mai i token di Vault nel sistema di controllo versione.
  • Rotazione delle chiavi: Il provider di chiavi di Vault supporta il versionamento delle chiavi tramite le versioni dei secret di Vault. Le nuove versioni cifrano; le vecchie versioni possono ancora decifrare.
  • Tombstone: I record con valori null (tombstone di Kafka) vengono inoltrati senza cifratura.
  • Header: Gli header dei record non vengono cifrati, solo i valori. Non inserire dati sensibili negli header.
  • Compressione: Il filtro scrive batch cifrati con Compression.NONE perché i dati cifrati non si comprimono bene.

Licenza

Apache License 2.0. Vedere LICENSE per i dettagli.

Scarica lo strumento
PropertyTypeRequiredDefaultDescription
kemAlgorithmenumNoML_KEM_768ML-KEM parameter set. One of ML_KEM_512, ML_KEM_768, ML_KEM_1024.
hybridModebooleanNotrueCombine ML-KEM with X25519 ECDH for defense-in-depth.
publicKeyPathstringFilesystem only-Filesystem path to the ML-KEM public key (X.509 DER encoded).
privateKeyPathstringFilesystem only-Filesystem path to the ML-KEM private key (PKCS#8 DER encoded).
topicPatternslist<string>No[".*"]Java regex patterns. Only records in matching topics are encrypted/decrypted.
keyProviderTypestringNofilesystemKey storage backend. One of filesystem, vault.
keyProviderConfigmap<string, string>Vault only{}Backend-specific configuration (see Vault section below).
PropertyRequiredDefaultDescription
vaultAddressYesVAULT_ADDR envVault server URL (e.g., http://vault:8200)
vaultTokenFor token authVAULT_TOKEN envVault authentication token
secretPathYes--Path within the secrets engine (e.g., kroxylicious/pqc)
secretEngineNosecretKV v2 secrets engine mount name
authMethodNotokenAuth method: token, approle, or kubernetes
roleIdFor approle--AppRole role ID
secretIdFor approle--AppRole secret ID
kubeRoleFor kubernetes--Kubernetes auth role name
kubeTokenPathNo/var/run/secrets/.../tokenService account token file path
Classe di testTestCosa viene verificato
PqcCryptoEngineTest12Roundtrip di cifratura/decifratura per tutte e tre le varianti ML-KEM, gestione dei valori null, payload vuoti e da 1MB, sicurezza semantica, rilevamento di manomissioni, rifiuto di versioni non valide, generazione delle chiavi, byte di versione dell'envelope
PqcEncryptionConfigTest9Valori predefiniti, valori espliciti, rifiuto dei null, deserializzazione JSON, immutabilità, proprietà enum, deserializzazione di keyProviderConfig
PqcKeyManagerTest6Risoluzione del KeyProvider, creazione del motore, delega ai provider, fallback per il tipo filesystem
FileSystemKeyProviderTest11Generazione delle chiavi, caricamento delle chiavi esistenti, ID chiave predefinito, rifiuto di ID chiave sconosciuto, validazione del percorso null, discovery tramite ServiceLoader, roundtrip crittografico
VaultKeyProviderTest17Validazione della configurazione (path/address/token/approle/kube mancanti), recupero della chiave da Vault, recupero di chiavi versionate, chiavi in cache, versioni non valide, campi mancanti nel secret, roundtrip crittografico, comportamento alla chiusura
DipendenzaVersioneAmbitoScopo
io.kroxylicious:kroxylicious-api0.19.0providedInterfacce API del filtro
org.apache.kafka:kafka-clients3.9.0providedTipi di messaggio del protocollo Kafka
com.fasterxml.jackson.core:jackson-annotations2.18.3providedBinding della configurazione
org.bouncycastle:bcprov-jdk18on1.83compileML-KEM, AES-GCM, X25519 (inclusi nel JAR shaded)
org.bouncycastle:bcutil-jdk18on1.83compileUtility di Bouncy Castle (incluse nel JAR shaded)
org.slf4j:slf4j-api2.0.17providedLogging
org.springframework.vault:spring-vault-core3.1.2compile (profilo vault)Client Vault KV v2 (incluso quando compilato con -Pvault)
~9,600 msg/s (0.10 ms/msg)
~10,200 msg/s (0.10 ms/msg)
ML-KEM-1024~8,500 msg/s (0.12 ms/msg)~6,900 msg/s (0.14 ms/msg)