Το PDFlibPas υπογράφει και επαληθεύει με Ed448 και με τις τρεις καμπύλες Brainpool ECDSA σε καθαρό Object Pascal. Χωρίς εξωτερική κρυπτογραφική βιβλιοθήκη, χωρίς πάροχο πλατφόρμας, χωρίς DLL: το PDFlibEd448 υλοποιεί το PureEdDSA του RFC 8032 στο edwards448, και το PDFlibBrainpool υλοποιεί τις brainpoolP256r1, brainpoolP384r1 και brainpoolP512r1 του RFC 5639. Και οι δύο χτίστηκαν με τον ίδιο τρόπο, έναντι δοκιμαστικών διανυσμάτων γνωστής απάντησης που δημιουργήθηκαν ανεξάρτητα πριν γραφτεί οποιοδήποτε Pascal, και οι δύο αξίζουν να παρουσιαστούν κυρίως για τα σφάλματά τους
Η αριθμητική σωμάτων είναι ασυνήθιστα ειλικρινής κώδικας. Είτε ταιριάζει με τα δημοσιευμένα διανύσματα byte προς byte είτε δεν ταιριάζει, οπότε δεν υπάρχει χώρος για «πάνω κάτω λειτουργικό». Το δύσκολο είναι ότι μια λανθασμένη υλοποίηση εξακολουθεί να παράγει υπογραφές, εξακολουθεί να επαληθεύει τις δικές της υπογραφές και εξακολουθεί να φαίνεται απολύτως πειστική
Γιατί αυτές οι καμπύλες, και γιατί σε Pascal
Οι καμπύλες Brainpool εμφανίζονται στα ευρωπαϊκά προφίλ πιστοποιημένης υπογραφής, οπότε μια βιβλιοθήκη που υπογράφει έγγραφα για αυτή την αγορά δεν μπορεί να τις αντιμετωπίζει ως περιθωριακές. Το Ed448 ανήκει στο σύνολο αλγορίθμων που φέρνει το ISO/TS 32002 στο PDF, όπου η εσωτερική του περίληψη είναι SHAKE256 και όχι SHA-2. Καμία από τις δύο οικογένειες δεν είναι διαθέσιμη στις συνήθεις κρυπτογραφικές βιβλιοθήκες Pascal, οπότε μια βιβλιοθήκη PDF που τις χρειάζεται πρέπει να τις διαθέτει η ίδια
Το επιχείρημα ανάπτυξης είναι το ίδιο που ισχύει για όλη την κρυπτογραφία αυτής της βιβλιοθήκης: μια εφαρμογή που διανέμει ένα εκτελέσιμο χωρίς κρυπτογραφική εξάρτηση δεν έχει πάροχο προς ανίχνευση, δεν έχει έκδοση προς ταίριασμα και δεν έχει συμπεριφορά που αλλάζει όταν ο υπολογιστής δέχεται ενημερώσεις. Η υπογραφή είναι ακριβώς ο τομέας όπου λιγότερο θέλετε μια εξάρτηση που μετακινείται
Οι σταθερές προέρχονται από το κείμενο της προδιαγραφής, ποτέ από μνήμη
Η πρώτη απόπειρα για το βασικό σημείο του edwards448 γράφτηκε από μνήμη και ήταν λανθασμένη. Δεν είναι μια σπάνια αβελτηρία, αλλά κοστίζει πολύ ακριβά, επειδή ένα λανθασμένο βασικό σημείο παράγει ένα αυτοσυνεπές σύστημα: η δημιουργία κλειδιών, η υπογραφή και η επαλήθευσή σας συμφωνούν μεταξύ τους και διαφωνούν με τον υπόλοιπο κόσμο
Η σωστή διαδικασία είναι να λαμβάνεται κάθε παράμετρος πεδίου από το κείμενο της προδιαγραφής και μετά να γίνεται διασταύρωση. Για το edwards448 αυτό σημαίνει τον πρώτο αριθμό, τη σταθερά της καμπύλης, την τάξη της ομάδας και τις δύο δεκαδικές συντεταγμένες του βασικού σημείου από το RFC 8032, τη μετατροπή τους στην εσωτερική αναπαράσταση limb και τον έλεγχό τους έναντι δημοσιευμένων δοκιμαστικών διανυσμάτων του ίδιου εγγράφου. Για τις καμπύλες Brainpool σημαίνει τις παραμέτρους από το RFC 5639, μια ανεξάρτητη υλοποίηση γραμμένη για τη δημιουργία διανυσμάτων και έναν αμφίδρομο έλεγχο έναντι βιβλιοθήκης συστήματος και προς τις δύο κατευθύνσεις, πριν εκτελεστεί οποιοδήποτε Pascal
Μια συντόμευση παραγωγής αξίζει προειδοποίηση επειδή μοιάζει καθολική και δεν είναι: η ανάκτηση του βασικού σημείου από σταθερή τιμή y λειτουργεί για την καμπύλη 25519 και δεν λειτουργεί για το edwards448, όπου η τιμή αυτή δεν έχει τετραγωνική ρίζα. Ένα script την κατέρριψε σε δευτερόλεπτα, που είναι πολύ φθηνότερο από το να την ανακαλύψετε μέσω debugger
Η μέθοδος: ένας κατοπτρισμός σε επίπεδο limb πριν από οποιοδήποτε Pascal
Η τεχνική που έκανε και τις δύο μονάδες διαχειρίσιμες είναι μια υλοποίηση-κατοπτρισμός σε γλώσσα με απεριόριστους ακεραίους, χτισμένη από κάτω προς τα πάνω. Πρώτα μόνο το στρώμα αριθμητικής: πολλαπλασιασμός και αφαίρεση στο σώμα και διάδοση κρατούμενου, με δοκιμές καταπόνησης βάσει των αλγεβρικών τους αναλλοίωτων σε μερικές εκατοντάδες τυχαίες περιπτώσεις. Μετά η πλήρης δημιουργία κλειδιών μέσα στον κατοπτρισμό, που είναι εκεί όπου ζουν τα σημασιολογικά σφάλματα και όπου η εύρεσή τους κοστίζει λίγο. Και μόνο ύστερα η μεταγραφή σε Pascal
Το όφελος είναι διαγνωστικό και όχι αναπτυξιακό. Μόλις ο κατοπτρισμός είναι γνωστός ως σωστός, κάθε ασυμφωνία μεταξύ κατοπτρισμού και Pascal είναι ολίσθηση μεταγραφής, και η εξέταση της ίδιας ενδιάμεσης τιμής και στις δύο υλοποιήσεις την εντοπίζει αμέσως. Έτσι μια κατηγορία σφαλμάτων που αλλιώς είναι σχεδόν αδύνατη να αποσφαλματωθεί, ένα μοναδικό λάθος limb βαθιά μέσα σε βαθμωτό πολλαπλασιασμό, γίνεται μια σύγκριση πέντε λεπτών
Τέσσερις βασικές αιτίες στο Ed448
Και οι τέσσερις βρέθηκαν με εξέταση ενδιάμεσων τιμών, και οι τέσσερις είναι ο τύπος του σφάλματος που παράγει έξοδο που μοιάζει έγκυρη
Η πρώτη είναι μια παγίδα συμβολισμού. Οι περισσότεροι δημοσιευμένοι τύποι για ενοποιημένη πρόσθεση Edwards προϋποθέτουν σταθερά καμπύλης ίση με μείον ένα, ενώ το edwards448 έχει συν ένα. Αν μεταφερθεί αναλλοίωτος, ο αριθμητής της συντεταγμένης y γράφεται ως άθροισμα εκεί που πρέπει να είναι διαφορά. Η διόρθωση δεν είναι να διορθωθεί το πρόσημο, αλλά να εξαχθεί εκ νέου η μορφή γινομένου χωρίς αντιστροφή από τον νόμο πρόσθεσης affine της σωστής καμπύλης, κάτι που παράγει τις τέσσερις εκφράσεις συντεταγμένων και δεν αφήνει περιθώριο να κληρονομηθεί πρόσημο από λάθος πηγή
Η δεύτερη βρίσκεται στην αποσυμπίεση σημείων. Η ανάκτηση του affine x από προβολικές συντεταγμένες απαιτεί έναν πολλαπλασιασμό με το αντίστροφο του Z. Ο πολλαπλασιασμός με το αντίστροφο στο τετράγωνο δίνει μια τιμή που εξακολουθεί να είναι έγκυρη προβολική αναπαράσταση και είναι η λάθος affine συντεταγμένη, οπότε το σύμπτωμα είναι ένα σωστό y με λάθος x. Κάθε φορά που η μία συντεταγμένη είναι σωστή και η άλλη όχι, το σφάλμα βρίσκεται στην κανονικοποίηση, όχι στην αριθμητική
Η τρίτη είναι μια συνήθεια που κουβαλήθηκε από τη βραχύτερη καμπύλη. Τόσο ο βαθμωτός ανά υπογραφή όσο και ο βαθμωτός πρόκλησης πρέπει να ανάγονται από την πλήρη περίληψη, που για το Ed448 είναι 114 bytes, και όχι από τα πρώτα 57 της. Η καμπύλη των 32 bytes χρησιμοποιεί επίσης ολόκληρη την περίληψη των 64 bytes της, οπότε ο κανόνας είναι συνεπής· λανθασμένη είναι μόνο η υπόθεση ότι «το μισό μήκος της περίληψης είναι το πλάτος του βαθμωτού»
Η τέταρτη είναι η σειρά. Το πρόθεμα διαχωρισμού πεδίου έρχεται πρώτο, πριν από το πρόθεμα του context και το μήνυμα, που δεν είναι η σειρά που υπονοεί η διαισθητική ανάγνωση των R και A στην προδιαγραφή. Το λάθος εδώ παράγει υπογραφές που επαληθεύονται μόνο στη δική σας υλοποίηση και πουθενά αλλού, που είναι η πιο παραπλανητική δυνατή αποτυχία
// Σχεδιασμός κρατούμενου σώματος: καθαρή διάδοση με σημασιολογία
// floor, ώστε να λειτουργούν θετικά και αρνητικά limbs και η αφαίρεση
// να μην χρειάζεται προσαρμογή. Το κρατούμενο κορυφής αναδιπλώνεται
// μέσω 2^448 = 2^224 + 1 (mod p), που αγγίζει το limb 0 και το limb 8.
// Περιορισμένο σε τέσσερις γύρους· στην πράξη παρατηρήθηκαν δύο
procedure FeCarry(var A: TFe448);
var
I, Round: Integer;
Carry: Int64;
begin
for Round := 1 to 4 do
begin
Carry := 0;
for I := 0 to 15 do
begin
A[I] := A[I] + Carry;
Carry := Floor28(A[I]); // floor, όχι περικοπή
A[I] := A[I] - (Carry shl 28);
end;
if Carry = 0 then
Break;
A[0] := A[0] + Carry; // 2^448 == 1
A[8] := A[8] + Carry; // 2^448 == 2^224
end;
end;
Μια προηγούμενη έκδοση της ρουτίνας εφάρμοζε προσαρμογή πριν τη διάδοση, και με μεγάλες εισόδους δίπλωνε ψευδές κρατούμενο λάθος τάξης μεγέθους στα χαμηλά limbs. Τα σχήματα κρατούμενου με προσαρμογή είναι διαρκής πηγή αυτής της κατηγορίας ελαττωμάτων· η σημασιολογία floor με δεσμευμένο βρόχο επανάληψης είναι ευκολότερη στη συλλογιστική και μετρήσιμα αρκετά γρήγορη
Δύο βασικές αιτίες στο Brainpool
Η πρώτη δεν είναι καθόλου κρυπτογραφία. Η λειτουργική αναπαράσταση είναι 33 limbs, οπότε το γινόμενο δύο τιμών χρειάζεται 66, και ο πίνακας γινομένου είχε δηλωθεί με 64. Η εγγραφή πέρα από το τέλος κατέστρεψε γειτονική μνήμη, που εμφανίστηκε πρώτα ως λάθος αποτελέσματα και έγινε κατάρρευση μόνο όταν προστέθηκε ευρύτερος έλεγχος. Ο κανόνας που προέκυψε αξίζει να εφαρμόζεται σε κάθε αριθμητικό buffer σταθερού μεγέθους: υπολογίστε το μέγεθος από το πλάτος γινομένου χειρότερης περίπτωσης, προσθέστε περιθώριο, και μετά μην το ξανασκεφτείτε. Ο πίνακας στον κώδικα που διανέμεται είναι 68 limbs
Η δεύτερη είναι ένα μπερδεμένο σχήμα ύψωσης σε δύναμη. Υπάρχουν δύο σωστές μορφές square-and-multiply και καταναλώνουν τον εκθέτη σε αντίθετες κατευθύνσεις: η μορφή από δεξιά προς αριστερά πολλαπλασιάζει και μετά υψώνει στο τετράγωνο τη βάση, και πρέπει να διαβάζει bits από το λιγότερο σημαντικό άκρο, ενώ η μορφή από αριστερά προς δεξιά υψώνει στο τετράγωνο και μετά πολλαπλασιάζει και διαβάζει από το πιο σημαντικό άκρο. Ο βρόχος modular αντιστροφής είχε σώμα από δεξιά προς αριστερά με διάβαση bits ξεκινώντας από το πιο σημαντικό άκρο. Και τα δύο μισά είναι σχολικά, ο συνδυασμός δεν είναι, και το αποτέλεσμα είναι λάθος αντίστροφο που εξακολουθεί να μοιάζει με πειστικό στοιχείο του σώματος
// Διπλασιασμός και πρόσθεση Jacobian όπου η εγγραφή προορισμού μπορεί
// να είναι η ίδια μεταβλητή με μια πηγή. Αντίγραφο ολόκληρης της
// εγγραφής στην είσοδο είναι η μόνη αξιόπιστη άμυνα: η εγγραφή στα
// limbs του R μολύνει τις μεταγενέστερες αναγνώσεις του P
procedure BPPointDouble(var R: TBPPoint; const P: TBPPoint;
const Curve: TBPCurve);
var
Pin: TBPPoint;
begin
Pin := P; // πρώτα αντίγραφο, μετά υπολογισμός μόνο από το Pin
// ... M = 3X^2 + A*Z^4, S = 4*X*Y^2, X3 = M^2 - 2S, ...
end;
Δύο μαθήματα διαδικασίας που κόστισαν περισσότερο από τα σφάλματα
Η σταδιακή επιδιόρθωση εν ώρα λειτουργία δεν συγκλίνει σε κρυπτογραφική μονάδα. Ένα προσχέδιο μαλώθηκε επανειλημμένα μέχρι να κουβαλά 32 διπλότυπες ρουτίνες και κατεστραμμένη δομή, και διορθώθηκε μόνο με πλήρη επανεγγραφή. Το μοτίβο προς υιοθέτηση είναι είτε να το γράψετε μια φορά από επικυρωμένο κατοπτρισμό είτε να το ξαναγράψετε· μια σειρά τοπικών διορθώσεων σε αριθμητική που δεν καταλαβαίνετε ακόμη συσσωρεύεται ταχύτερα από όσο διορθώνει
Και ελέγξτε τη χρονική σήμανση στο εκτελέσιμο πριν πιστέψετε ένα αποτέλεσμα δοκιμής. Ένα σταδιακό build που μεταγλωττίζει αλλά δεν επανασυνδέει τρέχει το προηγούμενο εκτελέσιμο, κάτι που κατασκεύασε έναν ολόκληρο γύρο ψευδών ιχνών για δοκιμές που έλειπαν και διπλότυπη έξοδο. Όταν αποσφαλματώνετε κρυπτογραφία, ένα ανεξήγητο αποτέλεσμα πρέπει να προκαλεί την ερώτηση «αυτό είναι το εκτελέσιμο που μόλις έφτιαξα;» πριν το «μήπως ο αλγόριθμος είναι λάθος;»
Απόδοση, εύρος και πώς να το καλείτε
Η modular αναγωγή στη μονάδα Brainpool είναι σειριακή σε bits αφαίρεση με ολίσθηση από το υψηλότερο αναμένο bit του γινομένου, οπότε ένας πολλαπλασιασμός κοστίζει κατά προσέγγιση όσο η τάξη του πλάτους bits. Μια επαλήθευση P-256 καταλήγει στα χαμηλά εκατοντάδες χιλιοστά του δευτερολέπτου, κάτι απόλυτα φυσιολογικό για υπογραφή ή επαλήθευση εγγράφων και ανεπαρκές για έναν τερματισμό TLS. Η αναγωγή Barrett είναι η προφανής αναβάθμιση και χρειάζεται ευρύτερη τιμή εργασίας από όση μεταφέρει η τρέχουσα αναπαράσταση, οπότε είναι αλλαγή για τη στιγμή που ένας φόρτος εργασίας θα τη ζητήσει, όχι προληπτική
uses
PDFlibEd448, PDFlibBrainpool;
var
PublicKey, Signature: AnsiString;
Curve: TBPCurve;
R, S, PubX, PubY: TBPValue;
begin
// Ed448: PureEdDSA, SHAKE256 εσωτερικά, κλειδιά 57 bytes
if Ed448PublicKeyFromSeed(Seed, PublicKey) and
Ed448Sign(DocumentDigest, Seed, Signature) then
Assert(Ed448Verify(DocumentDigest, PublicKey, Signature));
// Brainpool: ο καλών παρέχει το nonce ανά υπογραφή, οπότε η
// πολιτική nonce παραμένει στην εφαρμογή
Curve := BPLoadCurve(bpP256r1);
if BPKeyGen(PubX, PubY, PrivateD, Curve) and
BPSignFixedK(R, S, Hash, PrivateD, Nonce, Curve) then
Assert(BPVerify(R, S, Hash, PubX, PubY, Curve));
end;
Σημειώστε ότι το σημείο εισόδου υπογραφής Brainpool λαμβάνει το nonce αντί να το παράγει. Είναι σκόπιμο: η δημιουργία nonce είναι το ενιαίο πιο καταστροφικό λάθος στην ECDSA, αφού μια επαναλαμβανόμενη ή προβλέψιμη τιμή αποκαλύπτει το ιδιωτικό κλειδί, και η απόφαση για το πού προέρχεται η τυχαιότητα ανήκει στην εφαρμογή και στο καθεστώς συμμόρφωσής της, όχι σε μια βιβλιοθήκη PDF
Αυτές οι καμπύλες συνοδεύουν τη μετα-κβαντική εργασία που περιγράφεται στο άρθρο για το FIPS 204 ML-DSA, και συνδέονται στον ίδιο αγωγό υπογραφής και επικύρωσης που καλύπτεται στην υπογραφή και επικύρωση PAdES. Για δοκιμαστικά πιστοποιητικά σε αυτές τις καμπύλες, η τοπική διαδρομή δημιουργίας περιγράφεται στα αυτοϋπογεγραμμένα πιστοποιητικά με CryptoAPI. Ο πλήρης αλγοριθμικός πίνακας καταγράφεται στη σελίδα προϊόντος losLab PDF Developer Library