lib/rnp/misc.rb in rnp-1.0.4 vs lib/rnp/misc.rb in rnp-1.0.5

- old
+ new

@@ -1,9 +1,10 @@ # frozen_string_literal: true -# (c) 2018 Ribose Inc. +# (c) 2018-2020 Ribose Inc. +require "json" require 'ffi' require 'rnp/utils' require 'rnp/ffi/librnp' @@ -72,10 +73,17 @@ # # @param input [Input] the input to read data from # @param output [Output] the output to write the armored # data to. If nil, the result will be returned directly # as a String. + # @param type [String] the armor type. Valid values include: + # * message + # * public key + # * secret key + # * signature + # * cleartext + # * nil (try to guess the type) # @return [nil, String] def self.enarmor(input:, output: nil, type: nil) Output.default(output) do |output_| Rnp.call_ffi(:rnp_enarmor, input.ptr, output_.ptr, type) end @@ -110,12 +118,16 @@ # Get the version stamp of the rnp library as an unsigned # 32-bit integer. This number can be compared against other # stamps generated with {version_for}. # # @return [Integer] - def self.version - LibRnp.rnp_version + def self.version(str = nil) + if str.nil? + LibRnp.rnp_version + else + LibRnp.rnp_version_for(*str.split('.').map(&:to_i)) + end end # Encode the given major, minor, and patch numbers into a version # stamp. # @@ -142,7 +154,144 @@ # # @return [Integer] def self.version_patch(version) LibRnp.rnp_version_patch(version) end -end # class + # Retrieve the commit time of the latest commit. + # + # This will return 0 for release/non-master builds. + # + # @return [Integer] + def self.commit_time + LibRnp.rnp_version_commit_timestamp + end + + # Parse OpenPGP data to JSON. + # + # @param input [Input] the input to read the data + # @param mpi [Boolean] if true then MPIs will be included + # @param raw [Boolean] if true then raw bytes will be included + # @param grip [Boolean] if true then grips will be included + # @return [Array] + def self.parse(input:, mpi: false, raw: false, grip: false) + flags = 0 + flags |= LibRnp::RNP_JSON_DUMP_MPI if mpi + flags |= LibRnp::RNP_JSON_DUMP_RAW if raw + flags |= LibRnp::RNP_JSON_DUMP_GRIP if grip + pptr = FFI::MemoryPointer.new(:pointer) + Rnp.call_ffi(:rnp_dump_packets_to_json, input.ptr, flags, pptr) + begin + pjson = pptr.read_pointer + JSON.parse(pjson.read_string) unless pjson.null? + ensure + LibRnp.rnp_buffer_destroy(pjson) + end + end + + # Calculate s2k iterations + # + # @param hash [String] the hash algorithm to use + # @param msec [Integer] the desired number of milliseconds + # @return [Integer] + def self.s2k_iterations(hash:, msec:) + piters = FFI::MemoryPointer.new(:size_t) + Rnp.call_ffi(:rnp_calculate_iterations, hash, msec, piters) + piters.read(:size_t) + end + + # Enable debugging + # + # @param file [String] the file to enable debugging for (or nil for all) + # @return [void] + def self.enable_debug(file = nil) + Rnp.call_ffi(:rnp_enable_debug, file) + end + + # Disable previously-enabled debugging + # + # @return [void] + def self.disable_debug + Rnp.call_ffi(:rnp_disable_debug) + end + + # Guess the contents of an input + # + # @param input [Input] + # @return [String] the guessed content type (or 'unknown'), which may be + # used with {enarmor} + def self.guess_contents(input) + pptr = FFI::MemoryPointer.new(:pointer) + Rnp.call_ffi(:rnp_guess_contents, input.ptr, pptr) + begin + presult = pptr.read_pointer + presult.read_string unless presult.null? + ensure + LibRnp.rnp_buffer_destroy(presult) + end + end + + # Check if a specific feature is supported + # + # @param type [String] the type of feature ('symmetric algorithm', ...) + # @param name [String] the specific feature ('CAST5', ...) + # @return [Boolean] + def self.supports?(type, name) + presult = FFI::MemoryPointer.new(:bool) + Rnp.call_ffi(:rnp_supports_feature, type, name, presult) + presult.read(:bool) + end + + # Get a list of supported features (by type) + # + # @param type [String] the type of feature ('symmetric algorithm', ...) + # @return [Array] + def self.supported_features(type) + pptr = FFI::MemoryPointer.new(:pointer) + Rnp.call_ffi(:rnp_supported_features, type, pptr) + begin + presult = pptr.read_pointer + JSON.parse(presult.read_string) unless presult.null? + ensure + LibRnp.rnp_buffer_destroy(presult) + end + end + + # @api private + FEATURES = { + # Support for setting hash, creation, and expiration time for individual + # signatures in a sign operation. Older versions of rnp returned a + # "not implemented" error. + "per-signature-opts" => Rnp.version > Rnp.version("0.11.0") || + Rnp.commit_time >= 1546035818, + # Correct grip calculation for Elgamal/DSA keys. This was actually before + # the commit timestamp API was added, so this isn't accurate in one case. + "dsa-elg-grip-calc" => Rnp.version > Rnp.version("0.11.0") || + Rnp.commit_time >= 1538219020, + # Input reader callback signature was changed: + # ssize_t(void *app_ctx, void *buf, size_t len) + # bool(void *app_ctx, void *buf, size_t len, size_t *read) + "input-reader-cb-no-ssize_t" => Rnp.version >= Rnp.version("0.14.0") || + Rnp.commit_time >= 1585833163, + # Behavior on primary userid retrieveing was changed: + # Now userid is not considered as primary if it is revoked/expired/etc. + "primary-userid-must-be-valid" => Rnp.version >= Rnp.version("0.14.0") || + Rnp.commit_time >= 1605875599, + # Behavior was changed in v0.15.2 (issue #1509): + # userid is valid even if key expiration in userid expiration expires key. + "relax-userid-validity-checks" => Rnp.version >= Rnp.version("0.15.2") || + Rnp.commit_time >= 1624526708, + # Behavior on default key expiration time was changed: + # Now default key expiration time is 2 years + "default-key-expiration-2-years" => Rnp.version >= Rnp.version("0.16.1") || + Rnp.commit_time >= 1645578982, + # Behaviour on signature validation was changed: + # Now at least one valid signature is required for success + "require-single-valid-signature" => Rnp.version >= Rnp.version("0.16.1") || + Rnp.commit_time >= 1661781294, + }.freeze + + def self.has?(feature) + raise ArgumentError unless FEATURES.include?(feature) + FEATURES[feature] + end +end # class