¿Cómo funciona el algoritmo de verificación ECDSA durante la transacción?

Entiendo el concepto básico de ECDSA, donde si Bob quiere firmar un mensaje, genera un número aleatorio n, multiplícalo con secp256k1, r = el valor de x, s = (H(x) d r)*n^-1 mod q . Además, la verificación se realiza mediante P=u1(G)+u2(P), donde G es el punto de generación y P la clave pública de Bob.

Revisé la página de transacciones de wiki. Tengo entendido que ECDSA tiene lugar en algún lugar de la sección scriptSig/scriptPubKey.

Si Bob quiere gastar la salida de la transacción no gastada, Bob deberá crear un script de firma que haga referencia a transacciones anteriores, también proporcionar su clave pública completa, que luego se codificará para que coincida con las transacciones anteriores como receptor, Bob también deberá generar una firma para demostrar que la clave pública se origina a partir de su clave pública.

Me deja con muchas preguntas, como los pasos de verificación de transacciones: OP_CHECKSIG , donde supongo que la mayoría de los pasos intentan hacer coincidir el hash y, finalmente, en el paso 10 realiza la verificación ECDSA.

¿Todavía sigue el concepto básico de calcular u1, u2 y utilizar la clave pública completa proporcionada por Bob al intentar igualar el valor r al final? ¿O está haciendo algo totalmente diferente? Puedo entender por qué se requiere pubKeyStr y sigStr para la verificación de ECDSA, pero también incluye sha256^2, que supongo que es doble hash, con (verifThisStr). No entiendo de dónde vino eso en absoluto.

¿Qué se sustituiría en el mensaje hash H(x) al calcular s? ¿Y Bob elige al azar una variable solo para generar la firma cuando se le pide que reclame la transacción no gastada?

Respuestas (2)

Tomemos "transacción de pizza" https://blockchain.info/tx/cca7507897abc89628f450e8b1e0c6fca4ec3f7b34cccf55f3f531c659ff4d79

01000000018dd4f5fbd5e980fc02f35c6ce145935b11e284605bf599a13c6d415db55d07a1000000008b4830450221009908144ca6539e09512b9295c8a27050d478fbb96f8addbc3d075544dc41328702201aa528be2b907d316d2da068dd9eb1e23243d97e444d59290d2fddf25269ee0e0141042e930f39ba62c6534ee98ed20ca98959d34aa9e057cda01cfd422c6bab3667b76426529382c23f42b9b08d7832d4fee1d6b437a8526e59667ce9c4e9dcebcabbffffffff0200719a81860000001976a914df1bd49a6c9e34dfa8631f2c54cf39986027501b88ac009f0a5362000000434104cd5e9726e6afeae357b1806be25a4c3d3811775835d235417ea746b7db9eeab33cf01674b944c64561ce3388fa1abd0fa88b06c44ce81e2234aa70fe578d455dac00000000


// decoded by https://blockchain.info/decode-tx
{
   "lock_time":0,
   "size":300,
   "inputs":[
      {
         "prev_out":{
            "index":0,
            "hash":"a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d"
         },
         "script":"4830450221009908144ca6539e09512b9295c8a27050d478fbb96f8addbc3d075544dc41328702201aa528be2b907d316d2da068dd9eb1e23243d97e444d59290d2fddf25269ee0e0141042e930f39ba62c6534ee98ed20ca98959d34aa9e057cda01cfd422c6bab3667b76426529382c23f42b9b08d7832d4fee1d6b437a8526e59667ce9c4e9dcebcabb"
      }
   ],
   "version":1,
   "vin_sz":1,
   "hash":"cca7507897abc89628f450e8b1e0c6fca4ec3f7b34cccf55f3f531c659ff4d79",
   "vout_sz":2,
   "out":[
      {
         "script_string":"OP_DUP OP_HASH160 df1bd49a6c9e34dfa8631f2c54cf39986027501b OP_EQUALVERIFY OP_CHECKSIG",
         "address":"1MLh2UVHgonJY4ZtsakoXtkcXDJ2EPU6RY",
         "value":577700000000,
         "script":"76a914df1bd49a6c9e34dfa8631f2c54cf39986027501b88ac"
      },
      {
         "script_string":"04cd5e9726e6afeae357b1806be25a4c3d3811775835d235417ea746b7db9eeab33cf01674b944c64561ce3388fa1abd0fa88b06c44ce81e2234aa70fe578d455d OP_CHECKSIG",
         "address":"13TETb2WMr58mexBaNq1jmXV1J7Abk2tE2",
         "value":422300000000,
         "script":"4104cd5e9726e6afeae357b1806be25a4c3d3811775835d235417ea746b7db9eeab33cf01674b944c64561ce3388fa1abd0fa88b06c44ce81e2234aa70fe578d455dac"
      }
   ]
}

