Aggiunta su curve ellittiche


29

Aggiunta su curve ellittiche

Disclaimer: questo non rende giustizia al ricco tema delle curve ellittiche. È molto semplificato. Dato che le curve ellittiche hanno recentemente attirato molta attenzione da parte dei media nel contesto della crittografia, volevo fornire alcune piccole intuizioni su come funziona effettivamente il "calcolo" su una curva ellittica.

introduzione

Le curve ellittiche sono insiemi di punti (x,y)nel piano della forma y^2 = x^3+Ax+B. (Inoltre, 4A^3+27B^2 ≠ 0per evitare brutte singolarità.) Puoi considerare queste curve in qualsiasi campo. Se si utilizza il campo di numeri reali, le curve possono essere visualizzate e si presentano così:

due esempi di curve ellittiche
fonte

La particolarità di queste curve è che hanno un'operazione aritmetica incorporata che è l'analogo dell'aggiunta. È possibile aggiungere e sottrarre punti e questa operazione è sia associativa che commutativa (un gruppo abeliano).

Come funziona l'addizione?

Nota: l'aggiunta di punti sulle curve ellittiche non è intuitiva. Questo tipo di aggiunta è definita così com'è perché ha alcune belle proprietà. È strano, ma funziona.

Poiché le curve ellittiche formano un gruppo, esiste un'identità additiva che è l'equivalente di 0. Cioè, l'aggiunta 0a qualsiasi punto non cambierà il risultato. Questa identità additiva è il "punto" all'infinito. Tutte le linee sul piano includono questo punto all'infinito, quindi aggiungerlo non fa differenza.

Diciamo che ogni data linea interseca la curva in tre punti, che può essere 0, e che la somma di questi tre punti è 0. Tienilo a mente, dai un'occhiata a questa immagine.

casi speciali di aggiunta della curva ellittica
fonte

Ora, la domanda naturale è: che cos'è P+Q? Bene, se P+Q+R = 0, allora P+Q = -R(in alternativa scritto come R'). Dov'è -R? E 'dove R + (-R) = 0, che è sul lato opposto del x-asse dal Rmodo che la linea attraverso di loro è verticale, intersecando solo R, -Re 0. Puoi vederlo nella prima parte di questa immagine:

diagramma di varie aggiunte su curve ellittiche fonte

Un'altra cosa che puoi vedere in queste immagini è che la somma di un punto con se stessa significa che la linea è tangente alla curva.

Come trovare intersezioni di linee e curve ellittiche

Nel caso di due punti distinti

Generalmente c'è esattamente una linea attraverso due punti P=(x0,y0), Q=(x1,y1). Supponendo che non sia verticale e che i due punti siano distinti, possiamo scriverlo come y = m*x+q. Quando vogliamo trovare i punti di intersezione con la curva ellittica, possiamo semplicemente scrivere

0 = x^3+Ax+B-y^2 = x^3+Ax+B-(m*x+q)^2

che è un polinomio di terzo grado. Questi non sono generalmente così facili da risolvere, ma conosciamo già due zeri di questo polinomio: i due xcoordinate x0, x1dei due punti che vogliamo aggiungere!

In questo modo eliminiamo i fattori lineari (x-x0)e restiamo (x-x1)con un terzo fattore lineare la cui radice è la xcoordinata del punto R. ( -Ranche a causa della simmetria. Si noti che se R = (x2,y2)poi -R = (x2,-y2). -Viene dal gruppo; non è un meno vettoriale.)

Nel caso di aggiungere un punto Pa se stesso

In questo caso dobbiamo calcolare la tangente della curva in P=(x0,y0). Possiamo scrivere direttamente me qin termini di A,B,x0,y0:

     3*x0^2 + A
m = ------------
        2*y0

     -x0^3 + A*x0 + 2*B
q = --------------------
          2*y0

Otteniamo l'equazione y = m*x+qe possiamo procedere allo stesso modo del paragrafo precedente.

Un albero completo

Questo è un elenco completo di come gestire tutti questi casi:

Lasciate P,Qessere punti sulla curva ellittica (incluso il punto "infinito" 0)

  • Se P = 0o Q = 0, quindi P+Q = Qo P+Q = P, rispettivamente
  • Altrimenti P ≠ 0e Q ≠ 0, quindi lasciamo P = (x0,y0)e Q = (x1,y1):
    • Se P = -Q(questo significa x0 = x1e y0 = -y1) alloraP+Q = 0
    • Altro P ≠ -Q
      • Se x0 = x1allora abbiamo P=Qe calcoliamo la tangente (vedere la sezione sopra) per ottenere R. PoiP+Q = P+P = 2P = -R
      • Altrimenti: possiamo y = m*x+ycalcolare una linea del modulo attraverso quei due punti (vedere la sezione sopra) per calcolare R. PoiP+Q=-R

