123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384 |
- # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- module SecureMailing::PGP::Tool::Parse
- extend ActiveSupport::Concern
- include SecureMailing::PGP::Tool::Exec
- PGP_KEY_INFO = Struct.new(:fingerprint, :uids, :created_at, :expires_at, :secret)
- PGP_KEY_INFO_EXPIRES_AT_TIMESTAMP = 6
- PGP_KEY_INFO_CREATED_AT_TIMESTAMP = 5
- PGP_KEY_INFO_UID = 9
- PGP_KEY_INFO_UID_VALIDITY = 1
- PGP_KEY_INFO_UID_INVALID_STATE = %w[i d r n].freeze
- included do # rubocop:disable Metrics/BlockLength
- def info(key)
- result = gpg('show-key', options: %w[--with-colons], stdin: key)
- parse_info(result.stdout)
- end
- private
- def parse_info(data)
- # https://github.com/gpg/gnupg/blob/master/doc/DETAILS
- info = {
- fingerprint: nil,
- uids: [],
- created_at: nil,
- expires_at: nil,
- secret: false
- }
- data.split("\n").tap do |chunks|
- # We assume all relevant subkeys [SCE] have the same expiration date.
- dates = chunks.find { |chunk| chunk.start_with?(%r{pub|sec}) }
- info[:expires_at] = expires_at(dates)
- info[:created_at] = created_at(dates)
- info[:secret] = secret?(chunks)
- fpr = chunks.find { |chunk| chunk.start_with?('fpr') }
- info[:fingerprint] = fingerprint(fpr)
- uids = chunks.select { |chunk| chunk.start_with?('uid') }
- uids = uids.map { |uid| uid(uid) }
- info[:uids] = uids.compact
- end
- PGP_KEY_INFO.new(*info.values)
- end
- def created_at(chunk)
- timestamp = chunk.split(':').fetch(PGP_KEY_INFO_CREATED_AT_TIMESTAMP)
- return nil if timestamp == '0'
- Time.zone.at(timestamp.to_i)
- end
- def expires_at(chunk)
- timestamp = chunk.split(':').fetch(PGP_KEY_INFO_EXPIRES_AT_TIMESTAMP)
- return nil if timestamp.blank? || timestamp == '0'
- Time.zone.at(timestamp.to_i)
- end
- def fingerprint(chunk)
- chunk.split(':').last
- end
- def uid(chunk)
- hunks = chunk.split(':')
- return nil if PGP_KEY_INFO_UID_INVALID_STATE.include?(hunks.fetch(PGP_KEY_INFO_UID_VALIDITY))
- hunks.fetch(PGP_KEY_INFO_UID)
- end
- def secret?(chunks)
- chunks.any? { |chunk| chunk.start_with?('sec') }
- end
- end
- end
|