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) !== 1
in 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.
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
G. Maxwell