Key derivation code (working draft)

- read hex, base58, RFC1751, or passphrase
- derive Ed25519 keys from seed
- derive secp256k1 master key from seed
- encode keys to hex or base58

Note: at this time, the RFC1751 implementation is consistent with the
original spec but not consistent with rippled's RFC1751 implementation.
I don't know why rippled's RFC1751 implementation doesn't match the
spec. Maybe something to do with endianness.
This commit is contained in:
mDuo13
2019-10-09 19:54:43 -07:00
parent 80be728267
commit 1815326542
6 changed files with 809 additions and 30 deletions

View File

@@ -0,0 +1,374 @@
# Upstream version:
# https://github.com/dlitz/pycrypto/blob/master/lib/Crypto/Util/RFC1751.py
# This version has been adapted to Python 3 using the '2to3' utility
# and manually adjusted for better Python 3 compatibility.
# rfc1751.py : Converts between 128-bit strings and a human-readable
# sequence of words, as defined in RFC1751: "A Convention for
# Human-Readable 128-bit Keys", by Daniel L. McDonald.
#
# Part of the Python Cryptography Toolkit
#
# Written by Andrew M. Kuchling and others
#
# ===================================================================
# The contents of this file are dedicated to the public domain. To
# the extent that dedication to the public domain is not available,
# everyone is granted a worldwide, perpetual, royalty-free,
# non-exclusive license to exercise all rights associated with the
# contents of this file for any purpose whatsoever.
# No rights are reserved.
#
# 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.
# ===================================================================
__revision__ = "$Id$"
import binascii
from functools import reduce
from io import BytesIO
def bchr(s):
return bytes([s])
def bord(s):
return s
binary={0:'0000', 1:'0001', 2:'0010', 3:'0011', 4:'0100', 5:'0101',
6:'0110', 7:'0111', 8:'1000', 9:'1001', 10:'1010', 11:'1011',
12:'1100', 13:'1101', 14:'1110', 15:'1111'}
def _key2bin(s):
"Convert a key into a string of binary digits"
kl=[bord(x) for x in s]
kl=[binary[x>>4]+binary[x&15] for x in kl]
return ''.join(kl)
def _extract(key, start, length):
"""Extract a bitstring(2.x)/bytestring(2.x) from a string of binary digits, and return its
numeric value."""
k=key[start:start+length]
return reduce(lambda x,y: x*2+ord(y)-48, k, 0)
def key_to_english (key):
"""key_to_english(key:string(2.x)/bytes(3.x)) : string
Transform an arbitrary key into a string containing English words.
The key length must be a multiple of 8.
"""
english=''
for index in range(0, len(key), 8): # Loop over 8-byte subkeys
subkey=key[index:index+8]
# Compute the parity of the key
skbin=_key2bin(subkey) ; p=0
for i in range(0, 64, 2): p=p+_extract(skbin, i, 2)
# Append parity bits to the subkey
skbin=_key2bin(subkey+bchr((p<<6) & 255))
for i in range(0, 64, 11):
english=english+wordlist[_extract(skbin, i, 11)]+' '
return english[:-1] # Remove the trailing space
def english_to_key (s):
"""english_to_key(string):string(2.x)/bytes(2.x)
Transform a string into a corresponding key.
The string must contain words separated by whitespace; the number
of words must be a multiple of 6.
"""
L=s.upper().split() ; key=b''
for index in range(0, len(L), 6):
sublist=L[index:index+6] ; char=9*[0] ; bits=0
for i in sublist:
index = wordlist.index(i)
shift = (8-(bits+11)%8) %8
y = index << shift
cl, cc, cr = (y>>16), (y>>8)&0xff, y & 0xff
if (shift>5):
char[bits>>3] = char[bits>>3] | cl
char[(bits>>3)+1] = char[(bits>>3)+1] | cc
char[(bits>>3)+2] = char[(bits>>3)+2] | cr
elif shift>-3:
char[bits>>3] = char[bits>>3] | cc
char[(bits>>3)+1] = char[(bits>>3)+1] | cr
else: char[bits>>3] = char[bits>>3] | cr
bits=bits+11
subkey=reduce(lambda x,y:x+bchr(y), char, b'')
# Check the parity of the resulting key
skbin=_key2bin(subkey)
p=0
for i in range(0, 64, 2): p=p+_extract(skbin, i, 2)
if (p&3) != _extract(skbin, 64, 2):
raise ValueError("Parity error in resulting key")
key=key+subkey[0:8]
return key
wordlist=[ "A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD",
"AGO", "AID", "AIM", "AIR", "ALL", "ALP", "AM", "AMY", "AN", "ANA",
"AND", "ANN", "ANT", "ANY", "APE", "APS", "APT", "ARC", "ARE", "ARK",
"ARM", "ART", "AS", "ASH", "ASK", "AT", "ATE", "AUG", "AUK", "AVE",
"AWE", "AWK", "AWL", "AWN", "AX", "AYE", "BAD", "BAG", "BAH", "BAM",
"BAN", "BAR", "BAT", "BAY", "BE", "BED", "BEE", "BEG", "BEN", "BET",
"BEY", "BIB", "BID", "BIG", "BIN", "BIT", "BOB", "BOG", "BON", "BOO",
"BOP", "BOW", "BOY", "BUB", "BUD", "BUG", "BUM", "BUN", "BUS", "BUT",
"BUY", "BY", "BYE", "CAB", "CAL", "CAM", "CAN", "CAP", "CAR", "CAT",
"CAW", "COD", "COG", "COL", "CON", "COO", "COP", "COT", "COW", "COY",
"CRY", "CUB", "CUE", "CUP", "CUR", "CUT", "DAB", "DAD", "DAM", "DAN",
"DAR", "DAY", "DEE", "DEL", "DEN", "DES", "DEW", "DID", "DIE", "DIG",
"DIN", "DIP", "DO", "DOE", "DOG", "DON", "DOT", "DOW", "DRY", "DUB",
"DUD", "DUE", "DUG", "DUN", "EAR", "EAT", "ED", "EEL", "EGG", "EGO",
"ELI", "ELK", "ELM", "ELY", "EM", "END", "EST", "ETC", "EVA", "EVE",
"EWE", "EYE", "FAD", "FAN", "FAR", "FAT", "FAY", "FED", "FEE", "FEW",
"FIB", "FIG", "FIN", "FIR", "FIT", "FLO", "FLY", "FOE", "FOG", "FOR",
"FRY", "FUM", "FUN", "FUR", "GAB", "GAD", "GAG", "GAL", "GAM", "GAP",
"GAS", "GAY", "GEE", "GEL", "GEM", "GET", "GIG", "GIL", "GIN", "GO",
"GOT", "GUM", "GUN", "GUS", "GUT", "GUY", "GYM", "GYP", "HA", "HAD",
"HAL", "HAM", "HAN", "HAP", "HAS", "HAT", "HAW", "HAY", "HE", "HEM",
"HEN", "HER", "HEW", "HEY", "HI", "HID", "HIM", "HIP", "HIS", "HIT",
"HO", "HOB", "HOC", "HOE", "HOG", "HOP", "HOT", "HOW", "HUB", "HUE",
"HUG", "HUH", "HUM", "HUT", "I", "ICY", "IDA", "IF", "IKE", "ILL",
"INK", "INN", "IO", "ION", "IQ", "IRA", "IRE", "IRK", "IS", "IT",
"ITS", "IVY", "JAB", "JAG", "JAM", "JAN", "JAR", "JAW", "JAY", "JET",
"JIG", "JIM", "JO", "JOB", "JOE", "JOG", "JOT", "JOY", "JUG", "JUT",
"KAY", "KEG", "KEN", "KEY", "KID", "KIM", "KIN", "KIT", "LA", "LAB",
"LAC", "LAD", "LAG", "LAM", "LAP", "LAW", "LAY", "LEA", "LED", "LEE",
"LEG", "LEN", "LEO", "LET", "LEW", "LID", "LIE", "LIN", "LIP", "LIT",
"LO", "LOB", "LOG", "LOP", "LOS", "LOT", "LOU", "LOW", "LOY", "LUG",
"LYE", "MA", "MAC", "MAD", "MAE", "MAN", "MAO", "MAP", "MAT", "MAW",
"MAY", "ME", "MEG", "MEL", "MEN", "MET", "MEW", "MID", "MIN", "MIT",
"MOB", "MOD", "MOE", "MOO", "MOP", "MOS", "MOT", "MOW", "MUD", "MUG",
"MUM", "MY", "NAB", "NAG", "NAN", "NAP", "NAT", "NAY", "NE", "NED",
"NEE", "NET", "NEW", "NIB", "NIL", "NIP", "NIT", "NO", "NOB", "NOD",
"NON", "NOR", "NOT", "NOV", "NOW", "NU", "NUN", "NUT", "O", "OAF",
"OAK", "OAR", "OAT", "ODD", "ODE", "OF", "OFF", "OFT", "OH", "OIL",
"OK", "OLD", "ON", "ONE", "OR", "ORB", "ORE", "ORR", "OS", "OTT",
"OUR", "OUT", "OVA", "OW", "OWE", "OWL", "OWN", "OX", "PA", "PAD",
"PAL", "PAM", "PAN", "PAP", "PAR", "PAT", "PAW", "PAY", "PEA", "PEG",
"PEN", "PEP", "PER", "PET", "PEW", "PHI", "PI", "PIE", "PIN", "PIT",
"PLY", "PO", "POD", "POE", "POP", "POT", "POW", "PRO", "PRY", "PUB",
"PUG", "PUN", "PUP", "PUT", "QUO", "RAG", "RAM", "RAN", "RAP", "RAT",
"RAW", "RAY", "REB", "RED", "REP", "RET", "RIB", "RID", "RIG", "RIM",
"RIO", "RIP", "ROB", "ROD", "ROE", "RON", "ROT", "ROW", "ROY", "RUB",
"RUE", "RUG", "RUM", "RUN", "RYE", "SAC", "SAD", "SAG", "SAL", "SAM",
"SAN", "SAP", "SAT", "SAW", "SAY", "SEA", "SEC", "SEE", "SEN", "SET",
"SEW", "SHE", "SHY", "SIN", "SIP", "SIR", "SIS", "SIT", "SKI", "SKY",
"SLY", "SO", "SOB", "SOD", "SON", "SOP", "SOW", "SOY", "SPA", "SPY",
"SUB", "SUD", "SUE", "SUM", "SUN", "SUP", "TAB", "TAD", "TAG", "TAN",
"TAP", "TAR", "TEA", "TED", "TEE", "TEN", "THE", "THY", "TIC", "TIE",
"TIM", "TIN", "TIP", "TO", "TOE", "TOG", "TOM", "TON", "TOO", "TOP",
"TOW", "TOY", "TRY", "TUB", "TUG", "TUM", "TUN", "TWO", "UN", "UP",
"US", "USE", "VAN", "VAT", "VET", "VIE", "WAD", "WAG", "WAR", "WAS",
"WAY", "WE", "WEB", "WED", "WEE", "WET", "WHO", "WHY", "WIN", "WIT",
"WOK", "WON", "WOO", "WOW", "WRY", "WU", "YAM", "YAP", "YAW", "YE",
"YEA", "YES", "YET", "YOU", "ABED", "ABEL", "ABET", "ABLE", "ABUT",
"ACHE", "ACID", "ACME", "ACRE", "ACTA", "ACTS", "ADAM", "ADDS",
"ADEN", "AFAR", "AFRO", "AGEE", "AHEM", "AHOY", "AIDA", "AIDE",
"AIDS", "AIRY", "AJAR", "AKIN", "ALAN", "ALEC", "ALGA", "ALIA",
"ALLY", "ALMA", "ALOE", "ALSO", "ALTO", "ALUM", "ALVA", "AMEN",
"AMES", "AMID", "AMMO", "AMOK", "AMOS", "AMRA", "ANDY", "ANEW",
"ANNA", "ANNE", "ANTE", "ANTI", "AQUA", "ARAB", "ARCH", "AREA",
"ARGO", "ARID", "ARMY", "ARTS", "ARTY", "ASIA", "ASKS", "ATOM",
"AUNT", "AURA", "AUTO", "AVER", "AVID", "AVIS", "AVON", "AVOW",
"AWAY", "AWRY", "BABE", "BABY", "BACH", "BACK", "BADE", "BAIL",
"BAIT", "BAKE", "BALD", "BALE", "BALI", "BALK", "BALL", "BALM",
"BAND", "BANE", "BANG", "BANK", "BARB", "BARD", "BARE", "BARK",
"BARN", "BARR", "BASE", "BASH", "BASK", "BASS", "BATE", "BATH",
"BAWD", "BAWL", "BEAD", "BEAK", "BEAM", "BEAN", "BEAR", "BEAT",
"BEAU", "BECK", "BEEF", "BEEN", "BEER",
"BEET", "BELA", "BELL", "BELT", "BEND", "BENT", "BERG", "BERN",
"BERT", "BESS", "BEST", "BETA", "BETH", "BHOY", "BIAS", "BIDE",
"BIEN", "BILE", "BILK", "BILL", "BIND", "BING", "BIRD", "BITE",
"BITS", "BLAB", "BLAT", "BLED", "BLEW", "BLOB", "BLOC", "BLOT",
"BLOW", "BLUE", "BLUM", "BLUR", "BOAR", "BOAT", "BOCA", "BOCK",
"BODE", "BODY", "BOGY", "BOHR", "BOIL", "BOLD", "BOLO", "BOLT",
"BOMB", "BONA", "BOND", "BONE", "BONG", "BONN", "BONY", "BOOK",
"BOOM", "BOON", "BOOT", "BORE", "BORG", "BORN", "BOSE", "BOSS",
"BOTH", "BOUT", "BOWL", "BOYD", "BRAD", "BRAE", "BRAG", "BRAN",
"BRAY", "BRED", "BREW", "BRIG", "BRIM", "BROW", "BUCK", "BUDD",
"BUFF", "BULB", "BULK", "BULL", "BUNK", "BUNT", "BUOY", "BURG",
"BURL", "BURN", "BURR", "BURT", "BURY", "BUSH", "BUSS", "BUST",
"BUSY", "BYTE", "CADY", "CAFE", "CAGE", "CAIN", "CAKE", "CALF",
"CALL", "CALM", "CAME", "CANE", "CANT", "CARD", "CARE", "CARL",
"CARR", "CART", "CASE", "CASH", "CASK", "CAST", "CAVE", "CEIL",
"CELL", "CENT", "CERN", "CHAD", "CHAR", "CHAT", "CHAW", "CHEF",
"CHEN", "CHEW", "CHIC", "CHIN", "CHOU", "CHOW", "CHUB", "CHUG",
"CHUM", "CITE", "CITY", "CLAD", "CLAM", "CLAN", "CLAW", "CLAY",
"CLOD", "CLOG", "CLOT", "CLUB", "CLUE", "COAL", "COAT", "COCA",
"COCK", "COCO", "CODA", "CODE", "CODY", "COED", "COIL", "COIN",
"COKE", "COLA", "COLD", "COLT", "COMA", "COMB", "COME", "COOK",
"COOL", "COON", "COOT", "CORD", "CORE", "CORK", "CORN", "COST",
"COVE", "COWL", "CRAB", "CRAG", "CRAM", "CRAY", "CREW", "CRIB",
"CROW", "CRUD", "CUBA", "CUBE", "CUFF", "CULL", "CULT", "CUNY",
"CURB", "CURD", "CURE", "CURL", "CURT", "CUTS", "DADE", "DALE",
"DAME", "DANA", "DANE", "DANG", "DANK", "DARE", "DARK", "DARN",
"DART", "DASH", "DATA", "DATE", "DAVE", "DAVY", "DAWN", "DAYS",
"DEAD", "DEAF", "DEAL", "DEAN", "DEAR", "DEBT", "DECK", "DEED",
"DEEM", "DEER", "DEFT", "DEFY", "DELL", "DENT", "DENY", "DESK",
"DIAL", "DICE", "DIED", "DIET", "DIME", "DINE", "DING", "DINT",
"DIRE", "DIRT", "DISC", "DISH", "DISK", "DIVE", "DOCK", "DOES",
"DOLE", "DOLL", "DOLT", "DOME", "DONE", "DOOM", "DOOR", "DORA",
"DOSE", "DOTE", "DOUG", "DOUR", "DOVE", "DOWN", "DRAB", "DRAG",
"DRAM", "DRAW", "DREW", "DRUB", "DRUG", "DRUM", "DUAL", "DUCK",
"DUCT", "DUEL", "DUET", "DUKE", "DULL", "DUMB", "DUNE", "DUNK",
"DUSK", "DUST", "DUTY", "EACH", "EARL", "EARN", "EASE", "EAST",
"EASY", "EBEN", "ECHO", "EDDY", "EDEN", "EDGE", "EDGY", "EDIT",
"EDNA", "EGAN", "ELAN", "ELBA", "ELLA", "ELSE", "EMIL", "EMIT",
"EMMA", "ENDS", "ERIC", "EROS", "EVEN", "EVER", "EVIL", "EYED",
"FACE", "FACT", "FADE", "FAIL", "FAIN", "FAIR", "FAKE", "FALL",
"FAME", "FANG", "FARM", "FAST", "FATE", "FAWN", "FEAR", "FEAT",
"FEED", "FEEL", "FEET", "FELL", "FELT", "FEND", "FERN", "FEST",
"FEUD", "FIEF", "FIGS", "FILE", "FILL", "FILM", "FIND", "FINE",
"FINK", "FIRE", "FIRM", "FISH", "FISK", "FIST", "FITS", "FIVE",
"FLAG", "FLAK", "FLAM", "FLAT", "FLAW", "FLEA", "FLED", "FLEW",
"FLIT", "FLOC", "FLOG", "FLOW", "FLUB", "FLUE", "FOAL", "FOAM",
"FOGY", "FOIL", "FOLD", "FOLK", "FOND", "FONT", "FOOD", "FOOL",
"FOOT", "FORD", "FORE", "FORK", "FORM", "FORT", "FOSS", "FOUL",
"FOUR", "FOWL", "FRAU", "FRAY", "FRED", "FREE", "FRET", "FREY",
"FROG", "FROM", "FUEL", "FULL", "FUME", "FUND", "FUNK", "FURY",
"FUSE", "FUSS", "GAFF", "GAGE", "GAIL", "GAIN", "GAIT", "GALA",
"GALE", "GALL", "GALT", "GAME", "GANG", "GARB", "GARY", "GASH",
"GATE", "GAUL", "GAUR", "GAVE", "GAWK", "GEAR", "GELD", "GENE",
"GENT", "GERM", "GETS", "GIBE", "GIFT", "GILD", "GILL", "GILT",
"GINA", "GIRD", "GIRL", "GIST", "GIVE", "GLAD", "GLEE", "GLEN",
"GLIB", "GLOB", "GLOM", "GLOW", "GLUE", "GLUM", "GLUT", "GOAD",
"GOAL", "GOAT", "GOER", "GOES", "GOLD", "GOLF", "GONE", "GONG",
"GOOD", "GOOF", "GORE", "GORY", "GOSH", "GOUT", "GOWN", "GRAB",
"GRAD", "GRAY", "GREG", "GREW", "GREY", "GRID", "GRIM", "GRIN",
"GRIT", "GROW", "GRUB", "GULF", "GULL", "GUNK", "GURU", "GUSH",
"GUST", "GWEN", "GWYN", "HAAG", "HAAS", "HACK", "HAIL", "HAIR",
"HALE", "HALF", "HALL", "HALO", "HALT", "HAND", "HANG", "HANK",
"HANS", "HARD", "HARK", "HARM", "HART", "HASH", "HAST", "HATE",
"HATH", "HAUL", "HAVE", "HAWK", "HAYS", "HEAD", "HEAL", "HEAR",
"HEAT", "HEBE", "HECK", "HEED", "HEEL", "HEFT", "HELD", "HELL",
"HELM", "HERB", "HERD", "HERE", "HERO", "HERS", "HESS", "HEWN",
"HICK", "HIDE", "HIGH", "HIKE", "HILL", "HILT", "HIND", "HINT",
"HIRE", "HISS", "HIVE", "HOBO", "HOCK", "HOFF", "HOLD", "HOLE",
"HOLM", "HOLT", "HOME", "HONE", "HONK", "HOOD", "HOOF", "HOOK",
"HOOT", "HORN", "HOSE", "HOST", "HOUR", "HOVE", "HOWE", "HOWL",
"HOYT", "HUCK", "HUED", "HUFF", "HUGE", "HUGH", "HUGO", "HULK",
"HULL", "HUNK", "HUNT", "HURD", "HURL", "HURT", "HUSH", "HYDE",
"HYMN", "IBIS", "ICON", "IDEA", "IDLE", "IFFY", "INCA", "INCH",
"INTO", "IONS", "IOTA", "IOWA", "IRIS", "IRMA", "IRON", "ISLE",
"ITCH", "ITEM", "IVAN", "JACK", "JADE", "JAIL", "JAKE", "JANE",
"JAVA", "JEAN", "JEFF", "JERK", "JESS", "JEST", "JIBE", "JILL",
"JILT", "JIVE", "JOAN", "JOBS", "JOCK", "JOEL", "JOEY", "JOHN",
"JOIN", "JOKE", "JOLT", "JOVE", "JUDD", "JUDE", "JUDO", "JUDY",
"JUJU", "JUKE", "JULY", "JUNE", "JUNK", "JUNO", "JURY", "JUST",
"JUTE", "KAHN", "KALE", "KANE", "KANT", "KARL", "KATE", "KEEL",
"KEEN", "KENO", "KENT", "KERN", "KERR", "KEYS", "KICK", "KILL",
"KIND", "KING", "KIRK", "KISS", "KITE", "KLAN", "KNEE", "KNEW",
"KNIT", "KNOB", "KNOT", "KNOW", "KOCH", "KONG", "KUDO", "KURD",
"KURT", "KYLE", "LACE", "LACK", "LACY", "LADY", "LAID", "LAIN",
"LAIR", "LAKE", "LAMB", "LAME", "LAND", "LANE", "LANG", "LARD",
"LARK", "LASS", "LAST", "LATE", "LAUD", "LAVA", "LAWN", "LAWS",
"LAYS", "LEAD", "LEAF", "LEAK", "LEAN", "LEAR", "LEEK", "LEER",
"LEFT", "LEND", "LENS", "LENT", "LEON", "LESK", "LESS", "LEST",
"LETS", "LIAR", "LICE", "LICK", "LIED", "LIEN", "LIES", "LIEU",
"LIFE", "LIFT", "LIKE", "LILA", "LILT", "LILY", "LIMA", "LIMB",
"LIME", "LIND", "LINE", "LINK", "LINT", "LION", "LISA", "LIST",
"LIVE", "LOAD", "LOAF", "LOAM", "LOAN", "LOCK", "LOFT", "LOGE",
"LOIS", "LOLA", "LONE", "LONG", "LOOK", "LOON", "LOOT", "LORD",
"LORE", "LOSE", "LOSS", "LOST", "LOUD", "LOVE", "LOWE", "LUCK",
"LUCY", "LUGE", "LUKE", "LULU", "LUND", "LUNG", "LURA", "LURE",
"LURK", "LUSH", "LUST", "LYLE", "LYNN", "LYON", "LYRA", "MACE",
"MADE", "MAGI", "MAID", "MAIL", "MAIN", "MAKE", "MALE", "MALI",
"MALL", "MALT", "MANA", "MANN", "MANY", "MARC", "MARE", "MARK",
"MARS", "MART", "MARY", "MASH", "MASK", "MASS", "MAST", "MATE",
"MATH", "MAUL", "MAYO", "MEAD", "MEAL", "MEAN", "MEAT", "MEEK",
"MEET", "MELD", "MELT", "MEMO", "MEND", "MENU", "MERT", "MESH",
"MESS", "MICE", "MIKE", "MILD", "MILE", "MILK", "MILL", "MILT",
"MIMI", "MIND", "MINE", "MINI", "MINK", "MINT", "MIRE", "MISS",
"MIST", "MITE", "MITT", "MOAN", "MOAT", "MOCK", "MODE", "MOLD",
"MOLE", "MOLL", "MOLT", "MONA", "MONK", "MONT", "MOOD", "MOON",
"MOOR", "MOOT", "MORE", "MORN", "MORT", "MOSS", "MOST", "MOTH",
"MOVE", "MUCH", "MUCK", "MUDD", "MUFF", "MULE", "MULL", "MURK",
"MUSH", "MUST", "MUTE", "MUTT", "MYRA", "MYTH", "NAGY", "NAIL",
"NAIR", "NAME", "NARY", "NASH", "NAVE", "NAVY", "NEAL", "NEAR",
"NEAT", "NECK", "NEED", "NEIL", "NELL", "NEON", "NERO", "NESS",
"NEST", "NEWS", "NEWT", "NIBS", "NICE", "NICK", "NILE", "NINA",
"NINE", "NOAH", "NODE", "NOEL", "NOLL", "NONE", "NOOK", "NOON",
"NORM", "NOSE", "NOTE", "NOUN", "NOVA", "NUDE", "NULL", "NUMB",
"OATH", "OBEY", "OBOE", "ODIN", "OHIO", "OILY", "OINT", "OKAY",
"OLAF", "OLDY", "OLGA", "OLIN", "OMAN", "OMEN", "OMIT", "ONCE",
"ONES", "ONLY", "ONTO", "ONUS", "ORAL", "ORGY", "OSLO", "OTIS",
"OTTO", "OUCH", "OUST", "OUTS", "OVAL", "OVEN", "OVER", "OWLY",
"OWNS", "QUAD", "QUIT", "QUOD", "RACE", "RACK", "RACY", "RAFT",
"RAGE", "RAID", "RAIL", "RAIN", "RAKE", "RANK", "RANT", "RARE",
"RASH", "RATE", "RAVE", "RAYS", "READ", "REAL", "REAM", "REAR",
"RECK", "REED", "REEF", "REEK", "REEL", "REID", "REIN", "RENA",
"REND", "RENT", "REST", "RICE", "RICH", "RICK", "RIDE", "RIFT",
"RILL", "RIME", "RING", "RINK", "RISE", "RISK", "RITE", "ROAD",
"ROAM", "ROAR", "ROBE", "ROCK", "RODE", "ROIL", "ROLL", "ROME",
"ROOD", "ROOF", "ROOK", "ROOM", "ROOT", "ROSA", "ROSE", "ROSS",
"ROSY", "ROTH", "ROUT", "ROVE", "ROWE", "ROWS", "RUBE", "RUBY",
"RUDE", "RUDY", "RUIN", "RULE", "RUNG", "RUNS", "RUNT", "RUSE",
"RUSH", "RUSK", "RUSS", "RUST", "RUTH", "SACK", "SAFE", "SAGE",
"SAID", "SAIL", "SALE", "SALK", "SALT", "SAME", "SAND", "SANE",
"SANG", "SANK", "SARA", "SAUL", "SAVE", "SAYS", "SCAN", "SCAR",
"SCAT", "SCOT", "SEAL", "SEAM", "SEAR", "SEAT", "SEED", "SEEK",
"SEEM", "SEEN", "SEES", "SELF", "SELL", "SEND", "SENT", "SETS",
"SEWN", "SHAG", "SHAM", "SHAW", "SHAY", "SHED", "SHIM", "SHIN",
"SHOD", "SHOE", "SHOT", "SHOW", "SHUN", "SHUT", "SICK", "SIDE",
"SIFT", "SIGH", "SIGN", "SILK", "SILL", "SILO", "SILT", "SINE",
"SING", "SINK", "SIRE", "SITE", "SITS", "SITU", "SKAT", "SKEW",
"SKID", "SKIM", "SKIN", "SKIT", "SLAB", "SLAM", "SLAT", "SLAY",
"SLED", "SLEW", "SLID", "SLIM", "SLIT", "SLOB", "SLOG", "SLOT",
"SLOW", "SLUG", "SLUM", "SLUR", "SMOG", "SMUG", "SNAG", "SNOB",
"SNOW", "SNUB", "SNUG", "SOAK", "SOAR", "SOCK", "SODA", "SOFA",
"SOFT", "SOIL", "SOLD", "SOME", "SONG", "SOON", "SOOT", "SORE",
"SORT", "SOUL", "SOUR", "SOWN", "STAB", "STAG", "STAN", "STAR",
"STAY", "STEM", "STEW", "STIR", "STOW", "STUB", "STUN", "SUCH",
"SUDS", "SUIT", "SULK", "SUMS", "SUNG", "SUNK", "SURE", "SURF",
"SWAB", "SWAG", "SWAM", "SWAN", "SWAT", "SWAY", "SWIM", "SWUM",
"TACK", "TACT", "TAIL", "TAKE", "TALE", "TALK", "TALL", "TANK",
"TASK", "TATE", "TAUT", "TEAL", "TEAM", "TEAR", "TECH", "TEEM",
"TEEN", "TEET", "TELL", "TEND", "TENT", "TERM", "TERN", "TESS",
"TEST", "THAN", "THAT", "THEE", "THEM", "THEN", "THEY", "THIN",
"THIS", "THUD", "THUG", "TICK", "TIDE", "TIDY", "TIED", "TIER",
"TILE", "TILL", "TILT", "TIME", "TINA", "TINE", "TINT", "TINY",
"TIRE", "TOAD", "TOGO", "TOIL", "TOLD", "TOLL", "TONE", "TONG",
"TONY", "TOOK", "TOOL", "TOOT", "TORE", "TORN", "TOTE", "TOUR",
"TOUT", "TOWN", "TRAG", "TRAM", "TRAY", "TREE", "TREK", "TRIG",
"TRIM", "TRIO", "TROD", "TROT", "TROY", "TRUE", "TUBA", "TUBE",
"TUCK", "TUFT", "TUNA", "TUNE", "TUNG", "TURF", "TURN", "TUSK",
"TWIG", "TWIN", "TWIT", "ULAN", "UNIT", "URGE", "USED", "USER",
"USES", "UTAH", "VAIL", "VAIN", "VALE", "VARY", "VASE", "VAST",
"VEAL", "VEDA", "VEIL", "VEIN", "VEND", "VENT", "VERB", "VERY",
"VETO", "VICE", "VIEW", "VINE", "VISE", "VOID", "VOLT", "VOTE",
"WACK", "WADE", "WAGE", "WAIL", "WAIT", "WAKE", "WALE", "WALK",
"WALL", "WALT", "WAND", "WANE", "WANG", "WANT", "WARD", "WARM",
"WARN", "WART", "WASH", "WAST", "WATS", "WATT", "WAVE", "WAVY",
"WAYS", "WEAK", "WEAL", "WEAN", "WEAR", "WEED", "WEEK", "WEIR",
"WELD", "WELL", "WELT", "WENT", "WERE", "WERT", "WEST", "WHAM",
"WHAT", "WHEE", "WHEN", "WHET", "WHOA", "WHOM", "WICK", "WIFE",
"WILD", "WILL", "WIND", "WINE", "WING", "WINK", "WINO", "WIRE",
"WISE", "WISH", "WITH", "WOLF", "WONT", "WOOD", "WOOL", "WORD",
"WORE", "WORK", "WORM", "WORN", "WOVE", "WRIT", "WYNN", "YALE",
"YANG", "YANK", "YARD", "YARN", "YAWL", "YAWN", "YEAH", "YEAR",
"YELL", "YOGA", "YOKE" ]
if __name__=='__main__':
data = [('EB33F77EE73D4053', 'TIDE ITCH SLOW REIN RULE MOT'),
('CCAC2AED591056BE4F90FD441C534766',
'RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE'),
('EFF81F9BFBC65350920CDD7416DE8009',
'TROD MUTE TAIL WARM CHAR KONG HAAG CITY BORE O TEAL AWL')
]
for key, words in data:
print('Trying key', key)
key=binascii.a2b_hex(key)
w2=key_to_english(key)
if w2!=words:
print('key_to_english fails on key', repr(key), ', producing', str(w2))
k2=english_to_key(words)
if k2!=key:
print('english_to_key fails on key', repr(key), ', producing', repr(k2))

