rnp 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +26 -0
  8. data/README.adoc +208 -0
  9. data/Rakefile +6 -0
  10. data/Use_Cases.adoc +119 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/example-usage.rb +766 -0
  14. data/examples/highlevel/decrypt_mem.rb +44 -0
  15. data/examples/highlevel/encrypt_mem.rb +46 -0
  16. data/examples/lowlevel/decrypt_file.rb +76 -0
  17. data/examples/lowlevel/decrypt_mem.rb +80 -0
  18. data/examples/lowlevel/encrypt_file.rb +68 -0
  19. data/examples/lowlevel/encrypt_mem.rb +75 -0
  20. data/examples/lowlevel/load_pubkey.rb +118 -0
  21. data/examples/lowlevel/print_keyring_file.rb +68 -0
  22. data/examples/lowlevel/print_keyring_mem.rb +96 -0
  23. data/examples/lowlevel/sign_file.rb +104 -0
  24. data/examples/lowlevel/sign_mem.rb +96 -0
  25. data/examples/lowlevel/verify_file.rb +55 -0
  26. data/examples/lowlevel/verify_mem.rb +61 -0
  27. data/lib/rnp/highlevel/constants.rb +96 -0
  28. data/lib/rnp/highlevel/keyring.rb +259 -0
  29. data/lib/rnp/highlevel/publickey.rb +150 -0
  30. data/lib/rnp/highlevel/secretkey.rb +318 -0
  31. data/lib/rnp/highlevel/utils.rb +119 -0
  32. data/lib/rnp/highlevel.rb +5 -0
  33. data/lib/rnp/lowlevel/constants.rb +11 -0
  34. data/lib/rnp/lowlevel/dynarray.rb +129 -0
  35. data/lib/rnp/lowlevel/enums.rb +243 -0
  36. data/lib/rnp/lowlevel/libc.rb +28 -0
  37. data/lib/rnp/lowlevel/libopenssl.rb +15 -0
  38. data/lib/rnp/lowlevel/librnp.rb +213 -0
  39. data/lib/rnp/lowlevel/structs.rb +541 -0
  40. data/lib/rnp/lowlevel/utils.rb +25 -0
  41. data/lib/rnp/lowlevel.rb +6 -0
  42. data/lib/rnp/version.rb +3 -0
  43. data/lib/rnp.rb +5 -0
  44. data/rnp/lib/rnp.rb +5 -0
  45. data/rnp/spec/rnp_spec.rb +11 -0
  46. data/rnp.gemspec +35 -0
  47. metadata +82 -9
