/** * Converts a PEM encoded file into its binary form (RFC 1421, RFC 934) */ static status_t pem_to_bin(chunk_t *blob, bool *pgp) { typedef enum { PEM_PRE = 0, PEM_MSG = 1, PEM_HEADER = 2, PEM_BODY = 3, PEM_POST = 4, PEM_ABORT = 5 } state_t; encryption_algorithm_t alg = ENCR_UNDEFINED; size_t key_size = 0; bool encrypted = FALSE; state_t state = PEM_PRE; chunk_t src = *blob; chunk_t dst = *blob; chunk_t line = chunk_empty; chunk_t iv = chunk_empty; u_char iv_buf[HASH_SIZE_MD5]; status_t status = NOT_FOUND; enumerator_t *enumerator; shared_key_t *shared; dst.len = 0; iv.ptr = iv_buf; iv.len = 0; while (fetchline(&src, &line)) { if (state == PEM_PRE) { if (find_boundary("BEGIN", &line)) { state = PEM_MSG; } continue; } else { if (find_boundary("END", &line)) { state = PEM_POST; break; } if (state == PEM_MSG) { state = PEM_HEADER; if (memchr(line.ptr, ':', line.len) == NULL) { state = PEM_BODY; } } if (state == PEM_HEADER) { err_t ugh = NULL; chunk_t name = chunk_empty; chunk_t value = chunk_empty; /* an empty line separates HEADER and BODY */ if (line.len == 0) { state = PEM_BODY; continue; } /* we are looking for a parameter: value pair */ DBG2(DBG_ASN, " %.*s", (int)line.len, line.ptr); ugh = extract_parameter_value(&name, &value, &line); if (ugh != NULL) { continue; } if (match("Proc-Type", &name) && *value.ptr == '4') { encrypted = TRUE; } else if (match("DEK-Info", &name)) { chunk_t dek; if (!extract_token(&dek, ',', &value)) { dek = value; } if (match("DES-EDE3-CBC", &dek)) { alg = ENCR_3DES; key_size = 24; } else if (match("AES-128-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 16; } else if (match("AES-192-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 24; } else if (match("AES-256-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 32; } else { DBG1(DBG_ASN, " encryption algorithm '%.*s'" " not supported", (int)dek.len, dek.ptr); return NOT_SUPPORTED; } if (!eat_whitespace(&value) || value.len > 2*sizeof(iv_buf)) { return PARSE_ERROR; } iv = chunk_from_hex(value, iv_buf); } } else /* state is PEM_BODY */ { chunk_t data; /* remove any trailing whitespace */ if (!extract_token(&data ,' ', &line)) { data = line; } /* check for PGP armor checksum */ if (*data.ptr == '=') { *pgp = TRUE; data.ptr++; data.len--; DBG2(DBG_ASN, " armor checksum: %.*s", (int)data.len, data.ptr); continue; } if (blob->len - dst.len < data.len / 4 * 3) { state = PEM_ABORT; } data = chunk_from_base64(data, dst.ptr); dst.ptr += data.len; dst.len += data.len; } } } /* set length to size of binary blob */ blob->len = dst.len; if (state != PEM_POST) { DBG1(DBG_LIB, " file coded in unknown format, discarded"); return PARSE_ERROR; } if (!encrypted) { return SUCCESS; } enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, SHARED_PRIVATE_KEY_PASS, NULL, NULL); while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) { chunk_t passphrase, chunk; passphrase = shared->get_key(shared); chunk = chunk_clone(*blob); status = pem_decrypt(&chunk, alg, key_size, iv, passphrase); if (status == SUCCESS) { memcpy(blob->ptr, chunk.ptr, chunk.len); blob->len = chunk.len; } free(chunk.ptr); if (status != INVALID_ARG) { /* try again only if passphrase invalid */ break; } } enumerator->destroy(enumerator); return status; }
/* Converts a PEM encoded file into its binary form * * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993 * RFC 934 Message Encapsulation, January 1985 */ err_t pem_to_bin(chunk_t *blob, chunk_t *passphrase, bool *pgp) { typedef enum { PEM_PRE = 0, PEM_MSG = 1, PEM_HEADER = 2, PEM_BODY = 3, PEM_POST = 4, PEM_ABORT = 5 } state_t; encryption_algorithm_t alg = ENCR_UNDEFINED; size_t key_size = 0; bool encrypted = FALSE; state_t state = PEM_PRE; chunk_t src = *blob; chunk_t dst = *blob; chunk_t line = CHUNK_INITIALIZER; chunk_t iv = CHUNK_INITIALIZER; u_char iv_buf[16]; /* MD5 digest size */ /* zero size of converted blob */ dst.len = 0; /* zero size of IV */ iv.ptr = iv_buf; iv.len = 0; pem_init_logger(); while (fetchline(&src, &line)) { if (state == PEM_PRE) { if (find_boundary("BEGIN", &line)) { state = PEM_MSG; } continue; } else { if (find_boundary("END", &line)) { state = PEM_POST; break; } if (state == PEM_MSG) { state = (memchr(line.ptr, ':', line.len) == NULL) ? PEM_BODY : PEM_HEADER; } if (state == PEM_HEADER) { err_t ugh = NULL; chunk_t name = CHUNK_INITIALIZER; chunk_t value = CHUNK_INITIALIZER; /* an empty line separates HEADER and BODY */ if (line.len == 0) { state = PEM_BODY; continue; } /* we are looking for a parameter: value pair */ logger->log(logger, CONTROL|LEVEL2, " %.*s", (int)line.len, line.ptr); ugh = extract_parameter_value(&name, &value, &line); if (ugh != NULL) continue; if (match("Proc-Type", &name) && *value.ptr == '4') encrypted = TRUE; else if (match("DEK-Info", &name)) { size_t len = 0; chunk_t dek; if (!extract_token(&dek, ',', &value)) dek = value; /* we support DES-EDE3-CBC encrypted files, only */ if (match("DES-EDE3-CBC", &dek)) { alg = ENCR_3DES; key_size = 24; } else if (match("AES-128-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 16; } else if (match("AES-192-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 24; } else if (match("AES-256-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 32; } else { return "encryption algorithm not supported"; } eat_whitespace(&value); ugh = ttodata(value.ptr, value.len, 16, iv.ptr, 16, &len); if (ugh) return "error in IV"; iv.len = len; } } else /* state is PEM_BODY */ { const char *ugh = NULL; size_t len = 0; chunk_t data; /* remove any trailing whitespace */ if (!extract_token(&data ,' ', &line)) { data = line; } /* check for PGP armor checksum */ if (*data.ptr == '=') { *pgp = TRUE; data.ptr++; data.len--; logger->log(logger, CONTROL|LEVEL2, " Armor checksum: %.*s", (int)data.len, data.ptr); continue; } ugh = ttodata(data.ptr, data.len, 64, dst.ptr, blob->len - dst.len, &len); if (ugh) { state = PEM_ABORT; break; } else { dst.ptr += len; dst.len += len; } } } } /* set length to size of binary blob */ blob->len = dst.len; if (state != PEM_POST) return "file coded in unknown format, discarded"; return (encrypted)? pem_decrypt(blob, alg, key_size, &iv, passphrase) : NULL; }
/** * Converts a PEM encoded file into its binary form (RFC 1421, RFC 934) */ static status_t pem_to_bin(chunk_t *blob, chunk_t(*cb)(void*,int), void *cb_data, bool *pgp) { typedef enum { PEM_PRE = 0, PEM_MSG = 1, PEM_HEADER = 2, PEM_BODY = 3, PEM_POST = 4, PEM_ABORT = 5 } state_t; encryption_algorithm_t alg = ENCR_UNDEFINED; size_t key_size = 0; bool encrypted = FALSE; state_t state = PEM_PRE; chunk_t src = *blob; chunk_t dst = *blob; chunk_t line = chunk_empty; chunk_t iv = chunk_empty; chunk_t passphrase; int try = 0; u_char iv_buf[HASH_SIZE_MD5]; dst.len = 0; iv.ptr = iv_buf; iv.len = 0; while (fetchline(&src, &line)) { if (state == PEM_PRE) { if (find_boundary("BEGIN", &line)) { state = PEM_MSG; } continue; } else { if (find_boundary("END", &line)) { state = PEM_POST; break; } if (state == PEM_MSG) { state = PEM_HEADER; if (memchr(line.ptr, ':', line.len) == NULL) { state = PEM_BODY; } } if (state == PEM_HEADER) { err_t ugh = NULL; chunk_t name = chunk_empty; chunk_t value = chunk_empty; /* an empty line separates HEADER and BODY */ if (line.len == 0) { state = PEM_BODY; continue; } /* we are looking for a parameter: value pair */ DBG2(DBG_LIB, " %.*s", (int)line.len, line.ptr); ugh = extract_parameter_value(&name, &value, &line); if (ugh != NULL) { continue; } if (match("Proc-Type", &name) && *value.ptr == '4') { encrypted = TRUE; } else if (match("DEK-Info", &name)) { chunk_t dek; if (!extract_token(&dek, ',', &value)) { dek = value; } if (match("DES-EDE3-CBC", &dek)) { alg = ENCR_3DES; key_size = 24; } else if (match("AES-128-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 16; } else if (match("AES-192-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 24; } else if (match("AES-256-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 32; } else { DBG1(DBG_LIB, " encryption algorithm '%.*s'" " not supported", dek.len, dek.ptr); return NOT_SUPPORTED; } eat_whitespace(&value); iv = chunk_from_hex(value, iv.ptr); } } else /* state is PEM_BODY */ { chunk_t data; /* remove any trailing whitespace */ if (!extract_token(&data ,' ', &line)) { data = line; } /* check for PGP armor checksum */ if (*data.ptr == '=') { *pgp = TRUE; data.ptr++; data.len--; DBG2(DBG_LIB, " armor checksum: %.*s", (int)data.len, data.ptr); continue; } if (blob->len - dst.len < data.len / 4 * 3) { state = PEM_ABORT; } data = chunk_from_base64(data, dst.ptr); dst.ptr += data.len; dst.len += data.len; } } } /* set length to size of binary blob */ blob->len = dst.len; if (state != PEM_POST) { DBG1(DBG_LIB, " file coded in unknown format, discarded"); return PARSE_ERROR; } if (!encrypted) { return SUCCESS; } if (!cb) { DBG1(DBG_LIB, " missing passphrase"); return INVALID_ARG; } while (TRUE) { passphrase = cb(cb_data, ++try); if (!passphrase.len || !passphrase.ptr) { return INVALID_ARG; } switch (pem_decrypt(blob, alg, key_size, iv, passphrase)) { case INVALID_ARG: /* bad passphrase, retry */ continue; case SUCCESS: return SUCCESS; default: return FAILED; } } }