rnp 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+