ESPHome 2026.8.0b4
Loading...
Searching...
No Matches
ota_rsa_der.h
Go to the documentation of this file.
1#pragma once
2
3#include <cstddef>
4#include <cstdint>
5#include <cstring>
6
7namespace esphome::ota {
8
9// The PSA Crypto API imports an RSA public key as a DER RSAPublicKey
10// (RFC 3279 2.3.1), not as raw bignums:
11//
12// RSAPublicKey ::= SEQUENCE { modulus INTEGER, publicExponent INTEGER }
13//
14// The Secure Boot v2 signature block stores the modulus and exponent raw, so
15// they are wrapped here. Only RSA-3072 exists in that format, which fixes both
16// headers: a 3072-bit modulus always has its top bit set, so its INTEGER is
17// always tag + 2-byte length (0x181 = 385) + the sign pad; and the SEQUENCE
18// body is always 392..396 bytes, so its header is always tag + 2-byte length.
19// Only the exponent varies in width.
20constexpr size_t RSA_3072_MODULUS_BYTES = 384;
21constexpr uint8_t RSA_DER_MODULUS_PREFIX[] = {0x02, 0x82, 0x01, 0x81, 0x00};
23// 4-byte SEQUENCE header + modulus + the widest exponent INTEGER (tag, length,
24// sign pad, 4 bytes).
25constexpr size_t RSA_DER_PUBKEY_MAX = 4 + RSA_DER_MODULUS_LEN + 7;
26
33inline size_t rsa_der_public_key(const uint8_t *modulus_be, const uint8_t *exponent_be, size_t exponent_len,
34 uint8_t *out, size_t out_len) {
35 // A DER INTEGER is signed: drop leading zero bytes, then prepend one back if
36 // the value would otherwise read as negative.
37 while (exponent_len > 0 && exponent_be[0] == 0x00) {
38 exponent_be++;
39 exponent_len--;
40 }
41 if (exponent_len == 0) {
42 return 0; // a zero exponent is not a usable key
43 }
44 const bool pad = (exponent_be[0] & 0x80) != 0;
45 const size_t exponent_content_len = exponent_len + (pad ? 1 : 0);
46 if (exponent_content_len > 0x7F) {
47 return 0; // would need a long-form length, which this encoder does not write
48 }
49 const size_t exponent_der_len = 2 + exponent_content_len;
50 const size_t body_len = RSA_DER_MODULUS_LEN + exponent_der_len;
51 const size_t total_len = 4 + body_len;
52 if (total_len > out_len) {
53 return 0;
54 }
55
56 size_t i = 0;
57 out[i++] = 0x30; // SEQUENCE
58 out[i++] = 0x82; // 2-byte length follows
59 out[i++] = static_cast<uint8_t>(body_len >> 8);
60 out[i++] = static_cast<uint8_t>(body_len);
61 memcpy(out + i, RSA_DER_MODULUS_PREFIX, sizeof(RSA_DER_MODULUS_PREFIX));
62 i += sizeof(RSA_DER_MODULUS_PREFIX);
63 memcpy(out + i, modulus_be, RSA_3072_MODULUS_BYTES);
65 out[i++] = 0x02; // INTEGER
66 out[i++] = static_cast<uint8_t>(exponent_content_len);
67 if (pad) {
68 out[i++] = 0x00;
69 }
70 memcpy(out + i, exponent_be, exponent_len);
71 return total_len;
72}
73
74} // namespace esphome::ota
constexpr size_t RSA_3072_MODULUS_BYTES
Definition ota_rsa_der.h:20
size_t rsa_der_public_key(const uint8_t *modulus_be, const uint8_t *exponent_be, size_t exponent_len, uint8_t *out, size_t out_len)
Wrap a raw RSA-3072 modulus and exponent as a DER RSAPublicKey.
Definition ota_rsa_der.h:33
constexpr size_t RSA_DER_PUBKEY_MAX
Definition ota_rsa_der.h:25
constexpr uint8_t RSA_DER_MODULUS_PREFIX[]
Definition ota_rsa_der.h:21
constexpr size_t RSA_DER_MODULUS_LEN
Definition ota_rsa_der.h:22
uint8_t pad