Showing preview only (669K chars total). Download the full file or copy to clipboard to get everything.
Repository: micro-bitcoin/uBitcoin
Branch: master
Commit: 877542fdc163
Files: 82
Total size: 639.6 KB
Directory structure:
gitextract_1v3_gt78/
├── .gitignore
├── .mbedignore
├── LICENSE
├── README.md
├── examples/
│ ├── cpp/
│ │ ├── Makefile
│ │ ├── README.md
│ │ └── main.cpp
│ ├── ecdh/
│ │ └── ecdh.ino
│ ├── hash/
│ │ └── hash.ino
│ ├── mnemonic/
│ │ └── mnemonic.ino
│ ├── multisig/
│ │ └── multisig.ino
│ ├── psbt/
│ │ └── psbt.ino
│ ├── schnorr/
│ │ └── schnorr.ino
│ ├── tx/
│ │ └── tx.ino
│ └── xpubs/
│ └── xpubs.ino
├── keywords.txt
├── library.json
├── library.properties
├── src/
│ ├── BaseClasses.cpp
│ ├── BaseClasses.h
│ ├── Bitcoin.cpp
│ ├── Bitcoin.h
│ ├── BitcoinCurve.cpp
│ ├── BitcoinCurve.h
│ ├── Conversion.cpp
│ ├── Conversion.h
│ ├── Electrum.cpp
│ ├── Electrum.h
│ ├── HDWallet.cpp
│ ├── Hash.cpp
│ ├── Hash.h
│ ├── Networks.cpp
│ ├── Networks.h
│ ├── OpCodes.h
│ ├── PSBT.cpp
│ ├── PSBT.h
│ ├── Script.cpp
│ ├── Transaction.cpp
│ ├── uBitcoin_conf.h
│ └── utility/
│ ├── segwit_addr.c
│ ├── segwit_addr.h
│ └── trezor/
│ ├── address.c
│ ├── address.h
│ ├── base58.c
│ ├── base58.h
│ ├── bignum.c
│ ├── bignum.h
│ ├── bip32.h
│ ├── bip39.c
│ ├── bip39.h
│ ├── bip39_english.h
│ ├── ecdsa.c
│ ├── ecdsa.h
│ ├── hasher.c
│ ├── hasher.h
│ ├── hmac.c
│ ├── hmac.h
│ ├── memzero.c
│ ├── memzero.h
│ ├── options.h
│ ├── pbkdf2.c
│ ├── pbkdf2.h
│ ├── rand.c
│ ├── rand.h
│ ├── rfc6979.c
│ ├── rfc6979.h
│ ├── ripemd160.c
│ ├── ripemd160.h
│ ├── secp256k1.c
│ ├── secp256k1.h
│ ├── secp256k1.table
│ ├── sha2.c
│ ├── sha2.h
│ ├── sha3.c
│ └── sha3.h
└── tests/
├── Makefile
├── minunit.h
├── sysrand.c
├── test_conversion.cpp
├── test_hash.cpp
├── test_mnemonic.cpp
└── test_schnorr.cpp
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.vscode
build
================================================
FILE: .mbedignore
================================================
examples/*
tests/*
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2019 Stepan Snigirev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Micro-Bitcoin
C++ Bitcoin library for 32-bit microcontrollers. The library supports [Arduino IDE](https://www.arduino.cc/), [ARM mbed](https://www.mbed.com/en/) and bare metal.<br>
It provides a collection of convenient classes for Bitcoin: private and public keys, HD wallets, generation of the recovery phrases, PSBT transaction formats, scripts — everything required for a hardware wallet or other bitcoin-powered device.
The library should work on any decent 32-bit microcontroller, like esp32, riscV, stm32 series and others. It *doesn't work* on 8-bit microcontrollers like a classic Arduino as these microcontrollers are not powerful enough to run complicated crypto algorithms.
We use elliptic curve implementation from [trezor-crypto](https://github.com/trezor/trezor-firmware/tree/master/crypto). API is inspired by [Jimmy Song's](https://github.com/jimmysong/) Porgramming Blockchain class and the [book](https://github.com/jimmysong/programmingbitcoin).
## Documentation
Check out our [tutorial](https://micro-bitcoin.github.io/#/tutorial/README) where we write a minimal hardware wallet, or browse the [API docs](https://micro-bitcoin.github.io/#/api/README). We also have a collection of [recepies](https://micro-bitcoin.github.io/#/recepies/README) for some common use-cases.
Telegram group: https://t.me/arduinoBitcoin
## Alternative libraries
[DIY Bitcoin Hardware website](https://diybitcoinhardware.com/) has a nice collection of bitcoin-related projects, resources and libraries for makers.
A few bitcoin libraries:
- [secp256k1](https://github.com/bitcoin-core/secp256k1) — elliptic curve library from Bitcoin Core and a [version](https://github.com/diybitcoinhardware/secp256k1-embedded) working with Arduino IDE & Mbed out of the box.
- [libwally](https://github.com/ElementsProject/libwally-core/) - bitcoin library from Blockstream and a [version](https://github.com/diybitcoinhardware/libwally-embedded) working with Arduino IDE.
- [f469-disco](https://github.com/diybitcoinhardware/f469-disco) - micropython bitcoin bundle for STM Discovery board and other platforms.
## Installation
The library is [available](https://www.arduino.cc/reference/en/libraries/ubitcoin/) in the Arduino Library manager, or you can download and install it manually.
[Download](https://github.com/micro-bitcoin/uBitcoin/archive/master.zip) the zip file from our [repository](https://github.com/micro-bitcoin/uBitcoin/) and select in Arduino IDE `Sketch` → `Include library` → `Add .ZIP library...`.
Or clone it into your `Documents/Arduino/libraries` folder:
```sh
git clone https://github.com/micro-bitcoin/uBitcoin.git
```
When installed you will also see a few examples in `File` → `Examples` → `Bitcoin` menu.
## Basic usage example
First, don't forget to include necessary headers:
```cpp
// we use these two in our sketch:
#include "Bitcoin.h"
#include "PSBT.h" // if using PSBT functionality
// other headers of the library
#include "Conversion.h" // to get access to functions like toHex() or fromBase64()
#include "Hash.h" // if using hashes in your code
```
Now we can write a simple example that does the following:
1. Creates a master private key from a recovery phrase and empty password
2. Derives account and prints master public key for a watch-only wallet (`zpub` in this case)
3. Derives and print first segwit address
4. Parses, signs and prints signed PSBT transaction
```cpp
// derive master private key
HDPrivateKey hd("add good charge eagle walk culture book inherit fan nature seek repair", "");
// derive native segwit account (bip-84) for tesnet
HDPrivateKey account = hd.derive("m/84'/1'/0'/");
// print xpub: vpub5YkPqVJTA7gjK...AH2rXvcAe3p781G
Serial.println(account.xpub());
// or change the account type to UNKNOWN_TYPE to get tpub
HDPublicKey xpub = account.xpub();
xpub.type = UNKNOWN_TYPE;
// this time prints tpubDCnYy4Ty...dL4fLKsBFjFQwz
Serial.println(xpub);
// set back correct type to get segwit addresses by default
xpub.type = P2WPKH;
Serial.println(hd.fingerprint());
// print first address: tb1q6c8m3whsag5zadgl32nmhuf9q0qmtklws25n6g
Serial.println(xpub.derive("m/0/0").address());
PSBT tx;
// parse unsigned transaction
tx.parseBase64("cHNidP8BAHECAAAAAUQS8FqBzYocPDpeQmXBRBH7NwZHVJF39dYJDCXxq"
"zf6AAAAAAD+////AqCGAQAAAAAAFgAUuP0WcSBmiAZYi91nX90hg/cZJ1U8AgMAAAAAABYAF"
"C1RhUR+m/nFyQkPSlP0xmZVxlOqAAAAAAABAR/gkwQAAAAAABYAFNYPuLrw6igutR+Kp7vxJ"
"QPBtdvuIgYDzkBZaAkSIz0P0BexiPYfzInxu9mMeuaOQa1fGEUXcWIYoyAeuFQAAIABAACAA"
"AAAgAAAAAAAAAAAAAAiAgMxjOiFQofq7l9q42nsLA3Ta4zKpEs5eCnAvMnQaVeqsBijIB64V"
"AAAgAEAAIAAAACAAQAAAAAAAAAA");
// sign with the root key
tx.sign(hd);
// print signed transaction
Serial.println(tx.toBase64());
```
Ready for more? Check out the [tutorial](https://micro-bitcoin.github.io/#/tutorial/README) and start writing your very own hardware wallet!
================================================
FILE: examples/cpp/Makefile
================================================
TARGET ?= main
ifeq ($(OS),Windows_NT)
EXT ?= .exe
else
EXT ?=
endif
TARGET_EXEC ?= $(TARGET)$(EXT)
# Paths
# to make sure addprefix to LIB_DIR doesn't go out from build directory
BUILD_DIR = build
SRC_DIR = .
# uBitcoin library
LIB_DIR = ../../src
# Tools
ifeq ($(OS),Windows_NT)
TOOLCHAIN_PREFIX ?= x86_64-w64-mingw32-
MKDIR_P = mkdir
RM_R = rmdir /s /q
else
TOOLCHAIN_PREFIX ?=
MKDIR_P = mkdir -p
RM_R = rm -r
endif
# compilers
CC := $(TOOLCHAIN_PREFIX)gcc
CXX := $(TOOLCHAIN_PREFIX)g++
# main.cpp
CXX_SOURCES = $(wildcard $(SRC_DIR)/*.cpp)
C_SOURCES =
# uBitcoin sources
CXX_SOURCES += $(wildcard $(LIB_DIR)/*.cpp)
C_SOURCES += $(wildcard $(LIB_DIR)/utility/trezor/*.c) \
$(wildcard $(LIB_DIR)/utility/*.c) \
$(wildcard $(LIB_DIR)/*.c)
# include lib path, don't use mbed or arduino config (-DUSE_STDONLY), debug symbols, all warnings as errors
FLAGS = -I$(LIB_DIR) -g -Wall -Werror -ldl
CFLAGS = $(FLAGS)
CPPFLAGS = $(FLAGS) -DUSE_STDONLY -DUBTC_EXAMPLE
OBJS = $(patsubst $(SRC_DIR)/%, $(BUILD_DIR)/src/%.o, \
$(patsubst $(LIB_DIR)/%, $(BUILD_DIR)/lib/%.o, \
$(C_SOURCES) $(CXX_SOURCES)))
vpath %.cpp $(SRC_DIR)
vpath %.cpp $(LIB_DIR)
vpath %.c $(LIB_DIR)
.PHONY: clean all run
all: $(BUILD_DIR)/$(TARGET_EXEC)
run: $(BUILD_DIR)/$(TARGET_EXEC)
$(BUILD_DIR)/$(TARGET_EXEC)
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
$(CXX) $(OBJS) $(CPPFLAGS) -o $@
# lib c sources
$(BUILD_DIR)/lib/%.c.o: %.c
$(MKDIR_P) $(dir $@)
$(CC) -c $(CFLAGS) $< -o $@
# lib cpp sources
$(BUILD_DIR)/lib/%.cpp.o: %.cpp
$(MKDIR_P) $(dir $@)
$(CXX) -c $(CPPFLAGS) $< -o $@
# cpp sources
$(BUILD_DIR)/src/%.cpp.o: %.cpp
$(MKDIR_P) $(dir $@)
$(CXX) -c $(CPPFLAGS) $< -o $@
clean:
$(RM_R) $(BUILD_DIR)
================================================
FILE: examples/cpp/README.md
================================================
# C++ example
run with `make run`
================================================
FILE: examples/cpp/main.cpp
================================================
/*
* A simple example showing how to work with uBitcoin in c++ on a PC
*/
// only compile when UBTC_EXAMPLE flag is provided
// to not clash with platformio's compile-everything approach
#ifdef UBTC_EXAMPLE
#include <iostream>
#include "Bitcoin.h"
#include "PSBT.h"
#include <stdint.h>
#include <stdlib.h>
// You can define your random function to improve side-channel resistance
extern "C" {
// use system random function
uint32_t random32(void){
return (uint32_t)rand();
}
}
using namespace std;
char mnemonic[] = "flight canvas heart purse potato mixed offer tooth maple blue kitten salute almost staff physical remain coral clump midnight rotate innocent shield inch ski";
int main() {
// convert mnemonic to xprv
cout << "Your mnemonic: " << endl << mnemonic << endl;
HDPrivateKey hd(mnemonic, "");
cout << "Your xprv: " << endl << string(hd) << endl;
// derive account xpub
char derivation[] = "m/84h/1h/0h";
HDPublicKey xpub = hd.derive(derivation).xpub();
// set network to regtest, otherwise default addresses will be for testnet
xpub.network = &Regtest;
cout << "Account xpub at path " << derivation << ":" << endl;
cout << string(xpub) << endl;
// print first 5 receiving addresses
HDPublicKey recv_xpub = xpub.child(0);
for (uint32_t i = 0; i < 5; i++)
{
cout << "Address #" << i << ": " << recv_xpub.child(i).address() << endl;
}
// signing PSBT transaction with 2 inputs
PSBT psbt;
psbt.parseBase64(string("cHNidP8BAJoCAAAAAqQW9JR6TFv46IXybtf9tKAy5WsYusr6O4rsfN8DIywEAQAAAAD9////9YKXV2aJad3wScN70cgZHMhQtwhTjw95loZfUB57+H4AAAAAAP3///8CwOHkAAAAAAAWABQzSSTq9G6AboazU3oS+BWVAw1zp21KTAAAAAAAFgAU2SSg4OQMonZrrLpdtTzcNes1MthDAQAAAAEAcQIAAAAB6GDWQUAnmq5s8Nm68qPp3fHnpARmx67Q5ZRHGj1rCjgBAAAAAP7///8CdIv2XwAAAAAWABRozVhYn14Pmv8XoAJePV7AQggf/4CWmAAAAAAAFgAUcOVKtnxrbE7ragGagzMqQ7kJsZkAAAAAAQEfgJaYAAAAAAAWABRw5Uq2fGtsTutqAZqDMypDuQmxmSIGA3s6OgE8GCKOcHDJe7XY0q/i/XSe6e933ErCDCCKR5WoGARkI4xUAACAAQAAgAAAAIAAAAAAAAAAAAABAHECAAAAAaH0XE8I0jQHvCDfdDTUbHrm9+oHbq1yt5ansxoaeeNjAQAAAAD+////AoCWmAAAAAAAFgAUQZD8n6hVi91tRSlWl4WkMwuBnoXsVTuMAAAAABYAFMbknFZNyqOzappeWfZi2+EP0asDAAAAAAEBH4CWmAAAAAAAFgAUQZD8n6hVi91tRSlWl4WkMwuBnoUiBgKNwymEX374HvJHU9FIT4YmCn8CuNteCOxtw7bJXGfscxgEZCOMVAAAgAEAAIAAAACAAAAAAAEAAAAAACICA9OwnpVPPgWAC/O7SuxHNPjX46Iz2Qv9dcI033AqEyv+GARkI4xUAACAAQAAgAAAAIABAAAAAAAAAAA="));
// check parsing is ok
if(!psbt){
cout << "Failed parsing transaction" << endl;
exit(EXIT_FAILURE);
return EXIT_FAILURE;
}
cout << "Transactions details:" << endl;
// going through all outputs
cout << "Outputs:" << endl;
for(unsigned int i = 0; i < psbt.tx.outputsNumber; i++){
// print addresses
cout << psbt.tx.txOuts[i].address(&Regtest);
if(psbt.txOutsMeta[i].derivationsLen > 0){ // there is derivation path
// considering only single key for simplicity
PSBTDerivation der = psbt.txOutsMeta[i].derivations[0];
HDPublicKey pub = hd.derive(der.derivation, der.derivationLen).xpub();
pub.network = &Regtest;
if(pub.address() == psbt.tx.txOuts[i].address(&Regtest)){
cout << " (change) ";
}
}
cout << " -> " << psbt.tx.txOuts[i].btcAmount()*1e3 << " mBTC" << endl;
}
cout << "Fee: " << psbt.fee() << " sat" << endl;
// sign using our key
psbt.sign(hd);
cout << "Signed transaction:" << endl << psbt.toBase64() << endl; // now you can combine and finalize PSBTs in Bitcoin Core
return 0;
}
#endif // UBTC_EXAMPLE
================================================
FILE: examples/ecdh/ecdh.ino
================================================
/*
* This example shows how to use ECDH algorithm
* to get shared secret between two parties.
* It is useful if you want to establish
* secure communication with someone else.
* You can use ECDH to create symmetric key
* and then use AES in CBC mode to encrypt/decrypt data.
*/
#include <Bitcoin.h>
#include <Hash.h>
void setup(){
Serial.begin(9600);
while(!Serial){
; // wait for serial port to open
}
// generate random private keys and corresponding pubkeys
byte secret1[32];
byte secret2[32];
for(int i=0; i<sizeof(secret1); i++){
secret1[i] = random(256); // TODO: use good hardware randomness here!!!
secret2[i] = random(256);
}
PrivateKey pk1(secret1);
PrivateKey pk2(secret2);
PublicKey pub1 = pk1.publicKey();
PublicKey pub2 = pk2.publicKey();
// now each party does ecdh on it's private key with public key of the other party
// we calculate it twice here to make sure we get the same shared secret
byte shared_secret1[32];
byte shared_secret2[32];
// party 1 needs pubkey of party 2
pk1.ecdh(pub2, shared_secret1);
// party 2 needs pubkey of party 1
pk2.ecdh(pub1, shared_secret2);
// check they are the same
if(memcmp(shared_secret1, shared_secret2, 32)!=0){
Serial.println("Error in key negotiation! Something went wrong :(");
return;
}
Serial.println("Key negotiation completed.");
Serial.println("Symmetric key:");
Serial.println(toHex(shared_secret1, sizeof(shared_secret1)));
// Makes sense to calculate human-readable fingerprint of the secret
// so user can check on both devices that it's the same - no man in the middle.
// It can be for example Base58(sha256(secret)[:6]) (6 bytes of sha256 of the secret)
byte h[32];
sha256(shared_secret1, 32, h);
String fingerprint = toBase58(h, 6);
Serial.print("Key fingerprint: ");
Serial.println(fingerprint);
}
void loop(){
delay(10);
}
================================================
FILE: examples/hash/hash.ino
================================================
#include <Hash.h> // all single-line hashing algorithms
#include <Conversion.h> // to print byte arrays in hex format
void setup() {
Serial.begin(9600);
while(!Serial){
; // wait for serial port to open
}
String message = "Hello world!"; // message to hash
byte hash[64] = { 0 }; // hash
int hashLen = 0;
// sha-256
hashLen = sha256(message, hash);
Serial.println("SHA-256: " + toHex(hash, hashLen));
Serial.println("Should be: c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51a");
// you can also pass byte arrays to the hash function
// and update piece by piece
byte msg2[] = {1, 2, 3, 4, 5, 0xFA, 0xB0, 0x0B, 0x51};
// hash in one line
sha256(msg2, sizeof(msg2), hash);
// you can also create a class instance and use .write method
SHA256 h;
h.begin();
h.write(msg2, 5); // add first 5 bytes to hash
h.write(msg2+5, 4); // add another 4 bytes
h.end(hash); // result will be stored in the hash array
// ripemd-160
hashLen = rmd160(message, hash);
Serial.println("RMD-160: " + toHex(hash, hashLen));
Serial.println("Should be: 7f772647d88750add82d8e1a7a3e5c0902a346a3");
// hash160(msg) = rmd160(sha256(message))
hashLen = hash160(message, hash);
Serial.println("Hash160: " + toHex(hash, hashLen));
Serial.println("Should be: 621281c15fb62d5c6013ea29007491e8b174e1b9");
// sha256(sha256(message))
hashLen = doubleSha(message, hash);
Serial.println("DoubleSha: " + toHex(hash, hashLen));
Serial.println("Should be: 7982970534e089b839957b7e174725ce1878731ed6d700766e59cb16f1c25e27");
// sha512
hashLen = sha512(message, hash);
Serial.println("Sha512: " + toHex(hash, hashLen));
Serial.println("Should be: f6cde2a0f819314cdde55fc227d8d7dae3d28cc556222a0a8ad66d91ccad4aad6094f517a2182360c9aacf6a3dc323162cb6fd8cdffedb0fe038f55e85ffb5b6");
// sha512-hmac
// here we use more c-style approach
char key[] = "Bitcoin seed";
hashLen = sha512Hmac((byte*)key, strlen(key), (byte*)message.c_str(), message.length(), hash);
Serial.println("Sha512-HMAC: " + toHex(hash, hashLen));
Serial.println("Should be: f7fc496a2c17bd09a6328124dc6edebed987e7e93903deee0633a756f1ee81da0753334f6cfe226b5c712d893a68c547d3a5497cd73e1d010670c1e0e9d93a8a");
}
void loop() {
}
================================================
FILE: examples/mnemonic/mnemonic.ino
================================================
/*
* This example shows how to derive master private key from the recovery seed
* Generate a random recovery seed i.e. on https://iancoleman.io/bip39/
* and check the master private key, account private key, account public key
* and first address.
*/
#include "Bitcoin.h"
void printHD(String mnemonic, String password = ""){
HDPrivateKey hd(mnemonic, password);
if(!hd){ // check if it is valid
Serial.println("Invalid xpub");
return;
}
Serial.println("Mnemonic:");
Serial.println(mnemonic);
Serial.print("Password: \"");
Serial.println(password+"\"");
Serial.println("Root private key:");
Serial.println(hd);
Serial.println("bip84 master private key:");
HDPrivateKey account = hd.derive("m/84'/0'/0'/");
Serial.println(account);
Serial.println("bip84 master public key:");
Serial.println(account.xpub());
Serial.println("first address:");
Serial.println(account.derive("m/0/0/").address());
Serial.println("\n");
}
void setup() {
Serial.begin(115200);
printHD("arch volcano urge cradle turn labor skin secret squeeze denial jacket vintage fix glad lemon", "my secret password");
// entropy bytes to mnemonic
uint8_t arr[] = {'1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'};
String mn = mnemonicFromEntropy(arr, sizeof(arr));
Serial.println(mn);
uint8_t out[16];
size_t len = mnemonicToEntropy(mn, out, sizeof(out));
Serial.println(toHex(out, len));
}
void loop() {
delay(100);
}
================================================
FILE: examples/multisig/multisig.ino
================================================
/*
* A simple example showing how to derive multisig addresses
*/
#include "Bitcoin.h"
// 2 of 3 multisig
#define NUM_KEYS 3
#define THRESHOLD 2
// cosigners
HDPublicKey xpubs[] = {
// [73c5da0a/48h/1h/0h/2h] abandon * 11 + about
HDPublicKey("Vpub5n95dMZrDHj6SeBgJ1oz4Fae2N2eJNuWK3VTKDb2dzGpMFLUHLmtyDfen7AaQxwQ5mZnMyXdVrkEaoMLVTH8FmVBRVWPGFYWhmtDUGehGmq"),
// [fb7c1f11/48h/1h/0h/2h] ability * 11 + acid
HDPublicKey("Vpub5mpiGVPWYfDWqZAvCtJihfU559GdwhNC5gwCa9xjSBp4Bvr1DnqXYTtAogkjvWYN1LGTyKo5Yjhfz7mNAqVmw7KG66CM4mDd8vGH3zPQmBH"),
// [47fc1ba1/48h/1h/0h/2h] able * 11 + acid
HDPublicKey("Vpub5nRyr5ptHEvisEDuWRMY3rgQ99B1CU21wfkuXEekTi8jCshCWseGqBWWZ8U3Wgv4jDj2fizxBNZmpFjo56Ffu49Efpu4vAj5XErHyBEQoN9"),
};
void setup() {
Serial.begin();
// print first 5 addresses
for(int idx = 0; idx < 5; idx++){
// get derivation path
String derivation = String("m/0/")+String(idx);
Serial.println(derivation);
// generate individual public keys and put them into array
PublicKey pubkeys[NUM_KEYS];
for(int i=0; i<NUM_KEYS; i++){
// it will automatically convert to pubkey
pubkeys[i] = xpubs[i].derive(derivation);
}
// create multisig witness script from pubkeys above
Script witness_script = sortedmulti(THRESHOLD, pubkeys, NUM_KEYS);
// native segwit script-pubkey from witness script
Script script_pubkey = wsh(witness_script);
// address on regtest
Serial.print("address ");
Serial.print(idx);
Serial.print(": ");
Serial.println(script_pubkey.address(&Regtest));
}
}
void loop() {
// put your main code here, to run repeatedly:
}
================================================
FILE: examples/psbt/psbt.ino
================================================
/*
* This example shows how parse and sign PSBT transaction
*/
#include "Bitcoin.h"
#include "PSBT.h"
HDPrivateKey hd("flight canvas heart purse potato mixed offer tooth maple blue kitten salute almost staff physical remain coral clump midnight rotate innocent shield inch ski", "");
PSBT psbt;
void setup() {
Serial.begin(115200);
// 2-input psbt
psbt.parseBase64("cHNidP8BAJoCAAAAAqQW9JR6TFv46IXybtf9tKAy5WsYusr6O4rsfN8DIywEAQAAAAD9////9YKXV2aJad3wScN70cgZHMhQtwhTjw95loZfUB57+H4AAAAAAP3///8CwOHkAAAAAAAWABQzSSTq9G6AboazU3oS+BWVAw1zp21KTAAAAAAAFgAU2SSg4OQMonZrrLpdtTzcNes1MthDAQAAAAEAcQIAAAAB6GDWQUAnmq5s8Nm68qPp3fHnpARmx67Q5ZRHGj1rCjgBAAAAAP7///8CdIv2XwAAAAAWABRozVhYn14Pmv8XoAJePV7AQggf/4CWmAAAAAAAFgAUcOVKtnxrbE7ragGagzMqQ7kJsZkAAAAAAQEfgJaYAAAAAAAWABRw5Uq2fGtsTutqAZqDMypDuQmxmSIGA3s6OgE8GCKOcHDJe7XY0q/i/XSe6e933ErCDCCKR5WoGARkI4xUAACAAQAAgAAAAIAAAAAAAAAAAAABAHECAAAAAaH0XE8I0jQHvCDfdDTUbHrm9+oHbq1yt5ansxoaeeNjAQAAAAD+////AoCWmAAAAAAAFgAUQZD8n6hVi91tRSlWl4WkMwuBnoXsVTuMAAAAABYAFMbknFZNyqOzappeWfZi2+EP0asDAAAAAAEBH4CWmAAAAAAAFgAUQZD8n6hVi91tRSlWl4WkMwuBnoUiBgKNwymEX374HvJHU9FIT4YmCn8CuNteCOxtw7bJXGfscxgEZCOMVAAAgAEAAIAAAACAAAAAAAEAAAAAACICA9OwnpVPPgWAC/O7SuxHNPjX46Iz2Qv9dcI033AqEyv+GARkI4xUAACAAQAAgAAAAIABAAAAAAAAAAA=");
// check parsing is ok
if(!psbt){
Serial.println("Failed parsing transaction");
return;
}
Serial.println("Transactions details:");
// going through all outputs to print info
Serial.println("Outputs:");
for(int i=0; i<psbt.tx.outputsNumber; i++){
// print addresses
Serial.print(psbt.tx.txOuts[i].address(&Regtest));
if(psbt.txOutsMeta[i].derivationsLen > 0){ // there is derivation path
// considering only single key for simplicity
PSBTDerivation der = psbt.txOutsMeta[i].derivations[0];
HDPublicKey pub = hd.derive(der.derivation, der.derivationLen).xpub();
if(pub.address() == psbt.tx.txOuts[i].address()){
Serial.print(" (change) ");
}
}
Serial.print(" -> ");
Serial.print(psbt.tx.txOuts[i].btcAmount()*1e3);
Serial.println(" mBTC");
}
Serial.print("Fee: ");
Serial.print(float(psbt.fee())/100); // Arduino can't print 64-bit ints
Serial.println(" bits");
psbt.sign(hd);
Serial.println(psbt.toBase64()); // now you can combine and finalize PSBTs in Bitcoin Core
}
void loop() {
}
================================================
FILE: examples/schnorr/schnorr.ino
================================================
/*
* A simple example showing how to work with uBitcoin in c++ on a PC
*/
#include "Bitcoin.h"
#include "Hash.h"
char mnemonic[] = "flight canvas heart purse potato mixed offer tooth maple blue kitten salute almost staff physical remain coral clump midnight rotate innocent shield inch ski";
// convert mnemonic to xprv
HDPrivateKey hd(mnemonic, "");
void setup() {
Serial.begin(115200);
Serial.println("Your mnemonic: ");
Serial.println(mnemonic);
// derive private key you want to use for schnorr
PrivateKey prv = hd.derive("m/86h/0h/0h/0/1");
// print corresponding public key - schnorr keys are x-only 32-byte keys
PublicKey pub = prv.publicKey();
Serial.print("Private key: ");
Serial.println(prv);
Serial.print("X-only public key: ");
Serial.println(pub.x());
// sign message
uint8_t msg[32];
sha256("hello world!", msg);
// sign using Schnorr algorithm
SchnorrSignature sig = prv.schnorr_sign(msg);
// Unlike ECDSA, Schnorr signature is always 64-bytes long
Serial.println("Signature for message 'hello world!':");
Serial.println(sig);
// verify signatures
if(pub.schnorr_verify(sig, msg)){
Serial.println("All good, signature is valid");
}else{
Serial.println("Something is wrong! Signature is invalid.");
}
}
void loop() {
}
================================================
FILE: examples/tx/tx.ino
================================================
#include <Arduino.h>
#include "Bitcoin.h"
void setup() {
Serial.begin(9600);
while(!Serial){
;
}
// Single private key for testnet
PrivateKey privateKey("cQwxqQwCwGoirnTkVnNt4XqJuEv24HYBvVWCTLtL5g1kx9Q1AEhE");
Serial.println(privateKey.address());
TxIn txIn("fbeae5f43d76fc3035cb4190baaf8cc123dd04f11c98c8f19a8b12cb4ce90db0", 0);
// addresses to send bitcoins
char destination[] = "n3DN9cswq5jnXXUmLP3bXtR89yfDNWrie9";
String change = privateKey.address();
// amounts to send
// unsigned long can store up to 4294967295 satoshi (42.9 BTC)
// for larger amounts use uint64_t
unsigned long availableAmount = 2000000; // 58 mBTC
unsigned long fee = 1500;
unsigned long sendAmount = 1000000; // 20 mBTC
unsigned long changeAmount = availableAmount - sendAmount - fee;
TxOut txOutDestination(sendAmount, destination);
TxOut txOutChange(changeAmount, change.c_str());
Serial.println(txOutDestination);
// constructing actual transaction
Tx tx;
tx.addInput(txIn);
tx.addOutput(txOutDestination);
tx.addOutput(txOutChange);
// Printing transaction information
Serial.print("Tx length: ");
Serial.println(tx.length());
Serial.print("Version: ");
Serial.println(tx.version);
Serial.print("Inputs: ");
Serial.println(tx.inputsNumber);
for(int i=0; i< tx.inputsNumber; i++){
TxIn txin = tx.txIns[i];
Serial.print("\tHash: ");
Serial.println(toHex(txin.hash, 32));
Serial.print("\tOutput index: ");
Serial.println(txin.outputIndex);
Serial.print("\tScript length: ");
Serial.println(txin.scriptSig.length());
Serial.print("\tScript: ");
Serial.println(txin.scriptSig);
Serial.print("\tSequence: ");
Serial.println(txin.sequence);
if(tx.isSegwit()){
Serial.println("\tSEGWIT!");
}
}
Serial.print("Outputs: ");
Serial.println(tx.outputsNumber);
for(int i=0; i< tx.outputsNumber; i++){
Serial.print(tx.txOuts[i].address());
Serial.print(": ");
Serial.print(((float)tx.txOuts[i].amount)/100000);
Serial.println(" mBTC");
}
Serial.println("Unsigned transaction:");
Serial.println(tx);
// signing transaction
Serial.println("Signing transaction...");
Signature sig = tx.signInput(0, privateKey);
Serial.println(sig);
Serial.println("Signed transaction:");
Serial.println(tx);
Serial.println("Transaction id:");
Serial.println(tx.txid());
Serial.println("Done");
}
void loop() {
// put your main code here, to run repeatedly:
delay(100);
}
================================================
FILE: examples/xpubs/xpubs.ino
================================================
/*
* This example shows how to derive bitcoin addresses from the master public key (bip32)
* Enter account master public key to the serial console and get the addresses
* Use this tool to check: https://iancoleman.io/bip39/
*/
#include "Bitcoin.h"
void printAddresses(String pub){
HDPublicKey hd(pub);
if(!hd){ // check if it is valid
Serial.println("Invalid xpub");
return;
}
Serial.println("Master public key:");
Serial.println(hd);
Serial.println("First 5 receiving addresses:");
for(int i=0; i<5; i++){
String path = String("m/0/")+i;
Serial.print("Path:");
Serial.println(path);
Serial.print("Address: ");
Serial.println(hd.derive(path).address());
// Serial.print("Legacy: ");
// Serial.println(hd.derive(path).legacyAddress());
// Serial.print("Nested segwit: ");
// Serial.println(hd.derive(path).nestedSegwitAddress());
// Serial.print("Native segwit: ");
// Serial.println(hd.derive(path).segwitAddress());
}
Serial.println("\n");
}
void setup() {
Serial.begin(115200);
// bip 44
printAddresses("xpub6BoiLSuHTDytgQejDauyXkBqQ4xTw2tSFKgkHfmZky7jDQQecUKwbFocxZXSJMCSp8gFNBbD9Squ3etZbJkE2qQqVBrypLjWJaWUNmHh3LT");
// bip 49
printAddresses("ypub6XMsccDqTfZCUz5m4VjbRLebnjFTLDpeKTgRJuUtAxkpQicB7p4ZwHdmimRuMTcunPfdpzgpVt7DCDKRRffsgmWavWLXccumbyYazC3wh5N");
// bip 84
printAddresses("zpub6rcGDMmj82CUJT1uV2mCcsN4EPTgBnJjciDWpYv12yCAPi9TEG5KLPF5iPtqzL6hNmaa5ZGfhJCHctoex7cGgqVxsyWcCDUNUDhjaYRQXzV");
Serial.println("Enter your master public key:");
}
void loop() {
if(Serial.available()){
String pub = Serial.readStringUntil('\n');
Serial.println(pub);
if(pub.length() > 0){
printAddresses(pub);
}
}
delay(100);
}
================================================
FILE: keywords.txt
================================================
#######################################
# Syntax Coloring Map
#######################################
#######################################
# Hash.h Methods and Functions (KEYWORD2)
#######################################
rmd160 KEYWORD2
sha256 KEYWORD2
hash160 KEYWORD2
doubleSha KEYWORD2
sha512 KEYWORD2
sha512Hmac KEYWORD2
#######################################
# Datatypes and classes (KEYWORD1)
#######################################
Bitcoin KEYWORD1
Hash KEYWORD1
Conversion KEYWORD1
OpCodes KEYWORD1
Scalar KEYWORD1
Point KEYWORD1
PrivateKey KEYWORD1
PublicKey KEYWORD1
HDPrivateKey KEYWORD1
HDPublicKey KEYWORD1
Script KEYWORD1
Signature KEYWORD1
SchnorrSignature KEYWORD1
Tx KEYWORD1
TxIn KEYWORD1
TxOut KEYWORD1
ElectrumTx KEYWORD1
PSBT KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
parse KEYWORD2
parseHex KEYWORD2
parseBase64 KEYWORD2
toBase64 KEYWORD2
serialize KEYWORD2
bin KEYWORD2
der KEYWORD2
type KEYWORD2
length KEYWORD2
scriptLength KEYWORD2
serializeScript KEYWORD2
push KEYWORD2
scriptPubkey KEYWORD2
address KEYWORD2
pow KEYWORD2
sign KEYWORD2
schnorr_sign KEYWORD2
verify KEYWORD2
schnorr_verify KEYWORD2
fromSeed KEYWORD2
fromMnemonic KEYWORD2
xpub KEYWORD2
xprv KEYWORD2
child KEYWORD2
hardenedChild KEYWORD2
derive KEYWORD2
legacyAddress KEYWORD2
segwitAddress KEYWORD2
nestedSegwitAddress KEYWORD2
parse KEYWORD2
fromWIF KEYWORD2
publicKey KEYWORD2
sec KEYWORD2
fromHex KEYWORD2
toHex KEYWORD2
toString KEYWORD2
generateMnemonic KEYWORD2
checkMnemonic KEYWORD2
littleEndianToInt KEYWORD2
intToLittleEndian KEYWORD2
bigEndianToInt KEYWORD2
intToBigEndian KEYWORD2
lenVarInt KEYWORD2
readVarInt KEYWORD2
readVarInt KEYWORD2
writeVarInt KEYWORD2
writeVarInt KEYWORD2
addInput KEYWORD2
addOutput KEYWORD2
signInput KEYWORD2
signSegwitInput KEYWORD2
sigHash KEYWORD2
######################################
# Constants (LITERAL1)
#######################################
PRIME LITERAL1
P2PKH LITERAL1
P2SH LITERAL1
P2WPKH LITERAL1
P2WSH LITERAL1
P2SH_P2WPKH LITERAL1
P2SH_P2WSH LITERAL1
SIGHASH_ALL LITERAL1
SIGHASH_NONE LITERAL1
SIGHASH_SINGLE LITERAL1
######################################
# Opcodes (LITERAL1)
#######################################
OP_0 LITERAL1
OP_PUSHDATA1 LITERAL1
OP_PUSHDATA2 LITERAL1
OP_PUSHDATA4 LITERAL1
OP_1NEGATE LITERAL1
OP_RESERVED LITERAL1
OP_1 LITERAL1
OP_2 LITERAL1
OP_3 LITERAL1
OP_4 LITERAL1
OP_5 LITERAL1
OP_6 LITERAL1
OP_7 LITERAL1
OP_8 LITERAL1
OP_9 LITERAL1
OP_10 LITERAL1
OP_11 LITERAL1
OP_12 LITERAL1
OP_13 LITERAL1
OP_14 LITERAL1
OP_15 LITERAL1
OP_16 LITERAL1
OP_NOP LITERAL1
OP_VER LITERAL1
OP_IF LITERAL1
OP_NOTIF LITERAL1
OP_VERIF LITERAL1
OP_VERNOTIF LITERAL1
OP_ELSE LITERAL1
OP_ENDIF LITERAL1
OP_VERIFY LITERAL1
OP_RETURN LITERAL1
OP_TOALTSTACK LITERAL1
OP_FROMALTSTACK LITERAL1
OP_2DROP LITERAL1
OP_2DUP LITERAL1
OP_3DUP LITERAL1
OP_2OVER LITERAL1
OP_2ROT LITERAL1
OP_2SWAP LITERAL1
OP_IFDUP LITERAL1
OP_DEPTH LITERAL1
OP_DROP LITERAL1
OP_DUP LITERAL1
OP_NIP LITERAL1
OP_OVER LITERAL1
OP_PICK LITERAL1
OP_ROLL LITERAL1
OP_ROT LITERAL1
OP_SWAP LITERAL1
OP_TUCK LITERAL1
OP_CAT LITERAL1
OP_SUBSTR LITERAL1
OP_LEFT LITERAL1
OP_RIGHT LITERAL1
OP_SIZE LITERAL1
OP_INVERT LITERAL1
OP_AND LITERAL1
OP_OR LITERAL1
OP_XOR LITERAL1
OP_EQUAL LITERAL1
OP_EQUALVERIFY LITERAL1
OP_RESERVED1 LITERAL1
OP_RESERVED2 LITERAL1
OP_1ADD LITERAL1
OP_1SUB LITERAL1
OP_2MUL LITERAL1
OP_2DIV LITERAL1
OP_NEGATE LITERAL1
OP_ABS LITERAL1
OP_NOT LITERAL1
OP_0NOTEQUAL LITERAL1
OP_ADD LITERAL1
OP_SUB LITERAL1
OP_MUL LITERAL1
OP_DIV LITERAL1
OP_MOD LITERAL1
OP_LSHIFT LITERAL1
OP_RSHIFT LITERAL1
OP_BOOLAND LITERAL1
OP_BOOLOR LITERAL1
OP_NUMEQUAL LITERAL1
OP_NUMEQUALVERIFY LITERAL1
OP_NUMNOTEQUAL LITERAL1
OP_LESSTHAN LITERAL1
OP_GREATERTHAN LITERAL1
OP_LESSTHANOREQUAL LITERAL1
OP_GREATERTHANOREQUAL LITERAL1
OP_MIN LITERAL1
OP_MAX LITERAL1
OP_WITHIN LITERAL1
OP_RIPEMD160 LITERAL1
OP_SHA1 LITERAL1
OP_SHA256 LITERAL1
OP_HASH160 LITERAL1
OP_HASH256 LITERAL1
OP_CODESEPARATOR LITERAL1
OP_CHECKSIG LITERAL1
OP_CHECKSIGVERIFY LITERAL1
OP_CHECKMULTISIG LITERAL1
OP_CHECKMULTISIGVERIFY LITERAL1
OP_NOP1 LITERAL1
OP_CHECKLOCKTIMEVERIFY LITERAL1
OP_CHECKSEQUENCEVERIFY LITERAL1
OP_NOP4 LITERAL1
OP_NOP5 LITERAL1
OP_NOP6 LITERAL1
OP_NOP7 LITERAL1
OP_NOP8 LITERAL1
OP_NOP9 LITERAL1
OP_NOP10 LITERAL1
OP_NULLDATA LITERAL1
OP_PUBKEYHASH LITERAL1
OP_PUBKEY LITERAL1
OP_INVALIDOPCODE LITERAL1
================================================
FILE: library.json
================================================
{
"name": "uBitcoin",
"version": "0.2.0",
"description": "Brings Bitcoin to embedded devices. Write your own hardware wallet, vending machine or any other bitcoin-powered device. Supports public and private keys, HD wallets, transactions and scripts. Everything required to start working with Bitcoin on microcontrollers.",
"keywords": "bitcoin, cryptography",
"repository":
{
"type": "git",
"url": "https://github.com/micro-bitcoin/uBitcoin.git"
},
"authors":
[
{
"name": "Stepan Snigirev",
"email": "snigirev.stepan@gmail.com",
"url": "https://stepansnigirev.com"
}
],
"license": "MIT",
"homepage": "https://micro-bitcoin.github.io",
"dependencies": {
},
"frameworks": "*",
"platforms": "*"
}
================================================
FILE: library.properties
================================================
name=uBitcoin
version=0.2.0
author=Stepan Snigirev
maintainer=Stepan Snigirev <snigirev.stepan@gmail.com>
sentence=Brings Bitcoin to embedded devices
paragraph=Write your own hardware wallet, vending machine or any other bitcoin-powered device. Supports public and private keys, HD wallets, transactions and scripts. Everything required to start working with Bitcoin on microcontrollers.
category=Data Processing
url=https://github.com/micro-bitcoin/uBitcoin
architectures=*
================================================
FILE: src/BaseClasses.cpp
================================================
#include "uBitcoin_conf.h"
#include "BaseClasses.h"
#include <cstdlib>
#if USE_STD_STRING
using std::string;
#define String string
#endif
size_t SerializeStream::serialize(const Streamable * s, size_t offset){
return s->to_stream(this, offset);
}
size_t ParseStream::parse(Streamable * s){
return s->from_stream(this);
}
/************ Parse Byte Stream Class ************/
ParseByteStream::ParseByteStream(const uint8_t * arr, size_t length, encoding_format f){
last = -1;
format = f;
cursor = 0;
len = (arr == NULL) ? 0 : length;
buf = arr;
}
// TODO: call prev constructor
ParseByteStream::ParseByteStream(const char * arr, encoding_format f){
last = -1;
format = f;
cursor = 0;
len = (arr == NULL) ? 0 : strlen(arr);
buf = (const uint8_t *) arr;
}
ParseByteStream::~ParseByteStream(){
buf = NULL;
}
size_t ParseByteStream::available(){
if(format == HEX_ENCODING){
return (len - cursor)/2;
}else{
return len-cursor;
}
};
int ParseByteStream::read(){
if(format == HEX_ENCODING){
if(cursor < len-1){
uint8_t c1 = hexToVal(buf[cursor]);
uint8_t c2 = hexToVal(buf[cursor+1]);
if(c1 < 0x10 && c2 < 0x10){
cursor +=2;
last = (c1<<4) + c2;
return last;
}
}
}else{
if(cursor < len){
uint8_t c = buf[cursor];
cursor ++;
last = c;
return c;
}
}
return -1;
}
int ParseByteStream::getLast(){
return last;
}
size_t ParseByteStream::read(uint8_t *arr, size_t length){
size_t cc = 0;
while(cc<length){
int b = read();
if(b<0){
return cc;
}
arr[cc] = (uint8_t)b & 0xFF;
cc++;
}
return cc;
}
/************ Serialize Byte Stream Class ************/
SerializeByteStream::SerializeByteStream(uint8_t * arr, size_t length, encoding_format f){
format = f; cursor = 0; buf = arr; len = length;
memset(arr, 0, length);
}
// TODO: should length be here? See above - we used strlen
SerializeByteStream::SerializeByteStream(char * arr, size_t length, encoding_format f){
format = f; cursor = 0; buf = (uint8_t *)arr; len = length;
memset(arr, 0, length);
};
size_t SerializeByteStream::available(){
size_t a = len-cursor;
if(format == HEX_ENCODING){
a = a/2;
}
return a;
};
size_t SerializeByteStream::write(uint8_t b){
if(available() > 0){
if(format == HEX_ENCODING){
buf[cursor] = ((b >> 4) & 0x0F) + '0';
if(buf[cursor] > '9'){
buf[cursor] += 'a'-'9'-1;
}
cursor++;
buf[cursor] = (b & 0x0F) + '0';
if(buf[cursor] > '9'){
buf[cursor] += 'a'-'9'-1;
}
cursor++;
}else{
buf[cursor] = b;
cursor++;
}
return 1;
}
return 0;
};
size_t SerializeByteStream::write(const uint8_t *arr, size_t length){
size_t l = 0;
while(available()>0 && l < length){
write(arr[l]);
l++;
}
return l;
};
/************ Readable Class ************/
#ifdef ARDUINO
size_t Readable::printTo(Print& p) const{
size_t len = this->stringLength()+1;
char * arr = (char *)calloc(len, sizeof(char));
toString(arr, len);
p.print(arr);
free(arr);
return len-1;
}
#endif
#if USE_ARDUINO_STRING || USE_STD_STRING
String Readable::toString() const{
size_t len = this->stringLength()+1;
char * arr = (char *)calloc(len, sizeof(char));
toString(arr, len);
String s = arr;
free(arr);
return s;
};
#endif
/************ Streamable Class ************/
#if USE_ARDUINO_STRING || USE_STD_STRING
String Streamable::serialize(size_t offset, size_t len) const{
if(len == 0){
len = (length()-offset);
}
char * arr = (char *)calloc(2*len+1, sizeof(char));
serialize(arr, 2*len, offset, HEX_ENCODING);
String s = arr;
free(arr);
return s;
};
#endif
size_t Streamable::serialize(uint8_t * arr, size_t len, size_t offset, encoding_format format) const{
SerializeByteStream s(arr, len, format);
return to_stream(&s, offset);
}
================================================
FILE: src/BaseClasses.h
================================================
#ifndef __BITCOIN_BASE_CLASSES_H__
#define __BITCOIN_BASE_CLASSES_H__
#include "uBitcoin_conf.h"
#include "Conversion.h"
#include <stdint.h>
#include <string.h>
enum encoding_format{
RAW = 0,
HEX_ENCODING = 16
// TODO: would be nice to have base64 encoding here
};
enum parse_status{
PARSING_DONE = 0,
PARSING_INCOMPLETE = 1,
PARSING_FAILED = 2
};
// error codes struct / class?
class Streamable;
class ParseStream{
public:
virtual size_t available(){ return 0; };
virtual int read(){ return -1; };
virtual size_t read(uint8_t *arr, size_t length){ return 0; };
virtual int getLast(){ return -1; };
size_t parse(Streamable * s);
};
// TODO: skip leading non-hex if it's hex format
class ParseByteStream: public ParseStream{
private:
const uint8_t * buf;
size_t cursor;
size_t len;
encoding_format format;
int last;
public:
ParseByteStream(const uint8_t * arr, size_t length, encoding_format f=RAW);
ParseByteStream(const char * arr, encoding_format f=HEX_ENCODING);
~ParseByteStream();
size_t available();
int read();
size_t read(uint8_t *arr, size_t length);
virtual int getLast();
};
class SerializeStream{
public:
virtual size_t available(){ return 0; };
virtual size_t write(uint8_t b){ return 0; };
virtual size_t write(const uint8_t *arr, size_t len){ return 0; };
size_t serialize(const Streamable * s, size_t offset);
};
class SerializeByteStream: public SerializeStream{
private:
uint8_t * buf;
size_t cursor;
size_t len;
encoding_format format;
public:
SerializeByteStream(uint8_t * arr, size_t length, encoding_format f=RAW);
explicit SerializeByteStream(char * arr, size_t length, encoding_format f=HEX_ENCODING);
size_t available();
size_t write(uint8_t b);
size_t write(const uint8_t *arr, size_t length);
};
/** Abstract Readable class that can be encoded as a string and displayed to the user
* Can be converted to and from a string (char *, Arduino String and std::string)
* In Arduino it can be directly printed to the serial port, display or other Print device
*/
#ifdef ARDUINO
class Readable: public Printable{
#else
class Readable{
#endif
private:
protected:
/* override these functions in your class */
virtual size_t to_str(char * buf, size_t len) const = 0;
virtual size_t from_str(const char * buf, size_t len) = 0;
public:
/* override these function in your class */
virtual size_t stringLength() const = 0;
size_t toString(char * buf, size_t len) const{ return this->to_str(buf, len); };
size_t fromString(const char * buf, size_t len){ return this->from_str(buf, len); };
size_t fromString(const char * buf){ return this->from_str(buf, strlen(buf)); };
#ifdef ARDUINO
size_t printTo(Print& p) const;
#endif
#if USE_ARDUINO_STRING
String toString() const;
operator String(){ return this->toString(); };
#endif
#if USE_STD_STRING
std::string toString() const;
operator std::string(){ return this->toString(); };
#endif
};
/** Abstract Streamable class that can be serialized to/from a sequence of bytes
* and sent to Stream (File, Serial, Bluetooth) without allocating extra memory
* Class can be parsed and serialized in raw and hex formats
*/
class Streamable: public Readable{
friend class SerializeStream;
friend class ParseStream;
private:
protected:
virtual size_t from_stream(ParseStream *s) = 0;
virtual size_t to_stream(SerializeStream *s, size_t offset) const = 0;
virtual size_t to_str(char * buf, size_t len) const{
return serialize(buf, len);
}
virtual size_t from_str(const char * buf, size_t len){
return parse(buf, len);
}
parse_status status;
size_t bytes_parsed;
public:
Streamable() { status = PARSING_DONE; bytes_parsed = 0; };
virtual void reset(){ status = PARSING_DONE; bytes_parsed = 0; }; // used to reset parsing and mb object
virtual size_t length() const = 0;
virtual size_t stringLength() const{ return 2*length(); };
/** \brief Gets parsing status.
* PARSING_DONE - everything is ok,
* PARSING_INCOMPLETE - some data is still missing
* PARSING_FAILED - something went wrong, the data is probably incorrect.
*/
parse_status getStatus(){ return status; };
/** \brief Sets parsing status. Should be used with care. */
void setStatus(parse_status s){ status = s; };
size_t parse(const uint8_t * arr, size_t len, encoding_format format=RAW){
ParseByteStream s(arr, len, format);
return from_stream(&s);
}
#if USE_ARDUINO_STRING
size_t parse(String arr, encoding_format format=HEX_ENCODING){
return parse(arr.c_str(), strlen(arr.c_str()), format);
}
#endif
#if USE_STD_STRING
size_t parse(std::string arr, encoding_format format=HEX_ENCODING){
return parse(arr.c_str(), strlen(arr.c_str()), format);
}
#endif
#if !(USE_ARDUINO_STRING || USE_STD_STRING)
size_t parse(const char * arr, encoding_format format=HEX_ENCODING){
return parse(arr, strlen(arr), format);
}
#endif
size_t serialize(uint8_t * arr, size_t len, size_t offset = 0, encoding_format format=RAW) const;
size_t parse(const char * arr, size_t len, encoding_format format=HEX_ENCODING){
return parse((const uint8_t *) arr, len, format);
}
size_t serialize(char * arr, size_t len, size_t offset = 0, encoding_format format=HEX_ENCODING) const{
return serialize((uint8_t *)arr, len, offset, format);
}
#if USE_ARDUINO_STRING
String serialize(size_t offset=0, size_t len=0) const;
#endif
#if USE_STD_STRING
std::string serialize(size_t offset=0, size_t len=0) const;
std::string serialize(int offset, int len) const{
return serialize((size_t)offset, (size_t)len);
};
std::string serialize(size_t offset, int len) const{
return serialize((size_t)offset, (size_t)len);
};
#endif
};
#endif // __BITCOIN_BASE_CLASSES_H__
================================================
FILE: src/Bitcoin.cpp
================================================
#include "Bitcoin.h"
#include "Hash.h"
#include "Conversion.h"
#include <stdint.h>
#include <string.h>
#include "utility/trezor/sha2.h"
#include "utility/trezor/rfc6979.h"
#include "utility/trezor/ecdsa.h"
#include "utility/trezor/secp256k1.h"
#include "utility/segwit_addr.h"
#include "utility/trezor/bip39.h"
#include "utility/trezor/memzero.h"
#if USE_STD_STRING
using std::string;
#define String std::string
#endif
// error code when parsing fails
int ubtc_errno = 0;
const char * generateMnemonic(uint8_t numWords){
if(numWords<12 || numWords > 24 || numWords % 3 != 0){
return NULL;
}
int strength = numWords*32/3;
return mnemonic_generate(strength);
}
const char * generateMnemonic(uint8_t numWords, const uint8_t * entropy_data, size_t dataLen){
if(numWords<12 || numWords > 24 || numWords % 3 != 0){
return NULL;
}
uint8_t hash[32];
sha256(entropy_data, dataLen, hash);
size_t len = numWords*4/3;
return mnemonic_from_data(hash, len);
}
const char * mnemonicFromEntropy(const uint8_t * entropy_data, size_t dataLen){
return mnemonic_from_data(entropy_data, dataLen);
}
size_t mnemonicToEntropy(const char * mnemonic, size_t mnemonicLen, uint8_t * output, size_t outputLen){
int num_words = 1;
for (size_t i = 0; i < strlen(mnemonic); i++){
if(mnemonic[i] == ' '){
num_words ++;
}
}
size_t entropy_len = (num_words*4)/3;
if(outputLen < entropy_len){
return 0;
}
uint8_t res[33] = {0};
int r = mnemonic_to_entropy(mnemonic, res);
if(r == 0){
return 0;
}
memcpy(output, res, entropy_len);
return entropy_len;
}
#if USE_ARDUINO_STRING || USE_STD_STRING
size_t mnemonicToEntropy(String mnemonic, uint8_t * output, size_t outputLen){
return mnemonicToEntropy(mnemonic.c_str(), mnemonic.length(), output, outputLen);
}
#else
size_t mnemonicToEntropy(char * mnemonic, uint8_t * output, size_t outputLen){
return mnemonicToEntropy(mnemonic, strlen(mnemonic), output, outputLen);
}
#endif
const char * generateMnemonic(const uint8_t * entropy_data, size_t dataLen){
return generateMnemonic(24, entropy_data, dataLen);
}
#if !(USE_ARDUINO_STRING || USE_STD_STRING)
const char * generateMnemonic(uint8_t numWords, const char * entropy_string){
return generateMnemonic(numWords, (uint8_t*)entropy_string, strlen(entropy_string));
}
const char * generateMnemonic(const char * entropy_string){
return generateMnemonic(24, entropy_string);
}
bool checkMnemonic(const char * mnemonic){
return mnemonic_check(mnemonic);
}
#else
const char * generateMnemonic(uint8_t numWords, const String entropy_string){
return generateMnemonic(numWords, (uint8_t*)entropy_string.c_str(), strlen(entropy_string.c_str()));
}
const char * generateMnemonic(const String entropy_string){
return generateMnemonic(24, entropy_string);
}
bool checkMnemonic(const String mnemonic){
return mnemonic_check(mnemonic.c_str());
}
#endif
// ---------------------------------------------------------------- Signature class
Signature::Signature(){
memzero(tot, 3); index = 0;
memzero(r, 32);
memzero(s, 32);
}
Signature::Signature(const uint8_t r_arr[32], const uint8_t s_arr[32]){
memzero(tot, 3); index = 0;
memcpy(r, r_arr, 32);
memcpy(s, s_arr, 32);
}
Signature::Signature(const uint8_t * der){
memzero(tot, 3); index = 0; memzero(r, 32); memzero(s, 32);
fromDer(der, der[1]+2);
}
Signature::Signature(const uint8_t * der, size_t derLen){
memzero(tot, 3); index = 0; memzero(r, 32); memzero(s, 32);
fromDer(der, derLen);
}
Signature::Signature(const char * der){
memzero(tot, 3); index = 0; memzero(r, 32); memzero(s, 32);
ParseByteStream s(der);
Signature::from_stream(&s);
}
size_t Signature::from_stream(ParseStream *stream){
// der encoding is probably the most uneffective way to encode signatures...
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
// * total-length: 1-byte length descriptor of everything that follows
// * R-length: 1-byte length descriptor of the R value that follows.
// * R: arbitrary-length big-endian encoded R value. It must use the shortest
// possible encoding for a positive integers (which means no null bytes at
// the start, except a single one when the next byte has its highest bit set).
// * S-length: 1-byte length descriptor of the S value that follows.
// * S: arbitrary-length big-endian encoded S value. The same rules apply.
if(status == PARSING_FAILED){
return 0;
}
if(status == PARSING_DONE){
bytes_parsed = 0;
memzero(tot, 3);
memzero(r, 32);
memzero(s, 32);
}
status = PARSING_INCOMPLETE;
size_t bytes_read = 0;
uint8_t c = 0;
if(stream->available() && bytes_parsed+bytes_read < 1){
c = stream->read();
bytes_read++;
if(c!=0x30){
status = PARSING_FAILED;
return bytes_read;
}
}
if(stream->available() && bytes_parsed+bytes_read < 2){
tot[0] = stream->read();
bytes_read++;
if(tot[0] > 70){ status = PARSING_FAILED; return bytes_read; }
}
// r
if(stream->available() && bytes_parsed+bytes_read < 3){
c = stream->read();
bytes_read++;
if(c != 0x02){ status = PARSING_FAILED; return bytes_read; }
}
if(stream->available() && bytes_parsed+bytes_read < 4){
tot[1] = stream->read();
bytes_read++;
if(tot[1] > 33){ status = PARSING_FAILED; return bytes_read; }
}
if(stream->available() && tot[1]==33 && bytes_parsed+bytes_read < 5){
c = stream->read();
bytes_read++;
if(c != 0){ status = PARSING_FAILED; return bytes_read; }
}
while(stream->available() && bytes_parsed+bytes_read < (size_t)4+tot[1]){
r[bytes_parsed+bytes_read-4+32-tot[1]] = stream->read();
bytes_read++;
}
if(rlen() != tot[1]){ status = PARSING_FAILED; return bytes_read; }
// s
if(stream->available() && bytes_parsed+bytes_read < (size_t)4+tot[1]+1){
c = stream->read();
bytes_read++;
if(c != 0x02){ status = PARSING_FAILED; return bytes_read; }
}
if(stream->available() && bytes_parsed+bytes_read < (size_t)4+tot[1]+2){
tot[2] = stream->read();
bytes_read++;
if(tot[2] > 33){ status = PARSING_FAILED; return bytes_read; }
}
if(stream->available() && tot[2]==33 && bytes_parsed+bytes_read < (size_t)4+tot[1]+3){
c = stream->read();
bytes_read++;
if(c != 0){ status = PARSING_FAILED; return bytes_read; }
}
while(stream->available() && bytes_parsed+bytes_read < (size_t)4+tot[1]+2+tot[2]){
s[bytes_parsed+bytes_read-4-tot[1]-2-tot[2]+32] = stream->read();
bytes_read++;
}
if(slen() != tot[2]){ status = PARSING_FAILED; return bytes_read; }
if(bytes_parsed+bytes_read == (size_t)4+tot[1]+2+tot[2]){
status = PARSING_DONE;
}
bytes_parsed+=bytes_read;
return bytes_read;
}
size_t Signature::to_stream(SerializeStream *stream, size_t offset) const{
uint8_t arr[72];
der(arr, sizeof(arr));
size_t l = Signature::length();
size_t bytes_written = 0;
while(stream->available() && offset+bytes_written < l){
stream->write(arr[offset+bytes_written]);
bytes_written++;
}
return bytes_written;
}
size_t Signature::rlen() const{
uint8_t len = 33;
for(int i=0; i<32; i++){
if(r[i] > 0){
if(r[i] < 0x80){
len --;
}
break;
}else{
len--;
}
}
return len;
}
size_t Signature::slen() const{
uint8_t len = 33;
for(int i=0; i<32; i++){
if(s[i] > 0){
if(s[i] < 0x80){
len --;
}
break;
}else{
len--;
}
}
return len;
}
size_t Signature::length() const{
return rlen()+slen()+6;
}
size_t Signature::fromDer(const uint8_t * raw, size_t rawLen){
ParseByteStream stream(raw, rawLen);
return Signature::from_stream(&stream);
}
size_t Signature::der(uint8_t * bytes, size_t len) const{
memzero(bytes, len);
uint8_t _rlen = rlen();
uint8_t _slen = slen();
bytes[0] = 0x30;
bytes[1] = 4+_rlen+2+_slen-2;
bytes[2] = 0x02;
bytes[3] = _rlen;
if(_rlen == 33){
memcpy(bytes+5, r, 32);
}else{
memcpy(bytes+4, r+32-_rlen, _rlen);
}
bytes[4+_rlen] = 0x02;
bytes[4+_rlen+1] = _slen;
if(_slen == 33){
memcpy(bytes+4+_rlen+3, s, 32);
}else{
memcpy(bytes+4+_rlen+2, s+32-_slen, _slen);
}
return 4+_rlen+2+_slen;
}
void Signature::bin(uint8_t * arr, size_t len) const{
size_t l = len;
if(l > 32){
l = 32;
}
memcpy(arr, r, l);
if(len > 32){
l = len-32;
if(l > 32){
l = 32;
}
memcpy(arr+32, s, l);
}
if(len > 64){
arr[64] = index;
}
}
void Signature::fromBin(const uint8_t * arr, size_t len){
size_t l = len;
if(l > 32){
l = 32;
}
memcpy(r, arr, l);
if(len > 32){
l = len-32;
if(l > 32){
l = 32;
}
memcpy(s, arr+32, l);
}
if(len > 64){
index = arr[64];
}
}
// ---------------------------------------------------------------- SchnorrSignature
SchnorrSignature::SchnorrSignature(){
memzero(r, 32);
memzero(s, 32);
}
SchnorrSignature::SchnorrSignature(const uint8_t r_arr[32], const uint8_t s_arr[32]){
memcpy(r, r_arr, 32);
memcpy(s, s_arr, 32);
}
SchnorrSignature::SchnorrSignature(const uint8_t rs_arr[64]){
memcpy(r, rs_arr, 32);
memcpy(s, rs_arr+32, 32);
}
SchnorrSignature::SchnorrSignature(const char * rs){
memzero(r, 32); memzero(s, 32);
ParseByteStream s(rs);
SchnorrSignature::from_stream(&s);
}
size_t SchnorrSignature::from_stream(ParseStream *stream){
if(status == PARSING_FAILED){
return 0;
}
if(status == PARSING_DONE){
bytes_parsed = 0;
memzero(r, 32);
memzero(s, 32);
}
status = PARSING_INCOMPLETE;
size_t bytes_read = 0;
while(stream->available() && bytes_parsed+bytes_read < 32){
r[bytes_read+bytes_parsed] = stream->read();
bytes_read++;
}
while(stream->available() && bytes_parsed+bytes_read < 64){
s[bytes_read+bytes_parsed-32] = stream->read();
bytes_read++;
}
if(bytes_parsed+bytes_read == 64){
status = PARSING_DONE;
}
bytes_parsed+=bytes_read;
return bytes_read;
}
size_t SchnorrSignature::to_stream(SerializeStream *stream, size_t offset) const{
uint8_t arr[64];
memcpy(arr, r, 32);
memcpy(arr+32, s, 32);
size_t l = sizeof(arr);
size_t bytes_written = 0;
while(stream->available() && offset+bytes_written < l){
stream->write(arr[offset+bytes_written]);
bytes_written++;
}
return bytes_written;
}
// ---------------------------------------------------------------- PublicKey class
int PublicKey::legacyAddress(char * address, size_t len, const Network * network) const{
memzero(address, len);
uint8_t buffer[20];
uint8_t sec_arr[65] = { 0 };
int l = sec(sec_arr, sizeof(sec_arr));
hash160(sec_arr, l, buffer);
uint8_t addr[21];
addr[0] = network->p2pkh;
memcpy(addr+1, buffer, 20);
return toBase58Check(addr, 21, address, len);
}
#if USE_ARDUINO_STRING || USE_STD_STRING
String PublicKey::legacyAddress(const Network * network) const{
char addr[40] = { 0 };
legacyAddress(addr, sizeof(addr), network);
return String(addr);
}
#endif
int PublicKey::segwitAddress(char address[], size_t len, const Network * network) const{
memzero(address, len);
if(len < 76){ // TODO: 76 is too much for native segwit
return 0;
}
uint8_t hash[20];
uint8_t sec_arr[65] = { 0 };
int l = sec(sec_arr, sizeof(sec_arr));
hash160(sec_arr, l, hash);
segwit_addr_encode(address, network->bech32, 0, hash, 20);
return 76;
}
#if USE_ARDUINO_STRING || USE_STD_STRING
String PublicKey::segwitAddress(const Network * network) const{
char addr[76] = { 0 };
segwitAddress(addr, sizeof(addr), network);
return String(addr);
}
#endif
int PublicKey::nestedSegwitAddress(char address[], size_t len, const Network * network) const{
memzero(address, len);
uint8_t script[22] = { 0 };
// script[0] = 0x00; // no need to set - already zero
script[1] = 0x14;
uint8_t sec_arr[65] = { 0 };
int l = sec(sec_arr, sizeof(sec_arr));
hash160(sec_arr, l, script+2);
uint8_t addr[21];
addr[0] = network->p2sh;
hash160(script, 22, addr+1);
return toBase58Check(addr, 21, address, len);
}
#if USE_ARDUINO_STRING || USE_STD_STRING
String PublicKey::nestedSegwitAddress(const Network * network) const{
char addr[40] = { 0 };
nestedSegwitAddress(addr, sizeof(addr), network);
return String(addr);
}
#endif
Script PublicKey::script(ScriptType type) const{
return Script(*this, type);
}
bool PublicKey::verify(const Signature sig, const uint8_t hash[32]) const{
uint8_t signature[64] = {0};
sig.bin(signature, 64);
uint8_t pub[65];
serialize(pub, 65);
return (ecdsa_verify_digest(&secp256k1, pub, signature, hash)==0);
}
bool PublicKey::schnorr_verify(const SchnorrSignature sig, const uint8_t hash[32]) const{
PublicKey pub = *this;
if(!pub.isEven()){
pub = -pub;
}
uint8_t rs[64];
sig.serialize(rs, sizeof(rs));
PublicKey R;
R.from_x(rs, 32);
// calculate hash using tagged hash with "BIP0340/challenge" prefix
uint8_t e[32];
uint8_t tmp[32];
TaggedHash tch("BIP0340/challenge");
// write R
tch.write(rs, 32);
// write xonly pubkey
pub.x(tmp, sizeof(tmp));
tch.write(tmp, sizeof(tmp));
// write message
tch.write(hash, 32);
tch.end(e);
PrivateKey challenge(e);
PublicKey S = challenge*pub + R;
S.x(tmp, sizeof(tmp));
PrivateKey s(rs+32);
s.publicKey().x(e, 32);
return memcmp(tmp, e, 32) == 0;
};
// ---------------------------------------------------------------- PrivateKey class
size_t PrivateKey::from_stream(ParseStream *s){
if(status == PARSING_FAILED){
return 0;
}
if(status == PARSING_DONE){
bytes_parsed = 0;
}
status = PARSING_INCOMPLETE;
size_t bytes_read = 0;
while(s->available() > 0 && bytes_parsed+bytes_read < 32){
num[bytes_parsed+bytes_read] = s->read();
bytes_read++;
}
if(bytes_parsed+bytes_read == 32){
status = PARSING_DONE;
uint8_t zero[32] = { 0 };
if(memcmp(num, zero, 32)==0){ // should we add something else here?
status = PARSING_FAILED;
}
bignum256 n;
bn_read_be(num, &n);
bn_mod(&n, &secp256k1.order);
bn_write_be(&n, num);
pubKey = *this * GeneratorPoint;
}
bytes_parsed += bytes_read;
return bytes_read;
}
PrivateKey::PrivateKey(void){
reset();
memzero(num, 32); // empty key
network = &DEFAULT_NETWORK;
}
PrivateKey::PrivateKey(const uint8_t * secret_arr, bool use_compressed, const Network * net){
reset();
memcpy(num, secret_arr, 32);
network = net;
pubKey = *this * GeneratorPoint;
pubKey.compressed = use_compressed;
}
PrivateKey::PrivateKey(const ECScalar other){
reset();
other.getSecret(num);
pubKey = *this * GeneratorPoint;
pubKey.compressed = true;
network = &DEFAULT_NETWORK;
}
/*PrivateKey &PrivateKey::operator=(const PrivateKey &other){
if (this == &other){ return *this; } // self-assignment
reset();
other.getSecret(num);
network = other.network;
pubKey = *this * GeneratorPoint;
pubKey.compressed = other.pubKey.compressed;
return *this;
};*/
PrivateKey::~PrivateKey(void) {
reset();
// erase secret key from memory
memzero(num, 32);
}
int PrivateKey::wif(char * wifArr, size_t wifSize) const{
memzero(wifArr, wifSize);
uint8_t wifHex[34] = { 0 }; // prefix + 32 bytes secret (+ compressed )
size_t len = 33;
wifHex[0] = network->wif;
memcpy(wifHex+1, num, 32);
if(pubKey.compressed){
wifHex[33] = 0x01;
len++;
}
size_t l = toBase58Check(wifHex, len, wifArr, wifSize);
memzero(wifHex, sizeof(wifHex)); // secret should not stay in RAM
return l;
}
#if USE_ARDUINO_STRING || USE_STD_STRING
String PrivateKey::wif() const{
char wifString[53] = { 0 };
wif(wifString, sizeof(wifString));
return String(wifString);
}
#endif
int PrivateKey::fromWIF(const char * wifArr, size_t wifSize){
uint8_t arr[40] = { 0 };
size_t l = fromBase58Check(wifArr, wifSize, arr, sizeof(arr));
if( (l < 33) || (l > 34) ){
memzero(num, 32);
return 0;
}
bool compressed;
network = &DEFAULT_NETWORK;
bool found = false;
for(int i=0; i<networks_len; i++){
if(arr[0] == networks[i]->wif){
network = networks[i];
found = true;
break;
}
}
if(!found){
return 0;
}
if(l == 34){
compressed = (arr[33] > 0);
}
if(l == 33){
compressed = false;
}
memcpy(num, arr+1, 32);
memzero(arr, 40); // clear memory
pubKey = *this * GeneratorPoint;
pubKey.compressed = compressed;
return 1;
}
int PrivateKey::fromWIF(const char * wifArr){
return fromWIF(wifArr, strlen(wifArr));
}
PublicKey PrivateKey::publicKey() const{
return pubKey;
}
int PrivateKey::address(char * address, size_t len) const{
return pubKey.address(address, len, network);
}
int PrivateKey::legacyAddress(char * address, size_t len) const{
return pubKey.legacyAddress(address, len, network);
}
int PrivateKey::segwitAddress(char * address, size_t len) const{
return pubKey.segwitAddress(address, len, network);
}
int PrivateKey::nestedSegwitAddress(char * address, size_t len) const{
return pubKey.nestedSegwitAddress(address, len, network);
}
#if USE_ARDUINO_STRING || USE_STD_STRING
String PrivateKey::address() const{
return pubKey.address(network);
}
String PrivateKey::legacyAddress() const{
return pubKey.legacyAddress(network);
}
String PrivateKey::segwitAddress() const{
return pubKey.segwitAddress(network);
}
String PrivateKey::nestedSegwitAddress() const{
return pubKey.nestedSegwitAddress(network);
}
#endif
int PrivateKey::ecdh(const PublicKey pub, uint8_t shared_secret[32], bool use_hash){
// calculate pk * pub, serialize as uncompressed point and hash <x><y>
ECPoint mult = *this * pub;
mult.compressed = false;
uint8_t sec[65];
mult.sec(sec, sizeof(sec));
if(use_hash){
sha256(sec+1, 64, shared_secret);
}else{
// just copy x
memcpy(shared_secret, sec+1, 32);
}
return 1;
}
static int is_canonical(uint8_t by, uint8_t sig[64]){
return 1;
}
Signature PrivateKey::sign(const uint8_t hash[32]) const{
uint8_t signature[65] = {0};
uint8_t i = 0;
ecdsa_sign_digest(&secp256k1, num, hash, signature, &i, &is_canonical);
Signature sig(signature, signature+32);
sig.index = i;
return sig;
}
SchnorrSignature PrivateKey::schnorr_sign(const uint8_t hash[32]) const{
PrivateKey prv = *this;
PublicKey pub = prv.publicKey();
// check if pubkey is even, if not - negate
if(!pub.isEven()){
prv = -prv;
pub = -pub;
}
uint8_t r[32];
uint8_t s[32];
uint8_t tmp[32];
// generate k using tagged hash with "BIP0340/nonce" prefix
uint8_t nonce[32];
TaggedHash tnonce("BIP0340/nonce");
prv.getSecret(tmp);
tnonce.write(tmp, sizeof(tmp));
pub.x(tmp, sizeof(tmp));
tnonce.write(tmp, sizeof(tmp));
tnonce.write(hash, 32);
tnonce.end(nonce);
PrivateKey k(nonce);
PublicKey R = k.publicKey();
// flip k if r is not even
if(!R.isEven()){
k = -k;
R = -R;
}
// calculate hash using tagged hash with "BIP0340/challenge" prefix
uint8_t e[32];
TaggedHash tch("BIP0340/challenge");
R.x(r, sizeof(r));
tch.write(r, sizeof(r));
pub.x(tmp, sizeof(tmp));
tch.write(tmp, sizeof(tmp));
tch.write(hash, 32);
tch.end(e);
PrivateKey challenge(e);
// calculate s
PrivateKey S = k + challenge*prv;
S.getSecret(s);
SchnorrSignature sig(r, s);
return sig;
}
#if USE_ARDUINO_STRING || USE_STD_STRING
PrivateKey::PrivateKey(const String wifString){
fromWIF(wifString.c_str());
}
#else
PrivateKey::PrivateKey(const char * wifArr){
fromWIF(wifArr);
}
#endif
================================================
FILE: src/Bitcoin.h
================================================
/** @file Bitcoin.h
*/
#ifndef __BITCOIN_H__
#define __BITCOIN_H__
#include "uBitcoin_conf.h"
#include "BaseClasses.h"
#include "BitcoinCurve.h"
#include "Conversion.h"
#include "Networks.h"
#include "utility/trezor/rand.h"
#include <stdint.h>
#include <string.h>
/* TODO:
- autodetect mnemonic w/o passwd or xprv
- HD.derive()
- accept strings instead of char arrays for txout (address) and other things
- fail if script or witness is too large
- fix is_canonical function
- refactor fromWIF to return bytes read
- fix all warnings
- psbt
- docs
- publish on arduino libs and mbed
- operators +, += in script - concatenation
- signature & everything from char array might be not a bright idea
- tests (egde cases!)
- sidechannel for pubkey calculation - use rng
*/
extern int ubtc_errno;
// number of rounds for mnemonic to seed conversion
#define PBKDF2_ROUNDS 2048
#define HARDENED_INDEX 0x80000000
/** \brief Common script types */
enum ScriptType{
UNKNOWN_TYPE,
/** \brief a script directly in ScriptPubkey and not one of below */
DIRECT_SCRIPT,
/** \brief default script for signing */
P2PKH,
P2SH,
P2WPKH,
P2WSH,
P2SH_P2WPKH,
P2SH_P2WSH,
MULTISIG
};
/** \brief SigHash types */
enum SigHashType{
SIGHASH_ALL = 1,
SIGHASH_NONE = 2,
SIGHASH_SINGLE = 3
};
/* forward declarations */
class Signature;
class SchnorrSignature;
class PublicKey;
class PrivateKey;
class HDPublicKey;
class HDPrivateKey;
class Script;
class TxIn;
const char * generateMnemonic(uint8_t numWords);
const char * generateMnemonic(uint8_t numWords, const uint8_t * entropy_data, size_t dataLen);
const char * generateMnemonic(const uint8_t * entropy_data, size_t dataLen);
#if USE_ARDUINO_STRING
const char * generateMnemonic(uint8_t numWords, const String entropy_string);
const char * generateMnemonic(const String entropy_string);
bool checkMnemonic(const String mnemonic);
#elif USE_STD_STRING
const char * generateMnemonic(uint8_t numWords, const std::string entropy_string);
const char * generateMnemonic(const std::string entropy_string);
bool checkMnemonic(const std::string mnemonic);
#else
const char * generateMnemonic(uint8_t numWords, const char * entropy_string);
const char * generateMnemonic(const char * entropy_string);
bool checkMnemonic(const char * mnemonic);
#endif
const char * mnemonicFromEntropy(const uint8_t * entropy_data, size_t dataLen);
size_t mnemonicToEntropy(const char * mnemonic, size_t mnemonic_len, uint8_t * output, size_t outputLen);
#if USE_ARDUINO_STRING
size_t mnemonicToEntropy(String mnemonic, uint8_t * output, size_t outputLen);
#elif USE_STD_STRING
size_t mnemonicToEntropy(std::string mnemonic, uint8_t * output, size_t outputLen);
#else
size_t mnemonicToEntropy(char * mnemonic, uint8_t * output, size_t outputLen);
#endif
/**
* PublicKey class.
*
* Derived from ECPoint class, therefore you can add or substract them, multiply by ECScalar or PrivateKey.
*
* `compressed` flag determines what public key sec format to use by default:
* - `compressed = false` will use 65-byte representation (`04<x><y>`)
* - `compressed = true` will use 33-byte representation (`03<x>` if y is odd, `02<x>` if y is even)
*/
class PublicKey : public ECPoint{
public:
PublicKey(){ reset(); };
PublicKey(const uint8_t pubkeyArr[64], bool use_compressed){ reset(); memcpy(point, pubkeyArr, 64); compressed=use_compressed; };
PublicKey(const uint8_t * secArr){ reset(); parse(secArr, 33 + ((uint8_t)(secArr[0]==0x04))*32); };
explicit PublicKey(const char * secHex){ reset(); from_str(secHex, strlen(secHex)); };
PublicKey(const ECPoint p){ reset(); memcpy(point, p.point, 64); compressed=p.compressed; };
/**
* \brief Fills `addr` with legacy Pay-To-Pubkey-Hash address (P2PKH, `1...` for mainnet)
*/
int legacyAddress(char * addr, size_t len, const Network * network = &DEFAULT_NETWORK) const;
/**
* \brief Fills `addr` with native segwit address (P2WPKH, `bc1...` for mainnet)
*/
int segwitAddress(char * addr, size_t len, const Network * network = &DEFAULT_NETWORK) const;
/**
* \brief Fills `addr` with nested segwit address (P2SH-P2WPKH, `3...` for mainnet)
*/
int nestedSegwitAddress(char * addr, size_t len, const Network * network = &DEFAULT_NETWORK) const;
/**
* \brief Alias for `legacyAddress`
*/
int address(char * address, size_t len, const Network * network = &DEFAULT_NETWORK) const{ return legacyAddress(address, len, network); };
#if USE_ARDUINO_STRING
String legacyAddress(const Network * network = &DEFAULT_NETWORK) const;
String segwitAddress(const Network * network = &DEFAULT_NETWORK) const;
String nestedSegwitAddress(const Network * network = &DEFAULT_NETWORK) const;
String address(const Network * network = &DEFAULT_NETWORK) const{ return legacyAddress(network); };
#endif
#if USE_STD_STRING
std::string legacyAddress(const Network * network = &DEFAULT_NETWORK) const;
std::string segwitAddress(const Network * network = &DEFAULT_NETWORK) const;
std::string nestedSegwitAddress(const Network * network = &DEFAULT_NETWORK) const;
std::string address(const Network * network = &DEFAULT_NETWORK) const{ return legacyAddress(network); };
#endif
/**
* \brief verifies the ECDSA signature of the hash of the message
*/
bool verify(const Signature sig, const uint8_t hash[32]) const;
/**
* \brief verifies the Schnorr signature of the hash of the message
*/
bool schnorr_verify(const SchnorrSignature sig, const uint8_t hash[32]) const;
/**
* \brief Returns a Script with the type: `P2PKH`, `P2WPKH` or `P2SH_P2WPKH`
*/
Script script(ScriptType type = P2PKH) const;
};
/**
* PrivateKey class.
* Corresponding public key (point on curve) will be calculated in the constructor.
* as point calculation is pretty slow, class initialization can take some time.
*/
class PrivateKey : public ECScalar{
protected:
/** \brief corresponding point on curve ( secret * G ) */
PublicKey pubKey;
virtual size_t to_str(char * buf, size_t len) const{ return wif( buf, len); };
virtual size_t from_str(const char * buf, size_t len){ return fromWIF(buf, len); };
virtual size_t from_stream(ParseStream *s);
public:
PrivateKey();
PrivateKey(const uint8_t secret_arr[32], bool use_compressed = true, const Network * net = &DEFAULT_NETWORK);
PrivateKey(const ECScalar sc);
#if USE_ARDUINO_STRING
PrivateKey(const String wifString);
#elif USE_STD_STRING
PrivateKey(const std::string wifString);
#else
PrivateKey(const char * wifArr);
#endif
~PrivateKey();
/** \brief Length of the key in WIF format (52). In reality not always 52... */
virtual size_t stringLength() const{ return 52; };
virtual size_t length() const{ return 32; };
void setSecret(const uint8_t secret_arr[32]){ memcpy(num, secret_arr, 32); pubKey = *this * GeneratorPoint; };
/** \brief Pointer to the network to use. Mainnet or Testnet */
const Network * network;
/** \brief Writes the private key in Wallet Import Format */
int wif(char * wifArr, size_t len) const;
#if USE_ARDUINO_STRING
String wif() const;
#endif
#if USE_STD_STRING
std::string wif() const;
#endif
/** \brief Loads the private key from a string in Wallet Import Format */
int fromWIF(const char * wifArr, size_t wifSize);
int fromWIF(const char * wifArr);
/** \brief Returns the corresponding PublicKey = secret * GeneratorPoint */
PublicKey publicKey() const;
/** \brief Signs the hash and returns the Signature */
Signature sign(const uint8_t hash[32]) const; // pass 32-byte hash of the message here
/** \brief Signs the hash using Schnorr algorithm and returns the SchnorrSignature */
SchnorrSignature schnorr_sign(const uint8_t hash[32]) const;
/** \brief Alias for .publicKey().address(network) */
int address(char * address, size_t len) const;
/** \brief Alias for .publicKey().legacyAddress(network) */
int legacyAddress(char * address, size_t len) const;
/** \brief Alias for .publicKey().segwitAddress(network) */
int segwitAddress(char * address, size_t len) const;
/** \brief Alias for .publicKey().nestedSegwitAddress(network) */
int nestedSegwitAddress(char * address, size_t len) const;
#if USE_ARDUINO_STRING
String address() const;
String legacyAddress() const;
String segwitAddress() const;
String nestedSegwitAddress() const;
#endif
#if USE_STD_STRING
std::string address() const;
std::string legacyAddress() const;
std::string segwitAddress() const;
std::string nestedSegwitAddress() const;
#endif
// PrivateKey &operator=(const PrivateKey &other); // assignment
/** \brief Performs ECDH key agreement using public key of another party.
* 32-byte shared secret will be written to `shared_secret` array.
* Optional parameter hash (true by default) defines if you want sha256(<x><y>) or just <x>.
* Having hash=true is recommended unless you have a very good reason not to use it.
*/
int ecdh(const PublicKey pub, uint8_t shared_secret[32], bool hash=true);
};
/**
* \brief HD Private Key class. Derived from PrivateKey class.
* Works according to [bip32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki),
* [bip39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) and
* [slip32](https://github.com/satoshilabs/slips/blob/master/slip-0032.md).
* You can generate the key from mnemonic or seed, derive children and hardened children.
* xprv and xpub methods return strings according to slip32, xprv/xpub for bip44, yprv/ypub for bip49 and zprv/zpub for bip84
*/
class HDPrivateKey : public PrivateKey{
protected:
void init();
size_t to_bytes(uint8_t * arr, size_t len) const;
virtual size_t to_str(char * buf, size_t len) const{ return xprv( buf, len); };
virtual size_t from_str(const char * buf, size_t len);
virtual size_t from_stream(ParseStream *s);
virtual size_t to_stream(SerializeStream *s, size_t offset = 0) const;
uint8_t prefix[4]; // used for parsing only
public:
HDPrivateKey();
HDPrivateKey(const uint8_t secret[32], const uint8_t chain_code[32],
uint8_t key_depth = 0,
const uint8_t parent_fingerprint_arr[4] = NULL,
uint32_t childnumber = 0,
const Network * network = &DEFAULT_NETWORK,
ScriptType key_type = UNKNOWN_TYPE);
HDPrivateKey(const char xprvArr[]);
HDPrivateKey(const char * mnemonic, size_t mnemonicSize, const char * password, size_t passwordSize, const Network * network = &DEFAULT_NETWORK, void (*progress_callback)(float) = NULL);
#if USE_STD_STRING
HDPrivateKey(std::string mnemonic, std::string password, const Network * network = &DEFAULT_NETWORK, void (*progress_callback)(float) = NULL);
#endif
#if USE_ARDUINO_STRING
HDPrivateKey(String mnemonic, String password, const Network * network = &DEFAULT_NETWORK, void (*progress_callback)(float) = NULL);
#endif
/* HDPrivateKey(const HDPrivateKey &other):HDPrivateKey( // copy
other.num, other.chainCode, other.depth,
other.parentFingerprint, other.childNumber, other.network, other.type){};
*/
~HDPrivateKey();
virtual size_t length() const{ return 78; };
/** \brief Length of the key in base58 encoding (111). */
virtual size_t stringLength() const{ return 111; };
uint8_t chainCode[32];
uint8_t depth;
uint8_t parentFingerprint[4];
uint32_t childNumber;
ScriptType type;
int fromSeed(const uint8_t * seed, size_t seedSize, const Network * network = &DEFAULT_NETWORK);
// int fromSeed(const uint8_t seed[64], const Network * network = &DEFAULT_NETWORK);
int fromMnemonic(const char * mnemonic, size_t mnemonicSize, const char * password, size_t passwordSize, const Network * network = &DEFAULT_NETWORK, void (*progress_callback)(float) = NULL);
int fromMnemonic(const char * mnemonic, const char * password, const Network * network = &DEFAULT_NETWORK, void (*progress_callback)(float) = NULL){
return fromMnemonic(mnemonic, strlen(mnemonic), password, strlen(password), network, progress_callback);
}
#if USE_STD_STRING
int fromMnemonic(std::string mnemonic, std::string password, const Network * network = &DEFAULT_NETWORK, void (*progress_callback)(float) = NULL);
#endif
#if USE_ARDUINO_STRING
int fromMnemonic(String mnemonic, String password, const Network * network = &DEFAULT_NETWORK, void (*progress_callback)(float) = NULL);
#endif
int xprv(char * arr, size_t len) const;
int xpub(char * arr, size_t len) const;
HDPublicKey xpub() const;
int address(char * arr, size_t len) const;
#if USE_ARDUINO_STRING
String xprv() const;
String address() const;
#endif
#if USE_STD_STRING
std::string xprv() const;
std::string address() const;
#endif
/** \brief populates array with the fingerprint of the key */
void fingerprint(uint8_t arr[4]) const;
#if USE_STD_STRING
std::string fingerprint() const;
#endif
#if USE_ARDUINO_STRING
String fingerprint() const;
#endif
HDPrivateKey child(uint32_t index, bool hardened = false) const;
HDPrivateKey hardenedChild(uint32_t index) const;
/** \brief derives a child according to derivation path. Use 0x80000000 + index for hardened index. */
HDPrivateKey derive(uint32_t * index, size_t len) const;
/** \brief derives a child according to derivation path. For example "m/84h/1h/0h/1/23/" for the 23rd change address for testnet with P2WPKH type (bip84). */
HDPrivateKey derive(const char * path) const;
#if USE_ARDUINO_STRING
HDPrivateKey derive(String path) const{ return derive(path.c_str()); };
#endif
// just to make sure it is compressed
PublicKey publicKey() const{ PublicKey p = pubKey; p.compressed = true; return p; };
// HDPrivateKey &operator=(const HDPrivateKey &other); // assignment
};
/**
* \brief HD Public Key class. Derived from PublicKey class.
* Works according to [bip32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki),
* [bip39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) and
* [slip32](https://github.com/satoshilabs/slips/blob/master/slip-0032.md).
* You can derive children
* xpub method return strings according to slip32, xpub for bip44, ypub for bip49 and zpub for bip84
*/
class HDPublicKey : public PublicKey{
size_t to_bytes(uint8_t * arr, size_t len) const;
virtual size_t to_str(char * buf, size_t len) const{ return xpub( buf, len); };
virtual size_t from_str(const char * buf, size_t len);
virtual size_t from_stream(ParseStream *s);
virtual size_t to_stream(SerializeStream *s, size_t offset = 0) const;
uint8_t prefix[4]; // used for parsing only
public:
HDPublicKey();
HDPublicKey(const uint8_t point[64], const uint8_t chain_code[32],
uint8_t key_depth = 0,
const uint8_t parent_fingerprint_arr[4] = NULL,
uint32_t childnumber = 0,
const Network * net = &DEFAULT_NETWORK,
ScriptType key_type = UNKNOWN_TYPE);
HDPublicKey(const char * xpubArr);
/* HDPublicKey(const HDPublicKey &other):HDPublicKey( // copy
other.point, other.chainCode, other.depth,
other.parentFingerprint, other.childNumber, other.network, other.type){};
*/
#if USE_ARDUINO_STRING
HDPublicKey(String pub){ reset(); from_str(pub.c_str(), pub.length()); };
#endif
~HDPublicKey();
/** \brief Length of the key (78). */
virtual size_t length() const{ return 78; };
/** \brief Length of the key in base58 encoding (111). */
virtual size_t stringLength() const{ return 111; };
uint8_t chainCode[32];
uint8_t depth;
uint8_t parentFingerprint[4];
uint32_t childNumber;
ScriptType type;
const Network * network;
int xpub(char * arr, size_t len) const;
int address(char * arr, size_t len) const;
#if USE_ARDUINO_STRING
String xpub() const;
String address() const;
#endif
#if USE_STD_STRING
std::string xpub() const;
std::string address() const;
#endif
/** \brief populates array with the fingerprint of the key */
void fingerprint(uint8_t arr[4]) const;
#if USE_STD_STRING
std::string fingerprint() const;
#endif
#if USE_ARDUINO_STRING
String fingerprint() const;
#endif
/** \brief derive a child.
* You can derive only normal children (not hardened) from the public key.
*/
HDPublicKey child(uint32_t index) const;
/** \brief derives a child according to derivation path. */
HDPublicKey derive(uint32_t * index, size_t len) const;
/** \brief derives a child according to derivation path. For example "m/1/23/" for the 23rd change address. */
HDPublicKey derive(const char * path) const;
#if USE_ARDUINO_STRING
HDPublicKey derive(String path) const{ return derive(path.c_str()); };
#endif
// HDPublicKey &operator=(const HDPublicKey &other); // assignment
};
/**
* \brief Signature class.
* Reference: https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
*/
class Signature : public Streamable{
protected:
uint8_t r[32];
uint8_t s[32];
virtual size_t from_stream(ParseStream *s);
virtual size_t to_stream(SerializeStream *s, size_t offset = 0) const;
size_t rlen() const;
size_t slen() const;
uint8_t tot[3]; // temporary thingy for parsing
public:
Signature();
Signature(const uint8_t r_arr[32], const uint8_t s_arr[32]);
Signature(const uint8_t * der, size_t derLen);
Signature(const uint8_t * der);
explicit Signature(const char * der);
virtual size_t length() const;
uint8_t index; // used to derive pubkey from signature
/** \brief encodes signature in der format and writes it to array */
size_t der(uint8_t * arr, size_t len) const;
/** \brief parses signature in der format */
size_t fromDer(const uint8_t * arr, size_t len);
/** \brief populates array with <r[32]><s[32]><index> */
void bin(uint8_t * arr, size_t len) const;
/** \brief parses array as <r[32]><s[32]><index> */
void fromBin(const uint8_t * arr, size_t len);
bool isValid() const{ uint8_t arr[32] = { 0 }; return !((memcmp(r, arr, 32) == 0) && (memcmp(s, arr, 32)==0)); };
explicit operator bool() const{ return isValid(); };
bool operator==(const Signature& other) const{ return (memcmp(r, other.r, 32) == 0) && (memcmp(s, other.s, 32) == 0); };
bool operator!=(const Signature& other) const{ return !operator==(other); };
};
/**
* \brief SchnorrSignature class.
* Reference: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
*/
class SchnorrSignature : public Streamable{
protected:
uint8_t r[32];
uint8_t s[32];
virtual size_t from_stream(ParseStream *s);
virtual size_t to_stream(SerializeStream *s, size_t offset = 0) const;
public:
SchnorrSignature();
SchnorrSignature(const uint8_t r_arr[32], const uint8_t s_arr[32]);
SchnorrSignature(const uint8_t rs_arr[64]);
explicit SchnorrSignature(const char * rs);
virtual size_t length() const{ return 64; };
bool isValid() const{ uint8_t arr[32] = { 0 }; return !((memcmp(r, arr, 32) == 0) && (memcmp(s, arr, 32)==0)); };
explicit operator bool() const{ return isValid(); };
bool operator==(const SchnorrSignature& other) const{ return (memcmp(r, other.r, 32) == 0) && (memcmp(s, other.s, 32) == 0); };
bool operator!=(const SchnorrSignature& other) const{ return !operator==(other); };
};
/**
* \brief Script class. Parsing requires the length of the script in the beginning.
*/
class Script : public Streamable{
protected:
virtual size_t from_stream(ParseStream *s);
virtual size_t to_stream(SerializeStream *s, size_t offset = 0) const;
uint8_t lenLen; // for parsing only, length of the varint
void fromAddress(const char * address);
void init();
public:
uint8_t * scriptArray;
size_t scriptLen;
void clear();
Script();
Script(const uint8_t * buffer, size_t len);
/** \brief creates a script from address */
Script(const char * address){ init(); fromAddress(address); };
#if USE_ARDUINO_STRING
/** \brief creates a script from address */
Script(const String address){ init(); fromAddress(address.c_str()); };
#endif
#if USE_STD_STRING
/** \brief creates a script from address */
Script(const std::string address){ init(); fromAddress(address.c_str()); };
#endif
/** \brief creates one of standart scripts (P2PKH, P2WPKH) */
Script(const PublicKey pubkey, ScriptType type = P2PKH);
/** \brief creates one of standart scripts (P2SH, P2WSH) */
Script(const Script &other, ScriptType type);
Script(const Script &other); // copy
~Script(){ if(scriptArray){ free(scriptArray); } };
/** \brief tries to determine the script type */
ScriptType type() const;
/** \brief returns address corresponding to the script */
size_t address(char * buffer, size_t len, const Network * network = &DEFAULT_NETWORK) const;
#if USE_ARDUINO_STRING
String address(const Network * network = &DEFAULT_NETWORK) const;
#endif
#if USE_STD_STRING
std::string address(const Network * network = &DEFAULT_NETWORK) const;
#endif
/** \brief length of the script with varint */
virtual size_t length() const;
/** \brief pushes a single byte (op_code) to the end */
size_t push(uint8_t code);
/** \brief pushes bytes from data object to the end */
size_t push(const uint8_t * data, size_t len);
/** \brief adds <len><sec> to the script */
size_t push(const PublicKey pubkey);
/** \brief adds <len><der><sigType> to the script */
size_t push(const Signature sig, SigHashType sigType = SIGHASH_ALL);
/** \brief adds <len><script> to the script (used for P2SH) */
size_t push(const Script sc);
/** \brief returns scriptPubkey for this scripts (P2SH or P2WSH) */
Script scriptPubkey(ScriptType type = P2SH) const;
Script &operator=(const Script &other); // assignment
// Bool conversion. Allows to use if(script) construction. Returns false if script is empty, true otherwise
explicit operator bool() const{ return (scriptLen > 0); };
bool operator==(const Script& other) const{ return (scriptLen == other.scriptLen) && (memcmp(scriptArray, other.scriptArray, scriptLen) == 0); };
bool operator!=(const Script& other) const{ return !operator==(other); };
};
Script pkh(PublicKey pub);
Script wpkh(PublicKey pub);
Script multi(uint8_t threshold, const PublicKey * pubkeys, uint8_t pubkeys_len);
Script sortedmulti(uint8_t threshold, const PublicKey * pubkeys, uint8_t pubkeys_len);
Script wsh(Script witness_script);
Script sh(Script script);
/**
* \brief Witness class. Has a form of `<num><e0><e1><e2>...`
* where `<e>` can be a public key, signature or arbitrary data (i.e. hash)
*/
class Witness : public Streamable{
uint8_t * witnessArray;
size_t witnessLen;
virtual size_t from_stream(ParseStream *s);
virtual size_t to_stream(SerializeStream *s, size_t offset = 0) const;
uint32_t numElements;
// used for parsing only:
uint32_t cur_element; // index of current element
size_t cur_element_len; // length of current element
size_t cur_bytes_parsed; // number of bytes read from current element
uint8_t curLen; // for parsing only, length of the varint
uint8_t lenLen; // for parsing, length of the varint
virtual void reset(){ status = PARSING_DONE; bytes_parsed = 0; cur_element_len=0; cur_bytes_parsed=0; cur_element=0; lenLen=0; };
void init();
public:
void clear();
virtual size_t length() const;
Witness();
Witness(const uint8_t * buffer, size_t len);
Witness(const Signature sig, const PublicKey pub);
Witness(const Witness &other); // copy
~Witness(){ if(witnessArray){ free(witnessArray); } };
/** \brief returns number of elements in the witness */
uint8_t count() const{ return numElements; };
/** \brief adds `<len><data>` to the witness */
size_t push(const uint8_t * data, size_t len);
/** \brief adds `<len><sec>` to the witness */
size_t push(const PublicKey pubkey);
/** \brief adds `<len><der><sigType>` to the witness */
size_t push(const Signature sig, SigHashType sigType = SIGHASH_ALL);
/** \brief adds `<len><script>` to the witness */
size_t push(const Script sc);
Witness &operator=(Witness const &other); // assignment
explicit operator bool() const{ return (numElements > 0); };
bool operator==(const Witness& other) const{ return (witnessLen == other.witnessLen) && (memcmp(witnessArray, other.witnessArray, witnessLen) == 0) && (numElements == other.numElements); };
bool operator!=(const Witness& other) const{ return !operator==(other); };
};
/**
* \brief Transaction Input class. Serializes as `<prev_hash><prev_index><scriptSig><sequence>`<br>
* Stores information about previous transaction hash, prev output number,
* scriptSig, sequence and witness data if it is segwit.
*/
class TxIn : public Streamable{
protected:
virtual size_t from_stream(ParseStream *s);
virtual size_t to_stream(SerializeStream *s, size_t offset = 0) const;
void init();
public:
TxIn(void);
TxIn(const uint8_t prev_id[32], uint32_t prev_index, const Script script, uint32_t sequence_number = 0xffffffff);
TxIn(const uint8_t prev_id[32], uint32_t prev_index, uint32_t sequence_number = 0xffffffff);
explicit TxIn(const char * prev_id, uint32_t prev_index, const Script script, uint32_t sequence_number = 0xffffffff);
explicit TxIn(const char * prev_id, uint32_t prev_index, uint32_t sequence_number = 0xffffffff);
virtual size_t length() const;
uint8_t hash[32];
uint32_t outputIndex;
Script scriptSig;
uint32_t sequence;
Witness witness;
/** \brief checks if the input is segwit or not */
bool isSegwit() const{ return (witness.count() > 0); };
bool isValid() const{ return status==PARSING_DONE; };
explicit operator bool() const{ return isValid(); };
};
/**
* \brief Transaction Output class.<br>
* Stores information the amount and ScriptPubkey,
*/
class TxOut : public Streamable{
private:
uint8_t tmp[8]; // for parsing amounts
protected:
virtual size_t from_stream(ParseStream *s);
virtual size_t to_stream(SerializeStream *s, size_t offset = 0) const;
void init(){ status = PARSING_DONE; bytes_parsed=0; amount = 0; };
public:
TxOut(){ amount = 0; };
TxOut(uint64_t send_amount, const Script outputScript){ amount = send_amount; scriptPubkey = outputScript; };
TxOut(const Script outputScript, uint64_t send_amount){ amount = send_amount; scriptPubkey = outputScript; };
TxOut(uint64_t send_amount, const char * address){ amount = send_amount; scriptPubkey = Script(address); };
TxOut(const char * address, uint64_t send_amount){ amount = send_amount; scriptPubkey = Script(address); };
virtual size_t length() const{ return 8+scriptPubkey.length(); };
/** \brief this script defines the rules for the spending input */
Script scriptPubkey;
/** \brief the output amount in satoshi */
uint64_t amount;
/** \brief returns the output amount in BTC */
float btcAmount(){ return (float)amount/1e8; };
/** \brief returns the address corresponding to the output script */
size_t address(char * addr, size_t len, const Network * network = &DEFAULT_NETWORK) const{ return scriptPubkey.address(addr, len, network); };
#if USE_ARDUINO_STRING
String address(const Network * network = &DEFAULT_NETWORK) const{ return scriptPubkey.address(network); };
#endif
#if USE_STD_STRING
std::string address(const Network * network = &DEFAULT_NETWORK) const{ return scriptPubkey.address(network); };
#endif
bool isValid() const{ return status==PARSING_DONE; };
explicit operator bool() const{ return isValid(); };
};
/**
* \brief Transaction class.<br>
* Can be segwit or not. For legacy tx serializes as `<ver><inputsNumber><inputs><outputsNumber><outputs><locktime>`<br>
* For segwit tx serializes as `<ver><00><01><inputsNumber><inputs><outputsNumber><outputs><witnesses><locktime>`
*/
class Tx : public Streamable{
protected:
virtual size_t from_stream(ParseStream *s);
virtual size_t to_stream(SerializeStream *s, size_t offset = 0) const;
uint8_t segwit_flag;
void clear();
void init();
public:
Tx();
Tx(Tx const &other);
~Tx();
virtual size_t length() const;
uint32_t version;
size_t inputsNumber;
TxIn * txIns;
size_t outputsNumber;
TxOut * txOuts;
uint32_t locktime;
/** \brief checks wether transaction is segwit or not.<br>
* returns `true` if at least one of the inputs has non-empty witness
*/
bool isSegwit() const;
/** \brief populates hash with transaction hash */
int hash(uint8_t h[32]) const;
/** \brief populates hash with transaction hash if serialized as segwit */
int whash(uint8_t h[32]) const;
/** \brief populates array with id of the transaction (reverse of the hash) */
int txid(uint8_t id_arr[32]) const;
/** \brief populates array with witness id of the transaction */
int wtxid(uint8_t id_arr[32]) const;
#if USE_ARDUINO_STRING
String txid() const;
String wtxid() const;
#endif
#if USE_STD_STRING
std::string txid() const;
std::string wtxid() const;
#endif
/** \brief adds another input to the transaction */
uint8_t addInput(const TxIn txIn);
/** \brief adds another output to the transaction */
uint8_t addOutput(const TxOut txOut);
/** \brief calculates a hash to sign for certain input */
int sigHash(uint8_t h[32], uint8_t inputIndex, const Script scriptPubkey, SigHashType sighash = SIGHASH_ALL) const;
int hashPrevouts(uint8_t h[32]) const;
int hashSequence(uint8_t h[32]) const;
int hashOutputs(uint8_t h[32]) const;
int sigHashSegwit(uint8_t h[32], uint8_t inputIndex, const Script scriptPubKey, uint64_t amount, SigHashType sighash = SIGHASH_ALL) const;
#if 0
/** \brief sorts inputs and outputs in alphabetical order */
void sort();
#endif
/** \brief signs legacy input with certain script and returns a signature.
* Don't forget to construct txIns[i].scriptSig correctly if you are using P2SH.
* For P2WPKH, P2WSH and P2SH-P2WPKH use signSegwitInput method.
*/
Signature signInput(uint8_t inputIndex, const PrivateKey pk, const Script redeemScript, SigHashType sighash = SIGHASH_ALL);
/** \brief signs legacy input and returns a signature */
Signature signInput(uint8_t inputIndex, const PrivateKey pk){
return signInput(inputIndex, pk, Script(pk.publicKey(), P2PKH));
};
/** \brief signs segwit input with certain script and returns a signature.
* Don't forget to construct txIns[i].witness correctly if you are using P2WSH or P2SH-P2WSH.
* For P2PKH and P2SH use signInput method.
*/
Signature signSegwitInput(uint8_t inputIndex, const PrivateKey pk, const Script redeemScript, uint64_t amount, ScriptType type = P2WSH, SigHashType sighash = SIGHASH_ALL);
/** \brief signs segwit input and returns a signature. Uses native segwit (P2WPKH) by default,
* you can also specify the type to be P2SH-P2WPKH to sign nested segwit transaction.
*/
Signature signSegwitInput(uint8_t inputIndex, const PrivateKey pk, uint64_t amount, ScriptType type = P2WPKH){
return signSegwitInput(inputIndex, pk, Script(pk.publicKey(), P2WPKH), amount, type); // FIXME: are you sure?
};
Tx &operator=(Tx const &other);
bool isValid() const{ return status==PARSING_DONE; };
explicit operator bool() const{ return isValid(); };
};
#endif // __BITCOIN_H__
================================================
FILE: src/BitcoinCurve.cpp
================================================
#include "BitcoinCurve.h"
#include "Conversion.h"
#include "utility/trezor/rfc6979.h"
#include "utility/trezor/ecdsa.h"
#include "utility/trezor/secp256k1.h"
const ECPoint InfinityPoint;
const ECPoint GeneratorPoint("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798");
size_t ECPoint::from_stream(ParseStream *s){
static uint8_t first_byte;
if(status == PARSING_FAILED){
return 0;
}
if(status == PARSING_DONE){
bytes_parsed = 0;
}
status = PARSING_INCOMPLETE;
size_t bytes_to_read = 33;
size_t bytes_read = 0;
if(bytes_parsed > 0){ // we already know if it's compressed or not
bytes_to_read = ECPoint::length()-bytes_parsed;
}else{
if(s->available()){
uint8_t c = s->read();
bytes_read++; bytes_to_read--;
if(c < 0x02 || c > 0x04){
status = PARSING_FAILED;
bytes_parsed += bytes_read;
return bytes_read;
}
first_byte = c;
if(c == 0x04){ // uncompressed
bytes_to_read += 32;
compressed = false;
}else{
compressed = true;
}
}
}
while(s->available() && bytes_to_read > 0){ // actual data
point[bytes_parsed+bytes_read-1] = s->read();
bytes_read++; bytes_to_read--;
}
if(bytes_to_read==0){
if(compressed){
uint8_t buf[33];
buf[0] = first_byte;
memcpy(buf+1, point, 32);
uint8_t arr[65];
ecdsa_uncompress_pubkey(&secp256k1, buf, arr);
memcpy(point, arr+1, 64);
}
status = PARSING_DONE;
if(!ECPoint::isValid()){
status = PARSING_FAILED;
}
}
bytes_parsed += bytes_read;
return bytes_read;
}
size_t ECPoint::to_stream(SerializeStream *s, size_t offset) const{
size_t bytes_written = 0;
if(!s->available()){
return 0;
}
if(offset == 0){
if(compressed){
s->write(0x02 + (point[63] & 0x01));
}else{
s->write(0x04);
}
bytes_written ++;
offset++;
}
while(s->available() > 0 && offset < ECPoint::length()){
s->write(point[offset-1]);
offset++; bytes_written++;
}
return bytes_written;
}
size_t ECPoint::sec(uint8_t * arr, size_t len) const{
SerializeByteStream s(arr, len);
return ECPoint::to_stream(&s);
}
size_t ECPoint::fromSec(const uint8_t * arr, size_t len){
ParseByteStream s(arr, len);
return ECPoint::from_stream(&s);
}
ECPoint::ECPoint(const uint8_t pubkeyArr[64], bool use_compressed){
memcpy(point, pubkeyArr, 64);
compressed = use_compressed;
};
ECPoint::ECPoint(const uint8_t * secArr){
if(secArr[0] == 0x04){
ECPoint::fromSec(secArr, 65);
}else{
ECPoint::fromSec(secArr, 33);
}
};
ECPoint::ECPoint(const char * arr){
reset();
ECPoint::parse(arr, strlen(arr));
};
// bool verify(const Signature sig, const uint8_t hash[32]) const;
bool ECPoint::isValid() const{
if(status != PARSING_DONE){
return false;
}
curve_point pub;
uint8_t buf[65];
sec(buf, 65);
return ecdsa_read_pubkey(&secp256k1, buf, &pub);
};
bool ECPoint::isEven() const{
return !bool(point[63] & 0x01);
};
ECPoint ECPoint::operator+(const ECPoint& other) const{
if(*this == InfinityPoint){
return other;
}
if(other == InfinityPoint){
return *this;
}
curve_point p1, p2;
uint8_t buf[65];
sec(buf, 65);
ecdsa_read_pubkey(&secp256k1, buf, &p1);
other.sec(buf, 65);
ecdsa_read_pubkey(&secp256k1, buf, &p2);
point_add(&secp256k1,&p1,&p2);
ECPoint sum;
bn_write_be(&p2.x, sum.point);
bn_write_be(&p2.y, sum.point+32);
return sum;
};
ECPoint ECPoint::operator-() const{
if(*this == InfinityPoint){
return *this;
}
uint8_t buf[33];
x(buf+1, 32);
if(isEven()){
buf[0] = 0x03;
}else{
buf[0] = 0x02;
}
ECPoint a;
a.fromSec(buf, 33);
a.compressed = compressed;
return a;
}
ECPoint ECPoint::operator-(const ECPoint& other) const{
ECPoint a = -other;
return *this+a;
}
/*********** ECScalar ******************/
size_t ECScalar::from_stream(ParseStream *s){
if(status == PARSING_FAILED){
return 0;
}
if(status == PARSING_DONE){
bytes_parsed = 0;
}
status = PARSING_INCOMPLETE;
size_t bytes_read = 0;
while(s->available() > 0 && bytes_parsed+bytes_read < 32){
num[bytes_parsed+bytes_read] = s->read();
bytes_read++;
}
if(bytes_parsed+bytes_read == 32){
status = PARSING_DONE;
uint8_t zero[32] = { 0 };
if(memcmp(num, zero, 32)==0){ // should we add something else here?
status = PARSING_FAILED;
}
bignum256 n;
bn_read_be(num, &n);
bn_mod(&n, &secp256k1.order);
bn_write_be(&n, num);
}
bytes_parsed += bytes_read;
return bytes_read;
}
size_t ECScalar::to_stream(SerializeStream *s, size_t offset) const{
size_t bytes_written = 0;
while(s->available() && offset+bytes_written < 32){
s->write(num[bytes_written+offset]);
bytes_written++;
}
return bytes_written;
}
ECScalar ECScalar::operator+(const ECScalar& other) const{
bignum256 a, b;
bn_read_be(this->num, &a);
bn_read_be(other.num, &b);
bn_addmod(&a, &b, &secp256k1.order);
bn_mod(&a, &secp256k1.order);
ECScalar sum;
bn_write_be(&a, sum.num);
return sum;
}
ECScalar ECScalar::operator+(const uint32_t& i) const{
bignum256 a;
bn_read_be(this->num, &a);
bn_addi(&a, i);
bn_mod(&a, &secp256k1.order);
ECScalar sum;
bn_write_be(&a, sum.num);
return sum;
}
ECScalar ECScalar::operator-() const{
bignum256 a, b;
bn_read_be(this->num, &a);
bn_subtract(&secp256k1.order, &a, &b);
ECScalar neg;
bn_write_be(&b, neg.num);
return neg;
}
ECScalar ECScalar::operator-(const uint32_t& i) const{
bignum256 a;
bn_read_be(this->num, &a);
bn_subi(&a, i, &secp256k1.order);
bn_mod(&a, &secp256k1.order);
ECScalar sum;
bn_write_be(&a, sum.num);
return sum;
}
ECScalar ECScalar::operator-(const ECScalar& other) const{
return (*this+(-other));
}
ECScalar ECScalar::operator*(const ECScalar& other) const{
bignum256 a, b;
bn_read_be(this->num, &a);
bn_read_be(other.num, &b);
bn_multiply(&b, &a, &secp256k1.order);
bn_mod(&a, &secp256k1.order);
ECScalar mul;
bn_write_be(&a, mul.num);
return mul;
}
ECScalar ECScalar::operator/(const ECScalar& other) const{
bignum256 a, b;
bn_read_be(this->num, &a);
bn_read_be(other.num, &b);
bn_inverse(&b, &secp256k1.order);
bn_multiply(&b, &a, &secp256k1.order);
bn_mod(&a, &secp256k1.order);
ECScalar res;
bn_write_be(&a, res.num);
return res;
}
bool ECScalar::operator<(const ECScalar& other) const{
bignum256 a,b;
bn_read_be(num, &a);
bn_read_be(other.num, &b);
return bn_is_less(&a, &b);
}
bool ECPoint::operator<(const ECPoint& other) const{
uint8_t sec1[65];
uint8_t sec2[65];
sec(sec1, sizeof(sec1));
other.sec(sec2, sizeof(sec2));
return memcmp(sec1, sec2, sizeof(sec1)) > 0;
}
ECPoint operator*(const ECScalar& scalar, const ECPoint& point){
ECPoint r;
uint8_t num[32];
scalar.getSecret(num);
if(point == GeneratorPoint){
uint8_t pubkey[65];
ecdsa_get_public_key65(&secp256k1, num, pubkey);
r.parse(pubkey, 65);
}else{
bignum256 d;
bn_read_be(num, &d);
curve_point p, res;
bn_read_be(point.point, &p.x);
bn_read_be(point.point+32, &p.y);
point_multiply(&secp256k1, &d, &p, &res);
bn_write_be(&res.x, r.point);
bn_write_be(&res.y, r.point+32);
}
r.compressed = point.compressed;
return r;
}
================================================
FILE: src/BitcoinCurve.h
================================================
#ifndef __BITCOIN_CURVE_H__
#define __BITCOIN_CURVE_H__
#include "uBitcoin_conf.h"
#include "BaseClasses.h"
#include "utility/trezor/memzero.h"
#include "Conversion.h"
class ECPoint : public Streamable{
protected:
virtual size_t from_stream(ParseStream *s);
virtual size_t to_stream(SerializeStream *s, size_t offset = 0) const;
public:
uint8_t point[64]; // point on curve: (x,y)
bool compressed;
virtual void reset(){ bytes_parsed = 0; status=PARSING_DONE; memset(point, 0, 64); compressed = true; };
virtual size_t length() const{ return 33 + 32*(1-compressed); };
virtual size_t stringLength() const{ return 2*ECPoint::length(); };
ECPoint(){ reset(); };
ECPoint(const uint8_t pubkeyArr[64], bool use_compressed);
ECPoint(const uint8_t * secArr);
explicit ECPoint(const char * secHex);
size_t sec(uint8_t * arr, size_t len) const;
size_t fromSec(const uint8_t * arr, size_t len);
/** \brief fills array with x coordinate of the point */
size_t x(uint8_t * arr, size_t len) const{
if(len < 32){
return 0;
}
memcpy(arr, point, 32);
return 32;
};
/** \brief parses x-only pubkey, from two possible points selects one with even y */
size_t from_x(const uint8_t * arr, size_t len){
if(len < 32){
return 0;
}
uint8_t sec[33] = {0x02};
memcpy(sec+1, arr, 32);
return fromSec(sec, sizeof(sec));
}
#if USE_ARDUINO_STRING
String sec() const{
char arr[65*2+1] = "";
SerializeByteStream stream(arr, sizeof(arr));
ECPoint::to_stream(&stream);
if(compressed){
arr[33*2+1] = 0;
}
String s(arr);
return s;
};
String x() const{
uint8_t arr[32];
x(arr, sizeof(arr));
return toHex(arr, sizeof(arr));
};
#endif
#if USE_STD_STRING
std::string sec() const{
char arr[65*2+1] = "";
SerializeByteStream stream(arr, sizeof(arr));
ECPoint::to_stream(&stream);
if(compressed){
arr[33*2+1] = 0;
}
std::string s(arr);
return s;
};
std::string x() const{
uint8_t arr[32];
x(arr, sizeof(arr));
return toHex(arr, sizeof(arr));
};
#endif
// bool verify(const Signature sig, const uint8_t hash[32]) const;
virtual bool isValid() const;
/** \brief checks if pubkey has even Y coordinate */
bool isEven() const;
explicit operator bool() const { return isValid(); };
bool operator==(const ECPoint& other) const{ return (memcmp(point, other.point, 64) == 0); };
bool operator!=(const ECPoint& other) const{ return !operator==(other); };
ECPoint operator+(const ECPoint& other) const;
ECPoint operator-() const;
ECPoint operator-(const ECPoint& other) const;
ECPoint operator+=(const ECPoint& other){ *this = *this+other; return *this; };
ECPoint operator-=(const ECPoint& other){ *this = *this-other; return *this; };
// sec-hex-comparison for multisig sorting
bool operator<(const ECPoint& other) const;
bool operator>(const ECPoint& other) const{ return (other<*this); };
bool operator>=(const ECPoint& other) const{ return !(*this<other); };
bool operator<=(const ECPoint& other) const{ return !(*this>other); };
};
extern const ECPoint InfinityPoint;
extern const ECPoint GeneratorPoint;
class ECScalar : public Streamable{
protected:
virtual size_t from_stream(ParseStream *s);
virtual size_t to_stream(SerializeStream *s, size_t offset = 0) const;
uint8_t num[32]; // scalar mod secp526k1.order
virtual void init(){ bytes_parsed = 0; status=PARSING_DONE; memzero(num, 32); };
public:
virtual void reset(){ bytes_parsed = 0; status=PARSING_DONE; memzero(num, 32); };
virtual size_t length() const{ return 32; };
ECScalar(){ init(); };
ECScalar(const uint8_t * arr, size_t len){ init(); parse(arr, len); };
explicit ECScalar(const char * arr){ init(); parse(arr, strlen(arr)); };
ECScalar(uint32_t i){ init(); intToBigEndian(i, num, 32); };
~ECScalar(){ memzero(num, 32); };
/** \brief Populates array with the secret key */
virtual void setSecret(const uint8_t secret_arr[32]){ memcpy(num, secret_arr, 32); };
/** \brief Sets the secret key */
void getSecret(uint8_t buffer[32]) const{ memcpy(buffer, num, 32); };
ECScalar operator+(const ECScalar& other) const;
ECScalar operator+(const uint32_t& i) const;
ECScalar operator-() const;
ECScalar operator-(const ECScalar& other) const;
ECScalar operator-(const uint32_t& i) const;
ECScalar operator+=(const ECScalar& other){ *this = *this+other; return *this; };
ECScalar operator-=(const ECScalar& other){ *this = *this-other; return *this; };
ECScalar operator+=(const uint32_t& i){ *this = *this+i; return *this; };
ECScalar operator-=(const uint32_t& i){ *this = *this-i; return *this; };
ECScalar operator*(const ECScalar& other) const;
ECScalar operator/(const ECScalar& other) const;
ECScalar operator*=(const ECScalar& other){ *this = *this*other; return *this; };
ECScalar operator/=(const ECScalar& other){ *this = *this/other; return *this; };
virtual bool isValid() const{ uint8_t arr[32] = { 0 }; return (memcmp(num, arr, 32) != 0); };
explicit operator bool() const { return isValid(); };
bool operator==(const ECScalar& other) const{ return (memcmp(num, other.num, 32) == 0); };
bool operator!=(const ECScalar& other) const{ return !operator==(other); };
bool operator<(const ECScalar& other) const;
bool operator>(const ECScalar& other) const{ return (other<*this); };
bool operator>=(const ECScalar& other) const{ return !(*this<other); };
bool operator<=(const ECScalar& other) const{ return !(*this>other); };
};
inline ECScalar operator/(uint32_t i, ECScalar& scalar){ return ECScalar(i) / scalar; };
inline ECScalar operator/(ECScalar& scalar, uint32_t i){ return scalar / ECScalar(i); };
inline ECScalar operator*(uint32_t i, ECScalar& scalar){ return ECScalar(i) * scalar; };
inline ECScalar operator*(ECScalar& scalar, uint32_t i){ return scalar * ECScalar(i); };
inline ECScalar operator+(uint32_t i, ECScalar& scalar){ return ECScalar(i) + scalar; };
inline ECScalar operator+(ECScalar& scalar, uint32_t i){ return scalar + ECScalar(i); };
inline ECScalar operator-(uint32_t i, ECScalar& scalar){ return ECScalar(i) - scalar; };
inline ECScalar operator-(ECScalar& scalar, uint32_t i){ return scalar - ECScalar(i); };
ECPoint operator*(const ECScalar& d, const ECPoint& p);
inline ECPoint operator*(const ECPoint& p, const ECScalar& d){ return d*p; };
inline ECPoint operator/(const ECPoint& p, const ECScalar& d){ return (ECScalar(1)/d)*p; };
#endif // __BITCOIN_CURVE_H__
================================================
FILE: src/Conversion.cpp
================================================
#include "Conversion.h"
#include "Hash.h"
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include "utility/trezor/memzero.h"
#if USE_STD_STRING
using std::string;
#define String string
#endif
static const char BASE58_CHARS[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
static const char BASE43_CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ$*+-./:";
static const char BASE64_CHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
size_t toHex(const uint8_t * array, size_t arraySize, char * output, size_t outputSize){
if(array == NULL || output == NULL){ return 0; }
// uint8_t * array = (uint8_t *) arr;
if(outputSize < 2*arraySize){
return 0;
}
memzero(output, outputSize);
for(size_t i=0; i < arraySize; i++){
output[2*i] = (array[i] >> 4) + '0';
if(output[2*i] > '9'){
output[2*i] += 'a'-'9'-1;
}
output[2*i+1] = (array[i] & 0x0F) + '0';
if(output[2*i+1] > '9'){
output[2*i+1] += 'a'-'9'-1;
}
}
return 2*arraySize;
}
#if USE_ARDUINO_STRING || USE_STD_STRING
String toHex(const uint8_t * array, size_t arraySize){
if(array == NULL){ return String(); }
size_t outputSize = arraySize * 2 + 1;
char * output = (char *) malloc(outputSize);
if(output == NULL){ return String(); }
toHex(array, arraySize, output, outputSize);
String result(output);
memzero(output, outputSize);
free(output);
return result;
}
#endif
#if USE_ARDUINO_STRING
size_t toHex(uint8_t v, Print &s){
char c = (v >> 4) + '0';
if(c > '9'){
c += 'a'-'9'-1;
}
s.print(c);
c = (v & 0x0F) + '0';
if(c > '9'){
c += 'a'-'9'-1;
}
s.print(c);
return 2;
}
size_t toHex(const uint8_t * array, size_t arraySize, Print &s){
if(array == NULL){ return 0; }
size_t l = 0;
for(int i=0; i<arraySize; i++){
l += toHex(array[i], s);
}
return l;
}
#endif
uint8_t hexToVal(char c){
if(c >= '0' && c <= '9'){
return ((uint8_t)(c - '0')) & 0x0F;
}
if(c >= 'A' && c <= 'F'){
return ((uint8_t)(c-'A'+10)) & 0x0F;
}
if(c >= 'a' && c <= 'f'){
return ((uint8_t)(c-'a'+10)) & 0x0F;
}
return 0xFF;
}
size_t fromHex(const char * hex, size_t hexLen, uint8_t * array, size_t arraySize){
if(array == NULL || hex == NULL){ return 0; }
memzero(array, arraySize);
// ignoring all non-hex characters in the beginning
size_t offset = 0;
while(offset < hexLen){
uint8_t v = hexToVal(hex[offset]);
if(v > 0x0F){ // if invalid char
offset++;
}else{
break;
}
}
hexLen -= offset;
for(size_t i=0; i<hexLen/2; i++){
uint8_t v1 = hexToVal(hex[offset+2*i]);
uint8_t v2 = hexToVal(hex[offset+2*i+1]);
if((v1 > 0x0F) || (v2 > 0x0F)){ // if invalid char stop parsing
return i;
}
array[i] = (v1<<4) | v2;
}
return hexLen/2;
}
#if USE_STD_STRING || USE_ARDUINO_STRING
size_t fromHex(String encoded, uint8_t * output, size_t outputSize){
if(output == NULL){ return 0; }
return fromHex(encoded.c_str(), encoded.length(), output, outputSize);
};
#endif
#if !(USE_ARDUINO_STRING || USE_STD_STRING)
size_t fromHex(const char * hex, uint8_t * array, size_t arraySize){
if(array == NULL || hex == NULL){ return 0; }
return fromHex(hex, strlen(hex), array, arraySize);
}
#endif
/******************** Binary conversion *******************/
size_t fromBin(const char * bin, size_t binLen, uint8_t * array, size_t arraySize){
if(bin == NULL || array == NULL){ return 0; }
// array is big enough
if(arraySize*8 < binLen){
return 0;
}
size_t len = binLen/8;
if(binLen % 8 != 0){
len += 1; // not aligned to 8 bits
}
// zero output array
memzero(array, arraySize);
for(size_t i = 0; i < binLen; i++){
// we go in reverse order (from the end of the string)
uint8_t exp = (i%8); // shift
uint8_t n = (i/8); // current byte from the end
char c = bin[binLen-i-1];
if(c == '1'){
// set bit
array[len-n-1] |= (1<<exp);
}else if(c == '0'){
// correct char, nothing to do here
}else{
// wrong char
return 0;
}
}
return len;
}
size_t toBin(const uint8_t * array, size_t arraySize, char * output, size_t outputSize){
if(array == NULL || output == NULL){ return 0; }
if(outputSize < arraySize*8){
return 0;
}
memzero(output, outputSize);
for(size_t i=0; i<arraySize; i++){
for(size_t j=0; j<8; j++){
if(((array[i] >> j) & 1) == 1){
output[8*i+(7-j)] = '1';
}else{
output[8*i+(7-j)] = '0';
}
}
}
return 8*arraySize;
}
#if USE_ARDUINO_STRING || USE_STD_STRING
String toBin(const uint8_t * array, size_t arraySize){
if(array == NULL){ return String(); }
size_t outputSize = arraySize * 8 + 1;
char * output = (char *) malloc(outputSize);
if(output == NULL){ return String(); }
toBin(array, arraySize, output, outputSize);
String result(output);
memzero(output, outputSize);
free(output);
return result;
}
#endif
#if USE_ARDUINO_STRING
size_t toBin(uint8_t v, Print &s){
for(int i=7; i>=0; i--){
if(((v>>i) & 1)==1){
s.print('1');
}else{
s.print('0');
}
}
return 8;
}
size_t toBin(const uint8_t * array, size_t arraySize, Print &s){
if(array == NULL){ return 0; }
size_t l = 0;
for(int i=0; i<arraySize; i++){
l += toBin(array[i], s);
}
return l;
}
#endif
#if USE_STD_STRING || USE_ARDUINO_STRING
size_t fromBin(String encoded, uint8_t * output, size_t outputSize){
if(output == NULL){ return 0; }
return fromBin(encoded.c_str(), encoded.length(), output, outputSize);
};
#endif
#if !(USE_ARDUINO_STRING || USE_STD_STRING)
size_t fromBin(const char * hex, uint8_t * array, size_t arraySize){
if(hex == NULL || array == NULL){ return 0; }
return fromBin(hex, strlen(hex), array, arraySize);
}
#endif
/******************* Base 58 conversion *******************/
size_t toBase58Length(const uint8_t * array, size_t arraySize){
if(array == NULL){ return 0; }
// Counting leading zeroes
size_t zeroCount = 0;
while(zeroCount < arraySize && !array[zeroCount]){
zeroCount++;
}
/*
* Encoded string will have maximum length of
* len = arraySize * log(58)/log(256) ≈ arraySize * 1.37
* and should be rounded to larger value
* Limitation: size_t overflow when arraySize > 65536/56 = 1170 bytes
* Extra byte due to numerical error appears after 5117 bytes
*/
size_t size = (arraySize - zeroCount) * 183 / 134 + 1;
// size_t size = (arraySize - zeroCount) * 137 / 100 + 1;
return size+zeroCount;
}
size_t toBase58(const uint8_t * array, size_t arraySize, char * output, size_t outputSize){
if(array == NULL || output == NULL){ return 0; }
// Counting leading zeroes
size_t zeroCount = 0;
while(zeroCount < arraySize && !array[zeroCount]){
zeroCount++;
}
// TODO: refactor with real sizes
// size estimation. 56/41 ≈ log(58)/log(256)
size_t size = (arraySize - zeroCount) * 183 / 134 + 1;
// size_t size = (arraySize - zeroCount) * 137 / 100 + 1;
if(outputSize < size+zeroCount){
return 0;
}
memzero(output, outputSize);
// array copy for manipulations
size_t bufferSize = arraySize - zeroCount;
uint8_t * buffer = (uint8_t *)calloc(bufferSize, sizeof(uint8_t));
if(buffer == NULL){ return 0; }
for(size_t i = zeroCount; i < arraySize; i++){
buffer[i - zeroCount] = array[i];
}
for(size_t j = 0; j < size; j++){
uint16_t reminder = 0;
uint16_t temp = 0;
for(size_t i = 0; i < bufferSize; i++){
temp = (reminder * 256 + buffer[i]);
reminder = temp % 58;
buffer[i] = temp/58;
}
output[size+zeroCount-j-1] = BASE58_CHARS[reminder];
}
free(buffer);
for (size_t i = 0; i < zeroCount; i++){
output[i] = BASE58_CHARS[0];
}
if(outputSize > size+zeroCount){
output[size+zeroCount] = '\0';
}
// removing leading zeroes
// TODO: refactor
int shift = 0;
for(size_t i=zeroCount; i < size+zeroCount; i++){
if(output[i]==BASE58_CHARS[0]){
shift++;
}else{
break;
}
}
if(shift>0){
for(size_t i=zeroCount+shift; i < size+zeroCount; i++){
output[i-shift] = output[i];
output[i] = 0;
}
}
size_t l = size+zeroCount-shift;
memzero(output + l, outputSize-l);
return l;
}
#if USE_ARDUINO_STRING || USE_STD_STRING
String toBase58(const uint8_t * array, size_t arraySize){
if(array == NULL){ return String(); }
size_t len = toBase58Length(array, arraySize) + 1; // +1 for null terminator
char * buf = (char *)malloc(len);
if(buf == NULL){ return String(); }
toBase58(array, arraySize, buf, len);
String result(buf);
free(buf);
return result;
}
#endif
size_t toBase58Check(const uint8_t * array, size_t arraySize, char * output, size_t outputSize){
if(array == NULL || output == NULL){ return 0; }
uint8_t * arr = (uint8_t *) malloc(arraySize+4);
if(arr == NULL){ return 0; }
memcpy(arr, array, arraySize);
uint8_t hash[32];
doubleSha(arr, arraySize, hash);
memcpy(arr+arraySize, hash, 4);
size_t l = toBase58(arr, arraySize+4, output, outputSize);
memzero(arr, arraySize+4); // secret should not stay in RAM
free(arr);
return l;
}
#if USE_ARDUINO_STRING || USE_STD_STRING
String toBase58Check(const uint8_t * array, size_t arraySize){
if(array == NULL){ return String(); }
size_t len = toBase58Length(array, arraySize + 4) + 1; // +4 checksum +1 for null terminator
char * buf = (char *)malloc(len);
if(buf == NULL){ return String(); }
toBase58Check(array, arraySize, buf, len);
String result(buf);
free(buf);
return result;
}
#endif
// TODO: add zero count, fix wrong length
size_t fromBase58Length(const char * array, size_t arraySize){
if(array == NULL){ return 0; }
size_t size = arraySize * 361 / 493 + 1;
return size;
}
size_t fromBase58(const char * encoded, size_t encodedSize, uint8_t * output, size_t outputSize){
if(encoded == NULL || output == NULL){ return 0; }
memzero(output, outputSize);
size_t l;
// looking for the end of char array
for(l=0; l<encodedSize; l++){
const char * pch = strchr(BASE58_CHARS, encoded[l]);
if(pch==NULL){ // char not in the alphabet
break;
}
}
encodedSize = l;
size_t size = fromBase58Length(encoded, encodedSize);
uint8_t * tmp = (uint8_t *) calloc(size, sizeof(uint8_t));
if(tmp == NULL){ return 0; }
uint8_t zeroCount = 0;
for(size_t i = 0; i<encodedSize; i++){
if(encoded[i] == BASE58_CHARS[0]){
zeroCount++;
}else{
break;
}
}
uint16_t val = 0;
for(size_t i = 0; i < encodedSize; i++){
const char * pch = strchr(BASE58_CHARS, encoded[i]);
if(pch!=NULL){
val = pch - BASE58_CHARS;
for(size_t j = 0; j < size; j++){
uint16_t cur = tmp[size-j-1]*58;
cur += val;
val = cur/256;
tmp[size-j-1] = cur%256;
}
}else{
free(tmp);
return 0;
}
}
// shifting array
uint8_t shift = 0;
for(size_t i = zeroCount; i < size; i++){
if(tmp[i] == 0){
shift++;
}else{
break;
}
}
if(size-shift > outputSize){
free(tmp);
return 0;
}
memcpy(output, tmp+shift, size-shift);
free(tmp);
return size-shift;
}
size_t fromBase58Check(const char * encoded, size_t encodedSize, uint8_t * output, size_t outputSize){
if(encoded == NULL || output == NULL){ return 0; }
uint8_t * arr = (uint8_t *) malloc(outputSize+4);
if(arr == NULL){ return 0; }
size_t l = fromBase58(encoded, encodedSize, arr, outputSize+4);
if(l<4){
free(arr);
return 0;
}
uint8_t hash[32];
doubleSha(arr, l-4, hash);
if(memcmp(arr+l-4, hash, 4)!=0){
free(arr);
return 0;
}
memcpy(output, arr, l-4);
memzero(arr, outputSize+4); // secret should not stay in RAM
free(arr);
return l-4;
}
#if USE_STD_STRING || USE_ARDUINO_STRING
size_t fromBase58(String encoded, uint8_t * output, size_t outputSize){
return fromBase58(encoded.c_str(), encoded.length(), output, outputSize);
};
size_t fromBase58Check(String encoded, uint8_t * output, size_t outputSize){
return fromBase58Check(encoded.c_str(), encoded.length(), output, outputSize);
};
#endif
#if !(USE_ARDUINO_STRING || USE_STD_STRING)
size_t fromBase58(const char * encoded, uint8_t * array, size_t arraySize){
return fromBase58(encoded, strlen(encoded), array, arraySize);
}
size_t fromBase58Check(const char * encoded, uint8_t * array, size_t arraySize){
return fromBase58Check(encoded, strlen(encoded), array, arraySize);
}
#endif
/******************* Base 43 conversion *******************/
size_t toBase43Length(const uint8_t * array, size_t arraySize){
if(array == NULL){ return 0; }
// Counting leading zeroes
size_t zeroCount = 0;
while(zeroCount < arraySize && !array[zeroCount]){
zeroCount++;
}
// size estimation. log(256)/log(43)
size_t size = (arraySize - zeroCount) * 148 / 100 + 1;
return size+zeroCount;
}
size_t toBase43(const uint8_t * array, size_t arraySize, char * output, size_t outputSize){
if(array == NULL || output == NULL){ return 0; }
// Counting leading zeroes
size_t zeroCount = 0;
while(zeroCount < arraySize && !array[zeroCount]){
zeroCount++;
}
// TODO: refactor with real sizes
// size estimation. log(256)/log(43)
size_t size = (arraySize - zeroCount) * 148 / 100 + 1;
if(outputSize < size+zeroCount){
return 0;
}
memzero(output, outputSize);
// array copy for manipulations
size_t bufferSize = arraySize - zeroCount;
uint8_t * buffer = (uint8_t *)calloc(bufferSize, sizeof(uint8_t));
if(buffer == NULL){ return 0; }
for(size_t i = zeroCount; i < arraySize; i++){
buffer[i - zeroCount] = array[i];
}
for(size_t j = 0; j < size; j++){
uint16_t reminder = 0;
uint16_t temp = 0;
for(size_t i = 0; i < bufferSize; i++){
temp = (reminder * 256 + buffer[i]);
reminder = temp % 43;
buffer[i] = temp/43;
}
output[size+zeroCount-j-1] = BASE43_CHARS[reminder];
}
free(buffer);
for (size_t i = 0; i < zeroCount; i++){
output[i] = BASE43_CHARS[0];
}
if(outputSize > size+zeroCount){
output[size+zeroCount] = '\0';
}
// removing leading zeroes
// TODO: refactor
int shift = 0;
for(size_t i=zeroCount; i < size+zeroCount; i++){
if(output[i]==BASE43_CHARS[0]){
shift++;
}else{
break;
}
}
if(shift>0){
for(size_t i=zeroCount+shift; i < size+zeroCount; i++){
output[i-shift] = output[i];
output[i] = 0;
}
}
size_t l = size+zeroCount-shift;
memzero(output + l, outputSize-l);
return l;
}
#if (USE_STD_STRING || USE_ARDUINO_STRING)
String toBase43(const uint8_t * array, size_t arraySize){
if(array == NULL){ return String(); }
size_t l = toBase43Length(array, arraySize);
char * output = (char *)calloc(l+1, sizeof(char));
if(output == NULL){ return String(); }
toBase43(array, arraySize, output, l);
String s(output);
free(output);
return s;
}
#endif
// TODO: add zero count, fix wrong length
size_t fromBase43Length(const char * array, size_t arraySize){
if(array == NULL){ return 0; }
size_t size = arraySize * 68 / 100 + 1;
return size;
}
size_t fromBase43(const char * encoded, size_t encodedSize, uint8_t * output, size_t outputSize){
if(encoded == NULL || output == NULL){ return 0; }
memzero(output, outputSize);
size_t l;
// looking for the end of char array
for(l=0; l<encodedSize; l++){
const char * pch = strchr(BASE43_CHARS, encoded[l]);
if(pch==NULL){ // char not in the alphabet
break;
}
}
encodedSize = l;
size_t size = fromBase43Length(encoded, encodedSize);
uint8_t * tmp = (uint8_t *) calloc(size, sizeof(uint8_t));
if(tmp == NULL){ return 0; }
uint8_t zeroCount = 0;
for(size_t i = 0; i<encodedSize; i++){
if(encoded[i] == BASE43_CHARS[0]){
zeroCount++;
}else{
break;
}
}
uint16_t val = 0;
for(size_t i = 0; i < encodedSize; i++){
const char * pch = strchr(BASE43_CHARS, encoded[i]);
if(pch!=NULL){
val = pch - BASE43_CHARS;
for(size_t j = 0; j < size; j++){
uint16_t cur = tmp[size-j-1]*43;
cur += val;
val = cur/256;
tmp[size-j-1] = cur%256;
}
}else{
free(tmp);
return 0;
}
}
// shifting array
uint8_t shift = 0;
for(size_t i = zeroCount; i < size; i++){
if(tmp[i] == 0){
shift++;
}else{
break;
}
}
if(size-shift > outputSize){
free(tmp);
return 0;
}
memcpy(output, tmp+shift, size-shift);
free(tmp);
return size-shift;
}
#if (USE_STD_STRING || USE_ARDUINO_STRING)
size_t fromBase43(String encoded, uint8_t * output, size_t outputSize){
return fromBase43(encoded.c_str(), encoded.length(), output, outputSize);
};
#endif
#if !(USE_ARDUINO_STRING || USE_STD_STRING)
size_t fromBase43(const char * encoded, uint8_t * array, size_t arraySize){
return fromBase43(encoded, strlen(encoded), array, arraySize);
}
#endif
/******************* Base 64 conversion *******************/
size_t toBase64Length(const uint8_t * array, size_t arraySize, uint8_t flags){
if(array == NULL){ return 0; }
size_t v = (arraySize / 3) * 4;
if(arraySize % 3 != 0){
if(flags & BASE64_NOPADDING){
v += (arraySize % 3) + 1;
}else{
v += 4;
}
}
return v;
}
size_t toBase64(const uint8_t * array, size_t arraySize, char * output, size_t outputSize, uint8_t flags){
if(array == NULL || output == NULL){ return 0; }
memzero(output, outputSize);
size_t cur = 0;
if(outputSize < toBase64Length(array, arraySize, flags)){
return 0;
}
while(3 * cur + 3 < arraySize){
uint32_t val = bigEndianToInt(array+3*cur, 3);
for(uint8_t i=0; i<4; i++){
output[4*cur + i] = BASE64_CHARS[((val >> (6*(3-i))) & 0x3F)];
}
cur++;
}
size_t len = cur * 4;
if(arraySize % 3 != 0){
uint8_t rem = arraySize % 3;
uint32_t val = bigEndianToInt(array+3*cur, rem);
val = val << ((3-rem) * 8);
for(uint8_t i=0; i<(rem+1); i++){
output[4*cur + i] = BASE64_CHARS[((val >> (6*(3-i))) & 0x3F)];
}
if(flags & BASE64_NOPADDING){
len += (rem+1);
}else{
memset(output + 4 * cur + 1 + rem, '=', 3-rem);
len += 4;
}
}else{
uint32_t val = bigEndianToInt(array+3*cur, 3);
for(uint8_t i=0; i<4; i++){
output[4*cur + i] = BASE64_CHARS[((val >> (6*(3-i))) & 0x3F)];
}
len += 4;
}
// replace with urlsafe characters
if(flags & BASE64_URLSAFE){
for(size_t i=0; i<len; i++){
if(output[i] == '+'){
output[i] = '-';
}
if(output[i] == '/'){
output[i] = '_';
}
}
}
return len;
}
#if USE_ARDUINO_STRING || USE_STD_STRING
String toBase64(const uint8_t * array, size_t arraySize, uint8_t flags){
if(array == NULL){ return String(); }
size_t len = toBase64Length(array, arraySize, flags) + 1; // +1 for null terminator
char * buf = (char *)malloc(len);
if(buf==NULL){ return String(); }
toBase64(array, arraySize, buf, len, flags);
String result(buf);
free(buf);
return result;
}
#endif
size_t fromBase64Length(const char * array, size_t arraySize, uint8_t flags){
if(array == NULL){ return 0; }
if(arraySize % 4 != 0 && (flags & BASE64_NOPADDING) == 0){ return 0; }
size_t v = (arraySize / 4) * 3;
if(flags & BASE64_NOPADDING){
if(arraySize % 4 != 0){
v += (arraySize % 4)-1;
}
}else{
if(array[arraySize-1] == '='){
v--;
}
if(array[arraySize-2] == '='){
v--;
}
}
return v;
}
size_t fromBase64(const char * encoded, size_t encodedSize, uint8_t * output, size_t outputSize, uint8_t flags){
if(encoded == NULL || output == NULL){ return 0; }
size_t cur = 0;
memzero(output, outputSize);
if(outputSize < fromBase64Length(encoded, encodedSize, flags)){
return 0;
}
while(cur*4 < encodedSize){
if(cur*4+3 >= encodedSize && (flags & BASE64_NOPADDING) == 0){
memzero(output, outputSize);
return 0;
}
uint32_t val = 0;
for(size_t i=0; i<4; i++){
char c = encoded[cur*4+i];
// replace characters for urlsafe version
if(c=='-' && (flags & BASE64_URLSAFE)){
c = '+';
}
if(c=='_' && (flags & BASE64_URLSAFE)){
c = '/';
}
const char * pch = strchr(BASE64_CHARS, c);
if(pch==NULL || ((flags & BASE64_NOPADDING) && (c == 0))){
if((encoded[cur*4+i] == '=') || (c == 0 && (flags & BASE64_NOPADDING))){
if(i==3){
val = (val >> 2);
if(outputSize < 3*cur+2){
memzero(output, outputSize);
return 0;
}
intToBigEndian(val, output+3*cur, 2);
return 3*cur + 2;
}
if(i==2){
val = (val >> 4);
if(outputSize < 3*cur+1){
memzero(output, outputSize);
return 0;
}
output[3*cur] = (val & 0xFF);
return 3*cur + 1;
}
}
memzero(output, outputSize);
return 0;
}else{
val = (val << 6) + ((pch - BASE64_CHARS) & 0x3F);
}
}
if(outputSize < 3*(cur+1)){
memzero(output, outputSize);
return 0;
}
intToBigEndian(val, output+3*cur, 3);
cur++;
}
return 3 * cur;
}
#if USE_STD_STRING || USE_ARDUINO_STRING
size_t fromBase64(String encoded, uint8_t * output, size_t outputSize, uint8_t flags){
return fromBase64(encoded.c_str(), encoded.length(), output, outputSize, flags);
};
String base64ToHex(String b64, uint8_t flags){
size_t len = fromBase64Length(b64.c_str(), strlen(b64.c_str()), flags) + 1; // +1 for null terminator
uint8_t * buf = (uint8_t *)malloc(len);
if(buf==NULL){ return String(); }
len = fromBase64(b64, buf, len, flags);
String result = toHex(buf, len);
free(buf);
return result;
};
String hexToBase64(String hex, uint8_t flags){
size_t len = strlen(hex.c_str())/2+1; // +1 for null terminator
uint8_t * buf = (uint8_t *)malloc(len);
if(buf==NULL){ return String(); }
len = fromHex(hex, buf, len);
String result = toBase64(buf, len, flags);
free(buf);
return result;
};
#endif
#if !(USE_ARDUINO_STRING || USE_STD_STRING)
size_t fromBase64(const char * b64, uint8_t * array, size_t arraySize, uint8_t flags){
if(b64 == NULL || array == NULL){ return 0; }
return fromBase64(b64, strlen(b64), array, arraySize, flags);
}
#endif
/* Integer conversion */
uint64_t littleEndianToInt(const uint8_t * array, size_t arraySize){
uint64_t num = 0;
for(size_t i = 0; i < arraySize; i++){
num <<= 8;
num += (array[arraySize-i-1] & 0xFF);
}
return num;
}
void intToLittleEndian(uint64_t num, uint8_t * array, size_t arraySize){
for(size_t i = 0; i < arraySize; i++){
array[i] = ((num >> (8*i)) & 0xFF);
}
}
uint64_t bigEndianToInt(const uint8_t * array, size_t arraySize){
uint64_t num = 0;
for(size_t i = 0; i < arraySize; i++){
num <<= 8;
num += (array[i] & 0xFF);
}
return num;
}
void intToBigEndian(uint64_t num, uint8_t * array, size_t arraySize){
for(size_t i = 0; i < arraySize; i++){
array[arraySize-i-1] = ((num >> (8*i)) & 0xFF);
}
}
/* Varint */
uint8_t lenVarInt(uint64_t num){
if(num < 0xfd){
return 1;
}
if((num >> 16) == 0){
return 3;
}
if((num >> 32) == 0){
return 5;
}
return 9;
}
uint64_t readVarInt(const uint8_t * array, size_t arraySize){
if(array[0] < 0xfd){
return array[0];
}else{
size_t len = (1 << (array[0] - 0xfc));
if(len+1 > arraySize){
return 0;
}
return littleEndianToInt(array + 1, len);
}
}
// TODO: don't repeat yourself!
size_t writeVarInt(uint64_t num, uint8_t * array, size_t arraySize){
uint8_t len = lenVarInt(num);
if(arraySize < len){
return 0;
}
if(len == 1){
array[0] = (uint8_t)(num & 0xFF);
}else{
switch(len){
case 3: array[0] = 0xfd;
break;
case 5: array[0] = 0xfe;
break;
case 9: array[0] = 0xff;
break;
}
intToLittleEndian(num, array+1, len-1);
}
return len;
}
================================================
FILE: src/Conversion.h
================================================
#ifndef __CONVERSION_H__
#define __CONVERSION_H__
#include "uBitcoin_conf.h"
#include <string.h>
#include <stdint.h>
#include "utility/segwit_addr.h"
#define BASE64_STANDARD 0
#define BASE64_NOPADDING 1
#define BASE64_URLSAFE 2
// TODO: get rid of these blahLength functions, they are redundant
// just stop when array is full and return errorcode
size_t toBase58Length(const uint8_t * array, size_t arraySize);
size_t toBase58(const uint8_t * array, size_t arraySize, char * output, size_t outputSize);
#if USE_ARDUINO_STRING
String toBase58(const uint8_t * array, size_t arraySize);
#endif
#if USE_STD_STRING
std::string toBase58(const uint8_t * array, size_t arraySize);
#endif
// base58 conversion with 4-byte checksum at the end (doubleSha)
size_t toBase58Check(const uint8_t * array, size_t arraySize, char * output, size_t outputSize);
#if USE_ARDUINO_STRING
String toBase58Check(const uint8_t * array, size_t arraySize);
#endif
#if USE_STD_STRING
std::string toBase58Check(const uint8_t * array, size_t arraySize);
#endif
size_t fromBase58Length(const char * array, size_t arraySize);
size_t fromBase58(const char * encoded, size_t encodedSize, uint8_t * output, size_t outputSize);
size_t fromBase58Check(const char * encoded, size_t encodedSize, uint8_t * output, size_t outputSize);
#if !(USE_ARDUINO_STRING || USE_STD_STRING)
size_t fromBase58(const char * encoded, uint8_t * array, size_t arraySize);
size_t fromBase58Check(const char * encoded, uint8_t * array, size_t arraySize);
#endif
#if USE_ARDUINO_STRING
size_t fromBase58(String encoded, uint8_t * output, size_t outputSize);
size_t fromBase58Check(String encoded, uint8_t * output, size_t outputSize);
#endif
#if USE_STD_STRING
size_t fromBase58(std::string encoded, uint8_t * output, size_t outputSize);
size_t fromBase58Check(std::string encoded, uint8_t * output, size_t outputSize);
#endif
size_t toBase43Length(const uint8_t * array, size_t arraySize);
size_t toBase43(const uint8_t * array, size_t arraySize, char * output, size_t outputSize);
#if USE_ARDUINO_STRING
String toBase43(const uint8_t * array, size_t arraySize);
#endif
#if USE_STD_STRING
std::string toBase43(const uint8_t * array, size_t arraySize);
#endif
size_t fromBase43Length(const char * array, size_t arraySize);
size_t fromBase43(const char * encoded, size_t encodedSize, uint8_t * output, size_t outputSize);
#if !(USE_ARDUINO_STRING || USE_STD_STRING)
size_t fromBase43(const char * encoded, uint8_t * array, size_t arraySize);
#endif
#if USE_ARDUINO_STRING
size_t fromBase43(String encoded, uint8_t * output, size_t outputSize);
#endif
#if USE_STD_STRING
size_t fromBase43(std::string encoded, uint8_t * output, size_t outputSize);
#endif
size_t toHex(const uint8_t * array, size_t arraySize, char * output, size_t outputSize);
#if USE_ARDUINO_STRING
String toHex(const uint8_t * array, size_t arraySize);
size_t toHex(uint8_t v, Print &s); // printing single hex value to Print
size_t toHex(const uint8_t * array, size_t arraySize, Print &s); // TODO: pass pointer instead printing array in hex Print
#endif
#if USE_STD_STRING
std::string toHex(const uint8_t * array, size_t arraySize);
#endif
size_t fromHex(const char * hex, size_t hexLen, uint8_t * array, size_t arraySize);
uint8_t hexToVal(char c);
#if !(USE_ARDUINO_STRING || USE_STD_STRING)
size_t fromHex(const char * hex, uint8_t * array, size_t arraySize);
#endif
#if USE_ARDUINO_STRING
size_t fromHex(String encoded, uint8_t * output, size_t outputSize);
#endif
#if USE_STD_STRING
size_t fromHex(std::string encoded, uint8_t * output, size_t outputSize);
#endif
size_t toBin(const uint8_t * array, size_t arraySize, char * output, size_t outputSize);
#if USE_ARDUINO_STRING
String toBin(const uint8_t * array, size_t arraySize);
size_t toBin(uint8_t v, Print &s); // printing single value to Print
size_t toBin(const uint8_t * array, size_t arraySize, Print &s);
#endif
#if USE_STD_STRING
std::string toBin(const uint8_t * array, size_t arraySize);
#endif
size_t fromBin(const char * bin, size_t binLen, uint8_t * array, size_t arraySize);
#if !(USE_ARDUINO_STRING || USE_STD_STRING)
size_t fromBin(const char * hex, uint8_t * array, size_t arraySize);
#endif
#if USE_ARDUINO_STRING
size_t fromBin(String encoded, uint8_t * output, size_t outputSize);
#endif
#if USE_STD_STRING
size_t fromBin(std::string encoded, uint8_t * output, size_t outputSize);
#endif
size_t toBase64Length(const uint8_t * array, size_t arraySize, uint8_t flags = BASE64_STANDARD);
size_t toBase64(const uint8_t * array, size_t arraySize, char * output, size_t outputSize, uint8_t flags = BASE64_STANDARD);
#if USE_ARDUINO_STRING
String toBase64(const uint8_t * array, size_t arraySize, uint8_t flags = BASE64_STANDARD);
#endif
#if USE_STD_STRING
std::string toBase64(const uint8_t * array, size_t arraySize, uint8_t flags = BASE64_STANDARD);
#endif
size_t fromBase64Length(const char * array, size_t arraySize, uint8_t flags = BASE64_STANDARD);
size_t fromBase64(const char * encoded, size_t encodedSize, uint8_t * output, size_t outputSize, uint8_t flags = BASE64_STANDARD);
#if !(USE_ARDUINO_STRING || USE_STD_STRING)
size_t fromBase64(const char * encoded, uint8_t * array, size_t arraySize, uint8_t flags = BASE64_STANDARD);
#endif
#if USE_ARDUINO_STRING
size_t fromBase64(String encoded, uint8_t * output, size_t outputSize, uint8_t flags = BASE64_STANDARD);
String base64ToHex(String b64, uint8_t flags = BASE64_STANDARD);
String hexToBase64(String hex, uint8_t flags = BASE64_STANDARD);
#endif
#if USE_STD_STRING
size_t fromBase64(std::string encoded, uint8_t * output, size_t outputSize, uint8_t flags = BASE64_STANDARD);
std::string base64ToHex(std::string b64, uint8_t flags = BASE64_STANDARD);
std::string hexToBase64(std::string hex, uint8_t flags = BASE64_STANDARD);
#endif
/* int conversion */
uint64_t littleEndianToInt(const uint8_t * array, size_t arraySize);
void intToLittleEndian(uint64_t num, uint8_t * array, size_t arraySize);
uint64_t bigEndianToInt(const uint8_t * array, size_t arraySize);
void intToBigEndian(uint64_t num, uint8_t * array, size_t arraySize);
/* varint */
uint8_t lenVarInt(uint64_t num); // returns length of the array required for varint encoding
uint64_t readVarInt(const uint8_t * array, size_t arraySize);
size_t writeVarInt(uint64_t num, uint8_t * array, size_t arraySize);
#endif
================================================
FILE: src/Electrum.cpp
================================================
#include "Electrum.h"
ElectrumTx::ElectrumTx(ElectrumTx const &other){
tx = other.tx;
is_segwit = false;
txInsMeta = new ElectrumInputMetadata[tx.inputsNumber];
for(unsigned int i=0; i<tx.inputsNumber; i++){
txInsMeta[i] = other.txInsMeta[i];
}
status = other.status;
bytes_parsed = other.bytes_parsed;
}
ElectrumTx& ElectrumTx::operator=(ElectrumTx const &other){
if (this == &other){ return *this; } // self-assignment
if(tx.inputsNumber > 0){
delete [] txInsMeta;
}
tx = other.tx;
txInsMeta = new ElectrumInputMetadata[tx.inputsNumber];
for(unsigned int i=0; i<tx.inputsNumber; i++){
txInsMeta[i] = other.txInsMeta[i];
}
status = other.status;
bytes_parsed = other.bytes_parsed;
return *this;
}
size_t ElectrumTx::from_stream(ParseStream *s){
if(status == PARSING_FAILED){
return 0;
}
if(status == PARSING_DONE){
if(tx.inputsNumber > 0){
delete [] txInsMeta;
}
txInsMeta = NULL;
tx = Tx();
bytes_parsed = 0;
is_segwit = false;
}
status = PARSING_INCOMPLETE;
size_t bytes_read = 0;
uint8_t prefix[] = {0x45, 0x50, 0x54, 0x46, 0xFF, 0x00};
while(s->available() && bytes_read+bytes_parsed<6){
uint8_t c = s->read();
bytes_read++;
if(c != prefix[bytes_read+bytes_parsed-1]){
status = PARSING_FAILED;
bytes_parsed += bytes_read;
return bytes_read;
}
}
if(!is_segwit && s->available() && status == PARSING_INCOMPLETE && tx.getStatus() != PARSING_FAILED){
bytes_read += s->parse(&tx);
if(tx.getStatus() == PARSING_DONE){
status = PARSING_DONE;
is_segwit = false;
txInsMeta = new ElectrumInputMetadata[tx.inputsNumber];
for(unsigned i=0; i<tx.inputsNumber; i++){
if(tx.txIns[i].scriptSig.length() != 88){ // no idea how to parse other things
status = PARSING_FAILED;
bytes_parsed+=bytes_read;
return bytes_read;
}else{
uint8_t arr[88];
tx.txIns[i].scriptSig.serialize(arr, sizeof(arr));
txInsMeta[i].hd.parse(arr+6,sizeof(arr)-6);
if(txInsMeta[i].hd.getStatus() != PARSING_DONE){
status = PARSING_FAILED;
bytes_parsed+=bytes_read;
return bytes_read;
}
txInsMeta[i].derivation[0] = arr[84] + (arr[85] << 8);
txInsMeta[i].derivation[1] = arr[86] + (arr[87] << 8);
tx.txIns[i].scriptSig = Script();
}
}
}
}
if(tx.getStatus() == PARSING_FAILED){
if(tx.inputsNumber > 0 && tx.txIns[0].witness.getStatus() == PARSING_FAILED){
for(unsigned int i=0; i<tx.inputsNumber; i++){
tx.txIns[i].witness = Witness();
}
tx.setStatus(PARSING_DONE);
tx.locktime = 0;
is_segwit = true;
txInsMeta = new ElectrumInputMetadata[tx.inputsNumber];
for(unsigned int i=0; i<tx.inputsNumber; i++){
txInsMeta[i].amount = 0;
txInsMeta[i].derivation[0] = 0;
txInsMeta[i].derivation[1] = 0;
}
}else{
status = PARSING_FAILED;
bytes_parsed += bytes_read;
return bytes_read;
}
}
if(is_segwit && tx.getStatus() == PARSING_DONE){
size_t start = 6 + tx.length() + 2 - 4; // locktime is not parsed yet
for(unsigned int i = 0; i < tx.inputsNumber; i++){
while(s->available() && bytes_parsed+bytes_read < start + 5){
s->read();
bytes_read++;
}
start += 5;
while(s->available() && bytes_parsed+bytes_read < start + 8){
uint8_t c = s->read();
txInsMeta[i].amount += (((uint64_t)c) << (8*(bytes_read+bytes_parsed-start)));
bytes_read++;
}
start += 8;
while(s->available() && bytes_parsed+bytes_read < start + 7){
s->read();
bytes_read++;
}
start += 7;
while(s->available() && bytes_parsed+bytes_read < start + 78){
bytes_read+=s->parse(&txInsMeta[i].hd);
}
start += 78;
while(s->available() && bytes_parsed+bytes_read < start + 4){
uint8_t c = s->read();
size_t cur = bytes_parsed+bytes_read-start;
txInsMeta[i].derivation[cur/2] += (c << (8 * (cur % 2)));
bytes_read++;
}
start += 4;
}
while(s->available() && bytes_parsed+bytes_read < start + 4){
uint8_t c = s->read();
tx.locktime += (c << (8*(bytes_read+bytes_parsed-start)));
bytes_read++;
}
start+=4;
if(bytes_parsed+bytes_read == start){
status = PARSING_DONE;
}
}
bytes_parsed+=bytes_read;
return bytes_read;
}
ElectrumTx::~ElectrumTx(){
delete [] txInsMeta;
}
uint8_t ElectrumTx::sign(const HDPrivateKey account){
uint8_t res = 0; // number of signed inputs
for(unsigned int i=0; i<tx.inputsNumber; i++){
HDPublicKey pub = account.xpub();
ScriptType type = txInsMeta[i].hd.type;
pub.type = type;
if(pub == txInsMeta[i].hd){
PrivateKey pk = account.child(txInsMeta[i].derivation[0]).child(txInsMeta[i].derivation[1]);
if(type == P2PKH || type == P2SH || type == UNKNOWN_TYPE || type == DIRECT_SCRIPT){
tx.signInput(i, pk);
}else{
tx.signSegwitInput(i, pk, txInsMeta[i].amount, type);
}
res++;
}
}
return res;
}
uint64_t ElectrumTx::fee() const{
uint64_t inputs_amount = 0;
for(unsigned int i=0; i < tx.inputsNumber; i++){
if(txInsMeta[i].amount == 0){
return 0;
}
inputs_amount += txInsMeta[i].amount;
}
uint64_t outputs_amount = 0;
for(unsigned int i=0; i < tx.outputsNumber; i++){
outputs_amount += tx.txOuts[i].amount;
}
if(inputs_amount < outputs_amount){
return 0;
}
return inputs_amount-outputs_amount;
}
================================================
FILE: src/Electrum.h
================================================
#ifndef __ELECTRUM_H__
#define __ELECTRUM_H__
#include "Bitcoin.h"
/**
* \brief Metadata for Electrum unsigned transaction format to determine how to sign transaction.
* Only partially supported - 2 derivation indexes and HD public key. Segwit and legacy.
*/
typedef struct{
HDPublicKey hd;
uint16_t derivation[2];
uint64_t amount;
} ElectrumInputMetadata;
/**
* \brief Electrum Partially Signed Transaction class.<br>
* WARNING: Only partial support is implemented! No multisig and single keys.<br>
* For now implements only single HD key transactions are supported (no multisig).<br>
* If you explain me how electrum encodes unsigned multisig I will make it.
*/
class ElectrumTx : public Streamable{
protected:
virtual size_t from_stream(ParseStream *s);
virtual size_t to_stream(SerializeStream *s, size_t offset = 0) const{ return s->serialize(&tx, offset); };
bool is_segwit;
public:
ElectrumTx(){ txInsMeta = NULL; is_segwit = false; };
ElectrumTx(ElectrumTx const &other);
~ElectrumTx();
Tx tx;
virtual size_t length() const{ return tx.length(); };
/** \bried metadata for inputs */
ElectrumInputMetadata * txInsMeta;
/** \brief signs all inputs with matching hd pubkey with account HDPrivateKey.
* Returns number of inputs signed.
*/
uint8_t sign(const HDPrivateKey account);
/** \brief calculates fee if input amounts are known */
uint64_t fee() const;
ElectrumTx &operator=(ElectrumTx const &other);
bool isValid() const{ return status==PARSING_DONE; };
explicit operator bool() const{ return isValid(); };
};
#endif // __ELECTRUM_H__
================================================
FILE: src/HDWallet.cpp
================================================
#include <stdint.h>
#include <string.h>
#include "Bitcoin.h"
#include "Hash.h"
#include "Conversion.h"
#include "utility/trezor/sha2.h"
#include "utility/segwit_addr.h"
#include "utility/trezor/bignum.h"
#include "utility/trezor/ecdsa.h"
#include "utility/trezor/secp256k1.h"
#include "utility/trezor/memzero.h"
#if USE_STD_STRING
using std::string;
#define String string
#endif
// ---------------------------------------------------------------- HDPrivateKey class
void HDPrivateKey::init(){
reset();
memzero(chainCode, 32);
memzero(num, 32);
pubKey.compressed = true;
depth = 0;
memzero(parentFingerprint, 4);
childNumber = 0;
type = UNKNOWN_TYPE;
status = PARSING_DONE;
pubKey.compressed = true;
}
HDPrivateKey::HDPrivateKey(void){
init();
}
HDPrivateKey::HDPrivateKey(const uint8_t secret[32],
const uint8_t chain_code[32],
uint8_t key_depth,
const uint8_t parent_fingerprint_arr[4],
uint32_t child_number,
const Network * net,
ScriptType key_type){
init();
memcpy(num, secret, 32);
network = net;
pubKey = *this * GeneratorPoint;
pubKey.compressed = true;
type = key_type;
memcpy(chainCode, chain_code, 32);
depth = key_depth;
childNumber = child_number;
if(parent_fingerprint_arr != NULL){
memcpy(parentFingerprint, parent_fingerprint_arr, 4);
}else{
memzero(parentFingerprint, 4);
}
}
/*
HDPrivateKey &HDPrivateKey::operator=(const HDPrivateKey &other){
if (this == &other){ return *this; } // self-assignment
init();
type = other.type;
uint8_t secret[32];
other.getSecret(secret);
setSecret(secret);
memcpy(chainCode, other.chainCode, 32);
depth = other.depth;
childNumber = other.childNumber;
memcpy(parentFingerprint, other.parentFingerprint, 4);
return *this;
};*/
HDPrivateKey::HDPrivateKey(const char * xprvArr){
init();
from_str(xprvArr, strlen(xprvArr));
}
HDPrivateKey::HDPrivateKey(const char * mnemonic, size_t mnemonicSize, const char * password, size_t passwordSize, const Network * net, void (*progress_callback)(float)){
init();
fromMnemonic(mnemonic, mnemonicSize, password, passwordSize, net, progress_callback);
}
#if USE_ARDUINO_STRING || USE_STD_STRING
HDPrivateKey::HDPrivateKey(String mnemonic, String password, const Network * net, void (*progress_callback)(float)){
init();
fromMnemonic(mnemonic, password, net, progress_callback);
}
#endif
HDPrivateKey::~HDPrivateKey(void) {
memzero(chainCode, 32);
memzero(num, 32);
}
size_t HDPrivateKey::to_bytes(uint8_t * arr, size_t len) const{
uint8_t hex[78] = { 0 };
switch(type){
case P2WPKH:
memcpy(hex, network->zprv, 4);
break;
case P2SH_P2WPKH:
memcpy(hex, network->yprv, 4);
break;
case P2WSH:
memcpy(hex, network->Zprv, 4);
break;
case P2SH_P2WSH:
memcpy(hex, network->Yprv, 4);
break;
default:
memcpy(hex, network->xprv, 4);
}
hex[4] = depth;
memcpy(hex+5, parentFingerprint, 4);
for(uint8_t i=0; i<4; i++){
hex[12-i] = ((childNumber >> (i*8)) & 0xFF);
}
memcpy(hex+13, chainCode, 32);
memcpy(hex+46, num, 32);
if(len > sizeof(hex)){
len = sizeof(hex);
}
memcpy(arr, hex, len);
return len;
}
size_t HDPrivateKey::to_stream(SerializeStream *s, size_t offset) const{
uint8_t hex[78] = { 0 };
size_t bytes_written = 0;
to_bytes(hex, sizeof(hex));
while(s->available() && bytes_written+offset < sizeof(hex)){
s->write(hex[bytes_written+offset]);
bytes_written++;
}
return bytes_written;
}
size_t HDPrivateKey::from_stream(ParseStream *s){
if(status == PARSING_FAILED){
return 0;
}
if(status == PARSING_DONE){
bytes_parsed = 0;
}
status = PARSING_INCOMPLETE;
size_t bytes_read = 0;
// reading the prefix
while(s->available() > 0 && bytes_parsed+bytes_read < 4){
prefix[bytes_parsed+bytes_read] = s->read();
bytes_read++;
}
if(bytes_parsed+bytes_read == 4){
bool found = false;
for(int i=0; i<networks_len; i++){
if(memcmp(prefix, networks[i]->xprv, 4)==0){
type = UNKNOWN_TYPE;
found = true;
network = networks[i];
break;
}else if(memcmp(prefix, networks[i]->yprv, 4)==0){
type = P2SH_P2WPKH;
found = true;
network = networks[i];
break;
}else if(memcmp(prefix, networks[i]->zprv, 4)==0){
type = P2WPKH;
found = true;
network = networks[i];
break;
}else if(memcmp(prefix, networks[i]->Yprv, 4)==0){
type = P2SH_P2WSH;
found = true;
network = networks[i];
break;
}else if(memcmp(prefix, networks[i]->Zprv, 4)==0){
type = P2WSH;
found = true;
network = networks[i];
break;
}
}
if(!found){
status = PARSING_FAILED;
return bytes_read;
}
}
// depth
if(s->available() > 0 && bytes_parsed+bytes_read < 5){
depth = s->read();
bytes_read++;
}
// fingerprint
while(s->available() > 0 && bytes_parsed+bytes_read < 9){
parentFingerprint[bytes_parsed+bytes_read-5] = s->read();
bytes_read++;
}
// childnumber
while(s->available() > 0 && bytes_parsed+bytes_read < 13){
childNumber <<= 8;
childNumber += s->read();
bytes_read++;
}
// chaincode
while(s->available() > 0 && bytes_parsed+bytes_read < 45){
chainCode[bytes_parsed+bytes_read-13] = s->read();
bytes_read++;
}
// 00 before the private key
if(s->available() && bytes_parsed+bytes_read < 46){
uint8_t c = s->read();
bytes_read++;
if(c != 0){
status = PARSING_FAILED;
return bytes_read;
}
}
// num
while(s->available() > 0 && bytes_parsed+bytes_read < 78){
num[bytes_parsed+bytes_read-46] = s->read();
bytes_read++;
}
if(bytes_parsed+bytes_read == 78){
status = PARSING_DONE;
uint8_t zero[32] = { 0 };
if(memcmp(num, zero, 32)==0){ // should we add something else here?
status = PARSING_FAILED;
}
bignum256 n;
bn_read_be(num, &n);
bn_mod(&n, &secp256k1.order);
bn_write_be(&n, num);
pubKey = *this * GeneratorPoint;
pubKey.compressed = true;
}
bytes_parsed += bytes_read;
return bytes_read;
}
size_t HDPrivateKey::from_str(const char * xprvArr, size_t xprvLen){
init();
uint8_t arr[85] = { 0 };
size_t l = fromBase58Check(xprvArr, xprvLen, arr, sizeof(arr));
if(l == 0){
return 0; // decoding error
}
ParseByteStream s(arr, sizeof(arr));
HDPrivateKey::from_stream(&s);
return xprvLen;
}
int HDPrivateKey::fromSeed(const uint8_t * seed, size_t seedSize, const Network * net){
init();
uint8_t raw[64] = { 0 };
SHA512 sha;
char key[] = "Bitcoin seed";
sha.beginHMAC((uint8_t *)key, strlen(key));
sha.write(seed, seedSize);
sha.endHMAC(raw);
// sha512Hmac((byte *)key, strlen(key), seed, 64, raw);
memcpy(num, raw, 32);
network = net;
memcpy(chainCode, raw+32, 32);
pubKey = *this * GeneratorPoint;
pubKey.compressed = true;
return 1;
}
// int HDPrivateKey::fromSeed(const uint8_t seed[64], const Network * net){
// fromSeed(seed, 64);
// }
int HDPrivateKey::fromMnemonic(const char * mnemonic, size_t mnemonicSize, const char * password, size_t passwordSize, const Network * net, void (*progress_callback)(float)){
init();
uint8_t seed[64] = { 0 };
uint8_t ind[4] = { 0, 0, 0, 1 };
char salt[] = "mnemonic";
uint8_t u[64] = { 0 };
// first round
SHA512 sha;
sha.beginHMAC((uint8_t *)mnemonic, mnemonicSize);
sha.write((uint8_t *)salt, strlen(salt));
sha.write((uint8_t *)password, passwordSize);
sha.write(ind, sizeof(ind));
sha.endHMAC(u);
memcpy(seed, u, 64);
// other rounds
for(int i=1; i<PBKDF2_ROUNDS; i++){
if(progress_callback != NULL && (i & 0xFF) == 0xFF){
progress_callback((float)i/(float)(PBKDF2_ROUNDS-1));
}
sha.beginHMAC((uint8_t *)mnemonic, mnemonicSize);
sha.write(u, sizeof(u));
sha.endHMAC(u);
for(size_t j=0; j<sizeof(seed); j++){
seed[j] = seed[j] ^ u[j];
}
}
fromSeed(seed, sizeof(seed), net);
return 1;
}
#if USE_ARDUINO_STRING || USE_STD_STRING
int HDPrivateKey::fromMnemonic(String mnemonic, String password, const Network * net, void (*progress_callback)(float)){
return fromMnemonic(mnemonic.c_str(), mnemonic.length(), password.c_str(), password.length(), net, progress_callback);
}
#endif
int HDPrivateKey::xprv(char * arr, size_t len) const{
uint8_t hex[78] = { 0 };
to_bytes(hex, sizeof(hex));
return toBase58Check(hex, sizeof(hex), arr, len);
}
int HDPrivateKey::address(char * addr, size_t len) const{
switch(type){
case P2WPKH:
return segwitAddress(addr, len);
case P2SH_P2WPKH:
return nestedSegwitAddress(addr, len);
case P2PKH:
return legacyAddress(addr, len);
default:
return segwitAddress(addr, len);
}
}
#if USE_ARDUINO_STRING || USE_STD_STRING
String HDPrivateKey::xprv() const{
char arr[112] = { 0 };
xprv(arr, sizeof(arr));
return String(arr);
}
String HDPrivateKey::address() const{
switch(type){
case P2WPKH:
return segwitAddress();
case P2SH_P2WPKH:
return nestedSegwitAddress();
case P2PKH:
return legacyAddress();
default:
return segwitAddress();
}
}
#endif
int HDPrivateKey::xpub(char * arr, size_t len) const{
uint8_t hex[111] = { 0 }; // TODO: real length, in xpub compressed = true
switch(type){
case P2WPKH:
memcpy(hex, network->zpub, 4);
break;
case P2SH_P2WPKH:
memcpy(hex, network->ypub, 4);
break;
case P2WSH:
memcpy(hex, network->Zpub, 4);
break;
case P2SH_P2WSH:
memcpy(hex, network->Ypub, 4);
break;
default:
memcpy(hex, network->xpub, 4);
}
hex[4] = depth;
memcpy(hex+5, parentFingerprint, 4);
for(uint8_t i=0; i<4; i++){
hex[12-i] = ((childNumber >> (i*8)) & 0xFF);
}
memcpy(hex+13, chainCode, 32);
uint8_t sec[65] = { 0 };
int secLen = publicKey().sec(sec, sizeof(sec));
memcpy(hex+45, sec, secLen);
return toBase58Check(hex, 45+secLen, arr, len);
}
void HDPrivateKey::fingerprint(uint8_t arr[4]) const{
uint8_t secArr[65] = { 0 };
int l = publicKey().sec(secArr, sizeof(secArr));
uint8_t hash[20] = { 0 };
hash160(secArr, l, hash);
memcpy(arr, hash, 4);
}
#if USE_ARDUINO_STRING || USE_STD_STRING
String HDPrivateKey::fingerprint() const{
uint8_t arr[4];
fingerprint(arr);
return toHex(arr, 4);
}
#endif
HDPublicKey HDPrivateKey::xpub() const{
PublicKey p = publicKey();
return HDPublicKey(p.point, chainCode, depth, parentFingerprint, childNumber, network, type);
}
HDPrivateKey HDPrivateKey::child(uint32_t index, bool hardened) const{
if(index >= HARDENED_INDEX){
hardened = true;
}
HDPrivateKey child;
uint8_t sec[65] = { 0 };
PublicKey p = publicKey();
int l = p.sec(sec, sizeof(sec));
uint8_t hash[20] = { 0 };
hash160(sec, l, hash);
memcpy(child.parentFingerprint, hash, 4);
if(hardened && index < HARDENED_INDEX){
index += HARDENED_INDEX;
}
child.childNumber = index;
child.depth = depth+1;
child.type = type;
child.network = network;
if(hardened){
if(depth == 0){
switch(index){
case HARDENED_INDEX+44:
child.type = P2PKH;
break;
case HARDENED_INDEX+49:
child.type = P2SH_P2WPKH;
break;
case HARDENED_INDEX+84:
child.type = P2WPKH;
break;
case HARDENED_INDEX+48:
child.type = MULTISIG;
break;
case HARDENED_INDEX+45:
child.type = P2SH;
break;
}
}
if(depth == 1){
if(index == (HARDENED_INDEX+1)){
child.network = &Testnet;
}
if(index == HARDENED_INDEX){
child.network = &Mainnet;
}
}
if(depth == 3 && type == MULTISIG){
if(index == (HARDENED_INDEX+1)){
child.type = P2SH_P2WSH;
}
if(index == (HARDENED_INDEX+2)){
child.type = P2WSH;
}
}
}
uint8_t data[37];
if(hardened){
data[0] = 0;
getSecret(data+1);
}else{
memcpy(data, sec, 33);
}
intToBigEndian(index, data+33, 4);
uint8_t raw[64];
SHA512 sha;
sha.beginHMAC(chainCode, sizeof(chainCode));
sha.write(data, 37);
sha.endHMAC(raw);
memcpy(child.chainCode, raw+32, 32);
ECScalar r(raw, 32);
r += *this;
uint8_t secret[32];
r.getSecret(secret);
child.setSecret(secret);
memzero(secret, 32);
return child;
}
HDPrivateKey HDPrivateKey::hardenedChild(uint32_t index) const{
return child(index, true);
}
HDPrivateKey HDPrivateKey::derive(uint32_t * index, size_t len) const{
HDPrivateKey pk = *this;
for(size_t i=0; i<len; i++){
pk = pk.child(index[i]);
}
return pk;
}
HDPrivateKey HDPrivateKey::derive(const char * path) const{
static const char VALID_CHARS[] = "0123456789/'h";
size_t len = strlen(path);
const char * cur = path;
if(path[0] == 'm'){ // remove leading "m/"
cur+=2;
len-=2;
}
if(cur[len-1] == '/'){ // remove trailing "/"
len--;
}
HDPrivateKey pk; // invalid private key to return if something failed
size_t derivationLen = 1;
// checking if all chars are valid and counting derivation length
for(size_t i=0; i<len; i++){
const char * pch = strchr(VALID_CHARS, cur[i]);
if(pch == NULL){ // wrong character
return pk;
}
if(cur[i] == '/'){
derivationLen++;
}
}
uint32_t * derivation = (uint32_t *)calloc(derivationLen, sizeof(uint32_t));
if(derivation == NULL){ return pk; }
size_t current = 0;
for(size_t i=0; i<len; i++){
if(cur[i] == '/'){ // next
current++;
continue;
}
const char * pch = strchr(VALID_CHARS, cur[i]);
uint32_t val = pch-VALID_CHARS;
if(derivation[current] >= HARDENED_INDEX){ // can't have anything after hardened
free(derivation);
return pk;
}
if(val < 10){
derivation[current] = derivation[current]*10 + val;
}else{ // h or ' -> hardened
derivation[current] += HARDENED_INDEX;
}
}
pk = derive(derivation, derivationLen);
free(derivation);
return pk;
}
// ---------------------------------------------------------------- HDPublicKey class
size_t HDPublicKey::to_bytes(uint8_t * arr, size_t len) const{
uint8_t hex[78] = { 0 };
switch(type){
case P2WPKH:
memcpy(hex, network->zpub, 4);
break;
case P2SH_P2WPKH:
memcpy(hex, network->ypub, 4);
break;
case P2WSH:
memcpy(hex, network->Zpub, 4);
break;
case P2SH_P2WSH:
memcpy(hex, network->Ypub, 4);
break;
default:
memcpy(hex, network->xpub, 4);
}
hex[4] = depth;
memcpy(hex+5, parentFingerprint, 4);
for(uint8_t i=0; i<4; i++){
hex[12-i] = ((childNumber >> (i*8)) & 0xFF);
}
memcpy(hex+13, chainCode, 32);
memcpy(hex+46, point, 32);
hex[45] = 0x02 + (point[63] & 0x01);
if(len > sizeof(hex)){
len = sizeof(hex);
}
memcpy(arr, hex, len);
return len;
}
size_t HDPublicKey::to_stream(SerializeStream *s, size_t offset) const{
uint8_t hex[78] = { 0 };
size_t bytes_written = 0;
to_bytes(hex, sizeof(hex));
while(s->available() && bytes_written+offset < sizeof(hex)){
s->write(hex[bytes_written+offset]);
bytes_written++;
}
return bytes_written;
}
size_t HDPublicKey::from_stream(ParseStream *s){
if(status == PARSING_FAILED){
return 0;
}
if(status == PARSING_DONE){
bytes_parsed = 0;
}
status = PARSING_INCOMPLETE;
size_t bytes_read = 0;
// reading the prefix
while(s->available() > 0 && bytes_parsed+bytes_read < 4){
prefix[bytes_parsed+bytes_read] = s->read();
bytes_read++;
}
if(bytes_parsed+bytes_read == 4){
bool found = false;
for(int i=0; i<networks_len; i++){
if(memcmp(prefix, networks[i]->xpub, 4)==0){
type = UNKNOWN_TYPE;
found = true;
network = networks[i];
break;
}else if(memcmp(prefix, networks[i]->ypub, 4)==0){
type = P2SH_P2WPKH;
found = true;
network = networks[i];
break;
}else if(memcmp(prefix, networks[i]->zpub, 4)==0){
type = P2WPKH;
found = true;
network = networks[i];
break;
}else if(memcmp(prefix, networks[i]->Ypub, 4)==0){
type = P2SH_P2WSH;
found = true;
network = networks[i];
break;
}else if(memcmp(prefix, networks[i]->Zpub, 4)==0){
type = P2WSH;
found = true;
network = networks[i];
break;
}
}
if(!found){
status = PARSING_FAILED;
return bytes_read;
}
}
// depth
if(s->available() > 0 && bytes_parsed+bytes_read < 5){
depth = s->read();
bytes_read++;
}
// fingerprint
while(s->available() > 0 && bytes_parsed+bytes_read < 9){
parentFingerprint[bytes_parsed+bytes_read-5] = s->read();
bytes_read++;
}
// childnumber
while(s->available() > 0 && bytes_parsed+bytes_read < 13){
childNumber <<= 8;
childNumber += s->read();
bytes_read++;
}
// chaincode
while(s->available() > 0 && bytes_parsed+bytes_read < 45){
chainCode[bytes_parsed+bytes_read-13] = s->read();
bytes_read++;
}
// pubkey
while(s->available() > 0 && bytes_parsed+bytes_read < 78){
point[bytes_parsed+bytes_read-45] = s->read();
bytes_read++;
}
// uncompressing the pubkey
if(bytes_parsed+bytes_read == 78){
status = PARSING_DONE;
uint8_t arr[33];
memcpy(arr, point, 33);
uint8_t buf[65];
ecdsa_uncompress_pubkey(&secp256k1, arr, buf);
memcpy(point, buf+1, 64);
if(!isValid()){
status = PARSING_FAILED;
}
}
bytes_parsed += bytes_read;
return bytes_read;
}
size_t HDPublicKey::from_str(const char * xpubArr, size_t xpubLen){
uint8_t arr[85] = { 0 };
size_t l = fromBase58Check(xpubArr, xpubLen, arr, sizeof(arr));
if(l == 0){
return 0; // decoding error
}
ParseByteStream s(arr, sizeof(arr));
HDPublicKey::from_stream(&s);
return xpubLen;
}
HDPublicKey::HDPublicKey():PublicKey(){
memzero(prefix, 4);
compressed = true;
memzero(chainCode, 32);
depth = 0;
memzero(parentFingerprint, 4);
childNumber = 0;
network = &DEFAULT_NETWORK;
type = UNKNOWN_TYPE;
}
HDPublicKey::HDPublicKey(const uint8_t p[64],
const uint8_t chain_code[32],
uint8_t key_depth,
const uint8_t parent_fingerprint_arr[4],
uint32_t child_number,
const Network * net,
ScriptType key_type){
reset();
memcpy(point, p, 64);
memzero(prefix, 4);
compressed = true;
type = key_type;
network = net;
memcpy(chainCode, chain_code, 32);
depth = key_depth;
childNumber = child_number;
if(parent_fingerprint_arr != NULL){
memcpy(parentFingerprint, parent_fingerprint_arr, 4);
}else{
memzero(parentFingerprint, 4);
}
}
/*
HDPublicKey &HDPublicKey::operator=(const HDPublicKey &other){
if (this == &other){ return *this; } // self-assignment
type = other.type;
memcpy(point, other.point, 64);
memcpy(chainCode, other.chainCode, 32);
compressed = true;
depth = other.depth;
childNumber = other.childNumber;
memcpy(parentFingerprint, other.parentFingerprint, 4);
return *this;
};*/
HDPublicKey::HDPublicKey(const char * xpubArr){
reset();
memzero(prefix, 4);
childNumber = 0;
network = &DEFAULT_NETWORK;
from_str(xpubArr, strlen(xpubArr));
}
HDPublicKey::~HDPublicKey(void) {
memzero(point, 64);
memzero(chainCode, 32);
}
int HDPublicKey::xpub(char * arr, size_t len) const{
uint8_t hex[78] = { 0 };
HDPublicKey::to_bytes(hex, sizeof(hex));
return toBase58Check(hex, sizeof(hex), arr, len);
}
int HDPublicKey::address(char * addr, size_t len) const{
switch(type){
case P2WPKH:
return PublicKey::segwitAddress(addr, len, network);
case P2SH_P2WPKH:
return PublicKey::nestedSegwitAddress(addr, len, network);
case P2PKH:
return PublicKey::legacyAddress(addr, len, network);
default:
return PublicKey::segwitAddress(addr, len, network);
}
}
#if USE_ARDUINO_STRING || USE_STD_STRING
String HDPublicKey::xpub() const{
char arr[114] = { 0 };
xpub(arr, sizeof(arr));
return String(arr);
}
String HDPublicKey::address() const{
switch(type){
case P2WPKH:
return PublicKey::segwitAddress(network);
case P2SH_P2WPKH:
return PublicKey::nestedSegwitAddress(network);
case P2PKH:
return PublicKey::legacyAddress(network);
default:
return PublicKey::segwitAddress(network);
}
}
#endif
void HDPublicKey::fingerprint(uint8_t arr[4]) const{
uint8_t secArr[65] = { 0 };
int l = sec(secArr, sizeof(secArr));
uint8_t hash[20] = { 0 };
hash160(secArr, l, hash);
memcpy(arr, hash, 4);
}
#if USE_ARDUINO_STRING || USE_STD_STRING
String HDPublicKey::fingerprint() const{
uint8_t arr[4];
fingerprint(arr);
return toHex(arr, 4);
}
#endif
HDPublicKey HDPublicKey::child(uint32_t index) const{
HDPublicKey child;
uint8_t secArr[65] = { 0 };
int l = sec(secArr, sizeof(secArr));
uint8_t hash[20] = { 0 };
hash160(secArr, l, hash);
memcpy(child.parentFingerprint, hash, 4);
child.childNumber = index;
child.depth = depth+1;
child.type = type;
child.network = network;
uint8_t data[37];
memcpy(data, secArr, 33);
intToBigEndian(index, data+33, 4);
uint8_t raw[64];
SHA512 sha;
sha.beginHMAC(chainCode, sizeof(chainCode));
sha.write(data, 37);
sha.endHMAC(raw);
memcpy(child.chainCode, raw+32, 32);
ECScalar r(raw, 32);
ECPoint p = r*GeneratorPoint;
p += *this;
memcpy(child.point, p.point, 64);
child.compressed = true;
return child;
}
HDPublicKey HDPublicKey::derive(uint32_t * index, size_t len) const{
HDPublicKey pk = *this;
for(size_t i=0; i<len; i++){
pk = pk.child(index[i]);
}
return pk;
}
HDPublicKey HDPublicKey::derive(const char * path) const{
static const char VALID_CHARS[] = "0123456789/";
size_t len = strlen(path);
const char * cur = path;
if(path[0] == 'm'){ // remove leading "m/"
cur+=2;
len-=2;
}
if(cur[len-1] == '/'){ // remove trailing "/"
len--;
}
HDPublicKey pk; // dummy to return if something failed
size_t derivationLen = 1;
// checking if all chars are valid and counting derivation length
for(size_t i=0; i<len; i++){
const char * pch = strchr(VALID_CHARS, cur[i]);
if(pch == NULL){ // wrong character
return pk;
}
if(cur[i] == '/'){
derivationLen++;
}
}
uint32_t * derivation = (uint32_t *)calloc(derivationLen, sizeof(uint32_t));
if(derivation == NULL){ return pk; }
size_t current = 0;
for(size_t i=0; i<len; i++){
if(cur[i] == '/'){ // next
if(derivation[current] >= HARDENED_INDEX){ // can't be hardened
free(derivation);
return pk;
}
current++;
continue;
}
const char * pch = strchr(VALID_CHARS, cur[i]);
uint32_t val = pch-VALID_CHARS;
derivation[current] = derivation[current]*10 + val;
}
pk = derive(derivation, derivationLen);
free(derivation);
return pk;
}
================================================
FILE: src/Hash.cpp
================================================
#include "Hash.h"
#include "utility/trezor/hmac.h"
#include "utility/trezor/ripemd160.h"
#if USE_STD_STRING
using std::string;
#define String string
#endif
// generic funtcions for single line hash
static size_t hashData(HashAlgorithm * algo, const uint8_t * data, size_t len, uint8_t * hash){
algo->begin();
algo->write(data, len);
return algo->end(hash);
}
#if USE_ARDUINO_STRING || USE_STD_STRING
static size_t hashString(HashAlgorithm * algo, const String s, uint8_t * hash){
return hashData(algo, (const uint8_t *)s.c_str(), s.length(), hash);
}
#endif
/************************* RIPEMD-160 *************************/
int rmd160(const uint8_t * data, size_t len, uint8_t hash[20]){
RMD160 rmd;
return hashData(&rmd, data, len, hash);
}
int rmd160(const char * data, size_t len, uint8_t hash[20]){
return rmd160((uint8_t*)data, len, hash);
}
#if USE_ARDUINO_STRING || USE_STD_STRING
int rmd160(const String data, uint8_t hash[20]){
RMD160 rmd;
return hashString(&rmd, data, hash);
}
#endif
void RMD160::begin(){
ripemd160_Init(&ctx);
};
size_t RMD160::write(const uint8_t * data, size_t len){
ripemd160_Update(&ctx, data, len);
return len;
}
size_t RMD160::write(uint8_t b){
uint8_t arr[1] = { b };
ripemd160_Update(&ctx, arr, 1);
return 1;
}
size_t RMD160::end(uint8_t hash[20]){
ripemd160_Final(&ctx, hash);
return 20;
}
/************************** SHA-256 **************************/
int sha256(const uint8_t * data, size_t len, uint8_t hash[32]){
SHA256 sha;
return hashData(&sha, data, len, hash);
}
int sha256(const char * data, size_t len, uint8_t hash[32]){
return sha256((uint8_t*)data, len, hash);
}
#if USE_ARDUINO_STRING || USE_STD_STRING
int sha256(const String data, uint8_t hash[32]){
SHA256 sha;
return hashString(&sha, data, hash);
}
#endif
TaggedHash::TaggedHash(const char * tag){
begin();
uint8_t th[32];
sha256(tag, strlen(tag), th);
write(th, 32);
write(th, 32);
}
int tagged_hash(const char * tag, const uint8_t * data, size_t dataLen, uint8_t hash[32]){
TaggedHash th(tag);
return hashData(&th, data, dataLen, hash);
}
int tagged_hash(const char * tag, const char * data, size_t len, uint8_t hash[32]){
return tagged_hash(tag, (uint8_t*)data, len, hash);
}
#if USE_ARDUINO_STRING || USE_STD_STRING
int tagged_hash(const String tag, const String data, uint8_t hash[32]){
TaggedHash th(tag.c_str());
return hashString(&th, data, hash);
}
#endif
int sha256Hmac(const uint8_t * key, size_t keyLen, const uint8_t * data, size_t dataLen, uint8_t hash[32]){
ubtc_hmac_sha256(key, keyLen, data, dataLen, hash);
return 32;
}
void SHA256::begin(){
sha256_Init(&ctx.ctx);
}
void SHA256::beginHMAC(const uint8_t * key, size_t keySize){
ubtc_hmac_sha256_Init(&ctx, key, keySize);
}
size_t SHA256::write(const uint8_t * data, size_t len){
sha256_Update(&ctx.ctx, data, len);
return len;
}
size_t SHA256::write(uint8_t b){
uint8_t arr[1] = { b };
sha256_Update(&ctx.ctx, arr, 1);
return 1;
}
size_t SHA256::end(uint8_t hash[32]){
sha256_Final(&ctx.ctx, hash);
return 32;
}
size_t SHA256::endHMAC(uint8_t hmac[32]){
ubtc_hmac_sha256_Final(&ctx, hmac);
return 32;
}
/************************* Hash-160 **************************/
/******************** rmd160( sha256( m ) ) ******************/
int hash160(const uint8_t * data, size_t len, uint8_t hash[20]){
Hash160 h160;
return hashData(&h160, data, len, hash);
}
int hash160(const char * data, size_t len, uint8_t hash[20]){
return hash160((uint8_t*)data, len, hash);
}
#if USE_ARDUINO_STRING || USE_STD_STRING
int hash160(const String data, uint8_t hash[20]){
Hash160 h160;
return hashString(&h160, data, hash);
}
#endif
size_t Hash160::end(uint8_t hash[20]){
uint8_t h[32];
sha256_Final(&ctx.ctx, h);
rmd160(h, 32, hash);
return 20;
}
/********************** Double SHA-256 ***********************/
/******************** sha256( sha256( m ) ) ******************/
int doubleSha(const uint8_t * data, size_t len, uint8_t hash[32]){
DoubleSha sha;
return hashData(&sha, data, len, hash);
}
int doubleSha(const char * data, size_t len, uint8_t hash[32]){
return doubleSha((uint8_t*)data, len, hash);
}
#if USE_ARDUINO_STRING || USE_STD_STRING
int doubleSha(const String data, uint8_t hash[32]){
DoubleSha sha;
return hashString(&sha, data, hash);
}
#endif
size_t DoubleSha::end(uint8_t hash[32]){
uint8_t h[32];
sha256_Final(&ctx.ctx, h);
sha256(h, 32, hash);
return 32;
}
/************************** SHA-512 **************************/
int sha512(const uint8_t * data, size_t len, uint8_t hash[64]){
SHA512 sha;
return hashData(&sha, data, len, hash);
}
int sha512(const char * data, size_t len, uint8_t hash[64]){
return sha512((uint8_t*)data, len, hash);
}
#if USE_ARDUINO_STRING || USE_STD_STRING
int sha512(const String data, uint8_t hash[64]){
SHA512 sha;
return hashString(&sha, data, hash);
}
#endif
void SHA512::begin(){
sha512_Init(&ctx.ctx);
};
void SHA512::beginHMAC(const uint8_t * key, size_t keySize){
ubtc_hmac_sha512_Init(&ctx, key, keySize);
}
size_t SHA512::write(const uint8_t * data, size_t len){
sha512_Update(&ctx.ctx, data, len);
return len;
}
size_t SHA512::write(uint8_t b){
uint8_t arr[1] = { b };
sha512_Update(&ctx.ctx, arr, 1);
return 1;
}
size_t SHA512::end(uint8_t hash[64]){
sha512_Final(&ctx.ctx, hash);
return 64;
}
size_t SHA512::endHMAC(uint8_t hmac[64]){
ubtc_hmac_sha512_Final(&ctx, hmac);
return 64;
}
int sha512Hmac(const uint8_t * key, size_t keyLen, const uint8_t * data, size_t dataLen, uint8_t hash[64]){
ubtc_hmac_sha512(key, keyLen, data, dataLen, hash);
return 64;
}
================================================
FILE: src/Hash.h
================================================
/** @file Hash.h
* \brief All hashing functions and classes live here
*/
#ifndef __HASH_H__18NLNNCSJ2
#define __HASH_H__18NLNNCSJ2
#include "uBitcoin_conf.h"
#include "BaseClasses.h"
#include <stdint.h>
#include <string.h>
#include "utility/trezor/sha2.h"
#include "utility/trezor/ripemd160.h"
#include "utility/trezor/hmac.h"
/** \brief Abstract hashing class */
class HashAlgorithm : public SerializeStream{
public:
size_t available(){ return 100; };
void begin(){};
virtual size_t write(const uint8_t * data, size_t len) = 0;
virtual size_t write(uint8_t b) = 0;
virtual size_t end(uint8_t * hash) = 0;
};
/************************* RIPEMD-160 *************************/
/** \brief ripemd-160 one-line hashing function */
int rmd160(const uint8_t * data, size_t len, uint8_t hash[20]);
int rmd160(const char * data, size_t len, uint8_t hash[20]);
#if USE_ARDUINO_STRING
int rmd160(const String data, uint8_t hash[20]);
#endif
#if USE_STD_STRING
int rmd160(const std::string data, uint8_t hash[20]);
#endif
class RMD160 : public HashAlgorithm{
public:
RMD160(){ begin(); };
void begin();
size_t write(const uint8_t * data, size_t len);
size_t write(uint8_t b);
size_t end(uint8_t hash[20]);
protected:
RIPEMD160_CTX ctx;
};
/************************** SHA-256 **************************/
/** \brief sha256 one-line hashing function → 32 bytes output */
int sha256(const uint8_t * data, size_t len, uint8_t hash[32]);
int sha256(const char * data, size_t len, uint8_t hash[32]);
#if USE_ARDUINO_STRING
int sha256(const String data, uint8_t hash[32]);
#endif
#if USE_STD_STRING
int sha256(const std::string data, uint8_t hash[32]);
#endif
int sha256Hmac(const uint8_t * key, size_t keyLen, const uint8_t * data, size_t dataLen, uint8_t hash[32]);
class SHA256 : public HashAlgorithm{
public:
SHA256(){ begin(); };
void begin();
void beginHMAC(const uint8_t * key, size_t keySize);
size_t write(const uint8_t * data, size_t len);
size_t write(uint8_t b);
size_t end(uint8_t hash[32]);
size_t endHMAC(uint8_t hmac[32]);
protected:
HMAC_SHA256_CTX ctx;
};
/************************ Tagged hash ************************/
/** \brief tagged hash class as defined in bip-schnorr (bip340) */
class TaggedHash : public SHA256{
public:
TaggedHash(const char * tag);
};
/** \brief tagged hash one-line hashing function → 32 bytes output */
int tagged_hash(const char * tag, const uint8_t * data, size_t dataLen, uint8_t hash[32]);
int tagged_hash(const char * tag, const char * data, size_t dataLen, uint8_t hash[32]);
#if USE_ARDUINO_STRING
int tagged_hash(const String tag, const String data, uint8_t hash[32]);
#endif
#if USE_STD_STRING
int tagged_hash(const std::string tag, const std::string data, uint8_t hash[32]);
#endif
/************************* Hash-160 **************************/
/******************** rmd160( sha256( m ) ) ******************/
/** \brief rmd160(sha256(data)) → 20 bytes output */
int hash160(const uint8_t * data, size_t len, uint8_t hash[20]);
int hash160(const uint8_t * data, size_t len, uint8_t hash[20]);
int hash160(const char * data, size_t len, uint8_t hash[20]);
#if USE_ARDUINO_STRING
int hash160(const String data, uint8_t hash[20]);
#endif
#if USE_STD_STRING
int hash160(const std::string data, uint8_t hash[20]);
#endif
class Hash160 : public SHA256{
public:
size_t end(uint8_t hash[20]);
};
/********************** Double SHA-256 ***********************/
/******************** sha256( sha256( m ) ) ******************/
/** \brief sha256(sha256(data)) → 32 bytes output */
int doubleSha(const uint8_t * data, size_t len, uint8_t hash[32]);
int doubleSha(const char * data, size_t len, uint8_t hash[32]);
#if USE_ARDUINO_STRING
int doubleSha(const String data, uint8_t hash[32]);
#endif
#if USE_STD_STRING
int doubleSha(const std::string data, uint8_t hash[32]);
#endif
class DoubleSha : public SHA256{
public:
size_t end(uint8_t hash[32]);
};
/************************** SHA-512 **************************/
int sha512Hmac(const uint8_t * key, size_t keyLen, const uint8_t * data, size_t dataLen, uint8_t hash[64]);
int sha512(const uint8_t * data, size_t len, uint8_t hash[64]);
int sha512(const char * data, size_t len, uint8_t hash[64]);
#if USE_ARDUINO_STRING
int sha512(const String data, uint8_t hash[64]);
#endif
#if USE_STD_STRING
int sha512(const std::string data, uint8_t hash[64]);
#endif
class SHA512 : public HashAlgorithm{
public:
SHA512(){ begin(); };
void begin();
void beginHMAC(const uint8_t * key, size_t keySize);
size_t write(const uint8_t * data, size_t len);
size_t write(uint8_t b);
size_t end(uint8_t hash[64]);
size_t endHMAC(uint8_t hmac[64]);
protected:
HMAC_SHA512_CTX ctx;
};
#endif // __HASH_H__18NLNNCSJ2
================================================
FILE: src/Networks.cpp
================================================
// all known networks
#include "Networks.h"
const Network Mainnet = {
0x00, // p2pkh
0x05, // p2sh
"bc", // bech32
0x80, // wif
{ 0x04, 0x88, 0xad, 0xe4 }, // xprv
{ 0x04, 0x9d, 0x78, 0x78 }, // yprv
{ 0x04, 0xb2, 0x43, 0x0c }, // zprv
{ 0x02, 0x95, 0xb0, 0x05 }, // Yprv
{ 0x02, 0xaa, 0x7a, 0x99 }, // Zprv
{ 0x04, 0x88, 0xb2, 0x1e }, // xpub
{ 0x04, 0x9d, 0x7c, 0xb2 }, // ypub
{ 0x04, 0xb2, 0x47, 0x46 }, // zpub
{ 0x02, 0x95, 0xb4, 0x3f }, // Ypub
{ 0x02, 0xaa, 0x7e, 0xd3 }, // Zpub
0 // bip32 coin type
};
const Network Testnet = {
0x6F, // p2pkh
0xC4, // p2sh
"tb", // bech32
0xEF, // wif
{ 0x04, 0x35, 0x83, 0x94 }, // tprv
{ 0x04, 0x4a, 0x4e, 0x28 }, // uprv
{ 0x04, 0x5f, 0x18, 0xbc }, // vprv
{ 0x02, 0x42, 0x85, 0xb5 }, // Uprv
{ 0x02, 0x57, 0x50, 0x48 }, // Vprv
{ 0x04, 0x35, 0x87, 0xcf }, // tpub
{ 0x04, 0x4a, 0x52, 0x62 }, // upub
{ 0x04, 0x5f, 0x1c, 0xf6 }, // vpub
{ 0x02, 0x42, 0x89, 0xef }, // Upub
{ 0x02, 0x57, 0x54, 0x83 }, // Vpub
1 // bip32 coin type
};
const Network Regtest = {
0x6F, // p2pkh
0xC4, // p2sh
"bcrt", // bech32
0xEF, // wif
{ 0x04, 0x35, 0x83, 0x94 }, // tprv
{ 0x04, 0x4a, 0x4e, 0x28 }, // uprv
{ 0x04, 0x5f, 0x18, 0xbc }, // vprv
{ 0x02, 0x42, 0x85, 0xb5 }, // Uprv
{ 0x02, 0x57, 0x50, 0x48 }, // Vprv
{ 0x04, 0x35, 0x87, 0xcf }, // tpub
{ 0x04, 0x4a, 0x52, 0x62 }, // upub
{ 0x04, 0x5f, 0x1c, 0xf6 }, // vpub
{ 0x02, 0x42, 0x89, 0xef }, // Upub
{ 0x02, 0x57, 0x54, 0x83 }, // Vpub
1 // bip32 coin type
};
// signet is the same as testnet
const Network Signet = Testnet;
const Network * networks[4] = { &Mainnet, &Testnet, &Regtest, &Signet };
const uint8_t networks_len = 4;
================================================
FILE: src/Networks.h
================================================
#ifndef __UBTC_NETWORKS_H__
#define __UBTC_NETWORKS_H__
#include <stdint.h>
/** \brief Prefixes for particular network (Mainnet / Testnet ).<br>
* HD key prefixes are described here:<br>
* https://github.com/satoshilabs/slips/blob/master/slip-0132.md<br>
* useful tool: in https://iancoleman.io/bip39/
*/
typedef struct {
/** \brief Pay-To-Pubkey-Hash addresses */
uint8_t p2pkh;
/** \brief Pay-To-Script-Hash addresses */
uint8_t p2sh;
/** \brief Prefix for segwit addreses ...for regtest it is larger */
char bech32[5];
/** \brief Wallet Import Format, used in PrivateKey */
uint8_t wif;
/** \brief HD private key for legacy addresses (P2PKH) */
uint8_t xprv[4];
/** \brief HD private key for nested Segwit (P2SH-P2WPKH) */
uint8_t yprv[4];
/** \brief HD private key for native Segwit (P2WPKH) */
uint8_t zprv[4];
/** \brief HD private key for nested Segwit Multisig (P2SH-P2WSH) */
uint8_t Yprv[4];
/** \brief HD private key for native Segwit Multisig (P2WSH) */
uint8_t Zprv[4];
/** \brief HD public key for legacy addresses (P2PKH) */
uint8_t xpub[4];
/** \brief HD public key for nested Segwit (P2SH-P2WPKH) */
uint8_t ypub[4];
/** \brief HD public key for native Segwit (P2WPKH) */
uint8_t zpub[4];
/** \brief HD public key for nested Segwit Multisig (P2SH-P2WSH) */
uint8_t Ypub[4];
/** \brief HD public key for native Segwit Multisig (P2WSH) */
uint8_t Zpub[4];
/** \brief bip32 coin index */
uint32_t bip32;
} Network;
extern const Network Mainnet;
extern const Network Testnet;
extern const Network Regtest;
extern const Network Signet;
extern const Network * networks[];
extern const uint8_t networks_len;
#endif // __UBTC_NETWORKS_H__
================================================
FILE: src/OpCodes.h
================================================
#ifndef __OPCODES_H__R3NU8EN25O
#define __OPCODES_H__R3NU8EN25O
#include "uBitcoin_conf.h"
// reference: https://en.bitcoin.it/wiki/Script#Opcodes
// uncomment the following string to use disabled op-codes:
// #define INCLUDE_DISABLED
/* OP_CODES */
/* Constants */
#define OP_0 0
#define OP_PUSHDATA1 76
#define OP_PUSHDATA2 77
#define OP_PUSHDATA4 78
#define OP_1NEGATE 79
#define OP_RESERVED 80
#define OP_1 81
#define OP_2 82
#define OP_3 83
#define OP_4 84
#define OP_5 85
#define OP_6 86
#define OP_7 87
#define OP_8 88
#define OP_9 89
#define OP_10 90
#define OP_11 91
#define OP_12 92
#define OP_13 93
#define OP_14 94
#define OP_15 95
#define OP_16 96
/* Flow control */
#define OP_NOP 97
#define OP_VER 98
#define OP_IF 99
#define OP_NOTIF 100
#define OP_VERIF 101
#define OP_VERNOTIF 102
#define OP_ELSE 103
#define OP_ENDIF 104
#define OP_VERIFY 105
#define OP_RETURN 106
/* Stack */
#define OP_TOALTSTACK 107
#define OP_FROMALTSTACK 108
#define OP_2DROP 109
#define OP_2DUP 110
#define OP_3DUP 111
#define OP_2OVER 112
#define OP_2ROT 113
#define OP_2SWAP 114
#define OP_IFDUP 115
#define OP_DEPTH 116
#define OP_DROP 117
#define OP_DUP 118
#define OP_NIP 119
#define OP_OVER 120
#define OP_PICK 121
#define OP_ROLL 122
#define OP_ROT 123
#define OP_SWAP 124
#define OP_TUCK 125
/* Splice */
#ifdef INCLUDE_DISABLED
#define OP_CAT 126
#define OP_SUBSTR 127
#define OP_LEFT 128
#define OP_RIGHT 129
#endif
#define OP_SIZE 130
/* Bitwise logic */
#ifdef INCLUDE_DISABLED
#define OP_INVERT 131
#define OP_AND 132
#define OP_OR 133
#define OP_XOR 134
#endif
#define OP_EQUAL 135
#define OP_EQUALVERIFY 136
#define OP_RESERVED1 137
#define OP_RESERVED2 138
/* Arithmetic */
#define OP_1ADD 139
#define OP_1SUB 140
#ifdef INCLUDE_DISABLED
#define OP_2MUL 141
#define OP_2DIV 142
#endif
#define OP_NEGATE 143
#define OP_ABS 144
#define OP_NOT 145
#define OP_0NOTEQUAL 146
#define OP_ADD 147
#define OP_SUB 148
#ifdef INCLUDE_DISABLED
#define OP_MUL 149
#define OP_DIV 150
#define OP_MOD 151
#define OP_LSHIFT 152
#define OP_RSHIFT 153
#endif
#define OP_BOOLAND 154
#define OP_BOOLOR 155
#define OP_NUMEQUAL 156
#define OP_NUMEQUALVERIFY 157
#define OP_NUMNOTEQUAL 158
#define OP_LESSTHAN 159
#define OP_GREATERTHAN 160
#define OP_LESSTHANOREQUAL 161
#define OP_GREATERTHANOREQUAL 162
#define OP_MIN 163
#define OP_MAX 164
#define OP_WITHIN 165
/* Crypto */
#define OP_RIPEMD160 166
#define OP_SHA1 167
#define OP_SHA256 168
#define OP_HASH160 169
#define OP_HASH256 170
#define OP_CODESEPARATOR 171
#define OP_CHECKSIG 172
#define OP_CHECKSIGVERIFY 173
#define OP_CHECKMULTISIG 174
#define OP_CHECKMULTISIGVERIFY 175
#define OP_NOP1 176
/* Locktime */
#define OP_CHECKLOCKTIMEVERIFY 177
#define OP_CHECKSEQUENCEVERIFY 178
/* Reserved */
#define OP_NOP4 179
#define OP_NOP5 180
#define OP_NOP6 181
#define OP_NOP7 182
#define OP_NOP8 183
#define OP_NOP9 184
#define OP_NOP10 185
#define OP_NULLDATA 252
/* Pseudo-words
* These opcodes are used internally
* for assisting with transaction matching.
* They are invalid if used in actual scripts.
*/
#define OP_PUBKEYHASH 253
#define OP_PUBKEY 254
#define OP_INVALIDOPCODE 255
#endif /* __OPCODES_H__R3NU8EN25O */
================================================
FILE: src/PSBT.cpp
================================================
#include "PSBT.h"
#include "Conversion.h"
#if USE_STD_STRING
using std::string;
#define String string
#endif
#define UBTC_ERR_PSBT_MAGIC 1
#define UBTC_ERR_PSBT_SCOPE 2
#define UBTC_ERR_PSBT_KEY 3
#define UBTC_ERR_PSBT_VALUE 4
#define UBTC_ERR_PSBT_TX 5
#define UBTC_ERR_PSBT_IN 6
#define UBTC_ERR_PSBT_OUT 7
// descriptor checksum from https://github.com/bitcoin/bitcoin/blob/master/src/script/descriptor.cpp
uint64_t PolyMod(uint64_t c, int val){
uint8_t c0 = c >> 35;
c = ((c & 0x7ffffffff) << 5) ^ val;
if (c0 & 1) c ^= 0xf5dee51989;
if (c0 & 2) c ^= 0xa9fdca3312;
if (c0 & 4) c ^= 0x1bab10e32d;
if (c0 & 8) c ^= 0x3706b1677a;
if (c0 & 16) c ^= 0x644d626ffd;
return c;
}
size_t descriptorChecksum(const char * span, size_t spanLen, char * output, size_t outputSize){
/** A character set designed such that:
* - The most common 'unprotected' descriptor characters (hex, keypaths) are in the first group of 32.
* - Case errors cause an offset that's a multiple of 32.
* - As many alphabetic characters are in the same group (while following the above restrictions).
*
* If p(x) gives the position of a character c in this character set, every group of 3 characters
* (a,b,c) is encoded as the 4 symbols (p(a) & 31, p(b) & 31, p(c) & 31, (p(a) / 32) + 3 * (p(b) / 32) + 9 * (p(c) / 32).
* This means that changes that only affect the lower 5 bits of the position, or only the higher 2 bits, will just
* affect a single symbol.
*
* As a result, within-group-of-32 errors count as 1 symbol, as do cross-group errors that don't affect
* the position within the groups.
*/
memset(output, 0, outputSize);
if(outputSize < 8){
return 0;
}
static const char * INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWX
gitextract_1v3_gt78/
├── .gitignore
├── .mbedignore
├── LICENSE
├── README.md
├── examples/
│ ├── cpp/
│ │ ├── Makefile
│ │ ├── README.md
│ │ └── main.cpp
│ ├── ecdh/
│ │ └── ecdh.ino
│ ├── hash/
│ │ └── hash.ino
│ ├── mnemonic/
│ │ └── mnemonic.ino
│ ├── multisig/
│ │ └── multisig.ino
│ ├── psbt/
│ │ └── psbt.ino
│ ├── schnorr/
│ │ └── schnorr.ino
│ ├── tx/
│ │ └── tx.ino
│ └── xpubs/
│ └── xpubs.ino
├── keywords.txt
├── library.json
├── library.properties
├── src/
│ ├── BaseClasses.cpp
│ ├── BaseClasses.h
│ ├── Bitcoin.cpp
│ ├── Bitcoin.h
│ ├── BitcoinCurve.cpp
│ ├── BitcoinCurve.h
│ ├── Conversion.cpp
│ ├── Conversion.h
│ ├── Electrum.cpp
│ ├── Electrum.h
│ ├── HDWallet.cpp
│ ├── Hash.cpp
│ ├── Hash.h
│ ├── Networks.cpp
│ ├── Networks.h
│ ├── OpCodes.h
│ ├── PSBT.cpp
│ ├── PSBT.h
│ ├── Script.cpp
│ ├── Transaction.cpp
│ ├── uBitcoin_conf.h
│ └── utility/
│ ├── segwit_addr.c
│ ├── segwit_addr.h
│ └── trezor/
│ ├── address.c
│ ├── address.h
│ ├── base58.c
│ ├── base58.h
│ ├── bignum.c
│ ├── bignum.h
│ ├── bip32.h
│ ├── bip39.c
│ ├── bip39.h
│ ├── bip39_english.h
│ ├── ecdsa.c
│ ├── ecdsa.h
│ ├── hasher.c
│ ├── hasher.h
│ ├── hmac.c
│ ├── hmac.h
│ ├── memzero.c
│ ├── memzero.h
│ ├── options.h
│ ├── pbkdf2.c
│ ├── pbkdf2.h
│ ├── rand.c
│ ├── rand.h
│ ├── rfc6979.c
│ ├── rfc6979.h
│ ├── ripemd160.c
│ ├── ripemd160.h
│ ├── secp256k1.c
│ ├── secp256k1.h
│ ├── secp256k1.table
│ ├── sha2.c
│ ├── sha2.h
│ ├── sha3.c
│ └── sha3.h
└── tests/
├── Makefile
├── minunit.h
├── sysrand.c
├── test_conversion.cpp
├── test_hash.cpp
├── test_mnemonic.cpp
└── test_schnorr.cpp
SYMBOL INDEX (427 symbols across 49 files)
FILE: examples/cpp/main.cpp
function random32 (line 20) | uint32_t random32(void){
function main (line 30) | int main() {
FILE: src/BaseClasses.cpp
function String (line 142) | String Readable::toString() const{
function String (line 155) | String Streamable::serialize(size_t offset, size_t len) const{
FILE: src/BaseClasses.h
type encoding_format (line 10) | enum encoding_format{
type parse_status (line 16) | enum parse_status{
function class (line 25) | class ParseStream{
function class (line 35) | class ParseByteStream: public ParseStream{
function class (line 52) | class SerializeStream{
function class (line 60) | class SerializeByteStream: public SerializeStream{
function class (line 79) | class Readable: public Printable{
FILE: src/Bitcoin.cpp
function mnemonicToEntropy (line 43) | size_t mnemonicToEntropy(const char * mnemonic, size_t mnemonicLen, uint...
function mnemonicToEntropy (line 63) | size_t mnemonicToEntropy(String mnemonic, uint8_t * output, size_t outpu...
function mnemonicToEntropy (line 67) | size_t mnemonicToEntropy(char * mnemonic, uint8_t * output, size_t outpu...
function checkMnemonic (line 82) | bool checkMnemonic(const char * mnemonic){
function checkMnemonic (line 92) | bool checkMnemonic(const String mnemonic){
function String (line 387) | String PublicKey::legacyAddress(const Network * network) const{
function String (line 406) | String PublicKey::segwitAddress(const Network * network) const{
function String (line 428) | String PublicKey::nestedSegwitAddress(const Network * network) const{
function Script (line 434) | Script PublicKey::script(ScriptType type) const{
function String (line 553) | String PrivateKey::wif() const{
function PublicKey (line 597) | PublicKey PrivateKey::publicKey() const{
function String (line 614) | String PrivateKey::address() const{
function String (line 617) | String PrivateKey::legacyAddress() const{
function String (line 620) | String PrivateKey::segwitAddress() const{
function String (line 623) | String PrivateKey::nestedSegwitAddress() const{
function is_canonical (line 643) | static int is_canonical(uint8_t by, uint8_t sig[64]){
function Signature (line 647) | Signature PrivateKey::sign(const uint8_t hash[32]) const{
function SchnorrSignature (line 656) | SchnorrSignature PrivateKey::schnorr_sign(const uint8_t hash[32]) const{
FILE: src/Bitcoin.h
type ScriptType (line 40) | enum ScriptType{
type SigHashType (line 55) | enum SigHashType{
function class (line 107) | class PublicKey : public ECPoint{
function explicit (line 112) | explicit PublicKey(const char * secHex){ reset(); from_str(secHex, strle...
function virtual (line 165) | virtual size_t to_str(char * buf, size_t len) const{ return wif( buf, le...
function virtual (line 166) | virtual size_t from_str(const char * buf, size_t len){ return fromWIF(bu...
function setSecret (line 183) | void setSecret(const uint8_t secret_arr[32]){ memcpy(num, secret_arr, 32...
function PublicKey (line 200) | PublicKey publicKey() const;
function String (line 314) | String fingerprint() const;
function class (line 339) | class HDPublicKey : public PublicKey{
function class (line 413) | class Signature : public Streamable{
function class (line 452) | class SchnorrSignature : public Streamable{
function class (line 475) | class Script : public Streamable{
function ScriptType (line 506) | ScriptType type() const;
function operator (line 536) | bool operator==(const Script& other) const{ return (scriptLen == other.s...
function operator (line 537) | bool operator!=(const Script& other) const{ return !operator==(other); }
function class (line 551) | class Witness : public Streamable{
function operator (line 586) | bool operator==(const Witness& other) const{ return (witnessLen == other...
function operator (line 587) | bool operator!=(const Witness& other) const{ return !operator==(other); }
function class (line 595) | class TxIn : public Streamable{
function class (line 623) | class TxOut : public Streamable{
function btcAmount (line 643) | float btcAmount(){ return (float)amount/1e8; }
function class (line 661) | class Tx : public Streamable{
FILE: src/BitcoinCurve.cpp
function ECPoint (line 121) | ECPoint ECPoint::operator+(const ECPoint& other) const{
function ECPoint (line 140) | ECPoint ECPoint::operator-() const{
function ECPoint (line 156) | ECPoint ECPoint::operator-(const ECPoint& other) const{
function ECScalar (line 198) | ECScalar ECScalar::operator+(const ECScalar& other) const{
function ECScalar (line 208) | ECScalar ECScalar::operator+(const uint32_t& i) const{
function ECScalar (line 217) | ECScalar ECScalar::operator-() const{
function ECScalar (line 225) | ECScalar ECScalar::operator-(const uint32_t& i) const{
function ECScalar (line 234) | ECScalar ECScalar::operator-(const ECScalar& other) const{
function ECScalar (line 237) | ECScalar ECScalar::operator*(const ECScalar& other) const{
function ECScalar (line 247) | ECScalar ECScalar::operator/(const ECScalar& other) const{
function ECPoint (line 271) | ECPoint operator*(const ECScalar& scalar, const ECPoint& point){
FILE: src/BitcoinCurve.h
function class (line 9) | class ECPoint : public Streamable{
function class (line 103) | class ECScalar : public Streamable{
function explicit (line 115) | explicit ECScalar(const char * arr){ init(); parse(arr, strlen(arr)); }
function virtual (line 120) | virtual void setSecret(const uint8_t secret_arr[32]){ memcpy(num, secret...
function getSecret (line 122) | void getSecret(uint8_t buffer[32]) const{ memcpy(buffer, num, 32); }
function ECScalar (line 129) | ECScalar operator+=(const ECScalar& other){ *this = *this+other; return ...
function ECScalar (line 130) | ECScalar operator-=(const ECScalar& other){ *this = *this-other; return ...
function ECScalar (line 131) | ECScalar operator+=(const uint32_t& i){ *this = *this+i; return *this; }
function ECScalar (line 132) | ECScalar operator-=(const uint32_t& i){ *this = *this-i; return *this; }
function ECScalar (line 136) | ECScalar operator*=(const ECScalar& other){ *this = *this*other; return ...
function ECScalar (line 137) | ECScalar operator/=(const ECScalar& other){ *this = *this/other; return ...
function virtual (line 139) | virtual bool isValid() const{ uint8_t arr[32] = { 0 }; return (memcmp(nu...
function ECScalar (line 151) | inline ECScalar operator*(uint32_t i, ECScalar& scalar){ return ECScalar...
FILE: src/Conversion.cpp
function toHex (line 17) | size_t toHex(const uint8_t * array, size_t arraySize, char * output, siz...
function String (line 39) | String toHex(const uint8_t * array, size_t arraySize){
function toHex (line 56) | size_t toHex(uint8_t v, Print &s){
function toHex (line 71) | size_t toHex(const uint8_t * array, size_t arraySize, Print &s){
function hexToVal (line 81) | uint8_t hexToVal(char c){
function fromHex (line 94) | size_t fromHex(const char * hex, size_t hexLen, uint8_t * array, size_t ...
function fromHex (line 119) | size_t fromHex(String encoded, uint8_t * output, size_t outputSize){
function fromHex (line 125) | size_t fromHex(const char * hex, uint8_t * array, size_t arraySize){
function fromBin (line 133) | size_t fromBin(const char * bin, size_t binLen, uint8_t * array, size_t ...
function toBin (line 163) | size_t toBin(const uint8_t * array, size_t arraySize, char * output, siz...
function String (line 182) | String toBin(const uint8_t * array, size_t arraySize){
function toBin (line 199) | size_t toBin(uint8_t v, Print &s){
function toBin (line 210) | size_t toBin(const uint8_t * array, size_t arraySize, Print &s){
function fromBin (line 221) | size_t fromBin(String encoded, uint8_t * output, size_t outputSize){
function fromBin (line 227) | size_t fromBin(const char * hex, uint8_t * array, size_t arraySize){
function toBase58Length (line 234) | size_t toBase58Length(const uint8_t * array, size_t arraySize){
function toBase58 (line 255) | size_t toBase58(const uint8_t * array, size_t arraySize, char * output, ...
function String (line 320) | String toBase58(const uint8_t * array, size_t arraySize){
function toBase58Check (line 332) | size_t toBase58Check(const uint8_t * array, size_t arraySize, char * out...
function String (line 348) | String toBase58Check(const uint8_t * array, size_t arraySize){
function fromBase58Length (line 362) | size_t fromBase58Length(const char * array, size_t arraySize){
function fromBase58 (line 368) | size_t fromBase58(const char * encoded, size_t encodedSize, uint8_t * ou...
function fromBase58Check (line 428) | size_t fromBase58Check(const char * encoded, size_t encodedSize, uint8_t...
function fromBase58 (line 453) | size_t fromBase58(String encoded, uint8_t * output, size_t outputSize){
function fromBase58Check (line 456) | size_t fromBase58Check(String encoded, uint8_t * output, size_t outputSi...
function fromBase58 (line 461) | size_t fromBase58(const char * encoded, uint8_t * array, size_t arraySize){
function fromBase58Check (line 464) | size_t fromBase58Check(const char * encoded, uint8_t * array, size_t arr...
function toBase43Length (line 471) | size_t toBase43Length(const uint8_t * array, size_t arraySize){
function toBase43 (line 483) | size_t toBase43(const uint8_t * array, size_t arraySize, char * output, ...
function String (line 547) | String toBase43(const uint8_t * array, size_t arraySize){
function fromBase43Length (line 559) | size_t fromBase43Length(const char * array, size_t arraySize){
function fromBase43 (line 565) | size_t fromBase43(const char * encoded, size_t encodedSize, uint8_t * ou...
function fromBase43 (line 625) | size_t fromBase43(String encoded, uint8_t * output, size_t outputSize){
function fromBase43 (line 630) | size_t fromBase43(const char * encoded, uint8_t * array, size_t arraySize){
function toBase64Length (line 637) | size_t toBase64Length(const uint8_t * array, size_t arraySize, uint8_t f...
function toBase64 (line 649) | size_t toBase64(const uint8_t * array, size_t arraySize, char * output, ...
function String (line 698) | String toBase64(const uint8_t * array, size_t arraySize, uint8_t flags){
function fromBase64Length (line 709) | size_t fromBase64Length(const char * array, size_t arraySize, uint8_t fl...
function fromBase64 (line 727) | size_t fromBase64(const char * encoded, size_t encodedSize, uint8_t * ou...
function fromBase64 (line 787) | size_t fromBase64(String encoded, uint8_t * output, size_t outputSize, u...
function String (line 790) | String base64ToHex(String b64, uint8_t flags){
function String (line 799) | String hexToBase64(String hex, uint8_t flags){
function fromBase64 (line 810) | size_t fromBase64(const char * b64, uint8_t * array, size_t arraySize, u...
function littleEndianToInt (line 818) | uint64_t littleEndianToInt(const uint8_t * array, size_t arraySize){
function intToLittleEndian (line 827) | void intToLittleEndian(uint64_t num, uint8_t * array, size_t arraySize){
function bigEndianToInt (line 833) | uint64_t bigEndianToInt(const uint8_t * array, size_t arraySize){
function intToBigEndian (line 842) | void intToBigEndian(uint64_t num, uint8_t * array, size_t arraySize){
function lenVarInt (line 850) | uint8_t lenVarInt(uint64_t num){
function readVarInt (line 862) | uint64_t readVarInt(const uint8_t * array, size_t arraySize){
function writeVarInt (line 875) | size_t writeVarInt(uint64_t num, uint8_t * array, size_t arraySize){
FILE: src/Electrum.cpp
function ElectrumTx (line 13) | ElectrumTx& ElectrumTx::operator=(ElectrumTx const &other){
FILE: src/Electrum.h
type ElectrumInputMetadata (line 10) | typedef struct{
function class (line 21) | class ElectrumTx : public Streamable{
FILE: src/HDWallet.cpp
function String (line 314) | String HDPrivateKey::xprv() const{
function String (line 319) | String HDPrivateKey::address() const{
function String (line 371) | String HDPrivateKey::fingerprint() const{
function HDPublicKey (line 378) | HDPublicKey HDPrivateKey::xpub() const{
function HDPrivateKey (line 382) | HDPrivateKey HDPrivateKey::child(uint32_t index, bool hardened) const{
function HDPrivateKey (line 466) | HDPrivateKey HDPrivateKey::hardenedChild(uint32_t index) const{
function HDPrivateKey (line 470) | HDPrivateKey HDPrivateKey::derive(uint32_t * index, size_t len) const{
function HDPrivateKey (line 477) | HDPrivateKey HDPrivateKey::derive(const char * path) const{
function String (line 742) | String HDPublicKey::xpub() const{
function String (line 747) | String HDPublicKey::address() const{
function String (line 769) | String HDPublicKey::fingerprint() const{
function HDPublicKey (line 776) | HDPublicKey HDPublicKey::child(uint32_t index) const{
function HDPublicKey (line 809) | HDPublicKey HDPublicKey::derive(uint32_t * index, size_t len) const{
function HDPublicKey (line 816) | HDPublicKey HDPublicKey::derive(const char * path) const{
FILE: src/Hash.cpp
function hashData (line 11) | static size_t hashData(HashAlgorithm * algo, const uint8_t * data, size_...
function hashString (line 18) | static size_t hashString(HashAlgorithm * algo, const String s, uint8_t *...
function rmd160 (line 25) | int rmd160(const uint8_t * data, size_t len, uint8_t hash[20]){
function rmd160 (line 29) | int rmd160(const char * data, size_t len, uint8_t hash[20]){
function rmd160 (line 33) | int rmd160(const String data, uint8_t hash[20]){
function sha256 (line 58) | int sha256(const uint8_t * data, size_t len, uint8_t hash[32]){
function sha256 (line 62) | int sha256(const char * data, size_t len, uint8_t hash[32]){
function sha256 (line 66) | int sha256(const String data, uint8_t hash[32]){
function tagged_hash (line 80) | int tagged_hash(const char * tag, const uint8_t * data, size_t dataLen, ...
function tagged_hash (line 84) | int tagged_hash(const char * tag, const char * data, size_t len, uint8_t...
function tagged_hash (line 88) | int tagged_hash(const String tag, const String data, uint8_t hash[32]){
function sha256Hmac (line 94) | int sha256Hmac(const uint8_t * key, size_t keyLen, const uint8_t * data,...
function hash160 (line 126) | int hash160(const uint8_t * data, size_t len, uint8_t hash[20]){
function hash160 (line 130) | int hash160(const char * data, size_t len, uint8_t hash[20]){
function hash160 (line 134) | int hash160(const String data, uint8_t hash[20]){
function doubleSha (line 150) | int doubleSha(const uint8_t * data, size_t len, uint8_t hash[32]){
function doubleSha (line 154) | int doubleSha(const char * data, size_t len, uint8_t hash[32]){
function doubleSha (line 158) | int doubleSha(const String data, uint8_t hash[32]){
function sha512 (line 173) | int sha512(const uint8_t * data, size_t len, uint8_t hash[64]){
function sha512 (line 177) | int sha512(const char * data, size_t len, uint8_t hash[64]){
function sha512 (line 181) | int sha512(const String data, uint8_t hash[64]){
function sha512Hmac (line 211) | int sha512Hmac(const uint8_t * key, size_t keyLen, const uint8_t * data,...
FILE: src/Hash.h
function class (line 16) | class HashAlgorithm : public SerializeStream{
function class (line 37) | class RMD160 : public HashAlgorithm{
function class (line 62) | class SHA256 : public HashAlgorithm{
function class (line 78) | class TaggedHash : public SHA256{
function class (line 108) | class Hash160 : public SHA256{
function class (line 126) | class DoubleSha : public SHA256{
function class (line 144) | class SHA512 : public HashAlgorithm{
FILE: src/Networks.h
type Network (line 11) | typedef struct {
FILE: src/PSBT.cpp
function PolyMod (line 17) | uint64_t PolyMod(uint64_t c, int val){
function descriptorChecksum (line 28) | size_t descriptorChecksum(const char * span, size_t spanLen, char * outp...
function String (line 81) | String descriptorChecksum(String descriptor){
function String (line 689) | String PSBT::toBase64(){
function PSBT (line 740) | PSBT& PSBT::operator=(PSBT const &other){
FILE: src/PSBT.h
type PSBTDerivation (line 11) | typedef struct{
type PSBTPartialSignature (line 22) | typedef struct{
type PSBTInputMetadata (line 30) | typedef struct{
type PSBTOutputMetadata (line 48) | typedef struct{
function class (line 69) | class PSBT : public Streamable{
function fee (line 100) | uint64_t fee() const;
FILE: src/Script.cpp
function Script (line 16) | Script pkh(PublicKey pub){
function Script (line 25) | Script wpkh(PublicKey pub){
function Script (line 34) | Script multi(uint8_t threshold, const PublicKey * pubkeys, uint8_t pubke...
function swap (line 53) | static void swap(PublicKey *a, PublicKey *b) {
function partition (line 60) | static int partition(PublicKey array[], int low, int high) {
function quickSort (line 89) | static void quickSort(PublicKey array[], int low, int high) {
function Script (line 105) | Script sortedmulti(uint8_t threshold, const PublicKey * pubkeys, uint8_t...
function Script (line 112) | Script wsh(Script witness_script){
function Script (line 119) | Script sh(Script script){
function ScriptType (line 332) | ScriptType Script::type() const{
function String (line 408) | String Script::address(const Network * network) const{
function Script (line 479) | Script Script::scriptPubkey(ScriptType type) const{
function Script (line 483) | Script &Script::operator=(const Script &other){
function Witness (line 704) | Witness &Witness::operator=(Witness const &other){
FILE: src/Transaction.cpp
function Tx (line 230) | Tx& Tx::operator=(Tx const &other){ // copy-paste =(
function String (line 589) | String Tx::txid() const{
function String (line 594) | String Tx::wtxid() const{
function Signature (line 698) | Signature Tx::signInput(uint8_t inputIndex, const PrivateKey pk, const S...
function Signature (line 717) | Signature Tx::signSegwitInput(uint8_t inputIndex, const PrivateKey pk, c...
FILE: src/utility/segwit_addr.c
function bech32_polymod_step (line 27) | uint32_t bech32_polymod_step(uint32_t pre) {
function bech32_encode (line 50) | int bech32_encode(char *output, const char *hrp, const uint8_t *data, si...
function bech32_decode (line 86) | int bech32_decode(char* hrp, uint8_t *data, size_t *data_len, const char...
function convert_bits (line 143) | int convert_bits(uint8_t* out, size_t* outlen, int outbits, const uint8_...
function segwit_addr_encode (line 165) | int segwit_addr_encode(char *output, const char *hrp, int witver, const ...
function segwit_addr_decode (line 177) | int segwit_addr_decode(int* witver, uint8_t* witdata, size_t* witdata_le...
FILE: src/utility/trezor/address.c
function address_prefix_bytes_len (line 27) | size_t address_prefix_bytes_len(uint32_t address_type)
function address_write_prefix_bytes (line 35) | void address_write_prefix_bytes(uint32_t address_type, uint8_t *out)
function address_check_prefix (line 43) | bool address_check_prefix(const uint8_t *addr, uint32_t address_type)
function ethereum_address_checksum (line 60) | void ethereum_address_checksum(const uint8_t *addr, char *address, bool ...
FILE: src/utility/trezor/base58.c
function b58tobin (line 47) | bool b58tobin(void *bin, size_t *binszp, const char *b58)
function b58check (line 140) | int b58check(const void *bin, size_t binsz, HasherType hasher_type, cons...
function b58enc (line 160) | bool b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz)
function base58_encode_check (line 202) | int base58_encode_check(const uint8_t *data, int datalen, HasherType has...
function base58_decode_check (line 217) | int base58_decode_check(const char *str, HasherType hasher_type, uint8_t...
function b58gphcheck (line 236) | int b58gphcheck(const void *bin, size_t binsz, const char *base58str)
function base58gph_encode_check (line 256) | int base58gph_encode_check(const uint8_t *data, int datalen, char *str, ...
function base58gph_decode_check (line 271) | int base58gph_decode_check(const char *str, uint8_t *data, int datalen)
FILE: src/utility/trezor/bignum.c
function read_be (line 57) | inline uint32_t read_be(const uint8_t *data)
function write_be (line 65) | inline void write_be(uint8_t *data, uint32_t x)
function read_le (line 73) | inline uint32_t read_le(const uint8_t *data)
function write_le (line 81) | inline void write_le(uint8_t *data, uint32_t x)
function bn_read_be (line 91) | void bn_read_be(const uint8_t *in_number, bignum256 *out_number)
function bn_write_be (line 111) | void bn_write_be(const bignum256 *in_number, uint8_t *out_number)
function bn_read_le (line 126) | void bn_read_le(const uint8_t *in_number, bignum256 *out_number)
function bn_write_le (line 146) | void bn_write_le(const bignum256 *in_number, uint8_t *out_number)
function bn_read_uint32 (line 159) | void bn_read_uint32(uint32_t in_number, bignum256 *out_number)
function bn_read_uint64 (line 172) | void bn_read_uint64(uint64_t in_number, bignum256 *out_number)
function bn_bitcount (line 186) | int bn_bitcount(const bignum256 *a)
function bn_digitcount (line 200) | unsigned int bn_digitcount(const bignum256 *a)
function bn_zero (line 224) | void bn_zero(bignum256 *a)
function bn_one (line 233) | void bn_one(bignum256 *a)
function bn_is_zero (line 249) | int bn_is_zero(const bignum256 *a)
function bn_is_less (line 262) | int bn_is_less(const bignum256 *a, const bignum256 *b)
function bn_is_equal (line 277) | int bn_is_equal(const bignum256 *a, const bignum256 *b) {
function bn_cmov (line 289) | void bn_cmov(bignum256 *res, int cond, const bignum256 *truecase, const ...
function bn_lshift (line 304) | void bn_lshift(bignum256 *a)
function bn_rshift (line 315) | void bn_rshift(bignum256 *a)
function bn_setbit (line 325) | void bn_setbit(bignum256 *a, uint8_t bit)
function bn_clearbit (line 331) | void bn_clearbit(bignum256 *a, uint8_t bit)
function bn_testbit (line 337) | uint32_t bn_testbit(bignum256 *a, uint8_t bit)
function bn_xor (line 343) | void bn_xor(bignum256 *a, const bignum256 *b, const bignum256 *c)
function bn_mult_half (line 356) | void bn_mult_half(bignum256 * x, const bignum256 *prime)
function bn_mult_k (line 376) | void bn_mult_k(bignum256 *x, uint8_t k, const bignum256 *prime)
function bn_mod (line 387) | void bn_mod(bignum256 *x, const bignum256 *prime)
function bn_multiply_long (line 398) | void bn_multiply_long(const bignum256 *k, const bignum256 *x, uint32_t r...
function bn_multiply_reduce_step (line 430) | void bn_multiply_reduce_step(uint32_t res[18], const bignum256 *prime, u...
function bn_multiply_reduce (line 469) | void bn_multiply_reduce(bignum256 *x, uint32_t res[18], const bignum256 ...
function bn_multiply (line 488) | void bn_multiply(const bignum256 *k, bignum256 *x, const bignum256 *prime)
function bn_fast_mod (line 501) | void bn_fast_mod(bignum256 *x, const bignum256 *prime)
function bn_sqrt (line 523) | void bn_sqrt(bignum256 *x, const bignum256 *prime)
function bn_inverse (line 562) | void bn_inverse(bignum256 *x, const bignum256 *prime)
function bn_inverse (line 599) | void bn_inverse(bignum256 *x, const bignum256 *prime)
function bn_normalize (line 870) | void bn_normalize(bignum256 *a) {
function bn_add (line 877) | void bn_add(bignum256 *a, const bignum256 *b)
function bn_addmod (line 888) | void bn_addmod(bignum256 *a, const bignum256 *b, const bignum256 *prime)
function bn_addi (line 897) | void bn_addi(bignum256 *a, uint32_t b) {
function bn_subi (line 907) | void bn_subi(bignum256 *a, uint32_t b, const bignum256 *prime) {
function bn_subtractmod (line 917) | void bn_subtractmod(const bignum256 *a, const bignum256 *b, bignum256 *r...
function bn_subtract (line 929) | void bn_subtract(const bignum256 *a, const bignum256 *b, bignum256 *res)
function bn_divmod58 (line 941) | void bn_divmod58(bignum256 *a, uint32_t *r)
function bn_divmod1000 (line 965) | void bn_divmod1000(bignum256 *a, uint32_t *r)
function bn_format (line 988) | size_t bn_format(const bignum256 *amnt, const char *prefix, const char *...
function bn_print (line 1082) | void bn_print(const bignum256 *a)
function bn_print_raw (line 1095) | void bn_print_raw(const bignum256 *a)
FILE: src/utility/trezor/bignum.h
type bignum256 (line 35) | typedef struct {
function bn_write_uint32 (line 68) | static inline uint32_t bn_write_uint32(const bignum256 *in_number)
function bn_write_uint64 (line 73) | static inline uint64_t bn_write_uint64(const bignum256 *in_number)
function bn_copy (line 85) | static inline void bn_copy(const bignum256 *a, bignum256 *b) {
function bn_is_even (line 99) | static inline int bn_is_even(const bignum256 *a) {
function bn_is_odd (line 103) | static inline int bn_is_odd(const bignum256 *a) {
function bn_format_uint64 (line 159) | static inline size_t bn_format_uint64(uint64_t amount, const char *prefi...
FILE: src/utility/trezor/bip32.h
type curve_info (line 34) | typedef struct {
type HDNode (line 44) | typedef struct {
FILE: src/utility/trezor/bip39.c
function CONFIDENTIAL (line 40) | static CONFIDENTIAL struct {
function mnemonic_clear (line 97) | void mnemonic_clear(void)
function mnemonic_to_entropy (line 102) | int mnemonic_to_entropy(const char *mnemonic, uint8_t *entropy)
function mnemonic_check (line 166) | int mnemonic_check(const char *mnemonic)
function mnemonic_to_seed (line 188) | void mnemonic_to_seed(const char *mnemonic, const char *passphrase, uint...
FILE: src/utility/trezor/ecdsa.c
function point_copy (line 41) | void point_copy(const curve_point *cp1, curve_point *cp2)
function point_add (line 47) | void point_add(const ecdsa_curve *curve, const curve_point *cp1, curve_p...
function point_double (line 93) | void point_double(const ecdsa_curve *curve, curve_point *cp)
function point_set_infinity (line 137) | void point_set_infinity(curve_point *p)
function point_is_infinity (line 145) | int point_is_infinity(const curve_point *p)
function point_is_equal (line 151) | int point_is_equal(const curve_point *p, const curve_point *q)
function point_is_negative_of (line 158) | int point_is_negative_of(const curve_point *p, const curve_point *q)
function conditional_negate (line 175) | void conditional_negate(uint32_t cond, bignum256 *a, const bignum256 *pr...
type jacobian_curve_point (line 190) | typedef struct jacobian_curve_point {
function generate_k_random (line 195) | static void generate_k_random(bignum256 *k, const bignum256 *prime) {
function curve_to_jacobian (line 206) | void curve_to_jacobian(const curve_point *p, jacobian_curve_point *jp, c...
function jacobian_to_curve (line 221) | void jacobian_to_curve(const jacobian_curve_point *jp, curve_point *p, c...
function point_jacobian_add (line 238) | void point_jacobian_add(const curve_point *p1, jacobian_curve_point *p2,...
function point_jacobian_double (line 357) | void point_jacobian_double(jacobian_curve_point *p, const ecdsa_curve *c...
function point_multiply (line 426) | void point_multiply(const ecdsa_curve *curve, const bignum256 *k, const ...
function scalar_multiply (line 551) | void scalar_multiply(const ecdsa_curve *curve, const bignum256 *k, curve...
function scalar_multiply (line 637) | void scalar_multiply(const ecdsa_curve *curve, const bignum256 *k, curve...
function ecdh_multiply (line 644) | int ecdh_multiply(const ecdsa_curve *curve, const uint8_t *priv_key, con...
function ecdsa_sign (line 716) | int ecdsa_sign(const ecdsa_curve *curve, HasherType hasher_sign, const u...
function ecdsa_sign_digest (line 732) | int ecdsa_sign_digest(const ecdsa_curve *curve, const uint8_t *priv_key,...
function ecdsa_get_public_key33 (line 825) | void ecdsa_get_public_key33(const ecdsa_curve *curve, const uint8_t *pri...
function ecdsa_get_public_key65 (line 839) | void ecdsa_get_public_key65(const ecdsa_curve *curve, const uint8_t *pri...
function ecdsa_uncompress_pubkey (line 854) | int ecdsa_uncompress_pubkey(const ecdsa_curve *curve, const uint8_t *pub...
function ecdsa_get_pubkeyhash (line 869) | void ecdsa_get_pubkeyhash(const uint8_t *pub_key, HasherType hasher_pubk...
function ecdsa_get_address_raw (line 883) | void ecdsa_get_address_raw(const uint8_t *pub_key, uint32_t version, Has...
function ecdsa_get_address (line 890) | void ecdsa_get_address(const uint8_t *pub_key, uint32_t version, HasherT...
function ecdsa_get_address_segwit_p2sh_raw (line 900) | void ecdsa_get_address_segwit_p2sh_raw(const uint8_t *pub_key, uint32_t ...
function ecdsa_get_address_segwit_p2sh (line 911) | void ecdsa_get_address_segwit_p2sh(const uint8_t *pub_key, uint32_t vers...
function ecdsa_get_wif (line 920) | void ecdsa_get_wif(const uint8_t *priv_key, uint32_t version, HasherType...
function ecdsa_address_decode (line 932) | int ecdsa_address_decode(const char *addr, uint32_t version, HasherType ...
function uncompress_coords (line 940) | void uncompress_coords(const ecdsa_curve *curve, uint8_t odd, const bign...
function ecdsa_read_pubkey (line 954) | int ecdsa_read_pubkey(const ecdsa_curve *curve, const uint8_t *pub_key, ...
function ecdsa_validate_pubkey (line 978) | int ecdsa_validate_pubkey(const ecdsa_curve *curve, const curve_point *pub)
function ecdsa_verify (line 1017) | int ecdsa_verify(const ecdsa_curve *curve, HasherType hasher_sign, const...
function ecdsa_recover_pub_from_sig (line 1028) | int ecdsa_recover_pub_from_sig (const ecdsa_curve *curve, uint8_t *pub_k...
function ecdsa_verify_digest (line 1077) | int ecdsa_verify_digest(const ecdsa_curve *curve, const uint8_t *pub_key...
function ecdsa_sig_to_der (line 1131) | int ecdsa_sig_to_der(const uint8_t *sig, uint8_t *der)
FILE: src/utility/trezor/ecdsa.h
type curve_point (line 33) | typedef struct {
type ecdsa_curve (line 37) | typedef struct {
FILE: src/utility/trezor/hasher.c
function hasher_Init (line 26) | void hasher_Init(Hasher *hasher, HasherType type) {
function hasher_Reset (line 44) | void hasher_Reset(Hasher *hasher) {
function hasher_Update (line 48) | void hasher_Update(Hasher *hasher, const uint8_t *data, size_t length) {
function hasher_Final (line 64) | void hasher_Final(Hasher *hasher, uint8_t hash[HASHER_DIGEST_LENGTH]) {
function hasher_Raw (line 88) | void hasher_Raw(HasherType type, const uint8_t *data, size_t length, uin...
FILE: src/utility/trezor/hasher.h
type HasherType (line 34) | typedef enum {
type Hasher (line 45) | typedef struct {
FILE: src/utility/trezor/hmac.c
function ubtc_hmac_sha256_Init (line 33) | void ubtc_hmac_sha256_Init(HMAC_SHA256_CTX *hctx, const uint8_t *key, co...
function ubtc_hmac_sha256_Update (line 51) | void ubtc_hmac_sha256_Update(HMAC_SHA256_CTX *hctx, const uint8_t *msg, ...
function ubtc_hmac_sha256_Final (line 56) | void ubtc_hmac_sha256_Final(HMAC_SHA256_CTX *hctx, uint8_t *hmac)
function ubtc_hmac_sha256 (line 66) | void ubtc_hmac_sha256(const uint8_t *key, const uint32_t keylen, const u...
function ubtc_hmac_sha256_prepare (line 74) | void ubtc_hmac_sha256_prepare(const uint8_t *key, const uint32_t keylen,...
function ubtc_hmac_sha512_Init (line 108) | void ubtc_hmac_sha512_Init(HMAC_SHA512_CTX *hctx, const uint8_t *key, co...
function ubtc_hmac_sha512_Update (line 126) | void ubtc_hmac_sha512_Update(HMAC_SHA512_CTX *hctx, const uint8_t *msg, ...
function ubtc_hmac_sha512_Final (line 131) | void ubtc_hmac_sha512_Final(HMAC_SHA512_CTX *hctx, uint8_t *hmac)
function ubtc_hmac_sha512 (line 141) | void ubtc_hmac_sha512(const uint8_t *key, const uint32_t keylen, const u...
function ubtc_hmac_sha512_prepare (line 149) | void ubtc_hmac_sha512_prepare(const uint8_t *key, const uint32_t keylen,...
FILE: src/utility/trezor/hmac.h
type HMAC_SHA256_CTX (line 30) | typedef struct _HMAC_SHA256_CTX {
type HMAC_SHA512_CTX (line 35) | typedef struct _HMAC_SHA512_CTX {
FILE: src/utility/trezor/memzero.c
function memzero (line 3) | void memzero(void *s, size_t n)
FILE: src/utility/trezor/pbkdf2.c
function pbkdf2_hmac_sha256_Init (line 30) | void pbkdf2_hmac_sha256_Init(PBKDF2_HMAC_SHA256_CTX *pctx, const uint8_t...
function pbkdf2_hmac_sha256_Update (line 57) | void pbkdf2_hmac_sha256_Update(PBKDF2_HMAC_SHA256_CTX *pctx, uint32_t it...
function pbkdf2_hmac_sha256_Final (line 69) | void pbkdf2_hmac_sha256_Final(PBKDF2_HMAC_SHA256_CTX *pctx, uint8_t *key)
function pbkdf2_hmac_sha256 (line 80) | void pbkdf2_hmac_sha256(const uint8_t *pass, int passlen, const uint8_t ...
function pbkdf2_hmac_sha512_Init (line 104) | void pbkdf2_hmac_sha512_Init(PBKDF2_HMAC_SHA512_CTX *pctx, const uint8_t...
function pbkdf2_hmac_sha512_Update (line 132) | void pbkdf2_hmac_sha512_Update(PBKDF2_HMAC_SHA512_CTX *pctx, uint32_t it...
function pbkdf2_hmac_sha512_Final (line 144) | void pbkdf2_hmac_sha512_Final(PBKDF2_HMAC_SHA512_CTX *pctx, uint8_t *key)
function pbkdf2_hmac_sha512 (line 155) | void pbkdf2_hmac_sha512(const uint8_t *pass, int passlen, const uint8_t ...
FILE: src/utility/trezor/pbkdf2.h
type PBKDF2_HMAC_SHA256_CTX (line 30) | typedef struct _PBKDF2_HMAC_SHA256_CTX {
type PBKDF2_HMAC_SHA512_CTX (line 38) | typedef struct _PBKDF2_HMAC_SHA512_CTX {
FILE: src/utility/trezor/rand.c
function random32 (line 34) | uint32_t __attribute__((weak)) random32(void){
function random32 (line 41) | uint32_t __attribute__((weak)) random32(void){
function random32 (line 59) | uint32_t __attribute__((weak)) random32(void) {
function random32 (line 95) | uint32_t __attribute__((weak)) random32(void){
function random32 (line 120) | uint32_t __attribute__((weak)) random32(void) {
function random_buffer (line 139) | void __attribute__((weak)) random_buffer(uint8_t *buf, size_t len)
function random_uniform (line 150) | uint32_t random_uniform(uint32_t n)
function random_permute (line 157) | void random_permute(char *str, size_t len)
FILE: src/utility/trezor/rfc6979.c
function init_rfc6979 (line 30) | void init_rfc6979(const uint8_t *priv_key, const uint8_t *hash, rfc6979_...
function generate_rfc6979 (line 57) | void generate_rfc6979(uint8_t rnd[32], rfc6979_state *state)
function generate_k_rfc6979 (line 72) | void generate_k_rfc6979(bignum256 *k, rfc6979_state *state)
FILE: src/utility/trezor/rfc6979.h
type rfc6979_state (line 32) | typedef struct {
FILE: src/utility/trezor/ripemd160.c
function ripemd160_Init (line 59) | void ripemd160_Init(RIPEMD160_CTX *ctx)
function ripemd160_process (line 75) | void ripemd160_process( RIPEMD160_CTX *ctx, const uint8_t data[RIPEMD160...
function ripemd160_Update (line 256) | void ripemd160_Update( RIPEMD160_CTX *ctx, const uint8_t *input, uint32_...
function ripemd160_Final (line 306) | void ripemd160_Final( RIPEMD160_CTX *ctx, uint8_t output[RIPEMD160_DIGES...
function ripemd160 (line 337) | void ripemd160(const uint8_t *msg, uint32_t msg_len, uint8_t hash[RIPEMD...
FILE: src/utility/trezor/ripemd160.h
type RIPEMD160_CTX (line 9) | typedef struct _RIPEMD160_CTX {
FILE: src/utility/trezor/sha2.c
type sha2_byte (line 90) | typedef uint8_t sha2_byte;
type sha2_word32 (line 91) | typedef uint32_t sha2_word32;
type sha2_word64 (line 92) | typedef uint64_t sha2_word64;
function sha256_Init (line 273) | void sha256_Init(SHA256_CTX* context) {
function sha256_Transform (line 304) | void sha256_Transform(const sha2_word32* state_in, const sha2_word32* da...
function sha256_Transform (line 361) | void sha256_Transform(const sha2_word32* state_in, const sha2_word32* da...
function sha256_Update (line 432) | void sha256_Update(SHA256_CTX* context, const sha2_byte *data, size_t le...
function sha256_Final (line 490) | void sha256_Final(SHA256_CTX* context, uint8_t digest[SHA256_DIGEST_LENG...
function sha256_Raw (line 564) | void sha256_Raw(const sha2_byte* data, size_t len, uint8_t digest[SHA256...
function sha512_Init (line 581) | void sha512_Init(SHA512_CTX* context) {
function sha512_Transform (line 611) | void sha512_Transform(const sha2_word64* state_in, const sha2_word64* da...
function sha512_Transform (line 666) | void sha512_Transform(const sha2_word64* state_in, const sha2_word64* da...
function sha512_Update (line 737) | void sha512_Update(SHA512_CTX* context, const sha2_byte *data, size_t le...
function sha512_Last (line 795) | static void sha512_Last(SHA512_CTX* context) {
function sha512_Final (line 834) | void sha512_Final(SHA512_CTX* context, uint8_t digest[SHA512_DIGEST_LENG...
function sha512_Raw (line 873) | void sha512_Raw(const sha2_byte* data, size_t len, uint8_t digest[SHA512...
FILE: src/utility/trezor/sha2.h
type SHA256_CTX (line 44) | typedef struct _SHA256_CTX {
type SHA512_CTX (line 49) | typedef struct _SHA512_CTX {
FILE: src/utility/trezor/sha3.c
function keccak_Init (line 46) | static void keccak_Init(SHA3_CTX *ctx, unsigned bits)
function sha3_224_Init (line 61) | void sha3_224_Init(SHA3_CTX *ctx)
function sha3_256_Init (line 71) | void sha3_256_Init(SHA3_CTX *ctx)
function sha3_384_Init (line 81) | void sha3_384_Init(SHA3_CTX *ctx)
function sha3_512_Init (line 91) | void sha3_512_Init(SHA3_CTX *ctx)
function keccak_theta (line 97) | static void keccak_theta(uint64_t *A)
function keccak_pi (line 121) | static void keccak_pi(uint64_t *A)
function keccak_chi (line 153) | static void keccak_chi(uint64_t *A)
function sha3_permutation (line 166) | static void sha3_permutation(uint64_t *state)
function sha3_process_block (line 214) | static void sha3_process_block(uint64_t hash[25], const uint64_t *block,...
function sha3_Update (line 270) | void sha3_Update(SHA3_CTX *ctx, const unsigned char *msg, size_t size)
function sha3_Final (line 315) | void sha3_Final(SHA3_CTX *ctx, unsigned char* result)
function keccak_Final (line 344) | void keccak_Final(SHA3_CTX *ctx, unsigned char* result)
function keccak_256 (line 366) | void keccak_256(const unsigned char* data, size_t len, unsigned char* di...
function keccak_512 (line 374) | void keccak_512(const unsigned char* data, size_t len, unsigned char* di...
function sha3_256 (line 383) | void sha3_256(const unsigned char* data, size_t len, unsigned char* digest)
function sha3_512 (line 391) | void sha3_512(const unsigned char* data, size_t len, unsigned char* digest)
FILE: src/utility/trezor/sha3.h
type SHA3_CTX (line 50) | typedef struct SHA3_CTX
FILE: tests/minunit.h
function mu_timer_real (line 236) | static double mu_timer_real(void)
function mu_timer_cpu (line 311) | static double mu_timer_cpu(void)
FILE: tests/sysrand.c
function random32 (line 7) | uint32_t random32(void){
FILE: tests/test_conversion.cpp
function MU_TEST (line 18) | MU_TEST(test_base58) {
function MU_TEST (line 38) | MU_TEST(test_base64) {
function MU_TEST (line 80) | MU_TEST(test_nullptr) {
function MU_TEST_SUITE (line 100) | MU_TEST_SUITE(test_conversion) {
function main (line 106) | int main(int argc, char *argv[]) {
FILE: tests/test_hash.cpp
function MU_TEST (line 11) | MU_TEST(test_sha256) {
function MU_TEST (line 35) | MU_TEST(test_ripemd160) {
function MU_TEST (line 42) | MU_TEST(test_hash160) {
function MU_TEST (line 49) | MU_TEST(test_doublesha256) {
function MU_TEST (line 56) | MU_TEST(test_taggedhash) {
function MU_TEST (line 63) | MU_TEST(test_sha512) {
function MU_TEST_SUITE (line 70) | MU_TEST_SUITE(test_hash) {
function main (line 78) | int main(int argc, char *argv[]) {
FILE: tests/test_mnemonic.cpp
function MU_TEST (line 11) | MU_TEST(test_password) {
function MU_TEST_SUITE (line 17) | MU_TEST_SUITE(test_mnemonic) {
function main (line 21) | int main(int argc, char *argv[]) {
FILE: tests/test_schnorr.cpp
function MU_TEST (line 12) | MU_TEST(test_schnorrsign) {
function MU_TEST_SUITE (line 32) | MU_TEST_SUITE(test_schnorr) {
function main (line 36) | int main(int argc, char *argv[]) {
Condensed preview — 82 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (694K chars).
[
{
"path": ".gitignore",
"chars": 13,
"preview": ".vscode\nbuild"
},
{
"path": ".mbedignore",
"chars": 19,
"preview": "examples/*\ntests/*\n"
},
{
"path": "LICENSE",
"chars": 1072,
"preview": "MIT License\n\nCopyright (c) 2019 Stepan Snigirev\n\nPermission is hereby granted, free of charge, to any person obtaining a"
},
{
"path": "README.md",
"chars": 4859,
"preview": "# Micro-Bitcoin\n\nC++ Bitcoin library for 32-bit microcontrollers. The library supports [Arduino IDE](https://www.arduino"
},
{
"path": "examples/cpp/Makefile",
"chars": 1706,
"preview": "TARGET ?= main\n\nifeq ($(OS),Windows_NT)\nEXT ?= .exe\nelse\nEXT ?= \nendif\n\nTARGET_EXEC ?= $(TARGET)$(EXT)\n\n# Paths\n# to mak"
},
{
"path": "examples/cpp/README.md",
"chars": 35,
"preview": "# C++ example\n\nrun with `make run`\n"
},
{
"path": "examples/cpp/main.cpp",
"chars": 3626,
"preview": "/*\n * A simple example showing how to work with uBitcoin in c++ on a PC\n */\n\n// only compile when UBTC_EXAMPLE flag is p"
},
{
"path": "examples/ecdh/ecdh.ino",
"chars": 1900,
"preview": "/*\n * This example shows how to use ECDH algorithm\n * to get shared secret between two parties.\n * It is useful if you w"
},
{
"path": "examples/hash/hash.ino",
"chars": 2272,
"preview": "#include <Hash.h> // all single-line hashing algorithms\n#include <Conversion.h> // to print byte arrays in hex format\n\n"
},
{
"path": "examples/mnemonic/mnemonic.ino",
"chars": 1481,
"preview": "/*\n * This example shows how to derive master private key from the recovery seed\n * Generate a random recovery seed i.e."
},
{
"path": "examples/multisig/multisig.ino",
"chars": 1643,
"preview": "/*\n * A simple example showing how to derive multisig addresses\n */\n\n#include \"Bitcoin.h\"\n\n// 2 of 3 multisig\n#define NU"
},
{
"path": "examples/psbt/psbt.ino",
"chars": 2333,
"preview": "/*\n * This example shows how parse and sign PSBT transaction\n */\n\n#include \"Bitcoin.h\"\n#include \"PSBT.h\"\n\nHDPrivateKey h"
},
{
"path": "examples/schnorr/schnorr.ino",
"chars": 1295,
"preview": "/*\n * A simple example showing how to work with uBitcoin in c++ on a PC\n */\n#include \"Bitcoin.h\"\n#include \"Hash.h\"\n\nchar"
},
{
"path": "examples/tx/tx.ino",
"chars": 2544,
"preview": "#include <Arduino.h>\n#include \"Bitcoin.h\"\n\nvoid setup() {\n Serial.begin(9600);\n while(!Serial){\n ;\n }\n\n // Single"
},
{
"path": "examples/xpubs/xpubs.ino",
"chars": 1747,
"preview": "/*\n * This example shows how to derive bitcoin addresses from the master public key (bip32)\n * Enter account master publ"
},
{
"path": "keywords.txt",
"chars": 4426,
"preview": "#######################################\n# Syntax Coloring Map\n#######################################\n\n#################"
},
{
"path": "library.json",
"chars": 761,
"preview": "{\n \"name\": \"uBitcoin\",\n \"version\": \"0.2.0\",\n \"description\": \"Brings Bitcoin to embedded devices. Write your own hardw"
},
{
"path": "library.properties",
"chars": 475,
"preview": "name=uBitcoin\nversion=0.2.0\nauthor=Stepan Snigirev\nmaintainer=Stepan Snigirev <snigirev.stepan@gmail.com>\nsentence=Bring"
},
{
"path": "src/BaseClasses.cpp",
"chars": 4267,
"preview": "#include \"uBitcoin_conf.h\"\n#include \"BaseClasses.h\"\n#include <cstdlib>\n\n#if USE_STD_STRING\nusing std::string;\n#define St"
},
{
"path": "src/BaseClasses.h",
"chars": 6030,
"preview": "#ifndef __BITCOIN_BASE_CLASSES_H__\n#define __BITCOIN_BASE_CLASSES_H__\n\n#include \"uBitcoin_conf.h\"\n#include \"Conversion.h"
},
{
"path": "src/Bitcoin.cpp",
"chars": 20731,
"preview": "#include \"Bitcoin.h\"\n#include \"Hash.h\"\n#include \"Conversion.h\"\n\n#include <stdint.h>\n#include <string.h>\n#include \"utilit"
},
{
"path": "src/Bitcoin.h",
"chars": 32232,
"preview": "/** @file Bitcoin.h\n */\n\n#ifndef __BITCOIN_H__\n#define __BITCOIN_H__\n\n#include \"uBitcoin_conf.h\"\n#include \"BaseClasses.h"
},
{
"path": "src/BitcoinCurve.cpp",
"chars": 7093,
"preview": "#include \"BitcoinCurve.h\"\n#include \"Conversion.h\"\n#include \"utility/trezor/rfc6979.h\"\n#include \"utility/trezor/ecdsa.h\"\n"
},
{
"path": "src/BitcoinCurve.h",
"chars": 6842,
"preview": "#ifndef __BITCOIN_CURVE_H__\n#define __BITCOIN_CURVE_H__\n\n#include \"uBitcoin_conf.h\"\n#include \"BaseClasses.h\"\n#include \"u"
},
{
"path": "src/Conversion.cpp",
"chars": 26294,
"preview": "#include \"Conversion.h\"\n#include \"Hash.h\"\n#include <string.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include \"utility/"
},
{
"path": "src/Conversion.h",
"chars": 6349,
"preview": "#ifndef __CONVERSION_H__\n#define __CONVERSION_H__\n\n#include \"uBitcoin_conf.h\"\n\n#include <string.h>\n#include <stdint.h>\n#"
},
{
"path": "src/Electrum.cpp",
"chars": 6488,
"preview": "#include \"Electrum.h\"\n\nElectrumTx::ElectrumTx(ElectrumTx const &other){\n tx = other.tx;\n is_segwit = false;\n tx"
},
{
"path": "src/Electrum.h",
"chars": 1694,
"preview": "#ifndef __ELECTRUM_H__\n#define __ELECTRUM_H__\n\n#include \"Bitcoin.h\"\n\n/**\n * \\brief Metadata for Electrum unsigned transa"
},
{
"path": "src/HDWallet.cpp",
"chars": 25681,
"preview": "#include <stdint.h>\n#include <string.h>\n\n#include \"Bitcoin.h\"\n#include \"Hash.h\"\n#include \"Conversion.h\"\n#include \"utilit"
},
{
"path": "src/Hash.cpp",
"chars": 5823,
"preview": "#include \"Hash.h\"\n#include \"utility/trezor/hmac.h\"\n#include \"utility/trezor/ripemd160.h\"\n\n#if USE_STD_STRING\nusing std::"
},
{
"path": "src/Hash.h",
"chars": 4812,
"preview": "/** @file Hash.h\n * \\brief All hashing functions and classes live here\n */\n#ifndef __HASH_H__18NLNNCSJ2\n#define __HASH_"
},
{
"path": "src/Networks.cpp",
"chars": 1805,
"preview": "// all known networks\n#include \"Networks.h\"\n\nconst Network Mainnet = {\n 0x00, // p2pkh\n 0x05, // p2sh\n \"bc\", //"
},
{
"path": "src/Networks.h",
"chars": 1803,
"preview": "#ifndef __UBTC_NETWORKS_H__\n#define __UBTC_NETWORKS_H__\n\n#include <stdint.h>\n\n/** \\brief Prefixes for particular network"
},
{
"path": "src/OpCodes.h",
"chars": 3655,
"preview": "#ifndef __OPCODES_H__R3NU8EN25O\n#define __OPCODES_H__R3NU8EN25O\n\n#include \"uBitcoin_conf.h\"\n\n// reference: https://en.bi"
},
{
"path": "src/PSBT.cpp",
"chars": 34198,
"preview": "#include \"PSBT.h\"\n#include \"Conversion.h\"\n#if USE_STD_STRING\nusing std::string;\n#define String string\n#endif\n\n#define UB"
},
{
"path": "src/PSBT.h",
"chars": 3848,
"preview": "#ifndef __PSBT_H__\n#define __PSBT_H__\n\n#include \"Bitcoin.h\"\n\n// TODO: \n// - SIGHASH types, and other key-value pairs tha"
},
{
"path": "src/Script.cpp",
"chars": 22638,
"preview": "#include \"Bitcoin.h\"\n#include \"Hash.h\"\n#include \"Conversion.h\"\n#include \"OpCodes.h\"\n#include \"utility/segwit_addr.h\"\n\n#i"
},
{
"path": "src/Transaction.cpp",
"chars": 21988,
"preview": "#include <stdint.h>\n#include <string.h>\n#include \"Bitcoin.h\"\n#include \"Hash.h\"\n#include \"Conversion.h\"\n#include \"utility"
},
{
"path": "src/uBitcoin_conf.h",
"chars": 1668,
"preview": "#ifndef __UBITCOIN_CONF_H__\n#define __UBITCOIN_CONF_H__\n\n/* Change this if you want to have other network by default */\n"
},
{
"path": "src/utility/segwit_addr.c",
"chars": 6586,
"preview": "/* Copyright (c) 2017 Pieter Wuille\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n "
},
{
"path": "src/utility/segwit_addr.h",
"chars": 4008,
"preview": "/* Copyright (c) 2017 Pieter Wuille\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n "
},
{
"path": "src/utility/trezor/address.c",
"chars": 3172,
"preview": "/**\n * Copyright (c) 2016 Daira Hopwood\n * Copyright (c) 2016 Pavol Rusnak\n *\n * Permission is hereby granted, free of c"
},
{
"path": "src/utility/trezor/address.h",
"chars": 1613,
"preview": "/**\n * Copyright (c) 2016 Daira Hopwood\n * Copyright (c) 2016 Pavol Rusnak\n *\n * Permission is hereby granted, free of c"
},
{
"path": "src/utility/trezor/base58.c",
"chars": 7507,
"preview": "/**\n * Copyright (c) 2012-2014 Luke Dashjr\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby granted, f"
},
{
"path": "src/utility/trezor/base58.h",
"chars": 2064,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby grante"
},
{
"path": "src/utility/trezor/bignum.c",
"chars": 30130,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n * Copyright (c) 2015 Jochen"
},
{
"path": "src/utility/trezor/bignum.h",
"chars": 5072,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n * Copyright (c) 2016 Alex Beregs"
},
{
"path": "src/utility/trezor/bip32.h",
"chars": 5100,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby grante"
},
{
"path": "src/utility/trezor/bip39.c",
"chars": 6018,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby grante"
},
{
"path": "src/utility/trezor/bip39.h",
"chars": 1894,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby grante"
},
{
"path": "src/utility/trezor/bip39_english.h",
"chars": 20474,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby grante"
},
{
"path": "src/utility/trezor/ecdsa.c",
"chars": 33330,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n * Copyright (c) 2015 Jochen"
},
{
"path": "src/utility/trezor/ecdsa.h",
"chars": 5414,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby grante"
},
{
"path": "src/utility/trezor/hasher.c",
"chars": 2714,
"preview": "/**\n * Copyright (c) 2017 Saleem Rashid\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a"
},
{
"path": "src/utility/trezor/hasher.h",
"chars": 1948,
"preview": "/**\n * Copyright (c) 2017 Saleem Rashid\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a"
},
{
"path": "src/utility/trezor/hmac.c",
"chars": 6219,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby grante"
},
{
"path": "src/utility/trezor/hmac.h",
"chars": 2578,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby grante"
},
{
"path": "src/utility/trezor/memzero.c",
"chars": 75,
"preview": "#include <string.h>\n\nvoid memzero(void *s, size_t n)\n{\n\tmemset(s, 0, n);\n}\n"
},
{
"path": "src/utility/trezor/memzero.h",
"chars": 200,
"preview": "#ifndef __MEMZERO_H__\n#define __MEMZERO_H__\n\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n\nvoid memzero(v"
},
{
"path": "src/utility/trezor/options.h",
"chars": 2573,
"preview": "/**\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n"
},
{
"path": "src/utility/trezor/pbkdf2.c",
"chars": 6307,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby grante"
},
{
"path": "src/utility/trezor/pbkdf2.h",
"chars": 2750,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby grante"
},
{
"path": "src/utility/trezor/rand.c",
"chars": 4624,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby grante"
},
{
"path": "src/utility/trezor/rand.h",
"chars": 1491,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby grante"
},
{
"path": "src/utility/trezor/rfc6979.c",
"chars": 2971,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n * Copyright (c) 2015 Jochen"
},
{
"path": "src/utility/trezor/rfc6979.h",
"chars": 1707,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n * Copyright (c) 2015-2017 Jochen"
},
{
"path": "src/utility/trezor/ripemd160.c",
"chars": 10197,
"preview": "/*\n * RIPE MD-160 implementation\n *\n * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved\n * SPDX-License-Iden"
},
{
"path": "src/utility/trezor/ripemd160.h",
"chars": 794,
"preview": "#ifndef __RIPEMD160_H__\n#define __RIPEMD160_H__\n\n#include <stdint.h>\n\n#define RIPEMD160_BLOCK_LENGTH 64\n#define RIPEMD"
},
{
"path": "src/utility/trezor/secp256k1.c",
"chars": 3037,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby grante"
},
{
"path": "src/utility/trezor/secp256k1.h",
"chars": 1510,
"preview": "/**\n * Copyright (c) 2013-2014 Tomas Dzetkulic\n * Copyright (c) 2013-2014 Pavol Rusnak\n *\n * Permission is hereby grante"
},
{
"path": "src/utility/trezor/secp256k1.table",
"chars": 124720,
"preview": "\t{\n\t\t/* 1*16^0*G: */\n\t\t{{{0x16f81798, 0x27ca056c, 0x1ce28d95, 0x26ff36cb, 0x070b0702, 0x018a573a, 0x0bbac55a, 0x199fbe7"
},
{
"path": "src/utility/trezor/sha2.c",
"chars": 27303,
"preview": "/**\n * Copyright (c) 2000-2001 Aaron D. Gifford\n * Copyright (c) 2013-2014 Pavol Rusnak\n * All rights reserved.\n *\n * Re"
},
{
"path": "src/utility/trezor/sha2.h",
"chars": 4141,
"preview": "/**\n * Copyright (c) 2000-2001 Aaron D. Gifford\n * Copyright (c) 2013-2014 Pavol Rusnak\n * All rights reserved.\n *\n * Re"
},
{
"path": "src/utility/trezor/sha3.c",
"chars": 10948,
"preview": "/* sha3.c - an implementation of Secure Hash Algorithm 3 (Keccak).\n * based on the\n * The Keccak SHA-3 submission. Submi"
},
{
"path": "src/utility/trezor/sha3.h",
"chars": 2980,
"preview": "/* sha3.h - an implementation of Secure Hash Algorithm 3 (Keccak).\n * based on the\n * The Keccak SHA-3 submission. Submi"
},
{
"path": "tests/Makefile",
"chars": 1796,
"preview": "# Paths\nBUILD_DIR = build\nSRC_DIR = .\n# uBitcoin library\nLIB_DIR = ../src\n\n# Tools\nifeq ($(OS),Windows_NT)\nTOOLCHAIN_PRE"
},
{
"path": "tests/minunit.h",
"chars": 11691,
"preview": "/*\n * Copyright (c) 2012 David Siñuela Pastor, siu.4coders@gmail.com\n * \n * Permission is hereby granted, free of charge"
},
{
"path": "tests/sysrand.c",
"chars": 197,
"preview": "#ifdef UBTC_TEST // only compile with test flag\n\n#include <stdint.h>\n#include <stdlib.h>\n\n// use system random function\n"
},
{
"path": "tests/test_conversion.cpp",
"chars": 4027,
"preview": "#ifdef UBTC_TEST // only compile with test flag\n\n#include \"minunit.h\"\n#include \"Conversion.h\" // to print byte arrays in"
},
{
"path": "tests/test_hash.cpp",
"chars": 2771,
"preview": "#ifdef UBTC_TEST // only compile with test flag\n\n#include \"minunit.h\"\n#include \"Hash.h\" // all single-line hashing algo"
},
{
"path": "tests/test_mnemonic.cpp",
"chars": 759,
"preview": "#ifdef UBTC_TEST // only compile with test flag\n\n#include \"minunit.h\"\n#include \"Bitcoin.h\"\n\nusing namespace std;\n\n#defin"
},
{
"path": "tests/test_schnorr.cpp",
"chars": 1366,
"preview": "#ifdef UBTC_TEST // only compile with test flag\n\n#include \"minunit.h\"\n#include \"Bitcoin.h\"\n#include \"Hash.h\"\n#include \"C"
}
]
About this extraction
This page contains the full source code of the micro-bitcoin/uBitcoin GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 82 files (639.6 KB), approximately 253.7k tokens, and a symbol index with 427 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.