View File

@@ -0,0 +1,19 @@
Copyright (c) 2015 David Keijser
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.

View File

@@ -0,0 +1,177 @@
'''Base58 encoding
Implementations of Base58 and Base58Check endcodings that are compatible
with the XRP Ledger.
'''
# This This code is adapted from the module by David Keijser at
# <https://github.com/keis/base58>. - rome@ripple.com
# His notes are preserved below:
# This module is based upon base58 snippets found scattered over many bitcoin
# tools written in python. From what I gather the original source is from a
# forum post by Gavin Andresen, so direct your praise to him.
# This module adds shiny packaging and support for python3.
from hashlib import sha256
__version__ = '1.0.3-xrp'
# 58 character alphabet used
# alphabet = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' # Bitcoin
alphabet = b'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz' # XRP Ledger
if bytes == str: # python2
iseq, bseq, buffer = (
lambda s: map(ord, s),
lambda s: ''.join(map(chr, s)),
lambda s: s,
)
else: # python3
iseq, bseq, buffer = (
lambda s: s,
bytes,
lambda s: s.buffer,
)
def scrub_input(v):
if isinstance(v, str) and not isinstance(v, bytes):
v = v.encode('ascii')
if not isinstance(v, bytes):
raise TypeError(
"a bytes-like object is required (also str), not '%s'" %
type(v).__name__)
return v
def b58encode_int(i, default_one=True):
'''Encode an integer using Base58'''
if not i and default_one:
return alphabet[0:1]
string = b""
while i:
i, idx = divmod(i, 58)
string = alphabet[idx:idx+1] + string
return string
def b58encode(v):
'''Encode a string using Base58'''
v = scrub_input(v)
nPad = len(v)
v = v.lstrip(b'\0')
nPad -= len(v)
p, acc = 1, 0
for c in iseq(reversed(v)):
acc += p * c
p = p << 8
result = b58encode_int(acc, default_one=False)
return (alphabet[0:1] * nPad + result)
def b58decode_int(v):
'''Decode a Base58 encoded string as an integer'''
v = scrub_input(v)
decimal = 0
for char in v:
decimal = decimal * 58 + alphabet.index(char)
return decimal
def b58decode(v):
'''Decode a Base58 encoded string'''
v = scrub_input(v)
origlen = len(v)
v = v.lstrip(alphabet[0:1])
newlen = len(v)
acc = b58decode_int(v)
result = []
while acc > 0:
acc, mod = divmod(acc, 256)
result.append(mod)
return (b'\0' * (origlen - newlen) + bseq(reversed(result)))
def b58encode_check(v):
'''Encode a string using Base58 with a 4 character checksum'''
digest = sha256(sha256(v).digest()).digest()
return b58encode(v + digest[:4])
def b58decode_check(v):
'''Decode and verify the checksum of a Base58 encoded string'''
result = b58decode(v)
result, check = result[:-4], result[-4:]
digest = sha256(sha256(result).digest()).digest()
if check != digest[:4]:
raise ValueError("Invalid checksum")
return result
def main():
'''Base58 encode or decode FILE, or standard input, to standard output.'''
import sys
import argparse
stdout = buffer(sys.stdout)
parser = argparse.ArgumentParser(description=main.__doc__)
parser.add_argument(
'file',
metavar='FILE',
nargs='?',
type=argparse.FileType('r'),
default='-')
parser.add_argument(
'-d', '--decode',
action='store_true',
help='decode data')
parser.add_argument(
'-c', '--check',
action='store_true',
help='append a checksum before encoding')
args = parser.parse_args()
fun = {
(False, False): b58encode,
(False, True): b58encode_check,
(True, False): b58decode,
(True, True): b58decode_check
}[(args.decode, args.check)]
data = buffer(args.file).read()
try:
result = fun(data)
except Exception as e:
sys.exit(e)
if not isinstance(result, bytes):
result = result.encode('ascii')
stdout.write(result)
if __name__ == '__main__':
main()

