PGR21.com
- 자유 주제로 사용할 수 있는 게시판입니다.
- 토론 게시판의 용도를 겸합니다.
Date 2018/01/22 01:04:27
Name SC2
Subject 흔한 비트코인 설명글 1 - 맛보기

흔한 비트코인 설명글입니다.

약 5-10개 파트로 나눠서, 이따금 써서 올려야겠다는 것 외에는 아무런 계획도 없이 마구잡이식으로 작성한 글이므로, 두서가 없는 점 양해바랍니다. 오늘은 비트코인 관련 이야기 중 암호와 관련된 내용을 생각나는대로 아무거나 적어볼 생각입니다. 사실 트랜잭션 생성과 마이닝에 대해서 먼저 쓰는 것이 전체적인 구조 이해에 더 도움이 될 것 같지만, 그냥 crypto니까 맛보기는 암호로 시작하겠습니다.

들어가기에 앞서 몇 가지 주의사항입니다.

- 주의 1 : 이 글은 9세의 아동(보다 정확하게는 9세의 본인)을 가상의 타겟 독자로 삼고, 타겟 독자를 완전히 이해시키는 것(최소한 각 계산과정을 수행할 수 있게 만드는 것)을 일차적 목표로 하여 작성되었습니다. 따라서 애초에 설명 자체가 불필요한 것을 장황하게 설명하거나, 간단한 수식으로 표현할 수 있는 것을 지나치게 많은 예시를 들어가며 설명하는 경향이 매우 강하며, 통용되는 용어 대신에 dumb-down시킨 창작용어를 쓰는 경우도 많으므로, 간단한 수식과 코드의 이해가 가능하신 분이 이 글을 읽는 것은 시간 낭비입니다.

- 주의 2 : 퇴고 없이 그대로 포스팅하므로, 수많은 문법적 오류, 내용상 오류, 계산 오류 등이 존재할 수 있습니다.


I. 대칭키 방식의 동작 원리

다짜고짜 비트코인에 들어가는 암호화 방식을 설명하기 보다는, 간단한 배경지식에 대해 먼저 이야기한 뒤 비트코인 이야기로 들어가도록 하겠습니다. 원래는 Casesar, Enigma를 거쳐서 들어가려고 했으나, 쓰다보니 귀찮아져서 Enigma는 생략합니다. 대칭키 방식은 암호화에 사용되는 키와 복호화에 사용되는 키가 동일한 것을 의미합니다. 구체적인 것은 예시를 통해 설명하도록 하겠습니다.

1. 치환암호

  A. x글자 밀기형(Caesar cipher)

생각할 수 있는 가장 간단한 암호화 방식 중 하나입니다. 이름에 Caesar가 붙어있는 것은 그 분이 쓰셔서 그렇습니다. 이것은 알파벳을 다른 알파벳으로 치환하는 형태의 substitution cipher(치환암호) 중 하나인데, 그 중에서도 monoalphabetic substitution cipher에 해당하며, 그 중에서도 가장 간단한 형태로 단순히 일정하게 x칸 떨어진 글자를 사용하는 알고리즘입니다. 예를 들어서,

ABCDEFGHIJKLMNOPQRSTUVWXYZ ← Plain
BCDEFGHIJKLMNOPQRSTUVWXYZA ← Cipher

위와 같이 한글자를 옆으로 미는 형태에서 키는 +1이 되고, 이런걸 보통 ROT1(rotate by 1 place)이라고 부릅니다(ROT 형제 중 가장 유명한 것은 ROT13입니다). 위 알고리즘과 키를 이용하여 Plaintext(평문)으로부터 Ciphertext(암호문)을 뽑아보면(=암호화, encryption) 다음과 같습니다.

O say, can you see, by the dawn's early light ← Plaintext
P tbz, dbo zpv tff, cz uif ebxo't fbsmz mjhiu ← Ciphertext

위 암호화 방식을 알고, key(위 경우엔 +1)를 아는 사람은, 전달받은 Ciphertext로부터 Plaintext를 뽑을 수 있습니다(=복호화, decryption). 그러나 이것은 워낙에 간단한 알고리즘이기 때문에, 존재할 수 있는 키가 25개에 없다는 문제가 있습니다(알파벳이 26글자이므로 다른 결과를 내는 rotation이 총 26가지 존재하긴 하지만, 0은 원문 그대로이므로). 더 큰 문제는 키는 물론이고 암호화 알고리즘 자체를 모르는 사람이라 하더라도, 그냥 보다보면 복호화가 가능한 수준이기 때문에 사실상 아무런 보안도 제공하지 못한다는 겁니다.


  B. 조금 더 복잡한 치환암호

그렇다면 다음과 같이 조금 더 복잡한 형태의 치환암호는 어떨까요?

ABCDEFGHIJKLMNOPQRSTUVWXYZ ← Plain
TXNFIGCYRSMZWOEAVBKQJHDLPU ← Cipher 1
WOGCMUBKXZVYRSQFAPJHELITND ← Cipher 2
MJENUQIKYGRFHZOSTBXAVDPLCW ← Cipher 3
...

송수신자가 이런 치환키 300개를 공유한다면, 편지 도입부에 어떤 키를 사용한 것인지 미리 적어두고 암호문을 보내면, 그것을 해독하는 것은 앞서 언급한 x글자 밀기형 치환암호보다는 조금 더 어렵습니다. 하지만 여기에도 문제가 있습니다. 우선 이런 치환암호문을 복호화하려면 송수신자 모두는 이런 키를 공유해야 하는데, 키가 많으면 많을수록 이런 키를 전달하거나 보관하는 과정에서 유출될 가능성이 높아집니다.

게다가 알파벳은 사용빈도가 동일하지가 않습니다. 예를 들어서 영어의 경우, E/T/A 같은 알파벳은 자주 사용되고, Q/Z 같은 알파벳은 거의 사용되지 않습니다. 따라서 암호문이 길면 길수록 -암호문 샘플이 단 한개만 존재한다 하더라도- 연필만 가지고 키를 찾아내는 것이 불가능하지 않습니다.


C. polyalphabetic substitution cipher

이건 여러 형태가 존재할 수 있지만(e.g. 가장 유명한 것 중 하나로 Vigenère cipher), I.1.A.에서 언급한 x글자 밀기형 치환암호를 변형하여 만들어보겠습니다. 앞서 x글자 밀기형 치환암호의 경우 키가 25개만 존재한다고 했지만, 매 글자마다 다른 칸수의 밀기를 하도록 약속한다면, 키의 갯수를 크게 늘릴 수 있습니다.

예를 들어서 키를 4329086579로 정하고,

첫번째 글자는 4칸
두번째 글자는 3칸
세번째 글자는 2칸
네번째 글자는 9칸
다섯번째 글자는 0칸
(중략)
열번째 글자는 9칸

열한번째 글자는 4칸
열두번째글자는 3칸
(후략)

같은 식으로 밀기를 한다고 칩시다. 이렇게 10글자 키를 쓰면 가능한 키가 100억개로 늘어나기 때문에 손으로 모든 조합을 무작정 적어보는 것은 불가능하고, 키의 자리수를 모르는 공격자는 알파벳 빈도 공격을 하기도 어렵습니다. 또한 치환키 300개를 사용할 때보다 수신(예정)자에게 키를 전달하기가 쉬워지고, 외울 수 있을만한 길이이기 때문에 키를 적어둔 쪽지라는 취약점을 없앨 수 있습니다.

즉 별로 중요하지 않은 내용을 1:1 송수신하는 용도로 사용할 때에는 이 정도의 암호화만 하더라도 어느 정도는 쓸만합니다. 하지만 중요한 내용을 다수에게 보내는 용도로 사용하기는 부족함이 너무 많습니다. 우선 100만명이 10자리 숫자를 공유하고 있는데 그 숫자가 새어나가지 않기를 기대할 수는 없죠. 모두가 키를 외우고 있고 적어둔 키가 없어서 그런 메모지 탈취는 불가능하다 하더라도, 그걸 외우고 있는 사람 하나를 잡아서 고문하면 털어놓기 마련입니다.

또 손으로 모든 조합을 하나하나 시도해 볼 수는 없지만, 그렇다고 해서 손으로 해독하는 것이 불가능한 것은 아닙니다. 취약점 중 한가지만 설명해보자면, 이런 암호화 방식에서는 띄어쓰기 위치가 Plaintext와 Ciphertext 양쪽 모두에서 동일하기 때문에, Ciphertext만 봐도 평문버전을 짐작할 수 있는 단어들이 존재합니다. 예를 들어 편지 하단부에 [5글자 띄어쓰기 9글자]가 존재한다면, 이것은 [Yours Sincerely]일 가능성이 상당히 높습니다. 편지 하단부의 [4글자 띄어쓰기 7글자]라면 [Best/Warm/King/Fond] [Regards] 따위일 가능성이 높죠. 그 외에도 글 어디에서든 3글자 단어가 등장하면 the, and, for, not, you, but 같은 단어일 가능성이 높으며, 한글자 단어는 I 아니면 a일 가능성이 높습니다. 이런 조합들로 말이 되는 키 조합을 찾다 보면 결국 깨지기 마련입니다(모든 글자를 다 붙여쓰면 이런 공격이 조금 더 어려워지긴 합니다만, 그럼 키를 알고 있는 사람도 메시지가 짧으면 Pen is broken과 Penis broken을 구별할 수 없는 등의 문제가 발생하는데다, 이게 유일한 공격법인 것도 아니기 때문에 큰 의미가 없습니다).


2. Enigma

독일군이 사용하던 Enigma도 기본적으로 I.1.C.의 일종이고, 더 복잡한 버전에 불과합니다. 자세한 설명은 앞서 언급하였던 대로 생략합니다.


3. 현대의 대칭키 암호화

위와 같이 암호화를 할 때와 복호화를 할 때 동일한 키를 사용하는 것이 대칭키 암호화입니다. 대칭키 암호화 방식 하에서는 통신을 할 사람들 사이에서 키를 안전하게 전달하고 보관하는 것이 중요하며, 키를 갖지 않은 사람이 키를 찾아내는 데 오랜 시간이 걸리도록 디자인되어야 합니다. 그런데 컴퓨터는 전인류를 합친 것보다도 빠른 속도로 시도를 해볼 수 있기 때문에, 현대의 대칭키 암호화 알고리즘들은 상당히 큰 숫자의 키를 사용합니다. 예를 들어서 256bit 키라면 조합이 2^256개 존재하는데, 풀어쓰면
115,792,089,237,316,195,423,570,985,008,687,907,853,269,984,665,640,564,039,457,584,007,913,129,639,936개이며,

2048bit 키는 조합이 2^2048개가 존재하는데, 풀어쓰면
32,317,006,071,311,007,300,714,876,688,669,951,960,444,102,669,715,484,032,130,345,427,524,655,138,867,890,893,197,201,411,522,913,463,688,717,960,921,898,019,494,119,559,150,490,921,095,088,152,386,448,283,120,630,877,367,300,996,091,750,197,750,389,652,106,796,057,638,384,067,568,276,792,218,642,619,756,161,838,094,338,476,170,470,581,645,852,036,305,042,887,575,891,541,065,808,607,552,399,123,930,385,521,914,333,389,668,342,420,684,974,786,564,569,494,856,176,035,326,322,058,077,805,659,331,026,192,708,460,314,150,258,592,864,177,116,725,943,603,718,461,857,357,598,351,152,301,645,904,403,697,613,233,287,231,227,125,684,710,820,209,725,157,101,726,931,323,469,678,542,580,656,697,935,045,997,268,352,998,638,215,525,166,389,437,335,543,602,135,433,229,604,645,318,478,604,952,148,193,555,853,611,059,596,230,656개 입니다.

비트코인은 대칭키 암호화를 사용하지 않으니 더 이상의 자세한 이야기는 생략하도록 하겠습니다.



II. 공개키(비대칭키) 방식의 동작 원리

공개키 암호 기법은 원본 메시지를 암호화 할 때 사용하는 키와 복호화 할 때 사용하는 키가 다른 방식을 의미합니다. 다시 말해 애초에 키를 2개씩 페어로 생성하여 A키로 암호화 한 것은 B키로만 복호화 할 수 있고, B키로 암호화 한 것은 A키로만 복호화 할 수 있게 하는 겁니다. 이런 키페어 중 B키만을 불특정 다수에게 뿌리고 다닌다면 불특정 다수가 B키를 사용해서 A키를 소유한 사용자만이 복호화할 수 있는 암호 메시지를 만들 수 있게 되며, 역으로 A키를 가진 사람은 A키로 메시지를 암호화해서 뿌리는 방법으로 불특정 다수에게 그 메시지가 A키를 가진 소유자가 작성한 것이라는 것을 입증할 수 있게 됩니다.

