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 \ && apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/community/ --virtual .build-deps \
build-base \ build-base \
libffi-dev \ libffi-dev \
protobuf \
py3-gpgme \ py3-gpgme \
swig \ swig \
&& /usr/local/bin/python${PYTHON_RELEASE} -m venv ./ \ && /usr/local/bin/python${PYTHON_RELEASE} -m venv ./ \
&& ./bin/pip${PYTHON_RELEASE} install -U pip wheel \ && ./bin/pip${PYTHON_RELEASE} install -U pip wheel \
&& ./bin/pip${PYTHON_RELEASE} install -r ./requirements.txt \ && ./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/ \ && 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 \ && apk del --no-network .build-deps \
&& find ./lib -type f -executable -exec scanelf --needed --nobanner --format '%n#p' '{}' ';' \ && find ./lib -type f -executable -exec scanelf --needed --nobanner --format '%n#p' '{}' ';' \
|tr ',' '\n' \ |tr ',' '\n' \
@ -48,7 +45,7 @@ RUN apk add --no-cache \
|tar --strip-components 1 -C /opt/shellspec -xzf - \ |tar --strip-components 1 -C /opt/shellspec -xzf - \
&& ln -s /opt/shellspec/shellspec ./bin/shellspec && 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 README.md ./
COPY COPYING ./ COPY COPYING ./
COPY Makefile ./ COPY Makefile ./
@ -65,6 +62,7 @@ CMD ["bash"]
FROM dist as master FROM dist as master
ARG UID ARG UID
ARG USER ARG USER
ENV PATH=/opt/dpgpid/bin:$PATH
ENV UID=${UID:-999} ENV UID=${UID:-999}
ENV USER=dpgpid ENV USER=dpgpid

171
keygen
View File

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

View File

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

View File

@ -50,7 +50,7 @@ Describe 'keygen'
Describe '--version:' Describe '--version:'
It 'prints version' It 'prints version'
When run keygen --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 status should be success
The stderr should equal "" The stderr should equal ""
End End
@ -154,14 +154,6 @@ Describe 'keygen'
The stderr should equal "" The stderr should equal ""
End End
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:' Describe '-t ipfs -k username password:'
It 'prints ipfs keys for user "username" with password "password"' It 'prints ipfs keys for user "username" with password "password"'
When run keygen -t ipfs -k username password When run keygen -t ipfs -k username password
@ -180,6 +172,15 @@ Describe 'keygen'
The stderr should equal "" The stderr should equal ""
End End
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:" Describe "-o ${IPFS_PEM_FILE} -t ipfs username password:"
It 'writes ipfs keys to file for user "username" with password "password"' It 'writes ipfs keys to file for user "username" with password "password"'
When run keygen username password -o "${IPFS_PEM_FILE}" -t ipfs When run keygen username password -o "${IPFS_PEM_FILE}" -t ipfs