Class: NetPGP::SecretKey

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/netpgp/highlevel/secretkey.rb

Overview

Secret key

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeSecretKey

Returns a new instance of SecretKey.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/netpgp/highlevel/secretkey.rb', line 30

def initialize
  @public_key = nil
  @string_to_key_usage = nil
  @string_to_key_specifier = nil
  @symmetric_key_algorithm = nil
  @hash_algorithm = nil
  @iv = nil
  @check_hash = nil
  @mpi = {}
  @userids = []
  @parent = nil
  @subkeys = []
  @raw_subpackets = []
  @encrypted = false
  @passphrase = ''
end

Instance Attribute Details

#check_hashObject

Returns the value of attribute check_hash.



15
16
17
# File 'lib/netpgp/highlevel/secretkey.rb', line 15

def check_hash
  @check_hash
end

#encryptedObject

Returns the value of attribute encrypted.



15
16
17
# File 'lib/netpgp/highlevel/secretkey.rb', line 15

def encrypted
  @encrypted
end

#hash_algorithmObject

Returns the value of attribute hash_algorithm.



15
16
17
# File 'lib/netpgp/highlevel/secretkey.rb', line 15

def hash_algorithm
  @hash_algorithm
end

#ivObject

Returns the value of attribute iv.



15
16
17
# File 'lib/netpgp/highlevel/secretkey.rb', line 15

def iv
  @iv
end

#mpiObject

Returns the value of attribute mpi.



15
16
17
# File 'lib/netpgp/highlevel/secretkey.rb', line 15

def mpi
  @mpi
end

#parentObject

Returns the value of attribute parent.



15
16
17
# File 'lib/netpgp/highlevel/secretkey.rb', line 15

def parent
  @parent
end

#passphraseObject

Returns the value of attribute passphrase.



15
16
17
# File 'lib/netpgp/highlevel/secretkey.rb', line 15

def passphrase
  @passphrase
end

#public_keyObject

Returns the value of attribute public_key.



15
16
17
# File 'lib/netpgp/highlevel/secretkey.rb', line 15

def public_key
  @public_key
end

#raw_subpacketsObject

Returns the value of attribute raw_subpackets.



15
16
17
# File 'lib/netpgp/highlevel/secretkey.rb', line 15

def raw_subpackets
  @raw_subpackets
end

#string_to_key_specifierObject

Returns the value of attribute string_to_key_specifier.



15
16
17
# File 'lib/netpgp/highlevel/secretkey.rb', line 15

def string_to_key_specifier
  @string_to_key_specifier
end

#string_to_key_usageObject

Returns the value of attribute string_to_key_usage.



15
16
17
# File 'lib/netpgp/highlevel/secretkey.rb', line 15

def string_to_key_usage
  @string_to_key_usage
end

#subkeysObject

Returns the value of attribute subkeys.



15
16
17
# File 'lib/netpgp/highlevel/secretkey.rb', line 15

def subkeys
  @subkeys
end

#symmetric_key_algorithmObject

Returns the value of attribute symmetric_key_algorithm.



15
16
17
# File 'lib/netpgp/highlevel/secretkey.rb', line 15

def symmetric_key_algorithm
  @symmetric_key_algorithm
end

#useridsObject

Returns the value of attribute userids.



15
16
17
# File 'lib/netpgp/highlevel/secretkey.rb', line 15

def userids
  @userids
end

Class Method Details

.from_native(sk, encrypted = false) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/netpgp/highlevel/secretkey.rb', line 230

