#!/usr/bin/python3
#
# Repeatable ECDSA key generator using the system authseed
# this uses python-ecdsa but monkey patches os.urandom to
# an iterated sha512 stream seeded by the system authseed
#

#
# Usage: $0 <unique-key-name> [CURVE]
#

# TODO: Error handling when authseed is missing

import sys
import os
from hashlib import sha512
import hmac
import ecdsa

#
# Iterated SHA-512 HMAC based PRNG
#
class hmac_prng():
	def __init__(self, seed):
		self.pos   = 0
		self.init  = sha512(seed).digest()
		self.state = sha512(self.init).digest()
		self.block = hmac.new(self.init, self.state, sha512).digest()

	def __call__(self, nbytes):
		buf = ''
		while (len(buf) < nbytes):
			# bytes required?
			rbytes = nbytes - len(buf)
			# bytes remaining in this state (block)
			abytes = 64 - (self.pos % 64)

			if (abytes > 0):
				buf += self.block[(self.pos%64):min(rbytes,abytes)]
				self.pos += min(rbytes,abytes);

			if (self.pos % 64 == 0):
				self.state = sha512(self.state).digest()
				self.block = hmac.new(self.init, self.state, sha512).digest()

			return buf

curves = {x.name: x for x in ecdsa.curves.curves}

def usage():
	sys.stderr.write("\n")
	sys.stderr.write("Usage: %s <unique-key-name> [CURVE]\n" % (sys.argv[0]))
	sys.stderr.write("\n")
	sys.stderr.write(" CURVE defaults to NIST256p if not specified\n")
	sys.stderr.write(" Supported curves are %s\n" % (', '.join([x for x in curves])))
	sys.stderr.write("\n")

if len(sys.argv) < 2 or len(sys.argv) > 3:
	usage();
	sys.exit(1)

# Monkey patch os.urandom
seed = open('/etc/sysconfig/authseed', 'r').read();
seed = hmac.new(seed, sys.argv[1], sha512).digest()
os.__dict__['urandom'] = hmac_prng(seed)


if len(sys.argv) == 3:
		curve = sys.argv[2]
else:
		curve = 'NIST256p'

if not curve in curves:
		sys.stderr.write("\n !! Unknown curve '%s'\n" % curve)
		usage()
		sys.exit(1)

key = ecdsa.SigningKey.generate(curve=curves[curve])

sys.stdout.write(key.to_pem())
