PDFlibPas toteuttaa ML-DSAn, FIPS 204:ssa standardoidun Module-Lattice-Based Digital Signature Algorithmin, kokonaan Object Pascalilla. Kaikki kolme parametrijoukkoa toimitetaan tavallisina funktioina: MLDSA44Sign, MLDSA65Sign, MLDSA87Sign sekä vastaavat KeyGen- ja Verify-keinot. Ei OpenSSL:a, ei alustakohtaista DLL:ää, ei C-liimaa. Yksittäinen PDFlibMLDSA-yksikkö ei riipu mistään muusta kuin kirjaston SHAKE-sienestä, ja sen tuloste vastaa virallisia FIPS 204 known-answer-testivektoreita tavua tarkalleen
Se viimeinen virke on ainoa osa, joka vei oikeaa työtä. Verkkoaritmetiikan kirjoittaminen Pascalilla on mekaanista; sen saaminen sopimaan NISTin kanssa ei ole. Seuraava on siirron engineering-kertomus: miten kolme parametrijoukkoa päätyivät jakamaan yhden moottorin, ja ne täsmälliset viat, jotka erottivat kääntyy ja toimii tilanteesta vastaa KAT:ia. Jos punnitut post-kvanttivalintoja Delphi- tai C++Builder-dokumenttiputkeen, viat ovat hyödyllinen osa, koska jokainen niistä tuottaa uskottavan näköistä tulosta, joka hiljaisesti epäonnistuu yhteentoimivuudessa
Miksi kirjoittaa post-kvantti allekirjoittaja puhtaana Object Pascalina?
Koska vaihtoehto on yksi natiivi riippuvuus per kohde, ja Delphi-PDF-kirjastolla on niitä jo tarpeeksi. PDFlibPas rakentuu Delphillä, C++Builderilla ja FPC/Lazaruksella Win32-, Win64- ja Unix-kohdeille; C-post-kvanttikirjaston sitominen tarkoittaisi sen käännöksen seuraamista jokaisessa näistä paikoista sekä kutsukäytäntöjen ja muistiomistuksen pinnan niiden välillä. Puhdas Pascal-yksikkö kääntyy siellä missä kirjaston loputkin kääntyvät, ja se on koko perustelu
ML-DSA tekee tästä epätavallisen halpaa, koska sen ainoa primitiiviriippuvuus on SHAKE. Ei isoja kokonaislukukerrosta, ei elliptistä käyrää, ei erillistä tiivistyspakettia. PDFlibPas sai streaming-XOF:n julkaisussa heti ennen siirtoa: TPLShakeXOF yksikössä PDFlibDigest, jossa PLShakeXOFInit valitsee SHAKE128:n (rate 168) tai SHAKE256:n (rate 136), jota seuraavat PLShakeXOFAbsorb, PLShakeXOFFinalize ja PLShakeXOFSqueeze-silmukka, joka permutoi edelleen mielivaltaisen tulostusmitan verran. Jokainen hylkäysotantarutiini ML-DSA-yksikössä on kirjoitettu suoraan sitä neljän kutsun API:a vasten
Yksi moottori, kolme parametrijoukkoa: TMLDSAParams
PDFlibPas kuvaa kokonaisen ML-DSA-parametrijoukon yhdellä tietueella ja valitsee sen joukkonumerolla, joten ML-DSA-44, 65 ja 87 kulkevat samoja koodipolkuja. Ensimmäinen toimiva toteutus oli kiinteä 4x4-käännös, joka oli kytketty kovaa ML-DSA-44:lle; sen yleistäminen tarkoitti k:n ja l:n, etan, tauun, betan, gamma1:n ja gamma2:n, omean sekä haastepituuden nostamista TMLDSAParamsiin ja kaiken muun johtamista siitä. Julkiset sisääntulopisteet muuttuivat kolmen rivin kääreiksi
Type
TMLDSAParams= Record
K, L, D, Eta, Tau, Beta, Gamma1, Gamma2, Omega: Integer;
Alpha, MW1: Cardinal;
W1BW, EtaBW, Gamma1BW, T1BW: Integer;
T0Rng: Cardinal;
CTildaBytes: Integer;
PublicKeyBytes, SecretKeyBytes, SignatureBytes: Integer;
End;
// Johdetut kentät lasketaan, ei koskaan kirjoiteta taulukosta
Params.Alpha:= 2* Cardinal(Params.Gamma2);
Params.MW1:= (Q- 1)div Params.Alpha;
Params.W1BW:= BitWidth(Params.MW1- 1);
Params.EtaBW:= BitWidth(2* Cardinal(Params.Eta));
Params.Gamma1BW:= BitWidth(Cardinal(Params.Gamma1));
Function MLDSA65Sign(Const SecretKey, Message, Context, Rnd: AnsiString;
Out Signature: AnsiString): Boolean;
Var
Params: TMLDSAParams;
Begin
BuildMLDSAParams(65, Params);
Result:= MLDSASignInternal(Params, SecretKey, Message, Context, Rnd,
Signature);
End;
Viisi johdettua kenttää lasketaan tarkoituksella eikä kopioida FIPS 204 -taulukoista. Käsin kirjoitetut bittileveydet ovat täsmälleen se vakioluokka, joka näyttää katselmuksessa oikealta ja on yhden pielessä tuotannossa, ja kaksi todellista vikaa tässä siirrossa oli juuri sitä muotoa. Ilmoitetut mitat pysyvät nimettyinä vakioina validointia varten: 1312 / 2560 / 2420 tavua julkista avainta, salaista avainta ja allekirjoitusta ML-DSA-44:lle, 1952 / 4032 / 3309 ML-DSA-65:lle, 2592 / 4896 / 4627 ML-DSA-87:lle
Missä tyhjästä tehty ML-DSA-siirto menee ensin pieleen?
expand_assa, FIPS 204 algoritmi 32, ja vikatila on kauniisti harhaanjohtava. Matriisi A otannetaan kylvämällä SHAKE128 rho:lla ja sen jälkeen kahdella indeksitavulla, joten siemenpuskuri on 34 tavua: rho(32), sitten j, sitten i. Pascalilla kirjoitettuna 1-pohjaisella AnsiString-indeksoinnilla ne kaksi tavua ovat Msg[33] ja Msg[34]. Tämän siirron ensimmäinen luonnos kirjoitti ne Msg[34]ään ja Msg[35]ään, siirrettynä täsmälleen yhdellä tavulla, ja tulos oli avainpari, jonka rho vastasi testivektoria täydellisesti, kun taas jokainen t:n kerroin oli väärä. Vain matriisi oli saastunut, ja matriisi on se ainoa asia, jota julkinen avain ei kanna sanatarkasti
Kaksi muuta vikaa asui samassa rutiinissa. Absorb-pituuden on oltava 34, ei 35; yksi ylimääräinen roskatavu muuttaa koko puristetun virran. Ja sisimmän hylkäyssilmukan on kulutettava jokainen kolmen tavun ryhmä, jonka lohko voi tarjota, mukaan lukien se, joka alkaa 168-tavuisen SHAKE128-lohkon siirtymästä 165, eli 56 ryhmää per lohko. Siirtymässä 162 pysähtynyt ristitarkistusskripti pudotti jokaisen lohkon hännän ja siirsi otannetun t1-etuliitteen suunnilleen kolmannestoista tavusta alkaen
SetLength(Msg, 34);
Move(Rho[1], Msg[1], 32);
Msg[33]:= AnsiChar(J); // sarakeindeksi ensin
Msg[34]:= AnsiChar(I); // sitten riviindeksi
PLShakeXOFInit(Ctx, True); // SHAKE128, rate 168
PLShakeXOFAbsorb(Ctx, @Msg[1], 34);
PLShakeXOFFinalize(Ctx);
Cnt:= 0;
While Cnt< N Do
Begin
PLShakeXOFSqueeze(Ctx, @Buf[0], 168);
BOff:= 0;
// BOff+2 <= 167 säilyttää ryhmän siirtymässä 165: 56 kolmikkoa per lohko
While (BOff+ 2<= High(Buf))And (Cnt< N) Do
Begin
T3:= ((Buf[BOff+ 2]and $7F)shl 16)xor (Buf[BOff+ 1]shl 8)xor Buf[BOff];
If T3< Q Then
Begin
Poly^[Cnt]:= T3;
Inc(Cnt);
End;
Inc(BOff, 3);
End;
End;
Kun ne kolme oli korjattu, täydellisen ML-DSA-44 julkisen ja salaisen avaimen SHA-256-tiivistelmät vastasivat FIPS 204 known-answer-vektoreita. Yksi vianetsintäoppi kannattaa nimetäkin, koska se maksoi istunnon: kun rakennat Python-ristitarkistusta XOF-ohjatulle hylkäyssilmukalle, hashlib.shake_128().digest(n) palauttaa saman etuliitteen jokaisella kutsulla virran jatkamisen sijaan. Ota koko mitta kerralla ja viipaloi se ratemittaisiin lohkoihin, tai viiteyksikkösi kuluttaa mielellään uudelleen täsmälleen ne arvot, jotka Pascalisi hylkäsi oikein
Etan otanta: miksi ML-DSA-65 tarvitsee oman haaransa
PDFlibPas pitää kaksi erillistä polkua expand_sissä, koska FIPS 204 algoritmi 33 todella määrittää kaksi. Kun eta = 2, kukin niitti hylätään, kun se saavuttaa 15:n, ja muuten se pienennetään mod 5. Kun eta = 4, niitti hylätään 9:ssä tai ylempänä ja otetaan sitten käyttöön suoraan, ilman mitään modulaarista pienennystä. ML-DSA-65 on ainoa toimitettu joukko, jolla eta = 4, ja mod 5 -polun uudelleenkäyttö sille väristää s1:n ja s2:n jo ensimmäisestä kertoimesta alkaen tuottaen avainparin, joka on sisäisesti johdonmukainen, verifioituu itseään vastaan ja ei vastaa mitään, mitä kukaan muu tuottaa
Procedure StoreNibble(Nibble: Byte);
Var
M: Integer;
Centered: Cardinal;
Begin
If Cnt>= N Then
Exit;
If Eta= 4 Then
Begin
If Nibble>= 9 Then // hylkää, ota sitten niitti sellaisenaan
Exit;
M:= Nibble;
End
Else
Begin
If Nibble>= 15 Then // eta = 2: hylkää 15, pienennä sitten mod 5
Exit;
M:= Nibble mod 5;
End;
If Eta>= M Then
Centered:= Eta- M
Else
Centered:= Q- (M- Eta);
Vec[I][Cnt]:= Centered;
Inc(Cnt);
End;
Mitat ovat testi: c-tilde-pituus ja gamma1-bittileveys
Kaksi koodausparametria vaihtelee turvallisuustason mukana tavoin, jotka on helppo ohittaa, kun toimiva ML-DSA-44-käännös on juuri siinä. Haastetiiviste c-tilde on 2 x lambda / 8 tavua, eli 32 ML-DSA-44:lle, 48 ML-DSA-65:lle ja 64 ML-DSA-87:lle. Sen jättäminen kiinteäksi 32:ksi tuottaa 3293 tavun ML-DSA-65-allekirjoituksen vakio 3309:n sijaan, ja KAT-etuliite haarautuu heti. Tietuekenttä CTildaBytes on olemassa täsmälleen siksi, ettei sitä numeroa unohdettaisi
Toinen on maskipolynomin z pakkausleveys. PDFlibPas laskee sen BitWidth(Gamma1)na, ei eksponenttina: gamma1 = 2^19 ML-DSA-65:llä ja 87 tarvitsee 20 bittiä per kerroin, ei 19, ja se yksi bitti päättää, viekö kukin z-polynomi 640 tavua vai jotain, mitä mikään verifioija ei jäsennä. Verifioijalla oli siirron aikana vastaava vika, jossa z-deserialisointipuskuri mitoitettiin 192 tavuun 576:n sijaan. Allekirjoituksen pituus on halvin regressiotesti, jonka koskaan kirjoitat: väitä 2420, 3309 ja 4627 Length(Signature)ia vastaan, ja useimmat parametrisointivirheet ilmoittavat itsestään ennen kuin saavutat yhdenkään kryptografisen väitteen
Allekirjoittaminen ilman rajatonta silmukkaa
ML-DSA-allekirjoitus on hylkäyspohjainen, joten se yrittää uudelleen kasvatetulla kappalla, kunnes ehdokasallekirjoitus läpäisee normi- ja hint-tarkistuksensa. PDFlibPas rajoittaa sen eksplisiittisellä ulkoisella budjetilla 65535 yritystä; uupumuksen yhteydessä MLDSASignInternal palauttaa Falserin ja jättää allekirjoituksen tyhjäksi pyörimisen sijaan dokumentintuontisäikeen sisällä. Käytännössä virallinen ML-DSA-44-vektori onnistuu kappalla 4 ja 55 hintillä omega-kattoon 80 verrattuna, joten budjetti on turvakaide eikä toimiva raja
Vika, joka sai tuen kaiteen tuntumaan tarpeelliselta, ei ollut lainkaan numeerinen. Allekirjoittaminen näytti jumittuvan, epäily osui decomposeiin ja make_hintiin (FIPS 204 algoritmit 36 ja 39), ja todellinen syy oli käänteinen kertymiskohde: vektorin, joka syöttää hint-laskentaa, on kerrytettävä c*t0, kun taas alkuperäisen c*t0:n on säilyttävä koskemattomana normitarkistusta varten. Suuntaa molemmat samaan puskuriin, ja silmukka hylkää ikuisesti täysin oikealla aritmetiikalla. Sekä onnistumisen että budjetin uupumisen poluilla yksikkö nollaa johdetut siemenet, salaiset polynomit, maskit, haasteen ja koodauspuskurit; kutsujan toimittama siemen, salainen avain ja rnd pysyvät kutsujan vastuulla, mikä on oikea jako kirjastolle, joka ei voi tietää, mistä ne merkkijonot tulevat
Missä ML-DSA kohtaa PDF-allekirjoituspinon tänä päivänä?
Ole täsmällinen siitä, mitä on olemassa. PDFlibPas toimittaa ML-DSAn verifioituina allekirjoitusprimitiiveinä plus PKCS #11 -mekanismisidoksena, ei valmiina korvikkeena nykyiselle PAdES-tuotoksellesi. Token-polku on TPDFlibPKCS11Client.SignMLDSA, ja se on tahallaan erillinen sisääntulopiste, koska CKM_ML_DSA kuluttaa raakaviestin valmiiksi lasketun tiivistelmän sijaan, joten olemassa olevia SignHash- ja ulkoisen tiivistelmän takaisinkutsuja ei voi käyttää uudelleen. Sertifikaatiton löytäminen edellyttää CertificateOptionalin eksplisiittistä käyttöönottoa yksityisen avaimen tunnisteen tai ID:n kanssa, ja asiakas validoi CKA_PARAMETER_SETin CKP_ML_DSA_44 / 65 / 87 -valkoluetteloa vastaan yhdistämisen hetkellä, joten oletus-RSA- ja ECDSA-sertifikaattiparitus ei koskaan pääse höllentymään vahingossa
Dokumenttitason integraatio on se osa, jota yhä hallitsee standardityö kirjastokoodin sijaan. ISO 32000-2 §12.8 määrittää allekirjoitussanakirjan ja sen CMS-payloadin, ja ISO/TS 32002 on ajoneuvo, jolla se tuki laajennetaan uudempiin tiiviste- ja allekirjoitusalgoritmeihin; kunnes validoijasi ja vastapuolesi seuraavat, klassinen allekirjoittaminen pysyy tuotantopolulla. Käytännön asenne on rinnakkaiset raiteet: toimita edelleen PAdES B-B:stä B-LTA:aan ulottuvia allekirjoituksia aikaleimalla ja pitkän aikavälin validointidatalla kaikkeen, mikä on kolmannen osapuolen validoituva tänään, ja todista samalla ML-DSA-avainkäsittely ja token-integraatio. Paikallisiin kokeiluihin sama CryptoAPI:lle rakennettu itse allekirjoitettujen sertifikaattien työnkulku antaa allekirjoitusidentiteetin ilman julkista CA:ta
Testaa parametrijoukon muutos niin kuin testaisit minkä tahansa muun allekirjoitusmuutoksen. Ensin mitat, sitten viralliset vektorit, sitten negatiiviset tapaukset: muokattu allekirjoitustavu, ei-yhteensopiva kontekstimerkkijono, katkaistu avain. PDFlibPas kattaa ne kaikki DUnitX-testisarjassaan, ja sama kuri kuuluu omaan putkeesi, ihanteellisesti vaatimustenmukaisuus- ja allekirjoitustyöpöydän rinnalla, joka niputtaa validoinnin dokumenttikorpuksen yli, jotta regressio ei koskaan tavoita asiakasta huomaamatta
Post-kvanttivalmius dokumenttiohjelmistolle ei saavu yhtenä kytkimenä. Se saavuu primitiiveinä, joita voit testata, token-polkuna, jonka voit kytkeä, ja standardiraitana, jota seuraat panostamatta nykyistä julkaisua siihen. Nähdäksesi, miten ML-DSA-yksikkö istuu allekirjoituksen, salauksen ja PDF/A-työkalujen lopun rinnalla natiivissa Object Pascal -koodikannassa, PDFlibPas Delphi PDF library -tuotesivu luetteloi koko komponenttijoukon ja tuettujen kääntäjien matriisin