NIST gjorde ML-KEM til den offisielle standarden for nøkkelinnkapsling i august 2024 gjennom FIPS 203. Syv måneder senere, 11. mars 2025, valgte byrået HQC (Hamming Quasi-Cyclic) som en femte algoritme, tiltenkt rollen som reserve dersom det skulle dukke opp svakheter i gittertallmatematikken bak ML-KEM. For utviklere som bygger sikker kommunikasjon i 2026, betyr det at “post-kvante kryptering” ikke lenger er ett valg, men to algoritmer med helt ulik matematisk struktur som bør kombineres i en hybrid nøkkelutveksling.

Denne artikkelen er en praktisk oppskrift: du skal bygge en fungerende hybrid nøkkelutvekslingstjeneste som bruker ML-KEM-768 som hovedalgoritme og HQC-192 som uavhengig reserve, med klassisk X25519 som et tredje sikkerhetslag. Vi bruker OpenSSL 3.5+ og Open Quantum Safe-biblioteket (liboqs), skriver kode i Python, og går gjennom alt fra installasjon til feilsøking av vanlige implementeringsfeil. Målgruppen er utviklere og sikkerhetsansvarlige i norske og nordiske selskaper som allerede har fått beskjed fra Nasjonal sikkerhetsmyndighet om å begynne migreringen til kvanteresistente algoritmer.

Du trenger ikke være kryptograf for å følge denne guiden, men du bør kunne lese og kjøre Python, forstå grunnleggende TLS-begreper som håndtrykk og nøkkelutveksling, og være komfortabel med å bygge programvare fra kildekode på Linux. Alle 12 hovedstegene bygger på hverandre i rekkefølge, så følg dem i tur og orden fremfor å hoppe rett til koden dersom du er ny på post-kvante kryptografi.

Hvorfor hybrid ML-KEM og HQC, ikke bare én algoritme

ML-KEM (tidligere kjent som Kyber) bygger på gitterproblemer (module lattice problems). HQC bygger på kodeteori (Hamming-avstand i kvasi-sykliske koder). De to matematiske familiene har ingenting til felles, noe som er akkurat poenget. Dersom et fremtidig forskningsgjennombrudd knekker gitterbaserte problemer, faller ML-KEM, men HQC står fortsatt. Skjer det motsatte, gjelder samme logikk andre veien.

NIST omtaler selv ML-KEM som “hovedalgoritmen for generell kryptering”, mens HQC er ment som backup med et utkast til egen FIPS-standard (uformelt kalt FIPS 207) ventet i løpet av 2026 og endelig ferdigstillelse rundt 2027, ifølge NISTs presentasjon “FIPS 207: HQC-KEM” fra september 2025. Det betyr at HQC per i dag ikke er en ferdig standard du kan sette i produksjon alene, men den er stabil nok til at Open Quantum Safe-prosjektet allerede tilbyr en referanseimplementasjon du kan teste mot.

Den tyske sikkerhetsmyndigheten BSI anbefaler i sin felles PQC-uttalelse fra 2025 hybridoppsett med ML-KEM som primæralgoritme kombinert med klassisk elliptisk kurve-kryptografi som overgangsstrategi. Franske ANSSI følger samme linje i sin veiledning om post-kvante kryptografi. Ingen av de store europeiske sikkerhetsmyndighetene anbefaler i dag å stole på HQC alene, men flere peker på verdien av et tredje lag der HQC fungerer som ekstra beskyttelse mot ukjente svakheter i gitterkryptografi. Det er nettopp dette oppsettet vi bygger i denne guiden.

Bakgrunn: hvorfor NIST endte opp med to KEM-familier

NISTs post-kvante-prosess startet i 2016 med 82 innsendte kandidater. Etter flere runder med kryptoanalyse fra forskningsmiljøer over hele verden sto ML-KEM (den gang kalt Kyber) igjen som vinner i juli 2022, og ble formelt en ferdig standard som FIPS 203 i august 2024. Det som skiller HQC-runden fra dette, er at NIST bevisst holdt en fjerde runde åpen spesifikt for kodebaserte og andre ikke-gitterbaserte kandidater, nettopp for å unngå at hele det post-kvante økosystemet skulle hvile på én matematisk antakelse.

Da NIST kunngjorde HQC som vinner 11. mars 2025, pekte byrået eksplisitt på at den kodebaserte tilnærmingen har vært studert i akademia siden McEliece-kryptosystemet på slutten av 1970-tallet, og at HQC sin sikkerhet dermed hviler på et av de eldste og best undersøkte problemene i post-kvante kryptografi. Det er en helt annen risikoprofil enn ML-KEM, som baserer seg på module-lattice-problemer som først fikk bred kryptografisk interesse på 2000- og 2010-tallet. For deg som utvikler betyr dette i praksis at de to algoritmene ikke bare er matematisk uavhengige, men også har svært ulik forskningshistorikk bak seg, noe som er nøyaktig grunnen til at et hybridoppsett gir reell diversifisering av risiko, ikke bare kosmetisk variasjon.

Forutsetninger og versjoner du trenger

Før du starter, sjekk at miljøet ditt matcher disse minstekravene. Alt er testet på Ubuntu 24.04 LTS, men fremgangsmåten fungerer likt på Debian 12 og de fleste andre Linux-distribusjoner.