그리고 위 특징을 조합하면 불특정인간A(PubA/PrivA 키페어 소유)는 ①불특정인간B(PubB/PrivB 키페어 소유)만이 그 내용을 확인할 수 있으며, ②B가 그것을 보고 A가 쓴 것이 틀림 없다고 확신할 수 있는 암호 메시지를 보낼 수 있게 됩니다(여기서 Pub키는 불특정 다수에게 뿌리고 다니는 키, Priv키는 본인만이 가지고 있는 키를 의미합니다). 구체적으로 A가 원본 메시지를 자신의 PrivA키로 암호화 한 뒤, 그렇게 나온 암호 메시지를 PubB키로 재차 암호화하여, 최종적으로 생성된 암호 메시지를 불특정 다수에게 뿌리는 형태를 생각해보시면 됩니다. 그러면 PrivB키가 없는 다른 모든 불특정 다수의 사람은 이 암호 메시지를 복호화할 수가 없으나, B의 경우 PrivB키를 소유하고 있기 때문에 1차 복호화가 가능하며, 2차 복호화에 필요한 PubA키는 이미 불특정 다수에게 공개되어 있으므로 B도 이걸 가져와서 2차 복호화를 수행할 수 있게 됩니다. 그럼 이 메시지는 B만이 확인할 수 있으며, 또한 B는 이 메시지가 진짜 A가 보낸 메시지가 맞다는 것을 확신할 수 있게 됩니다.

원래는 각종 비대칭키에 대해 자세히 기재한 뒤 비트코인 이야기를 적으려고 했으나, 여기까지 쓰고보니 심경에 변화가 생겼습니다. 사실 구체적으로 비트코인의 암호화가 어떻게 동작하느냐는 트랜잭션을 설명하면서 같이 설명하는 것이 좋기 때문에, 구체적인 내용은 나중에 쓸 트랜잭션 글로 미루고 여기서 이만 줄이겠습니다.



III. 해시 함수

해시 함수는 임의 사이즈의 원본 데이터를 일정한 사이즈의 데이터로 소화시키는 함수입니다. 해시 함수를 통해서 도출되는 일정한 사이즈의 값을 해시 값(또는 해시 코드, 해시 등)이라고 합니다.

해시는 암호와 관련하여 자주 사용되기는 하지만, 암호화의 방식 중 하나인 것은 아닙니다. 암호화를 했다면 키를 가지고 있는 사람은 복호화를 통해 원본 메시지를 복원할 수 있어야 합니다. 하지만 해시함수는 단방향이기 때문에, 해시값으로부터 원본 메시지를 도출할 수 없습니다. 물론 해시함수를 완전 엉터리로 만들면 이런 과정이 일부 가능할 수도 있습니다만, 그런 경우에도 그게 복호화는 아닙니다. 복호화라는 것은 암호문으로부터 원본 메시지를 복원하는 과정인데, 해시값은 애초에 원본 메시지와 1:1로 대응되는 관계가 아니기 때문에, 설사 동일한 해시값이 나오는 어떤 데이터를 찾아내더라도 그게 그 해시값을 만든 사람이 사용한 원본 메시지인지는 알 수 없기 때문입니다.

암호쪽에서 사용되는 해시함수는 해시값으로부터 원본데이터를 도출하기가 어렵다는 것 외에도 몇 가지 특징이 있습니다. 그 중에서 특정한 해시값과 동일한 해시값이 나오는 원본 데이터를 다른 원본 데이터를 찾기가 어렵다는 것, 동일한 해시값이 나오는 서로 다른 원본 데이터를 찾기가 어렵다는 것, 그리고 원본 데이터가 조금만 달라져도 해시값이 완전히 달라진다는 것 정도가 중요한 요소라고 할 수 있습니다. 해시함수는 이런 특징을 살려 암호 저장용이나 무결성 확인용 등으로 자주 사용됩니다.

암호 저장용만 예를 들어보면, 웹사이트에서 회원가입을 받을 때 사용자의 암호를 받아서 원문 그대로 저장하는 경우, 사용자가 서버에 전송하는 과정에서 탈취될 가능성, 서버가 해킹되어서 탈취될 가능성 등 사용자의 암호 원문이 그대로 탈취당할 수 있는 복수의 루트가 존재하게 됩니다. 한 번 원문이 탈취당하면 암호를 돌려쓰는 사람이 많기 때문에 피해가 상당히 커지게 됩니다. 이 때 암호 원문을 받아서 저장하는 대신 그 암호의 해시값을 받아서 저장하면 이런 문제가 많이 해결됩니다. 해시값만 보고는 원문을 찾아낼 수가 없고, 그 해시값과 동일한 해시값이 나오는 다른 원문을 찾기도 어려우니까요. 하지만 이것만으로는 비밀번호로 password나 1q2w3e4r를 쓰는 사람을 구제할 수는 없기 때문에(공격자도 어지간한 원본 암호의 해시값 사전은 기본으로 들고 있는 관계로), 기본이 되어 있는 곳에서는 사용자마다 서로 다른 임의 난수를 첨가하여 해시를 돌린다거나(i.e. salt), 일정한 값을 첨가하여 해시를 돌린다던가(i.e. pepper), 복수의 해시함수를 반복적으로 돌리는 등의 여러 방법을 복합적으로 사용합니다.

해시 함수 이야기는 이쯤에서 줄이고, 이제 구체적인 해시 함수의 동작을 살펴보도록 하겠습니다. 이것저것 다룰까 하다가 비트코인이 쓰는것만 다루는게 나을듯 하여, SHA-256과 RIPEMD-160 이야기를 하면서 관련된 비트코인 이야기를 조금 해보겠습니다.


1. SHA-1

SHA-256을 이야기한다더니 왜 SHA-1이 나오냐 싶으시겠지만, SHA-256 해시 함수를 수식 없이 예시로만 설명하면 길이가 너무 길어집니다. 그래서 조금이라도 더 짧게 끊을 수 있는 SHA-1를 예시를 통해 설명한 뒤, SHA-256는 차이점만 기재하는 방식으로 가겠습니다(그래도 엄청 깁니다).

SHA-1은 최대 2^64-1비트의 원문에서 160 비트(160자리 2진수, 40자리의 16진수)의 해시값을 도출시키는 함수인데,  여기서는 [SHA-1s]라는 스트링의 SHA-1 해시값을 직접 구해보겠습니다.  작업은 원문 - 패딩 (사전작업 1단계) - 파싱 (사전작업 2단계) - 이니셜 해시 밸류 설정 (사전작업 3단계) - 해싱 순서로 진행되며, 예시를 자세히 들다 보니 길이가 어마어마하게 길어졌으니, 간단한 수식 읽기와 산수가 가능하신 분은 NIST의 자료(FIPS 180-4)를 보시는 편이 낫습니다.


A. 원문 메시지

우선 원문 메시지 [SHA-1sTt]를 구성하는 각각의 ASCII 코드는 다음과 같습니다(UTF-8에서도 동일). 참고로 T와 t는 1비트만 다른 동일 알파벳의 대소문자가 이런 연산을 거쳤을 때 얼마나 다른 결과를 나타낼지 상상해보라는 취지로 덧붙인 예시이고, s는 모든 대소문자의 ASCII 코드가 1비트만 다르지는 않다는 것을 보여주기 위한 예시입니다.

알파벳 - Hex - Decimal - Binary
S - 0x53 - 083 - 01010011
H - 0x48 - 072 - 01001000
A - 0x41 - 065 - 01000001
- - 0x2D - 045 - 00101101
1 - 0x31 - 049 - 00110001
s - 0x73 - 115 - 01110011
T - 0x54 - 084 - 01010100
t - 0x74 - 116 - 01110100

우리가 실제로 컴퓨터를 하면서 눈으로 보게 되는 것은 데이터를 다시 디코딩해서 만들어낸 [SHA-1sTt]입니다만, 실제로는 위 바이너리 형태로 저장 혹은 연산이 이뤄집니다. 그러니 원형으로 만들어서 계산을 해야 합니다.


B. 패딩 (사전작업 1단계)

위 2진수를 1바이트 단위로 보기좋게 한칸씩 띄어서 순서대로 늘어놓으면 다음과 같이 됩니다.
01010011 01001000 01000001 00101101 00110001 01110011 01010100 01110100

그 뒤에 1을 붙여서 아래와 같이 만듭니다.
01010011 01001000 01000001 00101101 00110001 01110011 01010100 01110100
1

이제 1 뒤에 0개 이상 512개 미만의 0을 붙여서 길이가 448mod512(=448)가 되도록 만듭니다. 위 예시의 경우 패딩용 1까지 포함해 65자리이니 0이 383개 필요하며, 뒤에 붙이면 이런 모양이 됩니다.
01010011 01001000 01000001 00101101 00110001 01110011 01010100 01110100
10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000


이제 [메시지 사이즈](위 예시의 경우 64자리이니 2진수 01000000)를 뒤에 붙여서 아래와 같이 512비트의 [블록]을 만듭니다. SHA-1의 경우 메시지 사이즈의 표기는 64비트로 하고, 뒷쪽부터 합니다. 원본 메시지가 512비트보다 길었던 경우에는 원본 메시지를 잘라서 512비트 단위 블록을 여러개 만들고, 마지막으로 남은 덩어리가 이런 모양이 됩니다. 마지막으로 남은 덩어리의 데이터가 447자리면 1과 메시지 사이즈를 붙여서 바로 끝나는 것이고, 448자리면 1 뒤에 511개의 0이 들어간 뒤 메시지 사이즈가 붙으며, 메시지가 512비트의 정배수라 마지막 덩어리에 1밖에 없으면 447개의 0을 붙이고 메시지 사이즈를 넣은 것이 마지막 블록이 됩니다.

01010011 01001000 01000001 00101101 00110001 01110011 01010100 01110100
11000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000000 00000000 00000000 00000000 01000000

이렇게 완성된 [512비트 블록(들)]은 80라운드짜리 연산을 수행하는 기본단위가 됩니다.


C. 파싱 (사전 작업 2단계)

우선 위 512비트 블록을 32비트 단위로 쪼개서, 16개의 32비트 [words]를 만듭니다(너무 기니까 앞으로는 가급적이면 binary words 옆에 붙여놓은 hex 버전의 words로 표기하겠습니다). 이런 32비트 words들이 앞으로 있을 각 연산의 기본 단위가 됩니다.

M0_1 - 01010011 01001000 01000001 00101101 - 5348412D
M1_1 - 00110001 01110011 01010100 01110100 - 31735474
M2_1 - 10000000 00000000 00000000 00000000 - 80000000
M3_1 - 00000000 00000000 00000000 00000000 - 00000000
M4_1 - 00000000 00000000 00000000 00000000 - 00000000
M5_1 - 00000000 00000000 00000000 00000000 - 00000000
M6_1 - 00000000 00000000 00000000 00000000 - 00000000
M7_1 - 00000000 00000000 00000000 00000000 - 00000000
M8_1 - 00000000 00000000 00000000 00000000 - 00000000
M9_1 - 00000000 00000000 00000000 00000000 - 00000000
M10_1 - 00000000 00000000 00000000 00000000 - 00000000
M11_1 - 00000000 00000000 00000000 00000000 - 00000000
M12_1 - 00000000 00000000 00000000 00000000 - 00000000
M13_1 - 00000000 00000000 00000000 00000000 - 00000000
M14_1 - 00000000 00000000 00000000 00000000 - 00000000
M15_1 - 00000000 00000000 00000000 01000000 - 00000040

위 예시에서는 블록이 하나밖에 없어서 이게 끝이지만, 512비트 블록이 N개 있는 경우 다음과 같이 16N개의 words를 만드시면 됩니다.

M0_1 , M1_1 , ... , M15_1,
M0_2 , M1_2 , ... , M15_2,
...
M0_N , M1_N , ... , M15_N


D. 초기 해시값 설정 (사전작업 3단계)

SHA-1에서는 Initial hash value를 다음과 같이 설정합니다.

H0_0 = 67452301
H1_0 = EFCDAB89
H2_0 = 98BADCFE
H3_0 = 10325476
H4_0 = C3D2E1F0

앞으로 이 값에 계속 일정한 방식으로 계산을 반복해서 최종적인 160비트 (16진수 40자리) 해시값을 뽑게 됩니다.


E. 해싱

우선 Mt_i 블록을 Wt에 복사합니다 (블록은 워드가 16개 존재하니, t는 0부터 15까지)

W0 = M0_1 - 5348412D
W1 = M1_1 - 31735474
W2 = M2_1 - 80000000
W3 = M3_1 - 00000000
W4 = M4_1 - 00000000
W5 = M5_1 - 00000000
W6 = M6_1 - 00000000
W7 = M7_1 - 00000000
W8 = M8_1 - 00000000
W9 = M9_1 - 00000000
W10 = M10_1 - 00000000
W11 = M11_1 - 00000000
W12 = M12_1 - 00000000
W13 = M13_1 - 00000000
W14 = M14_1 - 00000000
W15 = M15_1 - 00000040

이제 16≤t≤79 구간의 Wt를 만들어야 합니다. 계산은 Wt = ROTL1(Wt-3 XOR Wt-8 XOR Wt-14 XOR Wt-16)으로 합니다. 여기서 ROTL1은 바이너리 기준으로 왼쪽으로 한 칸 밀고, 왼쪽에 있던 것이 삐져나오면 그만큼 맨 오른쪽으로 이동시킨다는 뜻이고, XOR(Exclusive OR)은 바이너리 기준 둘 중 하나만 1인 경우에 1을 출력하고 그렇지 않은 경우에는 0을 출력하는 연산입니다(같으면 0, 다르면 1이라고 보아도 됩니다). 예를 들어서 01110011과 10100011의 XOR은 다음과 같은 결과값을 출력합니다.

______01110011
______10100011
XOR = 11010000

이제 아래와 같이 W16부터 W79까지 값을 계산합니다.

W16 = ROTL1(W13 XOR W8 XOR W2 XOR W0) = ROTL1(00000000 XOR 00000000 XOR 00000000 XOR 5348412D) = ROTL1(5348412D)
     = ROTL1(01010011 01001000 01000001 00101101) = 10100110 10010000 10000010 01011010 = A690825A
