Module: NetPGP

Defined in:
lib/netpgp/highlevel/utils.rb,
lib/netpgp/highlevel/keyring.rb,
lib/netpgp/highlevel/constants.rb,
lib/netpgp/highlevel/publickey.rb,
lib/netpgp/highlevel/secretkey.rb

Defined Under Namespace

Classes: HashAlgorithm, Keyring, PublicKey, PublicKeyAlgorithm, SecretKey, StringToKeySpecifier, StringToKeyUsage, SymmetricKeyAlgorithm

Constant Summary collapse

PARSE_KEYRING =
Proc.new do |state, passphrase_provider, pkt, data|
  next :PGP_RELEASE_MEMORY if state[:errors].any?

  begin
    lastkey = state[:keys].last
    case pkt[:tag]
    when :PGP_PTAG_CT_PUBLIC_KEY
      key = PublicKey::from_native(pkt[:u][:pubkey])
      state[:keys].push(key)
    when :PGP_PTAG_CT_PUBLIC_SUBKEY
      key = PublicKey::from_native(pkt[:u][:pubkey])
      lastkey.add_subkey(key)
      state[:keys].push(key)
    when :PGP_PTAG_CT_ENCRYPTED_SECRET_KEY
      key = SecretKey::from_native(pkt[:u][:seckey], true)
      state[:keys].push(key)
    when :PGP_PTAG_CT_ENCRYPTED_SECRET_SUBKEY
      key = SecretKey::from_native(pkt[:u][:seckey], true)
      lastkey.add_subkey(key)
      state[:keys].push(key)
    when :PGP_PTAG_CT_SECRET_KEY
      key = SecretKey::from_native(pkt[:u][:seckey])
      if state[:passphrase]
        key.passphrase = state[:passphrase]
        state[:passphrase] = nil
      end
      state[:keys].push(key)
    when :PGP_PTAG_CT_SECRET_SUBKEY
      key = SecretKey::from_native(pkt[:u][:seckey])
      lastkey.add_subkey(key)
      state[:keys].push(key)
    when :PGP_GET_PASSPHRASE
      seckey_ptr = pkt[:u][:skey_passphrase][:seckey]
      seckey = LibNetPGP::PGPSecKey.new(seckey_ptr)
      key = SecretKey::from_native(seckey)
      passphrase = passphrase_provider.call(key)
      if passphrase and passphrase != ''
        passphrase_mem = LibC::calloc(1, passphrase.bytesize + 1)
        passphrase_mem.write_bytes(passphrase)
        pkt[:u][:skey_passphrase][:passphrase].write_pointer(passphrase_mem)
        state[:passphrase] = passphrase
        next :PGP_KEEP_MEMORY
      end
    when :PGP_PARSER_PACKET_END
      if lastkey.is_a? NetPGP::SecretKey
        raw_packet = pkt[:u][:packet]
        bytes = raw_packet[:raw].read_bytes(raw_packet[:length])
        lastkey.raw_subpackets.push(bytes)
      end
    when :PGP_PTAG_CT_USER_ID
      lastkey.userids.push(pkt[:u][:userid].force_encoding('utf-8'))
    when :PGP_PTAG_SS_KEY_EXPIRY
      lastkey.expiration_time = lastkey.creation_time + pkt[:u][:ss_time]
    else
      # For debugging
      #puts "Unhandled tag: #{pkt[:tag]}"
    end # case
  rescue
    state[:errors].push($ERROR_INFO)
  end
  next :PGP_RELEASE_MEMORY
end
DEFAULT_PASSPHRASE_PROVIDER =
Proc.new do |seckey|
  nil
end

Class Method Summary collapse

Class Method Details

.add_subkey_signature(key, subkey) ⇒ Object

Add a subkey binding signature (type 0x18) to a key. Note that this should be used for encryption subkeys.

Parameters:



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/netpgp/highlevel/utils.rb', line 90