KomponentMinimumsversjonFormål i denne guiden
OpenSSL3.5.0 eller nyere (3.6 anbefalt)Native ML-KEM-støtte, TLS-testing
liboqs (Open Quantum Safe)Nyeste stabile versjon fra prosjektets repoHQC-implementasjon og referansetesting
Python3.11 eller nyereOrkestrering av hybrid nøkkelutveksling
liboqs-pythonNyeste versjon som matcher liboqsPython-bindings mot liboqs
cmake3.5 eller nyereBygging av liboqs fra kildekode
gcc/clangNyeste versjon i distroens pakkebrønnKompilering av native avhengigheter
ninja-buildNyeste versjonRaskere bygg av liboqs

Du trenger også minst 4 GB RAM til kompileringen av liboqs (den bygger dusinvis av algoritmevarianter samtidig) og omtrent 2 GB ledig diskplass. Sett av 30-45 minutter til selve oppsettet, pluss tid til å lese gjennom feilsøkingsdelen underveis.

Sjekk også følgende før du starter, siden det sparer deg for de vanligste avbruddene underveis: at brukeren din har sudo-rettigheter til å installere pakker og skrive til /opt og /usr/local/src, at systemklokken er riktig stilt (feil klokke gir kryptiske TLS-feil som ikke har noe med selve nøkkelutvekslingen å gjøre), og at du ikke har en eksisterende OpenSSL-installasjon på systemnivå som andre tjenester er avhengige av. Bygg alltid den PQC-aktiverte OpenSSL-versjonen til en egen prefiks-mappe, slik vi gjør i steg 1, i stedet for å overskrive systemets standard-OpenSSL. Det er den klart vanligste årsaken til at utviklere ødelegger andre deler av systemet mens de eksperimenterer med post-kvante kryptografi.

Steg 1: Installer OpenSSL 3.5+ med native ML-KEM

Ifølge Heise sin dekning av OpenSSL 3.5.0-utgivelsen ble ML-KEM, ML-DSA og SLH-DSA lagt til som native metoder i denne versjonen, uten behov for eksterne plugins. Sjekk først hva som allerede er installert:

openssl version
# Hvis versjonen er lavere enn 3.5.0, må du bygge fra kildekode

sudo apt update
sudo apt install -y build-essential checkinstall zlib1g-dev git

cd /usr/local/src
sudo git clone --depth 1 --branch openssl-3.5.0 https://github.com/openssl/openssl.git
cd openssl
sudo ./Configure --prefix=/opt/openssl-pqc --openssldir=/opt/openssl-pqc/ssl
sudo make -j$(nproc)
sudo make install_sw

export PATH=/opt/openssl-pqc/bin:$PATH
export LD_LIBRARY_PATH=/opt/openssl-pqc/lib64:$LD_LIBRARY_PATH
openssl version

Forventet output etter en vellykket installasjon:

OpenSSL 3.5.0 8 Apr 2025 (Library: OpenSSL 3.5.0 8 Apr 2025)

Bekreft at ML-KEM faktisk er tilgjengelig som KEM-algoritme:

openssl list -kem-algorithms | grep -i mlkem

Du skal se ML-KEM-512, ML-KEM-768 og ML-KEM-1024 listet opp, i tråd med den offisielle manualsiden til OpenSSL for EVP_KEM-ML-KEM, som beskriver hvordan innkapslingen følger FIPS 203 avsnitt 6.2, algoritme 17, ord for ord.

Steg 2: Bygg liboqs med HQC-støtte

HQC er ikke ferdig standardisert (draft-FIPS ventes i 2026), så den vanligste veien til en produksjonsnær implementasjon går via Open Quantum Safe-prosjektets liboqs, som vedlikeholder referanseimplementasjoner av kandidatalgoritmene NIST har valgt ut.

sudo apt install -y cmake ninja-build libssl-dev python3-pytest python3-pytest-xdist unzip xsltproc doxygen graphviz python3-yaml

cd /usr/local/src
sudo git clone --depth 1 https://github.com/open-quantum-safe/liboqs.git
cd liboqs
sudo mkdir build && cd build
sudo cmake -GNinja -DCMAKE_INSTALL_PREFIX=/opt/liboqs -DOQS_ENABLE_KEM_HQC=ON ..
sudo ninja
sudo ninja install

export LD_LIBRARY_PATH=/opt/liboqs/lib:$LD_LIBRARY_PATH

Bygget tar typisk 3-6 minutter avhengig av antall CPU-kjerner. Når det er ferdig, kjør den innebygde testsuiten for å bekrefte at HQC-varianter faktisk fungerer:

/usr/local/src/liboqs/build/tests/test_kem HQC-128
/usr/local/src/liboqs/build/tests/test_kem HQC-192
/usr/local/src/liboqs/build/tests/test_kem HQC-256

Alle tre skal avslutte med en linje som bekrefter at testen er bestått. Får du i stedet en feilmelding om manglende symbol eller “algorithm not enabled”, betyr det som regel at -DOQS_ENABLE_KEM_HQC=ON-flagget ikke ble tatt med i cmake-kommandoen, eller at du bygger mot en gammel commit fra før HQC ble lagt til i biblioteket.

Det er verdt å forstå hvorfor vi bruker to forskjellige biblioteker i denne guiden i stedet for ett. ML-KEM er en ferdig standard, så store, mye brukte biblioteker som OpenSSL har bygget den inn direkte i kjernen sin. HQC er fortsatt en kandidat på vei mot standardisering, og den typen algoritmer havner naturlig først i forskningsorienterte biblioteker som liboqs, som eksplisitt er bygget for å holde tritt med NISTs pågående prosess. Når HQC får sin egen ferdige FIPS-standard, er det sannsynlig at den også blir tatt inn i OpenSSL og tilsvarende produksjonsbiblioteker, slik ML-KEM allerede er.