W17 = ROTL1(W14 XOR W9 XOR W3 XOR W1) = ROTL1(31735474) = 01100010 11100110 10101000 11101000 = 62E6A8E8
W18 = 00000081
W19 = 4D2104B7
W20 = C5CD51D0
W21 = 00000102
W22 = 9A42096E
W23 = 8B9AA321
W24 = 4D2106B3
W25 = F149430D
W26 = 17354741
W27 = 00000408
W28 = 690825BA
W29 = 2E6A8C06
W30 = 79A51E7A
W31 = 00E85C67
W32 = 8BB610DF
W33 = 4E57E251
W34 = A42094EF
W35 = 8D2E22C5
W36 = F1A13EAA
W37 = 03A17594
W38 = 47D066C4
W39 = 173505C3
W40 = A4064973
W41 = F19D8721
W42 = 9A51E7AF
W43 = 0E85C770
W44 = 21230496
W45 = 6EE484B5
W46 = 768D5E23
W47 = F1537EC6
W48 = CAE893ED
W49 = 88D2332C
W50 = 7D066658
W51 = 95C40798
W52 = 3F00DFA8
W53 = 0EED2574
W54 = 013EEC18
W55 = 51F64718
W56 = F4A4308B
W57 = EDE93ECA
W58 = 2F0584F3
W59 = 0202E9AC
W60 = 0A8F77AF
W61 = 7CBEB7E9
W62 = 7EB390F5
W63 = 45F0FABA
W64 = 7FE8E5AE
W65 = 1C993517
W66 = 51E78F72
W67 = CD865DDD
W68 = 5050E210
W69 = E484B5EE
W70 = 8D5E2376
W71 = 537EC2F1
W72 = 819BC870
W73 = FC59820E
W74 = 4E277DBE
W75 = 654397D0
W76 = B06B0E89
W77 = 27DB0A07
W78 = D28d83FB
W79 = 74F807AB

그리고 H0(i-1), H1(i-1), H2(i-1), H3(i-1), H4(i-1)을 a,b,c,d,e에 각각 복사합니다.

a = H0_0 = 67452301
b = H1_0 = EFCDAB89
c = H2_0 = 98BADCFE
d = H3_0 = 10325476
e = H4_0 = C3D2E1F0

여기까지 했으면 준비작업은 끝났고, 이제 본격적인 계산에 들어갑니다. 복잡한 것은 아니고 t=0부터 시작해서 t=79이 될때까지

1. T에다가 ①a값을 좌측으로 5 이동시킨 값 + ②ft(b,c,d) + ③e + ④Kt + ⑤Wt를 합한 값을 넣음
2. e에다가 d 값을 넣음
3. d에다가 c 값을 넣음
4. c에다가 b를 좌측으로 30 이동시킨 값을 넣음(ROTL30(b))
5. b에다가 a를 넣음
6. a에다가 T를 넣음

위 여섯가지 작업을 t를 하나씩 올려가며 총 80번 반복해서 수행하면 됩니다. 여기서 ft(b,c,d)라는 것은 t 값에 따라서 다음과 같이 다른 연산을 수행하는 함수이며,

0≤t≤19 - (b AND c) XOR (NOTb AND d)
20≤t≤39 - b XOR c XOR d
40≤t≤59 - (b AND c) XOR (b AND d) XOR (c AND d)
60≤t≤79 - b XOR c XOR d

AND 연산은 다음과 같이 입력된 동일기리 바이너리의 값이 둘 다 1이면 1을, 그 외에는 0을 출력하는 연산이고,

______01110011
______10100011
AND = 00000011

NOT 연산은 다음과 같이 입력을 역으로 뒤집는 연산입니다.

______01110011
NOT = 10001100

마지막으로 Kt는 t값에 따라서 다음과 같이 일정한 값을 가지는 상수입니다.

0≤t≤19 - 5A827999
20≤t≤39 - 6ED9EBA1
40≤t≤59 - 8F1BBCDC
60≤t≤79 - CA62C1D6


예시를 다 적기에는 가망이 없어보이죠? 라운드별 결과만 정리하면 아래와 같이 됩니다.

a 0 : 0xf2fcd9e0 | b 0 : 0x67452301 | c 0 : 0x7bf36ae2 | d 0 : 0x98badcfe | e 0 : 0x10325476
a 1 : 0xf7bf5d9f | b 1 : 0xf2fcd9e0 | c 1 : 0x59d148c0 | d 1 : 0x7bf36ae2 | e 1 : 0x98badcfe
a 2 : 0xc4fc7557 | b 2 : 0xf7bf5d9f | c 2 : 0x3cbf3678 | d 2 : 0x59d148c0 | e 2 : 0x7bf36ae2
a 3 : 0xb303a3cb | b 3 : 0xc4fc7557 | c 3 : 0xfdefd767 | d 3 : 0x3cbf3678 | e 3 : 0x59d148c0
a 4 : 0x11b7933e | b 4 : 0xb303a3cb | c 4 : 0xf13f1d55 | d 4 : 0xfdefd767 | e 4 : 0x3cbf3678
a 5 : 0xcc236d38 | b 5 : 0x11b7933e | c 5 : 0xecc0e8f2 | d 5 : 0xf13f1d55 | e 5 : 0xfdefd767
a 6 : 0xbd68848c | b 6 : 0xcc236d38 | c 6 : 0x846de4cf | d 6 : 0xecc0e8f2 | e 6 : 0xf13f1d55
a 7 : 0x9db40d4f | b 7 : 0xbd68848c | c 7 : 0x3308db4e | d 7 : 0x846de4cf | e 7 : 0xecc0e8f2
a 8 : 0x2ed2eccd | b 8 : 0x9db40d4f | c 8 : 0x2f5a2123 | d 8 : 0x3308db4e | e 8 : 0x846de4cf
a 9 : 0xe866cb10 | b 9 : 0x2ed2eccd | c 9 : 0xe76d0353 | d 9 : 0x2f5a2123 | e 9 : 0x3308db4e
a10 : 0xc1acb867 | b10 : 0xe866cb10 | c10 : 0x4bb4bb33 | d10 : 0xe76d0353 | e10 : 0x2f5a2123
a11 : 0x0ea13307 | b11 : 0xc1acb867 | c11 : 0x3a19b2c4 | d11 : 0x4bb4bb33 | e11 : 0xe76d0353
a12 : 0x202e9121 | b12 : 0x0ea13307 | c12 : 0xf06b2e19 | d12 : 0x3a19b2c4 | e12 : 0x4bb4bb33
a13 : 0xdc42fbb1 | b13 : 0x202e9121 | c13 : 0xc3a84cc1 | d13 : 0xf06b2e19 | e13 : 0x3a19b2c4
a14 : 0xed64d0b1 | b14 : 0xdc42fbb1 | c14 : 0x480ba448 | d14 : 0xc3a84cc1 | e14 : 0xf06b2e19
a15 : 0x4332626f | b15 : 0xed64d0b1 | c15 : 0x7710beec | d15 : 0x480ba448 | e15 : 0xc3a84cc1
a16 : 0x90134b85 | b16 : 0x4332626f | c16 : 0x7b59342c | d16 : 0x7710beec | e16 : 0x480ba448
a17 : 0x7eeef427 | b17 : 0x90134b85 | c17 : 0xd0cc989b | d17 : 0x7b59342c | e17 : 0x7710beec
a18 : 0xaab9fa9e | b18 : 0x7eeef427 | c18 : 0x6404d2e1 | d18 : 0xd0cc989b | e18 : 0x7b59342c
a19 : 0x5e40df0a | b19 : 0xaab9fa9e | c19 : 0xdfbbbd09 | d19 : 0x6404d2e1 | e19 : 0xd0cc989b
a20 : 0xde964ccd | b20 : 0x5e40df0a | c20 : 0xaaae7ea7 | d20 : 0xdfbbbd09 | e20 : 0x6404d2e1
a21 : 0xd0fd75e3 | b21 : 0xde964ccd | c21 : 0x979037c2 | d21 : 0xaaae7ea7 | e21 : 0xdfbbbd09
a22 : 0xec2e743a | b22 : 0xd0fd75e3 | c22 : 0x77a59333 | d22 : 0x979037c2 | e22 : 0xaaae7ea7
a23 : 0x5bba65d8 | b23 : 0xec2e743a | c23 : 0xf43f5d78 | d23 : 0x77a59333 | e23 : 0x979037c2
a24 : 0x3a8c9f92 | b24 : 0x5bba65d8 | c24 : 0xbb0b9d0e | d24 : 0xf43f5d78 | e24 : 0x77a59333
a25 : 0x3deb59d6 | b25 : 0x3a8c9f92 | c25 : 0x16ee9976 | d25 : 0xbb0b9d0e | e25 : 0xf43f5d78
a26 : 0xcf23670b | b26 : 0x3deb59d6 | c26 : 0x8ea327e4 | d26 : 0x16ee9976 | e26 : 0xbb0b9d0e
a27 : 0xb3f95574 | b27 : 0xcf23670b | c27 : 0x8f7ad675 | d27 : 0x8ea327e4 | e27 : 0x16ee9976
a28 : 0x3cf5f001 | b28 : 0xb3f95574 | c28 : 0xf3c8d9c2 | d28 : 0x8f7ad675 | e28 : 0x8ea327e4
a29 : 0x99f0fa75 | b29 : 0x3cf5f001 | c29 : 0x2cfe555d | d29 : 0xf3c8d9c2 | e29 : 0x8f7ad675
a30 : 0x99dcabe1 | b30 : 0x99f0fa75 | c30 : 0x4f3d7c00 | d30 : 0x2cfe555d | e30 : 0xf3c8d9c2
a31 : 0x99547125 | b31 : 0x99dcabe1 | c31 : 0x667c3e9d | d31 : 0x4f3d7c00 | e31 : 0x2cfe555d
a32 : 0x02ba600c | b32 : 0x99547125 | c32 : 0x66772af8 | d32 : 0x667c3e9d | e32 : 0x4f3d7c00
a33 : 0xfd1ab0b2 | b33 : 0x02ba600c | c33 : 0x66551c49 | d33 : 0x66772af8 | e33 : 0x667c3e9d
a34 : 0x1f652c49 | b34 : 0xfd1ab0b2 | c34 : 0x00ae9803 | d34 : 0x66551c49 | e34 : 0x66772af8
a35 : 0xeb05f779 | b35 : 0x1f652c49 | c35 : 0xbf46ac2c | d35 : 0x00ae9803 | e35 : 0x66551c49
a36 : 0xc81c4e37 | b36 : 0xeb05f779 | c36 : 0x47d94b12 | d36 : 0xbf46ac2c | e36 : 0x00ae9803
a37 : 0x8a4dd078 | b37 : 0xc81c4e37 | c37 : 0x7ac17dde | d37 : 0x47d94b12 | e37 : 0xbf46ac2c
a38 : 0xb4af869d | b38 : 0x8a4dd078 | c38 : 0xf207138d | d38 : 0x7ac17dde | e38 : 0x47d94b12
a39 : 0x6664ce57 | b39 : 0xb4af869d | c39 : 0x2293741e | d39 : 0xf207138d | e39 : 0x7ac17dde
a40 : 0x2d0465b6 | b40 : 0x6664ce57 | c40 : 0x6d2be1a7 | d40 : 0x2293741e | e40 : 0xf207138d
a41 : 0x7970f266 | b41 : 0x2d0465b6 | c41 : 0xd9993395 | d41 : 0x6d2be1a7 | e41 : 0x2293741e
a42 : 0xe728c72f | b42 : 0x7970f266 | c42 : 0x8b41196d | d42 : 0xd9993395 | e42 : 0x6d2be1a7
a43 : 0xc9377f54 | b43 : 0xe728c72f | c43 : 0x9e5c3c99 | d43 : 0x8b41196d | e43 : 0xd9993395
a44 : 0x400ffccd | b44 : 0xc9377f54 | c44 : 0xf9ca31cb | d44 : 0x9e5c3c99 | e44 : 0x8b41196d
a45 : 0x649f327f | b45 : 0x400ffccd | c45 : 0x324ddfd5 | d45 : 0xf9ca31cb | e45 : 0x9e5c3c99
a46 : 0xa83ba551 | b46 : 0x649f327f | c46 : 0x5003ff33 | d46 : 0x324ddfd5 | e46 : 0xf9ca31cb
a47 : 0xf1be1719 | b47 : 0xa83ba551 | c47 : 0xd927cc9f | d47 : 0x5003ff33 | e47 : 0x324ddfd5
a48 : 0x9c3900ef | b48 : 0xf1be1719 | c48 : 0x6a0ee954 | d48 : 0xd927cc9f | e48 : 0x5003ff33
a49 : 0xe840da4b | b49 : 0x9c3900ef | c49 : 0x7c6f85c6 | d49 : 0x6a0ee954 | e49 : 0xd927cc9f
a50 : 0x6994bb16 | b50 : 0xe840da4b | c50 : 0xe70e403b | d50 : 0x7c6f85c6 | e50 : 0x6a0ee954
a51 : 0xadd4d0e0 | b51 : 0x6994bb16 | c51 : 0xfa103692 | d51 : 0xe70e403b | e51 : 0x7c6f85c6
a52 : 0xf03a7071 | b52 : 0xadd4d0e0 | c52 : 0x9a652ec5 | d52 : 0xfa103692 | e52 : 0xe70e403b
a53 : 0x46b96789 | b53 : 0xf03a7071 | c53 : 0x2b753438 | d53 : 0x9a652ec5 | e53 : 0xfa103692
a54 : 0x1c0d051f | b54 : 0x46b96789 | c54 : 0x7c0e9c1c | d54 : 0x2b753438 | e54 : 0x9a652ec5
a55 : 0x6b550ab4 | b55 : 0x1c0d051f | c55 : 0x51ae59e2 | d55 : 0x7c0e9c1c | e55 : 0x2b753438
a56 : 0x75e4954a | b56 : 0x6b550ab4 | c56 : 0xc7034147 | d56 : 0x51ae59e2 | e56 : 0x7c0e9c1c
a57 : 0xf8ad8af6 | b57 : 0x75e4954a | c57 : 0x1ad542ad | d57 : 0xc7034147 | e57 : 0x51ae59e2
a58 : 0x7d463bdf | b58 : 0xf8ad8af6 | c58 : 0x9d792552 | d58 : 0x1ad542ad | e58 : 0xc7034147
a59 : 0x99e666b4 | b59 : 0x7d463bdf | c59 : 0xbe2b62bd | d59 : 0x9d792552 | e59 : 0x1ad542ad
a60 : 0x8aa8cef5 | b60 : 0x99e666b4 | c60 : 0xdf518ef7 | d60 : 0xbe2b62bd | e60 : 0x9d792552
a61 : 0x325108c0 | b61 : 0x8aa8cef5 | c61 : 0x267999ad | d61 : 0xdf518ef7 | e61 : 0xbe2b62bd
a62 : 0xc4e3a73d | b62 : 0x325108c0 | c62 : 0x62aa33bd | d62 : 0x267999ad | e62 : 0xdf518ef7
a63 : 0x029cd60f | b63 : 0xc4e3a73d | c63 : 0x0c944230 | d63 : 0x62aa33bd | e63 : 0x267999ad
a64 : 0x6f3dd9c1 | b64 : 0x029cd60f | c64 : 0x7138e9cf | d64 : 0x0c944230 | e64 : 0x62aa33bd
a65 : 0xb091e0c7 | b65 : 0x6f3dd9c1 | c65 : 0xc0a73583 | d65 : 0x7138e9cf | e65 : 0x0c944230
a66 : 0x19bcb1fb | b66 : 0xb091e0c7 | c66 : 0x5bcf7670 | d66 : 0xc0a73583 | e66 : 0x7138e9cf
a67 : 0x6cb1ec19 | b67 : 0x19bcb1fb | c67 : 0xec247831 | d67 : 0x5bcf7670 | e67 : 0xc0a73583
a68 : 0x1ff01c50 | b68 : 0x6cb1ec19 | c68 : 0xc66f2c7e | d68 : 0xec247831 | e68 : 0x5bcf7670
a69 : 0x4fb5308d | b69 : 0x1ff01c50 | c69 : 0x5b2c7b06 | d69 : 0xc66f2c7e | e69 : 0xec247831
a70 : 0xbd3eba4e | b70 : 0x4fb5308d | c70 : 0x07fc0714 | d70 : 0x5b2c7b06 | e70 : 0xc66f2c7e
a71 : 0x9f8d47bb | b71 : 0xbd3eba4e | c71 : 0x53ed4c23 | d71 : 0x07fc0714 | e71 : 0x5b2c7b06
a72 : 0x8203ee38 | b72 : 0x9f8d47bb | c72 : 0xaf4fae93 | d72 : 0x53ed4c23 | e72 : 0x07fc0714
a73 : 0x7265b713 | b73 : 0x8203ee38 | c73 : 0xe7e351ee | d73 : 0xaf4fae93 | e73 : 0x53ed4c23
a74 : 0x83dd7f6a | b74 : 0x7265b713 | c74 : 0x2080fb8e | d74 : 0xe7e351ee | e74 : 0xaf4fae93
a75 : 0x0fac12fc | b75 : 0x83dd7f6a | c75 : 0xdc996dc4 | d75 : 0x2080fb8e | e75 : 0xe7e351ee
a76 : 0xd7f86aee | b76 : 0x0fac12fc | c76 : 0xa0f75fda | d76 : 0xdc996dc4 | e76 : 0x2080fb8e
a77 : 0x858e4627 | b77 : 0xd7f86aee | c77 : 0x03eb04bf | d77 : 0xa0f75fda | e77 : 0xdc996dc4
a78 : 0xa036aa10 | b78 : 0x858e4627 | c78 : 0xb5fe1abb | d78 : 0x03eb04bf | e78 : 0xa0f75fda
a79 : 0x1ac2c392 | b79 : 0xa036aa10 | c79 : 0xe1639189 | d79 : 0xb5fe1abb | e79 : 0x03eb04bf