data/example-usage.rb ADDED
@@ -0,0 +1,766 @@
1
+ # Example usage of Rnp Ruby binding
2
+
3
+ module Rnp
4
+
5
+ # https://tools.ietf.org/html/rfc4880#section-4.1
6
+ # An OpenPGP message is constructed from a number of records that are
7
+ # traditionally called packets.
8
+ #
9
+ # https://tools.ietf.org/html/rfc4880#section-11
10
+ # OpenPGP packets are assembled into sequences in order to create
11
+ # messages and to transfer keys. Not all possible packet sequences are
12
+ # meaningful and correct.
13
+ class Message; end
14
+
15
+ # RFC 4880 11.1. Transferable Public Keys
16
+ # OpenPGP users may transfer public keys. The essential elements of a
17
+ # transferable public key are as follows:
18
+ # - One Public-Key packet
19
+ # - Zero or more revocation signatures
20
+ # - One or more User ID packets
21
+ # - After each User ID packet, zero or more Signature packets (certifications)
22
+ # - Zero or more User Attribute packets
23
+ # - After each User Attribute packet, zero or more Signature packets (certifications)
24
+ # - Zero or more Subkey packets
25
+ # - After each Subkey packet, one Signature packet, plus optionally a revocation
26
+ class PublicKeyMessage; end
27
+ # message.public_key_packet => PublicKeyPacketV(3 or 4)
28
+ # message.revocation_signatures => [] of Signatures
29
+
30
+ # RFC 4880 11.2. Transferable Secret Keys
31
+ # OpenPGP users may transfer secret keys. The format of a transferable
32
+ # secret key is the same as a transferable public key except that secret-key
33
+ # and secret-subkey packets are used instead of the public key and
34
+ # public-subkey packets. Implementations SHOULD include self- signatures on
35
+ # any user IDs and subkeys, as this allows for a complete public key to be
36
+ # automatically extracted from the transferable secret key. Implementations
37
+ # MAY choose to omit the self-signatures, especially if a transferable public
38
+ # key accompanies the transferable secret key.
39
+ class SecretKeyMessage; end
40
+
41
+ # RFC 4880 11.3. OpenPGP Messages
42
+ # An OpenPGP message is a packet or sequence of packets that corresponds to the
43
+ # following grammatical rules (comma represents sequential composition, and
44
+ # vertical bar separates alternatives):
45
+ #
46
+ # OpenPGP Message :- Encrypted Message | Signed Message | Compressed Message | Literal Message.
47
+ # Compressed Message :- Compressed Data Packet.
48
+ # Literal Message :- Literal Data Packet.
49
+ # ESK :- Public-Key Encrypted Session Key Packet | Symmetric-Key Encrypted Session Key Packet.
50
+ # ESK Sequence :- ESK | ESK Sequence, ESK.
51
+ # Encrypted Data :- Symmetrically Encrypted Data Packet | Symmetrically Encrypted Integrity Protected Data Packet
52
+ # Encrypted Message :- Encrypted Data | ESK Sequence, Encrypted Data.
53
+ # One-Pass Signed Message :- One-Pass Signature Packet, OpenPGP Message, Corresponding Signature Packet.
54
+ # Signed Message :- Signature Packet, OpenPGP Message | One-Pass Signed Message.
55
+ class OpenPgpMessage < Message; end
56
+
57
+ # The user only needs to know about these
58
+ class LiteralMessage < OpenPgpMessage; end
59
+ class CompressedMessage < OpenPgpMessage; end
60
+ class EncryptedMessage < OpenPgpMessage; end
61
+ class SignedMessage < OpenPgpMessage; end
62
+
63
+ class OnePassSignedMessage < OpenPgpMessage; end
64
+ class EncryptedData; end
65
+ class EncryptedSessionKeySequence; end
66
+ class EncryptedSessionKey; end
67
+
68
+ # RFC 4880 11.4. Detached Signatures
69
+ # Some OpenPGP applications use so-called "detached signatures". For
70
+ # example, a program bundle may contain a file, and with it a second file
71
+ # that is a detached signature of the first file. These detached signatures
72
+ # are simply a Signature packet stored separately from the data for which
73
+ # they are a signature.
74
+ class Signature < Packet; end # a "Signature Packet"
75
+
76
+ # https://tools.ietf.org/html/rfc4880#section-4.1
77
+ # RFC 4880 Section 4-5
78
+ # A packet is a chunk of data that has a tag specifying its meaning. An
79
+ # OpenPGP message, keyring, certificate, and so forth consists of a number of
80
+ # packets. Some of those packets may contain other OpenPGP packets (for
81
+ # example, a compressed data packet, when uncompressed, contains OpenPGP
82
+ # packets).
83
+ class Packet; end
84
+ packet.type # RFC 4880 section 5
85
+ packet.version # version 3 or 4
86
+ packet.subpackets # sub-packets in Rnp::Packet format
87
+ packet.certify(key) # adds a certification signature after the packet
88
+ packet.parent # The parent packet if it is a subpacket
89
+
90
+ # 5.1. Public-Key Encrypted Session Key Packets (Tag 1)
91
+ class PublicKeyEncryptedSessionKeyPacket; end
92
+ # - A one-octet number giving the version number of the packet type. The
93
+ # currently defined value for packet version is 3.
94
+ packet.version # => 3 only
95
+
96
+ # - An eight-octet number that gives the Key ID of the public key to
97
+ # which the session key is encrypted. If the session key is
98
+ # encrypted to a subkey, then the Key ID of this subkey is used
99
+ # here instead of the Key ID of the primary key.
100
+ packet.public_key_id # => String Key ID
101
+
102
+ # - A one-octet number giving the public-key algorithm used.
103
+ packet.public_key_algorithm # => PublicKeyAlgorithm
104
+
105
+ # - A string of octets that is the encrypted session key. This string takes
106
+ # up the remainder of the packet, and its contents are dependent on the
107
+ # public-key algorithm used.
108
+ packet.encrypted_session_key # => String ciphertext
109
+ packet.decrypted_session_key # => String plaintext
110
+
111
+ # - Algorithm specific fields
112
+ # If RSA:
113
+ # - Multiprecision integer (MPI) of RSA encrypted value m**e mod n.
114
+ # { mpi: m**e mod n }
115
+ # If Elgamal:
116
+ # - MPI of Elgamal (Diffie-Hellman) value g**k mod p.
117
+ # - MPI of Elgamal (Diffie-Hellman) value m * y**k mod p.
118
+ # { first: g**k mod p, second: m* y**k mod p }
119
+ packet.algorithm_specific_fields
120
+
121
+ # 5.2. Signature Packet (Tag 2)
122
+ SignaturePacket = Signature # as defined above
123
+ # RFC 4880 5.2.2 (3 or 4)
124
+ signature.version
125
+ # RFC 4880 5.2.1 Signature Type
126
+ signature.type = [ # one of
127
+ SignatureType::GenericCertification,
128
+ SignatureType::PersonaCertification,
129
+ SignatureType::CasualCertification,
130
+ SignatureType::PositiveCertification,
131
+ SignatureType::SubkeyBinding,
132
+ SignatureType::PrimaryKeyBinding,
133
+ SignatureType::DirectKey,
134
+ SignatureType::KeyRevocation,
135
+ SignatureType::SubkeyRevocation,
136
+ SignatureType::CertificationRevocation,
137
+ SignatureType::Timestamp,
138
+ SignatureType::ThirdPartyConfirmation
139
+ ]
140
+ signature.public_key_algorithm # PublicKeyAlgorithm
141
+ signature.hash_algorithm # HashAlgorithm
142
+ signature.to_s # ASCII armored signature
143
+
144
+ # SIGNATURE ATTRIBUTES (Subpackets of the Signature Packet)
145
+ # 5.2.3 Returns array of Subpackets
146
+ signature.hashed_subpackets
147
+ signature.unhashed_subpackets
148
+ # 5.2.3.4. Signature Creation Time
149
+ # DateTime
150
+ signature.creation_time
151
+ # 5.2.3.5. Issuer: The OpenPGP Key ID of the key issuing the signature.
152
+ signature.issuer
153
+ # 5.2.3.6. Key Expiration Time
154
+ # DateTime
155
+ signature.key_expiration_time
156
+ # 5.2.3.7. Preferred Symmetric Algorithms
157
+ # Ordered [] of preferred symmetric algorithms
158
+ signature.preferred_symmetric_algorithms
159
+ # 5.2.3.8. Preferred Hash Algorithms
160
+ # Ordered [] of preferred hash algorithms
161
+ signature.preferred_hash_algorithms
162
+ # 5.2.3.9. Preferred Compression Algorithms
163
+ # Ordered [] of preferred compression algorithms, :zip by default
164
+ signature.preferred_compression_algorithms
165
+ # 5.2.3.10. Signature Expiration Time
166
+ # DateTime
167
+ signature.signature_expiration_time
168
+ # 5.2.3.11. Exportable Certification
169
+ # Boolean
170
+ signature.exportable_certification
171
+ # 5.2.3.12. Revocable
172
+ # Boolean
173
+ signature.revocable
174
+ # 5.2.3.13. Trust Signature
175
+ # Integer 0-255
176
+ signature.trust_signature
177
+ # 5.2.3.14. Regular Expression
178
+ # RegExp
179
+ signature.regex
180
+ # 5.2.3.15. Revocation Key
181
+ signature.revocation_key # String of Key ID
182
+ signature.revocation_key_sensitive # Boolean
183
+ signature.revocation_key_algorithm # Key Algorithm
184
+ # 5.2.3.16. Notation Data
185
+ class NotationData; end
186
+ # Each NotationData has a :name (email address), :value (UTF8 string), a flag
187
+ # :human_readable
188
+ signature.notation_data.first.human_readable # Boolean, "human-readable"
189
+ # [] of NotationData, each is a { name (email) => value (UTF8 string) }
190
+ signature.notation_data
191
+ # 5.2.3.17. Key Server Preferences
192
+ signature.key_server_preferences_no_modify # Boolean
193
+ # 5.2.3.18. Preferred Key Server
194
+ signature.preferred_key_server # a URI String
195
+ # 5.2.3.19. Primary User ID
196
+ signature.primary_userid # Boolean
197
+ # 5.2.3.20. Policy URI
198
+ signature.policy_uri # a URI String
199
+ # 5.2.3.21. Key Flags
200
+ # The "split key" (0x10) and "group key" (0x80) flags are placed on a
201
+ # self-signature only; they are meaningless on a certification signature. They
202
+ # SHOULD be placed only on a direct-key signature (type 0x1F) or a subkey
203
+ # signature (type 0x18), one that refers to the key the flag applies to.
204
+ signature.key_flags # Can be combination of the following, :cert is always required.
205
+ [
206
+ :cert, # First octet 0x01, This key may be used to certify other keys.
207
+ :sign, # First octet 0x02, This key may be used to sign data.
208
+ :encrypt_comm, # First octet 0x04, This key may be used to encrypt communications.
209
+ :encrypt_data, # First octet 0x08, This key may be used to encrypt storage.
210
+ :auth, # First octet 0x20, This key may be used for authentication.
211
+ :split, # 0x10 - The private component of this key may have been split by a secret-sharing mechanism
212
+ :group, # 0x80 - The private component of this key may be in the possession of more than one person.
213
+ ]
214
+ # 5.2.3.22. Signer's User ID
215
+ signature.userid # Userid object
216
+ # 5.2.3.23. Reason for Revocation
217
+ signature.revocation_code # one of :no_reason, :superseded, :compromised, :retired, :invalid
218
+ signature.revocation_reason # String
219
+ # 5.2.3.24. Features
220
+ signature.features # Not needed now
221
+ # 5.2.3.25. Signature Target
222
+ signature.target_public_key_algorithm # PublicKeyAlgorithm
223
+ signature.target_hash_algorithm # HashAlgorithm
224
+ signature.target_hash # String
225
+ # 5.2.3.26. Embedded Signature
226
+ signature.embedded_signature # => SignaturePacket
227
+
228
+
229
+ # 5.3. Symmetric-Key Encrypted Session Key Packets (Tag 3)
230
+ class SymmetricKeyEncryptedSessionKeyPacket; end
231
+ p.version # => 4
232
+ p.symmetric_algorithm # => SymmetricKeyAlgorithm
233
+ p.string_to_key # (S2K)
234
+ p.session_key
235
+
236
+ # 5.4. One-Pass Signature Packets (Tag 4)
237
+ class OnePassSignaturePacket; end
238
+ p.version # => 4
239
+ p.signature_type # => SignatureType
240
+ p.hash_algorithm # => HashAlgorithm
241
+ p.public_key_algorithm # => PublicKeyAlgorithm
242
+ p.key_id # => String
243
+ p.nested # => Boolean
244
+
245
+ # 5.5. Key Material Packet
246
+ # 5.5.1. Key Packet Variants
247
+ # 5.5.1.1. Public-Key Packet (Tag 6)
248
+ # 5.5.1.2. Public-Subkey Packet (Tag 14)
249
+ # 5.5.1.3. Secret-Key Packet (Tag 5)
250
+ # 5.5.1.4. Secret-Subkey Packet (Tag 7)
251
+ class KeyMaterialPacket; end
252
+
253
+ # 5.5.2. Public-Key Packet Formats
254
+ class PublicKeyPacketV3 < KeyMaterialPacket; end
255
+ class PublicSubkeyPacketV3 < PublicKeyPacketV3; end
256
+ # Methods
257
+ packet.version # => 3
258
+ packet.creation_time # => DateTime
259
+ packet.expiration_time # => Integer in days
260
+ packet.public_key_algorithm # => PublicKeyAlgorithm
261
+ packet.mpi # =>
262
+ # {
263
+ # n: "RSA MPI modulus n",
264
+ # e: "RSA MPI encryption exponent e"
265
+ # }
266
+
267
+ class PublicKeyPacketV4 < KeyMaterialPacket; end
268
+ class PublicSubkeyPacketV4 < PublicKeyPacketV4; end
269
+ # Methods
270
+ packet.version # => 4
271
+ packet.creation_time # => DateTime
272
+ packet.expiration_time # => Integer in days
273
+ packet.public_key_algorithm # => PublicKeyAlgorithm
274
+ # If RSA:
275
+ packet.mpi # => {
276
+ # n: RSA MPI modulus n
277
+ # e: RSA MPI encryption exponent e
278
+ # }
279
+ # If DSA:
280
+ packet.mpi # => {
281
+ # p: DSA prime p
282
+ # q: DSA group order q
283
+ # g: DSA group generator g
284
+ # y: DSA public key value y
285
+ # }
286
+ # If Elgamal:
287
+ packet.mpi # => {
288
+ # p: Elgamal prime p
289
+ # g: Elgamal group generator g
290
+ # y: Elgamal public key value y
291
+ # }
292
+
293
+ # 5.5.3. Secret-Key Packet Formats
294
+ class SecretKeyPacketV3 < PublicKeyPacketV3; end
295
+ class SecretSubkeyPacketV3 < SecretKeyPacketV3; end
296
+ class SecretKeyPacketV4 < PublicKeyPacketV4; end
297
+ class SecretSubkeyPacketV4 < SecretKeyPacketV4; end
298
+ # Methods (in addition to PublicKey methods above)
299
+ packet.public_key_packet # => PublicKeyPacketV3 || PublicSubkeyPacketV3
300
+ packet.string_to_key_usage # => 0-255
301
+ packet.symmetric_key_algorithm # (optional if usage is 255 or 254)
302
+ packet.string_to_key_specifier # (optional if usage is 255 or 254)
303
+ packet.iv # (optional, if secret key is encrypted)
304
+ packet.secret_key_data # => String
305
+
306
+ # If the string-to-key usage octet is zero or 255, then a two-octet checksum
307
+ # of the plaintext of the algorithm-specific portion (sum of all octets, mod
308
+ # 65536). If the string-to-key usage octet was 254, then a 20-octet SHA-1
309
+ # hash of the plaintext of the algorithm-specific portion. This checksum or
310
+ # hash is encrypted together with the algorithm-specific fields (if
311
+ # string-to-key usage octet is not zero). Note that for all other values, a
312
+ # two-octet checksum is required.
313
+ packet.checksum # => String
314
+
315
+ # If RSA secret key:
316
+ # {
317
+ # d: multiprecision integer (MPI) of RSA secret exponent d.
318
+ # p: MPI of RSA secret prime value p.
319
+ # q: MPI of RSA secret prime value q (p < q).
320
+ # u: MPI of u, the multiplicative inverse of p, mod q.
321
+ # }
322
+ # If DSA secret key:
323
+ # { x: MPI of DSA secret exponent x. }
324
+ # If Elgamal secret key:
325
+ # { x: MPI of Elgamal secret exponent x. }
326
+ packet.algorithm_specific_fields # => Hash
327
+
328
+ # 5.6. Compressed Data Packet (Tag 8)
329
+ class CompressedDataPacket; end
330
+ packet.algorithm # => CompressionAlgorithm
331
+ packet.data # => a valid decompressed Rnp::OpenPgpMessage
332
+ packet.to_s # => an armored compressed data packet
333
+
334
+ # 5.7. Symmetrically Encrypted Data Packet (Tag 9)
335
+ class SymmetricallyEncryptedDataPacket; end
336
+ packet.data # => [] of valid Rnp::Packet(s)
337
+
338
+ # 5.8. Marker Packet (Obsolete Literal Packet) (Tag 10)
339
+ class MarkerPacket; end
340
+ # The body of this packet consists of:
341
+ # - The three octets 0x50, 0x47, 0x50 (which spell "PGP" in UTF-8).
342
+ # Such a packet MUST be ignored when received.
343
+ packet.text # => "PGP"
344
+
345
+ # 5.9. Literal Data Packet (Tag 11)
346
+ class LiteralDataPacket; end
347
+ # A Literal Data packet contains the body of a message; data that is not to
348
+ # be further interpreted.
349
+ # - A one-octet field that describes how the data is formatted.
350
+ packet.format # => one of "b" (Binary), "t" (Text), "u" (UTF-8 text), "l" or
351
+ # "1" (Local) is deprecated.
352
+ packet.binary?
353
+ packet.text?
354
+ packet.utf8?
355
+
356
+ # - File name as a string (one-octet length, followed by a file name).
357
+ packet.filename # => String. Name of encrypted file or "_CONSOLE" (for your eyes only)
358
+
359
+ # - A four-octet number that indicates a date associated with the literal
360
+ # data.
361
+ packet.date # => DateTime, modification date of a file or the time packet was
362
+ # created, or 0 for no time
363
+
364
+ # - The remainder of the packet is literal data.
365
+ packet.data # => IO stream
366
+ packet.text # => If packet.text? then return text data with normalized <CR><LF> to local
367
+
368
+ # 5.10. Trust Packet (Tag 12)
369
+ class TrustPacket; end
370
+ # Only used in Keyrings
371
+
372
+ # 5.11. User ID Packet (Tag 13)
373
+ class UserIdPacket; end
374
+ # A RFC 2822 mail-addr.
375
+ # Same as class Rnp::Userid
376
+
377
+ # 5.12. User Attribute Packet (Tag 17)
378
+ class UserAttributePacket < UserIdPacket; end
379
+ packet.subpackets # => [] of Subpackets
380
+
381
+ class UserAttributeSubpacket; end
382
+ subpacket.type # => Only "1" is allowed as ImageAttributeSubpacket
383
+ subpacket.body # => Body of subpacket
384
+
385
+ # 5.12.1. The Image Attribute Subpacket
386
+ class ImageAttributeSubpacket < UserAttributeSubpacket; end
387
+ imagesubpacket.version # => 1 (only 1 allowed)
388
+ imagesubpacket.encoding # => 1 (only 1 allowed, means "JPEG")
389
+ imagesubpacket.body # => JPEG file IO
390
+
391
+ # 5.13. Sym. Encrypted Integrity Protected Data Packet (Tag 18) ..49
392
+ class SymmetricallyEncryptedIntegrityProtectedDataPacket < SymmetricallyEncryptedDataPacket; end
393
+ # - A one-octet version number. The only currently defined value is 1.
394
+ packet.version # => 1 (only 1 allowed)
395
+
396
+ # - Encrypted data, the output of the selected symmetric-key cipher operating
397
+ # in Cipher Feedback mode with shift amount equal to the block size of the
398
+ # cipher (CFB-n where n is the block size).
399
+ packet.data # => [decrypted Packet(s) (last one must be a ModificationDetectionCodePacket)]
400
+ packet.plaintext_data # => [decrypted Packet(s) without the last one (the ModificationDetectionCodePacket)]
401
+
402
+ # The symmetric cipher used MUST be specified in a Public-Key or
403
+ # Symmetric-Key Encrypted Session Key packet that precedes the Symmetrically
404
+ # Encrypted Data packet.
405
+ packet.cipher_packet # => a preceding PublicKeyEncryptedSessionKeyPacket or
406
+ # SymmetricKeyEncryptedSessionKeyPacket
407
+
408
+ # packet.mdc_packet => the ModificationDetectionCodePacket at the end of its packet.data
409
+
410
+ # 5.14. Modification Detection Code Packet (Tag 19)
411
+ class ModificationDetectionCodePacket; end
412
+ # - A 20-octet SHA-1 hash of the preceding plaintext data of the
413
+ # Symmetrically Encrypted Integrity Protected Data packet, including prefix
414
+ # data, the tag octet, and length octet of the Modification Detection Code
415
+ # packet.
416
+ #
417
+ packet.hash # => SHA1 hash
418
+ packet.hash_algorithm # => SHA1 allowed only
419
+
420
+ # These provide easier user access to the Packet(s)
421
+ Userid = UserIdPacket;
422
+ PublicKey = PublicKeyMessage
423
+ SecretKey = SecretKeyMessage
424
+
425
+ # These are the algorithms, for user read-only
426
+ class PublicKeyAlgorithm; end
427
+ class SymmetricKeyAlgorithm; end
428
+ class HashAlgorithm; end
429
+ class CompressionAlgorithm; end
430
+ # Not required yet
431
+ # class Keyring; end
432
+ end
433
+
434
+ # READING AN EXISTING SECRET KEY
435
+ key = Rnp::SecretKey.import(File.read("privatekey.key"))
436
+ key.passphrase = "xxx" # => non-interactive method of providing passphrase
437
+ key.to_s # => ASCII armored PGP secret key
438
+ key.public_key # => Rnp::PublicKey object
439
+
440
+ # GENERATING A NEW KEY
441
+ key = Rnp::SecretKey.new
442
+ key.generate(
443
+ key_length: Integer,
444
+ public_key_algorithm: PublicKeyAlgorithm::RSA,
445
+ algorithm_params: { e: Integer }, # content is public_key_algorithm specific
446
+ userid: String || Userid,
447
+ hash_algorithm: HashAlgorithm,
448
+ symmetric_key_algorithm: SymmetricKeyAlgorithm
449
+ )
450
+ # => calls Rnp's
451
+ # pgp_rsa_new_selfsign_key(
452
+ # const int numbits,
453
+ # const unsigned long e,
454
+ # uint8_t *userid,
455
+ # const char *hashalg,
456
+ # const char *cipher
457
+ # )
458
+ #
459
+ # Generates the following structure for the SecretKeyMessage:
460
+ # (Note: a User ID certification signature packet is called a self-signature in
461
+ # RFC 4880)
462
+ # [
463
+ # (a Secret-Key packet) SecretKeyPacketV4 (contains a PublicKeyPacketV4),
464
+ # (a User ID packet) UserIdPacket with primary_userid set to true,
465
+ # (a User ID certification signature) SignaturePacket(subpackets: [
466
+ # type = PositiveCertification
467
+ # primary_userid = true
468
+ # ])
469
+ # ]
470
+ #
471
+ # RFC 4880 5.5.2
472
+ # OpenPGP implementations MUST create keys with version 4 format. V3 keys are
473
+ # deprecated; an implementation MUST NOT generate a V3 key, but MAY accept it.
474
+ key.version # must be 4
475
+ key.secret_key_packet # => its Secret-Key packet
476
+ key.userids # => [] with its User ID packets
477
+ key.userid_signatures # => [] of Signature Packets of its User ID packets
478
+ key.passphrase # sets the passphrase if non-blank
479
+ key.key_id # => key id of key
480
+ key.fingerprint # => fingerprint of key
481
+
482
+ key.key_length # length of key
483
+ # :rsa
484
+ # https://tools.ietf.org/html/rfc4880#section-13.5
485
+ # An implementation SHOULD NOT implement RSA keys of size less than 1024 bits.
486
+ #
487
+ # :dsa
488
+ # https://tools.ietf.org/html/rfc4880#section-13.6
489
+ # An implementation SHOULD NOT implement DSA keys of size less than 1024 bits.
490
+ # It MUST NOT implement a DSA key with a q size of less than 160 bits. DSA
491
+ # keys MUST also be a multiple of 64 bits, and the q size MUST be a multiple of
492
+ # 8 bits. The Digital Signature Standard (DSS) [FIPS186] specifies that DSA be
493
+ # used in one of the following ways:
494
+ # * 1024-bit key, 160-bit q, SHA-1, SHA-224, SHA-256, SHA-384, or SHA-512 hash
495
+ # * 2048-bit key, 224-bit q, SHA-224, SHA-256, SHA-384, or SHA-512 hash
496
+ # * 2048-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash
497
+ # * 3072-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash
498
+ #
499
+ # :elgamal # Unsupported in Rnp
500
+ # https://tools.ietf.org/html/rfc4880#section-13.7
501
+ # An implementation SHOULD NOT implement Elgamal keys of size less than 1024
502
+ # bits.
503
+
504
+ # The "self-signatures": adding requrirements to the key
505
+ userid = key.userids.first
506
+ self_sig = key.userid_signature(userid) # retrieve SignaturePacket for a given Userid
507
+ self_sig.key_flags = [:sign, :cert] # adds to a self-signature packet
508
+ self_sig.preferred_symmetric_algorithms = [
509
+ SymmetricKeyAlgorithm::Aes256,
510
+ SymmetricKeyAlgorithm::Aes192,
511
+ SymmetricKeyAlgorithm::Aes,
512
+ SymmetricKeyAlgorithm::Cast5
513
+ ]
514
+ self_sig.preferred_hash_algorithms = [
515
+ HashAlgorithm::Sha512,
516
+ HashAlgorithm::Sha384,
517
+ HashAlgorithm::Sha256,
518
+ HashAlgorithm::Sha224
519
+ ]
520
+ self_sig.preferred_compression_algorithms = [
521
+ CompressionAlgorithm::Zlib,
522
+ CompressionAlgorithm::Bzip2,
523
+ CompressionAlgorithm::Zip,
524
+ CompressionAlgorithm::Uncompressed
525
+ ]
526
+
527
+
528
+ # Adding a subkey
529
+ #
530
+
531
+ subkey = SecretSubkeyPacketV4.new
532
+ subkey.generate(
533
+ key_length: Integer,
534
+ public_key_algorithm: PublicKeyAlgorithm,
535
+ algorithm_params: { e: Integer }, # content is public_key_algorithm specific
536
+ userid: String || Userid,
537
+ hash_algorithm: HashAlgorithm,
538
+ symmetric_key_algorithm: SymmetricKeyAlgorithm
539
+ )
540
+ subkey_self_sig = Signature.new
541
+ subkey_self_sig.type = SignatureType::SubkeyBinding
542
+ subkey_self_sig.userid = userid
543
+ subkey_self_sig.key_flags = [:encrypt_data, :encrypt_comm, :cert]
544
+ subkey_self_sig.key_expiration_time = DateTime
545
+ subkey_self_sig.creation_time = DateTime
546
+
547
+ # Adds subkey to key
548
+ key.add_subkey(subkey)
549
+
550
+ key.subkeys # => [] of SecretSubkeyPacketV4
551
+ key.subkey_signature(subkey) # => SignaturePacket of subkey
552
+
553
+ # Delegate to self-signature
554
+ key.expiration_time # => time in seconds after key creation time
555
+ key.creation_time # => key generation date in DateTime of key
556
+ key.flags # => [] of key flags
557
+
558
+ # key is now:
559
+ # [
560
+ # (a Secret-Key packet) SecretKeyPacketV4 (contains a PublicKeyPacketV4),
561
+ # (a User ID packet) UserIdPacket with primary_userid set to true,
562
+ # (a User ID certification signature) SignaturePacket(subpackets: [
563
+ # type = PositiveCertification
564
+ # primary_userid = true
565
+ # key_flags = [:sign, :cert]
566
+ # preferred_symmetric_algorithms = [
567
+ # SymmetricKeyAlgorithm::Aes256,
568
+ # SymmetricKeyAlgorithm::Aes192,
569
+ # SymmetricKeyAlgorithm::Aes,
570
+ # SymmetricKeyAlgorithm::Cast5
571
+ # ]
572
+ # preferred_hash_algorithms = [
573
+ # HashAlgorithm::Sha512,
574
+ # HashAlgorithm::Sha384,
575
+ # HashAlgorithm::Sha256,
576
+ # HashAlgorithm::Sha224
577
+ # ]
578
+ # preferred_compression_algorithms = [
579
+ # CompressionAlgorithm::Zlib,
580
+ # CompressionAlgorithm::Bzip2,
581
+ # CompressionAlgorithm::Zip,
582
+ # CompressionAlgorithm::Uncompressed
583
+ # ]
584
+ # ])
585
+ # (a Subkey packet) SecretSubkeyPacketV4 (contains a PublicSubkeyPacketV4),
586
+ # (a subkey binding signature) SignaturePacket(subpackets: [
587
+ # type = SubkeyBindingSignature
588
+ # userid = userid
589
+ # key_flags = [:encrypt_data, :encrypt_comm, :cert]
590
+ # key_expiration_time = DateTime
591
+ # creation_time = DateTime
592
+ # ]),
593
+ # ]
594
+
595
+ # Public Key Algorithms
596
+ # https://tools.ietf.org/html/rfc4880#section-9.1
597
+ # https://tools.ietf.org/html/rfc6637#section-5
598
+ # NOTE: Rnp only supports generation of RSA keys (see rsa_generate_keypair()
599
+ # in openssl_crypto.c)
600
+ PublicKeyAlgorithms = [
601
+ PublicKeyAlgorithm::Rsa, # RFC4880, ID 1, RSA Encrypt or Sign [HAC]
602
+ PublicKeyAlgorithm::RsaEncryptOnly, # RFC4880, ID 2, RSA Encrypt-Only [HAC]
603
+ PublicKeyAlgorithm::RsaSignOnly, # RFC4880, ID 3, RSA Sign-Only [HAC]
604
+ PublicKeyAlgorithm::Elgamal, # RFC4880, ID 16, Elgamal (Encrypt-Only) [ELGAMAL] [HAC]
605
+ PublicKeyAlgorithm::Dsa, # RFC4880, ID 17, DSA (Digital Signature Algorithm) [FIPS186] [HAC]
606
+ PublicKeyAlgorithm::Ecdh, # RFC6637, ID 18, ECDH public key algorithm
607
+ PublicKeyAlgorithm::Ecdsa # RFC6637, ID 19, ECDSA public key algorithm
608
+ ]
609
+ # https://tools.ietf.org/html/rfc4880#section-13.5
610
+ # There are algorithm types for RSA Sign-Only, and RSA Encrypt-Only keys.
611
+ # These types are deprecated. The "key flags" subpacket in a signature is a
612
+ # much better way to express the same idea, and generalizes it to all
613
+ # algorithms. An implementation SHOULD NOT create such a key, but MAY
614
+ # interpret it.
615
+ # => Do not allow generation of :rsa_e or :rsa_s keys
616
+
617
+ # Symmetric Key Algorithms
618
+ # https://tools.ietf.org/html/rfc4880#section-9.2
619
+ # https://tools.ietf.org/html/rfc5581#section-3
620
+ # NOTE: Rnp only supports:
621
+ # { "cast5", PGP_SA_CAST5 },
622
+ # { "idea", PGP_SA_IDEA },
623
+ # { "aes128", PGP_SA_AES_128 },
624
+ # { "aes256", PGP_SA_AES_256 },
625
+ # { "camellia128", PGP_SA_CAMELLIA_128 },
626
+ # { "camellia256", PGP_SA_CAMELLIA_256 },
627
+ # { "tripledes", PGP_SA_TRIPLEDES },
628
+ # { NULL, 0 }
629
+ SymmetricKeyAlgorithms = [
630
+ SymmetricKeyAlgorithm::None, #RFC4880, ID 0, Plaintext or unencrypted data
631
+ SymmetricKeyAlgorithm::Idea, #RFC4880, ID 1, IDEA [IDEA]
632
+ SymmetricKeyAlgorithm::Tripledes, #RFC4880, ID 2, TripleDES (DES-EDE, [SCHNEIER] [HAC], 168 bit key derived from 192)
633
+ SymmetricKeyAlgorithm::Cast5, #RFC4880, ID 3, CAST5 (128 bit key, as per [RFC2144])
634
+ SymmetricKeyAlgorithm::Blowfish, #RFC4880, ID 4, Blowfish (128 bit key, 16 rounds) [BLOWFISH]
635
+ SymmetricKeyAlgorithm::Aes128, #RFC4880, ID 7, AES with 128-bit key [AES]
636
+ SymmetricKeyAlgorithm::Aes192, #RFC4880, ID 8, AES with 192-bit key
637
+ SymmetricKeyAlgorithm::Aes256, #RFC4880, ID 9, AES with 256-bit key
638
+ SymmetricKeyAlgorithm::Blowfish256, #RFC4880, ID 10, Twofish with 256-bit key [TWOFISH]
639
+ SymmetricKeyAlgorithm::Camellia128, #RFC4880, ID 11, Camellia with 128-bit key
640
+ SymmetricKeyAlgorithm::Camellia192, #RFC4880, ID 12, Camellia with 192-bit key
641
+ SymmetricKeyAlgorithm::Camellia256 #RFC4880, ID 13, Camellia with 256-bit key
642
+ ]
643
+
644
+ # Hash Algorithms
645
+ # https://tools.ietf.org/html/rfc4880#section-9
646
+ # NOTE: Rnp only supports
647
+ # case PGP_HASH_MD5:
648
+ # case PGP_HASH_SHA1:
649
+ # case PGP_HASH_SHA256:
650
+ # case PGP_HASH_SHA384:
651
+ # case PGP_HASH_SHA512:
652
+ # case PGP_HASH_SHA224:
653
+ HashAlgorithms = [
654
+ HashAlgorithms::Md5, # RFC4880, ID 1, MD5 [HAC] Text: "MD5",
655
+ HashAlgorithms::Sha1, # RFC4880, ID 2, SHA-1 [FIPS180] Text: "SHA1"
656
+ HashAlgorithms::Ripemd160, # RFC4880, ID 3, RIPE-MD/160 [HAC] Text: "RIPEMD160"
657
+ HashAlgorithms::Sha256, # RFC4880, ID 8, SHA256 [FIPS180] Text: "SHA256"
658
+ HashAlgorithms::Sha384, # RFC4880, ID 9, SHA384 [FIPS180] Text: "SHA384"
659
+ HashAlgorithms::Sha512, # RFC4880, ID 10, SHA512 [FIPS180] Text: "SHA512"
660
+ HashAlgorithms::Sha224 # RFC4880, ID 11, SHA224 [FIPS180] Text: "SHA224"
661
+ ]
662
+
663
+ # Compression Algorithms
664
+ # https://tools.ietf.org/html/rfc4880#section-9.3
665
+ # NOTE: Rnp supports all three
666
+ # case PGP_C_ZIP:
667
+ # case PGP_C_ZLIB:
668
+ # case PGP_C_BZIP2:
669
+ CompressionAlgorithms = [
670
+ CompressionAlgorithm::Uncompressed || :nil, # RFC4880, ID 0, Uncompressed
671
+ CompressionAlgorithm::Zip, # RFC4880, ID 1, ZIP [RFC1951]
672
+ CompressionAlgorithm::Zlib, # RFC4880, ID 2, ZLIB [RFC1950]
673
+ CompressionAlgorithm::Bzip2 # RFC4880, ID 3, BZip2 [BZ2]
674
+ ]
675
+
676
+
677
+ # Verifying a PGP message
678
+ public_key.verify(message.signature, message.content)
679
+ secret_key.verify(message.signature, message.content)
680
+
681
+ # USER ID methods
682
+ # A User ID is the 'name-addr' specified in RFC 2822 3.4
683
+ # https://tools.ietf.org/html/rfc2822#section-3.4
684
+ userid = key.userids.first
685
+ # Note: Rnp pgp_get_userid
686
+ userid = Rnp::Userid.new(address: "joshuac@mail.net", name: "Josiah Carberry")
687
+ userid.address # => address of user id
688
+ userid.name # => name of user id
689
+ userid.to_s # => "Josiah Carberry <joshuac@mail.net>"
690
+ userid.primary_userid # => RFC 4880 5.2.3.19 is this the Primary User ID of a key? Only when userid is associated with key.
691
+
692
+ key.userids << userid # adds Rnp::Userid packet to a Message
693
+ # Note: Rnp pgp_add_userid
694
+
695
+ # SIGNATURE METHODS
696
+ signature = Rnp::Signature.import("detached_ascii_pgp_signature")
697
+ signature.verify(key, data)
698
+
699
+ # MESSAGE METHODS
700
+ message = Rnp::OpenPgpMessage.new
701
+ # Importing from ASCII armored PGP message
702
+ message.import_ascii(File.read("ascii_armored_pgp_message.txt"))
703
+ # Importing unarmored content
704
+ message.import_raw(File.read("base64_portion_of_multipart_email.eml"))
705
+ message.packets # => [] of Rnp::Packet objects
706
+ message.signature # => signature of message in Rnp::Signature
707
+ message.signer_userid # => signer in Rnp::Userid
708
+ message.signed? # => is message signed?
709
+ message.encrypted? # => is message encrypted?
710
+ message.decrypt(key) # => decrypt content of message
711
+ message.content # => decrypted content of message
712
+
713
+ # Plaintext OpenPGP message
714
+ plaintext_data = File.read("plaintext.txt")
715
+ literal_message = LiteralMessage.new(plaintext_data) # automatically creates a LiteralDataPacket inside
716
+
717
+ # Signed OpenPGP message
718
+ message = SignedMessage.new(literal_message)
719
+ message.content = literal_message # alternative to above
720
+ message.key = SecretKey
721
+ message.sign # => SignedMessage [SignaturePacket, LiteralMessage]
722
+
723
+ # Or
724
+ message = OnePassSignedMessage.new(
725
+ signature_type: PositiveCertification,
726
+ hash_algorithm: HashAlgorithm,
727
+ public_key_algorithm: PublicKeyAlgorithm,
728
+ key: SecretKey || PublicKey,
729
+ content: literal_message
730
+ ) # => OnePassSignedMessage is an OpenPgpMessage
731
+ message.to_s # ASCII armored message
732
+
733
+ # Or
734
+ message = SignedMessage.new
735
+ # Automatically creates a LiteralMessage, which contains a Literal Data Packet
736
+ message.content = plaintext_data
737
+ message.key = SecretKey
738
+ message.signature_type = PositiveCertification
739
+ message.hash_algorithm = HashAlgorithm
740
+ message.public_key_algorithm = PublicKeyAlgorithm
741
+
742
+ # Encrypted OpenPGP message
743
+ message = EncryptedMessage.new
744
+ message.key = YourPublicKey
745
+ message.public_key_algorithm = PublicKeyAlgorithm
746
+ # Automatically creates this:
747
+ # EncryptedMessage (
748
+ # EncryptedSessionKeySequence (
749
+ # EncryptedSessionKey (
750
+ # PublicKeyEncryptedSessionKeyPacket
751
+ # ),
752
+ # EncryptedData (
753
+ # SymmetricallyEncryptedIntegrityProtectedDataPacket (
754
+ # LiteralPacket(plaintext_data)
755
+ # )
756
+ # )
757
+ # )
758
+ # )
759
+ message.content = plaintext_data
760
+
761
+
762
+ # ALGORITHM METHODS
763
+ algo = message.public_key_algorithm # => public key algorithm in Rnp::PublicKeyAlgorithm format
764
+ algo.name # => name of algo, e.g., RSA
765
+ algo.parameters # => parameters of algo used, e.g., RSA parameters (RFC 4880 Algorithm Specific Fields)
766
+