본문 바로가기
블록체인 backEnd/backend

트랜잭션 서명 이해하기

by gun_poo 2023. 1. 15.

 앞선 글에서 트랜잭션을 한번 까보았다. 

까다보니 서명에 관해 정리를 해야할 필요성을 느껴 정리를 해본다.

 

타원 곡선

블록체인에서 타원곡선의 역할은 디지털 서명이다.

디지털 서명이란 a 라는 사람이 개인 키를 이용해 내가 보내려는 데이터에 서명을 하고 받은 사람은 a의 공개 키를 이용해 그 서명이 진짜 a의 서명인지 확인하는 기술을 의미한다.

 

디지털 서명은 트랜잭션 때문에 필요한데 이는 이전 글에서 설명했으니 생략한다.

 

트랜잭션에 쓰일 서명은 어떤 형태를 띄어야하는가?

  1. 송신자 만이 가지고 있는 고유값으로 서명을 만들어 낼 수 있어야 한다.
  2. 수신자가 이 서명이 송신자의 것이 맞다는 것을 확인 할 수 있어야 한다.

두 가지 조건을 만족하며 이를 만들 수 있는 기술이 타원 곡선이다.

 

타원곡선 방정식 : fucking math 이란

뭔 개소린가 싶어서 공부를 좀 하고 온다.... Funxxing Math Fxxxxxxing 

  • 타원 곡선 그래프 위의 점들의 집합을 G 라고 할때 아래의 조건을 만족한다
    • G가 속한 임의의 점 P, Q에 대해 P + Q 또한 G에 속한다.
    • (P + Q) + R = P + (Q + R)이다
    • 이상점 0이 있으며 P + 0 = 0 + P = P이다.
    • Q가 P의 역원이면 P + Q = 0
    • P + Q = Q + P이다

그래프를 보게 되면 y^2으로 인해 그래프가 x축에 대한 대칭으로 보인다.

a의 계수 값에 따라 곡선이 하나로 이어지지 않고 분리되는 모습을 보이기도 한다.

 

타원곡선 방정식을 3차 방정식으로 유도

3차 방정식

3차 방정식에서 그래프를 완만하게 다듬는다.

y>0 큰 부분을 자르고 x축 대칭으로 변환한다

타원곡선 그래프

타원 곡선 방정식을 증명하기 위해서는 상당히 전문적인 지식이 필요하기 때문에 생략한다. 대략의 개요정도만 알고가자

 

점과 점의 덧셈

점들을 지나는 직선을 긋고 c를 x축 대칭을 시키게 되면 그 점은 a+b의 점이다.

점 덧셈은 비선형이다.

 

점 덧셈은 일반 덧셈연산과 유사한 성질을 가진다.

  • 항등원이 존재
  • 교환법칙이 성립
  • 결합법칙이 성립
  • 역원이 존재

항등원이란 대수의 0과 같은 의미의 점이 존재한다라는 뜻이다.

  • 곡선 위의 I 라는 점이 존재하고 A라는 점과 더한 결과 값은 A가 된다

이때의 점을 무한원점이라 한다.

  • 덧셈의 역원과 관련이 있다.
  • 어떤 A라는점에 대해 -A 점이 존재하고 그 합은 항등원이 된다는 것
  • 다시 말해 A + ( -A) = I로 정의 할 수 있다.
  • 그래프로 볼때 이점들은 x축에 수직인 직선과 곡선의 교점이다.
  • x축에 수직인 직선과 타원곡선이 만나는 세번째 점은 영원히 만나지 않음으로 무한대에 있다.
  • 이러한 성질 때문에 무한원점이라 한다.

교환법칙, 결합법칙

a + b = b + c : a 와 b를 지나는 직선은 순서를 바꿔도 동일한 위치에서 곡선과 만난다.

(a+b) + c = a+ (b+c) : 3개 이상의 덧셈에서 어느 두항을 먼저 더해도 결과가 동일하다

 

점에 상수 곱하기

 

2 * p = 2P =? p + p 이기 때문에 교점 R x축 대칭을 시키게 되면 2P 이다.

 

응 어려워~ 

더 깊이 공부하기 위한 분들을 위해 다른 링크를 남겨드린다. 

나도 시간나면 볼 예정.... 시간나면...https://m.blog.naver.com/aepkoreanet/221178375642

 

비트코인에서 사용하는 타원곡선암호기술(ECC)

지난, “비트코인 시스템에서 사용하는 암호 기술“ 기사에서, 비트코인은 디지털 서명 암호기술로, 공개키...

blog.naver.com

 

어지럽다 머리아프다 눈아프다 힘없다 의욕이 안생긴다 하여 본론으로 다시돌아가겠다.

 