def self.add_subkey_signature(key, subkey)
  sig = nil
  sigoutput = nil
  mem_sig = nil
  begin
    sig = LibNetPGP::pgp_create_sig_new
    LibNetPGP::pgp_sig_start_subkey_sig(sig, key[:key][:pubkey], subkey[:key][:pubkey], :PGP_SIG_SUBKEY)
    LibNetPGP::pgp_add_time(sig, subkey[:key][:pubkey][:birthtime], 'birth')
    # TODO expiration
    LibNetPGP::pgp_add_issuer_keyid(sig, key[:sigid])
    LibNetPGP::pgp_end_hashed_subpkts(sig)

    sigoutput_ptr = FFI::MemoryPointer.new(:pointer)
    mem_sig_ptr = FFI::MemoryPointer.new(:pointer)
    LibNetPGP::pgp_setup_memory_write(sigoutput_ptr, mem_sig_ptr, 128)
    sigoutput = LibNetPGP::PGPOutput.new(sigoutput_ptr.read_pointer)
    LibNetPGP::pgp_write_sig(sigoutput, sig, key[:key][:pubkey], key[:key][:seckey])
    mem_sig = LibNetPGP::PGPMemory.new(mem_sig_ptr.read_pointer)
    sigpkt = LibNetPGP::PGPSubPacket.new
    sigpkt[:length] = LibNetPGP::pgp_mem_len(mem_sig)
    sigpkt[:raw] = LibNetPGP::pgp_mem_data(mem_sig)
    LibNetPGP::pgp_add_subpacket(subkey, sigpkt)
  ensure
    LibNetPGP::pgp_create_sig_delete(sig) if sig
    LibNetPGP::pgp_teardown_memory_write(sigoutput, mem_sig) if mem_sig
  end
end

.bignum_byte_count(bn) ⇒ Object



3
4
5
6
7
8
# File 'lib/netpgp/highlevel/utils.rb', line 3

def self.bignum_byte_count(bn)
  # Note: This probably assumes that the ruby implementation
  # uses the same BN representation that libnetpgp does.
  # It may be better to convert and use BN_num_bytes (or bits).
  bn.to_s(16).length / 2
end

.keys_to_native_keyring(keys, native) ⇒ Object



222
223
224
225
226
227
228
229
230
# File 'lib/netpgp/highlevel/keyring.rb', line 222

def self.keys_to_native_keyring(keys, native)
  raise if not native[:keys].null?

  for key in keys
    native_key = LibNetPGP::PGPKey.new
    key.to_native_key(native_key)
    LibNetPGP::dynarray_append_item(native, 'key', LibNetPGP::PGPKey, native_key)
  end
end

.load_keys(data, armored = true, &passphrase_provider) ⇒ Object



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/netpgp/highlevel/keyring.rb', line 191

def self.load_keys(data, armored=true, &passphrase_provider)
  # Just for readability
  print_errors = 0
  stream_mem = LibC::calloc(1, LibNetPGP::PGPStream.size)
  # This will free the above memory (PGPStream is a ManagedStruct)
  stream = LibNetPGP::PGPStream.new(stream_mem)
  stream[:readinfo][:accumulate] = 1
  LibNetPGP::pgp_parse_options(stream, :PGP_PTAG_SS_ALL, :PGP_PARSE_PARSED)

  # This memory will be GC'd
  mem = FFI::MemoryPointer.new(:uint8, data.bytesize)
  mem.write_bytes(data)

  LibNetPGP::pgp_reader_set_memory(stream, mem, mem.size)
  state = {keys: [], errors: []}
  provider = block_given? ? passphrase_provider : DEFAULT_PASSPHRASE_PROVIDER
  callback = NetPGP::PARSE_KEYRING.curry[state][provider]
  LibNetPGP::pgp_set_callback(stream, callback, nil)
  LibNetPGP::pgp_reader_push_dearmour(stream) if armored
  if LibNetPGP::pgp_parse(stream, print_errors) != 1
    state[:errors].push('pgp_parse failed')
  end
  LibNetPGP::pgp_reader_pop_dearmour(stream) if armored

  errors = stream_errors(stream)
  state[:errors].push(errors) if errors.any?

  raise state[:errors].join("\n") if state[:errors].any?
  state[:keys]
end

.mpi_from_native(native) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
# File 'lib/netpgp/highlevel/utils.rb', line 25

