lib/ccrypto/ruby/engines/cipher_engine.rb in ccrypto-ruby-0.1.0 vs lib/ccrypto/ruby/engines/cipher_engine.rb in ccrypto-ruby-0.1.1
- old
+ new
@@ -9,78 +9,199 @@
include TeLogger::TeLogHelper
teLogger_tag :r_cipher_eng
+ NotAbleToFigureOutOnOpenSSLv2 = [
+ "aes-128-cbc-hmac-sha1",
+ "aes-256-cbc-hmac-sha1",
+ "aes-128-cbc-hmac-sha256",
+ "aes-256-cbc-hmac-sha256",
+ "aes-128-ccm",
+ "aes-192-ccm",
+ "aes-256-ccm",
+ "aria-128-ccm",
+ "aria-192-ccm",
+ "aria-256-ccm",
+ "rc4-hmac-md5"
+ ]
+
+ #CherryPick = [
+ # "aes-128-cbc", "aes-192-cbc", "aes-256-cbc",
+ # "aes-128-gcm", "aes-192-gcm", "aes-256-gcm",
+ # "aria-128-cbc","aria-192-cbc", "aria-256-cbc",
+ # "aria-128-gcm","aria-192-gcm", "aria-256-gcm",
+ # "camellia-128-cbc","camellia-192-cbc", "camellia-256-cbc",
+ # "camellia-128-ctr","camellia-192-ctr", "camellia-256-ctr",
+ # "chacha20-poly1305"
+ #]
+
def self.supported_ciphers
if @sCipher.nil?
- @sCipher = OpenSSL::Cipher.ciphers
+ @sCipherStr = []
+ @sCipher = []
+
+ OpenSSL::Cipher.ciphers.each do |c|
+
+ teLogger.debug "found cipher : #{c}"
+ next if c =~ /^id-\w*/
+ teLogger.debug "loading cipher : #{c}"
+
+ if OpenSSL::VERSION < "3.0.0" and NotAbleToFigureOutOnOpenSSLv2.include?(c)
+ teLogger.debug "Running on OpenSSL < v3. Skipping cipher #{c}"
+ next
+ end
+
+ begin
+ co = OpenSSL::Cipher.new(c)
+ #teLogger.debug "Algo #{c} : authenticated? #{co.authenticated?}"
+ #p co.methods.sort
+ cc = c.split("-")
+ @sCipherStr << c
+
+ algo = cc.first
+ exclusion = ["chacha20","sm4"]
+ # to find string like aes128, aes256 from openssl
+ if not exclusion.include?(algo) and (algo =~ /[0-9]/) != nil
+ # match the one before the numbers
+ algo = $`
+ end
+
+ native = { algo_str: c }
+ keysize = co.key_len*8
+ opts = { keysize: keysize, ivLength: co.iv_len, authMode: co.authenticated? }
+
+ if c.downcase =~ /\w*(gcm|ecb|cbc|cfb|cfb1|cfb8|ofb|ctr|ccm|xts|wrap|poly1305)/
+ teLogger.debug "Mode extraction : #{$&} / #{$'}"
+ # after the match
+ if not_empty?($')
+ opts[:mode] = "#{$&}#{$'}"
+ else
+ # the match
+ opts[:mode] = $&.downcase
+ end
+ else
+ # no mode defined. Seems defaulted to cbc according to document
+ if co.authenticated?
+ opts[:mode] = "gcm"
+ else
+ opts[:mode] = "cbc"
+ end
+ end
+
+ teLogger.debug "Mode : #{opts[:mode]}"
+ if opts[:mode] == :xts.to_s
+ opts[:min_input_length] = 16
+ elsif opts[:mode] == "cbc-hmac-sha1"
+ opts[:min_input_length] = 16
+ opts[:mandatory_block_size] = 16
+ elsif opts[:mode] == "cbc-hmac-sha256"
+ opts[:min_input_length] = 32
+ opts[:mandatory_block_size] = 32
+ elsif opts[:mode] == "wrap"
+ case algo
+ when "aes"
+ case keysize
+ when 128
+ opts[:min_input_length] = 16
+ opts[:mandatory_block_size] = 8
+ when 192
+ opts[:min_input_length] = 24
+ opts[:mandatory_block_size] = 8
+ when 256
+ opts[:min_input_length] = 32
+ opts[:mandatory_block_size] = 8
+ end
+ when "des3", "des"
+ opts[:min_input_length] = 8
+ opts[:mandatory_block_size] = 8
+ end
+ end
+
+ conf = Ccrypto::CipherConfig.new(algo, opts)
+ conf.native_config = native
+
+ Ccrypto::SupportedCipherList.instance.register(conf)
+
+ @sCipher << conf
+ rescue OpenSSL::Cipher::CipherError => ex
+ teLogger.debug "Algo '#{c}' hit error : #{ex.message}"
+ end
+ end
end
@sCipher
end
+ def self.get_cipher(algo, keysize = nil, mode = nil)
+ supported_ciphers if Ccrypto::SupportedCipherList.instance.algo_count == 0
+
+ if is_empty?(algo)
+ []
+ elsif is_empty?(keysize) and is_empty?(mode)
+ teLogger.debug "get_cipher algo #{algo} only"
+ Ccrypto::SupportedCipherList.instance.find_algo(algo)
+ elsif is_empty?(mode) and not_empty?(keysize)
+ teLogger.debug "get_cipher algo #{algo} keysize #{keysize}"
+ Ccrypto::SupportedCipherList.instance.find_algo_keysize(algo, keysize)
+ elsif not_empty?(mode) and is_empty?(keysize)
+ teLogger.debug "get_cipher algo #{algo} mode #{mode}"
+ Ccrypto::SupportedCipherList.instance.find_algo_mode(algo, mode)
+ elsif not_empty?(keysize) and not_empty?(mode)
+ teLogger.debug "get_cipher #{algo}/#{keysize}/#{mode}"
+ Ccrypto::SupportedCipherList.instance.find_algo_keysize_mode(algo, keysize, mode)
+ end
+ end
+ class << self
+ alias_method :get_cipher_config, :get_cipher
+ end
+
+ def self.supported_cipher_list
+ supported_ciphers if Ccrypto::SupportedCipherList.instance.algo_count == 0
+ Ccrypto::SupportedCipherList.instance
+ end
+
def self.is_supported_cipher?(c)
case c
when String
- supported_ciphers.include?(c)
- when Hash
- spec = to_openssl_spec(c)
- begin
- OpenSSL::Cipher.new(spec)
- true
- rescue Exception => ex
+ supported_ciphers if @sCipherStr.nil?
+
+ if not @sCipherStr.nil?
+ @sCipherStr.include?(C)
+ else
false
end
+ when Ccrypto::CipherConfig
+ @sCipher.include?(c)
else
raise Ccrypto::CipherEngineException, "Unsupported input #{c} to check supported cipher"
end
end
def self.to_openssl_spec(spec)
- res = []
- teLogger.debug "to_openssl_spec #{spec}"
- case spec.algo
- when :blowfish
- res << "bf"
+ case spec
+ when Ccrypto::CipherConfig
+ spec.native_config[:algo_str]
+ #@sCipher[spec]
else
- res << spec.algo
+ raise Ccrypto::Error, "Unknown spec #{spec.inspect}"
end
- res << spec.keysize if not_empty?(spec.keysize) and spec.keysize.to_i > 0 and not spec.is_algo?(:chacha20) and not spec.is_algo?(:seed) and not spec.is_algo?(:sm4) and not spec.is_algo?(:blowfish)
+ end # self.to_openssl_spec(spec)
- res << spec.mode
-
- teLogger.debug "to_openssl_spec #{res}"
-
- res.join("-")
-
- end
-
def initialize(*args, &block)
+
@spec = args.first
- #teLogger = TteLogger.new
- teLogger.debug "Cipher spec : #{@spec}"
+ raise Ccrypto::CipherEngineException, "Not supported cipher spec #{@spec.class}" if not @spec.is_a?(Ccrypto::CipherConfig)
- begin
- case @spec
- #when String
- # @cipher = OpenSSL::Cipher.new(@spec)
- when Ccrypto::CipherEngineConfig
- @cipher = OpenSSL::Cipher.new(@spec.provider_config)
- when Ccrypto::DirectCipherConfig
- @cipher = OpenSSL::Cipher.new(self.class.to_openssl_spec(@spec))
- else
- raise Ccrypto::CipherEngineException, "Not supported cipher init type #{@spec.class}"
- end
- rescue OpenSSL::Cipher::CipherError, RuntimeError => ex
- raise Ccrypto::CipherEngineException, ex
- end
+ teLogger.debug "Cipher spec : #{@spec} (Native algo : #{@spec.native_config[:algo_str]})"
+ @cipher = OpenSSL::Cipher.new(@spec.native_config[:algo_str])
+
case @spec.cipherOps
when :encrypt, :enc
teLogger.debug "Operation encrypt"
@cipher.encrypt
when :decrypt, :dec
@@ -92,15 +213,15 @@
if @spec.has_iv?
teLogger.debug "IV from spec"
@cipher.iv = @spec.iv
- teLogger.debug "IV : #{to_hex(@spec.iv)}"
+ teLogger.debug "IV : #{to_hex(@spec.iv)} / #{@spec.iv.length}"
else
teLogger.debug "Generate random IV"
@spec.iv = @cipher.random_iv
- teLogger.debug "IV : #{to_hex(@spec.iv)}"
+ teLogger.debug "IV : #{to_hex(@spec.iv)} / #{@spec.iv.length}"
end
if @spec.has_key?
teLogger.debug "Key from spec"
@@ -116,54 +237,92 @@
teLogger.debug "Generate random Key"
@spec.key = @cipher.random_key
end
- if @spec.is_mode?(:gcm)
+ if @spec.is_mode?(:ccm)
+ #if not_empty?(@spec.auth_data)
+ if @spec.is_encrypt_cipher_mode?
+ teLogger.debug "Setting ccm plaintext data length (ccm mode) [#{@spec.plaintext_length}]"
+ @cipher.ccm_data_len = @spec.plaintext_length
+ teLogger.debug "Setting auth data (ccm mode)"
+ @cipher.auth_data = @spec.auth_data.nil? ? "" : @spec.auth_data
+ teLogger.debug "Setting auth tag len (ccm mode)"
+ @cipher.auth_tag_len = 12
+ elsif @spec.is_decrypt_cipher_mode?
+ teLogger.debug "Setting ccm cipher data length (ccm mode) #{@spec.ciphertext_length}"
+ @cipher.ccm_data_len = @spec.ciphertext_length
+ teLogger.debug "Setting auth data (ccm mode)"
+ @cipher.auth_data = @spec.auth_data.nil? ? "" : @spec.auth_data
+ teLogger.debug "Setting auth tag (ccm mode)"
+ @cipher.auth_tag = @spec.auth_tag
+ end
+ #end
- if not_empty?(@spec.auth_data)
- teLogger.debug "Setting auth data"
+
+ elsif @spec.is_auth_mode_cipher?
+
+ if not_empty?(@spec.auth_data) and not ((@spec.is_mode?("cbc-hmac-sha1") or @spec.is_mode?("cbc-hmac-sha256")))
+ teLogger.debug "Setting auth data (generic mode)"
@cipher.auth_data = @spec.auth_data
- end
- if not_empty?(@spec.auth_tag)
- raise CipherEngineException, "Tag length of 16 bytes is expected" if @spec.auth_tag.bytesize != 16
- teLogger.debug "Setting auth tag"
- @cipher.auth_tag = @spec.auth_tag
+ if @spec.is_decrypt_cipher_mode?
+ teLogger.debug "Setting auth tag (generic mode)"
+ raise CipherEngineException, "Tag length of 16 bytes is expected" if @spec.auth_tag.bytesize != 16 and @spec.is_mode?(:gcm)
+ @cipher.auth_tag = @spec.auth_tag
+ end
end
end
- end
+ end # initialize
+
def update(val)
- @cipher.update(val)
+ if not_empty?(val)
+ res = @cipher.update(val)
+ teLogger.debug "(update) Written #{val.length} bytes"
+ res
+ end
end
def final(val = nil)
res = []
begin
if not_empty?(val)
- res << @cipher.update(val)
+ teLogger.debug "(final) Written #{val.length} bytes"
+ res << @cipher.update(val)
end
res << @cipher.final
+ teLogger.debug "(final) cipher finalized"
rescue Exception => ex
+ teLogger.error self
+ teLogger.error ex.backtrace.join("\n")
raise CipherEngineException, ex
end
- if @spec.is_mode?(:gcm) and @spec.is_encrypt_cipher_mode?
- @spec.auth_tag = @cipher.auth_tag
+ #if @spec.is_mode?(:gcm)
+ if @spec.is_auth_mode_cipher? and @spec.is_encrypt_cipher_mode?
+ @spec.auth_tag = @cipher.auth_tag
end
res.join
end
def reset
@cipher.reset
+ end
+
+ def method_missing(mtd, *args, &block)
+ if not @cipher.nil?
+ @cipher.send(mtd, *args, &block)
+ else
+ super
+ end
end
end
end
end