Mire y decodifique el script input=0:

48  // push next 0x48 bytes
30450221009908144ca6539e09512b9295c8a27050d478fbb96f8addbc3d075544dc41328702201aa528be2b907d316d2da068dd9eb1e23243d97e444d59290d2fddf25269ee0e01
41  // push next 0x41 bytes
042e930f39ba62c6534ee98ed20ca98959d34aa9e057cda01cfd422c6bab3667b76426529382c23f42b9b08d7832d4fee1d6b437a8526e59667ce9c4e9dcebcabb

El primer impulso es una firma concatenada con hashtype=01 (SIGHASH_ALL)

La segunda pulsación es una clave pública para la dirección 17SkEw2md5avVNyYgj6RiXuQKNwkXaxFyQ

¿Cómo verificamos si esta transacción es válida? ¿Está correctamente firmado?

1) Eliminar el script de entrada de la transacción. Deberíamos eliminar bytes (no te olvides del script len)

8b4830450221009908144ca6539e09512b9295c8a27050d478fbb96f8addbc3d
075544dc41328702201aa528be2b907d316d2da068dd9eb1e23243d97e444d59
290d2fddf25269ee0e0141042e930f39ba62c6534ee98ed20ca98959d34aa9e0
57cda01cfd422c6bab3667b76426529382c23f42b9b08d7832d4fee1d6b437a8
526e59667ce9c4e9dcebcabb

2) Reemplácelo con el script de financiamiento a 17SkEw2md5avVNyYgj6RiXuQKNwkXaxFyQ

OP_DUP OP_HASH160 46af3fb481837fadbb421727f9959c2d32a36829 OP_EQUALVERIFY OP_CHECKSIG
1976a91446af3fb481837fadbb421727f9959c2d32a3682988ac

(¡No te olvides de la longitud del guión de nuevo!)

3) Agregue SIGHASH_ALL como valor low-endian de 32 bits. el resultado será

01000000018dd4f5fbd5e980fc02f35c6ce145935b11e284605bf599a13c6d41
5db55d07a1000000001976a91446af3fb481837fadbb421727f9959c2d32a368
2988acffffffff0200719a81860000001976a914df1bd49a6c9e34dfa8631f2c
54cf39986027501b88ac009f0a5362000000434104cd5e9726e6afeae357b180
6be25a4c3d3811775835d235417ea746b7db9eeab33cf01674b944c64561ce33
88fa1abd0fa88b06c44ce81e2234aa70fe578d455dac0000000001000000

4) Hash dos veces por SHA256. El resumen será 692678553d1b85ccf87d4d4443095f276cdf600f2bb7dd44f6effbd7458fd4c2

5) Bien, ahora tenemos tres elementos:

  • a) clave pública 042e930f39ba62c6[...cortar...]6e59667ce9c4e9dcebcabb
  • b) firma 304502210099081[...cortar...]d59290d2fddf25269ee0e
  • c) resumen 692678553d1b85ccf87d4d4443095f276cdf600f2bb7dd44f6effbd7458fd4c2

Pase estos valores al método de verificación ECDSA estándar y recibirá el resultado: verdadero o falso. Aquí hay una pequeña parte de mi cheque rápido y sucio con valores codificados:

const QByteArray xx ( QByteArray::fromHex ( "01000000018dd4f5fbd5e980fc02f35c6ce145935b11e284605bf599a13c6d41"
                                            "5db55d07a1000000001976a91446af3fb481837fadbb421727f9959c2d32a368"
                                            "2988acffffffff0200719a81860000001976a914df1bd49a6c9e34dfa8631f2c"
                                            "54cf39986027501b88ac009f0a5362000000434104cd5e9726e6afeae357b180"
                                            "6be25a4c3d3811775835d235417ea746b7db9eeab33cf01674b944c64561ce33"
                                            "88fa1abd0fa88b06c44ce81e2234aa70fe578d455dac0000000001000000" ) );
