# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright (c) 2012 Samsung SDS Co., LTD
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

"""Wrappers around standard crypto data elements.

Includes root and intermediate CAs and SSH key_pairs.

"""

from __future__ import absolute_import

import base64
import hashlib
import os
import string

import Crypto.Cipher.AES

from synaps import context
from synaps import db
from synaps import exception
from synaps import flags
from synaps import log as logging
from synaps.openstack.common import cfg
from synaps import utils


LOG = logging.getLogger(__name__)

crypto_opts = [
    cfg.StrOpt('ca_file',
               default='cacert.pem',
               help=_('Filename of root CA')),
    cfg.StrOpt('key_file',
               default=os.path.join('private', 'cakey.pem'),
               help=_('Filename of private key')),
    cfg.StrOpt('crl_file',
               default='crl.pem',
               help=_('Filename of root Certificate Revocation List')),
    cfg.StrOpt('keys_path',
               default='$state_path/keys',
               help=_('Where we keep our keys')),
    cfg.StrOpt('ca_path',
               default='$state_path/CA',
               help=_('Where we keep our root CA')),
    cfg.BoolOpt('use_project_ca',
                default=False,
                help=_('Should we use a CA for each project?')),
    cfg.StrOpt('user_cert_subject',
               default='/C=US/ST=California/O=OpenStack/'
                       'OU=NovaDev/CN=%.16s-%.16s-%s',
               help=_('Subject for certificate for users, %s for '
                      'project, user, timestamp')),
    cfg.StrOpt('project_cert_subject',
               default='/C=US/ST=California/O=OpenStack/'
                       'OU=NovaDev/CN=project-ca-%.16s-%s',
               help=_('Subject for certificate for projects, %s for '
                      'project, timestamp')),
    ]

FLAGS = flags.FLAGS
FLAGS.register_opts(crypto_opts)


def ca_folder(project_id=None):
    if FLAGS.use_project_ca and project_id:
        return os.path.join(FLAGS.ca_path, 'projects', project_id)
    return FLAGS.ca_path


def ca_path(project_id=None):
    return os.path.join(ca_folder(project_id), FLAGS.ca_file)


def key_path(project_id=None):
    return os.path.join(ca_folder(project_id), FLAGS.key_file)


def crl_path(project_id=None):
    return os.path.join(ca_folder(project_id), FLAGS.crl_file)


def fetch_ca(project_id=None):
    if not FLAGS.use_project_ca:
        project_id = None
    with open(ca_path(project_id), 'r') as cafile:
        return cafile.read()


def ensure_ca_filesystem():
    """Ensure the CA filesystem exists."""
    ca_dir = ca_folder()
    if not os.path.exists(ca_path()):
        genrootca_sh_path = os.path.join(os.path.dirname(__file__),
                                         'CA',
                                         'genrootca.sh')

        start = os.getcwd()
        if not os.path.exists(ca_dir):
            os.makedirs(ca_dir)
        os.chdir(ca_dir)
        utils.execute("sh", genrootca_sh_path)
        os.chdir(start)


def _generate_fingerprint(public_key_file):
    (out, err) = utils.execute('ssh-keygen', '-q', '-l', '-f', public_key_file)
    fingerprint = out.split(' ')[1]
    return fingerprint


def generate_fingerprint(public_key):
    with utils.tempdir() as tmpdir:
        try:
            pubfile = os.path.join(tmpdir, 'temp.pub')
            with open(pubfile, 'w') as f:
                f.write(public_key)
            return _generate_fingerprint(pubfile)
        except exception.ProcessExecutionError:
            raise exception.InvalidKeypair()


def generate_key_pair(bits=1024):
    # what is the magic 65537?

    with utils.tempdir() as tmpdir:
        keyfile = os.path.join(tmpdir, 'temp')
        utils.execute('ssh-keygen', '-q', '-b', bits, '-N', '',
                      '-t', 'rsa', '-f', keyfile)
        fingerprint = _generate_fingerprint('%s.pub' % (keyfile))
        private_key = open(keyfile).read()
        public_key = open(keyfile + '.pub').read()

    return (private_key, public_key, fingerprint)


def fetch_crl(project_id):
    """Get crl file for project."""
    if not FLAGS.use_project_ca:
        project_id = None
    with open(crl_path(project_id), 'r') as crlfile:
        return crlfile.read()


def decrypt_text(project_id, text):
    private_key = key_path(project_id)
    if not os.path.exists(private_key):
        raise exception.ProjectNotFound(project_id=project_id)
    try:
        dec, _err = utils.execute('openssl',
                                 'rsautl',
                                 '-decrypt',
                                 '-inkey', '%s' % private_key,
                                 process_input=text)
        return dec
    except exception.ProcessExecutionError: