#! /usr/bin/env python

# Script to manage additional trusted root certificate in the IOS simulator
#
# Allows to add/list/delete/export trusted root certificates to the IOS simulator
# TrustStore.sqlite3 file.
#
# Additionally, root certificates added to a device can be listed and exported from 
# a device backup
#
# type ./iosCertTrustManager.py -h for help
#
#
# This script contains code derived from Python-ASN1 to parse and re-encode
# ASN1. The following notice is included:
#
# Python-ASN1 is free software that is made available under the MIT license. 
# Consult the file "LICENSE" that is
# distributed together with this file for the exact licensing terms.
#
# Python-ASN1 is copyright (c) 2007-2008 by Geert Jansen <geert@boskant.nl>. 
# see https://github.com/geertj/python-asn1

import os
import sys
import argparse
import sqlite3
import ssl
import hashlib
import subprocess
import string
import binascii
import plistlib

def query_yes_no(question, default="yes"):
    """Ask a yes/no question via raw_input() and return their answer.
    
    "question" is a string that is presented to the user.
    "default" is the presumed answer if the user just hits <Enter>.
        It must be "yes" (the default), "no" or None (meaning
        an answer is required of the user).

    The "answer" return value is one of "yes" or "no".
    """
    valid = {"yes":"yes",   "y":"yes",  "ye":"yes",
             "no":"no",     "n":"no"}
    if default == None:
        prompt = " [y/n] "
    elif default == "yes":
        prompt = " [Y/n] "
    elif default == "no":
        prompt = " [y/N] "
    else:
        raise ValueError("invalid default answer: '%s'" % default)

    while 1:
        sys.stdout.write(question + prompt)
        choice = raw_input().lower()
        if default is not None and choice == '':
            return default
        elif choice in valid.keys():
            return valid[choice]
        else:
            sys.stdout.write("Please respond with 'yes' or 'no' "\
                             "(or 'y' or 'n').\n")

#----------------------------------------------------------------------
# A simple ASN1 decoder/encoder based on Python-ASN1
#----------------------------------------------------------------------

class ASN1:
    Sequence = 0x10
    Set = 0x11
    PrintableString = 0x13

    TypeConstructed = 0x20
    TypePrimitive = 0x00

    ClassUniversal = 0x00
    ClassApplication = 0x40
    ClassContext = 0x80
    ClassPrivate = 0xc0

class Error(Exception):
    """ASN1 error"""


class Encoder(object):
    """A simple ASN.1 encoder. Uses DER encoding."""

    def __init__(self):
        """Constructor."""
        self.m_stack = None

    def start(self):
        """Start encoding."""
        self.m_stack = [[]]

    def enter(self, nr, cls):
        """Start a constructed data value."""
        if self.m_stack is None:
            raise Error, 'Encoder not initialized. Call start() first.'
        self._emit_tag(nr, ASN1.TypeConstructed, cls)
        self.m_stack.append([])

    def leave(self):
        """Finish a constructed data value."""
        if self.m_stack is None:
            raise Error, 'Encoder not initialized. Call start() first.'
        if len(self.m_stack) == 1:
            raise Error, 'Tag stack is empty.'
        value = ''.join(self.m_stack[-1])
        del self.m_stack[-1]
        self._emit_length(len(value))
        self._emit(value)

    def write(self, value, nr, typ, cls):
        """Write a primitive data value."""
        if self.m_stack is None:
            raise Error, 'Encoder not initialized. Call start() first.'
        self._emit_tag(nr, typ, cls)
        self._emit_length(len(value))
        self._emit(value)

    def output(self):
        """Return the encoded output."""
        if self.m_stack is None:
            raise Error, 'Encoder not initialized. Call start() first.'
        if len(self.m_stack) != 1:
            raise Error, 'Stack is not empty.'
        output = ''.join(self.m_stack[0])
        return output

    def _emit_tag(self, nr, typ, cls):
        """Emit a tag."""
        if nr < 31:
            self._emit_tag_short(nr, typ, cls)
        else:
            self._emit_tag_long(nr, typ, cls)

    def _emit_tag_short(self, nr, typ, cls):
        """Emit a short (< 31 bytes) tag."""
        assert nr < 31
        self._emit(chr(nr | typ | cls))

    def _emit_tag_long(self, nr, typ, cls):
        """Emit a long (>= 31 bytes) tag."""
        head = chr(typ | cls | 0x1f)
        self._emit(head)
        values = []
        values.append((nr & 0x7f))
        nr >>= 7
        while nr:
            values.append((nr & 0x7f) | 0x80)
            nr >>= 7
        values.reverse()
        values = map(chr, values)
        for val in values:
            self._emit(val)

    def _emit_length(self, length):
        """Emit length octects."""
        if length < 128:
            self._emit_length_short(length)
        else:
            self._emit_length_long(length)

    def _emit_length_short(self, length):
        """Emit the short length form (< 128 octets)."""
        assert length < 128
        self._emit(chr(length))

    def _emit_length_long(self, length):
        """Emit the long length form (>= 128 octets)."""
        values = []
        while length:
            values.append(length & 0xff)
            length >>= 8
        values.reverse()
        values = map(chr, values)
        # really for correctness as this should not happen anytime soon
        assert len(values) < 127
        head = chr(0x80 | len(values))
        self._emit(head)
        for val in values:
            self._emit(val)

    def _emit(self, s):
        """Emit raw bytes."""
        assert isinstance(s, str)
        self.m_stack[-1].append(s)


