Skip to main content

codec_pem/
encode.rs

1// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use base64::{engine::general_purpose::STANDARD, Engine as _};
6use zeroize::Zeroizing;
7
8use crate::{PemEncodeOptions, PemError, PemLabel};
9
10/// Encode DER bytes as PEM text armor.
11pub fn encode_pem(
12    label: PemLabel,
13    der: &[u8],
14    options: PemEncodeOptions,
15) -> Result<Zeroizing<String>, PemError> {
16    if der.is_empty() || der.len() > options.max_der_len {
17        return Err(PemError::DerTooLarge);
18    }
19    if options.line_width == 0 || options.line_width > 76 {
20        return Err(PemError::InvalidOptions);
21    }
22
23    let newline = options.line_ending.as_str();
24    let encoded = Zeroizing::new(STANDARD.encode(der));
25    let mut output = Zeroizing::new(String::new());
26    output.push_str("-----BEGIN ");
27    output.push_str(label.as_str());
28    output.push_str("-----");
29    output.push_str(newline);
30
31    for chunk in encoded.as_bytes().chunks(options.line_width) {
32        let line = core::str::from_utf8(chunk).map_err(|_| PemError::InvalidBase64)?;
33        output.push_str(line);
34        output.push_str(newline);
35    }
36
37    output.push_str("-----END ");
38    output.push_str(label.as_str());
39    output.push_str("-----");
40    output.push_str(newline);
41
42    Ok(output)
43}