class String
Public Class Methods
random_byte()
click to toggle source
Obviously not cryptographically secure
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 11 def self.random_byte (0..255).to_a.sample.chr end
random_bytes(n)
click to toggle source
Not cryptographically secure
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 4 def self.random_bytes(n) n.times.with_object("") do |_, memo| memo << random_byte end end
Public Instance Methods
^(other)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 55 def ^(other) if length != other.length raise ArgumentError.new("Must be same lengths, self: #{self.bytesize}, other: #{other.bytesize}") end each_byte.with_index.with_object("") do |(byte, i), ret| ret << (byte.ord ^ other[i].ord) end end
bitflip(bit_index, byte_index: 0)
click to toggle source
Bitstring is indexed as a normal string, ie:
'd' = 0x64 = 01100100
01234567
'd'.bitflip(7) => 'e'
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 212 def bitflip(bit_index, byte_index: 0) byte_offset, bit_offset = bit_index.divmod(8) byte_offset += byte_index target = self.dup target[byte_offset] = (target[byte_offset].ord ^ (1 << (7-bit_offset))).chr target end
contains_duplicate_blocks?(blocksize = CryptoToolchain::AES_BLOCK_SIZE)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 238 def contains_duplicate_blocks?(blocksize = CryptoToolchain::AES_BLOCK_SIZE) _blocks = in_blocks(blocksize) _blocks.length > _blocks.uniq.length end
Also aliased as: is_ecb_encrypted?
decrypt_cbc(key: , iv: , blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128', strip_padding: true)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 175 def decrypt_cbc(key: , iv: , blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128', strip_padding: true) _blocks = in_blocks(blocksize) decrypted = _blocks.each_with_object("").with_index do |(block, memo), i| dec = OpenSSL::Cipher.new("#{cipher}-ECB") dec.decrypt dec.key = key dec.padding = 0 unciphered = dec.update(block) + dec.final chain_block = i == 0 ? iv : _blocks[i - 1] memo << (unciphered ^ chain_block) end if strip_padding decrypted.without_pkcs7_padding(blocksize) else decrypted end end
decrypt_ecb(key: , blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128')
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 151 def decrypt_ecb(key: , blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128') in_blocks(blocksize).each_with_object("") do |block, memo| dec = OpenSSL::Cipher.new("#{cipher}-ECB") dec.decrypt dec.key = key dec.padding = 0 plain = dec.update(block) + dec.final memo << plain end.without_pkcs7_padding(blocksize) end
encrypt_cbc(key: , iv: , blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128')
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 193 def encrypt_cbc(key: , iv: , blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128') _blocks = pad_pkcs7(blocksize).in_blocks(blocksize) _blocks.each_with_object("").with_index do |(block, memo), i| chain_block = i == 0 ? iv : memo[(blocksize * -1)..-1] intermediate = block ^ chain_block enc = OpenSSL::Cipher.new("#{cipher}-ECB") enc.encrypt enc.key = key enc.padding = 0 crypted = enc.update(intermediate) + enc.final memo << crypted end end
encrypt_ctr(key: , nonce: , cipher: 'AES-128', start_counter: 0)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 224 def encrypt_ctr(key: , nonce: , cipher: 'AES-128', start_counter: 0) each_byte.with_index(start_counter).with_object("") do |(byte, i), memo| ctr = i / 16 ctr_params = [nonce, ctr].pack("Q<Q<") enc = OpenSSL::Cipher.new("#{cipher}-ECB") enc.encrypt enc.key = key enc.padding = 0 keystream = enc.update(ctr_params) + enc.final memo << (byte.chr ^ keystream[i % 16]) end end
Also aliased as: decrypt_ctr
encrypt_ecb(key: , blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128')
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 162 def encrypt_ecb(key: , blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128') self.pad_pkcs7(blocksize).in_blocks(blocksize).each_with_object("").with_index do |(block, memo), i| enc = OpenSSL::Cipher.new("#{cipher}-ECB") enc.encrypt enc.key = key enc.padding = 0 plain = enc.update(block) + enc.final memo << plain end end
from_base64(strict: true)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 43 def from_base64(strict: true) if strict begin Base64.strict_decode64(self) rescue ArgumentError Base64.decode64(self) end else Base64.decode64(self) end end
from_hex()
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 15 def from_hex raise StandardError.new("Not hex") unless hex? [self].pack("H*") end
hamming_distance(other)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 80 def hamming_distance(other) (self ^ other).to_bits.count("1") end
hex?()
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 24 def hex? self !~ /[^0-9a-f]/i end
in_blocks(blocksize = CryptoToolchain::AES_BLOCK_SIZE)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 68 def in_blocks(blocksize = CryptoToolchain::AES_BLOCK_SIZE) bytes.map(&:chr).each_slice(blocksize).map(&:join) || [""] end
is_ecb_encrypted?(blocksize = CryptoToolchain::AES_BLOCK_SIZE)
Alias for: contains_duplicate_blocks?
is_pkcs1_5_padded?(bits)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 135 def is_pkcs1_5_padded?(bits) self[0..1] == "\x00\x02" end
is_pkcs7_padded?(blocksize = CryptoToolchain::AES_BLOCK_SIZE)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 139 def is_pkcs7_padded?(blocksize = CryptoToolchain::AES_BLOCK_SIZE) return in_blocks(blocksize).last.is_block_pkcs7_padded?(blocksize) end
pad_pkcs1_5(bits)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 126 def pad_pkcs1_5(bits) len = bits / 8 if self.bytesize > len - 11 raise ArgumentError.new("String #{self.inspect} is too long to pad with PKCS#1v1.5, length: #{self.bytesize}") end padstring = (len - 3 - self.bytesize).times.with_object("") { |_, memo| memo << rand(1..255).chr } "\x00\x02#{padstring}\x00#{self}" end
pad_pkcs7(blocksize = CryptoToolchain::AES_BLOCK_SIZE)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 116 def pad_pkcs7(blocksize = CryptoToolchain::AES_BLOCK_SIZE) _blocks = in_blocks(blocksize) pad_num = blocksize - _blocks.last.bytesize if pad_num == 0 "#{self}#{blocksize.chr * blocksize}" else "#{self}#{pad_num.chr * pad_num}" end end
potential_repeating_xor_keys(potential_keysizes: self.potential_repeating_xor_keysizes)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 96 def potential_repeating_xor_keys(potential_keysizes: self.potential_repeating_xor_keysizes) potential_keysizes.map do |keysize| arr = self.in_blocks(keysize) transposed = (0...keysize).each_with_object([]) do |i, memo| memo << arr.map { |row| row[i] }.join end key = transposed.each_with_object("") do |str, memo| memo << CryptoToolchain::Tools.detect_single_character_xor(str) end key end end
potential_repeating_xor_keysizes(take: 3, min: 2, max: 40)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 90 def potential_repeating_xor_keysizes(take: 3, min: 2, max: 40) (min..max).sort_by do |size| normalized_hamming_distance(self.in_blocks(size)) / size.to_f end.take(take) end
repeat_to(len)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 72 def repeat_to(len) ljust(len, self) end
score()
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 64 def score scan(/[etaoin shrdlu]/i).size end
snakecase()
click to toggle source
Thanks Ruby Facets!
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 245 def snakecase gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). gsub(/([a-z\d])([A-Z])/,'\1_\2'). tr('-', '_'). gsub(/\s/, '_'). gsub(/__+/, '_'). downcase end
Also aliased as: snake_case, underscore
swap_endian()
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 28 def swap_endian raise ArgumentError.new("Bytesize must be multiple of 4") unless bytesize % 4 == 0 unpack("L<*").pack("L>*") end
Also aliased as: swap_endianness
to_base64(strict: true)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 35 def to_base64(strict: true) if strict Base64.strict_encode64(self) else Base64.encode64(self) end end
to_bits()
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 84 def to_bits self.unpack("B*").first end
to_hex()
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 20 def to_hex unpack("H*").first end
to_number()
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 76 def to_number to_hex.to_i(16) end
unique_blocks(blocksize = CryptoToolchain::AES_BLOCK_SIZE)
click to toggle source
unique blocks. block size is in bytes
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 110 def unique_blocks(blocksize = CryptoToolchain::AES_BLOCK_SIZE) in_blocks(blocksize).each_with_object({}) do |block, found| found[block] ||= true end.keys end
without_pkcs7_padding(blocksize = CryptoToolchain::AES_BLOCK_SIZE, raise_error: false)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 143 def without_pkcs7_padding(blocksize = CryptoToolchain::AES_BLOCK_SIZE, raise_error: false) if !is_pkcs7_padded?(blocksize) raise ArgumentError.new("Not PKCS7 padded") unless is_pkcs7_padded?(blocksize) if raise_error return self end self[0..(bytesize - (1 + bytes.last))] end
Protected Instance Methods
is_block_pkcs7_padded?(blocksize = CryptoToolchain::AES_BLOCK_SIZE)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 258 def is_block_pkcs7_padded?(blocksize = CryptoToolchain::AES_BLOCK_SIZE) return true if self == blocksize.chr * blocksize (1...blocksize).each do |padding| return true if self[(blocksize - padding)..-1] == padding.chr * padding end false end
normalized_hamming_distance(blocks)
click to toggle source
# File lib/crypto_toolchain/extensions/string_extensions.rb, line 266 def normalized_hamming_distance(blocks) raise ArgumentError.new("arg should be an array") unless blocks.is_a?(Array) ( blocks[0].hamming_distance(blocks[1]) + blocks[0].hamming_distance(blocks[2]) + blocks[0].hamming_distance(blocks[3]) ) / 3.0 end