class Decoder(object):
    """A minimal ASN.1 decoder. Understands BER (and DER which is a subset)."""

    def __init__(self):
        """Constructor."""
        self.m_stack = None
        self.m_tag = None

    def start(self, data):
        """Start processing `data'."""
        if not isinstance(data, str):
            raise Error, 'Expecting string instance.'
        self.m_stack = [[0, data]]
        self.m_tag = None

    def peek(self):
        """Return the value of the next tag without moving to the next
        TLV record."""
        if self.m_stack is None:
            raise Error, 'No input selected. Call start() first.'
        if self._end_of_input():
            return None
        if self.m_tag is None:
            self.m_tag = self._read_tag()
        return self.m_tag

    def read(self):
        """Read a simple value and move to the next TLV record."""
        if self.m_stack is None:
            raise Error, 'No input selected. Call start() first.'
        if self._end_of_input():
            return None
        tag = self.peek()
        length = self._read_length()
        value = self._read_value(tag[0], length)
        self.m_tag = None
        return (tag, value)

    def eof(self):
        """Return True if we are end of input."""
        return self._end_of_input()

    def enter(self):
        """Enter a constructed tag."""
        if self.m_stack is None:
            raise Error, 'No input selected. Call start() first.'
        nr, typ, cls = self.peek()
        if typ != ASN1.TypeConstructed:
            raise Error, 'Cannot enter a non-constructed tag.'
        length = self._read_length()
        bytes = self._read_bytes(length)
        self.m_stack.append([0, bytes])
        self.m_tag = None

    def leave(self):
        """Leave the last entered constructed tag."""
        if self.m_stack is None:
            raise Error, 'No input selected. Call start() first.'
        if len(self.m_stack) == 1:
            raise Error, 'Tag stack is empty.'
        del self.m_stack[-1]
        self.m_tag = None

    def _read_tag(self):
        """Read a tag from the input."""
        byte = self._read_byte()
        cls = byte & 0xc0
        typ = byte & 0x20
        nr = byte & 0x1f
        if nr == 0x1f:
            nr = 0
            while True:
                byte = self._read_byte()
                nr = (nr << 7) | (byte & 0x7f)
                if not byte & 0x80:
                    break
        return (nr, typ, cls)

    def _read_length(self):
        """Read a length from the input."""
        byte = self._read_byte()
        if byte & 0x80:
            count = byte & 0x7f
            if count == 0x7f:
                raise Error, 'ASN1 syntax error'
            bytes = self._read_bytes(count)
            bytes = [ ord(b) for b in bytes ]
            length = 0L
            for byte in bytes:
                length = (length << 8) | byte
            try:
                length = int(length)
            except OverflowError:
                pass
        else:
            length = byte
        return length

    def _read_value(self, nr, length):
        """Read a value from the input."""
        bytes = self._read_bytes(length)
        value = bytes
        return value

    def _read_byte(self):
        """Return the next input byte, or raise an error on end-of-input."""
        index, input = self.m_stack[-1]
        try:
            byte = ord(input[index])
        except IndexError:
            raise Error, 'Premature end of input.'
        self.m_stack[-1][0] += 1
        return byte

    def _read_bytes(self, count):
        """Return the next `count' bytes of input. Raise error on
        end-of-input."""
        index, input = self.m_stack[-1]
        bytes = input[index:index+count]
        if len(bytes) != count:
            raise Error, 'Premature end of input.'
        self.m_stack[-1][0] += count
        return bytes

    def _end_of_input(self):
        """Return True if we are at the end of input."""
        index, input = self.m_stack[-1]
        assert not index > len(input)
        return index == len(input)

