add support for base64 output

* bump version v0.0.3
This commit is contained in:
Yann Autissier 2022-09-15 19:32:01 +02:00
parent 7b539afbdd
commit 12aeafbb3e
4 changed files with 128 additions and 86 deletions

View File

@ -13,16 +13,13 @@ RUN apk upgrade --no-cache \
&& apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/community/ --virtual .build-deps \
build-base \
libffi-dev \
protobuf \
py3-gpgme \
swig \
&& /usr/local/bin/python${PYTHON_RELEASE} -m venv ./ \
&& ./bin/pip${PYTHON_RELEASE} install -U pip wheel \
&& ./bin/pip${PYTHON_RELEASE} install -r ./requirements.txt \
&& wget https://raw.githubusercontent.com/libp2p/go-libp2p/master/core/crypto/pb/crypto.proto \
&& protoc --python_out=./lib/python${PYTHON_RELEASE}/site-packages/ crypto.proto \
&& cp -a /usr/lib/python${PYTHON_RELEASE}/site-packages/gpg ./lib/python${PYTHON_RELEASE}/site-packages/ \
&& rm -rf /root/.cache ./build ./crypto.proto \
&& rm -rf /root/.cache ./build \
&& apk del --no-network .build-deps \
&& find ./lib -type f -executable -exec scanelf --needed --nobanner --format '%n#p' '{}' ';' \
|tr ',' '\n' \
@ -48,7 +45,7 @@ RUN apk add --no-cache \
|tar --strip-components 1 -C /opt/shellspec -xzf - \
&& ln -s /opt/shellspec/shellspec ./bin/shellspec
COPY --from=ipfs/kubo:v0.14.0 /usr/local/bin/ipfs ./bin/
COPY --from=ipfs/kubo:v0.15.0 /usr/local/bin/ipfs ./bin/
COPY README.md ./
COPY COPYING ./
COPY Makefile ./
@ -65,6 +62,7 @@ CMD ["bash"]
FROM dist as master
ARG UID
ARG USER
ENV PATH=/opt/dpgpid/bin:$PATH
ENV UID=${UID:-999}
ENV USER=dpgpid

