Puncte:2

De ce există o ușoară nepotrivire de cele mai multe ori în cheile private X25519, în funcție de funcțiile utilizate, dar cheile publice se potrivesc întotdeauna (aceeași sămânță pentru ambele)?

drapel cn
mkl

Încerc să-mi închei capul mergând de la a sămânță la a Cheie de semnare, precum și obținerea unui cheie privată (cheie de criptare). Folosesc NaCl / libsodium.

Am creat codul de mai jos și rezultatele sunt interesante. Se pare pk1.cheie_privată și pk2.cheie_privată se potrivesc aproximativ 3% din timp. Oricum cheia publică se potrivește 100%, toate sunt generate începând cu aceeași sămânță. Ce se intampla aici?

  • eu obtin pk1 prin utilizarea PrivateKey.from_seed(seed)

  • eu obtin pk2 prin utilizarea SigningKey(seed).to_curve25519_private_key()

Exemple de nepotrivire (sunt apropiate, dar nu sunt egale):

# Nepotrivirea primului octet de 0x01
sămânță: f8d9e54a23971beebf2552c1a50ade6150cd051321398394f515e8d4b1ba0404
priv1: c1fd4612ee8ef24d295210a277e196e6bb4a9ae6b93f98d93f197860fe5dc048
priv2: c0fd4612ee8ef24d295210a277e196e6bb4a9ae6b93f98d93f197860fe5dc048

# Nepotrivirea primului octet de 0x03
sămânță: d612a66f92ee2f42ab1f7ea9a712a47c815843d21fc988b1d202459f235b6410
priv1: f33d5e80bb556333e2961c9868b1dc7e548836ee56808689ca022f1a19fe86bb
priv2: f03d5e80bb556333e2961c9868b1dc7e548836ee56808689ca022f1a19fe867b

# Nepotrivirea primului octet cu 0x01, ultimul octet nepotrivire cu 0x40
sămânță: 10b7e1c66cf08005a22289158a088e028160f892dc6c20d43025be4690aaed85
priv1: 194898f65d117579d50e80a9b7e07bd048bfd1300d55561dac9dfaed4ef02109
priv2: 184898f65d117579d50e80a9b7e07bd048bfd1300d55561dac9dfaed4ef02149
din nacl.signing import SigningKey
de la nacl.public import PrivateKey, PublicKey, Box, SealedBox
din nacl.bindings import crypto_sign_SEEDBYTES
din nacl.utils import StringFixer, aleatoriu

def run(debug=False):
    sămânță = aleatoriu (cripto_sign_SEEDBYTES)
    pk1 = PrivateKey.from_seed(seed)
    pk2 = SigningKey(seed).to_curve25519_private_key()
    dacă se depanează:
        print('seed: ', seed.hex()) 
        print('priv1: ', pk1._private_key.hex())
        print('priv2: ', pk2._private_key.hex())
        print('pub1: ', bytes(pk1.public_key).hex())
        print('pub2: ', bytes(pk2.public_key).hex())
    returnează sămânța, pk1, pk2

rulări = 10000
private_key_match = 0
public_key_match = 0
ambele_potrivire = 0

pentru eu în rază (rule):
    dacă i % 500 == 0:
        print(i, „din”, rulează)
    sămânță, pk1, pk2 = run()
    x = pk1._private_key == pk2._private_key
    y = bytes(pk1.public_key) == bytes(pk2.public_key)
    daca x:
        private_key_match += 1
    daca eu:
        public_key_match += 1
    dacă x și y:
        ambele_potriviți += 1

print('potrivirea cheii private:', private_key_match)
print('potrivirea cheii publice: ', public_key_match)
print('ambele se potrivesc: ', ambele_se potrivesc)
Maarten Bodewes avatar
drapel in
Cheia privată ar trebui să aibă câțiva biți setați la valori specifice, dacă au fost deja setate întâmplător, atunci obțineți ceea ce obțineți.
drapel cn
mkl
@MaartenBodewes, nu sunt sigur că urmăresc, cheia privată nu reprezintă un număr întreg utilizat în calculul unei chei publice, precum și în decriptare. De ce mai mulți biți ar fi lipsiți de importanță? Există vreo specificație care îmi lipsește?
Maarten Bodewes avatar
drapel in
Uf, ar trebui să caut ecuațiile pentru asta, totuși mă întreb dacă diferența nu este atunci când sunt aplicate măștile de biți. Speram să primesc ajutor de la colegii mei criptografi de aici.
Puncte:2
drapel cn

Din punct de vedere matematic, a Curba25519 cheia privată este un element al $\mathbb{F}_{2^{255}-19}$, adică un întreg modulo $2^{255}-19$. Acest lucru poate fi în mod natural reprezentat ca un număr întreg între $0$ și $2^{255}-19-1$, care în sine poate fi reprezentat folosind 255 de biți. Deoarece computerele practice lucrează cu octeți de 8 biți, cea mai mică reprezentare practică este ca un șir de 32 de biți.

Există un format standard pentru reprezentarea cheilor private Curve25519, care este descris în RFC 7748 §5. Acest format codifică numărul întreg între $0$ și $2^{255}-19-1$ ca șir de 32 de octeți (256 de biți) în ordine little-endian.

