bip_mnemonic 0.0.2 → 0.0.3
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.
- checksums.yaml +5 -5
 - data/lib/bip_mnemonic.rb +44 -29
 - metadata +7 -8
 - data/lib/pbkdf2.rb +0 -118
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 2 
     | 
    
         
            +
            SHA256:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 866130921b342eab2a72f02b33e659e1a644b6a832d13e31af08bcbcd209f9ea
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 8d2581038cc68b6ac8588b7274f425966bcb3d7078ee63638a1bc7a29893c3f1
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 9e3416ed270b6c5679bf406cbaae6c67f29e30991a15adcd129e6d07ca9e0234b8d0f5c776d26d5e5114d7e3a76a9abfe70d934cf6ee7176c570e3ef0168e2b7
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: c8ea91e39855b9604d49ecbbb285c6916f2db3bd8135bd1ac4860010e1cdd84c619ea5b7e88c9454fe87da83275176a5e06613e4ac5dba298436bb19228bd985
         
     | 
    
        data/lib/bip_mnemonic.rb
    CHANGED
    
    | 
         @@ -1,51 +1,66 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require ' 
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            require 'openssl'
         
     | 
| 
       3 
2 
     | 
    
         
             
            class BipMnemonic
         
     | 
| 
       4 
     | 
    
         
            -
              VERSION =  
     | 
| 
      
 3 
     | 
    
         
            +
              VERSION = '0.0.3'.freeze
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
       5 
5 
     | 
    
         
             
              def self.to_mnemonic(options)
         
     | 
| 
       6 
6 
     | 
    
         
             
                options ||= {}
         
     | 
| 
       7 
7 
     | 
    
         
             
                bits = options[:bits] || 128
         
     | 
| 
       8 
     | 
    
         
            -
                 
     | 
| 
       9 
     | 
    
         
            -
                   
     | 
| 
       10 
     | 
    
         
            -
                  entropy_bytes = [options[:entropy]].pack("H*")
         
     | 
| 
      
 8 
     | 
    
         
            +
                if options[:entropy].nil?
         
     | 
| 
      
 9 
     | 
    
         
            +
                  entropy_bytes = OpenSSL::Random.random_bytes(bits / 8)
         
     | 
| 
       11 
10 
     | 
    
         
             
                else
         
     | 
| 
       12 
     | 
    
         
            -
                   
     | 
| 
      
 11 
     | 
    
         
            +
                  raise ArgumentError, 'Entropy is empty' if options[:entropy].empty?
         
     | 
| 
      
 12 
     | 
    
         
            +
                  entropy_bytes = [options[:entropy]].pack('H*')
         
     | 
| 
       13 
13 
     | 
    
         
             
                end
         
     | 
