Compare commits

...

4 Commits
master ... wip

Author SHA1 Message Date
Yann Autissier 65fdb648ca wip: refacto 2022-10-08 12:51:28 +02:00
Yann Autissier fb08e6ed65 wip: refacto 2022-10-08 05:14:20 +02:00
Yann Autissier 029f49be22 add base36 and base58 multiformat output
* output cidv1 of key in base36 or base58
* fix rsa custom seed to concatenate p + q instead of adding it
* renamed output file format pb2 to p2p
2022-10-01 03:29:07 +02:00
Yann Autissier f1b4573294 wip: first did implementation 2022-09-29 06:21:36 +02:00
9 changed files with 1254 additions and 1119 deletions

1
about.py Normal file
View File

@ -0,0 +1 @@
__version__='0.1.0'

View File

@ -1,162 +0,0 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: crypto.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf.internal import enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='crypto.proto',
package='crypto.pb',
syntax='proto2',
serialized_options=_b('Z*github.com/libp2p/go-libp2p-core/crypto/pb'),
serialized_pb=_b('\n\x0c\x63rypto.proto\x12\tcrypto.pb\";\n\tPublicKey\x12 \n\x04Type\x18\x01 \x02(\x0e\x32\x12.crypto.pb.KeyType\x12\x0c\n\x04\x44\x61ta\x18\x02 \x02(\x0c\"<\n\nPrivateKey\x12 \n\x04Type\x18\x01 \x02(\x0e\x32\x12.crypto.pb.KeyType\x12\x0c\n\x04\x44\x61ta\x18\x02 \x02(\x0c*9\n\x07KeyType\x12\x07\n\x03RSA\x10\x00\x12\x0b\n\x07\x45\x64\x32\x35\x35\x31\x39\x10\x01\x12\r\n\tSecp256k1\x10\x02\x12\t\n\x05\x45\x43\x44SA\x10\x03\x42,Z*github.com/libp2p/go-libp2p-core/crypto/pb')
)
_KEYTYPE = _descriptor.EnumDescriptor(
name='KeyType',
full_name='crypto.pb.KeyType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='RSA', index=0, number=0,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='Ed25519', index=1, number=1,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='Secp256k1', index=2, number=2,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ECDSA', index=3, number=3,
serialized_options=None,
type=None),
],
containing_type=None,
serialized_options=None,
serialized_start=150,
serialized_end=207,
)
_sym_db.RegisterEnumDescriptor(_KEYTYPE)
KeyType = enum_type_wrapper.EnumTypeWrapper(_KEYTYPE)
RSA = 0
Ed25519 = 1
Secp256k1 = 2
ECDSA = 3
_PUBLICKEY = _descriptor.Descriptor(
name='PublicKey',
full_name='crypto.pb.PublicKey',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='Type', full_name='crypto.pb.PublicKey.Type', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='Data', full_name='crypto.pb.PublicKey.Data', index=1,
number=2, type=12, cpp_type=9, label=2,
has_default_value=False, default_value=_b(""),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=27,
serialized_end=86,
)
_PRIVATEKEY = _descriptor.Descriptor(
name='PrivateKey',
full_name='crypto.pb.PrivateKey',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='Type', full_name='crypto.pb.PrivateKey.Type', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='Data', full_name='crypto.pb.PrivateKey.Data', index=1,
number=2, type=12, cpp_type=9, label=2,
has_default_value=False, default_value=_b(""),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=88,
serialized_end=148,
)
_PUBLICKEY.fields_by_name['Type'].enum_type = _KEYTYPE
_PRIVATEKEY.fields_by_name['Type'].enum_type = _KEYTYPE
DESCRIPTOR.message_types_by_name['PublicKey'] = _PUBLICKEY
DESCRIPTOR.message_types_by_name['PrivateKey'] = _PRIVATEKEY
DESCRIPTOR.enum_types_by_name['KeyType'] = _KEYTYPE
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
PublicKey = _reflection.GeneratedProtocolMessageType('PublicKey', (_message.Message,), dict(
DESCRIPTOR = _PUBLICKEY,
__module__ = 'crypto_pb2'
# @@protoc_insertion_point(class_scope:crypto.pb.PublicKey)
))
_sym_db.RegisterMessage(PublicKey)
PrivateKey = _reflection.GeneratedProtocolMessageType('PrivateKey', (_message.Message,), dict(
DESCRIPTOR = _PRIVATEKEY,
__module__ = 'crypto_pb2'
# @@protoc_insertion_point(class_scope:crypto.pb.PrivateKey)
))
_sym_db.RegisterMessage(PrivateKey)
DESCRIPTOR._options = None
# @@protoc_insertion_point(module_scope)

307
dpgpid
View File

@ -1,6 +1,6 @@
#!/bin/sh
#!/usr/bin/env python3
# link: https://git.p2p.legal/aya/dpgpid/
# desc: dpgpid (Decentralized PGP IDentifiers) shares PGP keys with DIDs on IPFS
# desc: generate did:ipid keys from gpg
# Copyleft 2022 Yann Autissier <aya@asycn.io>
@ -12,58 +12,273 @@
# 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.
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
set -eu
import argparse
import configparser
import gpg
import json
import logging as log
from SecureBytes import clearmem
import os
import re
import struct
import sys
import time
import warnings
DATE=$(date -u +%FT%TZ%Z)
USAGE='[--help] [--version] show'
VERSION='0.0.1'
sys.path.append(os.path.abspath('../py-ipfs-http-client/'))
import ipfshttpclient
import key
dpgpid() {
while test $# != 0; do
case "$1" in
-h|--help)
help
;;
-v|--version)
version
;;
--)
shift
break
;;
-*)
usage
;;
show)
show
;;
*)
break
;;
esac
shift
done
}
__version__='0.1.0'
help() {
usage
}
class dpgpid:
def __init__(self):
self.parser = argparse.ArgumentParser(description="""
Generate did:ipid keys from gpg.
It converts a gpg key to a Decentralized IDentifier.""")
self.parser.add_argument(
'action',
help="{list,publish}",
nargs="?",
)
self.parser.add_argument(
"-d",
"--debug",
action="store_true",
help="show debug informations",
)
self.parser.add_argument(
"-q",
"--quiet",
action="store_true",
help="show only errors",
)
self.parser.add_argument(
"-t",
"--type",
choices=['dns', 'ipid','key'],
default="ipid",
dest="type",
help="did output format, default: ipid",
)
self.parser.add_argument(
"-v",
"--verbose",
action="store_true",
help="show more informations",
)
self.parser.add_argument(
"--version",
action="store_true",
help="show version and exit",
)
self.parser.add_argument(
'-u',
'--username',
dest="username",
)
self.parser.add_argument(
'-p',
'--password',
dest="password",
)
show() {
gpg --list-keys --with-colons |awk -F: '$1 == "pub" {print "did:pgp:"$5}'
}
def _check_args(self, args):
log.debug("dpgpid._check_args(%s)" % args)
if self.action not in ['list', 'publish']:
self.parser.error('dpgpid requires a valid action')
usage() {
printf "%s\n" "Usage: $(basename "$0") ${USAGE}"
}
def _cleanup(self):
log.debug("dpgpid._cleanup()")
self.ipfs.close()
version() {
printf "%s\n" "Version: $(basename "$0") v${VERSION}"
}
def _cli(self, argv):
args = self.parser.parse_args(argv)
vars(self).update(vars(args))
dpgpid "$@"
# display version
if args.version:
version()
sys.exit()
# define log format
log_format='%(asctime)s %(levelname)s: %(message)s'
log_datefmt='%Y/%m/%d %H:%M:%S'
if args.debug:
log_level='DEBUG'
elif args.quiet:
log_level='ERROR'
elif args.verbose:
log_level='INFO'
else:
log_level='WARNING'
log.basicConfig(format=log_format, datefmt=log_datefmt, level=log_level)
self._check_args(args)
self._load_config()
self.gpg = gpg.Context(armor=True, offline=True)
try:
self.ipfs = ipfshttpclient.connect('/ip4/172.18.0.2/tcp/5001/',session=True,timeout=3)
except Exception as e:
log.error('Unable to load ipfs client: %s' % e)
exit(2)
method = getattr(self, f'do_{self.type}', self._invalid_type)
return method()
def _invalid_type(self):
log.debug("dpgpid._invalid_type()")
self.parser.error(f"type {self.type} is not valid.")
def _load_config(self):
log.debug("dpgpid._load_config()")
self.config = configparser.RawConfigParser()
config_dir = os.path.join(os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), 'dpgpid')
log.debug("config_dir=%s" % config_dir)
self.config.read( [config_dir + '/dpgpid.conf'] )
def did_from_key(self):
log.debug("dpgpid.did_from_key()")
self.did = json.dumps({
"@context": "/ipfs/QmfS56jDfrXNaS6Xcsp3RJiXd2wyY7smeEAwyTAnL1RhEG",
"id": "did:ipid:" + self.key_id,
"created": self.key_created_at,
"publicKey": [{
"id": "did:ipid:" + self.key_id,
"type": "GpgVerificationKey2020",
"expires": self.key_expires_at,
"publicKeyGpg": self.key_public_key,
}, {
"id": "did:ipid:" + self.key_id,
"type": "JsonWebKey2020",
"expires": self.key_expires_at,
"publicKeyJwk": self.key_public_jwk,
}],
})
if self.key_updated_at:
self.did.update({"updated": self.key_updated_at,})
log.debug("dpgpid.did=%s" % self.did)
def do_list(self):
log.debug("dpgpid.do_list()")
gpgkeys = list(self.gpg.keylist(pattern=self.username, secret=True))
for gpgkey in gpgkeys:
self.key_from_gpg(gpgkey.fpr, self.password)
print("did:ipid:%s" % self.key_id)
self._cleanup()
def do_publish(self):
log.debug("dpgpid.do_publish()")
gpgkeys = list(self.gpg.keylist(pattern=self.username, secret=True))
if not gpgkeys:
log.warning(f"""Unable to find any key matching "{self.username}".""")
exit(1)
else:
gpgkey = gpgkeys[0]
log.info(f"""Found key id "{gpgkey.fpr}" matching "{self.username}".""")
self.key_from_gpg(gpgkey.fpr, self.password)
self.did_from_key()
self.did_cid = self.ipfs.add_json(self.did)
log.debug('dpgpid.did_cid=%s' % self.did_cid)
self.did_ipns = self.ipfs.name.publish(ipfs_path='/ipfs/' + self.did_cid, key=self.key_id)['Name']
log.debug('dpgpid.did_ipns=%s' % self.did_ipns)
self._cleanup()
def do_key(self):
log.debug("dpgpid.do_key()")
gpgkeys = list(self.gpg.keylist(pattern=self.username, secret=True))
if not gpgkeys:
log.warning(f"""Unable to find any key matching "{self.username}".""")
exit(1)
else:
gpgkey = gpgkeys[0]
log.info(f"""Found key id "{gpgkey.fpr}" matching "{self.username}".""")
self.key_from_gpg(gpgkey.fpr, self.password)
self.did_from_key()
self.did_cid = self.ipfs.add_json(self.did)
log.debug('dpgpid.did_cid=%s' % self.did_cid)
self.did_ipns = self.ipfs.name.publish(ipfs_path='/ipfs/' + self.did_cid, key=self.key_id)['Name']
log.debug('dpgpid.did_ipns=%s' % self.did_ipns)
didkit.keyToDID('key', self.key_secret_jwk)
self._cleanup()
def do_show(self):
log.debug("dpgpid.do_show()")
gpgkeys = list(self.gpg.keylist(pattern=self.username, secret=True))
if not gpgkeys:
log.warning(f"""Unable to find any key matching "{self.username}".""")
exit(1)
else:
gpgkey = gpgkeys[0]
log.info(f"""Found key id "{gpgkey.fpr}" matching "{self.username}".""")
self.key_from_gpg(gpgkey.fpr, self.password)
self.did_from_key()
print(self.did)
self._cleanup()
def ipfs_peerid(self, args):
log.debug("dpgpid.ipfs_peerid(%s)" % args)
try:
self.ipfs_peerid = self.ipfs.id()['ID']
except Exception as e:
log.error(f'Unable to get ipfs peer id: {e}')
exit()
log.debug('dpgpid.ipfs_peerid=%s' % self.ipfs_peerid)
def key_from_gpg(self, username, password):
log.debug("dpgpid.key_from_gpg(%s, %s)" % (username, password))
try:
key = keygen.keygen()
key.gpg = gpg.Context(armor=True, offline=True)
key.gpg.set_passphrase_cb(key.gpg_passphrase_cb)
key.username = username
key.password = password
key.ed25519_from_gpg()
key.pem_pkcs8_from_ed25519()
key.libp2p_from_ed25519()
key.b58mh_from_libp2p()
key.jwk_from_ed25519()
self.key_created_at = str(key.pgpy.created)
self.key_expires_at = str(key.pgpy.expires_at)
self.key_fingerprint = key.gpg_secret_key.fpr
self.key_id = key.ed25519_public_b58mh
self.key_is_expired = key.gpg_secret_key.expired
self.key_is_revoked = key.gpg_secret_key.revoked
self.key_public_key = str(key.pgpy.pubkey)
self.key_public_jwk = key.ed25519_public_jwk
self.key_secret_jwk = key.ed25519_secret_jwk
self.key_secret_pem = key.ed25519_secret_pem_pkcs8
self.key_signers = key.pgpy.signers
self.key_uids = key.gpg_secret_key.uids
self.key_updated_at = key.gpg_secret_key.last_update
log.debug("dpgpid.key.created_at=%s" % self.key_created_at)
log.debug("dpgpid.key.expires_at=%s" % self.key_expires_at)
log.debug("dpgpid.key.fingerprint=%s" % self.key_fingerprint)
log.debug("dpgpid.key.id=%s" % self.key_id)
log.debug("dpgpid.key.is_expired=%s" % self.key_is_expired)
log.debug("dpgpid.key.is_revoked=%s" % self.key_is_revoked)
log.debug("dpgpid.key.public_key=%s" % self.key_public_key)
log.debug("dpgpid.key.public_jwk=%s" % self.key_public_jwk)
log.debug("dpgpid.key.secret_jwk=%s" % self.key_secret_jwk)
log.debug("dpgpid.key.signers=%s" % self.key_signers)
log.debug("dpgpid.key.uids=%s" % self.key_uids)
log.debug("dpgpid.key.updated_at=%s" % self.key_updated_at)
except Exception as e:
log.error(f'Unable to get key from gpg: {e}')
exit(2)
def main(argv=None):
if argv is None:
argv = sys.argv[1:]
return dpgpid()._cli(argv)
def version(version=__version__):
print("%s v%s" % (sys.argv[0],version))
if __name__ == "__main__":
sys.exit(main())