def self.mpi_from_native(native)
  mpi = {}
  native.members.each {|member|
    if native[member].null?
      mpi[member] = nil
    else
      mpi[member] = LibNetPGP::bn2hex(native[member]).hex
    end
  }
  mpi
end

.mpi_to_native(mpi, native) ⇒ Object



51
52
53
54
55
56
57
58
59
# File 'lib/netpgp/highlevel/utils.rb', line 51

def self.mpi_to_native(mpi, native)
  mpi.each {|name,value|
    if mpi[name] == nil
      native[name] = nil
    else
      native[name] = LibNetPGP::num2bn(value)
    end
  }
end

.mpis_from_native(alg, native) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/netpgp/highlevel/utils.rb', line 37

def self.mpis_from_native(alg, native)
  case alg
    when :PGP_PKA_RSA, :PGP_PKA_RSA_ENCRYPT_ONLY, :PGP_PKA_RSA_SIGN_ONLY
      material = native[:key][:rsa]
    when :PGP_PKA_DSA
      material = native[:key][:dsa]
    when :PGP_PKA_ELGAMAL
      material = native[:key][:elgamal]
    else
      raise "Unsupported PK algorithm: #{alg}"
  end
  NetPGP::mpi_from_native(material)
end

.mpis_to_native(alg, mpi, native) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/netpgp/highlevel/utils.rb', line 61

def self.mpis_to_native(alg, mpi, native)
  case alg
  when :PGP_PKA_RSA, :PGP_PKA_RSA_ENCRYPT_ONLY, :PGP_PKA_RSA_SIGN_ONLY
    material = native[:key][:rsa]
  when :PGP_PKA_DSA
    material = native[:key][:dsa]
  when :PGP_PKA_ELGAMAL
    material = native[:key][:elgamal]
  else
    raise "Unsupported PK algorithm: #{alg}"
  end
  # Ensure we're not leaking memory from a prior call.
  # This just frees all the BNs.
  if native.is_a?(LibNetPGP::PGPSecKey)
    LibNetPGP::pgp_seckey_free(native)
  elsif native.is_a?(LibNetPGP::PGPPubKey)
    LibNetPGP::pgp_pubkey_free(native)
  else
    raise
  end
  NetPGP::mpi_to_native(mpi, material)
end

.stream_errors(stream) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/netpgp/highlevel/utils.rb', line 10

def self.stream_errors(stream)
  error_ptr = stream[:errors]

  errors = []
  until error_ptr.null?
    error = LibNetPGP::PGPError.new(error_ptr)

    error_desc = "#{error[:file]}:#{error[:line]}: #{error[:errcode]} #{error[:comment]}"
    errors.push(error_desc)

    error_ptr = error[:next]
  end
  errors
end

.verify(keys, data, armored = true) ⇒ Object



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/netpgp/highlevel/keyring.rb', line 232

def self.verify(keys, data, armored=true)
  native_keyring_ptr = LibC::calloc(1, LibNetPGP::PGPKeyring.size)
  native_keyring = LibNetPGP::PGPKeyring.new(native_keyring_ptr)
  NetPGP::keys_to_native_keyring(keys, native_keyring)

  pgpio = LibNetPGP::PGPIO.new
  pgpio[:outs] = LibC::fdopen($stdout.to_i, 'w')
  pgpio[:errs] = LibC::fdopen($stderr.to_i, 'w')
  pgpio[:res] = pgpio[:errs]

  data_buf = FFI::MemoryPointer.new(:uint8, data.bytesize)
  data_buf.write_bytes(data)

  # pgp_validate_mem frees this
  mem_ptr = LibC::calloc(1, LibNetPGP::PGPMemory.size)
  mem = LibNetPGP::PGPMemory.new(mem_ptr)
  LibNetPGP::pgp_memory_add(mem, data_buf, data_buf.size)

  # ManagedStruct, this frees itself
  result_ptr = LibC::calloc(1, LibNetPGP::PGPValidation.size)
  result = LibNetPGP::PGPValidation.new(result_ptr)

  ret = LibNetPGP::pgp_validate_mem(pgpio, result, mem, nil, armored ? 1 : 0, native_keyring)
  ret == 1
end