BibliotekML-KEM-statusHQC-status
OpenSSLNative støtte fra 3.5.0, egen manualside i 3.6Ikke native ennå, venter på ferdig FIPS-standard
liboqs (Open Quantum Safe)Referanseimplementasjon tilgjengeligReferanseimplementasjon tilgjengelig med byggeflagget vist over
BoringSSLBrukes i hybrid TLS-eksperimenter i Chrome-økosystemetIkke dokumentert som produksjonsstøttet per i dag
Bouncy Castle (Java)Følger opp NIST-standarden i sine PQC-pakkerFølges opp etter hvert som HQC nærmer seg ferdig standard

Merk at eksakte versjonsnumre for BoringSSL og Bouncy Castle sin HQC-støtte endrer seg raskt akkurat nå. Sjekk alltid prosjektenes egne endringslogger før du legger en spesifikk versjon inn i egen avhengighetsliste, i stedet for å stole på tall du finner i artikler som denne.

Steg 3: Installer Python-bindings og forbered prosjektet

mkdir -p ~/pqc-hybrid-demo && cd ~/pqc-hybrid-demo
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip cryptography pytest

cd /usr/local/src
git clone --depth 1 https://github.com/open-quantum-safe/liboqs-python.git
cd liboqs-python
pip install .

python3 -c "import oqs; print(oqs.get_enabled_kem_mechanisms())" | tr ',' '\n' | grep -i hqc

Hvis Python ikke finner liboqs-biblioteket, må du eksportere miljøvariabelen på nytt i det samme terminalvinduet: export LD_LIBRARY_PATH=/opt/liboqs/lib:$LD_LIBRARY_PATH. Dette er den klart vanligste feilkilden i denne delen av oppsettet, fordi variabelen ikke arves av nye SSH-økter eller shell-prosesser.

Steg 4: Forstå nøkkel- og ciphertext-størrelsene du jobber med

Før du skriver noe hybridkode, bør du vite nøyaktig hvor mange byte hver algoritme legger til i håndtrykket ditt. Dette er ikke en detalj du kan overse: HQC-256 alene legger til over 21 700 byte i offentlig nøkkel pluss ciphertext, noe som kan bli et reelt problem for MTU-begrensede nettverk eller IoT-enheter med begrenset båndbredde.

Algoritme og nivåOffentlig nøkkel (byte)Ciphertext (byte)Delt hemmelighet (byte)
ML-KEM-51280073632
ML-KEM-768 (anbefalt standard)1 1841 08832
ML-KEM-10241 5681 56832
HQC-1282 2494 48164
HQC-1924 5229 02664
HQC-2567 24514 46964

Tallene for HQC er hentet direkte fra parametertabellen i den offisielle HQC-spesifikasjonen. Legg merke til forskjellen: en full ML-KEM-768-utveksling legger til rundt 2 272 byte totalt (offentlig nøkkel pluss ciphertext), mens den tilsvarende sikkerhetsklassen i HQC (HQC-192) legger til 13 548 byte, altså nesten seks ganger så mye. Det er prisen du betaler for å ha en uavhengig matematisk reserve i hybridoppsettet.

Steg 5: Skriv nøkkelgenerering for alle tre algoritmene

Nå setter vi sammen selve hybridlogikken. Prinsippet er enkelt: generer nøkkelpar for X25519 (klassisk), ML-KEM-768 (gitterbasert) og HQC-192 (kodebasert) uavhengig av hverandre. Ingen av algoritmene skal vite om de andre.

import oqs
from cryptography.hazmat.primitives.asymmetric import x25519

def generer_hybrid_nokler():
    # Klassisk X25519
    x25519_privat = x25519.X25519PrivateKey.generate()

    # ML-KEM-768 (gitterbasert, NIST FIPS 203)
    mlkem = oqs.KeyEncapsulation("ML-KEM-768")
    mlkem_offentlig = mlkem.generate_keypair()

    # HQC-192 (kodebasert, NIST backup-KEM)
    hqc = oqs.KeyEncapsulation("HQC-192")
    hqc_offentlig = hqc.generate_keypair()

    return {
        "x25519": x25519_privat,
        "mlkem": (mlkem, mlkem_offentlig),
        "hqc": (hqc, hqc_offentlig),
    }

if __name__ == "__main__":
    nokler = generer_hybrid_nokler()
    print(f"ML-KEM offentlig nøkkel: {len(nokler['mlkem'][1])} byte")
    print(f"HQC offentlig nøkkel: {len(nokler['hqc'][1])} byte")

Kjør skriptet, og du bør få denne outputen:

ML-KEM offentlig nøkkel: 1184 byte
HQC offentlig nøkkel: 4522 byte

Steg 6: Kombiner de tre delte hemmelighetene med en KDF

Den delen utviklere oftest gjør feil, er å tro at man bare kan konkatenere de tre delte hemmelighetene og bruke resultatet direkte som krypteringsnøkkel. Det fungerer i en demo, men er svakt i praksis fordi det ikke gir noen formell garanti for at det kombinerte resultatet er like sikkert som den sterkeste av de tre komponentene. Riktig fremgangsmåte er å kjøre alle tre gjennom en nøkkelutledningsfunksjon (KDF) sammen, typisk HKDF med SHA-384.

from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes

def utled_hybrid_nokkel(hemmelighet_x25519, hemmelighet_mlkem, hemmelighet_hqc, kontekst=b"pqc-hybrid-v1"):
    kombinert = hemmelighet_x25519 + hemmelighet_mlkem + hemmelighet_hqc
    hkdf = HKDF(
        algorithm=hashes.SHA384(),
        length=32,
        salt=None,
        info=kontekst,
    )
    return hkdf.derive(kombinert)

Rekkefølgen på konkatenering spiller ingen rolle for sikkerheten så lenge den er konsistent på begge sider av forbindelsen, men bruk alltid en fast, dokumentert rekkefølge i koden din. Vi anbefaler klassisk først, deretter gitterbasert, deretter kodebasert, fordi det matcher konvensjonen i de fleste hybrid-TLS-utkast som sirkulerer i IETF-miljøet.

Steg 7: Bygg server-siden av utvekslingen

import oqs
import base64
from cryptography.hazmat.primitives.asymmetric import x25519
from cryptography.hazmat.primitives import serialization

class HybridServer:
    def __init__(self):
        self.x25519_privat = x25519.X25519PrivateKey.generate()
        self.mlkem = oqs.KeyEncapsulation("ML-KEM-768")
        self.mlkem_offentlig = self.mlkem.generate_keypair()
        self.hqc = oqs.KeyEncapsulation("HQC-192")
        self.hqc_offentlig = self.hqc.generate_keypair()

    def eksporter_offentlige_nokler(self):
        x25519_bytes = self.x25519_privat.public_key().public_bytes(
            encoding=serialization.Encoding.Raw,
            format=serialization.PublicFormat.Raw,
        )
        return {
            "x25519": base64.b64encode(x25519_bytes).decode(),
            "mlkem": base64.b64encode(self.mlkem_offentlig).decode(),
            "hqc": base64.b64encode(self.hqc_offentlig).decode(),
        }

    def dekapsuler(self, klient_x25519_offentlig_b64, mlkem_ciphertext_b64, hqc_ciphertext_b64):
        klient_x25519_offentlig = x25519.X25519PublicKey.from_public_bytes(
            base64.b64decode(klient_x25519_offentlig_b64)
        )
        hemmelighet_x25519 = self.x25519_privat.exchange(klient_x25519_offentlig)
        hemmelighet_mlkem = self.mlkem.decap_secret(base64.b64decode(mlkem_ciphertext_b64))
        hemmelighet_hqc = self.hqc.decap_secret(base64.b64decode(hqc_ciphertext_b64))
        return hemmelighet_x25519, hemmelighet_mlkem, hemmelighet_hqc

Steg 8: Bygg klient-siden og fullfør håndtrykket

import oqs
import base64
from cryptography.hazmat.primitives.asymmetric import x25519
from cryptography.hazmat.primitives import serialization

class HybridKlient:
    def __init__(self):
        self.x25519_privat = x25519.X25519PrivateKey.generate()

    def start_handtrykk(self, server_nokler):
        server_x25519 = x25519.X25519PublicKey.from_public_bytes(
            base64.b64decode(server_nokler["x25519"])
        )
        hemmelighet_x25519 = self.x25519_privat.exchange(server_x25519)

        mlkem = oqs.KeyEncapsulation("ML-KEM-768")
        mlkem_ct, hemmelighet_mlkem = mlkem.encap_secret(
            base64.b64decode(server_nokler["mlkem"])
        )

        hqc = oqs.KeyEncapsulation("HQC-192")
        hqc_ct, hemmelighet_hqc = hqc.encap_secret(
            base64.b64decode(server_nokler["hqc"])
        )

        egen_x25519_offentlig = self.x25519_privat.public_key().public_bytes(
            encoding=serialization.Encoding.Raw,
            format=serialization.PublicFormat.Raw,
        )

        return {
            "for_server": {
                "x25519": base64.b64encode(egen_x25519_offentlig).decode(),
                "mlkem_ct": base64.b64encode(mlkem_ct).decode(),
                "hqc_ct": base64.b64encode(hqc_ct).decode(),
            },
            "hemmeligheter": (hemmelighet_x25519, hemmelighet_mlkem, hemmelighet_hqc),
        }

Kjør en full simulering lokalt: instansier HybridServer, eksporter offentlige nøkler, send dem til HybridKlient.start_handtrykk(), og bekreft at nøklene som utledes på begge sider (via utled_hybrid_nokkel fra steg 6) faktisk er identiske med en enkel assert-setning.

Steg 9: Test hybridoppsettet mot ekte TLS med OpenSSL

Python-demoen over viser prinsippet, men i produksjon vil de fleste bruke hybrid nøkkelutveksling gjennom TLS 1.3, ikke en egen protokoll. Test at OpenSSL 3.5+ faktisk forhandler ML-KEM i en ekte TLS-forbindelse:

# Terminal 1: start en testserver med ML-KEM-gruppe
openssl s_server -cert server.crt -key server.key \
  -groups X25519MLKEM768 -tls1_3 -accept 8443 -www

# Terminal 2: koble til og verifiser hvilken gruppe som ble brukt
openssl s_client -connect localhost:8443 -groups X25519MLKEM768 -tls1_3 2>&1 | grep -i "server temp key"