이더리움에서 사용되는 타원곡선

 

블록체인을 이용하는 모든 사용자들이 공통적으로 타원 곡선 위의 어느 한점을 알고 있어야한다.

이 점을 타원곡선의 기준점 : base point G 라고 부른다.

귀엽다

타원 곡선의 기준점 G 를 이용하려면 모든 사람이 알고 있어야한다. 

타원곡선의 방정식은

이러 하다라고 아까 이야기했으며 가장 중요한 

비트코인과 이더리움에 사용되는 타원 곡선은

SECP256K1 이다.

a = 0, b = 7이며 기준점 G“02 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798” 인 타원 곡선을 의미한다.

 

서명을 보내는 쪽의 시나리오를 보자

  • 트랜잭션 생성
  • 프라이빗키를 타원곡선이 지정해주는 범위 내에서 정한다
    • 프라이빗 키 라는 것은 1 ~ n-1 까지의 정수 중 하나이다. 개인키는 단순히 숫자일뿐!
    • 여기서 n은 SECP256K1 타원곡선 하에서 1.157920892373162e+77 라는 값으로 정의되어 있다.
    • 16진수로 표현하면 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
    • 하여 1 ~ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140 까지의 숫자 범위 중 하나를 골라 쓰는 것이다.
    • 이 범위를 벗어나면 올바르지 않은 프라이빗 키라는 에러를 반환한다.
  • 프라이빗 키는 사실 두개가 필요하다
    • 서명 r, s 필요
      • 서명 r 찾기
        • 1 ~ n-1 범위의 정수를 랜덤하게 하나 고른다.
        • 이 값은 트랜잭션을 보낼때 마다 랜덤하게 컴퓨터가 뽑아서 넣어준다.
        • 이 값을 k라고 하는데 k를 구해야 r을 구할수 있다.
        • k * G(기준점 G) : 타원곡선 "점에서 상수를 곱하기"
        • 점에서 상수를 곱해도 타원 곡선위의 점이 되기에 이 점의 x 좌표가 바로 서명 r이다

 

서명 s 찾기

k : 위에서 랜덤하게 선택했던 값

z: 만들어낸 트랜잭션 정보가 담긴 값

r: 위에서 찾아낸 서명 r

privateKey : 프라이빗 키 값

mod n : n으로 나눈 나머지

서명 s 를 만드는데 프라이빗 키를 사용한다가 요지이다.

 

이렇게 만들어진 서명 r, s를 보낸다!

 

이제 받는 사람의 절차를 확인해보자

이러한 공식 결과가 서명 r과 같은지 비교한다. 

이 식을 대충 풀이해보자면 결국 결과 값은 '타원 곡선 위의 어느 한점' 이 나오게 되는데 그 점의 x 좌표가 내가 받은 서명 r과 같은지 비교하면 된다.

 

public key는 어디서 났는가? 원래 공개 키라는 것은 복구 키를 이용하면 구할수 있다.

이더리움에서는 '27 or 28'이라는 값으로 약속하자 라고 했기 때문에 서명만 가지고도 복구키를 이용해 보낸 사람의 공개키를 추출해낼수 있다. 이 복구 키를 v라고 한다.

정리하자면 "받은 서명과 트랜잭션을 가지고 '공개 키'를 이용하여 이 서명을 검증했다' 라는 것이다.

 

정리

msg.sender 입장

  • 트랜잭션 생성
  • 개인키 정하기 : 타원곡선 SECP256K1 에 의해 정해진 범위 내에서 고름
  • 서명 r 만들기 : 랜던 값 k 가 타원곡선에 의해 골라짐
    • 1~ n-1 범위의 임의 k 값을 구함
    • k와 타원곡선 기준점 G를 곱한다
    • k * G 점의 x좌표 구한다
    • 그 좌표를 서명 r로 한다
    • k^-1*(z + r * privateKey) mod n을 연산한다
  • 서명 s 만들기 : 서명 s는 서명 r을 이용해 만듬
  • 트랜잭션 전송
    • sendTransaction 함수 호출 
    • 개인키를 사용해 서명한다
    • sendSignedTransaction 을 이용해 이더리움 노드로 전송
