PEM.cpp 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /*
  2. * Copyright (c) 2021, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Base64.h>
  7. #include <AK/GenericLexer.h>
  8. #include <LibCrypto/ASN1/PEM.h>
  9. namespace Crypto {
  10. ByteBuffer decode_pem(ReadonlyBytes data)
  11. {
  12. GenericLexer lexer { data };
  13. ByteBuffer decoded;
  14. // FIXME: Parse multiple.
  15. enum {
  16. PreStartData,
  17. Started,
  18. Ended,
  19. } state { PreStartData };
  20. while (!lexer.is_eof()) {
  21. switch (state) {
  22. case PreStartData:
  23. if (lexer.consume_specific("-----BEGIN"sv))
  24. state = Started;
  25. lexer.consume_line();
  26. break;
  27. case Started: {
  28. if (lexer.consume_specific("-----END"sv)) {
  29. state = Ended;
  30. lexer.consume_line();
  31. break;
  32. }
  33. auto b64decoded = decode_base64(lexer.consume_line().trim_whitespace(TrimMode::Right));
  34. if (b64decoded.is_error()) {
  35. dbgln("Failed to decode PEM: {}", b64decoded.error().string_literal());
  36. return {};
  37. }
  38. if (decoded.try_append(b64decoded.value().data(), b64decoded.value().size()).is_error()) {
  39. dbgln("Failed to decode PEM, likely OOM condition");
  40. return {};
  41. }
  42. break;
  43. }
  44. case Ended:
  45. lexer.consume_all();
  46. break;
  47. default:
  48. VERIFY_NOT_REACHED();
  49. }
  50. }
  51. return decoded;
  52. }
  53. ErrorOr<Vector<ByteBuffer>> decode_pems(ReadonlyBytes data)
  54. {
  55. GenericLexer lexer { data };
  56. ByteBuffer decoded;
  57. Vector<ByteBuffer> pems;
  58. enum {
  59. Junk,
  60. Parsing,
  61. } state { Junk };
  62. while (!lexer.is_eof()) {
  63. switch (state) {
  64. case Junk:
  65. if (lexer.consume_specific("-----BEGIN"sv))
  66. state = Parsing;
  67. lexer.consume_line();
  68. break;
  69. case Parsing: {
  70. if (lexer.consume_specific("-----END"sv)) {
  71. state = Junk;
  72. lexer.consume_line();
  73. TRY(pems.try_append(decoded));
  74. decoded.clear();
  75. break;
  76. }
  77. auto b64decoded = TRY(decode_base64(lexer.consume_line().trim_whitespace(TrimMode::Right)));
  78. TRY(decoded.try_append(b64decoded.data(), b64decoded.size()));
  79. break;
  80. }
  81. default:
  82. VERIFY_NOT_REACHED();
  83. }
  84. }
  85. return pems;
  86. }
  87. ErrorOr<ByteBuffer> encode_pem(ReadonlyBytes data, PEMType type)
  88. {
  89. ByteBuffer encoded;
  90. StringView block_start;
  91. StringView block_end;
  92. switch (type) {
  93. case Certificate:
  94. block_start = "-----BEGIN CERTIFICATE-----\n"sv;
  95. block_end = "-----END CERTIFICATE-----\n"sv;
  96. break;
  97. case PrivateKey:
  98. block_start = "-----BEGIN PRIVATE KEY-----\n"sv;
  99. block_end = "-----END PRIVATE KEY-----\n"sv;
  100. break;
  101. default:
  102. VERIFY_NOT_REACHED();
  103. }
  104. auto b64encoded = TRY(encode_base64(data));
  105. TRY(encoded.try_append(block_start.bytes()));
  106. size_t to_read = 64;
  107. for (size_t i = 0; i < b64encoded.bytes().size(); i += to_read) {
  108. if (i + to_read > b64encoded.bytes().size())
  109. to_read = b64encoded.bytes().size() - i;
  110. TRY(encoded.try_append(b64encoded.bytes().slice(i, to_read)));
  111. TRY(encoded.try_append("\n"sv.bytes()));
  112. }
  113. TRY(encoded.try_append(block_end.bytes()));
  114. return encoded;
  115. }
  116. }