이제 마지막으로 a,b,c,d,e를 초반에 설정했던 이니셜 밸류에다가 더해서 새로운 H값을 만듭니다.

H0_i = a + H0_(i-1)
H1_i = b + H1_(i-1)
H2_i = c + H2_(i-1)
H3_i = d + H3_(i-1)
H4_i = e + H4_(i-1)

이 과정을 반복해서 마지막에 나온 H0_i, H1_i, H2_i, H3_i, H4_i 값을 순서대로 늘어놓으면 그게 곧 160비트짜리 SHA-1 해시값이 됩니다. 위 예시는 블록이 하나짜리라서 아래처럼 한번에 끝이지만, 원본 데이터가 1MB만 되더라도 블록이 1만개 이상 나옵니다.

H0_1 = 1AC2C392 + 67452301 = 8207E693
H1_1 = A036AA10 + EFCDAB89 = 190045599 (←이렇게 32비트를 초과하는 경우 맨 앞을 버립니다)
H2_1 = E1639189 + 98BADCFE = 17A1E6E87 (←상동)
H3_1 = B5FE1ABB + 10325476 = C6306F31
H4_1 = 03EB04BF + C3D2E1F0 = C7BDE6AF


F. 결과

8207E693900455997A1E6E87C6306F31C7BDE6AF

이제 속도 이야기를 잠깐 해보면, 사람이 위 연산을 암산 or 손과 메모지 or 사칙연산만 가능한 단순 계산기만 들고 직접 수행하는 경우, 시간이 얼마나 걸릴까요? 위 예시와 같이 1블록짜리 단순한 예제라 하더라도, 1시간 안에 정확한 값을 구했다면 상당히 빠른 축에 속할 것이고, 꽤 많은 분들은 시간이 수십시간씩 주어지고 계산기가 있는 조건에서도 정확한 값 구하기에 계속 실패할 가능성이 높을겁니다.

그런데 CPU의 경우 이 정도 크기(단일블록)의 원본 메시지를 해싱한다면 이보다 조금 더 복잡한 SHA-256 해시(SHA-1 대비 약 2-3배쯤 비싼 연산임)를 돌려도 초당 1억개 이상 계산할 수 있으며, GPU의 SHA-1 해시레이트는 이미 초당 100억개의 영역에 있습니다. 그리고 비트코인 마이닝에서는 이런 GPU가 너무 느려서 쓸모가 없는 수준입니다. 소비전력 1kW짜리 ASIC 마이너들은 초당 10-20조개의 SHA-256 해시를 처리할 수 있거든요.

고로 컴퓨터 시대에서는 단순히 사람에게 불가능하다는 것은 정말 아무런 의미가 없고, 컴퓨터에게도 ①특정 해시값을 보고 원본 메시지를 찾기나(이에 대한 내성을 preimage resistance라 합니다), ②특정한 원본 메시지와 동일한 해시값이 나오는 다른 메시지 찾기(이에 대한 내성을 second-preimage resistance라 합니다) 그리고 ③동일한 해시값이 나오는 두 개의 다른 원본 메시지 찾기(collision resistance)에 지나치게 오랜 시간을 필요로 하는 것이어야만 암호용 혹은 위변조확인용 등으로 사용되기에 적합한 알고리즘이라고 할 수 있습니다(그리고 용도에 따라 다르긴 합니다만, 일반적으로 '지나치게 오랜 시간'은 3년 혹은 100만년 같은 것을 이야기하는 것이 아니고, 100억짜리 컴퓨터로 10^40년 같은 것을 의미합니다).

하지만 컴퓨터 자체가 상당히 빠른 속도로 발전하는데다, 공략시간을 큰 폭으로 단축시킬 수 있는 취약점(들)이 존재할 수 있기 때문에, 안전하다고 여겨졌던 것도 시간이 흐르면서 위험한 것으로 확인되는 경우가 있습니다. 예를 들어서 MD5는 90년대에 안전하다고 여겨졌던 알고리즘이지만 2000년대 들어 완전한 충돌공격이 발표되면서 salt나 pepper를 활용하는 시스템이라도 지속적인 MD5의 사용은 권장하지 못할만한 일이 되어버렸고, SHA-1도 작년에 CWI와 Google에서 충돌공격을 발표하면서 그런 대열에 합류하고 있습니다(다만 위 취약점은 아주 제한적인 ③에 가까운 것이라서, 이런 문제가 있다고 해서 ②가 된다는 보장은 없습니다). 비트코인이 쓰는 SHA-256의 경우에는 아직까지는 이 정도 상태입니다.


2. SHA-256

SHA-256은 비트코인에서 여러 용도로 사용되는데, 이름에서 느껴지는 것처럼 256bit 해시값이 나오는 함수입니다. 가능한 원문의 길이는 SHA-1과 동일(2^64-1비트)하며, 블록사이즈도 동일한 512비트입니다. 차이점으로는 ①5x32비트로 시작해서 160비트(5x32) 결과물을 내는 SHA-1과 달리 8x32비트로 시작해서 256비트(8x32비트)를 낸다는 점, ②개별 라운드에서 t값에 따라 4가지 함수만을 사용하는 SHA-1과 달리 6가지 함수가 존재한다는 점, ③각 함수에서 사용하는 연산도 XOR/AND/NOT/ROTL 외에 ROTL/SHR 등이 추가된다는 점 등이 있습니다. SHA-1에서 충분히 자세하게 예시를 들었기 때문에 구체적인 내용은 NIST의 자료(FIPS 180-4)를 보고 직접 파악할 수 있을만한 상태가 되었다고 판단되므로, 자세한 내용은 생략하고 이 파트에서는 비트코인에서 SHA-256가 쓰이는 지점을 사용 분야별로 나눠서 설명해보도록 하겠습니다.

  A. 마이닝

SHA-256은 비트코인의 여러 부분에서 쓰이지만 가장 중요한 것은 마이닝입니다. 나중에 마이닝을 다룰 때 더 자세하게 쓰겠지만, 마이닝은 각 사용자들이 발생시킨 트랜잭션을 블록체인에 기록하여 각 주소의 UTXO(i.e. 일반적인 사용자들이 '잔고'로 인식하는 것)를 이동시켜 주는 역할을 수행합니다.

마이닝이 그럼 구체적으로 무슨 작업을 통해 트랜잭션을 블록체인에 기록하는 것이냐? 간략하게 말하면 무작위 원본메시지를 반복해서 SHA-256 해시를 돌리면서, 일정한 SHA-256 해시값이 도출되는 원본 메시지를 찾는 겁니다. 아까 SHA-256은 안전하다고 했는데 이렇게 원본 메시지를 찾을 수 있으면 망한거 아니냐 같은 생각이 드시는 분도 있겠지만, SHA-256 자체는 초당 10-20조개의 해시를 처리하는 마이너가 백만대 이상 붙어있는 지금 현재의 Bitcoin network로도 원본 메시지를 찾아낼 가망이 전혀 없습니다. 비트코인 네트워크 해시레이트 20 EH/s는 상당히 큰 수이긴 합니다만(20,000,000,000,000,000,000해시/초), 이건 brute force로 특정 SHA-256 해시값이 도출되는 원본 메시지를 찾거나, 해시충돌이 나오는 다른 원본 메시지를 찾는 것은 영영 불가능(10분마다 찾는 것은 고사하고)하다고 말해도 될만큼 여전히 충분히 느린 속도이기 때문입니다(2^128이 그만큼 큰 수이므로).

자세한 것은 나중에 마이닝 파트를 쓰게 되면 거기서 다룰테니, 여기서는 개괄적으로만 살펴보겠습니다. 우선 비트코인의 블록은 블록사이즈(4바이트) + 블록헤더(80바이트) + 포함된 트랜잭션 수(1-9바이트) + 트랜잭션묶음(최대 1MB-4B-80B-1B)으로 구성되는데, 마이너들이 구하고자 하는 블록의 해시값이라는 것은 블록 전체를 원본 메시지로 하는 SHA-256 해시값이 아니고, 그 블록 앞쪽에 위치한 80바이트를 차지하는 블록헤더만을 원본 메시지로 하는 SHA-256 해시값입니다.

블록헤더는 ①블록 버전(4바이트), ②바로 전 블록헤더의 해시값(32바이트)(이처럼 이전 블록의 값을 연쇄적으로 포함시켜가며 후속 블록을 생성하므로, 일련의 블록으로 구성된 블록[체인]이 됩니다), ③이 블록헤더의 해시값이 넘어서는 안 되는 값 nBits(4바이트), ④이 블록에 포함된 트랜잭션들의 Merkle root 해시값(32바이트), ⑤마이너가 해싱을 시작한 유닉스 시간(4바이트), ⑥이런 조건을 만족시키기 위해서 변경시키는 값 Nonce(4바이트)로 구성되어 있습니다.

