Class: String
- Defined in:
- lib/crypto_toolchain/extensions/string_extensions.rb
Class Method Summary collapse
-
.random_byte ⇒ Object
Obviously not cryptographically secure.
-
.random_bytes(n) ⇒ Object
Not cryptographically secure.
Instance Method Summary collapse
- #^(other) ⇒ Object
-
#bitflip(bit_index, byte_index: 0) ⇒ Object
(also: #bit_flip, #flipbit, #flip_bit, #flip)
Bitstring is indexed as a normal string, ie:.
- #contains_duplicate_blocks?(blocksize = CryptoToolchain::AES_BLOCK_SIZE) ⇒ Boolean (also: #is_ecb_encrypted?)
- #decrypt_cbc(key:, iv:, blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128', strip_padding: true) ⇒ Object
- #decrypt_ecb(key:, blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128') ⇒ Object
- #encrypt_cbc(key:, iv:, blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128') ⇒ Object
- #encrypt_ctr(key:, nonce:, cipher: 'AES-128', start_counter: 0) ⇒ Object (also: #decrypt_ctr)
- #encrypt_ecb(key:, blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128') ⇒ Object
- #from_base64(strict: true) ⇒ Object
- #from_hex ⇒ Object
- #hamming_distance(other) ⇒ Object
- #hex? ⇒ Boolean
- #in_blocks(blocksize = CryptoToolchain::AES_BLOCK_SIZE) ⇒ Object
- #is_pkcs1_5_padded?(bits) ⇒ Boolean
- #is_pkcs7_padded?(blocksize = CryptoToolchain::AES_BLOCK_SIZE) ⇒ Boolean
- #pad_pkcs1_5(bits) ⇒ Object
- #pad_pkcs7(blocksize = CryptoToolchain::AES_BLOCK_SIZE) ⇒ Object
- #potential_repeating_xor_keys(potential_keysizes: self.potential_repeating_xor_keysizes) ⇒ Object
- #potential_repeating_xor_keysizes(take: 3, min: 2, max: 40) ⇒ Object
- #repeat_to(len) ⇒ Object
- #score ⇒ Object
-
#snakecase ⇒ Object
(also: #snake_case, #underscore)
Thanks Ruby Facets!.
- #swap_endian ⇒ Object (also: #swap_endianness)
- #to_base64(strict: true) ⇒ Object
- #to_bits ⇒ Object (also: #bitstring, #bits)
- #to_hex ⇒ Object
- #to_number ⇒ Object
-
#unique_blocks(blocksize = CryptoToolchain::AES_BLOCK_SIZE) ⇒ Object
unique blocks.
- #without_pkcs7_padding(blocksize = CryptoToolchain::AES_BLOCK_SIZE, raise_error: false) ⇒ Object
Class Method Details
.random_byte ⇒ Object
Obviously not cryptographically secure
11 12 13 |
# File 'lib/crypto_toolchain/extensions/string_extensions.rb', line 11 def self.random_byte (0..255).to_a.sample.chr end |
.random_bytes(n) ⇒ Object
Not cryptographically secure
4 5 6 7 8 |
# 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 |
Instance Method Details
#^(other) ⇒ Object
55 56 57 58 59 60 61 62 |
# 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) ⇒ Object Also known as: bit_flip, flipbit, flip_bit, flip
Bitstring is indexed as a normal string, ie:
‘d’ = 0x64 = 01100100
01234567
‘d’.bitflip(7) => ‘e’
212 213 214 215 216 217 218 |
# 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) ⇒ Boolean Also known as: is_ecb_encrypted?
238 239 240 241 |
# 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 |
#decrypt_cbc(key:, iv:, blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128', strip_padding: true) ⇒ Object
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# 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') ⇒ Object
151 152 153 154 155 156 157 158 159 160 |
# 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') ⇒ Object
193 194 195 196 197 198 199 200 201 202 203 204 205 |
# 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) ⇒ Object Also known as: decrypt_ctr
224 225 226 227 228 229 230 231 232 233 234 235 |
# 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 |
#encrypt_ecb(key:, blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128') ⇒ Object
162 163 164 165 166 167 168 169 170 171 172 173 |
# 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) ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 |
# 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 ⇒ Object
15 16 17 18 |
# 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) ⇒ Object
80 81 82 |
# File 'lib/crypto_toolchain/extensions/string_extensions.rb', line 80 def hamming_distance(other) (self ^ other).to_bits.count("1") end |
#hex? ⇒ Boolean
24 25 26 |
# 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) ⇒ Object
68 69 70 |
# 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_pkcs1_5_padded?(bits) ⇒ Boolean
135 136 137 |
# 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) ⇒ Boolean
139 140 141 |
# 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) ⇒ Object
126 127 128 129 130 131 132 133 |
# 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) ⇒ Object
116 117 118 119 120 121 122 123 124 |
# 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) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 |
# 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) ⇒ Object
90 91 92 93 94 |
# 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) ⇒ Object
72 73 74 |
# File 'lib/crypto_toolchain/extensions/string_extensions.rb', line 72 def repeat_to(len) ljust(len, self) end |
#score ⇒ Object
64 65 66 |
# File 'lib/crypto_toolchain/extensions/string_extensions.rb', line 64 def score scan(/[etaoin shrdlu]/i).size end |
#snakecase ⇒ Object Also known as: snake_case, underscore
Thanks Ruby Facets!
245 246 247 248 249 250 251 252 |
# 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 |
#swap_endian ⇒ Object Also known as: swap_endianness
28 29 30 31 |
# 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 |
#to_base64(strict: true) ⇒ Object
35 36 37 38 39 40 41 |
# 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 ⇒ Object Also known as: bitstring, bits
84 85 86 |
# File 'lib/crypto_toolchain/extensions/string_extensions.rb', line 84 def to_bits self.unpack("B*").first end |
#to_hex ⇒ Object
20 21 22 |
# File 'lib/crypto_toolchain/extensions/string_extensions.rb', line 20 def to_hex unpack("H*").first end |
#to_number ⇒ Object
76 77 78 |
# 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) ⇒ Object
unique blocks. block size is in bytes
110 111 112 113 114 |
# 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) ⇒ Object
143 144 145 146 147 148 149 |
# 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 |