Un șir de 32 de octeți poate reprezenta numere din interval $[0,2^{256}-1]$, care este mai mare decât $[0,2^{255}-19-1]$. De asemenea, din diverse motive, nu toate numerele din interval $[0,2^{255}-19-1]$ sunt bune (vezi Structura cheii Curve25519 și „Fie ca al patrulea să fie cu tine: un atac al canalului lateral microarhitectural asupra mai multor aplicații din lumea reală ale Curbei25519” de Genkin și colab.). Constrângerile sunt:

  • Numărul trebuie să fie între $2^{254}$ și $2^{255}-19$, deci cei mai semnificativi doi biți ai numărului de 256 de biți trebuie să fie 0 și 1.
  • Numărul trebuie să fie un multiplu de 8, deci cei mai puțin semnificativi trei biți trebuie să fie 0.

RFC 7748 §5 specifică că atunci când se generează o cheie privată, trebuie să ia un șir aleator (sau pseudoaleatoriu, după caz) de 32 de octeți și să forțezi cei cinci biți menționați mai sus la valoarea lor obligatorie. O implementare a lui Curve25519 care preia o cheie privată ca intrare sub forma unui șir de 32 de octeți trebuie să aplice această mascare înainte de a începe să facă calcule asupra numărului pe care cheia îl reprezintă.

nacl.public.PrivateKey.from_seed returnează șirul brut de 32 de octeți. nacl.signing.SigningKey(seed) efectuează mascarea, deci to_curve25519_private_key exportă valoarea în forma sa canonică, mascata.

Mascarea se referă la 5 biți, deci având în vedere o sămânță aleatorie, există o $1/2^5 = 1/32 \aproximativ 3\%$ șansa că are deja valoarea corectă pentru cei 5 biți.

drapel cn
mkl
Răspuns excelent, mulțumesc!
Puncte:2
drapel cn

„curva25519” (cum a numit-o inițial Bernstein, acum redenumită X25519 sau XDH25519 pentru claritate, deoarece același curba într-o formă diferită este utilizat pentru Ed25519[ph]) necesită ca cheia privată (multiplicatorul) să fie un multiplu de 8 pentru a evita atacurile cu subgrupuri mici și, în mod convențional, este reprezentat în little-endian, astfel încât cei 3 biți inferiori ai primului octet sunt forțați să 0.

X25519 necesită, de asemenea, ca bitul înalt să fie 0 (deoarece câmpul său de bază este doar despre $2^{255}$) și Bernstein specifică bitul de lângă înalt să fie 1 (pentru a bloca o „optimizare” care ar permite atacuri de sincronizare); acestea afectează ultimul octet în formă little-endian, pe care se pare că nu l-ați observat în exemplele dvs., modifică și bb - 7b și 09 - 49.

Aceste două (sau trei) strângeri luate împreună vor schimba o valoare aleasă aleatoriu în toate cazurile, cu excepția (100/32)% din timp, care este suficient de aproape de 3% pentru munca guvernamentală.

Ed25519 are de fapt aceeași nevoie ca multiplicatorul său privat să fie un multiplu de 8, dar nu folosește cheia privată direct pentru multiplicator; în schimb, rulează cheia privată printr-un hash pentru a se extinde și a utiliza atât pentru multiplicator, cât și pentru o valoare secretă care orbește mesajul și blochează intern doar partea multiplicatoare, fără a modifica ceea ce utilizatorul vede ca cheie privată.

Dupe De ce cei 3 biți inferiori ai cheilor secrete curve25519/ed25519 sunt șterși în timpul creării?
și Structura cheii Curve25519
si vezi/compara https://datatracker.ietf.org/doc/html/rfc7748#page-8
vs https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5 .

knaccc avatar
drapel es
Rețineți că nu este neapărat nevoie ca cheile private pe oricare dintre variantele curbei 25519 să fie un multiplu de 8. Anumite implementări de semnătură sau DH pot face acest lucru, dar aceasta este doar o modalitate de a preveni un mic atac de subgrup. De asemenea, puteți preveni atacul înmulțind punctul EC cu ordinea grupului și verificând că rezultatul este punctul de la infinit. Acest lucru va verifica, de asemenea, că punctul EC se află în subgrupul corect mai mare, ceea ce previne alte tipuri de atacuri, cum ar fi un atac de unicitate a imaginii cheii de semnătură inel care poate fi conectat.
drapel cn
mkl
@dave_thompson_085, este foarte util, mulțumesc.Deci, se pare că una dintre funcții este implementată incomplet? Chiar dacă oricare cheie privată produce aceeași cheie publică (presupun că se întâmplă o verificare/curățare la generarea cheii pub)
dave_thompson_085 avatar
drapel cn
@miketry Nimic nu este incomplet. După cum am spus, schemele sunt definite diferit; X25519 necesită fixarea cheii private care este văzută extern, în timp ce Ed25519 o aplică intern, astfel încât cheia privată care este văzută nu este fixată, ci multiplicarea curbei eliptice în interior este.

Postează un răspuns

Majoritatea oamenilor nu înțeleg că a pune multe întrebări deblochează învățarea și îmbunătățește legătura interpersonală. În studiile lui Alison, de exemplu, deși oamenii își puteau aminti cu exactitate câte întrebări au fost puse în conversațiile lor, ei nu au intuit legătura dintre întrebări și apreciere. În patru studii, în care participanții au fost implicați în conversații ei înșiși sau au citit transcrieri ale conversațiilor altora, oamenii au avut tendința să nu realizeze că întrebarea ar influența – sau ar fi influențat – nivelul de prietenie dintre conversatori.