The life cycle of a certificate: * Generating a key pair. * Generating a CSR containing public key. * Submitting CSR to a CA. * Checking for response from CA. * Waiting for certificate to near expiration. Administrative action can also add these states: * On hold * Revoked We model that life cycle as a state machine. Now with some arbitrarily-named states for our per-certificate state machine: * Generating a key pair. States: NEED_KEY_PAIR, GENERATING_KEY_PAIR [*], NEED_KEY_GEN_PERMS, NEED_KEY_GEN_TOKEN, NEED_KEY_GEN_PIN, HAVE_KEY_PAIR * Reading info about key pair. NEED_KEYINFO, READING_KEYINFO [*], NEED_KEYINFO_READ_TOKEN, NEED_KEYINFO_READ_PIN, HAVE_KEYINFO * Generating a CSR containing public key. States: NEED_CSR, GENERATING_CSR [*], NEED_CSR_GEN_TOKEN, NEED_CSR_GEN_PIN, HAVE_CSR * Submitting CSR to a CA. States: NEED_TO_SUBMIT, SUBMITTING [*] * Don't know which CA to submit to. States: NEED_CA [*] * Don't have complete information about CA. States: CA_UNCONFIGURED [*] * Can't contact CA. States: CA_UNREACHABLE [*] * Rejected, very sad. States: CA_REJECTED [*] * CA is thinking. States: CA_WORKING [*] * Saving certificate to the desired location and parsing it for the information we think is interesting. States: NEED_TO_SAVE_CERT, PRE_SAVE_CERT[*], START_SAVING_CERT, SAVING_CERT [*], NEED_CERTSAVE_PERMS, NEED_TO_READ_CERT, READING_CERT [*], SAVED_CERT, POST_SAVED_CERT[*] * Waiting for certificate to near expiration. States: MONITORING * Notifying the admin of impending/passed expiration. States: NEED_TO_NOTIFY_VALIDITY, NOTIFYING_VALIDITY [*] * Notifying the admin of CA rejection. States: NEED_TO_NOTIFY_REJECTION, NOTIFYING_REJECTION [*] * Notifying the admin of CA issued cert, but not saved. States: NEED_TO_NOTIFY_ISSUED_FAILED, NOTIFYING_ISSUED_FAILED [*] * Notifying the admin of that we failed to save CA certs. States: NEED_TO_NOTIFY_ONLY_CA_SAVE_FAILED, NOTIFYING_ONLY_CA_SAVE_FAILED * Saving CA certs while saving an issued cert. States: NEED_TO_SAVE_CA_CERTS, START_SAVING_CA_CERTS, SAVING_CA_CERTS [*], NEED_CA_CERT_SAVE_PERMS * Notifying the admin of CA issued cert, and saved. States: NEED_TO_NOTIFY_ISSUED_SAVED, NOTIFYING_ISSUED_SAVED [*] * Saving CA certs while not saving an issued cert. States: NEED_TO_SAVE_ONLY_CA_CERTS, START_SAVING_ONLY_CA_CERTS, SAVING_ONLY_CA_CERTS [*] * Waiting for user input States: NEED_GUIDANCE [*] * Getting our bearings States: NEWLY_ADDED, NEWLY_ADDED_READING_KEYINFO [*], NEWLY_ADDED_NEED_KEYINFO_READ_TOKEN, NEWLY_ADDED_NEED_KEYINFO_READ_PIN, NEWLY_ADDED_START_READING_CERT, NEWLY_ADDED_READING_CERT [*], NEWLY_ADDED_DECIDING [*] [*] Denotes states in which we have to wait for instructions from the user or completion of interaction with external systems. State logic: NEED_KEY_PAIR: start-key-generation state_next = GENERATING_KEY_PAIR state_transition = now break GENERATING_KEY_PAIR: if starting-up state_next = NEED_KEY_PAIR state_transition = now else if keygen-finished if key-was-stored-successfully state_next = HAVE_KEY_PAIR state_transition = now elseif key-store-needs-token state_next = NEED_KEY_GEN_TOKEN state_transition = now elseif key-store-needs-pin state_next = NEED_KEY_GEN_PIN state_transition = now elseif key-store-needs-perms state_next = NEED_KEY_GEN_PERMS state_transition = now else state_next = NEED_KEY_PAIR state_transition = now else state_next = GENERATING_KEY_PAIR state_transition = when-notified break NEED_KEY_GEN_PERMS: if starting-up state_next = NEED_KEY_PAIR state_transition = now break NEED_KEY_GEN_TOKEN: if starting-up state_next = NEED_KEY_PAIR state_transition = soon break NEED_KEY_GEN_PIN: if starting-up state_next = NEED_KEY_PAIR state_transition = now break HAVE_KEY_PAIR: state_next = NEED_KEYINFO state_transition = now break NEED_KEYINFO: start-reading-key-information state_next = READING_KEYINFO state_transition = now break READING_KEYINFO: if starting-up state_next = NEED_KEYINFO state_transition = now else if finished-reading-key-information state_next = HAVE_KEYINFO state_transition = now elseif key-store-needs-token state_next = NEED_KEYINFO_READ_TOKEN state_transition = now elseif key-store-needs-pin state_next = NEED_KEYINFO_READ_PIN state_transition = now else state_next = NEED_KEY_PAIR state_transition = now break NEED_KEYINFO_READ_TOKEN: if starting-up state_next = NEED_KEYINFO state_transition = soon break NEED_KEYINFO_READ_PIN: if starting-up state_next = NEED_KEYINFO state_transition = soon break HAVE_KEYINFO: state_next = NEED_CSR state_transition = now break NEED_CSR: if starting-up state_next = HAVE_KEYINFO state_transition = now else if don't-have-a-full-template fill-in-template-values-based-on-defaults start-csr-generation-using-template-values state_next = GENERATING_CSR state_transition = now break GENERATING_CSR: if starting-up state_next = HAVE_KEYINFO state_transition = now else if csrgen-finished if csr-was-stored state_next = HAVE_CSR state_transition = now elseif key-store-needs-token state_next = NEED_CSR_GEN_TOKEN state_transition = now elseif key-store-needs-pin state_next = NEED_CSR_GEN_PIN state_transition = now else state_next = NEED_CSR state_transition = now else state_next = GENERATING_CSR state_transition = when-notified break NEED_CSR_GEN_TOKEN: if starting-up state_next = HAVE_KEYINFO state_transition = soon break NEED_CSR_GEN_PIN: if starting-up state_next = HAVE_KEYINFO state_transition = now break HAVE_CSR: state_next = NEED_TO_SUBMIT state_transition = now break NEED_TO_SUBMIT: if starting-up state_next = HAVE_CSR state_transition = now else start-csr-submission if csr-submission-started state_next = SUBMITTING state_transition = now else if don't-know-a-ca state_next = NEED_CA state_transition = now break SUBMITTING: if starting-up state_next = HAVE_CSR state_transition = now else if csr-submission-attempt-completed if ca-issued-cert state_next = NEED_TO_SAVE_CERT state_transition = now elseif ca-rejected-us if already-had-a-cert state_next = MONITORING state_transition = now else state_next = CA_NEED_TO_NOTIFY_REJECTION state_transition = later elseif ca-is-unreachable store-ca-cookie state_next = CA_UNREACHABLE state_transition = later elseif ca-is-thinking-about-it-and-have-cookie store-ca-cookie state_next = CA_WORKING state_transition = soon elseif ca-is-underconfigured if already-had-a-cert state_next = MONITORING state_transition = now else store-ca-cookie state_next = CA_UNCONFIGURED state_transition = later else state_next = NEED_GUIDANCE state_transition = now else state_next = SUBMITTING state_transition = when-notified break NEED_TO_SAVE_CERT: if pre-save-command-configured start-configured-pre-save-command state_next = PRE_SAVE_CERT state_transition = now else state_next = START_SAVING_CERT state_transition = now break PRE_SAVE_CERT: if starting-up state_next = NEED_TO_SAVE_CERT state_transition = now else if pre-save-completed state_next = START_SAVING_CERT state_transition = now break START_SAVING_CERT: start-saving-cert state_next = SAVING_CERT state_transition = now break SAVING_CERT: if starting-up state_next = NEED_TO_SAVE_CERT state_transition = now else if cert-save-completed state_next = NEED_TO_READ_CERT state_transition = now else if cert-save-needs-perms state_next = NEED_CERTSAVE_PERMS state_transition = now else state_next = NEED_TO_NOTIFY_ISSUED_FAILED state_transition = now break NEED_CERTSAVE_PERMS: if starting-up state_next = NEED_TO_SAVE_CERT state_transition = now break NEED_TO_READ_CERT: start-reading-cert state_next = READING_CERT state_transition = now break READING_CERT: if starting-up state_next = NEED_TO_READ_CERT state_transition = now else if cert-read-completed state_next = SAVED_CERT state_transition = now break SAVED_CERT: if post-save-command-configured start-configured-post-save-command state_next = POST_SAVED_CERT state_transition = now else state_next = NEED_TO_SAVE_CA_CERTS state_transition = now break NEED_TO_SAVE_CA_CERTS: state_next = START_SAVING_CA_CERTS state_transition = now START_SAVING_CA_CERTS: if starting-up state_next = NEED_TO_SAVE_CA_CERTS state_transition = now else start-saving-ca-certs state_next = SAVING_CA_CERTS state_transition = now break SAVING_CA_CERTS: if starting-up state_next = NEED_TO_SAVE_CA_CERTS state_transition = now else if saving-ca-certs-complete state_next = NEED_TO_NOTIFY_ISSUED_SAVED state_transition = now else if permissions-problem state_next = NEED_CA_CERT_SAVE_PERMS state_transition = now else state_next = NEED_TO_NOTIFY_ISSUED_SAVED state_transition = now NEED_TO_SAVE_ONLY_CA_CERTS: state_next = START_SAVING_ONLY_CA_CERTS state_transition = now START_SAVING_ONLY_CA_CERTS: if starting-up state_next = NEED_TO_SAVE_ONLY_CA_CERTS state_transition = now else start-saving-only-ca-certs state_next = SAVING_ONLY_CA_CERTS state_transition = now break SAVING_ONLY_CA_CERTS: if starting-up state_next = NEED_TO_SAVE_ONLY_CA_CERTS state_transition = now else if saving-ca-certs-complete state_next = MONITORING state_transition = now else if permissions-problem state_next = NEED_CA_CERT_SAVE_PERMS state_transition = now else state_next = NEED_TO_NOTIFY_ONLY_CA_SAVE_FAILED state_transition = now NEED_CA_CERT_SAVE_PERMS: if starting-up state_next = NEED_TO_SAVE_CA_CERTS state_transition = now break POST_SAVED_CERT: if starting-up state_next = SAVED_CERT state_transition = now else if post-save-completed state_next = NEED_TO_SAVE_CA_CERTS state_transition = now NEED_TO_NOTIFY_REJECTION: start-notifying state_next = NOTIFYING_REJECTION state_transition = now break NOTIFYING_REJECTION: if starting-up state_next = NEED_TO_NOTIFY_REJECTION state_transition = now else if notification-completed state_next = CA_REJECTED state_transition = now break NEED_TO_NOTIFY_ISSUED_FAILED: start-notifying state_next = NOTIFYING_ISSUED_FAILED state_transition = now break NOTIFYING_ISSUED_FAILED: if starting-up state_next = NEED_TO_NOTIFY_ISSUED_FAILED state_transition = now else if notification-completed state_next = NEED_TO_SAVE_CERT state_transition = soonish break NEED_TO_NOTIFY_ONLY_CA_SAVE_FAILED start-notifying state_next = NOTIFYING_ONLY_CA_SAVE_FAILED state_transition = now break NOTIFYING_ONLY_CA_SAVE_FAILED if starting-up state_next = NEED_TO_NOTIFY_ONLY_CA_SAVE_FAILED start-notifying state_transition = now else if notification-completed state_next = MONITORING state_transition = now break NEED_TO_NOTIFY_ISSUED_SAVED: start-notifying state_next = NOTIFYING_ISSUED_SAVED: state_transition = now break NOTIFYING_ISSUED_SAVED: if starting-up state_next = NEED_TO_NOTIFY_ISSUED_SAVED: state_transition = now else if notification-completed state_next = CA_MONITORING state_transition = now break CA_REJECTED: state_transition = soon break CA_WORKING: if starting-up state_next = HAVE_CSR state_transition = now else state_next = NEED_TO_SUBMIT state_transition = soon break CA_UNREACHABLE: if starting-up state_next = HAVE_CSR state_transition = now else state_next = NEED_TO_SUBMIT state_transition = soon break CA_UNCONFIGURED: if starting-up state_next = HAVE_CSR state_transition = now break NEED_CA: if starting-up state_next = HAVE_CSR state_transition = now break NEED_GUIDANCE: if have-guidance state_next = as-guided state_waitfor = now else state_next = NEED_GUIDANCE state_transition = timeout break MONITORING: if certificate-is-expired or (expiration-time-is-below-notify-threshold-value and expiration-time-was-above-notify-threshold-value) update-template-values-based-on-cert state_next = NEED_TO_NOTIFY_VALIDITY state_transition = now else if (expiration-time-is-below-renewal-threshold-value and expiration-time-was-above-renewal-threshold-value) state_next = NEED_CSR state_transition = now else state_next = MONITORING state_transition = timeout break NEED_TO_NOTIFY_VALIDITY: if starting-up state_next = MONITORING state_transition = now else start-notifying state_next = NOTIFYING_VALIDITY state_transition = now break NOTIFYING_VALIDITY: if starting-up state_next = NEED_TO_NOTIFY_VALIDITY state_transition = now else if notification-completed if this-cert-gets-autorenew and (expiration-time-is-below-renewal-threshold-value and expiration-time-was-above-renewal-threshold-value) state_next = NEED_CSR state_transition = now else state_next = MONITORING state_transition = timeout break NEWLY_ADDED: if key-storage-is-known state_next = NEWLY_ADDED_START_READING_KEYINFO state_transition = now else state_next = NEWLY_ADDED_START_READING_CERT state_transition = now break NEWLY_ADDED_START_READING_KEYINFO: start-reading-key-information state_next = NEWLY_ADDED_READING_KEYINFO state_transition = now break NEWLY_ADDED_READING_KEYINFO: if starting-up state_next = NEWLY_ADDED_START_READING_KEYINFO state_transition = now else if finished-reading-key-information state_next = NEWLY_ADDED_START_READING_CERT state_transition = now elseif key-store-needs-token state_next = NEWLY_ADDED_NEED_KEYINFO_READ_TOKEN state_transition = now elseif key-store-needs-pin state_next = NEWLY_ADDED_NEED_KEYINFO_READ_PIN state_transition = now else state_next = NEWLY_ADDED_START_READING_CERT state_transition = now break NEWLY_ADDED_NEED_KEYINFO_READ_TOKEN: if starting-up state_next = NEWLY_ADDED_START_READING_KEYINFO state_transition = now break NEWLY_ADDED_NEED_KEYINFO_READ_PIN: if starting-up state_next = NEWLY_ADDED_START_READING_KEYINFO state_transition = now break NEWLY_ADDED_START_READING_CERT: start-reading-cert state_next = NEWLY_ADDED_READING_CERT state_transition = now break NEWLY_ADDED_READING_CERT: if starting-up state_next = NEWLY_ADDED_START_READING_CERT state_transition = now else if finished-reading-cert state_next = NEWLY_ADDED_DECIDING state_transition = now break NEWLY_ADDED_DECIDING: if entry-has-no-associated-ca try-to-set-ca-using-known-ca-list if we-have-a-cert if we-have-ca-certs-to-save state_next = NEED_TO_SAVE_ONLY_CA_CERTS state_transition = now else state_next = MONITORING state_transition = now else if key-storage-is-known if key-is-present state_next = NEED_CSR state_transition = now else state_next = NEED_KEY_PAIR state_transition = now else state_next = NEED_GUIDANCE state_transition = now break Types of actions and user guidance: Reenroll-from-scratch ("request -g"): state_next = NEED_KEY_PAIR state_transition = now Submit-key ("request", if no csr or arguments alter it): state_next = NEED_CSR state_transition = now Submit-csr (automatic for "request") state_next = HAVE_CSR state_transition = now Resubmit-csr (need to add "resubmit") state_next = HAVE_CSR state_transition = now Start-Tracking-with-AutoRenew ("start-tracking"): add-cert-to-monitoring-list state_next = MONITORING state_transition = now Start-Tracking-without-AutoRenew ("start-tracking"): add-cert-to-monitoring-list state_next = MONITORING state_transition = now Cancel ("stop-tracking"): remove-cert-from-monitoring-list Status ("list"): dump-monitoring-list Data we need to track for each certificate/task/dbentry: * Type of key pair to generate [or use default settings] default: RSA,2048 * Location of key pair [use-once default] default: NSS,/etc/pki/nssdb,,Server-Key-default * Location of certificate [use-once default] default: NSS,/etc/pki/nssdb,,Server-Cert-default * Cached certificate issuer/serial/subject/spki/expiration/host/email * The last time we checked if expiration was imminent. * Interesting TTL values [or use default settings] default: 30*24*60*60,7*24*60*60,3*24*60*60,2*24*60*60,1*24*60*60 * How to notify administrator [or use default settings] syslog(LOG_AUTHPRIV?) or mail to root@? * CSR template information [or imported from existing certificate] * subject (cn=host name) * SANs¹ * DNS * email * principal name * ku¹, eku¹ ¹ Encoded as extensionRequest attributes. * Certificate State (state_current) * Whether to autorenew-at-expiration [or use default settings] * Whether to start monitoring at issue [or use default settings] * Type and location of CA [or use default settings] * Value of CA cookie for in-progress submissions. * Date of submission for in-progress submissions. When we interact with CAs, there are things that we "know" about them, or would like to know: * A nickname by which we refer to the CA. * The certificate which the CA uses as the issuer of certificates for end entities. * The root of the chain of trust, if it's not the CA's issuer certificate, and any other certificates in the chain between them. * A set of mandatory-to-supply pieces of information when we're requesting a new or renewed certificate from the CA. For IPA, for example, during requests for a new certificate (it doesn't distinguish between "new" and "renew" cases), this is the Kerberos principal name of the subject for whom we're requesting the certificate. * If it supports different types of enrollment (known as profiles in Dogtag, or as templates in ADCS), what the options are, and if there's a recommended default. Each of these pieces of information which we might obtain from the CA is essentially either missing, current, in the process of being fetched, or in the process of being disseminated out to the rest of the local system. We can model the states for each of these using roughly the same set of states: * Pulling data from the CA: States: NEED_TO_REFRESH,REFRESHING,UNREACHABLE * Saving the data obtained from the CA to places where it's expected: States: NEED_TO_SAVE_DATA,PRE_SAVE_DATA,START_SAVING_DATA, SAVING_DATA,POST_SAVE_DATA,SAVED_DATA * Figuring out when we'll need to re-fetch data: NEED_TO_ANALYZE, ANALYZING * Waiting until we need to re-fetch data: States: IDLE And this state machine: NEED_TO_REFRESH: start-refresh-submission if refresh-submission-started state_next = REFRESHING state_transition = now else state_transition = later break REFRESHING: if refresh-submission-attempt-completed if ca-gave-us-data state_next = NEED_TO_SAVE_DATA state_transition = now elseif ca-needs-retry state_next = NEED_TO_REFRESH state_transition = later elseif ca-is-unreachable state_next = UNREACHABLE state_transition = now else state_next = DISABLED state_transition = now else state_next = REFRESHING state_transition = when-notified break NEED_TO_SAVE_DATA: if pre-save-command-configured start-configured-pre-save-command state_next = PRE_SAVE_DATA state_transition = now else state_next = START_SAVING_DATA state_transition = now break PRE_SAVE_DATA: if pre-save-completed state_next = START_SAVING_DATA state_transition = now break START_SAVING_DATA: start-saving-data if saving-data-started state_next = SAVING_DATA state_transition = now break SAVING_DATA: if done-saving-data if post-save-command-configured start-configured-post-save-command state_next = POST_SAVE_DATA state_transition = now else state_next = SAVED_DATA state_transition = now break POST_SAVE_DATA: if post-save-completed state_next = SAVED_DATA state_transition = now break SAVED_DATA: state_next = NEED_TO_ANALYZE state_transition = now NEED_TO_ANALYZE: start-analysis if analysis-started state_next = ANALYZING state_transition = now else state_next = IDLE state_transition = now break ANALYZING: if fetched-data-will-need-to-be-refreshed: state_next = NEED_TO_REFRESH state_transition = timeout else: state_next = IDLE state_transition = now UNREACHABLE: state_next = NEED_TO_REFRESH state_transition = now