module ShadowsocksRuby::Cipher

This module provide classes to encapsulate different underlying crypto library, to utilize them with an unique interface.

It also provide some useful utility functions like {.hmac_sha1_digest} and {.bytes_to_key}.

@example

# Demonstrate how to build a cipher object and it's typical use case.
cipher = ShadowsocksRuby::Cipher.build('aes-256-cfb', 'secret123')
iv = cipher.random_id
encrypted_text = cipher.encrypt("hello world!", iv)
puts cipher.decrypt(encrypted_text, iv) # hello world!
puts cipher.key # ...... # in case key need to be used in some Digest algorithm.

Public Instance Methods

build(method, password) click to toggle source

Builder for cipher object

Supported methods are:

  • table

  • rc4-md5

  • chacha20, chacha2-ietf, salsa20 which are provided by RbNaCl

  • all cipher methods supported by ruby gems OpenSSL, use

    ruby -e "require 'openssl'; puts OpenSSL::Cipher.ciphers"

    to get a full list.

@param [String] method Cipher methods @param [String] password Password @return [OpenSSL, Table, RC4_MD5, RbNaCl] A duck type cipher object

# File lib/shadowsocks_ruby/cipher/cipher.rb, line 36
def build method, password
  case method
  when 'table'
    ShadowsocksRuby::Cipher::Table.new password
  when 'rc4-md5'
    ShadowsocksRuby::Cipher::RC4_MD5.new password
  when 'chacha20','chacha20-ietf','salsa20'
    ShadowsocksRuby::Cipher::RbNaCl.new method, password
  else
    ShadowsocksRuby::Cipher::OpenSSL.new method, password
  end
end
bytes_to_key(password, key_len, iv_len) click to toggle source

Equivalent to OpenSSL's EVP_BytesToKey() with count = 1

@param [String] Password Password bytes @param [Integer] key_len Key length, the length of key bytes to generate @param [Integer] iv_len IV length, needed by internal algorithm @return [String] Key bytes, of key_len length

# File lib/shadowsocks_ruby/cipher/cipher.rb, line 65
def bytes_to_key(password, key_len, iv_len)
  bytes_to_key1(nil, password, 1, key_len, iv_len)[0]
end
hmac_sha1_digest(key, message) click to toggle source

Generate first 10 bytes of HMAC using sha1 Digest

@param [String] key Key, use {#bytes_to_key} to convert a password to key if you need @param [String] message Message to digest @return [String] Digest, only first 10 bytes

# File lib/shadowsocks_ruby/cipher/cipher.rb, line 54
def hmac_sha1_digest(key, message)
  @digest ||= ::OpenSSL::Digest.new('sha1')
  ::OpenSSL::HMAC.digest(@digest, key, message)[0,10]
end

Private Instance Methods

bytes_to_key0(md_buf, salt, data, count) click to toggle source
# File lib/shadowsocks_ruby/cipher/cipher.rb, line 71
def bytes_to_key0(md_buf, salt, data, count)
  src = md_buf.empty? ? '' : md_buf
  src << data
  src << salt if salt

  dst = ::OpenSSL::Digest::MD5.digest(src)

  (count - 1).times do
    dst = ::OpenSSL::Digest::MD5.digest(dst)
  end

  return dst
end
bytes_to_key1(salt, data, count, nkey, niv) click to toggle source

Equivalent to OpenSSL's EVP_BytesToKey() Taken from d.hatena.ne.jp/winebarrel/20081208/p1 Fixed by Zhenkyle

# File lib/shadowsocks_ruby/cipher/cipher.rb, line 88
def bytes_to_key1(salt, data, count, nkey, niv)
  key = ''
  iv = ''
  md_buf = ''

  loop do
    md_buf = bytes_to_key0(md_buf, salt, data, count)

    if nkey.nonzero?
      key << (nkey > md_buf.length ? md_buf : md_buf[0, nkey])
      nkey -= nkey > md_buf.length ? md_buf.length : nkey
    elsif niv.nonzero?
      iv << (niv > md_buf.length ? md_buf : md_buf[0, niv])
      niv -= niv > md_buf.length ? md_buf.length : niv
    end

    if nkey.zero? and niv.zero?
      break
    end
  end

  return [key, iv]
end