171
keygen
View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3
# link: https://git.p2p.legal/aya/dpgpid/
# desc: generate ed25519 keys suitable for duniter or ipfs
# desc: generate ed25519 keys suitables for duniter or ipfs
# Copyleft 2022 Yann Autissier <aya@asycn.io>
# all crypto science belongs to Pascal Engélibert <tuxmain@zettascript.org>
@ -27,7 +27,6 @@ import argparse
import base58
import base64
import configparser
import crypto_pb2
import cryptography.hazmat.primitives.asymmetric.ed25519 as ed25519
from cryptography.hazmat.primitives import serialization
import duniterpy.key
@ -45,14 +44,15 @@ import sys
import time
import warnings
__version__='0.0.2'
__version__='0.0.3'
class keygen:
def __init__(self):
# desc: generate ed25519 keys suitable for duniter or ipfs
# desc: generate ed25519 keys suitables for duniter or ipfs
self.parser = argparse.ArgumentParser(description="""
Generate ed25519 keys. It converts your Duniter username and password or
a GPG key to an IPFS PeerID.""")
Generate ed25519 keys suitables for duniter or ipfs.
It converts your Duniter username and password or a GPG key to a duniter
pub/sec key or an IPFS PeerID/PrivateKEY.""")
self.parser.add_argument(
"-d",
"--debug",
@ -60,6 +60,13 @@ class keygen:
help="show debug informations (WARNING: including SECRET KEY value)",
)
self.parser.add_argument(
"-f",
"--format",
dest="format",
default=None,
help="output file format: [pb2|pem|pubsec], default: pem (pkcs8)",
)
self.parser.add_argument(
"-g",
"--gpg",
action="store_true",
@ -108,7 +115,7 @@ class keygen:
"--type",
dest="type",
default="base58",
help="output key type : [ base58 | duniter | ipfs ]",
help="output text format: [base58|base64|duniter|ipfs], default: base58",
)
self.parser.add_argument(
"-v",
@ -142,9 +149,12 @@ class keygen:
if hasattr(self, 'armored_pgp_secret_key') and self.armored_pgp_secret_key:
clearmem(self.armored_pgp_secret_key)
log.debug("cleared: keygen.armored_pgp_secret_key")
if hasattr(self, 'base58_secret_key') and self.base58_secret_key:
clearmem(self.base58_secret_key)
log.debug("cleared: keygen.base58_secret_key")
if hasattr(self, 'ed25519_secret_base58') and self.ed25519_secret_base58:
clearmem(self.ed25519_secret_base58)
log.debug("cleared: keygen.ed25519_secret_base58")
if hasattr(self, 'ed25519_secret_base64') and self.ed25519_secret_base64:
clearmem(self.ed25519_secret_base64)
log.debug("cleared: keygen.ed25519_secret_base64")
if hasattr(self, 'duniter'):
if hasattr(self.duniter, 'sk') and self.duniterpy.sk:
clearmem(self.duniterpy.sk)
@ -155,6 +165,9 @@ class keygen:
if hasattr(self, 'ed25519_secret_pem_pkcs8') and self.ed25519_secret_pem_pkcs8:
clearmem(self.ed25519_secret_pem_pkcs8)
log.debug("cleared: keygen.ed25515_secret_pem_pkcs8")
if hasattr(self, 'ed25519_secret_protobuf2') and self.ed25519_secret_protobuf2:
clearmem(self.ed25519_secret_protobuf2)
log.debug("cleared: keygen.ed25515_secret_protobuf2")
if hasattr(self, 'ipfs_privkey') and self.ipfs_privkey:
clearmem(self.ipfs_privkey)
log.debug("cleared: keygen.ipfs_privkey")
@ -189,6 +202,46 @@ class keygen:
log.debug("config_dir=%s" % config_dir)
self.config.read( [config_dir + '/keygen.conf'] )
def _output(self, public_key, secret_key, public_key_prefix, secret_key_prefix):
log.debug("keygen._output()")
if self.output is None:
self._output_text(public_key, secret_key, public_key_prefix, secret_key_prefix)
else:
self._output_file()
os.chmod(self.output, 0o600)
self._cleanup()
def _output_file(self):
log.debug("keygen._output_file()")
if self.format == 'pb2':
if not hasattr(self, 'ed25519_secret_protobuf2'):
self.protobuf2_from_ed25519()
with open(self.output, "wb") as fh:
fh.write(self.ed25519_secret_protobuf2)
elif self.format == 'pubsec':
if not hasattr(self, 'ed25519_public_base58') or not hasattr(self, 'ed25519_secret_base58'):
self.base58_from_ed25519()
with open(self.output, "w") as fh:
fh.write(
f"""Type: PubSec
Version: 1
pub: {self.ed25519_public_base58}
sec: {self.ed25519_secret_base58}
"""
)
else:
if not hasattr(self, 'ed25519_secret_pem_pkcs8'):
self.pem_pkcs8_from_ed25519()
with open(self.output, "w") as fh:
fh.write(self.ed25519_secret_pem_pkcs8)
def _output_text(self, public_key, secret_key, public_key_prefix, secret_key_prefix):
log.debug("keygen._output_text()")
if self.keys or not self.secret:
print("%s" % ''.join([self.prefix * public_key_prefix, public_key]))
if self.keys or self.secret:
print("%s" % ''.join([self.prefix * secret_key_prefix, secret_key]))
def _run(self, argv):
args = self.parser.parse_args(argv)
vars(self).update(vars(args))
@ -222,56 +275,46 @@ class keygen:
def base58_from_ed25519(self):
log.debug("keygen.base58_from_ed25519()")
self.base58_public_key = base58.b58encode(self.ed25519_public_bytes).decode('ascii')
self.base58_secret_key = base58.b58encode(self.ed25519_secret_bytes).decode('ascii')
log.debug("keygen.base58_public_key=%s" % self.base58_public_key)
log.debug("keygen.base58_secret_key=%s" % self.base58_secret_key)
self.ed25519_public_base58 = base58.b58encode(self.ed25519_public_bytes).decode('ascii')
log.debug("keygen.ed25519_public_base58=%s" % self.ed25519_public_base58)
self.ed25519_secret_base58 = base58.b58encode(self.ed25519_secret_bytes).decode('ascii')
log.debug("keygen.ed25519_secret_base58=%s" % self.ed25519_secret_base58)
def base64_from_ed25519(self):
log.debug("keygen.base64_from_ed25519()")
self.ed25519_public_base64 = base64.b64encode(self.ed25519_public_bytes).decode('ascii')
log.debug("keygen.ed25519_public_base64=%s" % self.ed25519_public_base64)
self.ed25519_secret_base64 = base64.b64encode(self.ed25519_secret_bytes).decode('ascii')
log.debug("keygen.ed25519_secret_base64=%s" % self.ed25519_secret_base64)
def base58_from_pubsec(self):
log.debug("keygen.base58_from_pubsec()")
for line in open(self.input, "r"):
if re.search("pub", line):
self.base58_public_key = line.replace('\n','').split(': ')[1]
self.ed25519_public_base58 = line.replace('\n','').split(': ')[1]
elif re.search("sec", line):
self.base58_secret_key = line.replace('\n','').split(': ')[1]
self.ed25519_secret_base58 = line.replace('\n','').split(': ')[1]
def do_base58(self):
log.debug("keygen.do_duniter()")
log.debug("keygen.do_base58()")
self.base58_from_ed25519()
if self.output is None:
if self.keys or not self.secret:
print("%s" % ''.join([self.prefix * 'pub: ', self.base58_public_key]))
if self.keys or self.secret:
print("%s" % ''.join([self.prefix * 'sec: ', self.base58_secret_key]))
else:
with open(self.output, "w") as fh:
fh.write(f"""Type: PubSec
Version: 1
pub: {self.base58_public_key}
sec: {self.base58_secret_key}
"""
)
os.chmod(self.output, 0o600)
self._cleanup()
self._output(self.ed25519_public_base58, self.ed25519_secret_base58, 'pub: ', 'sec: ')
def do_base64(self):
log.debug("keygen.do_base64()")
self.base64_from_ed25519()
self._output(self.ed25519_public_base64, self.ed25519_secret_base64, 'pub: ', 'sec: ')
def do_duniter(self):
if not self.format:
self.format = 'pubsec'
self.do_base58()
def do_ipfs(self):
log.debug("keygen.do_ipfs()")
self.ipfs_from_ed25519()
if self.output is None:
if self.keys or not self.secret:
print("%s" % ''.join(['PeerID: ' * self.prefix, self.ipfs_peerid]))
if self.keys or self.secret:
print("%s" % ''.join(['PrivKEY: ' * self.prefix, self.ipfs_privkey]))
else:
# with open(self.output, "wb") as fh:
# fh.write(self.ipfs_libp2p_protobuf_key)
with open(self.output, "w") as fh:
fh.write(self.ed25519_secret_pem_pkcs8)
os.chmod(self.output, 0o600)
self._cleanup()
self.protobuf2_from_ed25519()
self.ipfs_from_protobuf2()
self._output(self.ipfs_peerid, self.ipfs_privkey, 'PeerID: ', 'PrivKEY: ')
def duniterpy_from_salt_and_password(self):
log.debug("keygen.duniterpy_from_salt_and_password()")
@ -310,8 +353,8 @@ sec: {self.base58_secret_key}
def ed25519_from_base58(self):
log.debug("keygen.ed25519_from_base58()")
self.ed25519_public_bytes = base58.b58decode(self.base58_public_key)
self.ed25519_secret_bytes = base58.b58decode(self.base58_secret_key)
self.ed25519_public_bytes = base58.b58decode(self.ed25519_public_base58)
self.ed25519_secret_bytes = base58.b58decode(self.ed25519_secret_base58)
log.debug("keygen.ed25519_public_bytes=%s" % self.ed25519_public_bytes)
log.debug("keygen.ed25519_secret_bytes=%s" % self.ed25519_secret_bytes)
@ -324,6 +367,11 @@ sec: {self.base58_secret_key}
def ed25519_from_gpg(self):
log.debug("keygen.ed25519_from_gpg()")
self.pgpy_from_gpg()
self.ed25519_from_pgpy()
def pgpy_from_gpg(self):
log.debug("keygen.pgpy_from_gpg()")
self.gpg_seckeys = list(self.gpg.keylist(pattern=self.username, secret=True))
log.debug("keygen.gpg_seckeys=%s" % self.gpg_seckeys)
if not self.gpg_seckeys:
@ -354,7 +402,6 @@ sec: {self.base58_secret_key}
# SymmetricKeyAlgorithm IDEA, CAST5 and Blowfish (PGPy v0.5.4)
warnings.simplefilter('ignore')
self.pgpy, _ = pgpy.PGPKey.from_blob(self.armored_pgp_secret_key)
self.ed25519_from_pgpy()
def ed25519_from_pgpy(self):
log.debug("keygen.ed25519_from_pgpy()")
@ -396,27 +443,17 @@ sec: {self.base58_secret_key}
log.debug("keygen.gpg_passphrase_cb(%s, %s, %s)" % (uid_hint, passphrase_info, prev_was_bad))
return self.password
def ipfs_from_ed25519(self):
log.debug("keygen.ipfs_from_ed25519()")
def ipfs_from_protobuf2(self):
log.debug("keygen.ipfs_from_protobuf2()")
# PeerID
ipfs_pid = base58.b58encode(b'\x00$\x08\x01\x12 ' + self.ed25519_public_bytes)
self.ipfs_peerid = ipfs_pid.decode('ascii')
self.ipfs_peerid = base58.b58encode(self.ed25519_public_protobuf2).decode('ascii')
log.debug("keygen.ipfs_peerid=%s" % self.ipfs_peerid)
# PrivKey
pkey = crypto_pb2.PrivateKey()
pkey.Type = crypto_pb2.KeyType.Ed25519
pkey.Data = self.ed25519_secret_bytes
self.ipfs_privkey = base64.b64encode(pkey.SerializeToString()).decode('ascii')
self.ipfs_privkey = base64.b64encode(self.ed25519_secret_protobuf2).decode('ascii')
log.debug("keygen.ipfs_privkey=%s" % self.ipfs_privkey)
# libp2p-protobuf-cleartext format for ipfs key import
self.ipfs_libp2p_protobuf_key = pkey.SerializeToString()
# pem-pkcs8-cleartext format for ipfs key import
self.pem_pkcs8_from_ed25519()
def pem_pkcs8_from_ed25519(self):
log.debug("keygen.pem_pkcs8_from_ed25519()")
@ -467,9 +504,17 @@ sec: {self.base58_secret_key}
self.pgpy_key_type = 'undefined'
log.debug("keygen.pgpy_key_type=%s" % self.pgpy_key_type)
def protobuf2_from_ed25519(self):
# libp2p protobuf version 2
log.debug("keygen.protobuf2_from_ed25519()")
self.ed25519_public_protobuf2 = b'\x00$\x08\x01\x12 ' + self.ed25519_public_bytes
self.ed25519_secret_protobuf2 = b'\x08\x01\x12@' + self.ed25519_secret_bytes
log.debug("keygen.ed25519_public_protobuf2=%s" % self.ed25519_public_protobuf2)
log.debug("keygen.ed25519_secret_protobuf2=%s" % self.ed25519_secret_protobuf2)
##
# long_to_bytes comes from PyCrypto, which is released into Public Domain
# https://github.com/dlitz/pycrypto/blob/master/lib/Crypto/Util/number.py
def bytes_to_long(s):
"""bytes_to_long(string) : long
Convert a byte string to a long integer.