EC.prototype.sign = function sign(msg, key, enc, options) {
  if (typeof enc === 'object') {
    options = enc;
    enc = null;
  }
  if (!options)
    options = {};

  key = this.keyFromPrivate(key, enc);
  msg = this._truncateToN(new BN(msg, 16));

  // Zero-extend key to provide enough entropy
  var bytes = this.n.byteLength();
  var bkey = key.getPrivate().toArray('be', bytes);

  // Zero-extend nonce to have the same byte size as N
  var nonce = msg.toArray('be', bytes);

  // Instantiate Hmac_DRBG
  var drbg = new HmacDRBG({
    hash: this.hash,
    entropy: bkey,
    nonce: nonce,
    pers: options.pers,
    persEnc: options.persEnc || 'utf8',
  });

  // Number of bytes to generate
  var ns1 = this.n.sub(new BN(1));

  for (var iter = 0; ; iter++) {
  
  // 1 ~ n-1 범위의 k값을 랜덤으로 찾는다 "drbg.generate"가 랜덤함수
  ////////////////////////////////////////////////////////
    var k = options.k ?
      options.k(iter) :
      new BN(drbg.generate(this.n.byteLength()));
 ////////////////////////////////////////////////////////
    k = this._truncateToN(k, true);
    if (k.cmpn(1) <= 0 || k.cmp(ns1) >= 0)
      continue;
	
 // k와 기준점 G 를 곱한다
 ////////////////////////////
    var kp = this.g.mul(k);
 ////////////////////////////
    if (kp.isInfinity())
      continue;
 // k * G 점의 x축 좌표를 구한다
    var kpX = kp.getX();
 // k * G 점의 x 좌표를 서명 r로 한다
    var r = kpX.umod(this.n);
 ////////////////////////////
 
    if (r.cmpn(0) === 0)
      continue;
 // k^-1 *(z + r + privateKey) mod n 식
    var s = k.invm(this.n).mul(r.mul(key.getPrivate()).iadd(msg));
 //////////////////////////////////////////////////////////
    s = s.umod(this.n);
    if (s.cmpn(0) === 0)
      continue;

    var recoveryParam = (kp.getY().isOdd() ? 1 : 0) |
                        (kpX.cmp(r) !== 0 ? 2 : 0);

    // Use complement of `s`, if it is > `n / 2`
    if (options.canonical && s.cmp(this.nh) > 0) {
      s = this.n.sub(s);
      recoveryParam ^= 1;
    }

    return new Signature({ r: r, s: s, recoveryParam: recoveryParam });
  }
};

to의 입장

 

  • “ U1 * G + U2 * 공개키 “ 의 x 좌표를 구해서 서명 r의 값과 같은지 비교한다

개인키는 단순히 숫자인 반면 공개 키는 타원곡선 위의 한 점이다.

U1 * G + U2 * 공개키 는 사실 타원 곡선의 점의 연산이라고 볼수 있겠다.

  • 트랜잭션을 받는다
    • 전송된 트랜잭션 데이터와 함께 go-eth의 internal/ethapi/api.go의 sendRawTransaction 함수가 호출
    • 이 함수는 submitTransaction함수를 호출한다.
    • submitTransaction함수가 호출 되면서 eth/api_backend.go의 SendTx 함수를 호출한다.
    • SendTx가 호출되면서 core/tx_pool.go의 AddLocal 함수 호출한다.
    • AddLocal가 호출 되면서 poll.add 함수를 호출한다.
    • poll.add 함수가 호출되면서 pool.validateTx 함수를 호출한다.
    • validateTx가 호출 되면서 crypto/types/transaction_signing.go의 Sender함수를 호출한다.
    • Sender함수가 호출 되면서 같은 파일의 EIP155Signer Sender를 호출한다.
    • EIP155Signer Sender가 호출 되면서 같은 폴더의 recoverPlain 함수를 호출한다.
    • recoverPlain함수가 호출되면서 crypto/signature_cgo.go의 Ecrecover함수를 호출한다.
    • Ecrecover 함수를 호출하면서 crypto/secp256k1/secp256.go의 RecoverPubKey 함수를 호출한다.
    • RecoverPubKey함수를 호출하면서 C.secp256k1_ext_ecdsa_recover 함수를 호출한다.
    • C.secp256k1_ext_ecdsa_recover(context, (*C.uchar)(unsafe.Pointer(&pubkey[0])), sigdata, msgdata) ....
    • 공개키 : &pubkey[0]
    • 서명 r,s : sigdata
    • 트랜잭션 : msgdata
    • 도출된 세개의 값으로 U1*G + U2* Public Key 식을 계산하여 진위 여부 판별을 할 수 있다.
    • https://github.com/ethereum/go-ethereum 참조

 

 

트랜잭션 서명에 대해서 알아보았다. 생각보다 아주 많이 복잡하고 어렵다. 고더리움을 언제 부셔보나....

 

 

 

 

 

참조 : https://nujabes403.medium.com/%ED%83%80%EC%9B%90%EA%B3%A1%EC%84%A0%EA%B3%BC-%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8-733b57c2ad38

 

 

'블록체인 backEnd > backend' 카테고리의 다른 글

transaction 이해하기  (0) 2023.01.15

댓글