Forventet linje i output: Server Temp Key: X25519MLKEM768, 4096 bits. Siden HQC ennå ikke har en offisiell IANA-gruppekode for TLS 1.3, må HQC-laget i produksjon i dag håndteres utenfor selve TLS-håndtrykket, for eksempel som et eget lag over applikasjonsprotokollen, slik vi bygde i steg 7 og 8. Det endrer seg når draft-FIPS for HQC blir ferdig i løpet av 2026.

Du kan også bekrefte at hybridgruppen faktisk fungerer mot en ekte nettleser. Chrome og Firefox har begge eksperimentert med hybrid TLS med ML-KEM i sine nyere versjoner. Åpne utviklerverktøyene i nettleseren, gå til fanen for sikkerhet under en tilkobling til testserveren din, og se etter hvilken nøkkelutvekslingsgruppe som ble forhandlet i “Connection”-detaljene. Stemmer ikke gruppen med det du forventer, er den vanligste årsaken enten at nettleseren din er for gammel til å støtte hybrid ML-KEM, eller at serverens -groups-parameter i konfigurasjonen lister opp gruppen i feil prioritert rekkefølge, slik at nettleseren og serveren forhandler seg ned til en eldre, rent klassisk gruppe i stedet.

Et raskere alternativ til en full nettleser er å bruke curl kompilert mot samme PQC-aktiverte OpenSSL-build som serveren din. Da kan du skrive automatiserte røyktester som kjører i CI-pipelinen hver gang du endrer TLS-konfigurasjonen, i stedet for å måtte verifisere manuelt i en nettleser hver gang.

Steg 10: Sett opp automatiserte tester

Skriv minst disse testene før du setter noe i produksjon: korrekt utledede nøkler på begge sider av håndtrykket, at en endret ciphertext på hvilken som helst av de tre algoritmene fører til at hele håndtrykket feiler, at systemet håndterer manglende HQC-støtte i biblioteket på en trygg måte (feil, ikke stille degradering), og ytelsestest under last.

import pytest

def test_hybrid_nokler_matcher():
    server = HybridServer()
    klient = HybridKlient()
    resultat = klient.start_handtrykk(server.eksporter_offentlige_nokler())
    server_hemmeligheter = server.dekapsuler(
        resultat["for_server"]["x25519"],
        resultat["for_server"]["mlkem_ct"],
        resultat["for_server"]["hqc_ct"],
    )
    assert utled_hybrid_nokkel(*server_hemmeligheter) == utled_hybrid_nokkel(*resultat["hemmeligheter"])

def test_manipulert_ciphertext_feiler():
    server = HybridServer()
    klient = HybridKlient()
    resultat = klient.start_handtrykk(server.eksporter_offentlige_nokler())
    odelagt_ct = resultat["for_server"]["mlkem_ct"][:-4] + "AAAA"
    with pytest.raises(Exception):
        server.dekapsuler(resultat["for_server"]["x25519"], odelagt_ct, resultat["for_server"]["hqc_ct"])

Steg 11: Mål ytelsen på din egen maskinvare

NIST-presentasjonen “Building Post-Quantum Cloud Services” fra desember 2024 diskuterer ML-KEM i skytjenester, men gir ingen tall du kan kopiere direkte inn i din egen kapasitetsplanlegging fordi maskinvare, nettverk og trafikkmønster varierer for mye. Riktig fremgangsmåte er å måle selv med liboqs sitt innebygde benchmark-verktøy:

/usr/local/src/liboqs/build/tests/speed_kem ML-KEM-768
/usr/local/src/liboqs/build/tests/speed_kem HQC-192

Verktøyet skriver ut operasjoner per sekund for nøkkelgenerering, innkapsling og dekapsulering på nettopp din maskinvare. Fordi HQC har vesentlig større ciphertext enn ML-KEM (se tabellen i steg 4), bør du forvente at HQC bruker mer CPU-tid per operasjon og legger merkbart mer last på nettverksbåndbredden i høyfrekvente tilkoblingsscenarioer, som API-endepunkter med mange korte forbindelser.

Sikkerhetsgjennomgang: konstant tid og sidekanaler

En implementasjon som er matematisk korrekt, kan likevel lekke hemmeligheter gjennom tidsforskjeller, strømforbruk eller cache-oppførsel. Dette kalles sidekanalangrep, og det er en av de mest oversette risikoene når team bytter fra RSA og ECC til post-kvante-algoritmer, rett og slett fordi den nye kodebasen er mindre gjennomtestet i praksis enn tiår gamle biblioteker. OpenSSL sin egen dokumentasjon for EVP_KEM-ML-KEM er eksplisitt på at innkapslingsoperasjonen kun kan påvirkes gjennom kontrollert tilfeldighet ment for testing, nettopp for å holde den ordinære kjøreveien fri for tidsvariasjon som kan utnyttes.

Når du setter hybridoppsettet i produksjon, bør du kreve tre ting av enhver PQC-avhengighet du tar inn: at biblioteket har vært gjenstand for uavhengig kryptoanalyse eller kodegjennomgang, at prosjektet aktivt vedlikeholdes og patcher kjente sidekanalproblemer, og at du selv kjører automatiserte konstant-tid-tester (for eksempel med verktøy som dudect) som en del av CI-pipelinen din, ikke bare funksjonelle korrekthetstester. Dette gjelder i enda større grad for HQC enn for ML-KEM, fordi HQC sin referanseimplementasjon i liboqs har fått mindre produksjonsherding enn ML-KEM, som allerede er innlemmet i et modent bibliotek som OpenSSL.

