Sind die standardisierten Schnorr-Signaturen von sipa nicht mit Blindsignaturen kompatibel?

Hier schlägt sipa eine Standardisierung von schnorr-Signaturen vor: https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki

Zum Beispiel sign()Rücksendungen Sig{R,s}, die mit 64 Bytes (Rx || s) codiert werden können, wobei Ry über quadratische Reste disambiguiert werden kann.

Nach der Implementierung sign()und verify()gemäß diesem Dokument habe ich versucht, Blindsignaturen zu implementieren.

Die Überprüfung meiner unverblindeten Signatur schlägt jedoch in 50 % der Fälle fehl, da die Zeile return false if jacobi(R.y) !== 1in verify()unten angefügt ist.

Hier ist mein Pseudocodey-Impl:

type Signature = { Rx: int, s: int }
type Unblinder = { alpha: int, Rx: int }
type BlindedMessage = { challenge: int }
type BlindedSignature = { s: int }

fn blindMessage(nonce: Point, signer: Point, message: bytes): (Unblinder, BlindedMessage) {
    R = nonce
    P = signer
    alpha = rand()
    beta = rand()
    R' = R + alpha*G + beta*P
    // challenge
    c' = int(hash(R'.x || P || message)) % curve.n
    // blinded challenge
    c = c' + beta
    return (Unblinder(alpha, R'.x), BlindedMessage(c))
}

fn blindSign(signer: privkey, nonce: privkey, blindedMessage: BlindedMessage): BlindedSignature {
    c = blindedMessage.challenge
    x = signer
    k = nonce

    s = k + c*x
    return BlindedSignature(s)
}

fn unblind(unblinder: Unblinder, blindedSig: BlindedSignature): Signature {
    Rx = unblinder.Rx
    s = blindedSig.s + unblinder.alpha
    return Signature(Rx, s)
} 

// implemented according to sipa's spec
fn verify(pubkey: Point, message: bytes, sig: Signature): bool {
    pk = pubkey
    m = message
    P = pubkey

    (r, s) = sig
    e = int(hash(r || P || m)) % curve.n
    R = s*G - e*P

    return false if isInfinitePoint(R)
    return false if jacobi(R.y) !== 1 // <-- Fails 50% here
    return false if R.x !== r
    return true
}

Hier ist der Test, der in 50 % der Fälle fehlschlägt:

noncePriv = rand()
signerPriv = rand()
noncePub = noncePriv * curve.G
signerPub = signerPriv * curve.G
message = hash('my message')

// blind
(unblinder, blindedMessage) = blindMessage(noncePub, signerPub, message)

// sign
blindedSig = blindSign(signerPriv, noncePriv, blindedMessage)

// unblind 
sig = unblind(unblinder, blindedSig)

// verify
verified = verify(signerPub, message, sig)

assert(verified)

Die Spezifikation sign()wird verwendet nonce = jacobi(y) === 1 ? nonce : curve.N - nonce, um einen quadratischen Rest zu erzwingen y, und ich würde davon ausgehen, dass mein Test fehlschlägt, weil er in 50 % der Fälle einen Nicht-Rest erzeugt y. Ich konnte jedoch nicht herausfinden, wo und wie ich dies in meinem Blind-bezogenen Code anwenden sollte. Keine meiner Bemühungen hatte eine Auswirkung auf meine 50 % Bestehensquote.

Nur für zufällige Personen, die auf diese Frage stoßen: Eine so konstruierte Blindsignatur ist unsicher , wenn der Blindunterzeichner bereit ist, mehrere Blindsignaturen gleichzeitig zu leisten. Blindes Signieren muss gegen einen weiteren Signaturangriff basierend auf dem Wagner-Algorithmus gesichert werden.

Antworten (1)

Andrew Poelstra schlägt Folgendes vor:

R' = R + alpha*G + beta*G

sollte in eine Schleife gelegt werden .. wenn Rx kein quadratischer Rest ist. Sie können die Beta ändern, und dies sollte Ihnen Kompatibilität mit dem Schema von SIPA geben, ohne Sicherheit, Datenschutzverlust oder Blind-Sig-Verknüpfbarkeitsverlust