581
key/__init__.py Normal file
View File

@ -0,0 +1,581 @@
#!/usr/bin/env python3
# link: https://git.p2p.legal/aya/dpgpid/
# desc: dpgpid builds a decentralized gpg world of trust with did over ipfs
# Copyleft 2022 Yann Autissier <aya@asycn.io>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero 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 Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from cryptography.hazmat.primitives import serialization
from jwcrypto.jwk import JWK
from SecureBytes import clearmem
import base36
import base58
import base64
import duniterpy.key
import gpg
import logging as log
import pgpy
import pynentry
import re
import warnings
def from_args(args, config):
log.debug("key.from_args(%s, %s)" % (args, config))
from key import ed25519
if args.gpg:
_gpg = gpg.Context(armor=True, offline=True)
return from_gpg(_gpg, args.username[0], args.password)
else:
scrypt_params = duniterpy.key.scrypt_params.ScryptParams(
int(config.get('scrypt', 'n')) if config and config.has_option('scrypt', 'n') else 4096,
int(config.get('scrypt', 'r')) if config and config.has_option('scrypt', 'r') else 16,
int(config.get('scrypt', 'p')) if config and config.has_option('scrypt', 'p') else 1,
int(config.get('scrypt', 'sl')) if config and config.has_option('scrypt', 'sl') else 32,
)
if args.input:
return from_file(args.input, args.password, scrypt_params)
else:
if args.mnemonic:
return from_mnemonic(' '.join(args.username), scrypt_params)
else:
return from_credentials(args.username[0], args.password, scrypt_params)
def from_credentials(username, password=None, scrypt_params=None):
log.debug("key.from_credentials(%s, %s, %s)" % (username, password, scrypt_params))
try:
if not password:
with pynentry.PynEntry() as p:
p.description = f"""Please enter the passord for username "{username}"."""
p.prompt = 'Passsord:'
try:
password = p.get_pin()
except pynentry.PinEntryCancelled:
log.warning('Cancelled! Goodbye.')
exit(1)
return from_duniterpy(duniterpy.key.SigningKey.from_credentials(
username,
password,
scrypt_params
))
except Exception as e:
log.error(f'Unable to get key from credentials: {e}')
exit(2)
def from_duniterpy(duniterpy):
log.debug("key.from_duniterpy(%s)" % duniterpy)
key = ed25519.from_duniterpy(duniterpy)
key.duniterpy = duniterpy
return key
def from_file(input_file, password=None, scrypt_params=None):
log.debug("key.from_file(%s, %s, %s)" % (input_file, password, scrypt_params))
try:
with open(input_file, 'r') as file:
lines = file.readlines()
if len(lines) > 0:
line = lines[0].strip()
regex_ewif = re.compile('^Type: EWIF$')
regex_jwk = re.compile('^\\s*{\\s*"crv":\\s*"Ed25519",\\s*"d":\\s*"(.)+",\\s*"kty":\\s*"OKP",\\s*"x":\\s*"(.)+"\\s*}')
regex_nacl = re.compile('^\\s*{\\s*"priv":\\s*"[0-9a-fA-F]+",\\s*"verify":\\s*"[0-9a-fA-F]+",\\s*"sign":\\s*"[0-9a-fA-F]+"\\s*}')
regex_pem = re.compile('^-----BEGIN PRIVATE KEY-----$')
regex_pubsec = re.compile('^Type: PubSec$')
regex_seed = re.compile('^[0-9a-fA-F]{64}$')
regex_ssb = re.compile('\\s*{\\s*"curve":\\s*"ed25519",\\s*"public":\\s*"(.+)\\.ed25519",\\s*"private":\\s*"(.+)\\.ed25519",\\s*"id":\\s*"@(.+).ed25519"\\s*}')
regex_wif = re.compile('^Type: WIF$')
if re.search(regex_ewif, line):
log.info("input file format detected: ewif")
if not password:
with pynentry.PynEntry() as p:
p.description = f"""Data in EWIF file is encrypted.
Please enter a password to decrypt seed.
"""
p.prompt = 'Passphrase:'
try:
password = p.get_pin()
except pynentry.PinEntryCancelled:
log.warning('Cancelled! Goodbye.')
exit(1)
return from_duniterpy(duniterpy.key.SigningKey.from_ewif_file(input_file, password))
elif re.search(regex_jwk, line):
log.info("input file format detected: jwk")
return from_jwk(JWK.from_json(line))
elif re.search(regex_nacl, line):
log.info("input file format detected: nacl")
return from_duniterpy(duniterpy.key.SigningKey.from_private_key(input_file))
elif re.search(regex_pem, line):
log.info("input file format detected: pem")
return from_pem(''.join(lines).encode())
elif re.search(regex_pubsec, line):
log.info("input file format detected: pubsec")
return from_duniterpy(duniterpy.key.SigningKey.from_pubsec_file(input_file))
elif re.search(regex_seed, line):
log.info("input file format detected: seed")
return from_duniterpy(duniterpy.key.SigningKey.from_seedhex_file(input_file))
elif re.search(regex_ssb, line):
log.info("input file format detected: ssb")
return from_duniterpy(duniterpy.key.SigningKey.from_ssb_file(input_file))
elif re.search(regex_wif, line):
log.info("input file format detected: wif")
return from_duniterpy(duniterpy.key.SigningKey.from_wif_file(input_file))
elif len(line.split(' ')) == 12:
log.info("input file format detected: mnemonic")
return from_mnemonic(line, scrypt_params)
elif len(lines) > 1:
log.info("input file format detected: credentials")
return from_credentials(line, lines[1].strip(), scrypt_params)
else:
raise NotImplementedError('unknown input file format.')
else:
raise IOError('empty file.')
except UnicodeDecodeError as e:
try:
with open(input_file, 'rb') as file:
lines = file.readlines()
if len(lines) > 0:
line = lines[0].strip()
regex_dewif = re.compile(b'^\x00\x00\x00\x01\x00\x00\x00\x01')
regex_p2p = re.compile(b'^\x08\x01\x12@')
if re.search(regex_dewif, line):
log.info("input file format detected: dewif")
if not password:
with pynentry.PynEntry() as p:
p.description = f"""Data in DEWIF file is encrypted.
Please enter a password to decrypt seed.
"""
p.prompt = 'Passphrase:'
try:
password = p.get_pin()
except pynentry.PinEntryCancelled:
log.warning('Cancelled! Goodbye.')
exit(1)
return from_duniterpy(duniterpy.key.SigningKey.from_dewif_file(input_file, password))
if re.search(regex_p2p, line):
log.info("input file format detected: p2p")
return from_libp2p(line)
else:
raise NotImplementedError('unknown input file format.')
else:
raise IOError('empty file.')
except Exception as e:
log.error(f'Unable to get key from input file {input_file}: {e}')
exit(2)
except Exception as e:
log.error(f'Unable to get key from input file {input_file}: {e}')
exit(2)
def from_gpg(_gpg, username, password=None):
log.debug("key.from_gpg(%s, %s, %s)" % (_gpg, username, password))
try:
secret_keys = list(_gpg.keylist(pattern=username, secret=True))
log.debug("key.secret_keys=%s" % secret_keys)
if not secret_keys:
log.warning(f"""Unable to find any key matching "{username}".""")
exit(1)
else:
_gpg.secret_key = secret_keys[0]
log.info(f"""Found key id "{_gpg.secret_key.fpr}" matching "{username}".""")
log.debug("key._gpg.secret_key.expired=%s" % _gpg.secret_key.expired)
log.debug("key._gpg.secret_key.fpr=%s" % _gpg.secret_key.fpr)
log.debug("key._gpg.secret_key.revoked=%s" % _gpg.secret_key.revoked)
log.debug("key._gpg.secret_key.uids=%s" % _gpg.secret_key.uids)
log.debug("key._gpg.secret_key.owner_trust=%s" % _gpg.secret_key.owner_trust)
log.debug("key._gpg.secret_key.last_update=%s" % _gpg.secret_key.last_update)
if password:
gpg_passphrase_cb = gpg_passphrase(password).cb
_gpg.set_passphrase_cb(gpg_passphrase_cb)
_gpg.set_pinentry_mode(gpg.constants.PINENTRY_MODE_LOOPBACK)
_gpg.public_armor = _gpg.key_export(_gpg.secret_key.fpr)
_gpg.secret_armor = _gpg.key_export_secret(_gpg.secret_key.fpr)
if not _gpg.secret_armor:
log.error(f"""Unable to export gpg secret key id "{_gpg.secret_key.fpr}" for username "{username}". Please check your password!""")
exit(2)
with warnings.catch_warnings():
# remove CryptographyDeprecationWarning about deprecated
# SymmetricKeyAlgorithm IDEA, CAST5 and Blowfish (PGPy v0.5.4)
warnings.simplefilter('ignore')
_pgpy, _ = pgpy.PGPKey.from_blob(_gpg.secret_armor)
key = from_pgpy(_pgpy, password)
key.gpg = _gpg
return key
except Exception as e:
log.error(f'Unable to get key from gpg: {e}')
exit(2)
def from_jwk(jwk):
log.debug("key.from_jwk(%s)" % jwk)
key = ed25519.from_jwk(jwk)
key.jwk = jwk
return key
def from_libp2p(libp2p):
log.debug("key.from_libp2p(%s)" % libp2p)
return ed25519.from_libp2p(libp2p)
def from_mnemonic(mnemonic, scrypt_params=None):
log.debug("key.from_mnemonic(%s, %s)" % (mnemonic, scrypt_params))
try:
return from_duniterpy(duniterpy.key.SigningKey.from_dubp_mnemonic(
mnemonic,
scrypt_params
))
except Exception as e:
log.error(f'Unable to get key from mnemonic: {e}')
exit(2)
def from_pem(pem):
log.debug("key.from_pem(%s)" % pem)
return ed25519.from_pem(pem)
def from_pgpy(_pgpy, password=None):
log.debug("key.from_pgpy(%s, %s)" % (_pgpy, password))
try:
log.debug("key._pgpy.fingerprint.keyid=%s" % _pgpy.fingerprint.keyid)
log.debug("key._pgpy.is_protected=%s" % _pgpy.is_protected)
_pgpy.key_type = pgpy_key_type(_pgpy)
if _pgpy.is_protected:
if not password:
with pynentry.PynEntry() as p:
p.description = f"""The exported pgp key id "{_pgpy.fingerprint.keyid}" is password protected.
Please enter the passphrase again to unlock it.
"""
p.prompt = 'Passphrase:'
try:
password = p.get_pin()
except pynentry.PinEntryCancelled:
log.warning('Cancelled! Goodbye.')
exit(1)
try:
with warnings.catch_warnings():
# remove CryptographyDeprecationWarning about deprecated
# SymmetricKeyAlgorithm IDEA, CAST5 and Blowfish (PGPy v0.5.4)
warnings.simplefilter('ignore')
with _pgpy.unlock(password):
assert _pgpy.is_unlocked
log.debug("key._pgpy.is_unlocked=%s" % _pgpy.is_unlocked)
key = ed25519.from_pgpy(_pgpy)
except Exception as e:
log.error(f"""Unable to unlock pgp secret key id "{pgpy.fingerprint.keyid}": {e}""")
exit(2)
else:
key = ed25519.from_pgpy(_pgpy)
key.pgpy = _pgpy
return key
except Exception as e:
log.error(f'Unable to get key from pgpy: {e}')
exit(2)
def pgpy_key_type(_pgpy):
log.debug("key.pgpy_key_type(%s)" % _pgpy)
if isinstance(_pgpy._key.keymaterial, pgpy.packet.fields.RSAPriv):
key_type = 'RSA'
elif isinstance(_pgpy._key.keymaterial, pgpy.packet.fields.DSAPriv):
key_type = 'DSA'
elif isinstance(_pgpy._key.keymaterial, pgpy.packet.fields.ElGPriv):
key_type = 'ElGamal'
elif isinstance(_pgpy._key.keymaterial, pgpy.packet.fields.ECDSAPriv):
key_type = 'ECDSA'
elif isinstance(_pgpy._key.keymaterial, pgpy.packet.fields.EdDSAPriv):
key_type = 'EdDSA'
elif isinstance(_pgpy._key.keymaterial, pgpy.packet.fields.ECDHPriv):
key_type = 'ECDH'
else:
key_type = 'undefined'
log.debug("key.pgpy_key_type().key_type=%s" % key_type)
return key_type
class gpg_passphrase():
def __init__(self, password):
log.debug("gpg_passphrase().__init__(%s)" % password)
self.password = password
def cb(self, uid_hint, passphrase_info, prev_was_bad):
log.debug("gpg_passphrase().cb(%s, %s, %s)" % (uid_hint, passphrase_info, prev_was_bad))
return self.password
class key():
def __init__(self):
log.debug("key().__init__()")
self.algorithm = 'undefined'
self.cryptography = None
def __del__(self):
log.debug("key().__del__()")
self._cleanup()
def _cleanup(self):
log.debug("key()._cleanup()")
if hasattr(self, 'duniterpy'):
if hasattr(self.duniterpy, 'seed') and self.duniterpy.seed:
clearmem(self.duniterpy.seed)
log.debug("cleared: key().duniterpy.seed")
if hasattr(self.duniterpy, 'sk') and self.duniterpy.sk:
clearmem(self.duniterpy.sk)
log.debug("cleared: key().duniterpy.sk")
if hasattr(self, 'gpg'):
if hasattr(self.gpg, 'secret_armor') and self.gpg.secret_armor:
clearmem(self.gpg.secret_armor)
log.debug("cleared: key().gpg.secret_armor")
if hasattr(self, 'jwk'):
if hasattr(self, 'secret_jwk') and self.secret_jwk:
clearmem(self.secret_jwk)
log.debug("cleared: key().secret_jwk")
if hasattr(self.jwk, 'd') and self.jwk.d:
clearmem(self.jwk.d)
log.debug("cleared: key().jwk.d")
if hasattr(self, 'pgpy'):
if hasattr(self.pgpy._key.keymaterial, 'p') and self.pgpy._key.keymaterial.p and not isinstance(self.pgpy._key.keymaterial.p, pgpy.packet.fields.ECPoint):
clearmem(self.pgpy._key.keymaterial.p)
log.debug("cleared: key().pgpy._key.material.p")
if hasattr(self.pgpy._key.keymaterial, 'q') and self.pgpy._key.keymaterial.q:
clearmem(self.pgpy._key.keymaterial.q)
log.debug("cleared: key().pgpy._key.material.q")
if hasattr(self.pgpy._key.keymaterial, 's') and self.pgpy._key.keymaterial.s:
clearmem(self.pgpy._key.keymaterial.s)
log.debug("cleared: key().pgpy._key.material.s")
if hasattr(self, 'secret_b36mf') and self.secret_b36mf:
clearmem(self.secret_b36mf)
log.debug("cleared: key().secret_b36mf")
if hasattr(self, 'secret_b58mf') and self.secret_b58mf:
clearmem(self.secret_b58mf)
log.debug("cleared: key().secret_b58mf")
if hasattr(self, 'secret_b58mh') and self.secret_b58mh:
clearmem(self.secret_b58mh)
log.debug("cleared: key().secret_b58mh")
if hasattr(self, 'secret_b64mh') and self.secret_b64mh:
clearmem(self.secret_b64mh)
log.debug("cleared: key().secret_b64mh")
if hasattr(self, 'secret_base58') and self.secret_base58:
clearmem(self.secret_base58)
log.debug("cleared: key().secret_base58")
if hasattr(self, 'secret_base64') and self.secret_base64:
clearmem(self.secret_base64)
log.debug("cleared: key().secret_base64")
if hasattr(self, 'secret_cidv1') and self.secret_cidv1:
clearmem(self.secret_cidv1)
log.debug("cleared: key().secret_cidv1")
if hasattr(self, 'secret_libp2p') and self.secret_libp2p:
clearmem(self.secret_libp2p)
log.debug("cleared: key().secret_libp2p")
if hasattr(self, 'secret_pem_pkcs8') and self.secret_pem_pkcs8:
clearmem(self.secret_pem_pkcs8)
log.debug("cleared: key().secret_pem_pkcs8")
if hasattr(self, 'secret_proto2') and self.secret_proto2:
clearmem(self.secret_proto2)
log.debug("cleared: key().secret_proto2")
def to_b36mf(self):
log.debug("key().to_b36mf()")
if not hasattr(self, 'public_cidv1') or not hasattr(self, 'secret_cidv1'):
self.to_cidv1()
try:
self.public_b36mf = 'k' + base36.dumps(int.from_bytes(self.public_cidv1, byteorder='big'))
self.secret_b36mf = 'k' + base36.dumps(int.from_bytes(self.secret_cidv1, byteorder='big'))
except Exception as e:
log.error(f'Unable to get b36mf from cidv1: {e}')
exit(2)
log.debug("key().public_b36mf=%s" % self.public_b36mf)
log.debug("key().secret_b36mf=%s" % self.secret_b36mf)
def to_b58mf(self):
log.debug("key().to_b58mf()")
if not hasattr(self, 'public_cidv1') or not hasattr(self, 'secret_cidv1'):
self.to_cidv1()
try:
self.public_b58mf = 'z' + base58.b58encode(self.public_cidv1).decode('ascii')
self.secret_b58mf = 'z' + base58.b58encode(self.secret_cidv1).decode('ascii')
except Exception as e:
log.error(f'Unable to get b58mf from cidv1: {e}')
exit(2)
log.debug("key().public_b58mf=%s" % self.public_b58mf)
log.debug("key().secret_b58mf=%s" % self.secret_b58mf)
def to_b58mh(self):
log.debug("key().to_b58mh()")
if not hasattr(self, 'public_libp2p') or not hasattr(self, 'secret_libp2p'):
self.to_libp2p()
try:
self.public_b58mh = base58.b58encode(self.public_libp2p).decode('ascii')
self.secret_b58mh = base58.b58encode(self.secret_libp2p).decode('ascii')
except Exception as e:
log.error(f'Unable to get b58mh from libp2p: {e}')
exit(2)
log.debug("key().public_b58mh=%s" % self.public_b58mh)
log.debug("key().secret_b58mh=%s" % self.secret_b58mh)
def to_b64mh(self):
log.debug("key().to_b64mh()")
if not hasattr(self, 'public_libp2p') or not hasattr(self, 'secret_libp2p'):
self.to_libp2p()
try:
self.public_b64mh = base64.b64encode(self.public_libp2p).decode('ascii')
self.secret_b64mh = base64.b64encode(self.secret_libp2p).decode('ascii')
except Exception as e:
log.error(f'Unable to get b64mh from libp2p: {e}')
exit(2)
log.debug("key().public_b64mh=%s" % self.public_b64mh)
log.debug("key().secret_b64mh=%s" % self.secret_b64mh)
def to_base58(self):
log.debug("key().to_base58()")
try:
self.public_base58 = base58.b58encode(self.public_bytes).decode('ascii')
self.secret_base58 = base58.b58encode(self.secret_bytes).decode('ascii')
except Exception as e:
log.error(f'Unable to get base58: {e}')
exit(2)
log.debug("key().public_base58=%s" % self.public_base58)
log.debug("key().secret_base58=%s" % self.secret_base58)
def to_base64(self):
log.debug("key().to_base64()")
try:
self.public_base64 = base64.b64encode(self.public_bytes).decode('ascii')
self.secret_base64 = base64.b64encode(self.secret_bytes).decode('ascii')
except Exception as e:
log.error(f'Unable to get base64: {e}')
exit(2)
log.debug("key().public_base64=%s" % self.public_base64)
log.debug("key().secret_base64=%s" % self.secret_base64)
def to_cidv1(self):
log.debug("key().to_cidv1()")
if not hasattr(self, 'public_libp2p') or not hasattr(self, 'secret_libp2p'):
self.to_libp2p()
try:
# \x01: multicodec cid prefix = CIDv1
# \x72: multicodec content prefix = libp2p-key
self.public_cidv1 = b'\x01\x72' + self.public_libp2p
self.secret_cidv1 = b'\x01\x72' + self.secret_libp2p
except Exception as e:
log.error(f'Unable to get cidv1: {e}')
exit(2)
log.debug("key().public_cidv1=%s" % self.public_cidv1)
log.debug("key().secret_cidv1=%s" % self.secret_cidv1)
def to_duniterpy(self):
log.debug("key().to_duniterpy()")
raise NotImplementedError(f"key().to_duniterpy() is not implemented for algorithm {self.algorithm}")
def to_file(self, output_file, file_format=None, password=None):
log.debug("key().to_file(%s, %s, %s)" % (output_file, file_format, password))
try:
if file_format == 'dewif':
if not hasattr(self, 'duniterpy'):
self.to_duniterpy()
if not password:
with pynentry.PynEntry() as p:
p.description = f"""Data in DEWIF file needs to be encrypted.
Please enter a password to encrypt seed.
"""
p.prompt = 'Passphrase:'
try:
password = p.get_pin()
except pynentry.PinEntryCancelled:
log.warning('Cancelled! Goodbye.')
exit(1)
self.duniterpy.save_dewif_v1_file(output_file, password)
elif file_format == 'ewif':
if not hasattr(self, 'duniterpy'):
self.to_duniterpy()
if not password:
with pynentry.PynEntry() as p:
p.description = f"""Data in EWIF file needs to be encrypted.
Please enter a password to encrypt seed.
"""
p.prompt = 'Passphrase:'
try:
password = p.get_pin()
except pynentry.PinEntryCancelled:
log.warning('Cancelled! Goodbye.')
exit(1)
self.duniterpy.save_ewif_file(output_file, password)
elif file_format == 'jwk':
if not hasattr(self, 'jwk'):
self.to_jwk()
with open(output_file, "w") as file:
file.write(self.jwk.export())
elif file_format == 'nacl':
if not hasattr(self, 'duniterpy'):
self.to_duniterpy()
self.duniterpy.save_private_key(output_file)
elif file_format == 'p2p':
if not hasattr(self, 'secret_libp2p'):
self.to_libp2p()
with open(output_file, "wb") as file:
file.write(self.secret_libp2p)
elif file_format == 'pubsec':
if not hasattr(self, 'duniterpy'):
self.to_duniterpy()
self.duniterpy.save_pubsec_file(output_file)
elif file_format == 'seed':
if not hasattr(self, 'duniterpy'):
self.to_duniterpy()
self.duniterpy.save_seedhex_file(output_file)
elif file_format == 'wif':
if not hasattr(self, 'duniterpy'):
self.to_duniterpy()
self.duniterpy.save_wif_file(output_file)
else:
if not hasattr(self, 'secret_pem_pkcs8'):
self.to_pem_pkcs8()
with open(output_file, "w") as file:
file.write(self.secret_pem_pkcs8)
except Exception as e:
log.error(f'Unable to write key to output file {output_file}: {e}')
exit(2)
def to_jwk(self):
log.debug("key().to_jwk()")
try:
if not hasattr(self, 'jwk'):
self.jwk = JWK.from_pyca(self.cryptography)
self.public_jwk = self.jwk.export_public()
self.secret_jwk = self.jwk.export_private()
except Exception as e:
log.error(f'Unable to get jwk: {e}')
exit(2)
def to_libp2p(self):
log.debug("key().to_libp2p()")
try:
if not hasattr(self, 'public_proto2') or not hasattr(self, 'secret_proto2'):
self.to_proto2()
# \x00: multihash prefix = raw id
# \x24: multihash length = 36 bytes
self.public_libp2p = b'\x00$' + self.public_proto2
self.secret_libp2p = self.secret_proto2
except Exception as e:
log.error(f'Unable to get libp2p: {e}')
exit(2)
log.debug("key().public_libp2p=%s" % self.public_libp2p)
log.debug("key().secret_libp2p=%s" % self.secret_libp2p)
def to_pem_pkcs8(self):
log.debug("key().to_pem_pkcs8()")
try:
self.secret_pem_pkcs8 = self.cryptography.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
).decode('ascii')
except Exception as e:
log.error(f'Unable to get pem pkcs8: {e}')
exit(2)
log.debug("key().secret_pem_pkcs8=%s" % self.secret_pem_pkcs8)
def to_proto2(self):
log.debug("key().to_proto2()")
raise NotImplementedError(f"key().to_proto2() is not implemented for algorithm {self.algorithm}")

