keyserver: add generate key function

The superuser API, initdb, and tests will all need this functionality.
This commit is contained in:
Jimmy Zelinskie 2016-04-06 20:03:04 -04:00 committed by Jimmy Zelinskie
parent 23a8a29654
commit d19eb16b45
4 changed files with 35 additions and 24 deletions

View file

@ -1,10 +1,18 @@
import json
from calendar import timegm from calendar import timegm
from datetime import datetime, timedelta from datetime import datetime, timedelta
from hashlib import sha256
from peewee import JOIN_LEFT_OUTER from peewee import JOIN_LEFT_OUTER
from Crypto.PublicKey import RSA
from jwkest.jwk import RSAKey
from data.database import db_for_update, User, ServiceKey, ServiceKeyApproval from data.database import db_for_update, User, ServiceKey, ServiceKeyApproval
from data.model import ServiceKeyDoesNotExist, ServiceKeyAlreadyApproved, db_transaction, config from data.model import ServiceKeyDoesNotExist, ServiceKeyAlreadyApproved, db_transaction, config
from data.model.notification import create_notification, delete_all_notifications_by_path_prefix from data.model.notification import create_notification, delete_all_notifications_by_path_prefix
from util import canonicalize
def _expired_keys_clause(service): def _expired_keys_clause(service):
return ((ServiceKey.service == service) & return ((ServiceKey.service == service) &
@ -49,6 +57,18 @@ def create_service_key(name, kid, service, jwk, metadata, expiration_date):
_notify_superusers(key) _notify_superusers(key)
_gc_expired(service) _gc_expired(service)
return key
def generate_service_key(service, expiration_date, kid=None, name='', metadata=None):
private_key = RSA.generate(2048)
jwk = RSAKey(key=private_key.publickey()).serialize()
if kid is None:
kid = sha256(json.dumps(canonicalize(jwk), separators=(',', ':'))).hexdigest()
key = create_service_key(name, kid, service, jwk, metadata or {}, expiration_date)
return (private_key, key)
def replace_service_key(old_kid, kid, jwk, metadata, expiration_date): def replace_service_key(old_kid, kid, jwk, metadata, expiration_date):
try: try:

View file

@ -1,17 +1,13 @@
""" Superuser API. """ """ Superuser API. """
import json
import logging import logging
import os import os
import string import string
from datetime import datetime from datetime import datetime
from hashlib import sha256
from random import SystemRandom from random import SystemRandom
from Crypto.PublicKey import RSA
from flask import request, make_response, jsonify from flask import request, make_response, jsonify
from jwkest.jwk import RSAKey
import features import features
@ -26,7 +22,6 @@ from endpoints.api import (ApiResource, nickname, resource, validate_json_reques
from endpoints.api.logs import get_logs, get_aggregate_logs from endpoints.api.logs import get_logs, get_aggregate_logs
from data import model from data import model
from data.database import ServiceKeyApprovalType from data.database import ServiceKeyApprovalType
from util import canonicalize
from util.useremails import send_confirmation_email, send_recovery_email from util.useremails import send_confirmation_email, send_recovery_email
@ -576,22 +571,17 @@ class SuperUserServiceKeyManagement(ApiResource):
'ip': request.remote_addr, 'ip': request.remote_addr,
}) })
# Generate the private key but *do not save it on the server anywhere*. # Generate a key with a private key that we *never save*.
private_key = RSA.generate(2048) (private_key, key) = model.service_keys.generate_service_key(body['service'], metadata,
jwk = RSAKey(key=private_key.publickey()).serialize() expiration_date,
kid = sha256(json.dumps(canonicalize(jwk), separators=(',', ':'))).hexdigest() name=body.get('name', ''))
# Create the service key.
model.service_keys.create_service_key(body.get('name', ''), kid, body['service'], jwk,
metadata, expiration_date)
# Auto-approve the service key. # Auto-approve the service key.
model.service_keys.approve_service_key(kid, user, ServiceKeyApprovalType.SUPERUSER, model.service_keys.approve_service_key(key.kid, user, ServiceKeyApprovalType.SUPERUSER,
notes=body.get('notes', '')) notes=body.get('notes', ''))
# Log the creation and auto-approval of the service key. # Log the creation and auto-approval of the service key.
key_log_metadata = { key_log_metadata = {
'kid': kid, 'kid': key.kid,
'preshared': True, 'preshared': True,
'service': body['service'], 'service': body['service'],
'name': body.get('name', ''), 'name': body.get('name', ''),
@ -603,7 +593,7 @@ class SuperUserServiceKeyManagement(ApiResource):
log_action('service_key_approve', None, key_log_metadata) log_action('service_key_approve', None, key_log_metadata)
return jsonify({ return jsonify({
'kid': kid, 'kid': key.kid,
'name': body.get('name', ''), 'name': body.get('name', ''),
'public_key': private_key.publickey().exportKey('PEM'), 'public_key': private_key.publickey().exportKey('PEM'),
'private_key': private_key.exportKey('PEM'), 'private_key': private_key.exportKey('PEM'),

View file

@ -157,20 +157,18 @@ def __create_subtree(with_storage, repo, structure, creator_username, parent, ta
def __generate_service_key(kid, name, user, timestamp, approval_type, expiration=None, def __generate_service_key(kid, name, user, timestamp, approval_type, expiration=None,
metadata=None): metadata=None, service='sample_service'):
private_key = RSA.generate(1024) _, key = model.service_keys.generate_service_key(service, expiration, kid=kid,
jwk = RSAKey(key=private_key.publickey()).serialize() name=name, metadata=metadata)
metadata = metadata or {}
model.service_keys.create_service_key(name, kid, 'sample_service', jwk, metadata, expiration)
if approval_type is not None: if approval_type is not None:
model.service_keys.approve_service_key(kid, user, approval_type, model.service_keys.approve_service_key(key.kid, user, approval_type,
notes='The **test** apporval') notes='The **test** apporval')
key_metadata = { key_metadata = {
'kid': kid, 'kid': kid,
'preshared': True, 'preshared': True,
'service': 'sample_service', 'service': service,
'name': name, 'name': name,
'expiration_date': expiration, 'expiration_date': expiration,
'auto_approved': True 'auto_approved': True
@ -664,6 +662,9 @@ def populate_database(minimal=False, with_storage=False):
__generate_service_key('kid4', 'autorotatingkey', new_user_1, six_ago, __generate_service_key('kid4', 'autorotatingkey', new_user_1, six_ago,
ServiceKeyApprovalType.KEY_ROTATION, today + timedelta(1), ServiceKeyApprovalType.KEY_ROTATION, today + timedelta(1),
dict(rotation_ttl=timedelta(hours=12).total_seconds())) dict(rotation_ttl=timedelta(hours=12).total_seconds()))
__generate_service_key('kid5', 'key for another service', new_user_1, today,
ServiceKeyApprovalType.SUPERUSER, today + timedelta(14),
service='different_sample_service')
model.log.log_action('org_create_team', org.username, performer=new_user_1, model.log.log_action('org_create_team', org.username, performer=new_user_1,
timestamp=week_ago, metadata={'team': 'readers'}) timestamp=week_ago, metadata={'team': 'readers'})

Binary file not shown.