def self.from_native(sk, encrypted=false)
  seckey = SecretKey.new
  seckey.public_key = PublicKey::from_native(sk[:pubkey])
  seckey.string_to_key_usage = LibNetPGP::enum_value(sk[:s2k_usage])
  seckey.string_to_key_specifier = LibNetPGP::enum_value(sk[:s2k_specifier])
  seckey.symmetric_key_algorithm = LibNetPGP::enum_value(sk[:alg])
  seckey.hash_algorithm = LibNetPGP::enum_value(sk[:hash_alg]) || HashAlgorithm::SHA1
  seckey.iv = sk[:iv].to_ptr.read_bytes(sk[:iv].size)
  if not sk[:checkhash].null?
    seckey.check_hash = sk[:checkhash].read_bytes(LibNetPGP::PGP_CHECKHASH_SIZE)
  end
  seckey.mpi = NetPGP::mpis_from_native(sk[:pubkey][:alg], sk)
  seckey.encrypted = encrypted
  seckey
end

.generate(passphrase, options = {}) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/netpgp/highlevel/secretkey.rb', line 204

def self.generate(passphrase, options={})
  valid_options = [:key_length, :public_key_algorithm, :algorithm_params,
                   :hash_algorithm, :symmetric_key_algorithm]
  for option in options.keys
    raise if not valid_options.include?(option)
  end

  key_length = options[:key_length] || 4096
  pkalg = options[:public_key_algorithm] || PublicKeyAlgorithm::RSA
  pkalg_params = options[:algorithm_params] || {e: 65537}
  hashalg = options[:hash_algorithm] || HashAlgorithm::SHA1
  skalg = options[:symmetric_key_algorithm] || SymmetricKeyAlgorithm::CAST5
  hashalg_s = HashAlgorithm::to_s(hashalg)
  skalg_s = SymmetricKeyAlgorithm::to_s(skalg)

  native_key = nil
  begin
    native_key = LibNetPGP::pgp_rsa_new_key(key_length, pkalg_params[:e], hashalg_s, skalg_s)
    key = SecretKey::from_native(native_key[:key][:seckey])
    key.passphrase = passphrase
    key
  ensure
    LibNetPGP::pgp_keydata_free(native_key) if native_key
  end
end

Instance Method Details

#add_subkey(subkey) ⇒ Object



197
198
199
200
201
202
# File 'lib/netpgp/highlevel/secretkey.rb', line 197

def add_subkey(subkey)
  raise if subkey.subkeys.any?
  subkey.parent = self
  subkey.userids = @userids
  @subkeys.push(subkey)
end

#clearsign(data, armored = true, options = {}) ⇒ String

Cleartext signs data using this secret key. This is a shortcut for #sign.

Note: #passphrase must be set to the correct passphrase prior to this call. If no passphrase is required, it should be set to ”.

Parameters:

  • data (String)

    the data to be signed.

  • armored (Boolean) (defaults to: true)

    whether the output should be ASCII armored.

  • options (Hash) (defaults to: {})

    less-often used options that override defaults.

    • :from [Time] (defaults to Time.now) - signature creation time

    • :duration [Integer] (defaults to 0) - signature duration/expiration

    • :hash_algorithm [HashAlgorithm] (defaults to SHA1) - hash algorithm to use

Returns:

  • (String)

    the signed data, or nil on error.



150
151
152
153
# File 'lib/netpgp/highlevel/secretkey.rb', line 150

def clearsign(data, armored=true, options={})
  options[:cleartext] = true
  sign(data, armored, options)
end

#decrypt(data, armored = true) ⇒ Object

Decrypts data using this secret key.

Note: #passphrase must be set to the correct passphrase prior to this call. If no passphrase is required, it should be set to ” (not nil).

Parameters:

  • data (String)

    the encrypted data to be decrypted.

  • armored (Boolean) (defaults to: true)

    whether the encrypted data is ASCII armored.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/netpgp/highlevel/secretkey.rb', line 64

