Commit 896a3462 by Vincent Pelletier

A bit more pseudo-code content.

1 parent d5d18f24
# This file is part of caucase
# Copyright (C) 2017 Nexedi
# Alain Takoudjou <alain.takoudjou@nexedi.com>
# Vincent Pelletier <vincent@nexedi.com>
#
# caucase is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# caucase 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.
#
# You should have received a copy of the GNU General Public License
# along with caucase. If not, see <http://www.gnu.org/licenses/>.
# This file is part of caucase
# Copyright (C) 2017 Nexedi
# Alain Takoudjou <alain.takoudjou@nexedi.com>
# Vincent Pelletier <vincent@nexedi.com>
#
# caucase is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# caucase 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.
#
# You should have received a copy of the GNU General Public License
# along with caucase. If not, see <http://www.gnu.org/licenses/>.
import json
class CertificateAuthority(object):
def __init__(self, storage, ca_life_time, digest_list, crt_constraints...):
self._storage = storage
self.reload()
def reload(self):
"""
Refresh instance's knowledge of database content
(as storage house-keeping may/will happen outside our control)
"""
self._ca_key_pairs_list = storage.getCAKeyPairList()
if not self._ca_key_pairs_list:
self.createCAKeyPair()
self._ca_key_pairs_list = storage.getCAKeyPairList()
assert self._ca_key_pairs_list
def createCAKeyPair(self):
"""
Create a new ca key + certificate pair
"""
# generate CA key pair
self._storage.storeCAKeyPair(key_pair)
def getPendingCertificateRequestList(self):
return self._storage.getPendingCertificateRequestList()
def createCertificateSigningRequest(self, csr):
"""
Sanity-check CSR, stores it and generates a unique signing request
identifier (crt_id).
"""
# Check number of already-pending signing requests
# Check if csr is self-signed
# Check it has a CN (?)
# Check its extensions
# more ?
return self._storage.storeCertificateSigningRequest(csr)
def deletePendingCertificateRequest(self, crt_id):
"""
Reject a pending certificate signing request.
"""
self._storage.deletePendingCertificateRequest(crt_id)
def getCertificateSigningRequest(self, crt_id):
"""
Retrieve the content of a pending signing request.
"""
return self._storage.getCertificateSigningRequest(crt_id)
def createCertificate(self, crt_id):
csr = self.getCertificateSigningRequest(crt_id)
# Apply extensions (ex: "not a certificate", ...)
# Generate a certificate from the CSR
# Sign the certificate with the current CA key
self._storage.storeCertificate(crt_id, crt)
return crt
def getCertificate(self, crt_id):
return self._database.getCertificate(crt_id)
def getCACertificate(self):
"""
Return current CA certificate
"""
return self._ca_key_pairs_list[-1].crt
def getValidCACertificateChain(self):
"""
Return the ca certificate chain for all valid certificates
"""
result = []
iter_key_pair = iter(self._ca_key_paid_list)
previous_key_pair = iter_key_pair.next()
for key_pair in iter_key_pair:
result.append(utils.wrap({
'old': previous_key_pair.crt,
'new': key_pair.crt,
}, previous_key_pair.key, self.digest_list))
return result
def revokeCertificate(self, wrapped_crt):
payload = utils.unwrap(wrapped_crt, lambda x: x['crt'], self.digest_list)
crt = payload['revoke_crt']
self._storage.revokeCertificate(crt.getSerial(), crt.getNotAfterDate())
def renew(self, wrapped_csr):
payload = utils.unwrap(wrapped_crt, lambda x: x['crt'], self.digest_list)
csr = payload['renew_csr']
crt_id = self.createCertificateSigningRequest(csr)
self.createCertificate(crt_id)
return crt_id
def getCertificateRevocationList(self):
self._storage.getCertificateRevocationList()
# This file is part of caucase
# Copyright (C) 2017 Nexedi
# Alain Takoudjou <alain.takoudjou@nexedi.com>
# Vincent Pelletier <vincent@nexedi.com>
#
# caucase is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# caucase 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.
#
# You should have received a copy of the GNU General Public License
# along with caucase. If not, see <http://www.gnu.org/licenses/>.
# This file is part of caucase
# Copyright (C) 2017 Nexedi
# Alain Takoudjou <alain.takoudjou@nexedi.com>
# Vincent Pelletier <vincent@nexedi.com>
#
# caucase is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# caucase 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.
#
# You should have received a copy of the GNU General Public License
# along with caucase. If not, see <http://www.gnu.org/licenses/>.
# This file is part of caucase
# Copyright (C) 2017 Nexedi
# Alain Takoudjou <alain.takoudjou@nexedi.com>
# Vincent Pelletier <vincent@nexedi.com>
#
# caucase is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# caucase 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.
#
# You should have received a copy of the GNU General Public License
# along with caucase. If not, see <http://www.gnu.org/licenses/>.
class CertificateAuthorityException(Exception):
"""Base exception"""
pass
class NoStorage(Exception):
"""No space in storage"""
pass
class NotFound(Exception):
"""Requested resource does not exist"""
pass
class Found(Exception):
"""Requested ID is already in use"""
class BadSignature(Exception):
"""Non-x509 signature check failed"""
# This file is part of caucase
# Copyright (C) 2017 Nexedi
# Alain Takoudjou <alain.takoudjou@nexedi.com>
# Vincent Pelletier <vincent@nexedi.com>
#
# caucase is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# caucase 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.
#
# You should have received a copy of the GNU General Public License
# along with caucase. If not, see <http://www.gnu.org/licenses/>.
# This file is part of caucase
# Copyright (C) 2017 Nexedi
# Alain Takoudjou <alain.takoudjou@nexedi.com>
# Vincent Pelletier <vincent@nexedi.com>
#
# caucase is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# caucase 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.
#
# You should have received a copy of the GNU General Public License
# along with caucase. If not, see <http://www.gnu.org/licenses/>.
class Storage(object):
def __init__(self, crt_life_time, loaded_crt_life_time):
def getCAKeyPairList(self):
"""
Return the chronologically sorted (oldest in [0], newest in [-1]) certificate authority
key pairs.
"""
def storeCAKeyPair(self, key_pair):
"""
Store a certificate authority key pair.
"""
def storeCertificateSigningRequest(self, csr):
"""
Store acertificate signing request and generate a unique ID for it.
"""
raise NoStorage('Too many pending CSRs')
def deletePendingCertificateRequest(self, crt_id):
raise NotFound('No pending CSR with id %r' % (crt_id, ))
def getCertificateSigningRequest(self, crt_id):
raise NotFound('No pending CSR with id %r' % (crt_id, ))
def storeCertificate(self, crt_id, crt):
"""
Store certificate as crt_id.
"""
raise Found('CRT already exists')
def getCertificate(self, crt_id):
raise NotFound('No certficate with id %r' % (crt_id, ))
# schedule certificate removal
def revokeCertificate(self, serial, not_after_date):
"""
Add serial to the list of revoked certificates.
Associated certificate must expire at (or before) not_after_date, so
revocation can be pruned.
"""
# Store & make visible (ie, commit if applicable)
# Flush cached CRL (forcing re-generation).
def getCertificateRevocationList(self):
# Fetch cached CRL (or re-generate and store if not cached).
return crl
def housekeep(self):
"""
Remove outdated certificates (because they were retrieved long ago),
ca certificates (because they exceeded their "not valid after" date),
revocation of anway-expired certificates.
"""
# This file is part of caucase
# Copyright (C) 2017 Nexedi
# Alain Takoudjou <alain.takoudjou@nexedi.com>
# Vincent Pelletier <vincent@nexedi.com>
#
# caucase is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# caucase 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.
#
# You should have received a copy of the GNU General Public License
# along with caucase. If not, see <http://www.gnu.org/licenses/>.
import json
from .exceptions import BadSignature
def wrap(payload, key, digest_list):
"""
Sign payload (json-serialised) with key, using one of the given digests.
"""
# Choose a digest between the ones supported
payload = json.dumps(payload)
return {
"payload": payload,
"digest": digest,
"signature": sign(payload + digest + ' ', key, digest).encode('base64'),
}
def unwrap(wrapped, getCertificate, digest_list):
"""
Raise if signature does not match payload. Returns payload.
"""
# Check whether given digest is allowed
raise BadSignature('Given digest is not supported')
payload = json.loads(wrapped['payload'])
crt = getCertificate(payload)
if not check(wrapped['payload'] + wrapped['digest'] + ' ', crt, wrapped['signature'].decode('base64'), wrapped['digest']):
raise BadSignature('Signature mismatch')
return payload
# This file is part of caucase
# Copyright (C) 2017 Nexedi
# Alain Takoudjou <alain.takoudjou@nexedi.com>
# Vincent Pelletier <vincent@nexedi.com>
#
# caucase is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# caucase 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.
#
# You should have received a copy of the GNU General Public License
# along with caucase. If not, see <http://www.gnu.org/licenses/>.
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!