Vær også bevisst på at ingen offentlig kjente, praktisk utnyttbare sidekanalsårbarheter i selve ML-KEM- eller HQC-algoritmene er dokumentert i NIST sine offisielle kanaler eller i BSI og ANSSI sine 2025-vurderinger på det tidspunktet denne artikkelen ble skrevet. Det betyr ikke at implementasjonene er risikofrie, bare at risikoen i dag primært ligger i hvordan algoritmene implementeres i det enkelte bibliotek, ikke i selve den matematiske konstruksjonen. Følg derfor sikkerhetsvarslene til liboqs og OpenSSL løpende, og oppdater avhengighetene dine så snart patcher slippes.

Steg 12: Planlegg opprydding og nøkkelrotasjon

Slett alle midlertidige private nøkler fra minnet umiddelbart etter bruk, og bygg inn rotasjon av langtidsnøklene på et fast intervall (typisk 30-90 dager for tjenestenøkler). Bruk et bibliotek eller en teknikk som eksplisitt nuller ut minnebuffere etter bruk, siden Python sin normale søppelinnsamling ikke garanterer at sensitive byte faktisk overskrives i minnet.

Overvåkning og logging i produksjon

Et hybridoppsett du ikke overvåker, er verdiløst den dagen noe går galt. Logg minst følgende for hver håndtrykksøkt: hvilke algoritmer som faktisk ble forhandlet (ikke bare hvilke som var tilgjengelige), tidsbruk for hver av de tre delkomponentene i utvekslingen, og eventuelle dekapsuleringsfeil sammen med hvilken algoritme som feilet. Uten denne granulariteten er det nesten umulig å skille en reell konfigurasjonsfeil fra et forbigående nettverksproblem når noe bryter sammen i produksjon klokken to om natten.

Sett opp et eget varsel som utløses hvis andelen tilkoblinger som faller tilbake til ren klassisk kryptografi (uten noen av de to post-kvante-lagene) stiger over en fast terskel, for eksempel én prosent. En brå økning i fallback-rate er ofte det første tegnet på at noe er galt med enten sertifikathåndteringen eller selve biblioteksinstallasjonen på en undergruppe av serverne dine, og du vil helst oppdage det før det blir et sikkerhetsproblem i stor skala, ikke etter at noen andre påpeker det.

Vurder også å eksportere ciphertext-størrelsen som en egen metrikk. Fordi HQC sine ciphertext-verdier er så mye større enn ML-KEM sine (se tabellen i steg 4), vil en uventet økning i gjennomsnittlig pakkestørrelse på nøkkelutvekslingstrafikken din raskt fortelle deg om en klient av en eller annen grunn har falt tilbake til et høyere HQC-sikkerhetsnivå enn forventet, noe som igjen kan indikere en feilkonfigurasjon lenger opp i kjeden.

Tidsplan og kostnad for migrering i nordiske organisasjoner

De fleste selskaper undervurderer hvor lang tid en full post-kvante-migrering tar, fordi de kun regner med selve kodeendringen og glemmer resten av kjeden. En realistisk migreringsplan for en mellomstor norsk eller nordisk organisasjon strekker seg typisk over tre faser. Først en kartleggingsfase på fire til åtte uker der du identifiserer alle steder i arkitekturen som gjør nøkkelutveksling, inkludert tredjepartstjenester og eldre systemer du kanskje har glemt. Deretter en pilotfase på to til tre måneder der hybridoppsettet fra denne guiden testes mot et avgrenset sett tjenester med lav risiko. Til slutt en gradvis utrulling som ofte tar seks til tolv måneder ekstra for større organisasjoner med mange sammenkoblede systemer.

Kostnadsbildet handler mindre om lisenser, siden både OpenSSL og liboqs er åpen kildekode, og mer om ingeniørtid: oppgradering av sertifikatinfrastruktur, testing mot alle klienter som skal kobles til, og økt båndbredde- og CPU-forbruk fra de større nøklene. For organisasjoner som er underlagt NSM sine kryptografiske anbefalinger, bør denne kostnaden uansett veies opp mot konsekvensen av å bli sittende med sårbar kryptering den dagen kryptografisk relevante kvantedatamaskiner blir en realitet, et scenario sikkerhetsmyndigheter internasjonalt nå planlegger for som et “når”, ikke et “hvis”.

Fem vanlige feil ved implementering av hybrid ML-KEM og HQC

  • Å konkatenere delte hemmeligheter uten KDF. Som nevnt i steg 6 må de tre hemmelighetene alltid gjennom en nøkkelutledningsfunksjon sammen, aldri brukes rått som krypteringsnøkkel.
  • Å bruke feil sikkerhetsnivå på tvers av algoritmene. Å pare ML-KEM-1024 med HQC-128 gir ubalansert sikkerhet. Match nivåene: 512 med 128, 768 med 192, 1024 med 256.
  • Å glemme at HQC mangler offisiell TLS-gruppekode. Mange utviklere prøver å sette HQC direkte i -groups-parameteren til OpenSSL og blir forvirret når det feiler, fordi IANA ennå ikke har tildelt en offisiell kodepunkt for HQC i TLS 1.3.
  • Å ikke teste degraderingsscenarioer. Hva skjer hvis liboqs-biblioteket mangler på en produksjonsserver? Systemet skal feile tydelig, ikke falle stille tilbake til bare klassisk kryptografi.
  • Å undervurdere båndbreddekostnaden. Et fullt trippel-hybrid håndtrykk med ML-KEM-768 og HQC-192 legger til over 15 800 byte sammenlignet med et rent klassisk håndtrykk. På mobilnett eller IoT-lenker med begrenset MTU kan dette kreve fragmentering du må teste eksplisitt.