const MyKey32 digest ( xx.constData ( ), xx.size ( ) ); // construct object of sha256 (sha256 ( xx ) )
_trace ( digest.toString ( ) );                         // print result
const QByteArray pubkey ( QByteArray::fromHex ( "042e930f39ba62c6534ee98ed20ca98959d34aa9e057cda01cfd422c6bab3667b76426529382c23f42b9b08d7832d4fee1d6b437a8526e59667ce9c4e9dcebcabb" ) );
const QByteArray signature ( QByteArray::fromHex ( "30450221009908144ca6539e09512b9295c8a27050d478fbb96f8addbc3d075544dc41328702201aa528be2b907d316d2da068dd9eb1e23243d97e444d59290d2fddf25269ee0e" ) );
_trace ( QString ( "verify=%1" ).arg ( digest.verify ( pubkey, signature ) ) );

la salida es

"692678553d1b85ccf87d4d4443095f276cdf600f2bb7dd44f6effbd7458fd4c2"
"verify=1"
El breve recorrido es muy útil, hay algunas preguntas menores más al respecto. Todavía podría estar confundido, pero trataré de darle sentido. Después de la eliminación del script de entrada (Paso 1), Reemplácelo con el script de financiación (Paso 2) Agregue SIGHASH_ALL (Paso 3), que es la nueva transacción. De acuerdo con la otra respuesta, para el signiture s= (H(x)*d*r)n^-1 mod q, donde H(x) es todo en tx sin procesar, supongo que es exactamente como el resumen ( ?) Entonces, la firma (304502... ee0e) se genera mediante el resumen (692678... d4c2), la clave temporal (aleatoria) y la clave privada.
Si es así, veo que su varificación ECDSA solo admite 2 entradas, que es pubkey y signiture. Out puso fue el resumen. Dado que el concepto básico utiliza u1 y u2. Coincidía con u1(G)+u2(B), que B= clave pública, G= punto de generación, u1 = H(x)*s^-1, u2 = r d s-1*n, donde ocurre la cancelación deja la coordenada x de n*G = r = varify. Pero dado que nuestro key.verify no incluye el resumen, ¿está usando un método diferente? al regenerar el resumen con la clave pública y la firma?
Tengo mi propia lib. Class MyKey32 es una clase para realizar algunas acciones. Entonces, la última línea se puede escribir como "digest.verify (publickey, signature)" donde digest - es un objeto de 32 bytes
>Entonces, la firma (304502...ee0e) se genera mediante el resumen (692678...d4c2), la clave temporal (aleatoria) y la clave privada.<<< ¡Sí! El propietario de la clave privada creó en 2010 la misma matriz de bytes, calculó el resumen (igual que el mío, o para ser correcto: el mío es el mismo que el suyo) y creó la firma con la clave privada y el resumen. Para crear la firma, necesita privkey y digest. Para verificar la firma necesitamos pubkey y digest.

La pregunta es, ¿todavía sigue el concepto básico de calcular u1, u2 y utilizar la clave pública completa proporcionada por Bob tratando de hacer coincidir el valor r al final?

Sí, esa es la idea básica, ¿por qué pensarías que es diferente? Hay una forma un poco complicada de calcular el hash que debe firmarse, pero una vez que se calcula el hash, solo ECDSA trata el hash como un número entero de 256 bits.

también toma sha256^2, que supongo que es doble hashing, con (verifThisStr) que no entiendo de dónde vino eso en absoluto

Sí, sha256^2(x) simplemente se define como sha256(sha256(x)). No estoy seguro de a qué está tratando de hacer referencia "verifThisStr", pero si es el valor que debe verificarse con ECDSA, entonces solo está hablando del resultado del doble hash, tratado como un número entero.

Además, si todavía sigue el concepto básico, ¿qué se sustituiría en el mensaje hash H(x) al calcular s?

Las partes de la transacción que deben tener doble hash y luego firmarse dependen del SigHashType ( https://bitcoin.org/en/developer-guide#signature-hash-types ). Para SIGHASH_ALL estándar, incluye todo en el tx sin procesar excepto scriptSigs, ya que no puede firmar su propia firma.

¿Y Bob elige al azar una variable solo para generar la firma cuando se le pide que reclame la transacción no gastada?

Sí, se elige un número aleatorio al realizar una firma, por lo que las firmas ECDSA no son deterministas.

Cabe señalar que, en primer lugar, el número aleatorio kutilizado para generar R debe ser efímero, nunca repetido, de lo contrario corre el riesgo de exponer su clave privada maestra. En segundo lugar, es muy recomendable no generar kcon un generador de números aleatorios (RNG). La mejor práctica de la industria es utilizar un proceso aleatorio determinista sembrado con los propios datos de la transacción. Entonces no correrá el riesgo de reutilizar kdebido a un RNG erróneamente sembrado. Ver RFC 6979.