헤더를 구성하는 6가지 요소 중, ①블록 버전은 이 차트에서 보이는 것처럼 현재로썬 사실상 0x20000000로 고정되어 있다고 보시면 되고(블록 버전은 현재 적용받는 룰을 표시한 것으로, 마이너들의 룰 투표기 역할도 수행하나, 여기선 자세한 설명을 생략하고 BIP0009,BIP0034 링크로 대체), ②이전 블록헤더를 원본메시지로 하는 SHA-256 해시값도 마이너들이 서로 다른 블록을 잡고 계속 각기 다른 블록에 대한 후속 블록을 만들고 있지 않는 이상 동시에 한개만 존재할 수 있습니다. ③nBits는 난이도 조정용으로 들어가 있는 수치인데 -자세한 것은 아래에서 다루기로 하고- 일단 여기서 중요한 것은 비트코인의 난이도(nBits)가 매 2,016 블록을 주기로 재설정된다는 것입니다. 그러니 한 번 정해지면 약 2주간은 nBits에 변동이 없습니다(블록 생성 주기가 평균 10분이므로).

결국 위 ①②③ 3가지는 사실상 고정된 상수입니다. 마이너가 하는 것은 나머지 3가지 요소, 그러니까 ④Nonce, ⑤타임스탬프, ⑥Merkle root 해시값을 이리저리 조정해가면서 자기 블록의 SHA-256 해시값이 난이도 타겟상 조건을 만족시키도록 만드는 겁니다. 마이너가 조정하는 3가지 요소에 대해서 먼저 간략히 살펴본 후, nBits에 대해서 설명해보도록 하겠습니다.

④Nonce는 애초부터 brute-force로 조건을 만족시키는 해시값을 찾기 위해 0부터 하나씩 올려가면서 시도해보라고 주는 부분입니다. 그런데 용량이 4바이트(32비트)에 불과하기 때문에(즉 4,294,967,296개의 값만 존재함), 지금과 같은 난이도에서는 가능한 모든 Nonce 값을 다 돌려보더라도(ASIC 마이너 한대가 0.0003초만에 할 수 있는 연산), 조건을 충족시키는 값이 나올 가능성이 거의 없습니다. 그러니까 다른 요소들도 계속 바꿔가면서 해시값을 찾아나가게 됩니다.

⑤타임스탬프의 경우 별 의미는 없고 그냥 마이너가 자기가 해싱을 시작한 시간이라고 보고한 유닉스 시간인데(따로 검증해 보는 것이 아님), 제약조건은 딱 2개밖에 없습니다. 하나는 이전 11개 블록의 타임스탬프 중간값보다는 커야지 네트워크에서 받아들여질 수 있다는 것이고, 다른 너무 먼 미래의 시간을 쓰면 노드들에게 받아들여지지 않을 수 있다는 겁니다. 그러니까 타임스탬프도 -위 두가지 조건을 만족시키는 범위 내에서- 해시값 찾기 용으로 적당히 바꿀 수 있습니다. 그리고 해시레이트가 딱 42.94억/초 짜리 컴퓨터라면 그냥 매 초마다 새로운 타임스탬프를 이용해서 모든 Nonce를 돌려보면 딱 맞습니다. 하지만 컴퓨터가 압도적으로 빠르면(e.g. ASIC은 모든 Nonce의 계산을 0.0003초만에 끝냄) 타임스탬프를 너무 자주 바꿔야 하는데, 그러다 보면 위 제약조건에서 벗어날 수가 있으니 이쪽을 수정하기보다는 Merkle root쪽 해시값 변경을 사용합니다.

⑥Merkle root는 마이너가 블록에 포함시킬 모든 트랜잭션을 연쇄적으로 해싱하여 구해낸 최종적인 단일 해시값을 의미합니다. 사용자들이 비트코인 네트워크에 트랜잭션을 브로드캐스팅하면 이것들은 마이너에 의해 블록에 포함되기 전까지는 계속 mempool을 떠돌게 되는데, 마이너는 이렇게 떠도는 트랜잭션 중에서 자기가 블록에 포함시키고 싶은 트랜잭션을 고를 수 있습니다(주로 수수료를 높게 쓴 트랜잭션부터 순서대로 집어갑니다). 이렇게 포함시키고 싶은 트랜잭션을 약 1MB어치 정도 모았으면(1MB 블록에는 보통 수천개의 트랜잭션이 포함됩니다), 우선 모든 트랜잭션을 SHA-256로 두바퀴씩 돌려서(더블 SHA-256) 각각 해시값을 구합니다(i.e. SHA256(SHA256(각 트랜잭션)) 같은 형태로). 그렇게 나온 해시값을 2개씩 묶어서 다시 더블 SHA-256 해시값을 구하고, 다시 그걸 2개씩 묶어서 더블 SHA-256 해시값을 구하는 것을 반복하다 보면 결국 1개의 해시가 남는데, 이게 Merkle root 해시입니다.

왜 이런 방식을 사용하냐면, 통짜 해시보다 더 효율이 좋기 때문입니다. 예를 들어서 어떤 1MB 블록에 기본형에 가까운 190바이트 근처의 트랜잭션이 총 4,000개 가량 포함되었는데, 특정한 트랜잭션이 이 블록에 포함되었는지 여부를 검증하고자 한다고 칩시다. Merkle tree 방식에서는 트랜잭션 4천개짜리 블록이라면 트리가 13단계로 구성되는데(2^12=4096이므로), 이걸 검증하는 사람은 4,000개의 트랜잭션 전체에 대해 해시값을 구해볼 필요가 없습니다. 본인이 검증해보고자 하는 트랜잭션이 Merkle root 바로 아래 단계에 있는 경우 딱 1회의 더블 SHA-256 해싱을 통해 블록에 포함되었다는 것을 검증할 수 있고, 트리의 맨 아랫 단계에 들어가 있는 경우에도 총 12라운드의 더블 SHA-256 해싱을 통해 검증이 가능하니까요. 그리고 이처럼 최악의 경우라 하더라도 이렇게 해싱해야 하는 총 원본메시지 용량이 1KB 미만이 됩니다. 전체를 묶어서 해시를 돌리는 경우 횟수는 한번이지만 원본 메시지 용량이 1MB라서 더 많은 연산을 필요로 하게 됩니다(아까 설명한 SHA-1의 동작원리를 고려해서, 원본 메시지의 크기에 따른 연산량을 생각해보세요).

아무튼 마이너들은 블록에 포함시키고 싶은 트랜잭션 자체를 계속 바꿔가며 새로운 Merkle root값을 뽑아낼 수도 있습니다만, 선별한 트랜잭션들을 그대로 둔 채 해시값만 바꿀 수도 있습니다. 블록 안에는 반드시 마이너가 생성한 Coinbase 트랜잭션(coinbase 트랜잭션은 블록리워드를 지급하는 트랜잭션인데, 마이너가 자기 지갑에 지급하도록 생성합니다)이 맨 먼저 들어가는데, 이 트랜잭션은 마이너가 생성하는 것이니까 다 돌렸는데도 안 나오면 새로 트랜잭션을 만들어서 새 Coinbase 트랜잭션을 포함시킨 버전으로 Merkle root 해시값을 뽑으면 해시값이 바뀌게 됩니다.

이제 구체적으로 찾아야 하는 해시값의 조건을 설명하기 위해 nBits로 돌아가보겠습니다. 마이너들이 저렇게 변수들을 바꿔가며 찾으려고 하는 것은 [특정한 값]과 같거나 낮은 값의 해시값이 도출되는 원본 메시지(=블록헤더)입니다. 원본 메시지의 증감에 따른 해시값의 변화를 미리 예측할 방법이 없으니 무작위로 하나하나 다 바꿔가면서 찾는 것이죠. 여기서 이 [특정한 값]을 표현하는 것이 바로 nBits입니다.

예를 들어서 50만번째 블록을 보면(Block Height 499,999), 블록(헤더)의 해시값이 0000000000000000007962066dcd6675830883516bcf40047d42740a85eb2919입니다. 그리고 앞블록과 뒷블록도 이와 동일하게 블록 해시값이 18개의 0으로 시작합니다. 하나만 있어도 이상한 일인데, 3개가 연속으로 18개의 0으로 시작한다는 것은 자연적으로 일어날 가능성이 사실상 없습니다. 그럼 왜 이런 일이 일어났는가? nBits가 찾으라고 지시한 해시값이 (대충) 18개의 0으로 시작하는 해시값이기 때문입니다.

그런데 앞서도 언급했지만 nBits는 4바이트(32비트)입니다. 해시값은 256비트이므로, 목표가 되는 값을 32비트의 공간에 그대로 적을 수가 없습니다. 그래서 일정한 방식으로 인코딩한 값을 사용하며, 이것이 바로 nBits입니다. 구체적으로 위 50만번째 블록이 생성되던 당시의 nBits 값인 0x18009645를 예시로 하여 설명하겠습니다. 우선 nBits를 아래와 같이 두개로 쪼갭니다.

0x18
0x009645

이건 0x18바이트(10진수로는 24바이트, 0x18=24)의 숫자 중에서, 앞이 0x009645로 시작하는 수를 의미합니다. 즉 풀어쓰면

0x[009645]000000000000000000000000000000000000000000 (=48자리, 24바이트)보다 작은 값을 구하는 것이 목표라는 말이고,

가장 큰 256비트 수(2^256-1)와 나란히 놓고 앞에 0을 집어넣어보면 이런 모양이 됩니다.

0x0000000000000000009645000000000000000000000000000000000000000000
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF (=2^256-1)

그럼 이제 실제로 block height 499995-500005(모두 동일한 nBits 0x18009645를 공유하는 블록들)의 해시값과 위 숫자를 비교해봅시다.

0x0000000000000000009645000000000000000000000000000000000000000000 ← nBits에서 지정된 난이도 타겟

0x0000000000000000003a82a2e245d6a8be618045510e65eadce1eaa517a90845 ← Block Height 499,995의 해시값
0x00000000000000000001ddd1c1a1855e09ff84e2d2b5987cf165fcfbe8054bb3 ← Block Height 499,996의 해시값
0x00000000000000000034749458f656a299e5ec743fd856e5d2c3f1d51dd21dc6 ← Block Height 499,997의 해시값
0x00000000000000000085d465bc594e9d6d5664dfedb8dea5eedb55871248f3ec ← Block Height 499,998의 해시값
0x0000000000000000007962066dcd6675830883516bcf40047d42740a85eb2919 ← Block Height 499,999의 해시값
0x00000000000000000024fb37364cbf81fd49cc2d51c09c75c35433c3a1945d04 ← Block Height 500,000의 해시값
0x0000000000000000005c9959b3216f8640f94ec96edea69fe12ad7dee8b74e92 ← Block Height 500,001의 해시값
0x000000000000000000877d93d1412ca671750152ba0862db95f073b82c04b191 ← Block Height 500,002의 해시값
0x0000000000000000005467c7a728a3dcb17080d5fdca330043d51e298374f30e ← Block Height 500,003의 해시값
0x0000000000000000005d4da5924742e6d6372745f15c39feb05cf9b2e49e646d ← Block Height 500,004의 해시값
0x0000000000000000007150115460d0e92093aa937a913072d768f8136e289c2d ← Block Height 500,005의 해시값

즉 네트워크 해시레이트가 낮아지면 2016블록 주기로 점차 허용되는 값의 크기를 키워서(즉 정답이 나올 확률을 높게 만들어서) 그렇게 낮은 해시레이트에서도 10분에 한번쯤 답이 나오게 만들고, 네트워크 해시레이트가 높아지면 점차 허용되는 값의 크기를 낮춰서 10분에 한번쯤 답이 나오게 만드는 것이 nBits의 역할입니다. 풀버전 SHA-256을 깨는건 해시값으로 0x0이 64개가 나오는 원본 메시지를 찾으라는 것과 같은 의미입니다.


  B. 기타 사용례

여기까지 써놓고 보니 이렇게 두서 없이 작성하는 것에 회의가 느껴지네요. 나머지 SHA-256이 사용되는 부분들과 RIPEMD-160 관련 이야기는 나중에 파트별로 나눠서 쓸 때 기재하도록 하겠습니다.



IV. 비트코인 지갑주소의 생성방식

1. 서

원래는 이 부분에서 서명이 어떤식으로 동작하는가 따위를 다루려고 했습니다만, II. 에서 언급한 바와 같이 이런 내용은 이 후 새로 작성할 글에서 트랜잭션의 전체적인 개요를 다루면서 함께 설명하는 것이 나을 것 같아서 때려치우고 그냥 비트코인 지갑주소 생성방식이나 적어보도록 하겠습니다.


2. Private Key

  A. 생성방식

  비트코인의 private key는 무작위 256bit 숫자를 바탕으로 생성됩니다. 256bit는 2진수 256자리 숫자라는 뜻이고, 실제로는 16진수 형태의 키를 사용하기 때문에 64자리(2^256=16^64)입니다(byte로는 1 byte=8 bits, 256/8=32이므로, 32 bytes). 0 이상 2^256 이하의 모든 숫자가 허용되는 것은 아니지만 대부분의 숫자가 허용되는 ECDSA 키이며, 보다 구체적으로

0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001부터
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4140까지가 허용됩니다.

