module NumberStation

Ruby Number Station Author: David Kirwan gitub.com/davidkirwan Licence: GPL 3.0 NumberStation is a collection of utilities to aid in the running of a number station

Copyright (C) 2018  David Kirwan

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

Ruby Number Station Author: David Kirwan gitub.com/davidkirwan Licence: GPL 3.0 NumberStation is a collection of utilities to aid in the running of a number station

Copyright (C) 2018  David Kirwan

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

Ruby Number Station Author: David Kirwan gitub.com/davidkirwan Licence: GPL 3.0 NumberStation is a collection of utilities to aid in the running of a number station

Copyright (C) 2018  David Kirwan

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

Constants

ALPHABET
VERSION

Public Class Methods

call_espeak(input_file_path, output_file_path) click to toggle source
# File lib/number_station/phonetic_conversion.rb, line 97
def self.call_espeak(input_file_path, output_file_path)
  cmd = "espeak -ven+f3 -m -p 60 -s 180 -f #{input_file_path} --stdout | ffmpeg -i - -ar 44100 -ac 2 -ab 192k -f mp3 #{output_file_path}"

  unless NumberStation.command?('espeak') || NumberStation.command?('ffmpeg')
    NumberStation.log.error "number_station requires the espeak and ffmpeg utilities are installed in order to output an mp3 file."
  else
    `#{cmd}`
  end
end
command?(name) click to toggle source
# File lib/number_station.rb, line 35
def self.command?(name)
  `which #{name}`
  $?.success?
end
data() click to toggle source
# File lib/number_station.rb, line 52
def self.data()
  return @data
end
decrypt_message(message, pad_path, pad_num) click to toggle source
# File lib/number_station/decrypt_message.rb, line 26
def self.decrypt_message(message, pad_path, pad_num)
  NumberStation.log.debug "message length: #{message.size}"
  message_byte_array = message.scan(/.{1}/).each_slice(2).map { |f, l| (Integer(f,16) << 4) + Integer(l,16) } 

  begin
    pad_data = JSON.parse(File.read(pad_path))
  rescue Exception => e
    raise e
  end

  crypto_hex_str = pad_data["pads"][pad_num]["key"]
  if message.size > crypto_hex_str.size
    NumberStation.log.error "Error: The message length is greater than pad length. Unable to continue decryption."
    exit
  end
  NumberStation.log.debug "message length less than pad length: #{message.size <= crypto_hex_str.size}"

  crypto_byte_array = crypto_hex_str.scan(/.{1}/).each_slice(2).map { |f, l| (Integer(f,16) << 4) + Integer(l,16) }

  decrypted_byte_array = []
  message_byte_array.each_with_index do |i, index|
    decrypted_byte_array << (i ^ crypto_byte_array[index])
  end

  decrypted_string = decrypted_byte_array.pack('U*').force_encoding('utf-8')

  begin
    f_name = "#{pad_data["id"]}_#{pad_num}_#{Time.now.to_i}_decrypted.txt"
    NumberStation.log.info "Writing decrypted message to file #{f_name}"
    f = File.open(f_name, "w")
    f.write(decrypted_string)
    f.close
  rescue Exception => e
    raise e
  end
  return decrypted_string
end
encrypt_message(message, pad_path, pad_num) click to toggle source
# File lib/number_station/encrypt_message.rb, line 26
def self.encrypt_message(message, pad_path, pad_num)
  NumberStation.log.debug "message length: #{message.size}"
  message_byte_array = message.unpack('U*')

  begin
    pad_data = JSON.parse(File.read(pad_path))
  rescue Exception => e
    raise e
  end

  crypto_hex_str = pad_data["pads"][pad_num]["key"]

  if message.size > crypto_hex_str.size
    NumberStation.log.error "Exception: message length is larger than pad length. Break the message into smaller parts."
    exit
  end
  NumberStation.log.debug "message length less than pad length"

  unless pad_data["pads"][pad_num]["consumed"]
    NumberStation.log.debug "Marking key as consumed"
    pad_data["pads"][pad_num]["epoch_date"] = Time.now.to_i
    pad_data["pads"][pad_num]["consumed"] = true
    f = File.open(pad_path, "w")
    f.write(pad_data.to_json)
    f.close
  else
     msg = "Warning pad #{pad_num} has been consumed on #{Time.at(pad_data["pads"][pad_num]["epoch_date"])}"
     NumberStation.log.error msg
     exit
  end

  crypto_byte_array = crypto_hex_str.scan(/.{1}/).each_slice(2).map { |f, l| (Integer(f,16) << 4) + Integer(l,16) }

  encrypted_byte_array = []
  message_byte_array.each_with_index do |i, index|
    encrypted_byte_array << (i ^ crypto_byte_array[index])
  end
       
  encrypted_byte_str = encrypted_byte_array.map { |n| '%02X' % (n & 0xFF) }.join.downcase

  begin
    f_name = "#{pad_data["id"]}_#{pad_num}_#{Time.now.to_i}.txt"
    NumberStation.log.info "Writing encrypted message to file #{f_name}"
    f = File.open(f_name, "w")
    f.write(encrypted_byte_str)
    f.close
  rescue Exception => e
    raise e
  end

  return encrypted_byte_str
