Wenn man sich mit Ethereum beschäftigt, stößt man unweigerlich auf viele neue Begriffe. Es stellen sich z. B die Fragen
- Was ist ein Wallet?
- Was ist eine Mnemonic?
- Wofür brauche ich einen privaten Schlüssel?
- Und was ist eine Ethereum Adresse?
Wenn man die grundlegenden Begriffe geklärt hat und selbst ein Wallet in JavaScript programmieren möchte, stellt sich vor allem die Frage, wie man von einer Mnemonic zu einem privaten Schlüssel und einer Ethereum-Adresse kommt. Dieser Artikel ist der erste Teil einer Artikelreihe, welche sich intensiv mit dem Thema Ethereum-Wallet beschäftigt.
In diesem Artikel werden deswegen zuerst einmal kurz die wichtigsten Begriffe erklärt. Anschließend schauen wir uns den Weg oder besser gesagt Umweg an, über den wir von einer Mnemonic zu einem privaten Schlüssel und einer Ethereum-Adresse gelangen. Schlussendlich werden wir noch die verwendeten Standards und JavaScript-Bibliotheken, die zur Implementierung eines Wallets nötig sind, ansprechen. Zunächst werden wir im Folgenden einige Begriffe erklären.
Wallet
Beginnen wir mit dem Oberbegriff „Wallet“ im Kontext von Ethereum. Da ein „Wallet“ neben dem Verwalten von privaten Schlüsseln und Ethereum-Adressen in vielen Fällen auch in der Lage ist mit der Ethereum Blockchain zu interagieren, stößt man häufig auf unterschiedliche und uneinheitliche Definitionen im Internet.
Im Rahmen dieses Artikels verstehen wir unter den Begriff „Wallet“ eine Software, die zur Generierung, Wiederherstellung und Verwaltung von privaten Schlüsseln und Ethereum-Adressen anhand einer Mnemonic verwendet wird.
In diesem Kontext ist es wichtig zu erwähnen, dass Wallets niemals Ether oder andere Werte enthalten, sondern lediglich die privaten Schlüssel, welche einen Zugriff hierauf mittels signierter Transaktionen auf der Ethereum-Blockchain erlauben. Genau genommen handelt es sich bei den in diesem Artikel erklärten „Wallet“ um ein hierarchische deterministische Wallet, kurz auch HD-Wallet. Was sich hinter diesem Begriff genau verbirgt, werden wir später noch sehen.
Mnemonic
Der Begriff „Mnemonic“ bedeutet übersetzt „Gedächtnisstütze“ und stellt im Kontext von Ethereum 12 – 24 zufällig generierte Wörter dar, mit denen eine nahezu beliebige Anzahl an privaten Schlüsseln mit den dazugehörigen Ethereum-Adressen generiert und wiederhergestellt werden können. Der Vorteil einer Mnemonic liegt unter anderem darin, dass man sich nicht eine Vielzahl an privaten Schlüssel sichern und merken muss.
Private Key
Kann als privater Schlüssel übersetzt werden. Private Schlüssel können über Umwege aus einer Mnemonic abgeleitet werden und dienen im Rahmen von Ethereum unter anderem zur Signierung von Transaktionen, um bspw. Ether auf der Blockchain auf eine andere Adresse zu transferieren.
Ethereum-Adresse
In diesem Artikel beziehen wir uns auf die Ethereum-Adressen, die ein Schlüsselpaar (also einen privaten und einen öffentlichen Schlüssel, auf den wir später noch zu sprechen kommen) repräsentieren und nicht auf Ethereum-Adressen die zu einem auf der Blockchain bereitgestellten Smart Contract führen. In unserem Fall gehört eine Ethereum-Adresse deshalb zu einem privaten Schlüssel und nicht zu einem Smart Contract und kann sehr stark vereinfacht ausgedrückt mit einer Kontonummer verglichen werden, an die im Ethereum-Netzwerk bspw. Ether gesandt werden kann.
Von einer Mnemonic zu einer Ethereum-Adresse
Schauen wir uns nun einmal an, wie man von einer Mnemonic zu einer Ethereum-Adresse kommt. Auf die genauen technischen Details werden wir während der Implementierung unseres Wallets in späteren Artikeln noch genauer eingehen.
Mnemonic
Begonnen wird immer mit einer Mnemonic, was konkret heißt, dass 12 – 24 meist englische Wörter, je nach verwendeter Entropie, zufällig generiert werden.
Diese 12 – 24 Wörter haben insbesondere den Vorteil, dass sich bei der Übertragung dieser Wörter, bspw. auf ein Blatt Papier, weniger Fehler einschleichen können. Selbst wenn sich ein Fehler bei der Übertragung einschleicht, kann man diesen in den meisten Fällen schnell erkennen da eines der Wörter dann z. B. falsch geschrieben wäre.
Seed
Aus der vorherigen Mnemonic wird mittels einer genormten so genannten „Password-Based Key Derivation Function 2“ ein 512 Bit Seed erzeugt. Dies entspricht 64 Byte. Da ein Seed in der Regel in einer hexadezimalen Notation vorliegt und ein Byte im hexadezimalen System durch jeweils 2 Zeichen dargestellt wird, ist ein Seed also 128 Zeichen lang. Somit kann man auch schnell verstehen, dass es für einen Mensch wesentlich einfacher und auch weniger fehleranfällig ist, sich eine Mnemonic zu sichern als 128 aufeinanderfolgende Hex-Zeichen. Werden bei einem Seed eine oder mehrere Stellen falsch übertragen, ist es schwierig bis unmöglich den Fehler im Nachhinein zu finden.
Den Seed bekommen die Endnutzer eines HD-Wallets in der Regel nicht zu Gesicht. Auf die “Password-Based Key Derivation Function 2” gehen wir in einem späteren Artikel noch einmal genauer ein.
Erweiterter Master Key
Aus diesem Seed wird dann ein erweiterter privater Master Schlüssel erzeugt. Auch diesen bekommen Endnutzer eines HD-Wallets in der Regel nicht zu Gesicht. Unter Zuhilfenahme eines so genannten Ableitungs-Pfades, auf den wir später noch eingehen werden, können mittels einer Child-Key-Derivation-Funktion, beliebig viele – in der Abbildung durch ein „n“ gekennzeichnet – private Schlüssel abgeleitet werden. Auf die Child-Key-Derivation-Funktion werden wir ebenfalls in einem späteren Artikel noch einmal genauer drauf eingehen.
Private Key
Den privaten Schlüssel bzw. eine beliebige Anzahl von privaten Schlüsseln, bekommen dann die Endnutzer auch zu sehen. Diese können, wie schon zuvor erwähnt, dazu genutzt werden, Transaktionen auf der Ethereum-Blockchain auszuführen. Sie ermöglichen es somit z.B. Ether von einer Ethereum-Adresse auf eine andere zu transferieren. Gelangt ein unbefugter dritter in Besitz eines privaten Schlüssels, kann dieser über die für diesen Schlüssel zur Verfügung stehenden Ether frei verfügen.
Public Key
Wer sich bereits mit asymmetrischer Verschlüsselung auseinandergesetzt hat, weiß, dass man aus einem privaten Schlüssel einen öffentlichen Schlüssel berechnen kann. Der öffentliche Schlüssel wird im Fall von Ethereum zur Verifikation von signierten Transaktionen genutzt. Wir nutzen den öffentlichen Schlüssel an dieser Stelle jedoch lediglich, um hieraus eine Ethereum-Adresse abzuleiten. Den zu einem privaten Schlüssel gehörigen öffentlichen Schlüssel bekommen Endnutzer eines Wallets in der Regel auch nicht zu Gesicht.
Ethereum-Address
Ethereum-Adressen werden mittels der Keccak-256-Hashfunktion aus einem öffentlichen Schlüssel abgeleitet, indem ein Hash des öffentlichen Schlüssels gebildet wird und anschließend die letzten 20 Bytes dieses Hashs als Ethereum-Adresse benutzt werden. In der Regel wird noch ein Hexpräfix (also 0x) vorangestellt, um zu kennzeichnen, dass es sich hierbei um eine Darstellung in hexadezimaler Notation handelt.
Bitcoin Improvement Proposals und JavaScript-Bibliotheken
Ein Großteil des Weges von einer Mnemonic hin zu einer Ethereum-Adresse kann und wird mittels Bibliotheken umgesetzt, welche sich an die so genannten „Bitcoin Improvement Proposals“ halten. Hieraus resultieren zunächst einmal die Fragen, was ist ein Bitcoin Improvement Proposal ist und warum diese BIPs auch in Ethereum eingesetzt werden. Zu Deutsch übersetzt würde das Ganze wohl „Bitcoin Verbesserungsvorschlag“ bedeuten. BIPs sind das Standardverfahren, um einen Änderungs- oder Verbesserungsvorschlag für Bitcoin einzubringen. Einen Überblick über alle BIPs erhaltet Ihr hier.
Da sowohl Bitcoin als auch Ethereum auf dem asymmetrischen Kryptosystem der elliptischen Kurven basieren und auch die gleiche elliptische Kurve verwenden, kann und wird bei der Implementierung eines HD-Wallets für Ethereum auf die für Bitcoin entworfenen Bibliotheken, welche auf den “Bitcoin Improvement Proposals” basieren, zurückgegriffen.
BIP 39 mit dem Titel „Mnemonic code for generating deterministic keys“ dient der Erzeugung einer Mnemonic und der hierauf basierenden Erstellung eines Seeds. Wir werden diesen Bitcoin Improvement Proposal später mit der gleichnamigen JavaScript-Bibliothek „bip39“ implementieren.
BIP 32 mit dem Title „Hierarchical Deterministic Wallets“ beschreibt hierarchische deterministische Wallets und spezifiziert unter anderem wie man aus einem Seed einen erweiterten Master-Schlüssel und weitere Child-Keys (also Kind-Schlüssel) ableitet.
BIP 44 mit dem Titel „Multi-Account Hierarchy for Deterministic Wallets“ beschreibt einen Standard zur Navigation durch die in BIP 32 spezifizierte Baumstruktur von HD-Wallets mittels eines fünfstufigen Pfades.
Zur Umsetzung von BIP32 und 44 werden wir später die Bibliothek „hdkey“ verwenden.
Durch diese drei BIPs ist unter anderem eine Kompatibilität zwischen den verschiedenen Implementationen von HD-Wallets gegeben, sodass es möglich ist, eine Vielzahl von Schlüsseln mittels einer einzigen Mnemonic (bspw. 12 zufällig generierte Wörter) in verschiedenen HD-Wallet-Implementationen wiederherzustellen. Für die Ableitung des zum privaten Schlüssel gehörigen öffentlichen Schlüssels und der darauf basierenden Ableitung der Ethereum-Adresse werden wir die Bibliothek „Ethereumjs-util“ verwenden. Im Übrigen gibt es auch “Ethereum Improvment Proposals” auf die wir bei der Generierung der Ethereum-Adresse noch einmal zu sprechen kommen.
Zielsetzung
Schauen wir uns einmal unser Ziel an. Wir möchten ein HD-Wallet in JavaScript programmieren, welches eine Mnemonic generiert und hieraus eine beliebige Anzahl an privaten Schlüsseln und den dazugehörigen Ethereum-Adressen generiert. Leider ist es, wie wir bereits gesehen haben, nicht möglich direkt aus einem Mnemonic eine beliebige Anzahl an privaten Schlüsseln zu generieren – auch wenn es für den Endnutzer meistens so aussieht. Dazwischen liegen viele Schritte, welche wir in den folgenden Artikeln jeweils genauer besprechen werden.
Ausblick
In den nächsten Artikeln werden wir uns theoretisch und praktisch damit beschäftigen, wie man von einer Mnemonic hin zu einem privaten Schlüssel und damit auch zu einer Ethereum-Adresse gelangt. Hierzu werden wir uns die zuvor genannten JavaScript-Bibliotheken angucken und schauen, wie hier die besprochene Theorie umgesetzt wird. Zudem werden wir mithilfe dieser Bibliotheken ein eigenes JavaScript-Wallet implementieren. Mit dem ersten Schritt, nämlich der Generierung der Mnemonic werden wir uns in dem nächsten Artikel beschäftigen.
BIPs und verwendete Bibliotheken
BIPs:
[1] https://github.com/bitcoin/bips
[2] https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
[3] https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
[4] https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
Genutzte Bibliotheken:
[5] https://www.npmjs.com/package/bip39
[6] https://www.npmjs.com/package/hdkey
[7] https://www.npmjs.com/package/ethereumjs-util