Campi finiti

Per questa sfida considereremo i campi di dimensioni solo pdove pè primo (e per alcuni dettagli p ≠ 2, p ≠ 3). Questo ha il vantaggio che puoi semplicemente calcolare mod p. L'aritmetica in altri campi è molto più complicata.

Questo in questo esempio è impostato p = 5e tutte le uguaglianze qui sono congruenze mod 5.

2+4 ≡ 6 ≡ 1
2-4 ≡ -2 ≡ 3
2*4 ≡ 8 ≡ 3
2/4 ≡ 2*4 ≡ 3 because 4*4 ≡ 16 ≡ 1, therefore 1/4 ≡ 4

Sfida

Dati i parametri A,Bdi una curva ellittica, una caratteristica del campo primo pe due punti P,Qsulla curva ellittica, restituiscono la loro somma.

  • Si può presumere che i parametri A,Bdescrivano effettivamente una curva ellittica, ciò significa che 4A^3+27B^2 ≠ 0.
  • Si può presumere che in P,Qrealtà siano punti sulla curva ellittica o sul punto 0.
  • Puoi presumere che p ≠ 2,3sia primo.

Casi test

Ho realizzato un'implementazione (non molto elegante) in MATLAB / Octave, che puoi usare per i tuoi casi di test: ideone.com Spero sia corretto. Almeno ha riprodotto alcuni calcoli che ho fatto a mano.

Nota i banali casi di test che funzionano per tutte le curve che consideriamo qui:

Aggiunta di zero: P+0 = P aggiunta dell'inverso:(x,y) + (x,-y) = 0


Per p = 7, A = 0, B = 5i due punti P = (3,2)e Q = (6,2)sono sulla curva ellittica. Quindi vale:

2*Q = Q+Q = P
2*P = P+P = (5,2)
3*P = P+P+P = (5,2)+P = (6,5)
4*P = P+P+P+P = (5,2)+(5,2) = (6,5)+(5,2) = Q

Tutti i punti della curva ellittica lo sono (3,2),(5,2),(6,2),(3,5),(5,5),(6,5),0


Per p = 13, A = 3, B = 8noi abbiamo

(1,8)+(9,7) = (2,10)
(2,3)+(12,11) = (9,7)
2*(9,6) = (9,7)
3*(9,6) = 0

Per p = 17, A = 2, B = 2e P=(5,1) noi otteniamo

2*P = (6,3)
3*P = (10,6)
4*P = (3,1)
5*P = (9,16)
6*P = (16,13)
7*P = (0,6)
8*P = (13,7)
9*P = (7,6)
10*P = (7,11)

Se sei davvero ambizioso, prendi

p = 1550031797834347859248576414813139942411
A = 1009296542191532464076260367525816293976
x0 = 1317953763239595888465524145589872695690
y0 = 434829348619031278460656303481105428081
x1 = 1247392211317907151303247721489640699240
y1 = 207534858442090452193999571026315995117

e prova a trovare un numero naturale ntale che n*(x0,y0) = (x1,y1). Ulteriori informazioni qui.

Appendice

Prima di tutto un grande GRAZIE a @ El'endiaStarman per aver esaminato e modificato la mia bozza!

Perché le curve ellittiche?