#----------------------------------------------------------------------
# Certificate class
#----------------------------------------------------------------------

class Certificate:
    """Represents a loaded certificate
    """
    def __init__(self):
        self._init_data()

    def _init_data(self):
        self._fingerprint = None
        self._data = None
        self._subject = None
        self._filepath = None

    def load_PEMfile(self, certificate_path):
        """Load a certificate from a file in PEM format
        """
        self._init_data()
        self._filepath = certificate_path
        with open(self._filepath, "r") as inputFile:
            PEMdata = inputFile.read()
        # convert to binary (DER format)
        self._data = ssl.PEM_cert_to_DER_cert(PEMdata)
        
    def load_DERfile(self, certificate_path):
        """Load a certificate from a file in PEM format
        """
        self._init_data()
        self._filepath = certificate_path
        with open(self._filepath, "r") as inputFile:
            PEMdata = inputFile.read()
        # convert to binary (DER format)
        self._data = PEMdata

    def save_PEMfile(self, certificate_path):
        """Save a certificate to a file in PEM format
        """
        self._filepath = certificate_path
        # convert to text (PEM format)
        PEMdata = ssl.DER_cert_to_PEM_cert(self._data)
        with open(self._filepath, "w") as output_file:
            output_file.write(PEMdata)
        
    def load_data(self, data):
        self._init_data()
        self._data = data
        
    def get_data(self):
        return self._data

    def get_fingerprint(self):
        if self._fingerprint == None and self._data != None:
            sha1 = hashlib.sha1()
            sha1.update(self._data)
            self._fingerprint = sha1.digest()
        return self._fingerprint 

    def get_subject(self):
        """Get the certificate subject in human readable one line format
        """
        if self._data != None:
            # use openssl to extract the subject text in single line format
            possl = subprocess.Popen(['openssl',  'x509', '-inform',  'DER',  '-noout',  '-subject', '-issuer', '-dates', '-nameopt', 'oneline'], 
                shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=None)
            subjectText, error_text = possl.communicate(self.get_data())
            return subjectText
        return None

    def get_subject_ASN1(self):
        """Get the certificate subject in ASN1 encoded format as expected for the IOS trusted certificate keychain store
        """
        if self._subject == None and self._data != None:
            self._subject = bytearray()
            decoder = Decoder()
            decoder.start(self._data)
            decoder.enter()
            decoder.enter()
            tag, value = decoder.read()  # read version
            tag, value = decoder.read()  # serial
            tag, value = decoder.read()  
            tag, value = decoder.read()  # issuer
            tag, value = decoder.read()  # date
            decoder.enter() # enter in subject
            encoder = Encoder()
            encoder.start()
            self._process_subject(decoder, encoder)
            self._subject = encoder.output()
        return self._subject

    def _process_subject(self, input, output, indent=0):
        # trace = sys.stdout
        while not input.eof():
            tag = input.peek()
            if tag[1] == ASN1.TypePrimitive:
                tag, value = input.read()
                if tag[0] == ASN1.PrintableString:
                    value = string.upper(value)
                output.write(value, tag[0], tag[1], tag[2])
                #trace.write(' ' * indent)
                #trace.write('[%s] %s (value %s)' %
                #         (strclass(tag[2]), strid(tag[0]), repr(value)))
                #trace.write('\n')
            elif tag[1] == ASN1.TypeConstructed:
                #trace.write(' ' * indent)
                #trace.write('[%s] %s:\n' % (strclass(tag[2]), strid(tag[0])))
                input.enter()
                output.enter(tag[0], tag[2])
                self._process_subject(input, output, indent+2)
                output.leave()
                input.leave()

#----------------------------------------------------------------------
# IOS TrustStore.sqlite3 handling
#----------------------------------------------------------------------

