view test/test_dropbearconvert.py @ 1911:ced53051e200

Add ecdsa OpenSSH format for dropbearconvert
author Matt Johnston <matt@ucc.asn.au>
date Tue, 29 Mar 2022 23:27:55 +0800
parents 43ebe0028187
children
line wrap: on
line source

import subprocess
import tempfile

import pytest

keytypes = [
	"rsa", "rsa-4096",
	"ed25519",
	"ecdsa", "ecdsa-256", "ecdsa-384", "ecdsa-521",
	"dss",
	]

def parse_keytype(kt):
	if '-' in kt:
		return kt.split('-')
	else:
		return (kt, None)

@pytest.mark.parametrize("keytype", keytypes)
@pytest.mark.parametrize("keyformat", [None, "PEM"])
def test_from_openssh(request, tmp_path, keytype, keyformat):
	"""
	Convert OpenSSH to Dropbear format,
	PEM and OpenSSH internal
	"""
	opt = request.config.option
	kt, keybits = parse_keytype(keytype)

	if kt == 'dss' and keyformat is None:
		pytest.skip("dss doesn't support openssh format")

	os_kt = kt
	if os_kt == 'dss':
		# OpenSSH calls it 'dsa', Dropbear calls it 'dss'
		os_kt = 'dsa'

	os_key = tmp_path / 'oskey1'
	db_key = tmp_path / 'dbkey1'

	# Generate an OpenSSH key
	args = [
		opt.ssh_keygen,
		'-f', os_key,
		'-t', os_kt,
		'-N', '', # no password
	]
	if keybits is not None:
		args += ['-b', keybits]
	if keyformat:
		args += ['-m', keyformat]
	p = subprocess.run(args, check=True)

	# Convert to dropbear format
	args = [
		opt.dropbearconvert,
		'openssh', 'dropbear',
		os_key, db_key,
	]
	p = subprocess.run(args, check=True)

	# Compare pubkeys
	args = [
		opt.dropbearkey,
		'-f', db_key,
		'-y'
	]
	p = subprocess.run(args, check=True, stdout=subprocess.PIPE, text=True)
	db_pubkey = p.stdout.splitlines()[1].strip()
	os_pubkey = os_key.with_suffix('.pub').open().read().strip()
	# we compare the whole key including comment since it currently matches
	assert db_pubkey == os_pubkey

@pytest.mark.parametrize("keytype", keytypes)
def test_roundtrip(request, tmp_path, keytype):
	"""
	Dropbear's private key format is deterministic so
	we can compare round trip conversion. (OpenSSH's
	format has more variable comments and other fields).
	"""
	opt = request.config.option
	kt, keybits = parse_keytype(keytype)

	os_key = tmp_path / 'oskey1'
	db_key1 = tmp_path / 'dbkey1'
	db_key2 = tmp_path / 'dbkey2'

	# generate a key
	args = [
		opt.dropbearkey,
		'-t', kt,
		'-f', db_key1,
	]
	if keybits is not None:
		args += ['-s', keybits]
	p = subprocess.run(args, check=True)

	# convert to openssh
	args = [
		opt.dropbearconvert,
		'dropbear', 'openssh',
		db_key1, os_key,
	]
	p = subprocess.run(args, check=True)

	# Check ssh-keygen can read it
	args = [
		opt.ssh_keygen,
		'-f', os_key,
		'-y',
	]
	p = subprocess.run(args, check=True, text=True, stdout=subprocess.PIPE)
	os_pubkey = p.stdout.strip()

	# Compare public keys
	args = [
		opt.dropbearkey,
		'-f', db_key1,
		'-y',
	]
	p = subprocess.run(args, check=True, text=True, stdout=subprocess.PIPE)
	db_pubkey = p.stdout.splitlines()[1].strip()
	# comment may differ
	db_pubkey = db_pubkey.split(' ')[:2]
	os_pubkey = os_pubkey.split(' ')[:2]
	assert db_pubkey == os_pubkey

	# convert back to dropbear
	args = [
		opt.dropbearconvert,
		'openssh', 'dropbear',
		os_key, db_key2,
	]
	p = subprocess.run(args, check=True)
	# check the round trip is identical
	assert db_key1.open('rb').read() == db_key2.open('rb').read()