Feilsøking: de vanligste problemene og løsningene

  • “ImportError: cannot import name ‘oqs'” — liboqs-python ble ikke installert riktig i det aktive virtuelle miljøet. Kjør pip install . på nytt fra liboqs-python-mappen mens venv er aktivert.
  • “OSError: liboqs.so: cannot open shared object file”LD_LIBRARY_PATH peker ikke til /opt/liboqs/lib. Legg eksporten inn i ~/.bashrc for å unngå å gjenta den i hver ny terminal.
  • “Mechanism not supported” for HQC — liboqs ble bygget uten -DOQS_ENABLE_KEM_HQC=ON. Bygg på nytt fra bunnen med riktig flagg.
  • OpenSSL viser fortsatt gammel versjon etter installasjon — systemets PATH peker fortsatt til den distro-installerte OpenSSL-versjonen foran din egen build. Sjekk rekkefølgen med which -a openssl.
  • “groups X25519MLKEM768” gir feilmelding i s_server — du bygde OpenSSL uten enable-tls1_3, eller bruker en versjon eldre enn 3.5.0. Bekreft med openssl version.
  • Ytelsen er mye tregere enn forventet — sjekk at du ikke kjører en debug-build av liboqs. Bygg med -DCMAKE_BUILD_TYPE=Release for produksjonsmåling.
  • Nøklene matcher ikke på tvers av klient og server i testene — vanligste årsak er feil rekkefølge på konkatenering i HKDF-kallet i steg 6. Rekkefølgen må være identisk på begge sider.
  • Store ciphertext-verdier feiler i HTTP-headere eller URL-parametere — HQC sine 9 026 byte for nivå 192 er for stort for mange standard header-grenser. Send ciphertext i request body, ikke i headere.

Avanserte tips for produksjonsmiljøer

Når du beveger deg fra demo til produksjon, bør du vurdere følgende. For det første: ikke server HQC-laget over samme TCP-forbindelse som TLS-håndtrykket hvis du kan unngå det, fordi det kompliserer feilsøking av nettverksfeil betraktelig. Kjør heller HQC-utvekslingen som et eget, veldefinert steg rett etter at TLS-forbindelsen med ML-KEM er etablert, og send resultatet kryptert inne i den allerede sikrede kanalen.

For det andre: hold en feature-bryter i konfigurasjonen din som lar deg slå HQC-laget av og på uten å bygge på nytt. Fordi HQC-standardiseringen ikke er ferdig før 2026-2027, kan detaljer i referanseimplementasjonen endre seg, og du vil ha muligheten til raskt å deaktivere laget dersom liboqs slipper en brytende endring.

For det tredje: dokumenter nøyaktig hvilken versjon av liboqs som ble brukt til å generere hver nøkkel, i metadata knyttet til nøkkelen. Siden HQC fortsatt er under aktiv utvikling, kan interne detaljer i implementasjonen endre seg mellom versjoner selv om de offentlige API-ene forblir stabile.

Rammeverket “Applied Quantum PQC Migration Framework” (versjon 1.1, mars 2026) anbefaler hybrid ML-KEM-768 pluss X25519 som førstevalg for de fleste virksomheter som starter TLS-migreringen nå, med HQC lagt til som et ekstra lag først når organisasjonen har spesifikke krav til algoritme-diversitet, for eksempel i finanssektoren eller kritisk infrastruktur der en enkelt svikt i én matematisk familie ikke kan aksepteres.

For det fjerde: bygg alltid inn algoritme-agilitet fra dag én, altså muligheten til å bytte ut hvilken som helst av de tre algoritmene uten å skrive om resten av systemet. NSA sitt CNSA 2.0-rammeverk aksepterer i dag hybridoppsett kun som en midlertidig løsning på vei mot rene post-kvante-algoritmer, noe som betyr at kravene til nøyaktig hvilke algoritmer som skal kombineres, med stor sannsynlighet endrer seg igjen i løpet av de neste par årene. Et system der ML-KEM, HQC og den klassiske komponenten er tre separate, utskiftbare moduler, koster mer å bygge i første omgang, men sparer deg for en full omskriving neste gang standardene beveger seg.

Hva sier de nordiske sikkerhetsmyndighetene om tidsplanen

Nasjonal sikkerhetsmyndighet (NSM) har publisert egne kryptografiske anbefalinger som norske virksomheter bør legge til grunn når de planlegger overgangen. Anbefalingen er i tråd med den brede internasjonale linjen: bruk hybridoppsett med en klassisk algoritme og en NIST-godkjent post-kvante-algoritme i overgangsperioden, fremfor å bytte direkte til rene post-kvante-løsninger uten sikkerhetsnett. Den nederlandske etterretningstjenesten AIVD har publisert en tilsvarende migreringshåndbok for organisasjoner som håndterer langtidssensitiv informasjon, med anbefaling om å starte migreringen tidlig nettopp fordi data som fanges opp og lagres i dag kan bli dekryptert med en fremtidig kvantedatamaskin (“høst nå, dekrypter senere”).

For norske og nordiske selskaper i finans, kraft, telekom og offentlig sektor er den praktiske konsekvensen at hybrid ML-KEM-migrering ikke lenger er noe man kan utsette til 2027. Byggeklossene finnes allerede i OpenSSL 3.5+, og tilleggslaget med HQC er tilgjengelig for testing gjennom Open Quantum Safe-prosjektet i dag, selv om den offisielle FIPS-standardiseringen av HQC ennå ikke er ferdig.