View File

@@ -1,9 +1,14 @@
# Python Implementation from https://ed25519.cr.yp.to/software.html # Python Implementation from https://ed25519.cr.yp.to/software.html
# Adjusted to Python 3 syntax by rome@ripple.com
# Public domain software. This is a reference implementation that # Public domain software. This is a reference implementation that
# does not include recommended speed & security optimizations. # does not include recommended speed & security optimizations.
import hashlib import hashlib
def bchr(i):
return bytes([i])
b = 256 b = 256
q = 2**255 - 19 q = 2**255 - 19
l = 2**252 + 27742317777372353535851937790883648493 l = 2**252 + 27742317777372353535851937790883648493
@@ -13,7 +18,7 @@ def H(m):
def expmod(b,e,m): def expmod(b,e,m):
if e == 0: return 1 if e == 0: return 1
t = expmod(b,e/2,m)**2 % m t = expmod(b,e//2,m)**2 % m
if e & 1: t = (t*b) % m if e & 1: t = (t*b) % m
return t return t
@@ -21,11 +26,11 @@ def inv(x):
return expmod(x,q-2,q) return expmod(x,q-2,q)
d = -121665 * inv(121666) d = -121665 * inv(121666)
I = expmod(2,(q-1)/4,q) I = expmod(2,(q-1)//4,q)
def xrecover(y): def xrecover(y):
xx = (y*y-1) * inv(d*y*y+1) xx = (y*y-1) * inv(d*y*y+1)
x = expmod(xx,(q+3)/8,q) x = expmod(xx,(q+3)//8,q)
if (x*x - xx) % q != 0: x = (x*I) % q if (x*x - xx) % q != 0: x = (x*I) % q
if x % 2 != 0: x = q-x if x % 2 != 0: x = q-x
return x return x
@@ -45,23 +50,23 @@ def edwards(P,Q):
def scalarmult(P,e): def scalarmult(P,e):
if e == 0: return [0,1] if e == 0: return [0,1]
Q = scalarmult(P,e/2) Q = scalarmult(P,e//2)
Q = edwards(Q,Q) Q = edwards(Q,Q)
if e & 1: Q = edwards(Q,P) if e & 1: Q = edwards(Q,P)
return Q return Q
def encodeint(y): def encodeint(y):
bits = [(y >> i) & 1 for i in range(b)] bits = [(y >> i) & 1 for i in range(b)]
return ''.join([chr(sum([bits[i * 8 + j] << j for j in range(8)])) for i in range(b/8)]) return b''.join([bchr(sum([bits[i * 8 + j] << j for j in range(8)])) for i in range(b//8)])
def encodepoint(P): def encodepoint(P):
x = P[0] x = P[0]
y = P[1] y = P[1]
bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1] bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1]
return ''.join([chr(sum([bits[i * 8 + j] << j for j in range(8)])) for i in range(b/8)]) return b''.join([bchr(sum([bits[i * 8 + j] << j for j in range(8)])) for i in range(b//8)])
def bit(h,i): def bit(h,i):
return (ord(h[i/8]) >> (i%8)) & 1 return (h[i//8] >> (i%8)) & 1
def publickey(sk): def publickey(sk):
h = H(sk) h = H(sk)
@@ -76,7 +81,7 @@ def Hint(m):
def signature(m,sk,pk): def signature(m,sk,pk):
h = H(sk) h = H(sk)
a = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2)) a = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2))
r = Hint(''.join([h[i] for i in range(b/8,b/4)]) + m) r = Hint(bytes([h[i] for i in range(b//8,b//4)]) + m)
R = scalarmult(B,r) R = scalarmult(B,r)
S = (r + Hint(encodepoint(R) + pk + m) * a) % l S = (r + Hint(encodepoint(R) + pk + m) * a) % l
return encodepoint(R) + encodeint(S) return encodepoint(R) + encodeint(S)
@@ -98,11 +103,11 @@ def decodepoint(s):
return P return P
def checkvalid(s,m,pk): def checkvalid(s,m,pk):
if len(s) != b/4: raise Exception("signature length is wrong") if len(s) != b//4: raise Exception("signature length is wrong")
if len(pk) != b/8: raise Exception("public-key length is wrong") if len(pk) != b//8: raise Exception("public-key length is wrong")
R = decodepoint(s[0:b/8]) R = decodepoint(s[0:b//8])
A = decodepoint(pk) A = decodepoint(pk)
S = decodeint(s[b/8:b/4]) S = decodeint(s[b//8:b//4])
h = Hint(encodepoint(R) + pk + m) h = Hint(encodepoint(R) + pk + m)
if scalarmult(B,S) != edwards(R,scalarmult(A,h)): if scalarmult(B,S) != edwards(R,scalarmult(A,h)):
raise Exception("signature does not pass verification") raise Exception("signature does not pass verification")

229
content/_code-samples/key-derivation/key-derivation.py Normal file → Executable file
View File

@@ -4,8 +4,8 @@
# Author: rome@ripple.com # Author: rome@ripple.com
# Copyright Ripple 2019 # Copyright Ripple 2019
import argparse
import sys import sys
from hashlib import sha512 from hashlib import sha512
if sys.version_info[0] < 3: if sys.version_info[0] < 3:
@@ -16,7 +16,19 @@ elif sys.version_info.minor < 6:
else: else:
from secrets import randbits from secrets import randbits
# import cryptography.hazmat.primitives.asymmetric.ec as ecc
# import cryptography.hazmat.backends.default_backend as default_backend
from fastecdsa import keys, curve
import ed25519 import ed25519
import RFC1751
import base58.base58 as base58
XRPL_SEED_PREFIX = b'\x21'
XRPL_PUBKEY_PREFIX = b'\x23'
ED_PREFIX = b'\xed'
SECP_MODULUS = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
def sha512half(buf): def sha512half(buf):
""" """
@@ -24,25 +36,216 @@ def sha512half(buf):
""" """
return sha512(buf).digest()[:32] return sha512(buf).digest()[:32]
def decode_input(in_string): class Seed:
"""
A 16-byte value used for key derivation.
"""
def __init__(self, in_string=None):
""" """
Decode a buffer input in one of the formats the XRPL supports and convert Decode a buffer input in one of the formats the XRPL supports and convert
it to a buffer representing the seed to use for key derivation. it to a buffer representing the 16-byte seed to use for key derivation.
Formats include: Formats include:
- XRPL base58 encoding - XRPL base58 encoding
- RFC-1751 - RFC-1751
- passphrase
- hexadecimal - hexadecimal
- passphrase
""" """
#TODO: split by format # Keys are lazy-derived later
return seed = seed.encode("UTF-8") self._secp256k1_pri = None
self._secp256k1_pub = None
self._ed25519_pri = None
self._ed25519_pub = None
def derive_ed25519_private_key(seed=None): if in_string is None:
# Generate a new seed randomly from OS-level RNG.
self.bytes = randbits(32*8).to_bytes(32, byteorder="big")
# Is it base58?
try:
decoded = base58.b58decode_check(in_string)
if decoded[:1] == XRPL_SEED_PREFIX and len(decoded) == 17:
self.bytes = decoded[1:]
return
else:
raise ValueError
except:
pass
# Maybe it's RFC1751?
try:
decoded = RFC1751.english_to_key(in_string)
if len(decoded) == 16:
self.bytes = decoded
return
else:
raise ValueError
except:
pass
# OK, how about hexadecimal?
try:
decoded = bytes.fromhex(in_string)
if len(decoded) == 16:
self.bytes = decoded
return
else:
raise ValueError
except ValueError as e:
pass
# Fallback: Guess it's a passphrase.
encoded = in_string.encode("UTF-8")
self.bytes = sha512(encoded).digest()[:16]
return
def encode_base58(self):
""" """
Takes a seed (buffer) and outputs a 32-byte private key (buffer). Returns a string representation of this seed as an XRPL base58 encoded
If seed is not provided, generates a seed at random using the OS-level string such as 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb'.
random number generator, which should be secure enough for this purpose.
""" """
if seed is None: return base58.b58encode_check(XRPL_SEED_PREFIX + self.bytes).decode()
seed = randbits(32*8).to_bytes(32, byteorder="big")
return sha512half(seed) def encode_hex(self):
"""
Returns a string representation of this seed as hexadecimal.
"""
return self.bytes.hex().upper()
def encode_rfc1751(self):
"""
Returns a string representation of this seed as an RFC-1751 encoded
passphrase.
"""
return RFC1751.key_to_english(self.bytes)
@property
def ed25519_private_key(self):
"""
Returns a 32-byte Ed25519 private key (bytes).
Saves the calculation for later calls.
"""
if self._ed25519_pri is None:
self._ed25519_pri = sha512half(self.bytes)
return self._ed25519_pri
@property
def ed25519_public_key(self):
"""
33-byte Ed25519 public key (bytes)—really a 32-byte key
prefixed with the byte 0xED to indicate that it's an Ed25519 key.
"""
if self._ed25519_pub is None:
self._ed25519_pub = (ED_PREFIX +
ed25519.publickey(self.ed25519_private_key))
return self._ed25519_pub
@property
def secp256k1_private_key(self):
"""
32-byte secp256k1 private key (bytes)
"""
if self._secp256k1_pri is None:
self.derive_secp256k1_master_keys()
return self._secp256k1_pri
@property
def secp256k1_public_key(self):
"""
33-byte secp256k1 public key (bytes)
"""
if self._secp256k1_pub is None:
self.derive_secp256k1_master_keys()
return self._secp256k1_pub
def derive_secp256k1_master_keys(self):
"""
Uses the XRPL's convoluted key derivation process to get the
secp256k1 master keypair for this seed value.
Saves the values to the object for later reference.
"""
root_pri_i = secp256k1_private_key_from(self.bytes)
# root_pk_i.to_bytes(32, byteorder="big", signed=False)
root_pub_point = keys.get_public_key(root_pri_i, curve.secp256k1)
root_pub_b = compress_secp256k1_public(root_pub_point)
fam_b = bytes(4) # Account families are unused; just 4 bytes of zeroes
inter_pk_i = secp256k1_private_key_from(root_pub_b+fam_b)
inter_pub_point = keys.get_public_key(inter_pk_i, curve.secp256k1)
# Private keys are just ints, so just add them mod the secp256k1 modulus
master_pri_i = (root_pri_i + inter_pk_i) % SECP_MODULUS
# Public keys are points, so the fastecdsa lib handles adding them
master_pub_point = root_pub_point + inter_pub_point
self._secp256k1_pri = master_pri_i.to_bytes(32, byteorder="big", signed=False)
self._secp256k1_pub = compress_secp256k1_public(master_pub_point)
# Saving the full key to make it easier to sign things later
self._secp256k1_full = master_pub_point
def encode_secp256k1_public_base58(self):
"""
Return the base58-encoded version of the secp256k1 public key.
"""
return base58.b58encode_check(XRPL_PUBKEY_PREFIX +
self.secp256k1_public_key).decode()
def secp256k1_private_key_from(seed):
"""
Calculate a valid secp256k1 private key by hashing a seed value;
if the result isn't a valid key, increment a seq value and try
again.
Returns a private key as a 32-byte integer.
"""
seq = 0
while True:
buf = seed + seq.to_bytes(4, byteorder="big", signed=False)
h = sha512half(buf)
h_i = int.from_bytes(h, byteorder="big", signed=False)
if h_i > SECP_MODULUS or h_i == 0:
# Not a valid secp256k1 key
seq += 1
continue
break
return h_i
def compress_secp256k1_public(point):
"""
Returns a 33-byte compressed key from an secp256k1 public key,
which is a point in the form (x,y) where both x and y are 32-byte ints
"""
if point.y % 2:
prefix = b'\x03'
else:
prefix = b'\x02'
return prefix + point.x.to_bytes(32, byteorder="big", signed=False)
if __name__ == "__main__":
p = argparse.ArgumentParser()
p.add_argument("secret", help="The seed to derive a key from, in hex, XRPL base58, or RFC-1751; or the passphrase to derive a seed and key from.")
args = p.parse_args()
seed = Seed(args.secret)
seed.derive_secp256k1_master_keys()
print("""
Seed (base58): {base58}
Seed (hex): {hex}
Seed (RFC-1751): {rfc1751}
Ed25519 Secret Key (hex): {ed25519_secret}
Ed25519 Public Key (hex): {ed25519_public}
secp256k1 Secret Key (hex): {secp256k1_secret}
secp256k1 Public Key (hex): {secp256k1_public}
secp256k1 Public Key (base58): {secp256k1_pub_base58}
""".format(
base58=seed.encode_base58(),
hex=seed.encode_hex(),
rfc1751=seed.encode_rfc1751(),
ed25519_secret=seed.ed25519_private_key.hex().upper(),
ed25519_public=seed.ed25519_public_key.hex().upper(),
secp256k1_secret=seed.secp256k1_private_key.hex().upper(),
secp256k1_public=seed.secp256k1_public_key.hex().upper(),
secp256k1_pub_base58=seed.encode_secp256k1_public_base58(),
))

View File

@@ -0,0 +1 @@
fastecdsa==1.7.4