end
espeak_word_template(word) click to toggle source
# File lib/number_station/phonetic_conversion.rb, line 73
def self.espeak_word_template(word)
  return "<prosody pitch=\"#{randomsign() + rand(0..200).to_s}\">#{word}</prosody>"
end
generate_sentence(message) click to toggle source
# File lib/number_station/phonetic_conversion.rb, line 83
def self.generate_sentence(message)
  sentence = ""
  message.split(" ").each {|i| sentence += espeak_word_template(i)}
  return "<speak version=\"1.0\" xmlns=\"\" xmlns:xsi=\"\" xsi:schemaLocation=\"\" xml:lang=\"\"><voice gender=\"female\">#{sentence}</voice></speak>"
end
log() click to toggle source
# File lib/number_station.rb, line 44
def self.log()
  return @log
end
lookup_phonetic(c) click to toggle source
# File lib/number_station/phonetic_conversion.rb, line 64
def self.lookup_phonetic(c)
  begin
    return NumberStation::ALPHABET[c] + ' ' || ' '
  rescue Exception => e
    return ' '
  end
end
make_otp(pad_path, length, num_pads) click to toggle source
# File lib/number_station/make_onetime_pad.rb, line 26
def self.make_otp(pad_path, length, num_pads)
  path = pad_path || Dir.pwd
  len = length || 250
  num = num_pads || 5

  NumberStation.log.debug "make_otp"
  pads = {}
  id = rand(0..99999).to_s.rjust(5, "0")
  file_name = File.join(path, "one_time_pad_#{id}.json")
  NumberStation.log.debug "file_name: #{file_name}"

  0.upto(num.to_i - 1) do |i| 
    pads[i] = {
      "key"=>SecureRandom.hex(len.to_i),
      "epoch_date"=>nil,
      "consumed"=>false
    }
  end
  one_time_pads = {
    :id=> id,
    :pads=> pads
  }

  unless File.file?(file_name)
    f = File.open(file_name, "w")
    f.write(one_time_pads.to_json)
    f.close
  else
    raise Exception.new("Exception #{file_name} already exists")
  end
end
randomsign() click to toggle source
# File lib/number_station/phonetic_conversion.rb, line 78
def self.randomsign()
  return rand(0..1) == 0 ? "-" : "+"
end
set_data(data) click to toggle source
# File lib/number_station.rb, line 48
def self.set_data(data)
  @data = data
end
set_log(log) click to toggle source
# File lib/number_station.rb, line 40
def self.set_log(log)
  @log = log
end
to_phonetic(file_name) click to toggle source
# File lib/number_station/phonetic_conversion.rb, line 116
def self.to_phonetic(file_name)
  message = ''

  f = File.open(file_name)
  raw_message = f.read()
  f.close()

  raw_message.each_char do |c|
    message += NumberStation.lookup_phonetic(c)
  end
  return message
end
write_espeak_template_file(filename, sentence) click to toggle source
# File lib/number_station/phonetic_conversion.rb, line 90
def self.write_espeak_template_file(filename, sentence)
  f = File.open(filename, "w")
  f.write(sentence)
  f.close
end
write_mp3(message, output_file_path) click to toggle source
# File lib/number_station/phonetic_conversion.rb, line 108
def self.write_mp3(message, output_file_path)
  filename = NumberStation.data["resources"]["espeak_sentence_template"]
  sentence = NumberStation.generate_sentence(message)
  NumberStation.write_espeak_template_file(filename, sentence)
  NumberStation.call_espeak(filename, output_file_path)
end