View File

@ -1,9 +1,7 @@
base58
cryptography
duniterpy
google
pgpy
pynentry
protobuf
pynacl
SecureBytes
base58==2.1.1
cryptography==3.4.8
duniterpy==1.1.0
pgpy==0.5.4
pynentry==0.1.6
pynacl==1.5.0
SecureBytes==0.3.5

View File

@ -50,7 +50,7 @@ Describe 'keygen'
Describe '--version:'
It 'prints version'
When run keygen --version
The output should include 'v0.0.2'
The output should include 'v0.0.3'
The status should be success
The stderr should equal ""
End
@ -154,14 +154,6 @@ Describe 'keygen'
The stderr should equal ""
End
End
Describe '-t ipfs -s username password:'
It 'prints ipfs secret key for user "username" with password "password"'
When run keygen -t ipfs -s username password
The output should include 'CAESQA+XqCWjRqCjNe9oU3QA796bEH+T+rxgyPQ/EkXvE2MvNJoTbvcP+m51+XwxrmWqHaOpI1ZD0USwLjqAmV8Boas='
The status should be success
The stderr should equal ""
End
End
Describe '-t ipfs -k username password:'
It 'prints ipfs keys for user "username" with password "password"'
When run keygen -t ipfs -k username password
@ -180,6 +172,15 @@ Describe 'keygen'
The stderr should equal ""
End
End
Describe '-t base64 -pk username password:'
It 'prints prefixed base64 keys for user "username" with password "password"'
When run keygen -t base64 -pk username password
The output should include 'pub: NJoTbvcP+m51+XwxrmWqHaOpI1ZD0USwLjqAmV8Boas='
The output should include 'sec: D5eoJaNGoKM172hTdADv3psQf5P6vGDI9D8SRe8TYy80mhNu9w/6bnX5fDGuZaodo6kjVkPRRLAuOoCZXwGhqw=='
The status should be success
The stderr should equal ""
End
End
Describe "-o ${IPFS_PEM_FILE} -t ipfs username password:"
It 'writes ipfs keys to file for user "username" with password "password"'
When run keygen username password -o "${IPFS_PEM_FILE}" -t ipfs