Komplett prosjektstruktur

Slik ser den ferdige mappestrukturen ut når du har fulgt alle stegene over. Hver fil tilsvarer nøyaktig ett av kodeeksemplene lenger opp i artikkelen, så du kan kopiere dem inn i riktig fil etter hvert som du følger stegene:

pqc-hybrid-demo/
├── venv/
├── hybrid_server.py       # Steg 7
├── hybrid_klient.py       # Steg 8
├── kdf_utils.py           # Steg 6: HKDF-kombinering
├── noekkelgenerering.py   # Steg 5
├── test_hybrid.py         # Steg 10: pytest-suite
├── benchmark.sh           # Steg 11: ytelsesmåling
└── README.md

Kjør hele testsuiten med pytest test_hybrid.py -v for å bekrefte at alt fungerer sammen før du går videre til integrasjon med et ekte TLS-lag eller en meldingskø.

Ofte stilte spørsmål om ML-KEM og HQC

Må jeg bruke både ML-KEM og HQC, eller holder det med ML-KEM alene?

For de fleste virksomheter holder det med hybrid ML-KEM pluss en klassisk algoritme som X25519. HQC bør legges til som et tredje lag først når du har spesifikke krav til algoritme-diversitet, typisk i finans, kritisk infrastruktur eller andre sektorer der en enkelt svikt i gitterbasert kryptografi ville være uakseptabelt.

Er HQC klar for produksjon i 2026?

Nei, ikke som eneste algoritme. HQC har ikke en ferdig FIPS-standard ennå, med utkast ventet i løpet av 2026 og endelig ferdigstillelse rundt 2027. Bruk den kun som et ekstra lag ved siden av en allerede standardisert algoritme som ML-KEM.

Hvorfor er HQC sine nøkler og ciphertext så mye større enn ML-KEM sine?

De to algoritmene bygger på ulik matematikk. ML-KEM bruker gitterproblemer som gir kompakte strukturer, mens HQC bruker feilkorrigerende koder som krever mer redundans for samme sikkerhetsnivå. Det er nettopp denne strukturelle forskjellen som gjør at de to algoritmene er verdifulle sammen, siden et angrep som svekker den ene matematiske familien, ikke automatisk svekker den andre.

Kan jeg bruke ML-KEM og HQC direkte i TLS 1.3 i dag?

ML-KEM kan brukes direkte i TLS 1.3 gjennom OpenSSL 3.5 og nyere med gruppenavn som X25519MLKEM768. HQC har ikke en offisiell IANA-tildelt gruppekode for TLS 1.3 ennå, så den må i dag håndteres som et eget lag utenfor selve TLS-håndtrykket.

Hvilket sikkerhetsnivå bør jeg velge: ML-KEM-768 eller ML-KEM-1024?

ML-KEM-768 er anbefalt som standardvalg for de fleste bruksområder. ML-KEM-1024 gir høyere sikkerhetsmargin, men koster mer i nøkkelstørrelse og CPU-tid, og er mest relevant for svært langsiktig sensitiv informasjon.

Påvirker hybrid nøkkelutveksling ytelsen merkbart for sluttbrukere?

For ML-KEM alene er ytelseskostnaden liten nok til at store aktører allerede tester den i produksjonstrafikk. Legger du til HQC som et tredje lag, øker både beregningstid og båndbredde merkbart, og du bør måle konsekvensene på din egen infrastruktur med verktøyet i steg 11 før du ruller ut i stor skala.

Hvordan forbereder jeg meg på at HQC-standarden endres før den er ferdig i 2026-2027?

Hold HQC-laget bak en feature-bryter i konfigurasjonen, dokumenter nøyaktig hvilken liboqs-versjon som ble brukt til hver nøkkel, og følg med på oppdateringer fra Open Quantum Safe-prosjektet og NISTs offisielle kanaler før du gjør HQC til en obligatorisk del av produksjonstrafikken din.

Finnes det norske eller nordiske krav om å migrere til post-kvante kryptering nå?

NSM har publisert egne kryptografiske anbefalinger som legger den samme linjen som BSI og ANSSI internasjonalt: start hybridmigreringen nå, fremfor å vente til kvantedatamaskiner faktisk truer dagens kryptering. Dette gjelder særlig for organisasjoner som håndterer informasjon som må forbli konfidensiell i mange år fremover, siden krypterte data som avlyttes og lagres i dag potensielt kan dekrypteres senere når en tilstrekkelig kraftig kvantedatamaskin finnes.

Er dette oppsettet realistisk for et lite utviklerteam, eller krever det et dedikert sikkerhetsteam?

Selve ML-KEM-laget er realistisk for de fleste team med vanlig backend-erfaring, siden det i praksis håndteres av OpenSSL på samme måte som eksisterende TLS-oppsett. Det er HQC-laget som krever mer, fordi du da bygger og vedlikeholder liboqs selv og må følge med på en algoritme som ennå ikke er ferdig standardisert. Et pragmatisk startpunkt for mindre team er derfor å rulle ut ML-KEM-hybriden i steg 1, 5, 7 og 8 først, og vente med det fulle HQC-laget til enten teamet vokser eller kravene til algoritme-diversitet blir konkrete fra kunde eller regulator.

Relaterte artikler

Les mer om post-kvante kryptografi og relaterte temaer på Kryptografi-siden vår.