Mostly finished with wot command processing
This commit is contained in:
parent
d915a2aaed
commit
a517ad3aee
@ -1,13 +1,8 @@
|
|||||||
PyNaCl==1.5.0
|
PyNaCl==1.5.0
|
||||||
PySocks==1.7.1
|
|
||||||
stem==1.8.0
|
|
||||||
unpaddedbase32==0.2.0
|
|
||||||
niceware==0.2.1
|
|
||||||
psutil==5.9.1
|
psutil==5.9.1
|
||||||
filenuke==0.0.0
|
filenuke==0.0.0
|
||||||
ujson==5.4.0
|
ujson==5.4.0
|
||||||
cffi==1.15.1
|
cffi==1.15.1
|
||||||
onionrblocks==7.0.0
|
onionrblocks==7.0.0
|
||||||
ordered-set==4.1.0
|
ordered-set==4.1.0
|
||||||
cherrypy==18.8.0
|
|
||||||
json-rpc==1.13.0
|
json-rpc==1.13.0
|
@ -1,21 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
"""Generate a 16 word passphase with 256 bits of entropy.
|
|
||||||
|
|
||||||
Specify true to reduce to 128 bits"""
|
|
||||||
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import niceware
|
|
||||||
|
|
||||||
byte_count = 32 # 256 bits of entropy with niceware
|
|
||||||
|
|
||||||
arg = False
|
|
||||||
try:
|
|
||||||
arg = sys.argv[1].lower()
|
|
||||||
if arg == 'true':
|
|
||||||
byte_count = 16
|
|
||||||
except IndexError: pass
|
|
||||||
|
|
||||||
print(' '.join(niceware.generate_passphrase(byte_count)))
|
|
@ -1,95 +0,0 @@
|
|||||||
"""Onionr - Private P2P Communication.
|
|
||||||
|
|
||||||
This module defines user ID-related CLI commands
|
|
||||||
"""
|
|
||||||
import sys
|
|
||||||
import getpass
|
|
||||||
|
|
||||||
import unpaddedbase32
|
|
||||||
import niceware
|
|
||||||
|
|
||||||
import logger
|
|
||||||
import onionrexceptions
|
|
||||||
from onionrutils import stringvalidators, bytesconverter
|
|
||||||
import config
|
|
||||||
import keymanager
|
|
||||||
import onionrcrypto
|
|
||||||
import onionrvalues
|
|
||||||
"""
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
DETERMINISTIC_REQUIREMENT = onionrvalues.PASSWORD_LENGTH
|
|
||||||
|
|
||||||
|
|
||||||
def add_ID():
|
|
||||||
"""Command to create a new user ID key pair."""
|
|
||||||
key_manager = keymanager.KeyManager()
|
|
||||||
pw = ""
|
|
||||||
try:
|
|
||||||
sys.argv[2] # pylint: disable=W0104
|
|
||||||
if not sys.argv[2].lower() == 'true':
|
|
||||||
raise ValueError
|
|
||||||
except (IndexError, ValueError):
|
|
||||||
newID = key_manager.addKey()[0]
|
|
||||||
else:
|
|
||||||
pw = "-".join(niceware.generate_passphrase(32))
|
|
||||||
newID, privKey = onionrcrypto.generate_deterministic(pw)
|
|
||||||
try:
|
|
||||||
key_manager.addKey(pubKey=newID,
|
|
||||||
privKey=privKey)
|
|
||||||
except ValueError:
|
|
||||||
logger.error(
|
|
||||||
'That ID is already available, you can change to it ' +
|
|
||||||
'with the change-id command.', terminal=True)
|
|
||||||
return
|
|
||||||
if pw:
|
|
||||||
print("Phrase to restore ID:", pw)
|
|
||||||
logger.info('Added ID: %s' %
|
|
||||||
(bytesconverter.bytes_to_str(newID.replace('=', '')),), terminal=True)
|
|
||||||
|
|
||||||
|
|
||||||
add_ID.onionr_help = "If the first argument is true, " # type: ignore
|
|
||||||
add_ID.onionr_help += "Onionr will show a deterministic " # type: ignore
|
|
||||||
add_ID.onionr_help += "generation prompt. Otherwise it will " # type: ignore
|
|
||||||
add_ID.onionr_help += "generate & save a new random key pair." # type: ignore
|
|
||||||
|
|
||||||
|
|
||||||
def change_ID():
|
|
||||||
"""Command to change active ID from argv or stdin."""
|
|
||||||
key_manager = keymanager.KeyManager()
|
|
||||||
try:
|
|
||||||
key = sys.argv[2]
|
|
||||||
key = unpaddedbase32.repad(key.encode()).decode()
|
|
||||||
except IndexError:
|
|
||||||
logger.warn('Specify pubkey to use', terminal=True)
|
|
||||||
else:
|
|
||||||
if stringvalidators.validate_pub_key(key):
|
|
||||||
key_list = key_manager.getPubkeyList()
|
|
||||||
if key in key_list or key.replace('=', '') in key_list:
|
|
||||||
config.set('general.public_key', key)
|
|
||||||
config.save()
|
|
||||||
logger.info('Set active key to: %s' % (key,), terminal=True)
|
|
||||||
logger.info('Restart Onionr if it is running.', terminal=True)
|
|
||||||
else:
|
|
||||||
logger.warn('That key does not exist', terminal=True)
|
|
||||||
else:
|
|
||||||
logger.warn('Invalid key %s' % (key,), terminal=True)
|
|
||||||
|
|
||||||
|
|
||||||
change_ID.onionr_help = "<pubkey>: Switches Onionr to " # type: ignore
|
|
||||||
change_ID.onionr_help += "use a different user ID key. " # type: ignore
|
|
||||||
change_ID.onionr_help += "You should immediately restart " # type: ignore
|
|
||||||
change_ID.onionr_help += "Onionr if it is running." # type: ignore
|
|
@ -1,42 +0,0 @@
|
|||||||
"""
|
|
||||||
Onionr - Private P2P Communication
|
|
||||||
|
|
||||||
validate various string data types
|
|
||||||
"""
|
|
||||||
import base64
|
|
||||||
import string
|
|
||||||
import unpaddedbase32, nacl.signing, nacl.encoding
|
|
||||||
from onionrutils import bytesconverter
|
|
||||||
"""
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def validate_pub_key(key):
|
|
||||||
"""Validate if a string is a valid base32 encoded Ed25519 key"""
|
|
||||||
if type(key) is type(None):
|
|
||||||
return False
|
|
||||||
# Accept keys that have no = padding
|
|
||||||
key = unpaddedbase32.repad(bytesconverter.str_to_bytes(key))
|
|
||||||
|
|
||||||
retVal = False
|
|
||||||
try:
|
|
||||||
nacl.signing.SigningKey(seed=key, encoder=nacl.encoding.Base32Encoder)
|
|
||||||
except nacl.exceptions.ValueError:
|
|
||||||
pass
|
|
||||||
except base64.binascii.Error as _:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
retVal = True
|
|
||||||
return retVal
|
|
@ -3,7 +3,6 @@
|
|||||||
size related utilities
|
size related utilities
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
from onionrutils import stringvalidators
|
|
||||||
"""
|
"""
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -7,9 +7,6 @@ from nacl.signing import SigningKey, VerifyKey
|
|||||||
from nacl.encoding import Base32Encoder
|
from nacl.encoding import Base32Encoder
|
||||||
from nacl.exceptions import BadSignatureError
|
from nacl.exceptions import BadSignatureError
|
||||||
|
|
||||||
from wot.identity.processtrustsignature import process_trust_signature
|
|
||||||
from wot.identity.processrevokesignature import process_revoke_signature
|
|
||||||
from wot.identity.processidentityannounce import process_identity_announce
|
|
||||||
from wot.identity.name import IdentityName
|
from wot.identity.name import IdentityName
|
||||||
from wot.identity.name import max_len as max_name_len
|
from wot.identity.name import max_len as max_name_len
|
||||||
from wot.identity.identityset import IdentitySet, identities
|
from wot.identity.identityset import IdentitySet, identities
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
import logger
|
|
||||||
|
|
||||||
from nacl.signing import VerifyKey
|
|
||||||
|
|
||||||
from wot.blockprocessingevent import WotCommand
|
|
||||||
from wot.identity.identityset import identities
|
|
||||||
|
|
||||||
def process_identity_announce(identity_announce_payload):
|
|
||||||
if len(identity_announce_payload) != 97:
|
|
||||||
logger.warn(
|
|
||||||
f'Identity announce signature size is invalid',
|
|
||||||
terminal=True)
|
|
||||||
|
|
||||||
# verify that this is a signature for an announce command
|
|
||||||
if identity_announce_payload[0] != WotCommand.ANNOUNCE:
|
|
||||||
logger.warn(
|
|
||||||
f'Invalid command in signature' , terminal=True)
|
|
||||||
return
|
|
||||||
# signer is first 32 bytes
|
|
||||||
signer = identity_announce_payload[1:33]
|
|
||||||
# signature is last 64 bytes
|
|
||||||
signature = identity_announce_payload[33:]
|
|
||||||
|
|
||||||
# If bad signature, it raises nacl.exceptions.BadSignatureError
|
|
||||||
VerifyKey(signer).verify(identity_announce_payload[0] + signer, signature)
|
|
||||||
|
|
||||||
# noop if already announced
|
|
||||||
identities.add
|
|
@ -0,0 +1,4 @@
|
|||||||
|
from .processidentityannounce import process_identity_announce
|
||||||
|
from .processrevokesignature import process_revoke_signature
|
||||||
|
from .processtrustsignature import process_trust_signature
|
||||||
|
from .processrevokeidentity import process_identity_revoke
|
@ -0,0 +1,17 @@
|
|||||||
|
import logger
|
||||||
|
|
||||||
|
from nacl.signing import VerifyKey
|
||||||
|
|
||||||
|
from wot.blockprocessingevent import WotCommand
|
||||||
|
from wot.identity import Identity
|
||||||
|
from wot.identity.identityset import identities
|
||||||
|
|
||||||
|
def process_identity_announce(identity_announce_payload):
|
||||||
|
|
||||||
|
# verify that this is a signature for an announce command
|
||||||
|
if identity_announce_payload[0] != WotCommand.ANNOUNCE:
|
||||||
|
logger.warn(
|
||||||
|
f'Invalid command in signature' , terminal=True)
|
||||||
|
return
|
||||||
|
iden = Identity.deserialize(identity_announce_payload[1:])
|
||||||
|
identities.add(iden)
|
@ -0,0 +1,21 @@
|
|||||||
|
import logger
|
||||||
|
|
||||||
|
from nacl.signing import VerifyKey
|
||||||
|
|
||||||
|
from wot.blockprocessingevent import WotCommand
|
||||||
|
from wot.identity import Identity
|
||||||
|
from wot.identity.identityset import identities
|
||||||
|
|
||||||
|
def process_identity_revoke(revoke_payload: bytes):
|
||||||
|
wot_cmd = revoke_payload[0].to_bytes(1, 'big')
|
||||||
|
if revoke_payload[0] != WotCommand.REVOKE:
|
||||||
|
logger.warn(
|
||||||
|
f'Invalid command in signature', terminal=True)
|
||||||
|
return
|
||||||
|
revoked_identity = revoke_payload[1:33]
|
||||||
|
signature = revoke_payload[33:]
|
||||||
|
|
||||||
|
# raises nacl.exceptions.BadSignatureError if bad signature
|
||||||
|
VerifyKey(revoked_identity).verify(wot_cmd + revoked_identity, signature)
|
||||||
|
|
||||||
|
identities.remove(Identity(revoked_identity, "etc"))
|
@ -11,6 +11,7 @@ def process_trust_signature(sig_payload: bytes):
|
|||||||
if len(sig_payload) != 129:
|
if len(sig_payload) != 129:
|
||||||
logger.warn(
|
logger.warn(
|
||||||
f'Signature size is invalid for a signed identity')
|
f'Signature size is invalid for a signed identity')
|
||||||
|
return
|
||||||
|
|
||||||
# verify that this is a signature for a trust command
|
# verify that this is a signature for a trust command
|
||||||
if sig_payload[0] != WotCommand.TRUST:
|
if sig_payload[0] != WotCommand.TRUST:
|
@ -15,10 +15,11 @@ os.environ["ONIONR_HOME"] = TEST_DIR
|
|||||||
import unittest
|
import unittest
|
||||||
import sys
|
import sys
|
||||||
sys.path.append(".")
|
sys.path.append(".")
|
||||||
sys.path.append('static-data/default-plugins/wot/wot')
|
sys.path.append('static-data/default-plugins/wot/')
|
||||||
sys.path.append("src/")
|
sys.path.append("src/")
|
||||||
import identity
|
from wot.identityprocessing import process_identity_announce
|
||||||
from identity.identityset import identities
|
from wot import identity
|
||||||
|
from wot.identity.identityset import identities
|
||||||
|
|
||||||
|
|
||||||
class WotCommand(IntEnum):
|
class WotCommand(IntEnum):
|
||||||
@ -34,16 +35,14 @@ class TestAnnounceIdentityPayload(unittest.TestCase):
|
|||||||
identities.clear()
|
identities.clear()
|
||||||
|
|
||||||
signing_key = SigningKey.generate()
|
signing_key = SigningKey.generate()
|
||||||
main_iden = identity.Identity(signing_key.verify_key, "test")
|
main_iden = identity.Identity(signing_key, "test")
|
||||||
|
|
||||||
wot_cmd = int(WotCommand.ANNOUNCE).to_bytes(1, 'big')
|
wot_cmd = int(WotCommand.ANNOUNCE).to_bytes(1, 'big')
|
||||||
announce_signature = signing_key.sign(wot_cmd + bytes(main_iden))
|
serialized_iden = wot_cmd + main_iden.serialize()
|
||||||
announce_signature_payload = wot_cmd + bytes(signing_key.verify_key) + \
|
|
||||||
bytes(announce_signature)
|
|
||||||
|
|
||||||
identity.process_identity_announce(announce_signature_payload)
|
process_identity_announce(serialized_iden)
|
||||||
|
|
||||||
self.assertEqual(main_iden, identities[0])
|
self.assertEqual(bytes(main_iden.key), bytes(list(identities)[0].key))
|
||||||
self.assertEqual(len(identities), 1)
|
self.assertEqual(len(identities), 1)
|
||||||
self.assertEqual(len(main_iden.trusted), 0)
|
self.assertEqual(len(main_iden.trusted), 0)
|
||||||
|
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
import os, uuid
|
||||||
|
from random import randint
|
||||||
|
from time import sleep
|
||||||
|
from enum import IntEnum, auto
|
||||||
|
from nacl.signing import SigningKey, VerifyKey
|
||||||
|
from nacl.exceptions import BadSignatureError
|
||||||
|
import nacl
|
||||||
|
import secrets
|
||||||
|
import onionrblocks
|
||||||
|
|
||||||
|
|
||||||
|
TEST_DIR = 'testdata/%s-%s' % (str(uuid.uuid4())[:6], os.path.basename(__file__)) + '/'
|
||||||
|
print("Test directory:", TEST_DIR)
|
||||||
|
os.environ["ONIONR_HOME"] = TEST_DIR
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import sys
|
||||||
|
sys.path.append(".")
|
||||||
|
sys.path.append('static-data/default-plugins/wot/')
|
||||||
|
sys.path.append("src/")
|
||||||
|
from wot.identityprocessing import process_identity_revoke
|
||||||
|
from wot import identity
|
||||||
|
from wot.identity.identityset import identities
|
||||||
|
|
||||||
|
|
||||||
|
class WotCommand(IntEnum):
|
||||||
|
TRUST = 1
|
||||||
|
REVOKE_TRUST = auto()
|
||||||
|
ANNOUNCE = auto()
|
||||||
|
REVOKE = auto()
|
||||||
|
|
||||||
|
|
||||||
|
class TestRevokeIdentityPayload(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_revoke_identity_invalid(self):
|
||||||
|
identities.clear()
|
||||||
|
|
||||||
|
signing_key = SigningKey.generate()
|
||||||
|
main_iden = identity.Identity(signing_key, "test")
|
||||||
|
identities.add(main_iden)
|
||||||
|
|
||||||
|
wot_cmd = int(WotCommand.REVOKE).to_bytes(1, 'big')
|
||||||
|
|
||||||
|
signed = signing_key.sign(wot_cmd + bytes(main_iden.key))
|
||||||
|
revoke_payload = wot_cmd + bytes(signing_key.verify_key) + signed.signature
|
||||||
|
|
||||||
|
self.assertRaises(nacl.exceptions.Inv process_identity_revoke(revoke_payload)
|
||||||
|
|
||||||
|
self.assertEqual(len(identities), 1)
|
||||||
|
|
||||||
|
def test_revoke_identity_payload(self):
|
||||||
|
identities.clear()
|
||||||
|
|
||||||
|
signing_key = SigningKey.generate()
|
||||||
|
main_iden = identity.Identity(signing_key, "test")
|
||||||
|
identities.add(main_iden)
|
||||||
|
|
||||||
|
wot_cmd = int(WotCommand.REVOKE).to_bytes(1, 'big')
|
||||||
|
|
||||||
|
signed = signing_key.sign(wot_cmd + bytes(main_iden.key))
|
||||||
|
revoke_payload = wot_cmd + bytes(signing_key.verify_key) + signed.signature
|
||||||
|
|
||||||
|
process_identity_revoke(revoke_payload)
|
||||||
|
|
||||||
|
self.assertEqual(len(identities), 0)
|
||||||
|
|
||||||
|
|
||||||
|
unittest.main()
|
@ -15,10 +15,10 @@ os.environ["ONIONR_HOME"] = TEST_DIR
|
|||||||
import unittest
|
import unittest
|
||||||
import sys
|
import sys
|
||||||
sys.path.append(".")
|
sys.path.append(".")
|
||||||
sys.path.append('static-data/default-plugins/wot/wot')
|
sys.path.append('static-data/default-plugins/wot/')
|
||||||
sys.path.append("src/")
|
sys.path.append("src/")
|
||||||
import identity
|
from wot import identity
|
||||||
from identity.identityset import identities
|
from wot.identity.identityset import identities
|
||||||
|
|
||||||
|
|
||||||
class WotCommand(IntEnum):
|
class WotCommand(IntEnum):
|
||||||
|
Loading…
Reference in New Issue
Block a user