def decrypt(data, armored=true)
  begin
    rd, wr = IO.pipe
    wr.write(@passphrase + "\n")
    native_keyring_ptr = LibC::calloc(1, LibNetPGP::PGPKeyring.size)
    native_keyring = LibNetPGP::PGPKeyring.new(native_keyring_ptr)
    NetPGP::keys_to_native_keyring([self], native_keyring)
    pgpio = create_pgpio
    data_ptr = FFI::MemoryPointer.new(:uint8, data.bytesize)
    data_ptr.write_bytes(data)
    passfp = LibC::fdopen(rd.to_i, 'r')
    mem_ptr = LibNetPGP::pgp_decrypt_buf(pgpio, data_ptr, data_ptr.size,
                                         native_keyring, nil,
                                         armored ? 1 : 0, 0, passfp, 1, nil)
    return nil if mem_ptr.null?
    mem = LibNetPGP::PGPMemory.new(mem_ptr)
    mem[:buf].read_bytes(mem[:length])
  ensure
    rd.close
    wr.close
  end
end

#decrypted_seckeyObject



283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/netpgp/highlevel/secretkey.rb', line 283

def decrypted_seckey
  if encrypted?
    native_ptr = LibC::calloc(1, LibNetPGP::PGPKey.size)
    native = LibNetPGP::PGPKey.new(native_ptr)
    native_auto = FFI::AutoPointer.new(native_ptr, LibNetPGP::PGPKey.method(:release))
    to_native_key(native)
    rd, wr = IO.pipe
    wr.write(@passphrase + "\n")
    wr.close
    passfp = LibC::fdopen(rd.to_i, 'r')
    decrypted = LibNetPGP::pgp_decrypt_seckey(native, passfp)
    rd.close
    LibC::fclose(passfp)
    return nil if not decrypted or decrypted.null?
    LibNetPGP::PGPSecKey.new(decrypted)
  else
    native_ptr = LibC::calloc(1, LibNetPGP::PGPSecKey.size)
    native = LibNetPGP::PGPSecKey.new(native_ptr)
    to_native(native)
    native
  end
end

#detached_sign(infile, sigfile = nil, armored = true, options = {}) ⇒ Boolean

Creates a detached signature of a file.

Note: #passphrase must be set to the correct passphrase prior to this call. If no passphrase is required, it should be set to ”.

Parameters:

  • infile (String)

    the path to the input file for which a signature will be created.

  • sigfile (String) (defaults to: nil)

    the path to the signature file that will be created.

    This can be nil, in which case the filename will be the infile parameter with ‘.asc’ appended.

  • armored (Boolean) (defaults to: true)

    whether the output should be ASCII armored.

  • options (Hash) (defaults to: {})

    less-often used options that override defaults.

    • :from [Time] (defaults to Time.now) - signature creation time

    • :duration [Integer] (defaults to 0) - signature duration/expiration

    • :hash_algorithm [HashAlgorithm] (defaults to SHA1) - hash algorithm to use

Returns:

  • (Boolean)

    whether the signing was successful.



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/netpgp/highlevel/secretkey.rb', line 175

def detached_sign(infile, sigfile=nil, armored=true, options={})
  valid_options = [:from, :duration, :hash_algorithm]
  for option in options.keys
    raise if not valid_options.include?(option)
  end

  armored = armored ? 1 : 0
  from = options[:from] || Time.now
  duration = options[:duration] || 0
  hashalg = options[:hash_algorithm] || HashAlgorithm::SHA1

  hashname = HashAlgorithm::to_s(hashalg)
  from = from.to_i

  pgpio = create_pgpio
  # Note: pgp_sign_detached calls pgp_seckey_free for us
  seckey = decrypted_seckey
  return false if not seckey
  ret = LibNetPGP::pgp_sign_detached(pgpio, infile, sigfile, seckey, hashname, from, duration, armored, 1)
  return ret == 1
end

#encrypted?Boolean

Checks if a key is encrypted. An encrypted key requires a passphrase for signing/decrypting/etc and will have nil values for key material/mpis.

Returns:

  • (Boolean)


52
53
54
# File 'lib/netpgp/highlevel/secretkey.rb', line 52

def encrypted?
  @encrypted
end

#sign(data, armored = true, options = {}) ⇒ String

Signs data using this secret key.

Note: #passphrase must be set to the correct passphrase prior to this call. If no passphrase is required, it should be set to ”.

