diff --git a/bin/manifest b/bin/manifest new file mode 120000 index 0000000000..c4ffa3bf5c --- /dev/null +++ b/bin/manifest @@ -0,0 +1 @@ +python/Manifest.py \ No newline at end of file diff --git a/bin/python/Manifest.py b/bin/python/Manifest.py new file mode 100755 index 0000000000..31f3f0ce16 --- /dev/null +++ b/bin/python/Manifest.py @@ -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) diff --git a/bin/python/ripple/ledger/SField.py b/bin/python/ripple/ledger/SField.py new file mode 100644 index 0000000000..1dd0fd20ea --- /dev/null +++ b/bin/python/ripple/ledger/SField.py @@ -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) diff --git a/bin/python/ripple/manifest.py b/bin/python/ripple/manifest.py deleted file mode 100644 index 0b7a37d924..0000000000 --- a/bin/python/ripple/manifest.py +++ /dev/null @@ -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 - Create a new signed manifest with the given sequence - number, validator public key, and master secret key. - """) diff --git a/bin/python/ripple/test_manifest.py b/bin/python/ripple/test_manifest.py deleted file mode 100644 index 740afd4fe9..0000000000 --- a/bin/python/ripple/test_manifest.py +++ /dev/null @@ -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. diff --git a/bin/python/ripple/util/Base58.py b/bin/python/ripple/util/Base58.py new file mode 100644 index 0000000000..f7e392416f --- /dev/null +++ b/bin/python/ripple/util/Base58.py @@ -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))) diff --git a/bin/python/ripple/util/Sign.py b/bin/python/ripple/util/Sign.py new file mode 100644 index 0000000000..ab4adf023c --- /dev/null +++ b/bin/python/ripple/util/Sign.py @@ -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 + Check an existing key for validity. + + sign + 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 diff --git a/bin/python/ripple/util/test_Base58.py b/bin/python/ripple/util/test_Base58.py new file mode 100644 index 0000000000..40bcd1b9c6 --- /dev/null +++ b/bin/python/ripple/util/test_Base58.py @@ -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) diff --git a/bin/python/ripple/util/test_Sign.py b/bin/python/ripple/util/test_Sign.py new file mode 100644 index 0000000000..e44618b757 --- /dev/null +++ b/bin/python/ripple/util/test_Sign.py @@ -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'], + {}]])