이런 Private Key는 무작위 수를 프로그램이 생성하는 방법으로 만들 수도 있고(초기 버전에서 사용하던 방식), 그냥 손으로 0과 1을 랜덤으로 256개 적어서 만들 수도 있고, 일정한 시드를 바탕으로 순차적으로 생성하도록 만들 수도 있는데(후기 버전에서 추가된 방식, BIP 0032), 마지막에 언급한 HD wallet은 별로 중요한 것이 아니니까 자세한 설명은 나중에 키 파트를 쓰게 되면 거기서 하겠습니다.


  B. Private Key ↔ Wallet Import Format

실제로 지갑 프로그램들에서 export/import 할 때 사용하는 키는 위와 같은 원형의 private key가 아니고, 그것을 일정한 방식으로 인코딩한 포맷(Wallet Import Format)입니다. 이러한 WIF는 다음과 같은 방식으로 생성됩니다.

우선 사용자가 가진 private key(랜덤 2^256 숫자) 앞에다가 2자리 버전넘버 80을 붙이고(, 뒤에다 2자리 압축플래그 01을 붙여서)(optional), extended key(A)를 만듭니다. 그 뒤에 A를 SHA256로 해싱하여 새로운 해시값(B)를 구하고, 그 해시값(B)를 다시 SHA256로 해싱하여 최종적인 해시(C)를 구합니다. 그 뒤 이 해시값(C)의 첫 8글자를 딴 뒤(D), D를 A 뒤에다 체크섬으로 붙여서 E를 만듭니다. 그 다음 이 E에다 Base58 encoding(대소문자 26x2개와 숫자 10개에서 헷갈리는 대문자 I와 소문자 l, 그리고 숫자 0과 대문자 O를 제외한 58개의 알파벳과 숫자만으로 표현하는 것)을 해주면, 일반적으로 지갑 프로그램 들에서 사용하는 Wallet Import Format(F)가 됩니다.

위 내용을 예시를 통해 다시 적어보겠습니다. 우선 10진수 1234를 private key로 쓰는 경우, 이것은 16진수로 4D2이고, 다음과 같은 모양입니다.
00000000000000000000000000000000000000000000000000000000000004D2

여기에 앞에 2자리 버전넘버 80을 붙이면 다음의 값이 나오고,
8000000000000000000000000000000000000000000000000000000000000004D2

뒤에다가 2자리 압축플래그를 붙이면(optional) 다음의 extended key(A)가 나옵니다.
8000000000000000000000000000000000000000000000000000000000000004D201

위 extended key(A)의 SHA256 해시값(B)은 다음과 같고,
59B7F043A57BC13AEE2DFF8AF9CFD99B24809863A62B25C2F9FE22BB043B8DBA

위 해시값(B)의 SHA256 해시값(C)은 다음과 같으며,
C4528359AFF45376A2676957C4056062EB5B352CA2C044BD133AEB5CEA128167

위 값의 앞 4바이트(C4528359)를 따서(D), extended key(A) 뒤에다 붙이면 다음과 같은 형태(E)가 됩니다.
8000000000000000000000000000000000000000000000000000000000000004D201C4528359

그 다음 이 값(E)을 Base58 encoding을 해주면, 다음과 같은 형태의 Wallet Import Format(F)이 완성됩니다.
KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFUHdsRju8r4

역순으로 Base58 decoding - 뒤 8글자 삭제 - 앞 2글자(+뒤 2글자 삭제, optional) 삭제를 하면 원본 private key가 됩니다. 왜 이런 방식을 쓰느냐? 짧아서 보기 좋은 것도 있긴 하지만, 주로 오타 등의 방지용입니다. 보다시피 체크섬이 있어서 오타나 복붙 실수를 하면 확인이 가능하거든요. 그 외에도 Compressed public key와 연계된 private key의 base 58 encoding은 메인넷의 경우 항상 L 혹은 K로 시작하고(테스트넷은 c) 항상 52글자이며, Uncompressed public key와 연계된 private key의 base 58 encoding은 메인넷의 경우 항상 5로 시작하고(테스트넷은 9) 항상 51글자라는 특징이 있습니다.

아무튼 위 WIF가 [dumpprivkey 지갑주소] 명령어를 쳤을 때 나오는 키입니다.


  C. Mini Private Key → Private Key

Mini Private Key는 더 짧은 형태의 Private Key입니다. Mini Private Key는 ①대문자 S로 시작하는, ②30글자의 Base58 코드 중, ③뒤에 물음표 [?]를 붙인 스트링을 SHA256로 해싱했을 때 첫 바이트가 [00]이 나오는 것을 의미하며, 이렇게 해싱되어 나온 값이 Private Key가 됩니다. 다시 말해 Mini Private Key로부터 Private Key를 추출하는 과정은 일방통행이기 때문에, Private Key를 먼저 만든 뒤 Mini Private Key를 생성할 수는 없고, 먼저 위 조건을 만족시키는 Mini Private Key를 찾은 후 Private Key를 생성하여 사용하는 형태가 됩니다.

Private Key 대비 길이가 약 58% 수준이니 그냥 암기를 통해서 보관할 때 까먹기 위협에 대해 좀 더 안전하고(51-52글자보다는 30글자를 까먹을 확률이 낮으니), 눈으로 제대로 입력하였는지 확인하기가 용이하며, QR이나 바코드 등으로 만들었을 때 동일 수준의 밀도나 error correction이라면 더 작아지기 때문에 더 작게 인쇄할 수 있다는 등의 장점이 있습니다만, 그렇게 널리 사용되지는 않습니다.


  D. Brian Wallet → Private Key

또 다른 변형으로는 Brain wallet이 있는데, 기본적으로는 IV.2.C.와 같으나 자기가 외우기 쉬운 문자열을 택해서 그걸 원본으로 사용한다는 점이 다릅니다. 예를 들어서 성경 New International Version (NIV)의 Luke 23:41은

We are punished justly, for we are getting what our deeds deserve. But this man has done nothing wrong.

인데, 위 스트링의 SHA256 해시값은 다음과 같습니다.

0DC181342B7F271DCE9DC37255EC154B6DB5DC281B09487C7B853EA5CE8CC495

이런 식으로 도출된 해시값을 그대로 Private Key로 쓰는 겁니다(위 Private Key와 compressed public key 페어의 WIF는 KwgT5maRmxAiXo6ksCPJew6srJz1DrkRwdwEiAnTKhMHjhKBi1Ek, 비트코인 주소는 1MSLFtcGqtDqXSs2LPahBjmY3iN7ABJmQV가 됩니다).

이 방식에는 문제점이 여러가지가 있는데, 우선 위와 같이 책에서 추출하는 경우 같은 책이라도 버전이나 판본에 따라서 내용이 달라지는 경우가 꽤 있습니다. 그런데 해시 함수는 내용이 아주 조금만 바뀌어도 결과물이 완전히 달라지기 때문에, 자기가 특정 책의 희귀 판본에서 문구를 추출하였는데, 추출한 위치의 내용이 향후 판본에서 변경되었고, 자기가 추출한 책을 보관하고 있지 않은 경우(게다가 정확한 판본을 모르는 경우), 추출 위치를 기억함에도 불구하고 회수를 못 하는 경우가 발생할 수 있습니다.

또 자기가 초기 해시값 도출 과정에서 오타를 냈었던 경우

통합규정 1.3 이용안내 인용

"Pgr은 '명문화된 삭제규정'이 반드시 필요하지 않은 분을 환영합니다.
법 없이도 사는 사람, 남에게 상처를 주지 않으면서 같이 이야기 나눌 수 있는 분이면 좋겠습니다."
stowaway
18/01/22 01:12
수정 아이콘
엄청난 프로젝트인데 글자 수 때문에 중간에 끊긴 것 같아 보입니다!
기대 됩니다.
18/01/22 01:19
수정 아이콘
이하 짤린 내용입니다.


, 나중에 그 오타를 기억할 수가 없기 때문에(인식하지 못했던 것이므로) 자기 key를 brute force로 찾아야 하는 상황이 발생하는데, 오타가 예상 범주를 완전히 벗어나는 형태의 것이었다면 brute force로도 찾지 못할 가능성이 꽤 있습니다. 그 외에도 간단하고 짧은 문구, 아주 널리 인용된 문구 등을 원본으로 사용한 경우, 블록체인상에 등장한 모든 주소에 대한 BTC 잔고를 DB화 한 다음, brute force로 잔고가 있는 비트코인 주소가 나올 때까지 무작위 문구를 돌려보는 방법에도 당할 수가 있습니다.

예시를 두 가지만 들어보죠. 먼저 King James Version (KJV)의 Luke 23:41를 보면, 애초에 원문 자체가 다음과 같이 완전히 다르고,

And we indeed justly; for we receive the due reward of our deeds: but this man hath done nothing amiss.

Private Key로 사용할 SHA256 해시값이 다음과 같이 나오며,

60CED792108707427F8119EB26F65766C7701CB7A4CE2B027ABD742177EDEF74

그럼 WIF는 KzTtk6ZmP639U8R4tvhz1eRLnFZdRLMQp6LJiMfHQWDJ18xa9nyF, 비트코인 주소는 11ybzNjjvQobR7HwMGryVw2L7UrtvLjNz가 됩니다.

오타 버전도 예시를 들어보면(맨 마지막 알파벳 g를 대문자 G로 바꾼 버전),

We are punished justly, for we are getting what our deeds deserve. But this man has done nothing wronG.

Private Key로 사용할 SHA 256 해시값이 다음과 같이 나오며,

8C03C8A3A5CC977783FDA0CC22B6DB2FA9C22FB5C0F26314CBD9A8F8D36303D1

그럼 WIF는 L1ut51L59gZMsqURhJJy4QnCzt7RYqZg5gkC5nKkAdGBDxr3i1qq, 비트코인 주소는 1BD8y6nCF7cpmFdknt24NRrwVc3jhBDtRy가 됩니다. 변형은 수 없이 많지만 생략하겠습니다.



3. Public Key

A. 개요

Public Key는 위 Private Key에서 도출됩니다. 비트코인의 경우 Private Key로부터 Public Key를 도출하기 위해서 secp256k1라는 ECDSA(Elliptic Curve타원곡선 기반의 Digital Signature Algorithm디지털 서명 알고리즘)를 사용합니다(자세한 내용은 <a href="http://www.secg.org/sec2-v2.pdf">링크</a> 참조). 9세 아동에게 타원곡선을 설명한다고 생각하니 앞이 깜깜합니다만, 최대한 단순화시켜서 계산 자체는 할 수 있도록 적어보겠습니다.

우선 타원곡선은 y^2=x^3+ax+b로 표현할 수 있으며, 그래프에 그려보면 x축을 기준으로 상하 대칭인 곡선이 됩니다. 무한한 수의 곡선이 존재할 수 있지만 비트코인이 사용하는 타원곡선은 y^2 = x^3 + 7 (유한체의 범위 2^256 - 2^32 - 977)으로 정의되는 값이고, 위 타원곡선 위에 있는 베이스포인트(G)와 Private Key를 곱한값을 Public Key로 합니다. 비트코인에서 사용되는 베이스포인트는

X 79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
Y 483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 이며(즉 모든 비트코인 Public Key는 이 값을 공유함),

G * Private Key라는 곱셈은 G+G+G+...라는 덧셈연산을 Private Key 숫자만큼 반복수행하는 것과 같습니다. Private Key가 1234라면 G+G+G..를 1234번 한 값이죠. 여기서 더하기라는 것은 사칙연산의 더하기와는 다릅니다. 아래에서 자세히 보도록하죠.

B. Private Key → Public Key

우선 기본적인 더하기를 보겠습니다. A+B=C로 표현되는 타원곡선 위의 점 3개가 있다고 칩시다. 이걸 그래프 형태에서 보면, 아래 그림(비록 x,y축이 없습니다만 적당히 상상해주세요)에서 x축 위에 있는 점 3개는 왼쪽부터 각 A, B, -C이 되고, x축 아래에 있는 점은 C가 됩니다. 그러니까 곡선위의 점 A에서 또 다른 곡선위의 점 B를 향해 직선을 그었을 때, 그 직선이 도달하는 또 다른 곡선위의 점은 -C이고, 그것의 X축 미러버전인 곡선위의 점이 C가 됩니다.

<img src="http://www.labs.hp.com/research/info_theory/images/curveplot.gif" width="222" height="301">

이걸 산수로 표현하면 다음과 같이 됩니다.

Cx(C의 x값) = ((Ay-By)/(Ax-Bx))^2 - (Ax + Bx)
Cy = ((Ay-By)/(Ax-Bx))(Ax - Cx) - Ay

이제 G+G=2G 형태를 보겠습니다. 그래프로 보면 곡선위의 점 G에서 <a href="https://upload.wikimedia.org/wikipedia/commons/7/7a/Graph_of_sliding_derivative_line.gif">탄젠트 라인</a>을 그려서, 그 라인과 만나는 타원곡선위의 점이 -2G이고, X축 미러버전인 타원곡선위의 점이 2G가 됩니다. 2G를 A라고 하면, 이걸 산수로 표현하면 다음과 같이 됩니다.

Ax = ((3(Gx)^2 + 1)/2Gx)^2 - 2Gx
Ay = ((3(Gx)^2 + 1)/2Gx)(Gx-Ax)-Gy