| 
       14 
     | 
    
         
            -
                entropy_binary = entropy_bytes.unpack( 
     | 
| 
      
 14 
     | 
    
         
            +
                entropy_binary = entropy_bytes.unpack('B*').first
         
     | 
| 
       15 
15 
     | 
    
         
             
                seed_binary = entropy_binary + checksum(entropy_binary)
         
     | 
| 
       16 
     | 
    
         
            -
                words_array = File.readlines( 
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
      
 16 
     | 
    
         
            +
                words_array = File.readlines(
         
     | 
| 
      
 17 
     | 
    
         
            +
                  File.join(
         
     | 
| 
      
 18 
     | 
    
         
            +
                    File.dirname(File.expand_path(__FILE__)), '../words/english.txt'
         
     | 
| 
      
 19 
     | 
    
         
            +
                  )
         
     | 
| 
      
 20 
     | 
    
         
            +
                ).map(&:strip)
         
     | 
| 
      
 21 
     | 
    
         
            +
                seed_binary.chars
         
     | 
| 
      
 22 
     | 
    
         
            +
                           .each_slice(11)
         
     | 
| 
      
 23 
     | 
    
         
            +
                           .map(&:join)
         
     | 
| 
      
 24 
     | 
    
         
            +
                           .map { |item| item.to_i(2) }
         
     | 
| 
      
 25 
     | 
    
         
            +
                           .map { |i| words_array[i] }
         
     | 
| 
      
 26 
     | 
    
         
            +
                           .join(' ')
         
     | 
| 
       18 
27 
     | 
    
         
             
              end
         
     | 
| 
       19 
28 
     | 
    
         | 
| 
       20 
29 
     | 
    
         
             
              def self.to_entropy(options)
         
     | 
| 
       21 
30 
     | 
    
         
             
                options ||= {}
         
     | 
| 
       22 
     | 
    
         
            -
                raise ArgumentError,  
     | 
| 
       23 
     | 
    
         
            -
                raise ArgumentError,  
     | 
| 
       24 
     | 
    
         
            -
                words_array = File.readlines( 
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
      
 31 
     | 
    
         
            +
                raise ArgumentError, 'Mnemonic not set' if options[:mnemonic].nil?
         
     | 
| 
      
 32 
     | 
    
         
            +
                raise ArgumentError, 'Mnemonic is empty' if options[:mnemonic].empty?
         
     | 
| 
      
 33 
     | 
    
         
            +
                words_array = File.readlines(
         
     | 
| 
      
 34 
     | 
    
         
            +
                  File.join(
         
     | 
| 
      
 35 
     | 
    
         
            +
                    File.dirname(File.expand_path(__FILE__)), '../words/english.txt'
         
     | 
| 
      
 36 
     | 
    
         
            +
                  )
         
     | 
| 
      
 37 
     | 
    
         
            +
                ).map(&:strip)
         
     | 
| 
      
 38 
     | 
    
         
            +
                mnemonic_array = options[:mnemonic].split(' ').map do |word|
         
     | 
| 
       26 
39 
     | 
    
         
             
                  word_index = words_array.index(word)
         
     | 
| 
       27 
     | 
    
         
            -
                  raise IndexError,  
     | 
| 
       28 
     | 
    
         
            -
                  word_index.to_s(2).rjust(11, 
     | 
| 
      
 40 
     | 
    
         
            +
                  raise IndexError, 'Word not found in words list' if word_index.nil?
         
     | 
| 
      
 41 
     | 
    
         
            +
                  word_index.to_s(2).rjust(11, '0')
         
     | 
| 
       29 
42 
     | 
    
         
             
                end
         
     | 
| 
       30 
43 
     | 
    
         
             
                mnemonic_binary_with_checksum = mnemonic_array.join.to_s
         
     | 
| 
       31 
     | 
    
         
            -
                entropy_binary = 
     | 
| 
       32 
     | 
    
         
            -
                checksum_bits = mnemonic_binary_with_checksum.slice(-(entropy_binary.length/32),(entropy_binary.length/32))
         
     | 
| 
       33 
     | 
    
         
            -
                 
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
                else
         
     | 
| 
       36 
     | 
    
         
            -
                  raise SecurityError, "Checksum mismatch, invalid mnemonic"
         
     | 
| 
       37 
     | 
    
         
            -
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
                entropy_binary =mnemonic_binary_with_checksum.slice(0, mnemonic_binary_with_checksum.length * 32 / 33)
         
     | 
| 
      
 45 
     | 
    
         
            +
                checksum_bits = mnemonic_binary_with_checksum.slice(-(entropy_binary.length / 32), (entropy_binary.length / 32))
         
     | 
| 
      
 46 
     | 
    
         
            +
                raise SecurityError, 'Checksum mismatch, invalid mnemonic' unless checksum(entropy_binary) == checksum_bits
         
     | 
| 
      
 47 
     | 
    
         
            +
                [entropy_binary].pack('B*').unpack('H*').first
         
     | 
| 
       38 
48 
     | 
    
         
             
              end
         
     | 
| 
       39 
49 
     | 
    
         | 
| 
       40 
50 
     | 
    
         
             
              def self.checksum(entropy_binary)
         
     | 
| 
       41 
     | 
    
         
            -
                sha256hash = OpenSSL::Digest::SHA256.hexdigest([entropy_binary].pack( 
     | 
| 
       42 
     | 
    
         
            -
                sha256hash_binary = [sha256hash].pack( 
     | 
| 
       43 
     | 
    
         
            -
                 
     | 
| 
      
 51 
     | 
    
         
            +
                sha256hash = OpenSSL::Digest::SHA256.hexdigest([entropy_binary].pack('B*'))
         
     | 
| 
      
 52 
     | 
    
         
            +
                sha256hash_binary = [sha256hash].pack('H*').unpack('B*').first
         
     | 
| 
      
 53 
     | 
    
         
            +
                sha256hash_binary.slice(0, (entropy_binary.length / 32))
         
     | 
| 
       44 
54 
     | 
    
         
             
              end
         
     | 
| 
       45 
55 
     | 
    
         | 
| 
       46 
56 
     | 
    
         
             
              def self.to_seed(options)
         
     | 
| 
       47 
     | 
    
         
            -
                raise ArgumentError,  
     | 
| 
       48 
     | 
    
         
            -
                 
     | 
| 
      
 57 
     | 
    
         
            +
                raise ArgumentError, 'Mnemonic not set' if options[:mnemonic].nil?
         
     | 
| 
      
 58 
     | 
    
         
            +
                OpenSSL::PKCS5.pbkdf2_hmac(
         
     | 
| 
      
 59 
     | 
    
         
            +
                  options[:mnemonic],
         
     | 
| 
      
 60 
     | 
    
         
            +
                  "mnemonic#{options[:password]}",
         
     | 
| 
      
 61 
     | 
    
         
            +
                  2048,
         
     | 
| 
      
 62 
     | 
    
         
            +
                  64,
         
     | 
| 
      
 63 
     | 
    
         
            +
                  OpenSSL::Digest::SHA512.new
         
     | 
| 
      
 64 
     | 
    
         
            +
                ).unpack('H*')[0]
         
     | 
| 
       49 
65 
     | 
    
         
             
              end
         
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
66 
     | 
    
         
             
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,17 +1,17 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: bip_mnemonic
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0.0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.0.3
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Sreekanth GS
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date:  
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2019-06-18 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
     | 
    
         
            -
              name:  
     | 
| 
      
 14 
     | 
    
         
            +
              name: rdoc
         
     | 
| 
       15 
15 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
       16 
16 
     | 
    
         
             
                requirements:
         
     | 
| 
       17 
17 
     | 
    
         
             
                - - ">="
         
     | 
| 
         @@ -25,7 +25,7 @@ dependencies: 
     | 
|
| 
       25 
25 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       26 
26 
     | 
    
         
             
                    version: '0'
         
     | 
| 
       27 
27 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       28 
     | 
    
         
            -
              name:  
     | 
| 
      
 28 
     | 
    
         
            +
              name: rspec
         
     | 
| 
       29 
29 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
       30 
30 
     | 
    
         
             
                requirements:
         
     | 
| 
       31 
31 
     | 
    
         
             
                - - ">="
         
     | 
| 
         @@ -47,10 +47,10 @@ extensions: [] 
     | 
|
| 
       47 
47 
     | 
    
         
             
            extra_rdoc_files: []
         
     | 
| 
       48 
48 
     | 
    
         
             
            files:
         
     | 
| 
       49 
49 
     | 
    
         
             
            - lib/bip_mnemonic.rb
         
     | 
| 
       50 
     | 
    
         
            -
            - lib/pbkdf2.rb
         
     | 
| 
       51 
50 
     | 
    
         
             
            - words/english.txt
         
     | 
| 
       52 
51 
     | 
    
         
             
            homepage: http://github.com/sreekanthgs/mnemonic
         
     | 
| 
       53 
     | 
    
         
            -
            licenses: 
     | 
| 
      
 52 
     | 
    
         
            +
            licenses:
         
     | 
| 
      
 53 
     | 
    
         
            +
            - MIT
         
     | 
| 
       54 
54 
     | 
    
         
             
            metadata: {}
         
     | 
| 
       55 
55 
     | 
    
         
             
            post_install_message: 
         
     | 
| 
       56 
56 
     | 
    
         
             
            rdoc_options: []
         
     | 
| 
         @@ -67,8 +67,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       67 
67 
     | 
    
         
             
                - !ruby/object:Gem::Version
         
     | 
| 
       68 
68 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       69 
69 
     | 
    
         
             
            requirements: []
         
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
            rubygems_version: 2.4.6
         
     | 
| 
      
 70 
     | 
    
         
            +
            rubygems_version: 3.0.3
         
     | 
| 
       72 
71 
     | 
    
         
             
            signing_key: 
         
     | 
| 
       73 
72 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       74 
73 
     | 
    
         
             
            summary: BipMnemonic words and seed generator based on BIP-39
         
     | 
    
        data/lib/pbkdf2.rb
    DELETED
    
    | 
         @@ -1,118 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # THIS IS A MINIMALLY MODIFIED IMPLEMENTATION OF PBKDF2, ORIGINALLY IMPLEMENTED BY
         
     | 
| 
       2 
     | 
    
         
            -
            # Sam Quigley. ORIGINAL IMPLEMENTATION AVAILABLE AT:
         
     | 
| 
       3 
     | 
    
         
            -
            # https://github.com/emerose/pbkdf2-ruby
         
     | 
| 
       4 
     | 
    
         
            -
            #
         
     | 
| 
       5 
     | 
    
         
            -
            # Copyright (c) 2008 Sam Quigley <quigley@emerose.com>
         
     | 
| 
       6 
     | 
    
         
            -
            #
         
     | 
| 
       7 
     | 
    
         
            -
            # Permission is hereby granted, free of charge, to any person obtaining a copy
         
     | 
| 
       8 
     | 
    
         
            -
            # of this software and associated documentation files (the "Software"), to deal
         
     | 
| 
       9 
     | 
    
         
            -
            # in the Software without restriction, including without limitation the rights
         
     | 
| 
       10 
     | 
    
         
            -
            # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         
     | 
| 
       11 
     | 
    
         
            -
            # copies of the Software, and to permit persons to whom the Software is
         
     | 
| 
       12 
     | 
    
         
            -
            # furnished to do so, subject to the following conditions:
         
     | 
| 
       13 
     | 
    
         
            -
            #
         
     | 
| 
       14 
     | 
    
         
            -
            # The above copyright notice and this permission notice shall be included in
         
     | 
| 
       15 
     | 
    
         
            -
            # all copies or substantial portions of the Software.
         
     | 
| 
       16 
     | 
    
         
            -
            #
         
     | 
| 
       17 
     | 
    
         
            -
            # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         
     | 
| 
       18 
     | 
    
         
            -
            # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         
     | 
| 
       19 
     | 
    
         
            -
            # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         
     | 
| 
       20 
     | 
    
         
            -
            # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         
     | 
| 
       21 
     | 
    
         
            -
            # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         
     | 
| 
       22 
     | 
    
         
            -
            # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
         
     | 
| 
       23 
     | 
    
         
            -
            # THE SOFTWARE.
         
     | 
| 
       24 
     | 
    
         
            -
            #
         
     | 
| 
       25 
     | 
    
         
            -
            # This license is sometimes referred to as "The MIT License"
         
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
            require 'openssl'
         
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
            class PBKDF2
         
     | 
| 
       30 
     | 
    
         
            -
              VERSION = "0.0.1"
         
     | 
| 
       31 
     | 
    
         
            -
              def self.hex_string(options = {})
         
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
                yield self if block_given?
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
                # Raise errors for unset options, all are mandatory
         
     | 
| 
       36 
     | 
    
         
            -
                raise ArgumentError, ":password is not set" if options[:password].nil?
         
     | 
| 
       37 
     | 
    
         
            -
                raise ArgumentError, ":salt is not set" if options[:salt].nil?
         
     | 
| 
       38 
     | 
    
         
            -
                raise ArgumentError, ":iterations is not set" if options[:iterations].nil?
         
     | 
| 
       39 
     | 
    
         
            -
                raise ArgumentError, ":key_length is not set" if options[:key_length].nil?
         
     | 
| 
       40 
     | 
    
         
            -
                raise ArgumentError, ":hash_function is not set" if options[:hash_function].nil?
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
                options[:hash_function] = options[:hash_function].new
         
     | 
| 
       43 
     | 
    
         
            -
                validate(options)
         
     | 
| 
       44 
     | 
    
         
            -
                options[:key_length] = options[:key_length]/8
         
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
                calculate!(options)
         
     | 
| 
       47 
     | 
    
         
            -
              end
         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
              def self.validate(options)
         
     | 
| 
       50 
     | 
    
         
            -
                raise ArgumentError, "Key is too short (< 1)" if options[:key_length] < 1
         
     | 
| 
       51 
     | 
    
         
            -
                raise ArgumentError, "key is too long for hash function" if options[:key_length]/8 > ((2**32 - 1) * options[:hash_function].size)
         
     | 
| 
       52 
     | 
    
         
            -
                raise ArgumentError, "iterations can't be less than 1" if options[:iterations] < 1
         
     | 
| 
       53 
     | 
    
         
            -
              end
         
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
              def self.prf(options, data)
         
     | 
| 
       56 
     | 
    
         
            -
                OpenSSL::HMAC.digest(options[:hash_function], options[:password], data)
         
     | 
| 
       57 
     | 
    
         
            -
              end
         
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
              # this is a translation of the helper function "F" defined in the spec
         
     | 
| 
       60 
     | 
    
         
            -
              def self.calculate_block(options, block_num)
         
     | 
| 
       61 
     | 
    
         
            -
                # u_1:
         
     | 
| 
       62 
     | 
    
         
            -
                u = prf(options, options[:salt]+[block_num].pack("N"))
         
     | 
| 
       63 
     | 
    
         
            -
                ret = u
         
     | 
| 
       64 
     | 
    
         
            -
                # u_2 through u_c:
         
     | 
| 
       65 
     | 
    
         
            -
                2.upto(options[:iterations]) do
         
     | 
| 
       66 
     | 
    
         
            -
                  # calculate u_n
         
     | 
| 
       67 
     | 
    
         
            -
                  u = prf(options, u)
         
     | 
| 
       68 
     | 
    
         
            -
                  # xor it with the previous results
         
     | 
| 
       69 
     | 
    
         
            -
                  ret = ret^u
         
     | 
| 
       70 
     | 
    
         
            -
                end
         
     | 
| 
       71 
     | 
    
         
            -
                ret
         
     | 
| 
       72 
     | 
    
         
            -
              end
         
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
              # the bit that actually does the calculating
         
     | 
| 
       75 
     | 
    
         
            -
              def self.calculate!(options)
         
     | 
| 
       76 
     | 
    
         
            -
                # how many blocks we'll need to calculate (the last may be truncated)
         
     | 
| 
       77 
     | 
    
         
            -
                blocks_needed = (options[:key_length].to_f / options[:hash_function].size).ceil
         
     | 
| 
       78 
     | 
    
         
            -
                # reset
         
     | 
| 
       79 
     | 
    
         
            -
                value = ""
         
     | 
| 
       80 
     | 
    
         
            -
                # main block-calculating loop:
         
     | 
| 
       81 
     | 
    
         
            -
                1.upto(blocks_needed) do |block_num|
         
     | 
| 
       82 
     | 
    
         
            -
                 value << calculate_block(options,block_num)
         
     | 
| 
       83 
     | 
    
         
            -
                end
         
     | 
| 
       84 
     | 
    
         
            -
                # truncate to desired length:
         
     | 
| 
       85 
     | 
    
         
            -
                value = value.slice(0,options[:key_length]).unpack("H*").first
         
     | 
| 
       86 
     | 
    
         
            -
                value
         
     | 
| 
       87 
     | 
    
         
            -
              end
         
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
            end
         
     | 
| 
       90 
     | 
    
         
            -
             
     | 
| 
       91 
     | 
    
         
            -
            class String
         
     | 
| 
       92 
     | 
    
         
            -
              if RUBY_VERSION >= "1.9"
         
     | 
| 
       93 
     | 
    
         
            -
                def xor_impl(other)
         
     | 
| 
       94 
     | 
    
         
            -
                  result = "".encode("ASCII-8BIT")
         
     | 
| 
       95 
     | 
    
         
            -
                  o_bytes = other.bytes.to_a
         
     | 
| 
       96 
     | 
    
         
            -
                  bytes.each_with_index do |c, i|
         
     | 
| 
       97 
     | 
    
         
            -
                    result << (c ^ o_bytes[i])
         
     | 
| 
       98 
     | 
    
         
            -
                  end
         
     | 
| 
       99 
     | 
    
         
            -
                  result
         
     | 
| 
       100 
     | 
    
         
            -
                end
         
     | 
| 
       101 
     | 
    
         
            -
              else
         
     | 
| 
       102 
     | 
    
         
            -
                def xor_impl(other)
         
     | 
| 
       103 
     | 
    
         
            -
                  result = (0..length-1).collect { |i| self[i] ^ other[i] }
         
     | 
| 
       104 
     | 
    
         
            -
                  result.pack("C*")
         
     | 
| 
       105 
     | 
    
         
            -
                end
         
     | 
| 
       106 
     | 
    
         
            -
              end
         
     | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
       108 
     | 
    
         
            -
              private :xor_impl
         
     | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
       110 
     | 
    
         
            -
              def ^(other)
         
     | 
| 
       111 
     | 
    
         
            -
                raise ArgumentError, "Can't bitwise-XOR a String with a non-String" \
         
     | 
| 
       112 
     | 
    
         
            -
                  unless other.kind_of? String
         
     | 
| 
       113 
     | 
    
         
            -
                raise ArgumentError, "Can't bitwise-XOR strings of different length" \
         
     | 
| 
       114 
     | 
    
         
            -
                  unless length == other.length
         
     | 
| 
       115 
     | 
    
         
            -
             
     | 
| 
       116 
     | 
    
         
            -
                xor_impl(other)
         
     | 
| 
       117 
     | 
    
         
            -
              end
         
     | 
| 
       118 
     | 
    
         
            -
            end
         
     |