136
key/ed25519.py Normal file
View File

@ -0,0 +1,136 @@
#!/usr/bin/env python3
# link: https://git.p2p.legal/aya/dpgpid/
# desc: dpgpid builds a decentralized gpg world of trust with did over ipfs
# Copyleft 2022 Yann Autissier <aya@asycn.io>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero 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 Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from . import key
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization
from SecureBytes import clearmem
import duniterpy.key
import logging as log
def from_duniterpy(duniterpy):
log.debug("ed25519.from_duniterpy(%s)" % duniterpy)
try:
return ed25519(duniterpy.sk[:32])
except Exception as e:
log.error(f'Unable to get ed25519 from duniterpy: {e}')
exit(2)
def from_jwk(jwk):
log.debug("ed25519.from_jwk(%s)" % jwk)
try:
return ed25519(jwk._okp_pri().private_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PrivateFormat.Raw,
encryption_algorithm=serialization.NoEncryption()
))
except Exception as e:
log.error(f'Unable to get ed25519 from jwk: {e}')
exit(2)
def from_libp2p(libp2p):
log.debug("ed25519.from_libp2p(%s) % libp2p")
try:
return ed25519(libp2p.lstrip(b'\x08\x01\x12@')[:32])
except Exception as e:
log.error(f'Unable to get ed25519 from libp2p: {e}')
exit(2)
def from_pem(pem):
log.debug("ed25519.from_pem(%s)" % pem)
try:
return ed25519(serialization.load_pem_private_key(pem, password=None).private_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PrivateFormat.Raw,
encryption_algorithm=serialization.NoEncryption()
))
except Exception as e:
log.error(f'Unable to get ed25519 from pem: {e}')
exit(2)
def from_pgpy(_pgpy):
log.debug("ed25519.from_pgpy(%s)" % _pgpy)
try:
if _pgpy.key_type == 'RSA':
log.debug("ed25519._pgpy._key.keymaterial.p=%s" % _pgpy._key.keymaterial.p)
log.debug("ed25519._pgpy._key.keymaterial.q=%s" % _pgpy._key.keymaterial.q)
# rsa custom seed: sha256 hash of (p + q), where + is a string concatenation
# self.ed25519_seed_bytes = nacl.bindings.crypto_hash_sha256((rsa_int).to_bytes(rsa_len,byteorder='big'))
rsa_int = int(str(_pgpy._key.keymaterial.p) + str(_pgpy._key.keymaterial.q))
rsa_len = (rsa_int.bit_length() + 7) // 8
from cryptography.hazmat.primitives import hashes
digest = hashes.Hash(hashes.SHA256())
digest.update((rsa_int).to_bytes(rsa_len,byteorder='big'))
seed = digest.finalize()
# seed_bytes = nacl.bindings.crypto_hash_sha256((rsa_int).to_bytes(rsa_len,byteorder='big'))
elif _pgpy.key_type in ('ECDSA', 'EdDSA', 'ECDH'):
log.debug("ed25519._pgpy._key.keymaterial.s=%s" % _pgpy._key.keymaterial.s)
seed = _pgpy._key.keymaterial.s.to_bytes(32, byteorder='big')
else:
raise NotImplementedError(f"getting seed from pgpy key type {_pgpy.key_type} is not implemented.")
return ed25519(seed)
except Exception as e:
log.error(f'Unable to get ed25519 from pgpy: {e}')
exit(2)
class ed25519(key):
def __init__(self, seed: bytes):
log.debug("ed25519().__init__(%s)" % seed)
super().__init__()
self.algorithm = 'ed25519'
self.cryptography = Ed25519PrivateKey.from_private_bytes(seed)
self.public_bytes = self.cryptography.public_key().public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw,
)
self.secret_bytes = seed + self.public_bytes
self.seed_bytes = seed
def _cleanup(self):
log.debug("ed25519()._cleanup()")
if hasattr(self, 'secret_bytes') and self.secret_bytes:
clearmem(self.secret_bytes)
log.debug("cleared: ed25519().secret_bytes")
if hasattr(self, 'seed_bytes') and self.seed_bytes:
clearmem(self.seed_bytes)
log.debug("cleared: ed25519().seed_bytes")
super()._cleanup()
def to_duniterpy(self):
log.debug("ed25519().to_duniterpy()")
try:
if not hasattr(self, 'duniterpy'):
self.duniterpy = duniterpy.key.SigningKey(self.seed_bytes)
except Exception as e:
log.error(f'Unable to get duniterpy: {e}')
exit(2)
log.debug("ed25519().duniterpy.seed: %s" % self.duniterpy.seed)
def to_proto2(self):
log.debug("ed25519().to_proto2()")
try:
## libp2p Protocol Buffer serialization
self.public_proto2 = b'\x08\x01\x12 ' + self.public_bytes
self.secret_proto2 = b'\x08\x01\x12@' + self.secret_bytes
except Exception as e:
log.error(f'Unable to get proto2: {e}')
exit(2)
log.debug("ed25519().public_proto2=%s" % self.public_proto2)
log.debug("ed25519().secret_proto2=%s" % self.secret_proto2)