2G에서 다시 탄젠트라인을 그어서 만나는 점의 X축 미러버전을 찾으면 4G가 되고, 이걸 계속 반복하는 방식으로 Private Key * G를 구합니다. 이렇게 하면 2의 거듭제곱만 구할 수 있지 않은가 같은 생각이 드시는 분을 위해, Private Key가 2의 거듭제곱이 아닌 숫자일 때 계산하는 형태를 보여드리겠습니다. 예를 들어서 Private Key가 10진수 1234라면, 변환 과정을 눈으로 편하게 보려면 이걸 16진수와 2진수로 변환을 해야 합니다. 1234는 hex로 4D2, binary로 100 1101 0010입니다.

그럼 2진수를 다음과 같이 역순으로 펼쳐놓고, 옆에 2^(n-1) * G를 순서대로 적어줍니다(n은 1부터 시작).

0 - 2^(n-1) * G = 2^(1-1) * G = 2^0 * G = 1 * G = 1G
1 - 2^(2-1) * G = 2^1 * G = 2G
0 - 2^(3-1) * G = 4G
0 - 8G
1 - 16G
0 - 32G
1 - 64G
1 - 128G
0 - 256G
0 - 512G
1 - 1024G

이제 옆에 1이 있는 값들을 더하고, 그렇게 해서 나온 값을 다음 1이 있는 값과 더합니다. 위 예시에서는 2, 16, 64, 128, 1024G 옆에 1이 있는데, 그러면 아래와 같이 됩니다.

1G + 1G = 2G
2G + 16G = 18G
18G + 64G = 82G
82G + 128G = 210G
210G + 1024G = 1234G

위 예시에서 알 수 있다시피 숫자가 상당히 크더라도 그렇게 많은 연산을 필요로 하지 않습니다. 근데 이걸 역으로 되돌리려면 매우 많은 연산을 필요로 하게 됩니다. 그래서 Private Key에서 Public Key를 추출할 수는 있지만, 그 반대 방향은 어렵습니다.

위 예시는 눈으로 볼 수 있게 만들기 위해 일부러 아주 작은 숫자들을 사용한 것이고, 실제로는 Private Key가 원본 메시지 [SHA-1sTt]의 SHA-256 해시값인 0x7B9606D17DB0133BAF6CC1E0551F8CBB80C5CC30A78B839B2C5A306C0FC02997라면 (WIF 포맷으로 L1Mwpavk2rRMcwG4aFpPXKxMd94Lg9HBv6VXVDwdc372Esnn1nwv, 10진수로 55899554493801927162787837131230320679804227820265683610181220092466069252503), 위 과정을 통해서 도출된 Public Key는

x = 1F68AFB1683BD2C60D23DC2F07B9A0F21C93BAAB9F7A9FDB3573F1EDED2C5BF8
y = 0276E0BCCD8861739071F099CE602ADDF8A39FC75411477930424D86F4B302A6

이 됩니다.


4. Address

이건 실제로 사람들이 사용하는 주소인데, Public Key로부터 도출됩니다. 바로 위에 예시를 들었던 Public Key로 이어서 하겠습니다.

x = 1F68AFB1683BD2C60D23DC2F07B9A0F21C93BAAB9F7A9FDB3573F1EDED2C5BF8
y = 0276E0BCCD8861739071F099CE602ADDF8A39FC75411477930424D86F4B302A6

Public Key의 y값이 짝수면 x값 앞에 02를 붙이고, y값이 홀수인 경우에는 x값 앞에 03을 붙입니다. 위 예시의 경우 y값이 짝수이므로 다음과 같이 됩니다.
021F68AFB1683BD2C60D23DC2F07B9A0F21C93BAAB9F7A9FDB3573F1EDED2C5BF8

이걸로 SHA-256 해시를 돌리면 다음의 해시값이 나오고,
ABD7B985687893701664953C079B714388BD948C118AAC7B9C13589EC3AEF2ED

위 해시값을 RIPEMD-160 해시를 돌리면 다음의 해시값이 나옵니다.
A353DC0E2ED3B9CD06D00C0DEAC3DD81FA5DC553

그럼 앞에다 버전넘버 00을 붙이고,
00A353DC0E2ED3B9CD06D00C0DEAC3DD81FA5DC553

위 IV.2.B.에서 보인것과 똑같은 방식으로 더블 SHA-256를 돌려서 나온 값 C26065B1를 뒤에 붙이면 다음과 같이 됩니다.
00A353DC0E2ED3B9CD06D00C0DEAC3DD81FA5DC553C26065B1

이걸 최종적으로 Base58 encoding을 하면 다음의 비트코인 주소가 생성됩니다.
1FtbbwE4kskwizHS6AYNp5qmZ8cwkcX7Eg



V. 마치며

오늘은 그냥 맛보기로 암호와 관련된 것들을 아무거나 생각나는대로 써갈겼기 때문에 아주 두서가 없습니다만, 다음부터는 파트별로 나눠서 기재할 것이기에 어느 정도 짜임새가 있을 것이라고 생각합니다. 아마도 다음에는 트랜잭션 관련 글을 쓸텐데, 그걸 보시면 전체적인 동작 원리를 이해하는데 무리가 없을겁니다.
바카스
18/01/22 01:21
수정 아이콘
햐 스크랩감이다
절름발이이리
18/01/22 01:23
수정 아이콘
9세의 본인을 자랑하는 글
오클랜드에이스
18/01/22 01:26
수정 아이콘
아니 이건 뭐... 논문인가요

추천을 안할수가 없네요
La La Land
18/01/22 01:30
수정 아이콘
아 나이먹기 힘드네
larrabee
18/01/22 01:32
수정 아이콘
맛..맛보기요????
엑스밴드
18/01/22 01:32
수정 아이콘
9세요???
18/01/22 01:33
수정 아이콘
9세가 보기엔 내용이 덜덜...
Scarlet Pleasure
18/01/22 01:41
수정 아이콘
맛 보기요?
18/01/22 01:43
수정 아이콘
논문 미리 감사합니다
시스코인
18/01/22 01:44
수정 아이콘
9살이 아니라 내가 90살을 먹어도 이해 못할거 같다.. 문과라서 문송합니다..
루트에리노
18/01/22 01:47
수정 아이콘
저 이과인데도 안됩니다! 문송 안하셔도 됩니다!
18/01/22 01:45
수정 아이콘
저기 그 9살이 혹시 폰 노이만 혹은 아인슈타인인가요?
루트에리노
18/01/22 01:47
수정 아이콘
와 이게 되는 9세라니 나는 9살때 구구단 외웠는데...
18/01/22 01:47
수정 아이콘
읽지는 않았지만 추천!
kartagra
18/01/22 01:53
수정 아이콘
맛보기라기엔 너무 강력하네요 크크크
새벽포도
18/01/22 02:11
수정 아이콘
9세 수준이 아니라 9세주 수준 아닌가요?
18/01/22 02:25
수정 아이콘
저 개발자 출신인데, 뭔말인지 모르겠습니다.
수지느
18/01/22 02:31
수정 아이콘
크크크크 9세가아니라 프로그래밍 9년차 아닌가요
진짜 쉬운건줄알았는데 내리다보니 허미..완전본격적 크크크
사악군
18/01/22 02:33
수정 아이콘
9살에 곰을 잡은 마사
연어초밥
18/01/22 02:33
수정 아이콘
진심으로 9세 아동을 타겟으로 삼았다는 말씀은 아니죠?? 프로그래머 9년차를 달리 부르는 업계 용어인건가..
Steinman
18/01/22 02:51
수정 아이콘
9세에 사서삼경을 뗐다던 과거 성현들이 떠오르네요....
La La Land
18/01/22 03:03
수정 아이콘
당신이 한국의 사토시 나카모토인 것입니까....?
이밤이저물기전에
18/01/22 03:24
수정 아이콘
추천 꾹 누르고 갑니다.
집에 가서 통독 해야되겠네요
18/01/22 03:35
수정 아이콘
감사히 읽겠습니당.
현은령
18/01/22 04:09
수정 아이콘
읽으면서 9살만 머리에 맴돌았어요
18/01/22 04:17
수정 아이콘
(수정됨) ..........9세?

진지를 조금 먹고 비판을 해보자면,
일반인들이 이해하기 힘든 전문용어와
수식의 남발로 인해 접근성이 형편없어
제목의 [흔한][맛보기]라는 느낌이 전혀
들지 않습니다.

개인적인 생각일 수 있지만,
비트코인을 모르는 사람
(게다가 그게 초장에 말한 9세???)
에게는 이 글은 거의 가치가 없습니다.

차라리 유게에서 보일만한,
[대한민국의 흔한 9세가 이해할만한
비트코인 설명글.txt]
이라는 제목에
어울리는 글입니다.

9세를 앞에 언급하신걸 보면
개그를 의도한 글일 가능성도 있는데
그렇다면 성공작이라고 할 수 있고
괜히 진지먹어서 글쓴 분께 죄송합니다만,

그게 아니라 정말로 진지하게
9세(정확하게는 9세의 본인)
를 대상으로 해서 쓰신거라면
자신의 지식자랑 및 우월감 표출이라고
생각되어지는 편이 강해보여서,
이번편으로 마무리 짓는게 나아보입니다.
18/01/22 05:51
수정 아이콘
9세란 말에 딴지걸기에는 글 자체가 너무 좋고 유익합니다. 어려운 내용을 정말 가능한 한 쉽게 풀어쓰신 데에 큰 의미가 있다 보고, 제게도 큰 도움이 되었습니다.
이런 글이 씌어지는 것에 감사는 못할망정 자기자랑이니 어쩌니 하는 말로 딴지 걸고 글쓴분이 후속편 쓰기 싫게 만드려면 그냥 침묵하고 지나가시는게 세상에 더 나은 겻과를 가져다줄 것 같습니다.
18/01/22 06:27
수정 아이콘
그렇군요, 죄송합니다.
짧은 식견으로 딴지를 걸었네요.
18/01/22 07:05
수정 아이콘
진심인건가요? 아니면 옛날에 있던 원숭이어쩌고 풍자글인가요? 제가 그때 피쟐을 잘 안봐서...
18/01/22 07:27
수정 아이콘
전 진심으로 썼습니다.
진짜 주문같이 보이고
뭔 소린지 하나도 모르겠거든요.
제 짧은 식견이니 글쓴분께 사과드립니다.
18/01/22 07:34
수정 아이콘
가치가 없느니 마느니 하는시간에 그냥 지나가면 될일인데 이글을 보고 이런 댓글자체글 왜 다는지 모르겠네요. 누구보다 유익하게 봤습니다. 제가 본 최악의 댓글이네요.
18/01/22 07:48
수정 아이콘
제가 글을 잘못 판단하고
비판글을 쓴건 사과해야되는 일이지만,
가치가 없다고 생각되서 글을 쓰는것 자체는, 사람마다 판단이 다른거라고 생각하는데요.
18/01/22 07:54
수정 아이콘
왜 다는지 모르겠다고 했으며, 제 판단으로는 최악이라서 최악이라고 달았는데요.
18/01/22 07:57
수정 아이콘
넹, 알겠습니다.
피식인
18/01/22 07:21
수정 아이콘
글 내용은 굉장히 유익하긴 합니다. 9세 운운하는건 좀 오버스럽지만, 쉽게 풀어 쓰려고 예시도 단계별로 잘 짚어주고 있어요..
18/01/22 07:29
수정 아이콘
그렇군요,
글쓴분의 노력을 몰라보고
폄하했네요, 죄송합니다.
와일드볼트
18/01/22 08:10
수정 아이콘
저번에 원숭이도 할 수 있는~ 글 보고 이거 못하면 원숭이도 아니냐면서 엄청 어그로 끌면서 날리쳐서 결국 글쓴이 탈퇴하게 만들던 어그로랑 하는 행동이 완전 동일하시네요.

자신의 지식자랑 및 우월감 표출?
그냥 나는 생각하며 읽기는 싫지만 내가 대충보고 모르는건 상대가 잘난척 하는거야! 라고 생각하시는거는 아니고요?
내용도 그냥 제목에 비트코인이랑 내용에 비트코인에서 사용되는 예시가 들어갔을 뿐
사실상 암호학이나 소프트웨어 보안 관련 배우면 맨처음 배우는 내용들이고, 학부생 1~2학년이면 충분히 이해할만한 내용의 지식을
굉장히 자세하고 정성들여 쓴 글인데 거기다 대뜸 [[자신의 지식자랑 및 우월감 표출이라고 생각되어지는 편이 강해보여서, 이번편으로 마무리 짓는게 나아보입니다.]] 같은 소리를 하시는건 정말 최악이네요.

