Aquí, sipa propone una estandarización de las firmas de schnorr: https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki
Por ejemplo, sign()
devuelve Sig{R,s}
que se pueden codificar con 64 bytes (Rx || s) donde Ry se puede desambiguar a través de un residuo cuadrático.
Después de implementar sign()
y verify()
de acuerdo con ese documento, intenté implementar firmas ciegas.
Sin embargo, la verificación de mi firma no cegada falla el 50 % de las veces debido a la línea return false if jacobi(R.y) !== 1
en verify()
, adjunta a continuación.
Aquí está mi pseudocódigo 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
}
Aquí está la prueba que fallará el 50% de las veces:
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)
sign()
Los usos de la especificación nonce = jacobi(y) === 1 ? nonce : curve.N - nonce
para forzar un residuo cuadrático y
, y asumiría que mi prueba falla porque el 50% del tiempo genera un no residuo y
. Sin embargo, no pude averiguar dónde y cómo aplicar esto en mi código relacionado con ciegos. Ninguno de mis esfuerzos tuvo un efecto en mi tasa de aprobación del 50%.
Andrew Poelstra sugiere que:
R' = R + alpha*G + beta*G
debe ponerse en un bucle .. siempre que Rx no sea un residuo cuadrático. Puede cambiar la versión beta y esto debería darle compatibilidad con el esquema de sipa sin seguridad, pérdida de privacidad o pérdida de capacidad de vinculación ciega.
G.Maxwell