/* * LITLS -- EC Private Key PEM Encoding * Encodes raw P-256 key material to SEC1 ECPrivateKey PEM format. */ #include "litls.h" #include /* * SEC1 ECPrivateKey DER structure: * * SEQUENCE { * INTEGER 1, -- version * OCTET STRING (32B private key), -- privateKey * [0] EXPLICIT OID 1.2.840.10045.3.1.7, -- parameters (P-256) * [1] EXPLICIT BIT STRING (65B uncompressed pub) -- publicKey * } */ /* OID 1.2.840.10045.3.1.7 (prime256v1 / secp256r1) */ static const uint8_t OID_P256[] = {0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07}; int litls_ec_privkey_to_pem(const uint8_t priv[32], const uint8_t pub[65], char *pem_out, size_t pem_cap, size_t *pem_len) { uint8_t der[256]; size_t pos = 0; /* We'll build the inner content first, then wrap with outer SEQUENCE */ /* Version: INTEGER 1 */ uint8_t version[] = {0x02, 0x01, 0x01}; /* Private key: OCTET STRING (32 bytes) */ uint8_t privkey_hdr[] = {0x04, 0x20}; /* tag + length 32 */ /* Parameters: [0] EXPLICIT { OID } */ /* a0 + length + OID_P256 */ uint8_t params_hdr[] = {0xA0, (uint8_t)(sizeof(OID_P256))}; /* Public key: [1] EXPLICIT { BIT STRING { 0x00 unused bits, pub } } */ /* Bit string: 03 + length(1+65=66) + 0x00 + pub */ uint8_t pubkey_bitstr_hdr[] = {0x03, 0x42, 0x00}; /* tag, length 66, 0 unused bits */ uint8_t pubkey_ctx_hdr[] = {0xA1, 0x44}; /* [1] EXPLICIT, length 68 */ /* Calculate inner length */ size_t inner_len = sizeof(version) + sizeof(privkey_hdr) + 32 + sizeof(params_hdr) + sizeof(OID_P256) + sizeof(pubkey_ctx_hdr) + sizeof(pubkey_bitstr_hdr) + 65; /* Outer SEQUENCE header */ if (inner_len > 127) { der[pos++] = 0x30; der[pos++] = 0x81; der[pos++] = (uint8_t)inner_len; } else { der[pos++] = 0x30; der[pos++] = (uint8_t)inner_len; } /* Version */ memcpy(der + pos, version, sizeof(version)); pos += sizeof(version); /* Private key */ memcpy(der + pos, privkey_hdr, sizeof(privkey_hdr)); pos += sizeof(privkey_hdr); memcpy(der + pos, priv, 32); pos += 32; /* Parameters */ memcpy(der + pos, params_hdr, sizeof(params_hdr)); pos += sizeof(params_hdr); memcpy(der + pos, OID_P256, sizeof(OID_P256)); pos += sizeof(OID_P256); /* Public key */ memcpy(der + pos, pubkey_ctx_hdr, sizeof(pubkey_ctx_hdr)); pos += sizeof(pubkey_ctx_hdr); memcpy(der + pos, pubkey_bitstr_hdr, sizeof(pubkey_bitstr_hdr)); pos += sizeof(pubkey_bitstr_hdr); memcpy(der + pos, pub, 65); pos += 65; /* PEM encode */ static const char hdr[] = "-----BEGIN EC PRIVATE KEY-----\n"; static const char ftr[] = "-----END EC PRIVATE KEY-----\n"; /* Base64 encode the DER */ size_t b64_cap = ((pos + 2) / 3) * 4 + 1; char b64_buf[b64_cap]; size_t b64_len = litls_base64_encode(der, pos, b64_buf, b64_cap); /* Calculate total PEM size: header + base64 with line breaks + footer */ /* Insert newlines every 64 chars */ size_t num_lines = (b64_len + 63) / 64; size_t total_size = sizeof(hdr) - 1 + b64_len + num_lines + sizeof(ftr) - 1; if (pem_cap < total_size + 1) return -1; size_t out_pos = 0; memcpy(pem_out + out_pos, hdr, sizeof(hdr) - 1); out_pos += sizeof(hdr) - 1; /* Write base64 with line breaks every 64 chars */ size_t b64_pos = 0; while (b64_pos < b64_len) { size_t chunk = b64_len - b64_pos; if (chunk > 64) chunk = 64; memcpy(pem_out + out_pos, b64_buf + b64_pos, chunk); out_pos += chunk; pem_out[out_pos++] = '\n'; b64_pos += chunk; } memcpy(pem_out + out_pos, ftr, sizeof(ftr) - 1); out_pos += sizeof(ftr) - 1; pem_out[out_pos] = '\0'; *pem_len = out_pos; return 0; }