덧글에 언급하신걸 보시니 본인이 쓰신게 무슨 대단한 비판글로 착각하고 계신가 보신데
그냥 저열한 악플 그이상 그이하도 아닙니다.
그리고 타인에게 멋대로 가치가 있니 없느니 글 쓰는게 자유라면 제가 당장 렌야님 글을 전부 가치가 없으니 앞으로 평생 PGR에 글 쓰지 말라고 해도 그건 사람마다 판단이 다른거니 함부로 해도 되는 말인가요? 말끝에 -입니다. 붙인다고 그게 최소한의 예의를 같춘 말이 되는건 아닙니다.
아침부터 눈살 찌푸려지게 만드는 악플이네요.
18/01/22 08:20
수정 아이콘
원숭이 관련글은 제가 안봐서 모르겠습니다만,
전 학부생이 아니라
9세도 이해할만한 글이라길래
읽어봤더니 어려운글만 잔뜩있고
뭔소린지 하나도 몰라서
저렇게 느끼고 리플을 썼는데
제 식견이 부족한 탓이죠.
제가 무슨 대단한 비판글이라고는 생각 안하고요,
저열한 악플이라고 생각하신다면
그렇게 느끼신 글을 끄적거린건 죄송합니다.
사악군
18/01/22 08:36
수정 아이콘
9살드립은 농담이죠..
18/01/22 10:11
수정 아이콘
그런것 같네요.
이밤이저물기전에
18/01/22 08:43
수정 아이콘
지식 공유는 본인 지식수준에 대해 자격지심이 있는 사람에게 불편을 끼치니 애당초 하질 말아야 한다!!
진심봇
18/01/22 17:39
수정 아이콘
(수정됨) 지금 이 글은 제가 본 수십,수백개이상의 SHA-256 관련 글중에서 가장 뛰어난 글입니다.
보통 쉽게 설명한 글은 틀린점 혹은 지나치게 생략한 부분이 많은데 이 글은 그러한 부분이 없으면서도 비교적 굉장히 쉽게 적혀져있습니다.
순서대로 기본 암호학부터 전개하면서 과정과 흐름이 나와있고 그 각각은 구체적인 예시와 사례를 들어 이해하는걸 돕고 있습니다.

문제가 된다면 독자의 기대치는 비트코인은 이러한 전망,기술이 좋습니다. 등의 내용을 기대하셨다는 것인데, 그건 글쓴이 문제가 아니지요, 옛날부터 비트코인 설명문이라면 이러한 프로그래밍 글을 뜻하는게 맞고요.

저도 관련 글을 많이 찾아보았으니 감히 단언하자면, 이 글은 현존 관련글중에 틀린점이 없으면서도 가장 쉬운 글입니다.
렌야님 댓글은 예의가 아닌 댓글로 보입니다. 글쓸때마다 하나하나 포인트를 과몰입까지 고려해서 할 순 없습니다.
그러면 이세상 모든 무기랑 연관된 단어는 사용이 불가능하고, 심지어 자연물도, 혹은 모든것이 과몰입하면 사람죽은일과 관련있을지 모르는데 못쓰겠지요, 과한 비판입니다.
주파수
18/01/22 18:43
수정 아이콘
본문을 망치는 어그로네요.
18/01/22 08:11
수정 아이콘
일단 추천 박고 퇴근하면 천천히 읽어보겠습니다
18/01/22 08:29
수정 아이콘
제 식견이 부족한 탓에 정성들인 글쓴님의 글에
다른분들이 불쾌해 하실만한 리플을 달았네요.
글쓴님과 제 리플을 보고 안좋은 감정을 느끼신
분들께는 사과의 말씀을 드립니다.
일단 본 리플은 놔두겠습니다,
글에 대한 책임은 져야할테니.
붉은밭
18/01/22 09:20
수정 아이콘
위로(?).. 를 드리자면 저도 처음에 렌야님과 비슷하게 생각했습니다.
리플에서 많은 분들이 '이글은 정성이 담긴 좋은 글이다'라고 해서 그렇구나~ 하고 알았지
나름 각잡고 정독하는데도 무슨소린지 하나도 모르겠더군요.
저처럼 프로그래밍 쪽으로 문외한인 사람은 다 비슷하지 않을까 싶은데..
물론 제가 이해가 안된다고해서 좋은글이 안좋은 글이 되는건 아닙니다만
18/01/22 10:13
수정 아이콘
위로 감사합니다.
말씀하신대로 이해가 안된다고해서
좋은글을 멋대로 판단하고 비방했으니
욕먹는거야 당연한거라고 생각합니다.
프뤼륑뤼륑
18/01/22 09:46
수정 아이콘
글 자체가 맥락없이 쓰인건 맞아요. 9살짜리도 알아들을 수 있는 가상화폐 얘기를 한다고 해서 클릭했는데 프로그래밍 전문용어가 주르륵 나오고, 왜 그런 용어들이 나오는지의 맥락도 설명이 없으니 기분 나쁠 수 있죠. 지적 우월감이라는 표현이 백퍼센트 틀린건 아니라고 생각해요.
18/01/22 10:15
수정 아이콘
(수정됨) 사실 그냥 지나칠수도 있었지만
기분 나쁜 느낌도 살짝 있었습니다.
(제 맘대로 글쓴님이 의도안한
지적우월감을 느낀거니까)
하지만 잘 알지도 못하면서
저렇게 글을 쓴 건
명백한 제 잘못이니깐요.
순둥이
18/01/22 09:13
수정 아이콘
전공자라 다 아는 내용이지망 너무 테크니컬하게 쓰신게 아닌가 싶네요

뭉뜽그려 쓰면 또 그것대로 문제가 있으려나 흐흐
18/01/22 10:12
수정 아이콘
쉽다면서요 ......
18/01/22 10:28
수정 아이콘
스크랩해서 읽어봐야겠네요
감사합니다
법대로
18/01/22 10:57
수정 아이콘
감사합니다!
-안군-
18/01/22 10:59
수정 아이콘
근데, 너무 어마무시한 프로젝트가 되어버린 듯한 느낌적인 느낌이...;;
해쉬랑 개인키/공개키 설명까지만 했는데도 이 정도면... 그 이후는 어떻게 감당하시려고요;;
아직 설명해야 할 내용이 산더미인데...

저도 암호화폐에 대한 기술적인 내용을 간추려서 글을 하나 써 볼까 했는데, 이 글을 보니 마음이 쪼그라듭니다. ㅠㅠ
아... 그냥 수박 겉핥기 식으로 썼다가는 엄청난 태클에 시달리겠구나... 싶어서;;
네오크로우
18/01/22 11:21
수정 아이콘
헤헤... 나만 이해 못하는 글이 아니었구나...
ㅠ.ㅠ
차라리꽉눌러붙을
18/01/22 12:00
수정 아이콘
여윽시 피지알이다...이런 거 찾아 헤맸는데...일반인 대상으로 쓰여진 글은 없었어서...
감사히 잘 보겠습니다!!!!!!!!!!!!!!!
소린이
18/01/22 12:05
수정 아이콘
엄마 이거 뭐야 무서워
18/01/22 12:08
수정 아이콘
(수정됨) 글 잘 읽었습니다.
그런데, 대부분 어려운 내용을 직관적으로 설명하기는 어렵고, 그렇게 해줘봐야 대부분의 댓글을 보면 알겠지만, 딴지 걸리기 쉽상입니다.
그렇다고, 잘못 생각하고 있는 타인을 시간쓰고 에너지쓰고 고생해서 가르쳐줘야 할 아무런 동기도 없지요.
제가 수고스럽게 글을 쓰지도 않고, 댓글 논쟁에 참여도 대꾸도 않는 이유입니다.
그러니, 누군가 이런 글을 노력해서 풀어서 친절히 밥을 떠먹야 준다면 그걸 잘하고 못하고를 떠나서 감사해야 한다고 생각합니다.
참고로, 잘 이해안되면, 그냥 위키 찾아서 읽으시면 됩니다.
다만, 글 내용을 계산보다는 이론적 배경과 그 의미 설명을 했다면 더 좋았을것 같습니다. 그리고, 비트코인이 왜 동작할수 있는건지, 이전과 왜 차이나는지 등이요.. 사실 이거 잘 설명하기 어려운거지만요.
bemanner
18/01/22 12:13
수정 아이콘
앞부분은 다른 서적에서 본 부분이기도 해서 그냥저냥 보는데
해쉬 함수의 구체적인 예시 부분 부터는 필기하면서 봐야 따라가겠네요
좋은 글 감사합니다.
18/01/22 13:02
수정 아이콘
재미있겠네요! 시리즈 기대합니다.
18/01/22 13:04
수정 아이콘
솔직히 말해서 9세 드립은 의도와는 달리 영 망한 드립이라고 생각되기는 합니다...

그러나 서두에 써 놓은 네 줄짜리 드립이 망했다고 그걸로 그 아래 전부를 평가하면 안 되는 거겠지요.

대가일수록 어려운 것도 쉽게 설명한다는 말이 상식처럼 통합니다만, 맞는 말이기는 하지만 대가들도 무슨 마법사는 아닙니다. 진짜로 어려운 걸 9세 수준으로 낮춰서 설명해낼 수는 없어요. 파인만의 빨간책이 쉽게 풀어 설명하기 분야의 최고봉을 찍는 책이라고들 하는데, 사실 이 책도 대학생은 되어야 이해할 수 있는 책이지요.

제 깜냥은 해시 설명하는 데까지였고 즐겁고 감사히 읽었습니다. 그 아래부터는 과감히 탈주하기로...
루트에리노
18/01/22 14:51
수정 아이콘
대가들일수록 어려운거 어렵게 설명하죠. 어 이걸 왜모르지? 하면서
Cazorla 19
18/01/22 13:08
수정 아이콘
흑흑
아몰랑 가즈아..
cadenza79
18/01/22 14:10
수정 아이콘
와... 입이 벌어집니다...
18/01/22 14:58
수정 아이콘
음...일단 시리즈가 진행되어야 이 글도 이해를 할 수가 있을 것 같습니다.

얼른 다음편 가즈아아ㅏㅏㅏㅏㅏ
보석상
18/01/22 18:29
수정 아이콘
엄청나네요 잘봤습니다
근성까지도 대단하네요 이걸 일일히 쓸 생각을 하다니
다음 글도 너무 기대됩니다
주파수
18/01/22 18:45
수정 아이콘
글쓴분 댓글 항상 잘 읽고 있습니다.
제가 느끼기에 항상 객관적이고 사실적이고 정확한 정보 전달을 하시기에 매우 인상깊었습니다.
브라이언
18/01/23 07:41
수정 아이콘
9세 드립은 빼는게 낫겠네요
전혀 재미도 없고, 이해못하는 사람들에게 약올리는거 같네요
18/01/23 11:24
수정 아이콘
댓글로 올리신 뒷부분이 글자수 제한 때문에 짤린 게 안타까울 뿐....

전에 중학생을 위한 상대성이론인가 읽었을 때가 생각나네요....

물론 저는 중학생 수준에도 9세 수준에도 한참 못 미치는 것 같지만요...ㅠㅠ

그래도 도전! 외치고 이 글을 교본 삼아 공부 한 번 해보려고요.

좋은 글 감사합니다.
목록 삭게로! 맨위로
번호 제목 이름 날짜 조회 추천
공지 [공지]2024년 4월 총선을 앞두고 선거게시판을 오픈합니다 → 오픈완료 [53] jjohny=쿠마 24/03/09 14693 6
공지 [공지] 정치카테고리 운영 규칙을 변경합니다. [허들 적용 완료] [126] 오호 20/12/30 247758 0
공지 자유게시판 글 작성시의 표현 사용에 대해 다시 공지드립니다. [16] empty 19/02/25 324299 8
공지 [필독] 성인 정보를 포함하는 글에 대한 공지입니다 [51] OrBef 16/05/03 447372 28
공지 통합 규정(2019.11.8. 개정) [2] jjohny=쿠마 19/11/08 317026 3
101196 웹소설 추천 : 천재흑마법사 (완결. 오늘!) [9] 맛있는사이다774 24/03/28 774 0
101195 도둑질한 아이 사진 게시한 무인점포 점주 벌금형 [14] VictoryFood1928 24/03/28 1928 5
101194 시리즈 웹툰 "겜바바" 소개 [36] 겨울삼각형3128 24/03/28 3128 2
101193 <악은 존재하지 않는다> - 마침표와 물음표 사이.(노스포) [4] aDayInTheLife2951 24/03/28 2951 3
101192 고질라 x 콩 후기(노스포) [21] OcularImplants4157 24/03/28 4157 2
101191 미디어물의 PC주의에 대해 어떻게 생각하세요? [80] 프뤼륑뤼륑7528 24/03/27 7528 3
101190 버스 매니아도 고개를 저을 대륙횡단 버스노선 [54] Dresden10317 24/03/27 10317 3
101188 미국 볼티모어 다리 붕괴 [17] Leeka10052 24/03/26 10052 0
101187 Farewell Queen of the Sky! 아시아나항공 보잉 747-400(HL7428) OZ712 탑승 썰 [4] 쓸때없이힘만듬3599 24/03/26 3599 5
101186 [스포없음] 넷플릭스 신작 삼체(Three Body Problem)를 보았습니다. [48] 록타이트8028 24/03/26 8028 10
101185 시흥의 열두 딸들 - 아낌없이 주는 시흥의 역사 (5) [3] 계층방정3101 24/03/26 3101 7
101184 [웹소설] '탐관오리가 상태창을 숨김' 추천 [56] 사람되고싶다6691 24/03/26 6691 19
101183 진짜 역대급으로 박 터지는 다음 분기(4월~) 애니들 [58] 대장햄토리6317 24/03/25 6317 2
101182 '브로콜리 너마저'와 기억의 미화. [9] aDayInTheLife3920 24/03/25 3920 5
101181 탕수육 부먹파, 찍먹파의 성격을 통계 분석해 보았습니다. [51] 인생을살아주세요4926 24/03/25 4926 68
101179 한국,중국 마트 물가 비교 [49] 불쌍한오빠6416 24/03/25 6416 7
101177 맥주의 배신? [28] 지그제프8290 24/03/24 8290 2
101175 [스포있음] 천만 돌파 기념 천만관객에 안들어가는 파묘 관객의 후기 [17] Dončić5926 24/03/24 5926 7
목록 이전 다음
댓글

+ : 최근 1시간내에 달린 댓글
+ : 최근 2시간내에 달린 댓글
맨 위로