Module: Confium::OpenPGP

Defined in:
lib/confium/openpgp.rb

Overview

OpenPGP ASCII armor (RFC 9580 §6) — Radix-64 framing with a CRC-24 checksum, implemented in pure Ruby.

This replaced the earlier native wrapper around librnp, which pulled a full vendored Botan/json-c C/C++ build into every install just to do armor framing. The wire format is unchanged; spec/fixtures/openpgp_armor_vectors.json holds differential vectors captured from the native implementation.

Constant Summary collapse

MESSAGE =
'message'
PUBLIC_KEY =
'public key'
SECRET_KEY =
'secret key'
SIGNATURE =
'signature'
CLEARTEXT =
'cleartext signed message'

Class Method Summary collapse

Class Method Details

.armor(data, type = MESSAGE) ⇒ String

ASCII-armor encode raw bytes. Output uses CRLF line endings and 76-character data lines, byte-for-byte matching the earlier native (rnp) implementation.

Parameters:

  • data (String)

    Binary data to encode.

  • type (String) (defaults to: MESSAGE)

    One of MESSAGE, PUBLIC_KEY, SECRET_KEY, SIGNATURE, CLEARTEXT (armored as a plain message). Defaults to MESSAGE.

Returns:

  • (String)

    Armored ASCII string.

Raises:

  • (ArgumentError)

    if type is not a known armor type.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/confium/openpgp.rb', line 57

def armor(data, type = MESSAGE)
  label = LABELS[type]
  raise ArgumentError, "unknown armor type: #{type.inspect}" unless label

  bytes = data.to_s.b
  b64 = [bytes].pack('m0')
  lines = b64.scan(/.{1,76}/)
  <<~ARMOR.gsub("\n", "\r\n")
    -----BEGIN PGP #{label}-----

    #{lines.join("\n")}
    =#{crc24_armor(bytes)}
    -----END PGP #{label}-----
  ARMOR
end

.dearmor(data) ⇒ String

Decode ASCII-armored data to raw bytes. Accepts LF or CRLF line endings, arbitrary line widths, and Armor Headers (Comment:, Version:, ...) between the BEGIN line and the blank line. The CRC-24 checksum line is verified when present.

Parameters:

  • data (String)

    Armored ASCII string.

Returns:

  • (String)

    Raw binary data (ASCII-8BIT).

Raises:



83
84
85
86
87
88
89
90
91
92
# File 'lib/confium/openpgp.rb', line 83

def dearmor(data)
  b64, crc_line = extract_body(data.to_s)
  # @type var bytes: String
  bytes = b64.unpack1('m0')
  return ''.b if crc_line == crc24_armor(''.b) && b64.empty?

  raise ParseError, 'armor CRC-24 checksum mismatch' if crc_line && crc24_armor(bytes) != crc_line.to_s

  bytes
end