#!/usr/bin/env python
# Copyright (C) 2011  Nanakos Chrysostomos <chris@include.gr>
import Xlib.display
import Xlib.X
import Xlib.XK
import Xlib.protocol.event

UseXTest = True

try :
    import Xlib.ext.xtest
except ImportError:
    UseXTest = False
    print "no XTest extension; using XSendEvent"

import sys, time

display = Xlib.display.Display()
window = display.get_input_focus()._data["focus"];

if UseXTest and not display.query_extension("XTEST") :
    UseXTest = False


##########Python modules for OTP Generation###################
import re, os, time, socket
from Crypto.Cipher import AES
from OpenSSL import SSL
import hmac, hashlib
import random
import time
import ConfigParser

def hex2modhex(string):
                hexx = "0123456789abcdef"
                modhex = "cbdefghijklnrtuv"
                retVal = ''
                for i in range (0, len(string)):
                        pos = hexx.find(string[i])
                        if pos > -1:
                                retVal += modhex[pos]
                        else:
                                raise Exception, '"' + string[i] + '": Character is not a valid hex string'
                return retVal

def CRC(string):
                crc = 0xffff;
                for i in range(0, len(string)):
                        b=ord(string[i])
                        for j in range(0, 8):
                                n = ((b)^(crc)) & 1
                                if n == 1:
                                        crc = (crc>> 1)^ 0x8408
                                elif n == 0:
                                        crc = crc>> 1
                                b=b>> 1
                return crc

special_X_keysyms = {
    ' ' : "space",
    '\t' : "Tab",
    '\n' : "Return",
    '\r' : "Return",
    '\e' : "Escape",
    '!' : "exclam",
    '#' : "numbersign",
    '%' : "percent",
    '$' : "dollar",
    '&' : "ampersand",
    '"' : "quotedbl",
    '\'' : "apostrophe",
    '(' : "parenleft",
    ')' : "parenright",
    '*' : "asterisk",
    '=' : "equal",
    '+' : "plus",
    ',' : "comma",
    '-' : "minus",
    '.' : "period",
    '/' : "slash",
    ':' : "colon",
    ';' : "semicolon",
    '<' : "less",
    '>' : "greater",
    '?' : "question",
    '@' : "at",
    '[' : "bracketleft",
    ']' : "bracketright",
    '\\' : "backslash",
    '^' : "asciicircum",
    '_' : "underscore",
    '`' : "grave",
    '{' : "braceleft",
    '|' : "bar",
    '}' : "braceright",
    '~' : "asciitilde"
    }


def get_keysym(ch) :
    keysym = Xlib.XK.string_to_keysym(ch)
    if keysym == 0 :
        keysym = Xlib.XK.string_to_keysym(special_X_keysyms[ch])
    return keysym

def is_shifted(ch) :
    if ch.isupper() :
        return True
    if "~!@#$%^&*()_+{}|:\"<>?".find(ch) >= 0 :
        return True
    return False

def char_to_keycode(ch) :
    keysym = get_keysym(ch)
    keycode = display.keysym_to_keycode(keysym)
    if keycode == 0 :
        print "Sorry, can't map", ch

    if (is_shifted(ch)) :
        shift_mask = Xlib.X.ShiftMask
    else :
        shift_mask = 0

    return keycode, shift_mask

def send_string(str) :
    for ch in str :
        keycode, shift_mask = char_to_keycode(ch)
        if (UseXTest) :
            if shift_mask != 0 :
                Xlib.ext.xtest.fake_input(display, Xlib.X.KeyPress, 50)
            Xlib.ext.xtest.fake_input(display, Xlib.X.KeyPress, keycode)
            Xlib.ext.xtest.fake_input(display, Xlib.X.KeyRelease, keycode)
            if shift_mask != 0 :
                Xlib.ext.xtest.fake_input(display, Xlib.X.KeyRelease, 50)
        else :
            event = Xlib.protocol.event.KeyPress(
                time = int(time.time()),
                root = display.screen().root,
                window = window,
                same_screen = 0, child = Xlib.X.NONE,
                root_x = 0, root_y = 0, event_x = 0, event_y = 0,
                state = shift_mask,
                detail = keycode
                )
            window.send_event(event, propagate = True)
            event = Xlib.protocol.event.KeyRelease(
                time = int(time.time()),
                root = display.screen().root,
                window = window,
                same_screen = 0, child = Xlib.X.NONE,
                root_x = 0, root_y = 0, event_x = 0, event_y = 0,
                state = shift_mask,
                detail = keycode
                )
            window.send_event(event, propagate = True)

yubisoft_cfg = os.path.expanduser('~/.local/share/cinnamon/applets/softyubikey@yubiserver.include.gr/yubisoft.cfg')
config = ConfigParser.RawConfigParser()
config.read(yubisoft_cfg)
publicid = config.get('Yubisoft','publicid')
privateid = config.get('Yubisoft','privateid')
aeskey=config.get('Yubisoft','aeskey')
softsession=int(config.get('Yubisoft','session'))
softcounter = int(config.get('Yubisoft','counter'))
softtimestamp = int(config.get('Yubisoft','timestamp'))
softcounter = softcounter + 1
softtimestamp = softtimestamp + 8

if softcounter > 255:
        softcounter = 1
        softsession = softsession+1
if softtimestamp > 999999:
        softtimestamp = 1
        softsession = softsession+1
        softcounter = 1

session_counter = "%0.4x" % softsession
timestamp = "%0.6x" % softtimestamp
session_token_counter = "%0.2x" % softcounter
random.seed(os.urandom(8))
pseudo_random = "%0.4x" % (random.uniform(10,1000))
pre_aesdata= privateid + (session_counter[2:4]+session_counter[0:2]) + (timestamp[4:6]+timestamp[2:4]+timestamp[0:2]) + session_token_counter + (pseudo_random[2:4]+ pseudo_random[0:2])
crc_chksum = hex(CRC(pre_aesdata.decode('hex')))
#residue=hex((int(bin(~int(crc_chksum,16)),2)&0xFFFF))
residue = "0x%0.4x" % (int(bin(~int(crc_chksum,16)),2)&0xFFFF)
pre_aesdata = pre_aesdata+residue[4:6]+residue[2:4]
encoded=AES.new(aeskey.decode('hex'), AES.MODE_ECB).encrypt(pre_aesdata.decode('hex')).encode('hex')
final_otp=publicid+hex2modhex(encoded)
config.set('Yubisoft','counter',softcounter)
config.set('Yubisoft','session',softsession)
config.set('Yubisoft','timestamp',softtimestamp)
with open(yubisoft_cfg,'wb') as configfile:
        config.write(configfile)

send_string(final_otp)
send_string(sys.argv[1])
display.sync()

