Documentation
¶
Overview ¶
Package vanity25519 implements an efficient curve25519 vanity key generator.
This package provides functions to generate vanity curve25519 key pair with a specific pattern in its public key. It uses an optimized search algorithm that generates candidate public keys by adding offsets to the start public key, avoiding the need to perform full scalar multiplication for each candidate.
The algorithm has amortized cost (3.5M + 3A) per candidate key, where M is field multiplication and A is field addition.
For comparison, brute-force key pair generator requires 2561 field multiplications using square-and-multiply or 743 field multiplications using Twisted Edwards curve per candidate key.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Add ¶
Add returns curve25519 private key obtained by adding offset found by Search function to the start private key.
func HasPrefixBits ¶
HasPrefixBits returns a function that checks if the input has the specified prefix bits.
func Search ¶
func Search(ctx context.Context, startPublicKey []byte, startOffset *big.Int, batchSize int, accept func(candidatePublicKey []byte) bool, yield func(publicKey []byte, offset *big.Int)) uint64
Search generates candidate curve25519 public keys by adding batches of incrementing offsets to the start public key. Once matching candidate is found the corresponding private key can be obtained from its offset using Add function.
Parameters:
- startPublicKey: the start curve25519 public key to generate candidates from
- startOffset: the initial offset to start generating candidates from
- batchSize: number of candidates to generate per batch, must be positive and even
- accept: function that evaluates each candidate public key (hence it must be fast) and retruns true to accept the key
- yield: function called for each accepted candidate public key and its offset from the start key
Performance: amortized (3.5M + 3A) per candidate key, where M is field multiplication and A is field addition.
The search continues until context is done and returns number of generated candidates. The function panics if batchSize is not positive and even, or if startPublicKey is not a valid curve25519 public key.
Example ¶
package main
import (
"context"
"crypto/ecdh"
"crypto/rand"
"encoding/base64"
"fmt"
"math/big"
"os"
"github.com/AlexanderYastrebov/vanity25519"
)
func main() {
startKey, _ := ecdh.X25519().GenerateKey(rand.Reader)
startPublicKey := startKey.PublicKey().Bytes()
prefix, _ := base64.StdEncoding.DecodeString("AY/" + "x") // pad to 4 characters to decode properly
testPrefix := vanity25519.HasPrefixBits(prefix, 3*6) // search for 3-character prefix, i.e. 18 bits
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var found *big.Int
attempts := vanity25519.Search(ctx, startPublicKey, big.NewInt(0), 4096, testPrefix, func(_ []byte, offset *big.Int) {
found = offset
cancel()
})
vkb, _ := vanity25519.Add(startKey.Bytes(), found)
vk, _ := ecdh.X25519().NewPrivateKey(vkb)
vpk := base64.StdEncoding.EncodeToString(vk.PublicKey().Bytes())
fmt.Fprintf(os.Stderr, "Found %s after %d attempts\n", vpk, attempts)
fmt.Printf("Found key: %s...\n", vpk[:3])
}
Output: Found key: AY/...
Example (Parallel) ¶
package main
import (
"context"
"crypto/ecdh"
"crypto/rand"
"encoding/base64"
"fmt"
"math/big"
"os"
"runtime"
"sync"
"sync/atomic"
"github.com/AlexanderYastrebov/vanity25519"
)
func main() {
startKey, _ := ecdh.X25519().GenerateKey(rand.Reader)
startPublicKey := startKey.PublicKey().Bytes()
prefix, _ := base64.StdEncoding.DecodeString("AY/" + "x") // pad to 4 characters to decode properly
testPrefix := vanity25519.HasPrefixBits(prefix, 3*6) // search for 3-character prefix, i.e. 18 bits
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var attempts atomic.Uint64
var found atomic.Pointer[big.Int]
var wg sync.WaitGroup
for range runtime.NumCPU() {
wg.Add(1)
go func() {
defer wg.Done()
skip, _ := rand.Int(rand.Reader, new(big.Int).SetUint64(1<<64-1))
n := vanity25519.Search(ctx, startPublicKey, skip, 4096, testPrefix, func(_ []byte, offset *big.Int) {
if found.CompareAndSwap(nil, offset) {
cancel()
}
})
attempts.Add(n)
}()
}
wg.Wait()
vkb, _ := vanity25519.Add(startKey.Bytes(), found.Load())
vk, _ := ecdh.X25519().NewPrivateKey(vkb)
vpk := base64.StdEncoding.EncodeToString(vk.PublicKey().Bytes())
fmt.Fprintf(os.Stderr, "Found %s after %d attempts\n", vpk, attempts.Load())
fmt.Printf("Found key: %s...\n", vpk[:3])
}
Output: Found key: AY/...
Types ¶
This section is empty.