class TrustStore:
    """Represents the IOS trusted certificate store
    """
    def __init__(self, path, title=None):
        self._path = path
        if title: 
            self._title = title 
        else: 
            self._title = path
        self._tset = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"\
            "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"\
            "<plist version=\"1.0\">\n"\
            "<array/>\n"\
            "</plist>\n"
        #with open('cert_tset.plist', "rb") as inputFile:
        #    self._tset = inputFile.read()
    
    
    def is_valid(self):
        conn = sqlite3.connect(self._path)
        c = conn.cursor()
        row = c.execute('SELECT count(*) FROM sqlite_master WHERE type=\'table\' AND name=\'tsettings\'').fetchone()
        conn.close()
        return (row[0] > 0) 
    
    def _add_record(self, sha1, subj, tset, data):
        if not self.is_valid():
            print "  Invalid TrustStore.sqlite3"
            return
        conn = sqlite3.connect(self._path)
        c = conn.cursor()
        c.execute('SELECT COUNT(*) FROM tsettings WHERE subj=?', [sqlite3.Binary(subj)])
        row = c.fetchone()
        if row[0] == 0:
            c.execute('INSERT INTO tsettings (sha1, subj, tset, data) VALUES (?, ?, ?, ?)', [sqlite3.Binary(sha1), sqlite3.Binary(subj), sqlite3.Binary(tset), sqlite3.Binary(data)])
            print '  Certificate added'
        else:
            c.execute('UPDATE tsettings SET sha1=?, tset=?, data=? WHERE subj=?', [sqlite3.Binary(sha1), sqlite3.Binary(tset), sqlite3.Binary(data), sqlite3.Binary(subj)])
            print '  Existing certificate replaced'
        conn.commit()
        conn.close()

    def _loadBlob(self, baseName, name):
        with open(baseName + '_' + name + '.bin', 'rb') as inputFile:
            return inputFile.read()

    def _saveBlob(self, baseName, name, data):
        with open(baseName + '_' + name + '.bin', 'wb') as outputFile:
            outputFile.write (data)

    
    def add_certificate(self, certificate):
        self._add_record(certificate.get_fingerprint(), certificate.get_subject_ASN1(), 
            self._tset, certificate.get_data())
    
    def export_certificates(self, base_filename):
        if not self.is_valid():
            print "  Invalid TrustStore.sqlite3"
            return
        conn = sqlite3.connect(self._path)
        c = conn.cursor()
        index = 1
        print
        print self._title
        for row in c.execute('SELECT subj, data FROM tsettings'):
            cert = Certificate()
            cert.load_data(row[1])
            if query_yes_no("  " + cert.get_subject() + "    Export certificate", "no") == "yes":
                cert.save_PEMfile(base_filename + "_" + str(index) + ".crt")
                index = index + 1
        conn.close()
    
    def export_certificates_data(self, base_filename):
        if not self.is_valid():
            print "  Invalid TrustStore.sqlite3"
            return
        conn = sqlite3.connect(self._path)
        c = conn.cursor()
        index = 1
        for row in c.execute('SELECT sha1, subj, tset, data FROM tsettings'):
            cert = Certificate()
            cert.load_data(row[3])
            if query_yes_no("  " + cert.get_subject() + "    Export certificate", "no") == "yes":
                base_filename2 = base_filename + "_" + str(index)
                self._saveBlob(base_filename2, 'sha1', row[0])
                self._saveBlob(base_filename2, 'subj', row[1])
                self._saveBlob(base_filename2, 'tset', row[2])
                self._saveBlob(base_filename2, 'data', row[3])
        conn.close()

    def import_certificate_data(self, base_filename):
        certificateSha1 = self._loadBlob(base_filename, 'sha1')
        certificateSubject = self._loadBlob(base_filename, 'subj')
        certificateTSet = self._loadBlob(base_filename, 'tset')
        certificateData = self._loadBlob(base_filename, 'data')
        self._add_record(certificateSha1, certificateSubject, certificateTSet, certificateData)

    def list_certificates(self):
        #print
        #print self._title
        if not self.is_valid():
            print "  Invalid TrustStore.sqlite3"
            return
        conn = sqlite3.connect(self._path)
        c = conn.cursor()
        l = []
        for row in c.execute('SELECT * FROM tsettings'):
            cert = Certificate()
            cert.load_data(row[3])
            #print "  ", cert.get_subject()
            l.append(cert.get_subject())
        conn.close()
        return l

    def delete_certificates(self):
        if not self.is_valid():
            print "  Invalid TrustStore.sqlite3"
            return
        conn = sqlite3.connect(self._path)
        c = conn.cursor()
        print
        print self._title
        todelete = []
        for row in c.execute('SELECT subj, data FROM tsettings'):
            cert = Certificate()
            cert.load_data(row[1])
            if query_yes_no("  " + cert.get_subject() + "    Delete certificate", "no") == "yes":
                todelete.append(row[0])
        for item in todelete:
            c.execute('DELETE FROM tsettings WHERE subj=?', [item])
        conn.commit()
        conn.close()
        
    def delete_cert_by_subject(self, _cert):
        if not self.is_valid():
            print "  Invalid TrustStore.sqlite3"
            return False
        conn = sqlite3.connect(self._path)
        c = conn.cursor()
        #print
        #print self._title
        todelete = []
        for row in c.execute('SELECT subj, data FROM tsettings'):
            cert = Certificate()
            cert.load_data(row[1])
            _subject = cert.get_subject()
            if _subject == _cert:
                if query_yes_no(_subject + "    Delete certificate", "no") == "yes":
                    todelete.append(row[0])
        for item in todelete:
            c.execute('DELETE FROM tsettings WHERE subj=?', [item])
        conn.commit()
        conn.close()
        return True