819
keygen
View File

@ -1,14 +1,8 @@
#!/usr/bin/env python3
# link: https://git.p2p.legal/aya/dpgpid/
# desc: generate ed25519 keys for duniter and ipfs from gpg
# desc: dpgpid builds a decentralized gpg world of trust with did over ipfs
# Copyleft 2022 Yann Autissier <aya@asycn.io>
# all crypto science belongs to Pascal Engélibert <tuxmain@zettascript.org>
# coming from files available at https://git.p2p.legal/qo-op/Astroport.ONE/tools
# gpgme stuff has been provided by Ben McGinnes
# and comes from http://files.au.adversary.org/crypto/gpgme-python-howto.html
# gpg key extraction is taken from work of Simon Vareille available at
# https://gist.github.com/SimonVareille/fda49baf5f3e15b5c88e25560aeb2822
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@ -24,28 +18,13 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import argparse
import base58
import base64
import configparser
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import serialization
import duniterpy.key
import gpg
from jwcrypto import jwk
import logging as log
import nacl.bindings
import nacl.encoding
import pgpy
import pynentry
from SecureBytes import clearmem
import os
import re
import struct
import sys
import time
import warnings
__version__='0.0.5'
from about import __version__
import key
class keygen:
def __init__(self):
@ -62,7 +41,7 @@ class keygen:
self.parser.add_argument(
"-f",
"--format",
choices=['ewif', 'jwk', 'nacl','pb2','pem','pubsec','seed','wif'],
choices=['ewif', 'jwk', 'nacl','p2p','pem','pubsec','seed','wif'],
default=None,
dest="format",
help="output file format, default: pem (pkcs8)",
@ -77,7 +56,7 @@ class keygen:
"-i",
"--input",
dest="input",
help="read ed25519 key from file FILE, autodetect format: {credentials,ewif,jwk,nacl,mnemonic,pb2,pubsec,seed,wif}",
help="read ed25519 key from file FILE, autodetect format: {credentials,ewif,jwk,nacl,mnemonic,p2p,pem,pubsec,seed,wif}",
metavar='FILE',
)
self.parser.add_argument(
@ -102,9 +81,9 @@ class keygen:
)
self.parser.add_argument(
"-p",
"--prefix",
action="store_true",
help="prefix output text with key type",
"--password",
dest="password",
help="user password for duniter, gpg key and file encryption",
)
self.parser.add_argument(
"-q",
@ -121,7 +100,7 @@ class keygen:
self.parser.add_argument(
"-t",
"--type",
choices=['b58mh','b64mh','base58','base64','duniter','ipfs','jwk'],
choices=['b36mf', 'b58mf', 'b58mh','b64mh','base58','base64','duniter','ipfs','jwk'],
default="base58",
dest="type",
help="output text format, default: base58",
@ -139,171 +118,16 @@ class keygen:
)
self.parser.add_argument(
'username',
nargs="?",
)
self.parser.add_argument(
'password',
nargs="?",
help="username, mnemonic or gpg key",
nargs="*",
)
def _check_args(self, args):
log.debug("keygen._check_args(%s)" % args)
if self.input is None and self.username is None:
log.debug("keygen()._check_args(%s)" % args)
if self.input is None and not len(self.username):
self.parser.error('keygen requires an input file or a username')
def _cleanup(self):
log.debug("keygen._cleanup()")
if hasattr(self, 'duniterpy'):
if hasattr(self.duniterpy, 'seed') and self.duniterpy.seed:
clearmem(self.duniterpy.seed)
log.debug("cleared: keygen.duniterpy.seed")
if hasattr(self.duniterpy, 'sk') and self.duniterpy.sk:
clearmem(self.duniterpy.sk)
log.debug("cleared: keygen.duniterpy.sk")
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, 'ed25519_secret_bytes') and self.ed25519_secret_bytes:
clearmem(self.ed25519_secret_bytes)
log.debug("cleared: keygen.ed25519_secret_bytes")
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_protobuf') and self.ed25519_secret_protobuf:
clearmem(self.ed25519_secret_protobuf)
log.debug("cleared: keygen.ed25515_secret_protobuf")
if hasattr(self, 'ed25519_seed_bytes') and self.ed25519_seed_bytes:
clearmem(self.ed25519_seed_bytes)
log.debug("cleared: keygen.ed25519_seed_bytes")
if hasattr(self, 'ipfs_privkey') and self.ipfs_privkey:
clearmem(self.ipfs_privkey)
log.debug("cleared: keygen.ipfs_privkey")
if hasattr(self, 'jwk'):
if hasattr(self.jwk, 'd') and self.jwk.d:
clearmem(self.jwk.d)
log.debug("cleared: keygen.jwk.d")
if hasattr(self, 'password') and self.password:
clearmem(self.password)
log.debug("cleared: keygen.password")
if hasattr(self, 'pgp_secret_armor') and self.pgp_secret_armor:
clearmem(self.pgp_secret_armor)
log.debug("cleared: keygen.pgp_secret_armor")
if hasattr(self, 'pgpy'):
if hasattr(self.pgpy._key.keymaterial, 'p') and self.pgpy._key.keymaterial.p and not isinstance(self.pgpy._key.keymaterial.p, pgpy.packet.fields.ECPoint):
clearmem(self.pgpy._key.keymaterial.p)
log.debug("cleared: keygen.pgpy._key.material.p")
if hasattr(self.pgpy._key.keymaterial, 'q') and self.pgpy._key.keymaterial.q:
clearmem(self.pgpy._key.keymaterial.q)
log.debug("cleared: keygen.pgpy._key.material.q")
if hasattr(self.pgpy._key.keymaterial, 's') and self.pgpy._key.keymaterial.s:
clearmem(self.pgpy._key.keymaterial.s)
log.debug("cleared: keygen.pgpy._key.material.s")
if hasattr(self, 'username') and self.username:
clearmem(self.username)
log.debug("cleared: keygen.username")
def _invalid_type(self):
log.debug("keygen._invalid_type()")
self.parser.error(f"type {self.type} is not valid.")
def _load_config(self):
log.debug("keygen._load_config()")
self.config = configparser.RawConfigParser()
config_dir = os.path.join(os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), 'dpgpid')
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()")
try:
if self.format == 'dewif':
if not hasattr(self, 'duniterpy'):
self.duniterpy_from_ed25519_seed_bytes()
if not self.password:
with pynentry.PynEntry() as p:
p.description = f"""Data in DEWIF file needs to be encrypted.
Please enter a password to encrypt seed.
"""
p.prompt = 'Passphrase:'
try:
self.password = p.get_pin()
except pynentry.PinEntryCancelled:
log.warning('Cancelled! Goodbye.')
self._cleanup()
exit(1)
self.duniterpy.save_dewif_v1_file(self.output, self.password)
elif self.format == 'ewif':
if not hasattr(self, 'duniterpy'):
self.duniterpy_from_ed25519_seed_bytes()
if not self.password:
with pynentry.PynEntry() as p:
p.description = f"""Data in EWIF file needs to be encrypted.
Please enter a password to encrypt seed.
"""
p.prompt = 'Passphrase:'
try:
self.password = p.get_pin()
except pynentry.PinEntryCancelled:
log.warning('Cancelled! Goodbye.')
self._cleanup()
exit(1)
self.duniterpy.save_ewif_file(self.output, self.password)
elif self.format == 'jwk':
if not hasattr(self, 'jwk'):
self.jwk_from_ed25519()
with open(self.output, "w") as file:
file.write(self.jwk.export())
elif self.format == 'nacl':
if not hasattr(self, 'duniterpy'):
self.duniterpy_from_ed25519_seed_bytes()
self.duniterpy.save_private_key(self.output)
elif self.format == 'pb2':
if not hasattr(self, 'ed25519_secret_protobuf'):
self.protobuf_from_ed25519()
with open(self.output, "wb") as file:
file.write(self.ed25519_secret_protobuf)
elif self.format == 'pubsec':
if not hasattr(self, 'duniterpy'):
self.duniterpy_from_ed25519_seed_bytes()
self.duniterpy.save_pubsec_file(self.output)
elif self.format == 'seed':
if not hasattr(self, 'duniterpy'):
self.duniterpy_from_ed25519_seed_bytes()
self.duniterpy.save_seedhex_file(self.output)
elif self.format == 'wif':
if not hasattr(self, 'duniterpy'):
self.duniterpy_from_ed25519_seed_bytes()
self.duniterpy.save_wif_file(self.output)
else:
if not hasattr(self, 'ed25519_secret_pem_pkcs8'):
self.pem_pkcs8_from_ed25519()
with open(self.output, "w") as file:
file.write(self.ed25519_secret_pem_pkcs8)
except Exception as e:
log.error(f'Unable to output file {self.output}: {e}')
self._cleanup()
exit(2)
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 _cli(self, argv):
args = self.parser.parse_args(argv)
vars(self).update(vars(args))
@ -324,573 +148,118 @@ class keygen:
else:
log_level='WARNING'
log.basicConfig(format=log_format, datefmt=log_datefmt, level=log_level)
log.debug("keygen.run(%s)" % argv)
log.debug("keygen()._cli(%s)" % argv)
self._check_args(args)
self._load_config()
self.gpg = gpg.Context(armor=True, offline=True)
self.gpg.set_passphrase_cb(self.gpg_passphrase_cb)
self.ed25519(args)
self.key = key.from_args(args, self.config)
method = getattr(self, f'do_{self.type}', self._invalid_type)
return method()
def b58mh_from_protobuf(self):
log.debug("keygen.b58mh_from_protobuf()")
try:
self.ed25519_public_b58mh = base58.b58encode(self.ed25519_public_protobuf).decode('ascii')
self.ed25519_secret_b58mh = base58.b58encode(self.ed25519_secret_protobuf).decode('ascii')
except Exception as e:
log.error(f'Unable to get b58mh from protobuf: {e}')
self._cleanup()
exit(2)
log.debug("keygen.ed25519_public_b58mh=%s" % self.ed25519_public_b58mh)
log.debug("keygen.ed25519_secret_b58mh=%s" % self.ed25519_secret_b58mh)
def _invalid_type(self):
log.debug("keygen()._invalid_type()")
self.parser.error(f"type {self.type} is not valid.")
def b64mh_from_protobuf(self):
log.debug("keygen.b64mh_from_protobuf()")
try:
self.ed25519_public_b64mh = base64.b64encode(self.ed25519_public_protobuf).decode('ascii')
self.ed25519_secret_b64mh = base64.b64encode(self.ed25519_secret_protobuf).decode('ascii')
except Exception as e:
log.error(f'Unable to get b64mh from protobuf: {e}')
self._cleanup()
exit(2)
log.debug("keygen.ed25519_public_b64mh=%s" % self.ed25519_public_b64mh)
log.debug("keygen.ed25519_secret_b64mh=%s" % self.ed25519_secret_b64mh)
def _load_config(self):
log.debug("keygen()._load_config()")
self.config = configparser.RawConfigParser()
config_dir = os.path.join(os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), 'dpgpid')
log.debug("config_dir=%s" % config_dir)
self.config.read( [config_dir + '/keygen.conf'] )
def base58_from_ed25519(self):
log.debug("keygen.base58_from_ed25519()")
try:
self.ed25519_public_base58 = base58.b58encode(self.ed25519_public_bytes).decode('ascii')
self.ed25519_secret_base58 = base58.b58encode(self.ed25519_secret_bytes).decode('ascii')
except Exception as e:
log.error(f'Unable to get base58 from ed25519: {e}')
self._cleanup()
exit(2)
log.debug("keygen.ed25519_public_base58=%s" % self.ed25519_public_base58)
log.debug("keygen.ed25519_secret_base58=%s" % self.ed25519_secret_base58)
def _output(self):
log.debug("keygen()._output()")
if self.output is None:
self._output_text()
else:
self._output_file()
os.chmod(self.output, 0o600)
def base64_from_ed25519(self):
log.debug("keygen.base64_from_ed25519()")
try:
self.ed25519_public_base64 = base64.b64encode(self.ed25519_public_bytes).decode('ascii')
self.ed25519_secret_base64 = base64.b64encode(self.ed25519_secret_bytes).decode('ascii')
except Exception as e:
log.error(f'Unable to get base64 from ed25519: {e}')
self._cleanup()
exit(2)
log.debug("keygen.ed25519_public_base64=%s" % self.ed25519_public_base64)
log.debug("keygen.ed25519_secret_base64=%s" % self.ed25519_secret_base64)
def _output_file(self):
log.debug("keygen()._output_file()")
self.key.to_file(self.output, self.format, self.password)
def _output_text(self):
log.debug("keygen()._output_text()")
if self.keys or not self.secret:
print("%s" % self.public_key)
if self.keys or self.secret:
print("%s" % self.secret_key)
def do_b36mf(self):
log.debug("keygen().do_b36mf()")
self.key.to_b36mf()
self.public_key = self.key.public_b36mf
self.secret_key = self.key.secret_b36mf
self._output()
def do_b58mf(self):
log.debug("keygen().do_b58mf()")
self.key.to_b58mf()
self.public_key = self.key.public_b58mf
self.secret_key = self.key.secret_b58mf
self._output()
def do_b58mh(self):
log.debug("keygen.do_b58mh()")
self.protobuf_from_ed25519()
self.b58mh_from_protobuf()
self._output(self.ed25519_public_b58mh, self.ed25519_secret_b58mh, 'pub: ', 'sec: ')
log.debug("keygen().do_b58mh()")
self.key.to_b58mh()
self.public_key = self.key.public_b58mh
self.secret_key = self.key.secret_b58mh
self._output()
def do_b64mh(self):
log.debug("keygen.do_b64mh()")
self.protobuf_from_ed25519()
self.b64mh_from_protobuf()
self._output(self.ed25519_public_b64mh, self.ed25519_secret_b64mh, 'pub: ', 'sec: ')
log.debug("keygen().do_b64mh()")
self.key.to_b64mh()
self.public_key = self.key.public_b64mh
self.secret_key = self.key.secret_b64mh
self._output()
def do_base58(self):
log.debug("keygen.do_base58()")
self.base58_from_ed25519()
self._output(self.ed25519_public_base58, self.ed25519_secret_base58, 'pub: ', 'sec: ')
log.debug("keygen().do_base58()")
self.key.to_base58()
self.public_key = self.key.public_base58
self.secret_key = self.key.secret_base58
self._output()
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: ')
log.debug("keygen().do_base64()")
self.key.to_base64()
self.public_key = self.key.public_base64
self.secret_key = self.key.secret_base64
self._output()
def do_duniter(self):
log.debug("keygen.do_duniter()")
log.debug("keygen().do_duniter()")
if not self.format:
self.format = 'pubsec'
self.base58_from_ed25519()
self._output(self.ed25519_public_base58, self.ed25519_secret_base58, 'pub: ', 'sec: ')
self.key.to_base58()
self.public_key = self.key.public_base58
self.secret_key = self.key.secret_base58
self._output()
def do_ipfs(self):
log.debug("keygen.do_ipfs()")
self.protobuf_from_ed25519()
self.b58mh_from_protobuf()
self.b64mh_from_protobuf()
self._output(self.ed25519_public_b58mh, self.ed25519_secret_b64mh, 'PeerID: ', 'PrivKEY: ')
log.debug("keygen().do_ipfs()")
self.key.to_b58mh()
self.key.to_b64mh()
self.public_key = self.key.public_b58mh
self.secret_key = self.key.secret_b64mh
self._output()
def do_jwk(self):
log.debug("keygen.do_jwk()")
self.jwk_from_ed25519()
self._output(self.jwk.export_public(), self.jwk.export_private(), 'pub: ', 'sec: ')
def duniterpy_from_credentials(self):
log.debug("keygen.duniterpy_from_credentials()")
try:
scrypt_params = duniterpy.key.scrypt_params.ScryptParams(
int(self.config.get('scrypt', 'n')) if self.config.has_option('scrypt', 'n') else 4096,
int(self.config.get('scrypt', 'r')) if self.config.has_option('scrypt', 'r') else 16,
int(self.config.get('scrypt', 'p')) if self.config.has_option('scrypt', 'p') else 1,
int(self.config.get('scrypt', 'sl')) if self.config.has_option('scrypt', 'sl') else 32,
)
if not self.password:
with pynentry.PynEntry() as p:
p.description = f"""Please enter the passord for username "{self.username}"."""
p.prompt = 'Passsord:'
try:
self.password = p.get_pin()
except pynentry.PinEntryCancelled:
log.warning('Cancelled! Goodbye.')
self._cleanup()
exit(1)
self.duniterpy = duniterpy.key.SigningKey.from_credentials(
self.username,
self.password,
scrypt_params
)
except Exception as e:
log.error(f'Unable to get duniter from credentials: {e}')
self._cleanup()
exit(2)
log.debug("keygen.duniterpy.seed: %s" % self.duniterpy.seed)
def duniterpy_from_ed25519_seed_bytes(self):
log.debug("keygen.duniterpy_from_ed25519_seed_bytes()")
try:
self.duniterpy = duniterpy.key.SigningKey(self.ed25519_seed_bytes)
except Exception as e:
log.error(f'Unable to get duniterpy from ed25519 seed bytes: {e}')
self._cleanup()
exit(2)
log.debug("keygen.duniterpy.seed: %s" % self.duniterpy.seed)
def duniterpy_from_file(self):
log.debug("keygen.duniterpy_from_file()")
try:
with open(self.input, 'r') as file:
lines = file.readlines()
if len(lines) > 0:
line = lines[0].strip()
regex_ewif = re.compile('^Type: EWIF$')
regex_jwk = re.compile('^\\s*{\\s*"crv":\\s*"Ed25519",\\s*"d":\\s*"(.)+",\\s*"kty":\\s*"OKP",\\s*"x":\\s*"(.)+"\\s*}')
regex_nacl = re.compile('^\\s*{\\s*"priv":\\s*"[0-9a-fA-F]+",\\s*"verify":\\s*"[0-9a-fA-F]+",\\s*"sign":\\s*"[0-9a-fA-F]+"\\s*}')
regex_pem = re.compile('^-----BEGIN PRIVATE KEY-----$')
regex_pubsec = re.compile('^Type: PubSec$')
regex_seed = re.compile('^[0-9a-fA-F]{64}$')
regex_ssb = re.compile('\\s*{\\s*"curve":\\s*"ed25519",\\s*"public":\\s*"(.+)\\.ed25519",\\s*"private":\\s*"(.+)\\.ed25519",\\s*"id":\\s*"@(.+).ed25519"\\s*}')
regex_wif = re.compile('^Type: WIF$')
if re.search(regex_ewif, line):
log.info("input file format detected: ewif")
if not self.password:
with pynentry.PynEntry() as p:
p.description = f"""Data in EWIF file is encrypted.
Please enter a password to decrypt seed.
"""
p.prompt = 'Passphrase:'
try:
self.password = p.get_pin()
except pynentry.PinEntryCancelled:
log.warning('Cancelled! Goodbye.')
self._cleanup()
exit(1)
self.duniterpy = duniterpy.key.SigningKey.from_ewif_file(self.input, self.password)
elif re.search(regex_jwk, line):
log.info("input file format detected: jwk")
self.jwk_from_json(line)
self.ed25519_seed_bytes_from_jwk()
self.duniterpy_from_ed25519_seed_bytes()
elif re.search(regex_nacl, line):
log.info("input file format detected: nacl")
self.duniterpy = duniterpy.key.SigningKey.from_private_key(self.input)
elif re.search(regex_pem, line):
log.info("input file format detected: pem")
self.ed25519_seed_bytes_from_pem(''.join(lines).encode())
self.duniterpy_from_ed25519_seed_bytes()
elif re.search(regex_pubsec, line):
log.info("input file format detected: pubsec")
self.duniterpy = duniterpy.key.SigningKey.from_pubsec_file(self.input)
elif re.search(regex_seed, line):
log.info("input file format detected: seed")
self.duniterpy = duniterpy.key.SigningKey.from_seedhex_file(self.input)
elif re.search(regex_ssb, line):
log.info("input file format detected: ssb")
self.duniterpy = duniterpy.key.SigningKey.from_ssb_file(self.input)
elif re.search(regex_wif, line):
log.info("input file format detected: wif")
self.duniterpy = duniterpy.key.SigningKey.from_wif_file(self.input)
elif len(line.split(' ')) == 12:
log.info("input file format detected: mnemonic")
self.username = line
self.duniterpy_from_mnemonic()
elif len(lines) > 1:
log.info("input file format detected: credentials")
self.username = line
self.password = lines[1].strip()
self.duniterpy_from_credentials()
else:
raise NotImplementedError('unknown input file format.')
else:
raise NotImplementedError('empty file.')
except UnicodeDecodeError as e:
try:
with open(self.input, 'rb') as file:
lines = file.readlines()
if len(lines) > 0:
line = lines[0].strip()
regex_dewif = re.compile(b'^\x00\x00\x00\x01\x00\x00\x00\x01')
regex_pb2 = re.compile(b'^\x08\x01\x12@')
if re.search(regex_dewif, line):
log.info("input file format detected: dewif")
if not self.password:
with pynentry.PynEntry() as p:
p.description = f"""Data in DEWIF file is encrypted.
Please enter a password to decrypt seed.
"""
p.prompt = 'Passphrase:'
try:
self.password = p.get_pin()
except pynentry.PinEntryCancelled:
log.warning('Cancelled! Goodbye.')
self._cleanup()
exit(1)
self.duniterpy = duniterpy.key.SigningKey.from_dewif_file(self.input, self.password)
if re.search(regex_pb2, line):
log.info("input file format detected: pb2")
self.ed25519_secret_protobuf = line
self.ed25519_seed_bytes_from_protobuf()
self.duniterpy_from_ed25519_seed_bytes()
else:
raise NotImplementedError('unknown input file format.')
else:
raise NotImplementedError('empty file.')
except Exception as e:
log.error(f'Unable to get duniterpy from file {self.input}: {e}')
self._cleanup()
exit(2)
except Exception as e:
log.error(f'Unable to get duniterpy from file {self.input}: {e}')
self._cleanup()
exit(2)
log.debug("keygen.duniterpy.seed: %s" % self.duniterpy.seed)
def duniterpy_from_mnemonic(self):
log.debug("keygen.duniterpy_from_mnemonic()")
try:
scrypt_params = duniterpy.key.scrypt_params.ScryptParams(
int(self.config.get('scrypt', 'n')) if self.config.has_option('scrypt', 'n') else 4096,
int(self.config.get('scrypt', 'r')) if self.config.has_option('scrypt', 'r') else 16,
int(self.config.get('scrypt', 'p')) if self.config.has_option('scrypt', 'p') else 1,
int(self.config.get('scrypt', 'sl')) if self.config.has_option('scrypt', 'sl') else 32,
)
self.duniterpy = duniterpy.key.SigningKey.from_dubp_mnemonic(
self.username,
scrypt_params
)
except Exception as e:
log.error(f'Unable to get duniterpy from mnemonic: {e}')
self._cleanup()
exit(2)
log.debug("keygen.duniterpy.seed: %s" % self.duniterpy.seed)
def ed25519(self, args):
log.debug("keygen.ed25519(%s)" % args)
if args.gpg:
self.ed25519_from_gpg()
else:
if self.input:
self.duniterpy_from_file()
else:
if self.mnemonic:
self.duniterpy_from_mnemonic()
else:
self.duniterpy_from_credentials()
self.ed25519_from_duniterpy()
def ed25519_from_duniterpy(self):
log.debug("keygen.ed25519_from_duniterpy()")
try:
self.ed25519_seed_bytes_from_duniterpy()
self.ed25519_from_seed_bytes()
except:
log.error(f'Unable to get ed25519 from duniterpy: {e}')
self._cleanup()
exit(2)
def ed25519_from_gpg(self):
log.debug("keygen.ed25519_from_gpg()")
try:
self.pgpy_from_gpg()
self.ed25519_from_pgpy()
except Exception as e:
log.error(f'Unable to get ed25519 from pgp: {e}')
self._cleanup()
exit(2)
def ed25519_from_pgpy(self):
log.debug("keygen.ed25519_from_pgpy()")
try:
log.debug("keygen.pgpy.fingerprint.keyid=%s" % self.pgpy.fingerprint.keyid)
log.debug("keygen.pgpy.is_protected=%s" % self.pgpy.is_protected)
if self.pgpy.is_protected:
if not self.password:
with pynentry.PynEntry() as p:
p.description = f"""The exported pgp key id "{self.pgpy.fingerprint.keyid}" of user "{self.username}" is password protected.
Please enter the passphrase again to unlock it.
"""
p.prompt = 'Passphrase:'
try:
self.password = p.get_pin()
except pynentry.PinEntryCancelled:
log.warning('Cancelled! Goodbye.')
self._cleanup()
exit(1)
try:
with warnings.catch_warnings():
# remove CryptographyDeprecationWarning about deprecated
# SymmetricKeyAlgorithm IDEA, CAST5 and Blowfish (PGPy v0.5.4)
warnings.simplefilter('ignore')
with self.pgpy.unlock(self.password):
assert self.pgpy.is_unlocked
log.debug("keygen.pgpy.is_unlocked=%s" % self.pgpy.is_unlocked)
self.ed25519_seed_bytes_from_pgpy()
except Exception as e:
log.error(f"""Unable to unlock pgp secret key id "{self.pgpy.fingerprint.keyid}" of user "{self.username}": {e}""")
self._cleanup()
exit(2)
else:
self.ed25519_seed_bytes_from_pgpy()
self.ed25519_from_seed_bytes()
except Exception as e:
log.error(f'Unable to get ed25519 seed bytes from pgpy: {e}')
self._cleanup()
exit(2)
def ed25519_from_seed_bytes(self):
log.debug("keygen.ed25519_from_seed_bytes()")
try:
self.ed25519_public_bytes, self.ed25519_secret_bytes = nacl.bindings.crypto_sign_seed_keypair(self.ed25519_seed_bytes)
self.ed25519 = ed25519.Ed25519PrivateKey.from_private_bytes(self.ed25519_seed_bytes)
except Exception as e:
log.error(f'Unable to get ed25519 from seed bytes: {e}')
self._cleanup()
exit(2)
log.debug("keygen.ed25519_public_bytes=%s" % self.ed25519_public_bytes)
log.debug("keygen.ed25519_secret_bytes=%s" % self.ed25519_secret_bytes)
def ed25519_seed_bytes_from_duniterpy(self):
log.debug("keygen.ed25519_seed_bytes_from_duniterpy()")
try:
self.ed25519_seed_bytes = self.duniterpy.sk[:32]
except Exception as e:
log.error(f'Unable to get ed25519 seed bytes from duniterpy: {e}')
self._cleanup()
exit(2)
log.debug("keygen.ed25519_seed_bytes=%s" % self.ed25519_seed_bytes)
def ed25519_seed_bytes_from_jwk(self):
log.debug("keygen.ed25519_seed_bytes_from_jwk()")
try:
self.ed25519_seed_bytes = self.jwk._okp_pri().private_bytes(encoding=serialization.Encoding.Raw, format=serialization.PrivateFormat.Raw, encryption_algorithm=serialization.NoEncryption())
except Exception as e:
log.error(f'Unable to get ed25519 seed bytes from jwk: {e}')
self._cleanup()
exit(2)
def ed25519_seed_bytes_from_pem(self, pem):
log.debug("keygen.ed25519_seed_bytes_from_pem()")
try:
self.ed25519_seed_bytes = serialization.load_pem_private_key(pem, password=None).private_bytes(encoding=serialization.Encoding.Raw, format=serialization.PrivateFormat.Raw, encryption_algorithm=serialization.NoEncryption())
except Exception as e:
log.error(f'Unable to get ed25519 seed bytes from pem: {e}')
self._cleanup()
exit(2)
def ed25519_seed_bytes_from_pgpy(self):
log.debug("keygen.ed25519_seed_bytes_from_pgpy()")
try:
self.pgpy_key_type()
if self.pgpy_key_type == 'RSA':
log.debug("keygen.pgpy._key.keymaterial.p=%s" % self.pgpy._key.keymaterial.p)
log.debug("keygen.pgpy._key.keymaterial.q=%s" % self.pgpy._key.keymaterial.q)
# custom seed: use sha256 hash of (p + q)
self.ed25519_seed_bytes = nacl.bindings.crypto_hash_sha256(long_to_bytes(self.pgpy._key.keymaterial.p + self.pgpy._key.keymaterial.q))
elif self.pgpy_key_type in ('ECDSA', 'EdDSA', 'ECDH'):
log.debug("keygen.pgpy._key.keymaterial.s=%s" % self.pgpy._key.keymaterial.s)
self.ed25519_seed_bytes = long_to_bytes(self.pgpy._key.keymaterial.s)
else:
raise NotImplementedError(f"getting seed from {self.pgpy_key_type} key is not implemented")
except Exception as e:
log.error(f'Unable to get ed25519 seed bytes from pgpy: {e}')
self._cleanup()
exit(2)
log.debug("keygen.ed25519_seed_bytes=%s" % self.ed25519_seed_bytes)
def ed25519_seed_bytes_from_protobuf(self):
log.debug("keygen.ed25519_seed_bytes_from_protobuf()")
try:
self.ed25519_seed_bytes = self.ed25519_secret_protobuf.lstrip(b'\x08\x01\x12@')[:32]
except Exception as e:
log.error(f'Unable to get ed25519 seed bytes from protobuf: {e}')
self._cleanup()
exit(2)
log.debug("keygen.ed25519_seed_bytes=%s" % self.ed25519_seed_bytes)
def gpg_passphrase_cb(self, 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
def jwk_from_ed25519(self):
log.debug("keygen.jwk_from_ed25519()")
try:
self.jwk = jwk.JWK.from_pyca(self.ed25519)
except Exception as e:
log.error(f'Unable to get jwk from ed25519: {e}')
self._cleanup()
exit(2)
def jwk_from_json(self, json):
log.debug("keygen.jwk_from_json()")
try:
self.jwk = jwk.JWK.from_json(json)
except Exception as e:
log.error(f'Unable to get jwk from json: {e}')
self._cleanup()
exit(2)
def pem_pkcs8_from_ed25519(self):
log.debug("keygen.pem_pkcs8_from_ed25519()")
try:
self.ed25519_secret_pem_pkcs8 = self.ed25519.private_bytes(encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption()).decode('ascii')
except Exception as e:
log.error(f'Unable to get pem pkcs8 from ed25519: {e}')
self._cleanup()
exit(2)
log.debug("keygen.ed25519_secret_pem_pkcs8=%s" % self.ed25519_secret_pem_pkcs8)
def pgpy_from_gpg(self):
log.debug("keygen.pgpy_from_gpg()")
try:
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:
log.warning(f"""Unable to find any key matching username "{self.username}".""")
self._cleanup()
exit(1)
else:
self.gpg_seckey = self.gpg_seckeys[0]
log.info(f"""Found key id "{self.gpg_seckey.fpr}" matching username "{self.username}".""")
log.debug("keygen.gpg_seckey.expired=%s" % self.gpg_seckey.expired)
log.debug("keygen.gpg_seckey.fpr=%s" % self.gpg_seckey.fpr)
log.debug("keygen.gpg_seckey.revoked=%s" % self.gpg_seckey.revoked)
log.debug("keygen.gpg_seckey.uids=%s" % self.gpg_seckey.uids)
log.debug("keygen.gpg_seckey.owner_trust=%s" % self.gpg_seckey.owner_trust)
log.debug("keygen.gpg_seckey.last_update=%s" % self.gpg_seckey.last_update)
if self.password:
self.gpg.set_pinentry_mode(gpg.constants.PINENTRY_MODE_LOOPBACK)
self.pgp_public_armor = self.gpg.key_export(self.gpg_seckey.fpr)
self.pgp_secret_armor = self.gpg.key_export_secret(self.gpg_seckey.fpr)
log.debug("keygen.pgp_secret_armor=%s" % self.pgp_secret_armor)
if not self.pgp_secret_armor:
log.error(f"""Unable to export gpg secret key id "{self.gpg_seckey.fpr}" of user "{self.username}". Please check your password!""")
self._cleanup()
exit(2)
with warnings.catch_warnings():
# remove CryptographyDeprecationWarning about deprecated
# SymmetricKeyAlgorithm IDEA, CAST5 and Blowfish (PGPy v0.5.4)
warnings.simplefilter('ignore')
self.pgpy, _ = pgpy.PGPKey.from_blob(self.pgp_secret_armor)
except Exception as e:
log.error(f'Unable to get pgpy from gpg: {e}')
self._cleanup()
exit(2)
def pgpy_key_type(self):
log.debug("keygen.pgpy_key_type()")
if isinstance(self.pgpy._key.keymaterial, pgpy.packet.fields.RSAPriv):
self.pgpy_key_type = 'RSA'
elif isinstance(self.pgpy._key.keymaterial, pgpy.packet.fields.DSAPriv):
self.pgpy_key_type = 'DSA'
elif isinstance(self.pgpy._key.keymaterial, pgpy.packet.fields.ElGPriv):
self.pgpy_key_type = 'ElGamal'
elif isinstance(self.pgpy._key.keymaterial, pgpy.packet.fields.ECDSAPriv):
self.pgpy_key_type = 'ECDSA'
elif isinstance(self.pgpy._key.keymaterial, pgpy.packet.fields.EdDSAPriv):
self.pgpy_key_type = 'EdDSA'
elif isinstance(self.pgpy._key.keymaterial, pgpy.packet.fields.ECDHPriv):
self.pgpy_key_type = 'ECDH'
else:
self.pgpy_key_type = 'undefined'
log.debug("keygen.pgpy_key_type=%s" % self.pgpy_key_type)
def protobuf_from_ed25519(self):
# libp2p protobuf version 2
log.debug("keygen.protobuf_from_ed25519()")
try:
self.ed25519_public_protobuf = b'\x00$\x08\x01\x12 ' + self.ed25519_public_bytes
self.ed25519_secret_protobuf = b'\x08\x01\x12@' + self.ed25519_secret_bytes
except Exception as e:
log.error(f'Unable to get protobuf from ed25519: {e}')
self._cleanup()
exit(2)
log.debug("keygen.ed25519_public_protobuf=%s" % self.ed25519_public_protobuf)
log.debug("keygen.ed25519_secret_protobuf=%s" % self.ed25519_secret_protobuf)
##
# 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.
This is (essentially) the inverse of long_to_bytes().
"""
acc = 0
unpack = struct.unpack
length = len(s)
if length % 4:
extra = (4 - length % 4)
s = b'\000' * extra + s
length = length + extra
for i in range(0, length, 4):
acc = (acc << 32) + unpack('>I', s[i:i+4])[0]
return acc
def long_to_bytes(n, blocksize=0):
"""long_to_bytes(n:long, blocksize:int) : string
Convert a long integer to a byte string.
If optional blocksize is given and greater than zero, pad the front of the
byte string with binary zeros so that the length is a multiple of
blocksize.
"""
# after much testing, this algorithm was deemed to be the fastest
s = b''
n = int(n)
pack = struct.pack
while n > 0:
s = pack('>I', n & 0xffffffff) + s
n = n >> 32
# strip off leading zeros
for i in range(len(s)):
if s[i] != b'\000'[0]:
break
else:
# only happens when n == 0
s = b'\000'
i = 0
s = s[i:]
# add back some pad bytes. this could be done more efficiently w.r.t. the
# de-padding being done above, but sigh...
if blocksize > 0 and len(s) % blocksize:
s = (blocksize - len(s) % blocksize) * b'\000' + s
return s
log.debug("keygen().do_jwk()")
self.key.to_jwk()
self.public_key = self.key.public_jwk
self.secret_key = self.key.secret_jwk
self._output()
def main(argv=None):
if argv is None:
argv = sys.argv[1:]
cli = keygen()
return cli._run(argv)
return keygen()._cli(argv)
def version(version=__version__):
print("%s v%s" % (sys.argv[0],version))
if __name__ == "__main__":
sys.exit(main())

View File

@ -1,6 +1,8 @@
base36==0.1.1
base58==2.1.1
cryptography==3.4.8
duniterpy==1.1.0
ipfshttpclient==0.7.0
jwcrypto==1.4.2
pgpy==0.5.4
pynentry==0.1.6

View File

@ -28,13 +28,21 @@ Describe 'Dependency'
The stderr should equal ""
End
End
Describe 'python3:'
It 'is available'
When run python3 --help
The output should include 'python3'
The status should be success
The stderr should equal ""
End
End
End
Describe 'dpgpid'
Describe '--help:'
It 'prints help'
When run dpgpid --help
The output should include 'Usage:'
The output should include 'usage:'
The status should be success
The stderr should equal ""
End
@ -42,7 +50,7 @@ Describe 'dpgpid'
Describe '--version:'
It 'prints version'
When run dpgpid --version
The output should include 'v0.0.1'
The output should include 'v0.1.0'
The status should be success
The stderr should equal ""
End

View File

@ -6,7 +6,7 @@ DUBP_FILE="${SHELLSPEC_TMPBASE}/mnemonic"
EWIF_FILE="${SHELLSPEC_TMPBASE}/username.ewif"
JWK_FILE="${SHELLSPEC_TMPBASE}/username.jwk"
NACL_FILE="${SHELLSPEC_TMPBASE}/username.nacl"
PB2_FILE="${SHELLSPEC_TMPBASE}/username.pb2"
P2P_FILE="${SHELLSPEC_TMPBASE}/username.p2p"
PEM_FILE="${SHELLSPEC_TMPBASE}/username.pem"
PUBSEC_FILE="${SHELLSPEC_TMPBASE}/username.pubsec"
SEED_FILE="${SHELLSPEC_TMPBASE}/username.seed"
@ -36,14 +36,6 @@ Describe 'Dependency'
The stderr should be present
End
End
Describe 'python3:'
It 'is available'
When run python3 --help
The output should include 'python3'
The status should be success
The stderr should equal ""
End
End
End
Describe 'keygen'
@ -58,199 +50,192 @@ Describe 'keygen'
Describe '--version:'
It 'prints version'
When run keygen --version
The output should include 'v0.0.5'
The output should include 'v0.1.0'
The status should be success
The stderr should equal ""
End
End
Describe 'username password:'
Describe '-p password username:'
It 'prints base58 public key for user "username" and password "password"'
When run keygen username password
When run keygen -p password username
The output should include '4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The status should be success
The stderr should equal ""
End
End
Describe '-p username password:'
It 'prints prefixed base58 public key for user "username" and password "password"'
When run keygen -p username password
The output should include 'pub: 4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The status should be success
The stderr should equal ""
End
End
Describe '-s username password:'
Describe '-p password -s username:'
It 'prints base58 secret key for user "username" and password "password"'
When run keygen -s username password
When run keygen -p password -s username
The output should include 'K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
The status should be success
The stderr should equal ""
End
End
Describe '-ps username password:'
It 'prints prefixed base58 secret key for user "username" and password "password"'
When run keygen -ps username password
The output should include 'sec: K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
The status should be success
The stderr should equal ""
End
End
Describe '-k username password:'
Describe '-k -p password username:'
It 'prints base58 public and secret keys for user "username" and password "password"'
When run keygen -k username password
When run keygen -k -p password username
The output should include '4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
The status should be success
The stderr should equal ""
End
End
Describe '-pk username password:'
It 'prints prefixed base58 public and secret keys for user "username" and password "password"'
When run keygen -pk username password
The output should include 'pub: 4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'sec: K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
Describe '-k -p password -t b36mf username:'
It 'prints base36 multiformat public and secret keys for user "username" and password "password"'
When run keygen -k -p password -t b36mf username
The output should include 'k51qzi5uqu5dhhsbw068pust1xf763zdmyu2mb8rf6ewu2oz3in3a2g6pgtqy3'
The output should include 'kmxn88f5mep5chc4tc002gyhtl9vgiluellgje285y2hn5a5kjdvqge7oeb0jryupt1q09w48h2nxg0ofcjco0wjwa824v3p9tvw6us9gdkb'
The status should be success
The stderr should equal ""
End
End
Describe '-pkt b58mh username password:'
It 'prints prefixed base58 multihash public and secret keys for user "username" and password "password"'
When run keygen -pkt b58mh username password
The output should include 'pub: 12D3KooWDMhdm5yrvtrbkshXFjkqLedHieUnPioczy9wzdnzquHC'
The output should include 'sec: 23jhTarm17VAHUwPkHD2Kv5sPfuQrsXSZUzKUrRkP2oP8bgnLjVExhG4AVoayCLxbXN4g2pjVG5qiJRucUtogbj7zGapz'
Describe '-k -p password -t b58mf username:'
It 'prints base58 multiformat public and secret keys for user "username" and password "password"'
When run keygen -k -p password -t b58mf username
The output should include 'z5AanNVJCxnJNiidpTZyuYzkQcrHRCyhxMV7Z4KYDV1MYy2ETMrEbUn'
The output should include 'z4gg7xjCuszBpvNVcDAmNYVNrZxwXfDDQGoAShWmmQBkWRzZbR8A4ZBpkk4iTj3YSLBxvGZRf1AjCyGDdczhs7tshCsbFK4e'
The status should be success
The stderr should equal ""
End
End
Describe '-pkt b64mh username password:'
It 'prints prefixed base64 multihash public and secret keys for user "username" and password "password"'
When run keygen -pkt b64mh username password
The output should include 'pub: ACQIARIgNJoTbvcP+m51+XwxrmWqHaOpI1ZD0USwLjqAmV8Boas='
The output should include 'sec: CAESQA+XqCWjRqCjNe9oU3QA796bEH+T+rxgyPQ/EkXvE2MvNJoTbvcP+m51+XwxrmWqHaOpI1ZD0USwLjqAmV8Boas='
Describe '-k -p password -t b58mh username:'
It 'prints base58 multihash public and secret keys for user "username" and password "password"'
When run keygen -k -p password -t b58mh username
The output should include '12D3KooWDMhdm5yrvtrbkshXFjkqLedHieUnPioczy9wzdnzquHC'
The output should include '23jhTarm17VAHUwPkHD2Kv5sPfuQrsXSZUzKUrRkP2oP8bgnLjVExhG4AVoayCLxbXN4g2pjVG5qiJRucUtogbj7zGapz'
The status should be success
The stderr should equal ""
End
End
Describe '-pkt base58 username password:'
It 'prints prefixed base58 public and secret keys for user "username" and password "password"'
When run keygen -pkt base58 username password
The output should include 'pub: 4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'sec: K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
Describe '-k -p password -t b64mh username:'
It 'prints base64 multihash public and secret keys for user "username" and password "password"'
When run keygen -k -p password -t b64mh username
The output should include 'ACQIARIgNJoTbvcP+m51+XwxrmWqHaOpI1ZD0USwLjqAmV8Boas='
The output should include 'CAESQA+XqCWjRqCjNe9oU3QA796bEH+T+rxgyPQ/EkXvE2MvNJoTbvcP+m51+XwxrmWqHaOpI1ZD0USwLjqAmV8Boas='
The status should be success
The stderr should equal ""
End
End
Describe '-pkt base64 username password:'
It 'prints prefixed base64 public and secret keys for user "username" and password "password"'
When run keygen -pkt base64 username password
The output should include 'pub: NJoTbvcP+m51+XwxrmWqHaOpI1ZD0USwLjqAmV8Boas='
The output should include 'sec: D5eoJaNGoKM172hTdADv3psQf5P6vGDI9D8SRe8TYy80mhNu9w/6bnX5fDGuZaodo6kjVkPRRLAuOoCZXwGhqw=='
Describe '-k -p password -t base58 username:'
It 'prints base58 public and secret keys for user "username" and password "password"'
When run keygen -k -p password -t base58 username
The output should include '4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
The status should be success
The stderr should equal ""
End
End
Describe '-pkt duniter username password:'
It 'prints prefixed duniter public and secret keys for user "username" and password "password"'
When run keygen -pkt duniter username password
The output should include 'pub: 4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'sec: K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
Describe '-k -p password -t base64 username:'
It 'prints base64 public and secret keys for user "username" and password "password"'
When run keygen -k -p password -t base64 username
The output should include 'NJoTbvcP+m51+XwxrmWqHaOpI1ZD0USwLjqAmV8Boas='
The output should include 'D5eoJaNGoKM172hTdADv3psQf5P6vGDI9D8SRe8TYy80mhNu9w/6bnX5fDGuZaodo6kjVkPRRLAuOoCZXwGhqw=='
The status should be success
The stderr should equal ""
End
End
Describe '-pkt ipfs username password:'
It 'prints prefixed ipfs public and secret keys for user "username" and password "password"'
When run keygen -pkt ipfs username password
The output should include 'PeerID: 12D3KooWDMhdm5yrvtrbkshXFjkqLedHieUnPioczy9wzdnzquHC'
The output should include 'PrivKEY: CAESQA+XqCWjRqCjNe9oU3QA796bEH+T+rxgyPQ/EkXvE2MvNJoTbvcP+m51+XwxrmWqHaOpI1ZD0USwLjqAmV8Boas='
Describe '-k -p password -t duniter username:'
It 'prints duniter public and secret keys for user "username" and password "password"'
When run keygen -k -p password -t duniter username
The output should include '4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
The status should be success
The stderr should equal ""
End
End
Describe '-pkt jwk username password:'
It 'prints prefixed jwk public and secret keys for user "username" and password "password"'
When run keygen -pkt jwk username password
The output should include 'pub: {"crv":"Ed25519","kty":"OKP","x":"NJoTbvcP-m51-XwxrmWqHaOpI1ZD0USwLjqAmV8Boas"}'
The output should include 'sec: {"crv":"Ed25519","d":"D5eoJaNGoKM172hTdADv3psQf5P6vGDI9D8SRe8TYy8","kty":"OKP","x":"NJoTbvcP-m51-XwxrmWqHaOpI1ZD0USwLjqAmV8Boas"}'
Describe '-k -p password -t ipfs username:'
It 'prints ipfs public and secret keys for user "username" and password "password"'
When run keygen -k -p password -t ipfs username
The output should include '12D3KooWDMhdm5yrvtrbkshXFjkqLedHieUnPioczy9wzdnzquHC'
The output should include 'CAESQA+XqCWjRqCjNe9oU3QA796bEH+T+rxgyPQ/EkXvE2MvNJoTbvcP+m51+XwxrmWqHaOpI1ZD0USwLjqAmV8Boas='
The status should be success
The stderr should equal ""
End
End
Describe '-pkm "tongue cute mail ...":'
It 'prints prefixed base58 public and secret keys for mnemonic "tongue cute mail ..."'
When run keygen -pkm "tongue cute mail fossil great frozen same social weasel impact brush kind"
The output should include 'pub: 732SSfuwjB7jkt9th1zerGhphs6nknaCBCTozxUcPWPU'
The output should include 'sec: 4NHNg9KSp81nXAN4Gmwx4EZ9bCdahnJ9jozJa1cGj9oDvzx9kCtNSvasqTZVm6VJXBQxyakZ5uZnj8AS6g87kK3x'
Describe '-k -p password -t jwk username:'
It 'prints jwk public and secret keys for user "username" and password "password"'
When run keygen -k -p password -t jwk username
The output should include '{"crv":"Ed25519","kty":"OKP","x":"NJoTbvcP-m51-XwxrmWqHaOpI1ZD0USwLjqAmV8Boas"}'
The output should include '{"crv":"Ed25519","d":"D5eoJaNGoKM172hTdADv3psQf5P6vGDI9D8SRe8TYy8","kty":"OKP","x":"NJoTbvcP-m51-XwxrmWqHaOpI1ZD0USwLjqAmV8Boas"}'
The status should be success
The stderr should equal ""
End
End
Describe "-pki ${CRED_FILE}"
Describe '-k -m "tongue cute mail ...":'
It 'prints base58 public and secret keys for mnemonic "tongue cute mail ..."'
When run keygen -k -m "tongue cute mail fossil great frozen same social weasel impact brush kind"
The output should include '732SSfuwjB7jkt9th1zerGhphs6nknaCBCTozxUcPWPU'
The output should include '4NHNg9KSp81nXAN4Gmwx4EZ9bCdahnJ9jozJa1cGj9oDvzx9kCtNSvasqTZVm6VJXBQxyakZ5uZnj8AS6g87kK3x'
The status should be success
The stderr should equal ""
End
End
Describe "-i ${CRED_FILE} -k -v:"
printf 'username\npassword\n' > "${CRED_FILE}"
It 'prints prefixed base58 public and secret keys for username and password read from credentials file"'
When run keygen -pki "${CRED_FILE}" -v
The output should include 'pub: 4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'sec: K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
It 'prints base58 public and secret keys for username and password read from credentials file"'
When run keygen -i "${CRED_FILE}" -k -v
The output should include '4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
The status should be success
The stderr should include 'input file format detected: credentials'
End
rm -f "${CRED_FILE}"
End
Describe "-pki ${DUBP_FILE}"
Describe "-i ${DUBP_FILE} -k -v:"
printf 'tongue cute mail fossil great frozen same social weasel impact brush kind\n' > "${DUBP_FILE}"
It 'prints prefixed base58 public and secret keys for mnemonic read from dubp file"'
When run keygen -pki "${DUBP_FILE}" -v
The output should include 'pub: 732SSfuwjB7jkt9th1zerGhphs6nknaCBCTozxUcPWPU'
The output should include 'sec: 4NHNg9KSp81nXAN4Gmwx4EZ9bCdahnJ9jozJa1cGj9oDvzx9kCtNSvasqTZVm6VJXBQxyakZ5uZnj8AS6g87kK3x'
It 'prints base58 public and secret keys for mnemonic read from dubp file"'
When run keygen -i "${DUBP_FILE}" -k -v
The output should include '732SSfuwjB7jkt9th1zerGhphs6nknaCBCTozxUcPWPU'
The output should include '4NHNg9KSp81nXAN4Gmwx4EZ9bCdahnJ9jozJa1cGj9oDvzx9kCtNSvasqTZVm6VJXBQxyakZ5uZnj8AS6g87kK3x'
The status should be success
The stderr should include 'input file format detected: mnemonic'
End
rm -f "${DUBP_FILE}"
End
Describe "-f jwk -o ${JWK_FILE} username password:"
Describe "-f jwk -o ${JWK_FILE} -p password username:"
rm -f "${JWK_FILE}"
It 'writes secret key to a JWK file for user "username" and password "password"'
When run keygen -f jwk -o "${JWK_FILE}" username password
When run keygen -f jwk -o "${JWK_FILE}" -p password username
The path "${JWK_FILE}" should exist
The contents of file "${JWK_FILE}" should include '{"crv":"Ed25519","d":"D5eoJaNGoKM172hTdADv3psQf5P6vGDI9D8SRe8TYy8","kty":"OKP","x":"NJoTbvcP-m51-XwxrmWqHaOpI1ZD0USwLjqAmV8Boas"}'
The status should be success
The stderr should equal ""
End
End
Describe "-pki ${JWK_FILE}:"
It 'prints prefixed base58 public and secret keys for ed25519 key read from JWK file"'
When run keygen -pki "${JWK_FILE}" -v
The output should include 'pub: 4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'sec: K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
Describe "-i ${JWK_FILE} -k -v:"
It 'prints base58 public and secret keys for ed25519 key read from JWK file"'
When run keygen -i "${JWK_FILE}" -k -v
The output should include '4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
The status should be success
The stderr should include 'input file format detected: jwk'
End
rm -f "${JWK_FILE}"
End
Describe "-f nacl -o ${NACL_FILE} username password:"
Describe "-f nacl -o ${NACL_FILE} -p password username:"
rm -f "${NACL_FILE}"
It 'writes secret key to a libnacl file for user "username" and password "password"'
When run keygen -f nacl -o "${NACL_FILE}" username password
When run keygen -f nacl -o "${NACL_FILE}" -p password username
The path "${NACL_FILE}" should exist
The contents of file "${NACL_FILE}" should include '{"priv": "0f97a825a346a0a335ef68537400efde9b107f93fabc60c8f43f1245ef13632f349a136ef70ffa6e75f97c31ae65aa1da3a9235643d144b02e3a80995f01a1ab", "verify": "349a136ef70ffa6e75f97c31ae65aa1da3a9235643d144b02e3a80995f01a1ab", "sign": "0f97a825a346a0a335ef68537400efde9b107f93fabc60c8f43f1245ef13632f"}'
The status should be success
The stderr should equal ""
End
End
Describe "-pki ${NACL_FILE}:"
It 'prints prefixed base58 public and secret keys for ed25519 key read from libnacl file"'
When run keygen -pki "${NACL_FILE}" -v
The output should include 'pub: 4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'sec: K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
Describe "-i ${NACL_FILE} -k -v:"
It 'prints base58 public and secret keys for ed25519 key read from libnacl file"'
When run keygen -i "${NACL_FILE}" -k -v
The output should include '4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
The status should be success
The stderr should include 'input file format detected: nacl'
End
rm -f "${NACL_FILE}"
End
Describe "-f ewif -o ${EWIF_FILE} username password:"
Describe "-f ewif -o ${EWIF_FILE} -p password username:"
rm -f "${EWIF_FILE}"
It 'writes encrypted secret key to a ewif file for user "username" and password "password"'
When run keygen -f ewif -o "${EWIF_FILE}" username password
It 'writes encrypted secret key to an EWIF file for user "username" and password "password"'
When run keygen -f ewif -o "${EWIF_FILE}" -p password username
The path "${EWIF_FILE}" should exist
The contents of file "${EWIF_FILE}" should include 'Type: EWIF'
The contents of file "${EWIF_FILE}" should include 'Version: 1'
@ -259,20 +244,20 @@ Describe 'keygen'
The stderr should equal ""
End
End
Describe "-pki ${EWIF_FILE}:"
It 'prints prefixed base58 public and secret keys for ed25519 key read from EWIF file"'
When run keygen -pki "${EWIF_FILE}" -v username password
The output should include 'pub: 4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'sec: K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
Describe "-i ${EWIF_FILE} -k -p password -v:"
It 'prints base58 public and secret keys for ed25519 key read from EWIF file encrypted with password "password"'
When run keygen -i "${EWIF_FILE}" -k -p password -v username
The output should include '4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
The status should be success
The stderr should include 'input file format detected: ewif'
End
rm -f "${EWIF_FILE}"
End
Describe "-o ${PEM_FILE} username password:"
Describe "-o ${PEM_FILE} -p password username:"
rm -f "${PEM_FILE}"
It 'writes pkcs8 secret key to a pem file for user "username" and password "password"'
When run keygen -o "${PEM_FILE}" -t ipfs username password
When run keygen -o "${PEM_FILE}" -p password username
The path "${PEM_FILE}" should exist
The contents of file "${PEM_FILE}" should include '-----BEGIN PRIVATE KEY-----'
The contents of file "${PEM_FILE}" should include 'MC4CAQAwBQYDK2VwBCIEIA+XqCWjRqCjNe9oU3QA796bEH+T+rxgyPQ/EkXvE2Mv'
@ -281,10 +266,10 @@ Describe 'keygen'
The stderr should equal ""
End
End
Describe "-f pem -o ${PEM_FILE} username password:"
Describe "-f pem -o ${PEM_FILE} -p password username:"
rm -f "${PEM_FILE}"
It 'writes pkcs8 secret key to a pem file for user "username" and password "password"'
When run keygen -f pem -o "${PEM_FILE}" -t ipfs username password
When run keygen -f pem -o "${PEM_FILE}" -p password username
The path "${PEM_FILE}" should exist
The contents of file "${PEM_FILE}" should include '-----BEGIN PRIVATE KEY-----'
The contents of file "${PEM_FILE}" should include 'MC4CAQAwBQYDK2VwBCIEIA+XqCWjRqCjNe9oU3QA796bEH+T+rxgyPQ/EkXvE2Mv'
@ -293,49 +278,49 @@ Describe 'keygen'
The stderr should equal ""
End
End
Describe "-pki ${PEM_FILE}:"
It 'prints prefixed base58 public and secret keys for ed25519 key read from pkcs8 pem file"'
When run keygen -pki "${PEM_FILE}" -v
The output should include 'pub: 4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'sec: K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
Describe "-i ${PEM_FILE} -k -v:"
It 'prints base58 public and secret keys for ed25519 key read from pkcs8 pem file"'
When run keygen -i "${PEM_FILE}" -k -v
The output should include '4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
The status should be success
The stderr should include 'input file format detected: pem'
End
rm -f "${PEM_FILE}"
End
Describe "-f pb2 -o ${PB2_FILE} username password:"
rm -f "${PB2_FILE}"
It 'writes protobuf2 secret key to a pb2 file for user "username" and password "password"'
decode_pb2() {
xxd -p "${PB2_FILE}"
Describe "-f p2p -o ${P2P_FILE} -p password username:"
rm -f "${P2P_FILE}"
It 'writes libp2p secret key to a p2p file for user "username" and password "password"'
decode_p2p() {
xxd -p "${P2P_FILE}"
}
not_xxd() {
! which xxd >/dev/null 2>&1
}
Skip if 'You should install xxd' not_xxd
When run keygen -f pb2 -o "${PB2_FILE}" username password
The path "${PB2_FILE}" should exist
The result of function decode_pb2 should include '080112400f97a825a346a0a335ef68537400efde9b107f93fabc60c8f43f'
The result of function decode_pb2 should include '1245ef13632f349a136ef70ffa6e75f97c31ae65aa1da3a9235643d144b0'
The result of function decode_pb2 should include '2e3a80995f01a1ab'
When run keygen -f p2p -o "${P2P_FILE}" -p password username
The path "${P2P_FILE}" should exist
The result of function decode_p2p should include '080112400f97a825a346a0a335ef68537400efde9b107f93fabc60c8f43f'
The result of function decode_p2p should include '1245ef13632f349a136ef70ffa6e75f97c31ae65aa1da3a9235643d144b0'
The result of function decode_p2p should include '2e3a80995f01a1ab'
The status should be success
The stderr should equal ""
End
End
Describe "-pki ${PB2_FILE}:"
It 'prints prefixed base58 public and secret keys for ed25519 key read from pb2 file"'
When run keygen -pki "${PB2_FILE}" -v
The output should include 'pub: 4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'sec: K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
Describe "-i ${P2P_FILE} -k -v:"
It 'prints base58 public and secret keys for ed25519 key read from p2p file"'
When run keygen -i "${P2P_FILE}" -k -v
The output should include '4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
The status should be success
The stderr should include 'input file format detected: pb2'
The stderr should include 'input file format detected: p2p'
End
rm -f "${PB2_FILE}"
rm -f "${P2P_FILE}"
End
Describe "-f pubsec -o ${PUBSEC_FILE} username password:"
Describe "-f pubsec -o ${PUBSEC_FILE} -p password username:"
rm -f "${PUBSEC_FILE}"
It 'writes base58 public and secret keys to a pubsec file for user "username" and password "password"'
When run keygen -f pubsec -o "${PUBSEC_FILE}" username password
When run keygen -f pubsec -o "${PUBSEC_FILE}" -p password username
The path "${PUBSEC_FILE}" should exist
The contents of file "${PUBSEC_FILE}" should include 'Type: PubSec'
The contents of file "${PUBSEC_FILE}" should include 'Version: 1'
@ -345,10 +330,10 @@ Describe 'keygen'
The stderr should equal ""
End
End
Describe "-o ${PUBSEC_FILE} -t duniter username password:"
Describe "-o ${PUBSEC_FILE} -p password -t duniter username:"
rm -f "${PUBSEC_FILE}"
It 'writes duniter public and secret keys to a pubsec file for user "username" and password "password"'
When run keygen -o "${PUBSEC_FILE}" -t duniter username password
When run keygen -o "${PUBSEC_FILE}" -p password -t duniter username
The path "${PUBSEC_FILE}" should exist
The contents of file "${PUBSEC_FILE}" should include 'Type: PubSec'
The contents of file "${PUBSEC_FILE}" should include 'Version: 1'
@ -358,51 +343,51 @@ Describe 'keygen'
The stderr should equal ""
End
End
Describe "-pki ${PUBSEC_FILE} -t ipfs:"
It 'prints prefixed ipfs public and secret keys for base58 keys read in a pubsec file'
When run keygen -pki "${PUBSEC_FILE}" -t ipfs -v
The output should include 'PeerID: 12D3KooWDMhdm5yrvtrbkshXFjkqLedHieUnPioczy9wzdnzquHC'
The output should include 'PrivKEY: CAESQA+XqCWjRqCjNe9oU3QA796bEH+T+rxgyPQ/EkXvE2MvNJoTbvcP+m51+XwxrmWqHaOpI1ZD0USwLjqAmV8Boas='
Describe "-i ${PUBSEC_FILE} -k -t ipfs -v:"
It 'prints ipfs public and secret keys for base58 keys read in a pubsec file'
When run keygen -i "${PUBSEC_FILE}" -k -t ipfs -v
The output should include '12D3KooWDMhdm5yrvtrbkshXFjkqLedHieUnPioczy9wzdnzquHC'
The output should include 'CAESQA+XqCWjRqCjNe9oU3QA796bEH+T+rxgyPQ/EkXvE2MvNJoTbvcP+m51+XwxrmWqHaOpI1ZD0USwLjqAmV8Boas='
The status should be success
The stderr should include 'input file format detected: pubsec'
End
rm -f "${PUBSEC_FILE}"
End
Describe "-pki ${SSB_FILE}:"
Describe "-i ${SSB_FILE} -k -v:"
printf '{ "curve": "ed25519", "public": "cFVodZoKwLcmXbM6UeASdl8+7+Uo8PNOuFnlcqk7qUc=.ed25519", "private": "lUqlXYxjkM0/ljtGnwoM0CfP6ORA2DKZnzsQ4dJ1tKJwVWh1mgrAtyZdszpR4BJ2Xz7v5Sjw8064WeVyqTupRw==.ed25519", "id": "@cFVodZoKwLcmXbM6UeASdl8+7+Uo8PNOuFnlcqk7qUc=.ed25519" }\n' > "${SSB_FILE}"
It 'prints prefixed base58 public and secret keys for ed25519 key read from ssb file"'
When run keygen -pki "${SSB_FILE}" -v
The output should include 'pub: 8ZWCTFBUczYRucyvTgJL6oefj28u243LYU4ZjYKn4XDG'
The output should include 'sec: 3z7vcMHQhnVPTEEaFQ5gxn2NHkmJsFHkZ4W2aoAvt3Jt5ZYhFV1M6NEkm7Lr75pEF61oSkQVsaih9cQWBP2JmbVQ'
When run keygen -i "${SSB_FILE}" -k -v
The output should include '8ZWCTFBUczYRucyvTgJL6oefj28u243LYU4ZjYKn4XDG'
The output should include '3z7vcMHQhnVPTEEaFQ5gxn2NHkmJsFHkZ4W2aoAvt3Jt5ZYhFV1M6NEkm7Lr75pEF61oSkQVsaih9cQWBP2JmbVQ'
The status should be success
The stderr should include 'input file format detected: ssb'
End
rm -f "${SSB_FILE}"
End
Describe "-f seed -o ${SEED_FILE} username password:"
Describe "-f seed -o ${SEED_FILE} -p password username:"
rm -f "${SEED_FILE}"
It 'writes encoded secret key to a wif file for user "username" and password "password"'
When run keygen -f seed -o "${SEED_FILE}" username password
When run keygen -f seed -o "${SEED_FILE}" -p password username
The path "${SEED_FILE}" should exist
The contents of file "${SEED_FILE}" should include '0f97a825a346a0a335ef68537400efde9b107f93fabc60c8f43f1245ef13632f'
The status should be success
The stderr should equal ""
End
End
Describe "-pki ${SEED_FILE}:"
It 'prints prefixed base58 public and secret keys for ed25519 key read from seed file"'
When run keygen -pki "${SEED_FILE}" -v
The output should include 'pub: 4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'sec: K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
Describe "-i ${SEED_FILE} -k -v:"
It 'prints base58 public and secret keys for ed25519 key read from seed file"'
When run keygen -i "${SEED_FILE}" -k -v
The output should include '4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
The status should be success
The stderr should include 'input file format detected: seed'
End
rm -f "${SEED_FILE}"
End
Describe "-f wif -o ${WIF_FILE} username password:"
Describe "-f wif -o ${WIF_FILE} -p password username:"
rm -f "${WIF_FILE}"
It 'writes encoded secret key to a wif file for user "username" and password "password"'
When run keygen -f wif -o "${WIF_FILE}" username password
When run keygen -f wif -o "${WIF_FILE}" -p password username
The path "${WIF_FILE}" should exist
The contents of file "${WIF_FILE}" should include 'Type: WIF'
The contents of file "${WIF_FILE}" should include 'Version: 1'
@ -411,62 +396,62 @@ Describe 'keygen'
The stderr should equal ""
End
End
Describe "-pki ${WIF_FILE}:"
It 'prints prefixed base58 public and secret keys for ed25519 key read from WIF file"'
When run keygen -pki "${WIF_FILE}" -v
The output should include 'pub: 4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'sec: K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
Describe "-i ${WIF_FILE} -k -v:"
It 'prints base58 public and secret keys for ed25519 key read from WIF file"'
When run keygen -i "${WIF_FILE}" -k -v
The output should include '4YLU1xQ9jzb7LzC6d91VZrYTEKS9N2j93Nnvcee6wxZG'
The output should include 'K5heSX4xGUPtRbxcZh6zbgaKbDv8FeVc9JuSNWtUs7C1oGNKqv7kQJ3DHdouTPzoW4duKKnuLQK8LbHKfN9fkjC'
The status should be success
The stderr should include 'input file format detected: wif'
End
rm -f "${WIF_FILE}"
End
Describe '-t gpg username password birthday:'
Describe '-b 0 -p password -t gpg username:'
It 'creates a password protected gpg key for user "username"'
Skip "You should implement it !"
When run keygen -t pgp username password birthday
When run keygen -b 0 -p password -t pgp username
The status should be success
End
End
Describe '-pkg username:'
Describe '-g -k username:'
gpg --batch --import --quiet specs/username.asc
It 'prints prefixed base58 public and secret keys for ed25519 gpg key matching uid "username"'
When run keygen -pkg username
The output should include 'pub: 2g5UL2zhkn5i7oNYDpWo3fBuWvRYVU1AbMtdVmnGzPNv'
The output should include 'sec: 5WtYFfA26nTfG496gAKhkrLYUMMnwXexmE1E8Q7PvtQEyscHfirsdMzW34zDp7WEkt3exNEVwoG4ajZYrm62wpi2'
It 'prints base58 public and secret keys for ed25519 gpg key matching uid "username"'
When run keygen -g -k username
The output should include '2g5UL2zhkn5i7oNYDpWo3fBuWvRYVU1AbMtdVmnGzPNv'
The output should include '5WtYFfA26nTfG496gAKhkrLYUMMnwXexmE1E8Q7PvtQEyscHfirsdMzW34zDp7WEkt3exNEVwoG4ajZYrm62wpi2'
The status should be success
The stderr should equal ""
End
gpg --batch --delete-secret-and-public-key --yes 4D1CDB77E91FFCD81B10F9A7079E5BF4721944FB
End
Describe '-pkg username@protected password:'
Describe '-g -k -p password username@protected:'
gpg --batch --import --quiet specs/username_protected.asc
It 'prints prefixed public and secret keys for ed25519 gpg key matching uid "username@protected" and locked with password "password"'
When run keygen -pkg username@protected password
The output should include 'pub: C1cRu7yb5rZhsmRHQkeZxusAhtYYJypcnXpY3HycEKsU'
The output should include 'sec: VWaEdDroSCoagJDsBnDNUtXJtKAJYdqL6XKNiomz8DtiyF44FvpiMmhidXt2j8HhDBKPZ67xBGcZPnj4Myk6cB8'
It 'prints public and secret keys for ed25519 gpg key matching uid "username@protected" and locked with password "password"'
When run keygen -g -k -p password username@protected
The output should include 'C1cRu7yb5rZhsmRHQkeZxusAhtYYJypcnXpY3HycEKsU'
The output should include 'VWaEdDroSCoagJDsBnDNUtXJtKAJYdqL6XKNiomz8DtiyF44FvpiMmhidXt2j8HhDBKPZ67xBGcZPnj4Myk6cB8'
The status should be success
The stderr should equal ""
End
gpg --batch --delete-secret-and-public-key --yes 6AF574897D4979B7956AC31B6222A29CBC31A087
End
Describe '-pkg usersame:'
Describe '-g -k usersame:'
gpg --batch --import --quiet specs/usersame.asc
It 'prints prefixed base58 public and secret keys for rsa gpg key matching uid "usersame"'
When run keygen -pkg usersame
The output should include 'pub: 4NxSjjys6bo8mhM919MkvNkNPFu4zpFyxu1r7yJ39K87'
The output should include 'sec: 2cLFNeXiqcKKv5BF9JVTwtWmFHLvjDkJkrCyQbST9oYbsQLHsVaUAzbwrv5YfzQcPHu6e6XUzdstKy4kLhgDSGiw'
It 'prints base58 public and secret keys for rsa gpg key matching uid "usersame"'
When run keygen -g -k usersame
The output should include 'EGdSY9fKom7MnvHALNQU7LUoEEE2sju5ntL9eRXJ5tTM'
The output should include '4jPG9MH9LVA7HhcfFs41pXVjxDQLdgu3Mtc64Ph6U3vUMNWfJqTBdFFaviq5r6zJC8PpWUiaUhjVnYAa2E9UrFTZ'
The status should be success
The stderr should equal ""
End
gpg --batch --delete-secret-and-public-key --yes 845E099CFD17FD07346F9D26CAB2E65557C656DF
End
Describe '-pkg usersame@protected password:'
Describe '-g -k -p password usersame@protected:'
gpg --batch --import --quiet specs/usersame_protected.asc
It 'prints prefixed public and secret keys for rsa gpg key matching uid "usersame@protected" and locked with password "password"'
When run keygen -pkg usersame@protected password
The output should include 'pub: 5kh2uqNTuYsLN7fwSRP3JWM4Hotcpdkb7frRNZwo9BPp'
The output should include 'sec: LdWjjkP7gRzH4k4gNkQs2er26bE2Dhz7cGPE8fMNixe1Uv2ZHbo1QtyZxmDeTP77y6HVLbHNoXdMTHdo6ip9PHk'
It 'prints public and secret keys for rsa gpg key matching uid "usersame@protected" and locked with password "password"'
When run keygen -g -k -p password usersame@protected
The output should include '6KNNPBxkMYnccYvpePBKDewZ3JiQnmWA4e7QSsvZUzLM'
The output should include '4q4SM9qoWc2eLtfYWs7K9hb7oSCNjCLc8U6ELNrScteVGVnSBP4YMDM5V8RPsHURqCqP5ndPkqGoB74cmRxfJro7'
The status should be success
The stderr should equal ""
End