mirror of
https://github.com/zammad/zammad
synced 2026-05-24 09:48:36 +00:00
84 lines
2.2 KiB
Ruby
84 lines
2.2 KiB
Ruby
# Copyright (C) 2012-2026 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
|