diff --git a/zen/new_file_in_astroport.sh b/zen/new_file_in_astroport.sh
index 7dd96f6..8a9998b 100755
--- a/zen/new_file_in_astroport.sh
+++ b/zen/new_file_in_astroport.sh
@@ -158,7 +158,11 @@ if [[ $KEY ]]; then
echo "$KEY" > ~/.zen/ipfs/.${IPFSNODEID}/KEY/${INDEXPREFIX}${REFERENCE}/${G1PUB}/.ipns.link
# CREATE .zen = ZEN economic value
touch ~/.zen/ipfs/.${IPFSNODEID}/KEY/${INDEXPREFIX}${REFERENCE}/${G1PUB}/.zen
+ ## CAN CONVERT IPNS KEY INTO .dunikey FILE
+ ## $MY_PATH/tools/natools.py privkey -f ipfs-keystore -k ~/.ipfs/keystore/$KEYFILE -F pubsec -o /tmp/${INDEXPREFIX}${REFERENCE}.dunikey
+ ## natools can convert... ;)
$MY_PATH/tools/natools.py encrypt -p $G1PUB -i ~/.ipfs/keystore/$KEYFILE -o ~/.zen/ipfs/.${IPFSNODEID}/KEY/${INDEXPREFIX}${REFERENCE}/${G1PUB}/.ipns.key.natools.encrypt
+
# CREATE other encrypted copies for friends depending DEFCON & stars
# > STARGATE 1 - 2 - 3 - 4 - 5 !!
################ ENCRYPT keystore/$KEYFILE
diff --git a/zen/tools/natools.py b/zen/tools/natools.py
index 9de1187..63f14a0 100755
--- a/zen/tools/natools.py
+++ b/zen/tools/natools.py
@@ -17,9 +17,9 @@
along with this program. If not, see .
"""
-__version__ = "1.0"
+__version__ = "1.3.1"
-import os, sys, duniterpy.key, libnacl.sign, base58, base64, getpass
+import os, sys, duniterpy.key, libnacl, base58, base64, getpass
def getargv(arg:str, default:str="", n:int=1, args:list=sys.argv) -> str:
if arg in args and len(args) > args.index(arg)+n:
@@ -30,7 +30,7 @@ def getargv(arg:str, default:str="", n:int=1, args:list=sys.argv) -> str:
def read_data(data_path, b=True):
if data_path == "-":
if b:
- return sys.stdin.read().encode()
+ return sys.stdin.buffer.read()
else:
return sys.stdin.read()
else:
@@ -48,13 +48,31 @@ def encrypt(data, pubkey):
def decrypt(data, privkey):
return privkey.decrypt_seal(data)
+def box_encrypt(data, privkey, pubkey, nonce=None, attach_nonce=False):
+ signer = libnacl.sign.Signer(privkey.seed)
+ sk = libnacl.public.SecretKey(libnacl.crypto_sign_ed25519_sk_to_curve25519(signer.sk))
+ verifier = libnacl.sign.Verifier(base58.b58decode(pubkey).hex())
+ pk = libnacl.public.PublicKey(libnacl.crypto_sign_ed25519_pk_to_curve25519(verifier.vk))
+ box = libnacl.public.Box(sk.sk, pk.pk)
+ data = box.encrypt(data, nonce) if nonce else box.encrypt(data)
+ return data if attach_nonce else data[24:]
+
+def box_decrypt(data, privkey, pubkey, nonce=None):
+ signer = libnacl.sign.Signer(privkey.seed)
+ sk = libnacl.public.SecretKey(libnacl.crypto_sign_ed25519_sk_to_curve25519(signer.sk))
+ verifier = libnacl.sign.Verifier(base58.b58decode(pubkey).hex())
+ pk = libnacl.public.PublicKey(libnacl.crypto_sign_ed25519_pk_to_curve25519(verifier.vk))
+ box = libnacl.public.Box(sk.sk, pk.pk)
+ return box.decrypt(data, nonce) if nonce else box.decrypt(data)
+
def sign(data, privkey):
return privkey.sign(data)
def verify(data, pubkey):
try:
+ ret = libnacl.sign.Verifier(duniterpy.key.PublicKey(pubkey).hex_pk()).verify(data)
sys.stderr.write("Signature OK!\n")
- return libnacl.sign.Verifier(duniterpy.key.PublicKey(pubkey).hex_pk()).verify(data)
+ return ret
except ValueError:
sys.stderr.write("Bad signature!\n")
exit(1)
@@ -69,7 +87,7 @@ def get_privkey(privkey_path, privkey_format):
if privkey_path == "*":
privkey_path = "-"
if privkey_path == "-":
- return duniterpy.key.SigningKey.from_credentials(getpass.getpass("Password: "), getpass.getpass("Salt: "))
+ return duniterpy.key.SigningKey.from_credentials(getpass.getpass("Salt: "), getpass.getpass("Password: "))
else:
return duniterpy.key.SigningKey.from_credentials_file(privkey_path)
@@ -97,6 +115,45 @@ def get_privkey(privkey_path, privkey_format):
if privkey_path == "*":
privkey_path = "authfile.key"
return duniterpy.key.SigningKey.from_private_key(privkey_path)
+
+ elif privkey_format == "ipfs-keystore":
+ if privkey_path == "*":
+ privkey_path = "key_self"
+ return duniterpy.key.SigningKey(read_data(privkey_path)[4:36])
+
+ print("Error: unknown privkey format")
+
+def format_privkey(privkey, output_privkey_format):
+ if output_privkey_format == "pubsec":
+ return "Type: PubSec\nVersion: 1\npub: {}\nsec: {}".format(privkey.pubkey, base58.b58encode(privkey.sk).decode()).encode()
+
+ elif output_privkey_format == "seedh":
+ return privkey.hex_seed()
+
+ elif output_privkey_format == "ipfs-keystore":
+ return b"\x08\x01\x12@"+privkey.sk
+
+ print("Error: unknown output privkey format")
+
+def fill_pubkey(pubkey, length=32):
+ while pubkey[0] == 0:
+ pubkey = pubkey[1:]
+ return b"\x00"*(length-len(pubkey)) + pubkey
+
+def pubkey_checksum(pubkey, length=32, clength=3):
+ return base58.b58encode(libnacl.crypto_hash_sha256(libnacl.crypto_hash_sha256(fill_pubkey(base58.b58decode(pubkey), length)))).decode()[:clength]
+
+# returns (pubkey:bytes|None, deprecated_length:bool)
+def check_pubkey(pubkey):
+ if ":" in pubkey:
+ parts = pubkey.split(":")
+ if len(parts[1]) < 3 or len(parts[1]) > 32:
+ return (None, False)
+ for i in range(32, 0, -1):
+ if pubkey_checksum(parts[0], i, len(parts[1])) == parts[1]:
+ return (parts[0], i < 32)
+ return (None, False)
+ return (pubkey, False)
fmt = {
"raw": lambda data: data,
@@ -108,25 +165,45 @@ fmt = {
"85": lambda data: base64.b85encode(data),
}
+defmt = {
+ "raw": lambda data: data,
+ "16": lambda data: bytes.fromhex(data),
+ "32": lambda data: base64.b32decode(data),
+ "58": lambda data: base58.b58decode(data),
+ "64": lambda data: base64.b64decode(data),
+ "85": lambda data: base64.b85decode(data),
+}
+
def show_help():
print("""Usage:
python3 natools.py [options]
Commands:
- encrypt Encrypt data
- decrypt Decrypt data
- sign Sign data
- verify Verify data
+ encrypt Encrypt data
+ decrypt Decrypt data
+ box-encrypt Encrypt data (NaCl box)
+ box-decrypt Decrypt data (NaCl box)
+ sign Sign data
+ verify Verify data
+ pubkey Display pubkey
+ privkey Display private key
+ pk Display b58 pubkey shorthand
Options:
+ -c Display pubkey checksum
-f Private key format (default: cred)
- key cred pubsec seedh ssb wif wifh
+ key cred pubsec seedh ssb wif wifh ipfs-keystore
+ -F Output private key format (default: pubsec)
+ pubsec seedh ipfs-keystore
-i Input file path (default: -)
+ -I Input format: raw 16 32 58 64 85 (default: raw)
-k Privkey file path (* for auto) (default: *)
- -p Pubkey (base58)
- -o Output file path (default: -)
+ -n Nonce (b64, 24 bytes) (for NaCl box)
+ -N Attach nonce to output (for NaCl box encryption)
--noinc Do not include msg after signature
+ -o Output file path (default: -)
-O Output format: raw 16 32 58 64 64u 85 (default: raw)
+ -p Pubkey (base58)
--help Show help
--version Show version
@@ -145,22 +222,57 @@ if __name__ == "__main__":
print(__version__)
exit()
- privkey_format = getargv("-f", "auto")
+ privkey_format = getargv("-f", "cred")
+ output_privkey_format = getargv("-F", "pubsec")
data_path = getargv("-i", "-")
privkey_path = getargv("-k", "*")
pubkey = getargv("-p")
result_path = getargv("-o", "-")
output_format = getargv("-O", "raw")
+ input_format = getargv("-I", "raw")
+
+ if pubkey:
+ pubkey, len_deprecated = check_pubkey(pubkey)
+ if not pubkey:
+ print("Invalid pubkey checksum! Please check spelling.")
+ exit(1)
+ if len(base58.b58decode(pubkey)) > 32:
+ print("Invalid pubkey: too long!")
+ exit(1)
+ if len_deprecated:
+ print("Warning: valid pubkey checksum, but deprecated format (truncating zeros)")
try:
if sys.argv[1] == "encrypt":
- write_data(fmt[output_format](encrypt(read_data(data_path), pubkey)), result_path)
+ if not pubkey:
+ print("Please provide pubkey!")
+ exit(1)
+ write_data(fmt[output_format](encrypt(defmt[input_format](read_data(data_path)), pubkey)), result_path)
elif sys.argv[1] == "decrypt":
- write_data(fmt[output_format](decrypt(read_data(data_path), get_privkey(privkey_path, privkey_format))), result_path)
+ write_data(fmt[output_format](decrypt(defmt[input_format](read_data(data_path)), get_privkey(privkey_path, privkey_format))), result_path)
+
+ elif sys.argv[1] == "box-encrypt":
+ if not pubkey:
+ print("Please provide pubkey!")
+ exit(1)
+ nonce = getargv("-n", None)
+ if nonce:
+ nonce = base64.b64decode(nonce)
+ attach_nonce = "-N" in sys.argv
+ write_data(fmt[output_format](box_encrypt(defmt[input_format](read_data(data_path)), get_privkey(privkey_path, privkey_format), pubkey, nonce, attach_nonce)), result_path)
+
+ elif sys.argv[1] == "box-decrypt":
+ if not pubkey:
+ print("Please provide pubkey!")
+ exit(1)
+ nonce = getargv("-n", None)
+ if nonce:
+ nonce = base64.b64decode(nonce)
+ write_data(fmt[output_format](box_decrypt(defmt[input_format](read_data(data_path)), get_privkey(privkey_path, privkey_format), pubkey, nonce)), result_path)
elif sys.argv[1] == "sign":
- data = read_data(data_path)
+ data = defmt[input_format](read_data(data_path))
signed = sign(data, get_privkey(privkey_path, privkey_format))
if "--noinc" in sys.argv:
@@ -169,14 +281,42 @@ if __name__ == "__main__":
write_data(fmt[output_format](signed), result_path)
elif sys.argv[1] == "verify":
- write_data(fmt[output_format](verify(read_data(data_path), pubkey)), result_path)
+ if not pubkey:
+ print("Please provide pubkey!")
+ exit(1)
+ write_data(fmt[output_format](verify(defmt[input_format](read_data(data_path)), pubkey)), result_path)
+
+ elif sys.argv[1] == "pubkey":
+ if pubkey:
+ if "-c" in sys.argv and output_format == "58":
+ write_data("{}:{}".format(pubkey, pubkey_checksum(pubkey)).encode(), result_path)
+ else:
+ write_data(fmt[output_format](base58.b58decode(pubkey)), result_path)
+ else:
+ pubkey = get_privkey(privkey_path, privkey_format).pubkey
+ if "-c" in sys.argv and output_format == "58":
+ write_data("{}:{}".format(pubkey, pubkey_checksum(pubkey)).encode(), result_path)
+ else:
+ write_data(fmt[output_format](base58.b58decode(pubkey)), result_path)
+
+ elif sys.argv[1] == "privkey":
+ privkey = get_privkey(privkey_path, privkey_format)
+ write_data(fmt[output_format](format_privkey(privkey, output_privkey_format)), result_path)
+
+ elif sys.argv[1] == "pk":
+ if not pubkey:
+ pubkey = get_privkey(privkey_path, privkey_format).pubkey
+ if "-c" in sys.argv:
+ print("{}:{}".format(pubkey, pubkey_checksum(pubkey)))
+ else:
+ print(pubkey)
else:
show_help()
except Exception as e:
if "--debug" in sys.argv:
- 0/0 # DEBUG MODE
+ 0/0 # DEBUG MODE (raise error when handling error to display backtrace)
sys.stderr.write("Error: {}\n".format(e))
show_help()
exit(1)