#----------------------------------------------------------------------
# IOS Simulator access
#----------------------------------------------------------------------

class IOSSimulator:
    """Represents an instance of an IOS simulator folder
    """
    simulatorDir = os.getenv('HOME') + "/Library/Developer/CoreSimulator/Devices/"
    trustStorePath = "/data/Library/Keychains/TrustStore.sqlite3"
    
    def __init__(self, subdir):
        self.plist = plistlib.readPlist(self.simulatorDir + subdir + "/device.plist")
        self.version = self.plist["runtime"].split(".")[-1].replace("iOS-", "").replace("-", ".")
        self.title = self.plist["name"] + " " + self.version
        self.truststore_file = self.simulatorDir + subdir + self.trustStorePath
        
    def is_valid(self):
        return os.path.isfile(self.truststore_file)

def ios_simulators():
    """An iterator over the available IOS simulator versions
    """
    for sdk_dir in os.listdir(IOSSimulator.simulatorDir):
        if not sdk_dir.startswith('.'):
            simulator = IOSSimulator(sdk_dir)
            if simulator.is_valid():
                yield simulator
        
#----------------------------------------------------------------------
# Device backup support
#----------------------------------------------------------------------

class DeviceBackup:
    """Represents an instance of an IOS simulator folder
    """
    trustStore_filename = "61c8b15a0110ab17d1b7467c3a042eb1458426c6"
    
    def __init__(self, path):
        self._path = path
        self._isvalid = False
        info_plist = self._path + "/Info.plist"
        if os.path.isfile(info_plist):
            try:
                info = plistlib.readPlist(info_plist)
                self.device_name = info["Device Name"]
                self.title = "Backup of " + self.device_name + " - " + str(info["Last Backup Date"])
                self._isvalid = True
            except:
                pass
        
    def is_valid(self):
        return self._isvalid
    
    def get_truststore_file(self):
        return self._path + "/" + DeviceBackup.trustStore_filename


def device_backups():
    """An iterator over the available device backups
    """
    base_backupdir = os.getenv('HOME') + "/Library/Application Support/MobileSync/Backup/"
    for backup_dir in os.listdir(base_backupdir):
        backup = DeviceBackup(base_backupdir + backup_dir)
        if backup.is_valid():
            yield backup

#----------------------------------------------------------------------
# Individual command implementation and main function
#----------------------------------------------------------------------

