mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-21 03:26:01 +00:00
Productionize manifest generator.
* Better output. * Better error checking and reporting. * Clearer names. * Python style.
This commit is contained in:
1
bin/manifest
Symbolic link
1
bin/manifest
Symbolic link
@@ -0,0 +1 @@
|
||||
python/Manifest.py
|
||||
7
bin/python/Manifest.py
Executable file
7
bin/python/Manifest.py
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
from ripple.util import Sign
|
||||
|
||||
result = Sign.run_command(sys.argv[1:])
|
||||
exit(0 if result else -1)
|
||||
51
bin/python/ripple/ledger/SField.py
Normal file
51
bin/python/ripple/ledger/SField.py
Normal file
@@ -0,0 +1,51 @@
|
||||
# Constants from ripple/protocol/SField.h
|
||||
|
||||
# special types
|
||||
STI_UNKNOWN = -2
|
||||
STI_DONE = -1
|
||||
STI_NOTPRESENT = 0
|
||||
|
||||
# # types (common)
|
||||
STI_UINT16 = 1
|
||||
STI_UINT32 = 2
|
||||
STI_UINT64 = 3
|
||||
STI_HASH128 = 4
|
||||
STI_HASH256 = 5
|
||||
STI_AMOUNT = 6
|
||||
STI_VL = 7
|
||||
STI_ACCOUNT = 8
|
||||
# 9-13 are reserved
|
||||
STI_OBJECT = 14
|
||||
STI_ARRAY = 15
|
||||
|
||||
# types (uncommon)
|
||||
STI_UINT8 = 16
|
||||
STI_HASH160 = 17
|
||||
STI_PATHSET = 18
|
||||
STI_VECTOR256 = 19
|
||||
|
||||
# high level types
|
||||
# cannot be serialized inside other types
|
||||
STI_TRANSACTION = 10001
|
||||
STI_LEDGERENTRY = 10002
|
||||
STI_VALIDATION = 10003
|
||||
STI_METADATA = 10004
|
||||
|
||||
def field_code(sti, name):
|
||||
if sti < 16:
|
||||
if name < 16:
|
||||
bytes = [(sti << 4) + name]
|
||||
else:
|
||||
bytes = [sti << 4, name]
|
||||
elif name < 16:
|
||||
bytes = [name, sti]
|
||||
else:
|
||||
bytes = [0, sti, name]
|
||||
return ''.join(chr(i) for i in bytes)
|
||||
|
||||
# Selected constants from SField.cpp
|
||||
|
||||
sfSequence = field_code(STI_UINT32, 4)
|
||||
sfPublicKey = field_code(STI_VL, 1)
|
||||
sfSigningPubKey = field_code(STI_VL, 3)
|
||||
sfSignature = field_code(STI_VL, 6)
|
||||
@@ -1,204 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import base64, os, random, sys
|
||||
import ed25519
|
||||
import ecdsa
|
||||
from hashlib import sha256
|
||||
from ripple.util.Encode import base58encode, base58decode
|
||||
|
||||
#-----------------------------------------------------------
|
||||
|
||||
#
|
||||
# RippleAddress library
|
||||
#
|
||||
# Human strings are base-58 with a
|
||||
# version prefix and a checksum suffix.
|
||||
#
|
||||
|
||||
VER_NONE = 1
|
||||
VER_NODE_PUBLIC = 28
|
||||
VER_NODE_PRIVATE = 32
|
||||
VER_ACCOUNT_ID = 0
|
||||
VER_ACCOUNT_PUBLIC = 35
|
||||
VER_ACCOUNT_PRIVATE = 34
|
||||
VER_FAMILY_GENERATOR = 41
|
||||
VER_FAMILY_SEED = 33
|
||||
|
||||
def ra_check(b):
|
||||
""".Returns a 4-byte checksum of a binary."""
|
||||
return sha256(sha256(b).digest()).digest()[:4]
|
||||
|
||||
def ra_encode(ver, b):
|
||||
"""Encodes a binary as human string."""
|
||||
b = chr(ver) + b
|
||||
return base58encode(b + ra_check(b))
|
||||
|
||||
def ra_decode(s):
|
||||
"""Decodes a human base-58 string into its version and binary."""
|
||||
b = base58decode(s)
|
||||
check = b[-4:]
|
||||
if (check != ra_check(b[:-4])):
|
||||
raise ValueError('bad checksum')
|
||||
ver = ord(b[0])
|
||||
b = b[1:-4]
|
||||
return ver, b
|
||||
|
||||
def field_code(kind, name):
|
||||
s = ""
|
||||
if (kind < 16):
|
||||
if (name < 16):
|
||||
s += chr((kind << 4) + name)
|
||||
else:
|
||||
s += chr(kind << 4)
|
||||
s += chr(name)
|
||||
elif (name < 16):
|
||||
s += chr(name)
|
||||
s += chr(kind)
|
||||
else:
|
||||
s += '\0'
|
||||
s += chr(kind)
|
||||
s += chr(name)
|
||||
return s
|
||||
|
||||
#-----------------------------------------------------------
|
||||
|
||||
STI_UINT32 = 2
|
||||
STI_VL = 7
|
||||
|
||||
sfSequence = field_code(STI_UINT32, 4)
|
||||
sfPublicKey = field_code(STI_VL, 1)
|
||||
sfSigningPubKey = field_code(STI_VL, 3)
|
||||
sfSignature = field_code(STI_VL, 6)
|
||||
|
||||
def to_bytes(n, length, endianess='big'):
|
||||
h = '%x' % n
|
||||
s = ('0'*(len(h) % 2) + h).zfill(length*2).decode('hex')
|
||||
return s if endianess == 'big' else s[::-1]
|
||||
|
||||
def lenvl(b):
|
||||
s = ''
|
||||
n = len(b)
|
||||
if (n < 192):
|
||||
s += chr(n)
|
||||
return s
|
||||
raise Exception('too long')
|
||||
|
||||
def strvl(b):
|
||||
return lenvl(b) + b
|
||||
|
||||
def str32(n):
|
||||
return to_bytes(n, 4)
|
||||
|
||||
#-----------------------------------------------------------
|
||||
|
||||
def gen_seed(urandom=os.urandom):
|
||||
seed = urandom(16)
|
||||
return seed
|
||||
|
||||
def gen_ed(urandom=os.urandom):
|
||||
sk = urandom(32)
|
||||
pk = ed25519.publickey(sk)
|
||||
return sk, pk
|
||||
|
||||
def gen_ec():
|
||||
# Can't be unit tested easily.
|
||||
sk = ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1)
|
||||
vk = sk.get_verifying_key()
|
||||
sig = sk.sign("message")
|
||||
assert vk.verify(sig, "message")
|
||||
return sk, vk
|
||||
|
||||
def gen_manifest(pk, vpk, seq):
|
||||
s = ""
|
||||
s += sfSequence
|
||||
s += str32(seq)
|
||||
s += sfPublicKey
|
||||
s += strvl(pk)
|
||||
s += sfSigningPubKey
|
||||
s += strvl(vpk)
|
||||
return s
|
||||
|
||||
def sign_manifest(m, sk, pk):
|
||||
s = "MAN\0"
|
||||
s += m
|
||||
sig = ed25519.signature(s, sk, pk)
|
||||
m += sfSignature + strvl(sig)
|
||||
return m
|
||||
|
||||
#-----------------------------------------------------------
|
||||
|
||||
def wrap(s, cols = 60):
|
||||
n = len(s)
|
||||
if (n <= cols):
|
||||
return s
|
||||
l = (n + cols - 1) / cols
|
||||
w = n / l
|
||||
while(l > 0):
|
||||
s = s[:w * l] + '\n' + s[w*l:]
|
||||
l -= 1
|
||||
return s
|
||||
|
||||
#-----------------------------------------------------------
|
||||
|
||||
if __name__ == '__main__':
|
||||
if (len(sys.argv) == 2 and sys.argv[1] == 'create'):
|
||||
sk, pk = gen_ed()
|
||||
apk = chr(0xed) + pk
|
||||
pkh = ra_encode(VER_NODE_PUBLIC, apk)
|
||||
skh = ra_encode(VER_NODE_PRIVATE, sk)
|
||||
v0, apk0 = ra_decode(pkh)
|
||||
assert v0 == VER_NODE_PUBLIC
|
||||
assert apk0 == apk
|
||||
print ("[validators]")
|
||||
print (pkh)
|
||||
print
|
||||
print ("[master_secret]")
|
||||
print (skh)
|
||||
exit()
|
||||
|
||||
if (len(sys.argv) == 3 and sys.argv[1] == 'check'):
|
||||
ver, b = ra_decode(sys.argv[2])
|
||||
print ('ver = ' + str(ver))
|
||||
print ('len = ' + str(len(b)))
|
||||
h = ra_encode(ver, b)
|
||||
print (h)
|
||||
assert h == sys.argv[2]
|
||||
exit()
|
||||
|
||||
if (len(sys.argv) == 5 and sys.argv[1] == 'sign'):
|
||||
seq = int(sys.argv[2])
|
||||
vpkh = sys.argv[3]
|
||||
skh = sys.argv[4]
|
||||
try:
|
||||
v, avpk = ra_decode(vpkh)
|
||||
if (v != VER_NODE_PUBLIC or
|
||||
len(avpk) != 33 or
|
||||
(ord(avpk[0]) != 2 and ord(avpk[0]) != 3)):
|
||||
raise ValueError()
|
||||
except ValueError:
|
||||
print ("Bad validator-public: " + vpkh)
|
||||
exit()
|
||||
try:
|
||||
v, sk = ra_decode(skh)
|
||||
if (v != VER_NODE_PRIVATE or
|
||||
len(sk) != 32):
|
||||
raise ValueError()
|
||||
except ValueError:
|
||||
print ("Bad master-secret: " + skh)
|
||||
pk = ed25519.publickey(sk)
|
||||
apk = chr(0xed) + pk
|
||||
m = gen_manifest(apk, avpk, seq)
|
||||
m1 = sign_manifest(m, sk, pk)
|
||||
print ('[validation_manifest]')
|
||||
print wrap(base64.b64encode(m1))
|
||||
exit()
|
||||
|
||||
print("""\
|
||||
Usage:
|
||||
create
|
||||
Create a new master public/secret key pair.
|
||||
|
||||
sign <sequence> <validator-public> <master-secret>
|
||||
Create a new signed manifest with the given sequence
|
||||
number, validator public key, and master secret key.
|
||||
""")
|
||||
@@ -1,85 +0,0 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from ripple import manifest
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
BINARY = 'nN9kfUnKTf7PpgLG'
|
||||
|
||||
class test_manifest(TestCase):
|
||||
def test_check(self):
|
||||
self.assertEquals(manifest.ra_check(BINARY), '\xaa\xaar\x9d')
|
||||
|
||||
def test_encode(self):
|
||||
self.assertEquals(
|
||||
manifest.ra_encode(manifest.VER_ACCOUNT_PUBLIC, BINARY),
|
||||
'sB49XwJgmdEZDo8LmYwki7FYkiaN7')
|
||||
|
||||
def test_decode(self):
|
||||
ver, b = manifest.ra_decode('sB49XwJgmdEZDo8LmYwki7FYkiaN7')
|
||||
self.assertEquals(ver, manifest.VER_ACCOUNT_PUBLIC)
|
||||
self.assertEquals(b, BINARY)
|
||||
|
||||
def test_field_code(self):
|
||||
self.assertEquals(manifest.field_code(manifest.STI_UINT32, 4), '$')
|
||||
self.assertEquals(manifest.field_code(manifest.STI_VL, 1), 'q')
|
||||
self.assertEquals(manifest.field_code(manifest.STI_VL, 3), 's')
|
||||
self.assertEquals(manifest.field_code(manifest.STI_VL, 6), 'v')
|
||||
|
||||
def test_to_bytes(self):
|
||||
self.assertEquals(
|
||||
manifest.to_bytes(12345, 16, endianess='big'),
|
||||
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0009')
|
||||
|
||||
self.assertEquals(
|
||||
manifest.to_bytes(12345, 16, endianess='not big'),
|
||||
'90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
|
||||
|
||||
def test_strvl(self):
|
||||
self.assertEquals(manifest.strvl(BINARY), '\x10nN9kfUnKTf7PpgLG')
|
||||
|
||||
def test_to_str32(self):
|
||||
self.assertEquals(manifest.str32(12345), '\x00\x0009')
|
||||
|
||||
def urandom(self, bytes):
|
||||
return '\5' * bytes
|
||||
|
||||
def test_gen_seed(self):
|
||||
self.assertEquals(manifest.gen_seed(self.urandom),
|
||||
'\5\5\5\5\5\5\5\5\5\5\5\5\5\5\5\5')
|
||||
|
||||
def test_gen_ed(self):
|
||||
private, public = manifest.gen_ed(self.urandom)
|
||||
self.assertEquals(private,
|
||||
'\5\5\5\5\5\5\5\5\5\5\5\5\5\5\5\5'
|
||||
'\5\5\5\5\5\5\5\5\5\5\5\5\5\5\5\5')
|
||||
self.assertEquals(public,
|
||||
'nz\x1c\xdd)\xb0\xb7\x8f\xd1:\xf4\xc5Y\x8f\xef\xf4'
|
||||
'\xef*\x97\x16n<\xa6\xf2\xe4\xfb\xfc\xcd\x80P[\xf1')
|
||||
|
||||
def test_gen_manifest(self):
|
||||
_, pk = manifest.gen_ed(self.urandom)
|
||||
m = manifest.gen_manifest(pk, 'verify', 12345)
|
||||
self.assertEquals(
|
||||
m, '$\x00\x0009q nz\x1c\xdd)\xb0\xb7\x8f\xd1:\xf4\xc5Y\x8f\xef\xf4'
|
||||
'\xef*\x97\x16n<\xa6\xf2\xe4\xfb\xfc\xcd\x80P[\xf1s\x06verify')
|
||||
|
||||
def test_sign_manifest(self):
|
||||
sk, pk = manifest.gen_ed(self.urandom)
|
||||
s = manifest.sign_manifest('manifest', sk, pk)
|
||||
self.assertEquals(
|
||||
s, 'manifestv@\xe5\x84\xbe\xc4\x80N\xa0v"\xb2\x80A\x88\x06\xc0'
|
||||
'\xd2\xbae\x92\x89\xa8\'!\xdd\x00\x88\x06s\xe0\xf74\xe3Yg\xad{$'
|
||||
'\x17\xd3\x99\xaa\x16\xb0\xeaZ\xd7]\r\xb3\xdc\x1b\x8f\xc1Z\xdfHU'
|
||||
'\xb5\x92\xac\x82jI\x02')
|
||||
|
||||
def test_wrap(self):
|
||||
wrap = lambda s: manifest.wrap(s, 5)
|
||||
self.assertEquals(wrap(''), '')
|
||||
self.assertEquals(wrap('12345'), '12345')
|
||||
self.assertEquals(wrap('123456'), '123\n456\n')
|
||||
self.assertEquals(wrap('12345678'), '1234\n5678\n')
|
||||
self.assertEquals(wrap('1234567890'), '12345\n67890\n')
|
||||
self.assertEquals(wrap('12345678901'), '123\n456\n789\n01')
|
||||
# TOD: there seems to be a carriage return added randomly
|
||||
# to the last character.
|
||||
94
bin/python/ripple/util/Base58.py
Normal file
94
bin/python/ripple/util/Base58.py
Normal file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from hashlib import sha256
|
||||
|
||||
#
|
||||
# Human strings are base-58 with a
|
||||
# version prefix and a checksum suffix.
|
||||
#
|
||||
# Copied from ripple/protocol/RippleAddress.h
|
||||
#
|
||||
|
||||
VER_NONE = 1
|
||||
VER_NODE_PUBLIC = 28
|
||||
VER_NODE_PRIVATE = 32
|
||||
VER_ACCOUNT_ID = 0
|
||||
VER_ACCOUNT_PUBLIC = 35
|
||||
VER_ACCOUNT_PRIVATE = 34
|
||||
VER_FAMILY_GENERATOR = 41
|
||||
VER_FAMILY_SEED = 33
|
||||
|
||||
ALPHABET = 'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz'
|
||||
|
||||
VERSION_NAME = {
|
||||
VER_NONE: 'VER_NONE',
|
||||
VER_NODE_PUBLIC: 'VER_NODE_PUBLIC',
|
||||
VER_NODE_PRIVATE: 'VER_NODE_PRIVATE',
|
||||
VER_ACCOUNT_ID: 'VER_ACCOUNT_ID',
|
||||
VER_ACCOUNT_PUBLIC: 'VER_ACCOUNT_PUBLIC',
|
||||
VER_ACCOUNT_PRIVATE: 'VER_ACCOUNT_PRIVATE',
|
||||
VER_FAMILY_GENERATOR: 'VER_FAMILY_GENERATOR',
|
||||
VER_FAMILY_SEED: 'VER_FAMILY_SEED'
|
||||
}
|
||||
|
||||
class Alphabet(object):
|
||||
def __init__(self, radix, digit_to_char, char_to_digit):
|
||||
self.radix = radix
|
||||
self.digit_to_char = digit_to_char
|
||||
self.char_to_digit = char_to_digit
|
||||
|
||||
def transcode_from(self, s, source_alphabet):
|
||||
n, zero_count = source_alphabet._digits_to_number(s)
|
||||
digits = []
|
||||
while n > 0:
|
||||
n, digit = divmod(n, self.radix)
|
||||
digits.append(self.digit_to_char(digit))
|
||||
|
||||
s = ''.join(digits)
|
||||
return self.digit_to_char(0) * zero_count + s[::-1]
|
||||
|
||||
def _digits_to_number(self, digits):
|
||||
stripped = digits.lstrip(self.digit_to_char(0))
|
||||
n = 0
|
||||
for d in stripped:
|
||||
n *= self.radix
|
||||
n += self.char_to_digit(d)
|
||||
return n, len(digits) - len(stripped)
|
||||
|
||||
_INVERSE_INDEX = dict((c, i) for (i, c) in enumerate(ALPHABET))
|
||||
|
||||
# In base 58 encoding, the digits come from the ALPHABET string.
|
||||
BASE58 = Alphabet(len(ALPHABET), ALPHABET.__getitem__, _INVERSE_INDEX.get)
|
||||
|
||||
# In base 256 encoding, each digit is just a character between 0 and 255.
|
||||
BASE256 = Alphabet(256, chr, ord)
|
||||
|
||||
def encode(b):
|
||||
return BASE58.transcode_from(b, BASE256)
|
||||
|
||||
def decode(b):
|
||||
return BASE256.transcode_from(b, BASE58)
|
||||
|
||||
def checksum(b):
|
||||
"""Returns a 4-byte checksum of a binary."""
|
||||
return sha256(sha256(b).digest()).digest()[:4]
|
||||
|
||||
def encode_version(ver, b):
|
||||
"""Encodes a version encoding and a binary as human string."""
|
||||
b = chr(ver) + b
|
||||
return encode(b + checksum(b))
|
||||
|
||||
def decode_version(s):
|
||||
"""Decodes a human base-58 string into its version encoding and binary."""
|
||||
b = decode(s)
|
||||
body, check = b[:-4], b[-4:]
|
||||
assert check == checksum(body), ('Bad checksum for', s)
|
||||
return ord(body[0]), body[1:]
|
||||
|
||||
def version_name(ver):
|
||||
return VERSION_NAME.get(ver) or ('(unknown version %s)' % ver)
|
||||
|
||||
def check_version(version, expected):
|
||||
if version != expected:
|
||||
raise ValueError('Expected version %s but got %s' % (
|
||||
version_name(version), version_name(expected)))
|
||||
164
bin/python/ripple/util/Sign.py
Normal file
164
bin/python/ripple/util/Sign.py
Normal file
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import base64, os, random, struct, sys
|
||||
import ed25519
|
||||
import ecdsa
|
||||
from ripple.util import Base58
|
||||
from ripple.ledger import SField
|
||||
|
||||
ED25519_BYTE = chr(0xed)
|
||||
WRAP_COLUMNS = 60
|
||||
|
||||
USAGE = """\
|
||||
Usage:
|
||||
create
|
||||
Create a new master public/secret key pair.
|
||||
|
||||
check <key>
|
||||
Check an existing key for validity.
|
||||
|
||||
sign <sequence> <validator-public> <master-secret>
|
||||
Create a new signed manifest with the given sequence
|
||||
number, validator public key, and master secret key.
|
||||
"""
|
||||
|
||||
def prepend_length_byte(b):
|
||||
assert len(b) <= 192, 'Too long'
|
||||
return chr(len(b)) + b
|
||||
|
||||
def to_int32(i):
|
||||
return struct.pack('>I', i)
|
||||
|
||||
#-----------------------------------------------------------
|
||||
|
||||
def make_seed(urandom=os.urandom):
|
||||
# This is not used.
|
||||
return urandom(16)
|
||||
|
||||
def make_ed25519_keypair(urandom=os.urandom):
|
||||
private_key = urandom(32)
|
||||
return private_key, ed25519.publickey(private_key)
|
||||
|
||||
def make_ecdsa_keypair():
|
||||
# This is not used.
|
||||
private_key = ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1)
|
||||
# Can't be unit tested easily - need a mock for ecdsa.
|
||||
vk = private_key.get_verifying_key()
|
||||
sig = private_key.sign('message')
|
||||
assert vk.verify(sig, 'message')
|
||||
return private_key, vk
|
||||
|
||||
def make_seed_from_passphrase(passphrase):
|
||||
# For convenience, like say testing against rippled we can hash a passphrase
|
||||
# to get the seed. validation_create (Josh may have killed it by now) takes
|
||||
# an optional arg, which can be a base58 encoded seed, or a passphrase.
|
||||
return hashlib.sha512(passphrase).digest()[:16]
|
||||
|
||||
def make_manifest(public_key, validator_public_key, seq):
|
||||
return ''.join([
|
||||
SField.sfSequence,
|
||||
to_int32(seq),
|
||||
SField.sfPublicKey, # Master public key.
|
||||
prepend_length_byte(public_key),
|
||||
SField.sfSigningPubKey, # Ephemeral public key.
|
||||
prepend_length_byte(validator_public_key)])
|
||||
|
||||
def sign_manifest(manifest, private_key, public_key):
|
||||
sig = ed25519.signature('MAN\0' + manifest, private_key, public_key)
|
||||
return manifest + SField.sfSignature + prepend_length_byte(sig)
|
||||
|
||||
def wrap(s, cols=WRAP_COLUMNS):
|
||||
if s:
|
||||
size = max((len(s) + cols - 1) / cols, 1)
|
||||
w = len(s) / size
|
||||
s = '\n'.join(s[i:i + w] for i in range(0, len(s), w))
|
||||
return s
|
||||
|
||||
def create_ed_keys(urandom=os.urandom):
|
||||
private_key, public_key = make_ed25519_keypair(urandom)
|
||||
public_key_human = Base58.encode_version(
|
||||
Base58.VER_NODE_PUBLIC, ED25519_BYTE + public_key)
|
||||
private_key_human = Base58.encode_version(
|
||||
Base58.VER_NODE_PRIVATE, private_key)
|
||||
return public_key_human, private_key_human
|
||||
|
||||
def check_validator_public(v, validator_public_key):
|
||||
Base58.check_version(v, Base58.VER_NODE_PUBLIC)
|
||||
if len(validator_public_key) != 33:
|
||||
raise ValueError('Validator key should be length 33, is %s' %
|
||||
len(validator_public_key))
|
||||
b = ord(validator_public_key[0])
|
||||
if b not in (2, 3):
|
||||
raise ValueError('First validator key byte must be 2 or 3, is %d' % b)
|
||||
|
||||
def check_master_secret(v, private_key):
|
||||
Base58.check_version(v, Base58.VER_NODE_PRIVATE)
|
||||
if len(private_key) != 32:
|
||||
raise ValueError('Length of master secret should be 32, is %s' %
|
||||
len(private_key))
|
||||
|
||||
|
||||
def get_signature(seq, validator_public_key_human, private_key_human):
|
||||
v, validator_public_key = Base58.decode_version(validator_public_key_human)
|
||||
check_validator_public(v, validator_public_key)
|
||||
|
||||
v, private_key = Base58.decode_version(private_key_human)
|
||||
check_master_secret(v, private_key)
|
||||
|
||||
pk = ed25519.publickey(private_key)
|
||||
apk = ED25519_BYTE + pk
|
||||
m = make_manifest(apk, validator_public_key, seq)
|
||||
m1 = sign_manifest(m, private_key, pk)
|
||||
return base64.b64encode(m1)
|
||||
|
||||
|
||||
# Testable versions of functions.
|
||||
def perform_create(urandom=os.urandom, print=print):
|
||||
public, private = create_ed_keys(urandom)
|
||||
print('[validator_keys]', public, '', '[master_secret]', private, sep='\n')
|
||||
|
||||
def perform_check(s, print=print):
|
||||
version, b = Base58.decode_version(s)
|
||||
print('version = ' + Base58.version_name(version))
|
||||
print('decoded length = ' + str(len(b)))
|
||||
assert Base58.encode_version(version, b) == s
|
||||
|
||||
def perform_sign(
|
||||
seq, validator_public_key_human, private_key_human, print=print):
|
||||
print('[validation_manifest]')
|
||||
print(wrap(get_signature(
|
||||
int(seq), validator_public_key_human, private_key_human)))
|
||||
|
||||
# Externally visible versions of functions.
|
||||
def create():
|
||||
perform_create()
|
||||
|
||||
def check(s):
|
||||
perform_check(s)
|
||||
|
||||
def sign(seq, validator_public_key_human, private_key_human):
|
||||
perform_sign(seq, validator_public_key_human, private_key_human)
|
||||
|
||||
|
||||
def usage(*errors):
|
||||
if errors:
|
||||
print(*errors)
|
||||
print(USAGE)
|
||||
return not errors
|
||||
|
||||
_COMMANDS = dict((f.__name__, f) for f in (create, check, sign))
|
||||
|
||||
def run_command(args):
|
||||
if not args:
|
||||
return usage()
|
||||
name = args[0]
|
||||
command = _COMMANDS.get(name)
|
||||
if not command:
|
||||
return usage('No such command:', command)
|
||||
try:
|
||||
command(*args[1:])
|
||||
except TypeError:
|
||||
return usage('Wrong number of arguments for:', command)
|
||||
return True
|
||||
47
bin/python/ripple/util/test_Base58.py
Normal file
47
bin/python/ripple/util/test_Base58.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from ripple.util import Base58
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
BINARY = 'nN9kfUnKTf7PpgLG'
|
||||
|
||||
class test_Base58(TestCase):
|
||||
def run_test(self, before, after):
|
||||
self.assertEquals(Base58.decode(before), after)
|
||||
self.assertEquals(Base58.encode(after), before)
|
||||
|
||||
def test_trivial(self):
|
||||
self.run_test('', '')
|
||||
|
||||
def test_zeroes(self):
|
||||
for before, after in (('', ''), ('abc', 'I\x8b')):
|
||||
for i in range(1, 257):
|
||||
self.run_test('r' * i + before, '\0' * i + after)
|
||||
|
||||
def test_single_digits(self):
|
||||
for i, c in enumerate(Base58.ALPHABET):
|
||||
self.run_test(c, chr(i))
|
||||
|
||||
def test_various(self):
|
||||
# Test three random numbers.
|
||||
self.run_test('88Mw', '\x88L\xed')
|
||||
self.run_test(
|
||||
'nN9kfUnKTf7PpgLG', '\x03\xdc\x9co\xdea\xefn\xd3\xb8\xe2\xc1')
|
||||
self.run_test(
|
||||
'zzWWb4C5p6kNrVa4fEBoZpZKd3XQLXch7QJbLCuLdoS1CWr8qdAZHEmwMiJy8Hwp',
|
||||
'xN\x82\xfcQ\x1f\xb3~\xdf\xc7\xb37#\xc6~A\xe9\xf6-\x1f\xcb"\xfab'
|
||||
'(\'\xccv\x9e\x85\xc3\xd1\x19\x941{\x8et\xfbS}\x86.k\x07\xb5\xb3')
|
||||
|
||||
def test_check(self):
|
||||
self.assertEquals(Base58.checksum(BINARY), '\xaa\xaar\x9d')
|
||||
|
||||
def test_encode(self):
|
||||
self.assertEquals(
|
||||
Base58.encode_version(Base58.VER_ACCOUNT_PUBLIC, BINARY),
|
||||
'sB49XwJgmdEZDo8LmYwki7FYkiaN7')
|
||||
|
||||
def test_decode(self):
|
||||
ver, b = Base58.decode_version('sB49XwJgmdEZDo8LmYwki7FYkiaN7')
|
||||
self.assertEquals(ver, Base58.VER_ACCOUNT_PUBLIC)
|
||||
self.assertEquals(b, BINARY)
|
||||
127
bin/python/ripple/util/test_Sign.py
Normal file
127
bin/python/ripple/util/test_Sign.py
Normal file
@@ -0,0 +1,127 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from ripple.util import Sign
|
||||
from ripple.util import Base58
|
||||
from ripple.ledger import SField
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
BINARY = 'nN9kfUnKTf7PpgLG'
|
||||
|
||||
class test_Sign(TestCase):
|
||||
SEQUENCE = 23
|
||||
SIGNATURE = (
|
||||
'JAAAABdxIe2DIKUZd9jDjKikknxnDfWCHkSXYZReFenvsmoVCdIw6nMhAnZ2dnZ2'
|
||||
'dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dkDOjlWtQSvRTjuwe+4iNusg0sJM'
|
||||
'zqkBJwDz30b2SkxZ7Fte/Vx4htM/kkfUfJCaxmxE5N4dHSKuiO9iDHsktqIA')
|
||||
|
||||
def setUp(self):
|
||||
self.results = []
|
||||
|
||||
def print(self, *args, **kwds):
|
||||
self.results.append([list(args), kwds])
|
||||
|
||||
def test_field_code(self):
|
||||
self.assertEquals(SField.field_code(SField.STI_UINT32, 4), '$')
|
||||
self.assertEquals(SField.field_code(SField.STI_VL, 1), 'q')
|
||||
self.assertEquals(SField.field_code(SField.STI_VL, 3), 's')
|
||||
self.assertEquals(SField.field_code(SField.STI_VL, 6), 'v')
|
||||
|
||||
def test_strvl(self):
|
||||
self.assertEquals(Sign.prepend_length_byte(BINARY),
|
||||
'\x10nN9kfUnKTf7PpgLG')
|
||||
|
||||
def urandom(self, bytes):
|
||||
return '\5' * bytes
|
||||
|
||||
def test_make_seed(self):
|
||||
self.assertEquals(Sign.make_seed(self.urandom),
|
||||
'\5\5\5\5\5\5\5\5\5\5\5\5\5\5\5\5')
|
||||
|
||||
def test_make_ed(self):
|
||||
private, public = Sign.make_ed25519_keypair(self.urandom)
|
||||
self.assertEquals(private,
|
||||
'\5\5\5\5\5\5\5\5\5\5\5\5\5\5\5\5'
|
||||
'\5\5\5\5\5\5\5\5\5\5\5\5\5\5\5\5')
|
||||
self.assertEquals(public,
|
||||
'nz\x1c\xdd)\xb0\xb7\x8f\xd1:\xf4\xc5Y\x8f\xef\xf4'
|
||||
'\xef*\x97\x16n<\xa6\xf2\xe4\xfb\xfc\xcd\x80P[\xf1')
|
||||
|
||||
def test_make_manifest(self):
|
||||
_, pk = Sign.make_ed25519_keypair(self.urandom)
|
||||
m = Sign.make_manifest(pk, 'verify', 12345)
|
||||
self.assertEquals(
|
||||
m, '$\x00\x0009q nz\x1c\xdd)\xb0\xb7\x8f\xd1:\xf4\xc5Y\x8f\xef\xf4'
|
||||
'\xef*\x97\x16n<\xa6\xf2\xe4\xfb\xfc\xcd\x80P[\xf1s\x06verify')
|
||||
|
||||
def test_sign_manifest(self):
|
||||
sk, pk = Sign.make_ed25519_keypair(self.urandom)
|
||||
s = Sign.sign_manifest('manifest', sk, pk)
|
||||
self.assertEquals(
|
||||
s, 'manifestv@\xe5\x84\xbe\xc4\x80N\xa0v"\xb2\x80A\x88\x06\xc0'
|
||||
'\xd2\xbae\x92\x89\xa8\'!\xdd\x00\x88\x06s\xe0\xf74\xe3Yg\xad{$'
|
||||
'\x17\xd3\x99\xaa\x16\xb0\xeaZ\xd7]\r\xb3\xdc\x1b\x8f\xc1Z\xdfHU'
|
||||
'\xb5\x92\xac\x82jI\x02')
|
||||
|
||||
def test_wrap(self):
|
||||
wrap = lambda s: Sign.wrap(s, 5)
|
||||
self.assertEquals(wrap(''), '')
|
||||
self.assertEquals(wrap('12345'), '12345')
|
||||
self.assertEquals(wrap('123456'), '123\n456')
|
||||
self.assertEquals(wrap('12345678'), '1234\n5678')
|
||||
self.assertEquals(wrap('1234567890'), '12345\n67890')
|
||||
self.assertEquals(wrap('12345678901'), '123\n456\n789\n01')
|
||||
|
||||
def test_create_ed_keys(self):
|
||||
pkh, skh = Sign.create_ed_keys(self.urandom)
|
||||
self.assertEquals(
|
||||
pkh, 'nHUUaKHpxyRP4TZZ79tTpXuTpoM8pRNs5crZpGVA5jdrjib5easY')
|
||||
self.assertEquals(
|
||||
skh, 'pnEp13Zu7xTeKQVQ2RZVaUraE9GXKqFtnXQVUFKXbTE6wsP4wne')
|
||||
|
||||
def get_test_keypair(self):
|
||||
public = (Base58.VER_NODE_PUBLIC, '\x02' + (32 * 'v'))
|
||||
private = (Base58.VER_NODE_PRIVATE, 32 * 'k')
|
||||
|
||||
Sign.check_validator_public(*public)
|
||||
Sign.check_master_secret(*private)
|
||||
|
||||
return (Base58.encode_version(*public), Base58.encode_version(*private))
|
||||
|
||||
def test_get_signature(self):
|
||||
signature = Sign.get_signature(self.SEQUENCE, *self.get_test_keypair())
|
||||
self.assertEquals(
|
||||
signature,
|
||||
'JAAAABdxIe2DIKUZd9jDjKikknxnDfWCHkSXYZReFenvsmoVCdIw6nMhAnZ2dnZ2'
|
||||
'dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dkDOjlWtQSvRTjuwe+4iNusg0sJM'
|
||||
'zqkBJwDz30b2SkxZ7Fte/Vx4htM/kkfUfJCaxmxE5N4dHSKuiO9iDHsktqIA')
|
||||
|
||||
def test_check(self):
|
||||
public = Base58.encode_version(Base58.VER_NODE_PRIVATE, 32 * 'k')
|
||||
Sign.perform_check(public, self.print)
|
||||
self.assertEquals(self.results,
|
||||
[[['version = VER_NODE_PRIVATE'], {}],
|
||||
[['decoded length = 32'], {}]])
|
||||
|
||||
def test_create(self):
|
||||
Sign.perform_create(self.urandom, self.print)
|
||||
self.assertEquals(
|
||||
self.results,
|
||||
[[['[validator_keys]',
|
||||
'nHUUaKHpxyRP4TZZ79tTpXuTpoM8pRNs5crZpGVA5jdrjib5easY',
|
||||
'',
|
||||
'[master_secret]',
|
||||
'pnEp13Zu7xTeKQVQ2RZVaUraE9GXKqFtnXQVUFKXbTE6wsP4wne'],
|
||||
{'sep': '\n'}]])
|
||||
|
||||
def test_sign(self):
|
||||
public, private = self.get_test_keypair()
|
||||
Sign.perform_sign(self.SEQUENCE, public, private, print=self.print)
|
||||
self.assertEquals(
|
||||
self.results,
|
||||
[[['[validation_manifest]'], {}],
|
||||
[['JAAAABdxIe2DIKUZd9jDjKikknxnDfWCHkSXYZReFenvsmo\n'
|
||||
'VCdIw6nMhAnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dn\n'
|
||||
'Z2dnZ2dkDOjlWtQSvRTjuwe+4iNusg0sJMzqkBJwDz30b2S\n'
|
||||
'kxZ7Fte/Vx4htM/kkfUfJCaxmxE5N4dHSKuiO9iDHsktqIA'],
|
||||
{}]])
|
||||
Reference in New Issue
Block a user