Parameters:

  • data (String)

    the data to be signed.

  • armored (Boolean) (defaults to: true)

    whether the output should be ASCII armored.

  • options (Hash) (defaults to: {})

    less-often used options that override defaults.

    • :from [Time] (defaults to Time.now) - signature creation time

    • :duration [Numeric] (defaults to 0) - signature duration/expiration

    • :hash_algorithm [NetPGP::HashAlgorithm] (defaults to SHA1) - hash algorithm to use

    • :cleartext [Boolean] (defaults to false) - whether this should be a cleartext/clearsign signature, which includes the original data in cleartext in the same document.

Returns:

  • (String)

    the signed data, or nil on error.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/netpgp/highlevel/secretkey.rb', line 103

def sign(data, armored=true, options={})
  valid_options = [:from, :duration, :hash_algorithm, :cleartext]
  for option in options.keys
    raise if not valid_options.include?(option)
  end

  armored = armored ? 1 : 0
  from = options[:from] || Time.now
  duration = options[:duration] || 0
  hashalg = options[:hash_algorithm] || HashAlgorithm::SHA1
  cleartext = options[:cleartext] ? 1 : 0

  from = from.to_i
  hashname = HashAlgorithm::to_s(hashalg)

  pgpio = create_pgpio
  data_buf = FFI::MemoryPointer.new(:uint8, data.bytesize)
  data_buf.write_bytes(data)
  seckey = decrypted_seckey
  return nil if not seckey
  memory = nil
  begin
    memory_ptr = LibNetPGP::pgp_sign_buf(pgpio, data_buf, data_buf.size, seckey, from, duration, hashname, armored, cleartext)
    return nil if not memory_ptr or memory_ptr.null?
    memory = LibNetPGP::PGPMemory.new(memory_ptr)
    signed_data = memory[:buf].read_bytes(memory[:length])
    signed_data
  ensure
    LibNetPGP::pgp_memory_free(memory) if memory
    LibNetPGP::pgp_seckey_free(seckey) if seckey
  end
end

#to_native(native) ⇒ Object



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/netpgp/highlevel/secretkey.rb', line 246

def to_native(native)
  @public_key.to_native(native[:pubkey])
  native[:s2k_usage] = @string_to_key_usage
  native[:s2k_specifier] = @string_to_key_specifier
  native[:alg] = @symmetric_key_algorithm
  native[:hash_alg] = @hash_algorithm
  # zero IV, then copy
  native[:iv].to_ptr.write_bytes("\x00" * native[:iv].size)
  native[:iv].to_ptr.write_bytes(@iv) if @iv
  NetPGP::mpis_to_native(PublicKeyAlgorithm::to_native(@public_key.public_key_algorithm), @mpi, native)
  # note: this has to come after mpis_to_native because that frees ptrs
  if @check_hash
    native[:checkhash] = LibC::calloc(1, LibNetPGP::PGP_CHECKHASH_SIZE)
    native[:checkhash].write_bytes(@check_hash)
  end
end

#to_native_key(native_key) ⇒ Object



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/netpgp/highlevel/secretkey.rb', line 263

def to_native_key(native_key)
  raise if not native_key[:packets].null?
  native_key[:type] = :PGP_PTAG_CT_SECRET_KEY
  native_key[:sigid] = @public_key.key_id
  to_native(native_key[:key][:seckey])
  if not @parent
    @userids.each {|userid|
      LibNetPGP::dynarray_append_item(native_key, 'uid', :string, userid)
    }
  end
  @raw_subpackets.each {|bytes|
    packet = LibNetPGP::PGPSubPacket.new
    length = bytes.bytesize
    packet[:length] = length
    packet[:raw] = LibC::calloc(1, length)
    packet[:raw].write_bytes(bytes)
    LibNetPGP::dynarray_append_item(native_key, 'packet', LibNetPGP::PGPSubPacket, packet)
  }
end