Beh, potrebbe apparire come una sorta di equazione arbitraria, ma non lo è, è abbastanza generale: generalmente consideriamo quelle "forme" geometriche sul piano proiettivo (che è da dove proviene l '"infinito". Lì consideriamo tutte omogenee polinomi di terzo grado (quelli di livello inferiore o superiore sarebbero troppo difficili o semplicemente banali da esaminare). Dopo aver applicato alcune restrizioni per ottenere le belle proprietà che desideriamo, e dopo aver deomogenizzato quei polinomi (proiettando su uno dei tre piani affini ) finiamo con equazioni comey^2+a*x*y+b*y = x^3+c*x^2+d*x+eQuesta è una curva ellittica nella lunga forma di Weierstrass. Queste sono sostanzialmente le stesse curve che abbiamo considerato, ma solo leggermente inclinate. Con una trasformazione lineare delle coordinate, puoi facilmente ricavarne una breve equazione. esempio , che conserva ancora tutte le proprietà interessanti.

Perché abbiamo escluso p=2,3?

Ciò ha a che fare con il fatto che per la forma breve di Weierstrass abbiamo bisogno della restrizione 4A^3+27B^2 ≠ 0per evitare singolarità (più su quello che segue). In un campo della caratteristica 2 che abbiamo 4 = 0e in un campo della caratteristica 3 che abbiamo 27 = 0, questo rende impossibile avere curve in forma di weierstrass breve per questi tipi di campi.

Cosa sono le singolarità?

Se l'equazione 4A^3+27B^2=0vale, abbiamo singolarità come le seguenti: Come vedi in quei punti non puoi trovare una derivata e quindi nessuna tangente, che "uccide" l'operazione. Potresti guardare le equazioni y^2 = x^3oy^2 = x^3-3*x+2

Perché si chiamano comunque curve ellittiche ?

Il motivo è che le equazioni di questa forma compaiono negli integrali ellittici, ad esempio quali sono ciò che si ottiene quando si desidera caclulare ad esempio la lunghezza di un'ellisse. Una breve presentazione sull'origine del nome.

Cosa hanno a che fare con la crittografia?

Ci sono modi per calcolare in modo nP = P+P+...+Pmolto efficiente. Questo può essere usato ad esempio nello scambio di chiavi Diffie Hellman . L'aritmetica modulare può essere sostituita dall'aggiunta di sottogruppi di torsione, questi sono solo i punti sulla curva che hanno un ordine finito. (Ciò significa che mP = 0per alcuni m, che in pratica è solo un calcolo mod m).

Risposte:


4

Pyth, 105 100 byte

A,@Q3eQ?qGZH?qHZG?&=YqhHhGqeG%_eHhQZ_m%dhQ,-*J?Y*+*3^hG2@Q1^*2eG-hQ2*-eGeH^-hGhH-hQ2-hGK--^J2hGhHeGK

L'input è previsto come (p, A, P, Q), dove Pe Qsono i due punti del modulo (x, y)o, se sono il 0punto speciale , proprio come 0. Puoi provarlo online qui . Gli ultimi due esempi mostrano come funziona lo speciale 0.

Per risparmiare qualche byte, utilizzo solo mod pla risposta finale. Ciò significa che fa cose come x0^palcune volte senza fare esponenziazione modulare, quindi potrebbe essere molto lento.

Funziona seguendo approssimativamente la stessa logica di questa funzione Python:

def add_ellip(p, A, P, Q): # points are in format (x, y)
    z = 0 # representing special 0 point

    if (P == z):
        return Q
    if (Q == z):
        return P

    if P[0] == Q[0]:
        if (P == (Q[0], -Q[1] % p)):
            return z
        else:
            m = ((3*pow(P[0], 2, p) + A)*pow(2*P[1], p-2, p)) % p
    else:
        m = (P[1] - Q[1])*pow(P[0] - Q[0], p-2, p) % p

    x = (pow(m, 2, p) - P[0] - Q[0]) % p
    y = (m*(P[0] - x) - P[1]) % p
    return (x, y)

Ciò dipende fortemente dal fatto che l'inverso moltiplicativo modulare di xè uguale a x^(p-2) mod pse pè primo. Così siamo in grado di calcolare mla pendenza della linea, trovando l'inverso moltiplicativo modulare del denominatore e moltiplicandolo per numeratore. Abbastanza utile. La funzione Python dovrebbe calcolare problemi più grandi un po 'più efficientemente a causa dell'uso di pow.

Ho anche usato le scorciatoie mostrate sulla pagina di Wikipedia su questo argomento . È abbastanza interessante che finisco per usarlo solo Auna volta, e Bper niente.

Anche solo per divertimento:

def pow2floor(x):
    p = 1
    x >>= 1
    while (x > 0):
        x >>= 1
        p <<= 1
    return p

def multi_nP(p, A, n, P):
    d = {}

    def rec_helper(n, P):
        if (n == 0):
            return (0, 0)
        elif (n == 1):
            return P
        elif (n in d):
            return d[n]
        else:
            p2f = pow2floor(n)
            remainder = n - p2f

            lower_half = rec_helper(p2f//2, P)
            d[p2f//2] = lower_half
            nP = add_ellip(p, A, lower_half, lower_half)

            if (remainder):
                nP = add_ellip(p, A, nP, rec_helper(remainder, P))

            d[n] = nP
            return nP

    return rec_helper(n, P)

La multi_nPfunzione calcola n*Pper un dato numero intero ne punto P. Usa una strategia ricorsiva suddividendo nin due parti p2fe remaindertale p2f + remainder = ne quello p2f = 2^k. Quindi chiamiamo di nuovo la funzione su quelle parti, aggiungendo il risultato con add_ellip. Ho anche usato un approccio di programmazione dinamica di base salvando valori già calcolati nel dict d.

La prossima funzione risolverebbe teoricamente la domanda bonus usando la stessa strategia:

def find_nPQ(p, A, P, Q): # P is input point, Q is what we're looking for
    d = {}
    found_Q = False

    def rec_helper(n, P):
        if (n == 0):
            return (0, 0)
        elif (n == 1):
            return P
        elif (n in d):
            return d[n]
        else:
            p2f = pow2floor(n)
            remainder = n - p2f

            lower_half = rec_helper(p2f//2, P)
            d[p2f//2] = lower_half

            nP = add_ellip(p, A, lower_half, lower_half)

            if (remainder):
                nP = add_ellip(p, A, nP, rec_helper(remainder, P))

            d[n] = nP
            return nP


    for x in range(p):
        xP = rec_helper(x, P)
        if (xP == Q):
            return x

Sfortunatamente, non corre da nessuna parte abbastanza velocemente per calcolarlo. Immagino che potrebbero esserci modi molto più efficienti per farlo, soprattutto se non dovremo scorrere ogni possibile valore per n.


Fantastico, onestamente non mi aspettavo più alcun ansewers =) Come gestisci il punto all'infinito? (Nota che y^2 = x^3 + xè una curva ellittica valida ed (0,0) ≠ 0è un punto sulla curva!)
flawr

Ottima domanda ... Immagino di non averlo gestito! : P Mi scuso, ricordo di aver visto la prima foto in cui mi B = 0chiedevo come 0avrebbe funzionato ... e poi me ne sono dimenticato. Penso di aver pensato Bche non avrei potuto essere 0 a un certo punto a tarda notte. Hai qualche suggerimento su come dovrebbe essere l'input per questo? Forse se B = 0, quindi definire 0 = (-1, -1)? Sono felice di modificare il mio codice per gestirlo, sto solo pensando che sarebbe bello se fosse standardizzato anche per altri invii ...
Rhyzomatic

Bene, ho lasciato quel pont aperto in modo tale che gli invii possano usare ciò che è conveniente per loro. Ma ovviamente si potrebbe dire che, ad esempio, tutti i punti finiti sulla curva hanno coordinate non negative e tutto il resto è considerato come il punto Infinity o qualcosa di simile. Oppure, se è più facile, puoi anche supporre che l'ingresso [0](solo una coordinata) sia il punto di infinito o qualcosa di simile!
flawr

Fammi sapere se questo non lo gestisce abbastanza bene. E grazie, questo in realtà mi ha salvato 5 byte!
Rhyzomatic

@flawr, saresti in grado di dirmi se sono sulla buona strada per un calcolo efficiente nP? Potresti indicarmi qualsiasi risorsa sull'argomento per far fluire i pensieri? Sto facendo fatica a trovare qualcosa su Google. Grazie!
Rhyzomatic

0

Python 3, 193 191 byte

Una soluzione basata sulla risposta Python di Rhyzomatic e sulla loro logica Python. In particolare. Mi è piaciuto come hanno trovato la terza radice di un polinomio cubico monico x^3 + bx^2 + cx + dquando hai due radici x_1e x_2notandolo b == x_1 + x_2 + x_3e sottraendo di conseguenza. Ho intenzione di aggiungere una spiegazione, giocare a golf e forse trasferirla in Ruby, se Ruby risulta essere più breve.

def e(p,A,B,P,Q):
 if P==0:return Q
 if Q==0:return P
 f,g=P;j,k=Q
 if f==j:
  if g==-k%p:return 0
  m=(3*f*f+A)*pow(2*j,p-2)
 else:m=(g-k)*pow(f-j,p-2)
 x=m*m-f-j;y=m*(f-x)-g;return(x%p,y%p)

Ungolfing:

def elliptic_curve_addition(p, A, B, P, Q):
    if P == 0:
        return Q
    if Q == 0:
        return P
    f,q = P
    j,k = Q
    if f==j:
        if g == (-k) % p:
            return 0
        m = (3 * f**2 + A) * pow(2*j, p-2)
    else:
        m = (g-k) * pow(f-j, p-2)
    x = m**2 - f - j
    y = m * (f-x) - g
    return (x%p, y%p)

Sono sorpreso che Python sia meno del doppio della risposta di Pyth!
Flawr,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.