class Program:
    def import_to_simulator(self, certificate_filepath, truststore_filepath=None):
        cert = Certificate()
        cert.load_PEMfile(certificate_filepath)
        print cert.get_subject()
        if truststore_filepath:
            if query_yes_no("Import certificate to " + truststore_filepath, "no") == "yes":
                tstore = TrustStore(truststore_filepath)
                tstore.add_certificate(cert)
            return
        for simulator in ios_simulators():
            if query_yes_no("Import certificate to " + simulator.title, "no") == "yes":
                print "Importing to " + simulator.truststore_file
                tstore = TrustStore(simulator.truststore_file)
                tstore.add_certificate(cert)
    
    def addfromdump(self, dump_base_filename, truststore_filepath=None):
        if truststore_filepath:
            if query_yes_no("Import to " + truststore_filepath, "no") == "yes":
                tstore = TrustStore(truststore_filepath)
                tstore.import_certificate_data(dump_base_filename)
            return
        for simulator in ios_simulators():
            if query_yes_no("Import to " + simulator.title, "no") == "yes":
                print "Importing to " + simulator.truststore_file
                tstore = TrustStore(simulator.truststore_file)
                tstore.import_certificate_data(dump_base_filename)
    
    def list_simulator_trustedcertificates(self, truststore_filepath=None):
        if truststore_filepath:
            tstore = TrustStore(truststore_filepath)
            tstore.list_certificates()
            return
        for simulator in ios_simulators():
            tstore = TrustStore(simulator.truststore_file, simulator.title)
            tstore.list_certificates()
    
    def export_simulator_trustedcertificates(self, certificate_base_filename, mode_dump, truststore_filepath=None):
        if truststore_filepath:
            tstore = TrustStore(truststore_filepath)
            if mode_dump:
                tstore.export_certificates_data(certificate_base_filename)
            else:
                tstore.export_certificates(certificate_base_filename)
            return
        for simulator in ios_simulators():
            tstore = TrustStore(simulator.truststore_file, simulator.title)
            if mode_dump:
                tstore.export_certificates_data(certificate_base_filename + "_" + simulator.version)
            else:
                tstore.export_certificates(certificate_base_filename + "_" + simulator.version)
    
    def delete_simulator_trustedcertificates(self, truststore_filepath=None):
        if truststore_filepath:
            tstore = TrustStore(truststore_filepath)
            tstore.delete_certificates()
            return
        for simulator in ios_simulators():
            tstore = TrustStore(simulator.truststore_file, simulator.title)
            tstore.delete_certificates()
    
    def list_device_trustedcertificates(self):
        for backup in device_backups():
            tstore = TrustStore(backup.get_truststore_file(), backup.title)
            tstore.list_certificates()
    
    def export_device_trustedcertificates(self, certificate_base_filename, mode_dump):
        for backup in device_backups():
            tstore = TrustStore(backup.get_truststore_file(), backup.title)
            if mode_dump:
                tstore.export_certificates_data(certificate_base_filename + "_" + backup.device_name)
            else:
                tstore.export_certificates(certificate_base_filename + "_" + backup.device_name)
    
    def run(self):
        parser = argparse.ArgumentParser()
        group = parser.add_mutually_exclusive_group(required=True)
        group.add_argument("-l", "--list", help="list custom trusted certificates in IOS simulator", action="store_true")
        group.add_argument("-d", "--delete", help="delete custom trusted certificates in IOS simulator", action="store_true")
        group.add_argument("-a", "--add", help="specifies a certificate file in PEM format to import and add to the IOS simulator trusted list", dest='certificate_file')
        group.add_argument("-e", "--export", help="export custom trusted certificates from IOS simulator in PEM format. ", dest='export_base_filename')
        group.add_argument("--dump", help="dump custom trusted certificates records from IOS simulator. ", dest='dump_base_filename')
        group.add_argument("--addfromdump", help="add custom trusted certificates records to IOS simulator from dump file created with --dump. ", dest='adddump_base_filename')
        parser.add_argument("-t", "--truststore", help="specify the path of the IOS TrustStore.sqlite3 file to edit. The default is to select and prompt for each available version")
        parser.add_argument("-b", "--devicebackup", help="(experimental) select a device backup as the TrustStore.sqlite3 source for list or export", action="store_true")
        args = parser.parse_args()
        if args.truststore and not os.path.isfile(args.truststore):
            print "invalid file: ", args.truststore
            exit(1)
        if args.devicebackup:
            if args.list:
                self.list_device_trustedcertificates()
            elif args.export_base_filename:
                self.export_device_trustedcertificates(args.export_base_filename, False)
            elif args.dump_base_filename:
                self.export_device_trustedcertificates(args.dump_base_filename, True)
            else:
                print "option not supported"
        elif args.list:
            self.list_simulator_trustedcertificates(args.truststore)
        elif args.delete:
            self.delete_simulator_trustedcertificates(args.truststore)
        elif args.certificate_file:
            self.import_to_simulator(args.certificate_file, args.truststore)
        elif args.export_base_filename:
            self.export_simulator_trustedcertificates(args.export_base_filename, False, args.truststore)
        elif args.dump_base_filename:
            self.export_simulator_trustedcertificates(args.dump_base_filename, True, args.truststore)
        elif args.adddump_base_filename:
            self.addfromdump(args.adddump_base_filename, args.truststore)
        print

if __name__ == "__main__":
    program = Program()
    program.run()