Created Lockiv Repo master
Created Lockiv Repo

file:b/HashAuth.py (new)
--- /dev/null
+++ b/HashAuth.py
@@ -1,1 +1,31 @@
+import hashlib
+import calendar
+import time
 
+class HashAuth:
+
+	ITERATIONS = 10000
+
+	def __init__(self, secret_key):
+
+		self.time_since_epoch = calendar.timegm( time.gmtime() )
+		self.hashed_value = self.hash( str( self.time_since_epoch ) + secret_key )
+
+	def hash(self, data):
+
+		digest = hashlib.sha512(data).digest()
+
+		for x in xrange(self.ITERATIONS):
+			digest = hashlib.sha512(digest).digest()
+
+		return digest
+
+	def test(self, secret_key):
+
+		hashed_value = self.hash( str( self.time_since_epoch ) + secret_key )
+
+		if self.hashed_value == hashed_value:
+			return True
+
+		return False
+

file:b/KeyLoader.py (new)
--- /dev/null
+++ b/KeyLoader.py
@@ -1,1 +1,17 @@
+import getpass
+import hashlib
 
+import lock_config
+
+def KeyLoader():
+
+	secret_key = lock_config.KEY
+
+	password = getpass.getpass()
+
+	# Mix password with secret key for extra security
+	secret_key = hashlib.sha512( password + secret_key ).digest()
+
+	return secret_key
+
+

file:b/LockCommand.py (new)
--- /dev/null
+++ b/LockCommand.py
@@ -1,1 +1,7 @@
 
+class LockCommand:
+
+	def __init__(self, command, auth):
+		self.command = command
+		self.auth = auth
+

--- /dev/null
+++ b/LockCommandClient.py
@@ -1,1 +1,24 @@
+from KeyLoader import KeyLoader
+from SendCommand import send_command
 
+import lock_config
+import sys
+
+secret_key = KeyLoader()
+
+def run():
+
+	if len(sys.argv) <= 1:
+		print "Error: No command specified!"
+		return
+
+	command = sys.argv[1]
+
+	if command != "lock" and command != "unlock":
+		print "Error: Invalid Command: " + command
+		return;
+
+	send_command(command, secret_key, lock_config.SERVER, lock_config.PORT)
+
+run()
+

--- /dev/null
+++ b/LockCommandServer.py
@@ -1,1 +1,72 @@
+from HashAuth import HashAuth
+from LockCommand import LockCommand
+from KeyLoader import KeyLoader
 
+import LockControl
+
+import lock_config
+
+import time
+import calendar
+
+import sys
+import pickle
+import socket
+
+import signal
+import traceback
+
+running = True
+
+def hangup_handler(signum, frame):
+	global running
+	print "Hang up Recievied"
+	running = False
+
+def log_exception(ex_cls, ex, tb):
+	print "Exception Occured"
+	print ''.join(traceback.format_tb(tb))
+	print '{0}: {1}'.format(ex_cls, ex)
+	sys.exit(0)
+
+signal.signal(signal.SIGHUP, hangup_handler)
+signal.signal(signal.SIGINT, hangup_handler)
+
+sys.excepthook = log_exception
+
+secret_key = KeyLoader()
+
+LockControl.init()
+
+last_time = calendar.timegm( time.gmtime() )
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+sock.bind((lock_config.SERVER_IP, lock_config.PORT))
+
+while running:
+
+	data, addr = sock.recvfrom(1024)
+
+	try:
+		command_obj = pickle.loads(data)
+		valid = command_obj.auth.test(secret_key)
+
+		cur_time = calendar.timegm( time.gmtime() )
+
+		# +/- 30 seconds and Sooner than Last Auth
+		if cur_time + 30 > command_obj.auth.time_since_epoch and cur_time - 30 < command_obj.auth.time_since_epoch and last_time < command_obj.auth.time_since_epoch:
+			last_time = command_obj.auth.time_since_epoch
+
+		else:
+			valid = False
+
+		if valid:
+			LockControl.command(command_obj.command)
+
+		else:
+			print "Authentification failed!"
+
+        except:
+                print "Invalid data"
+
+

file:b/LockControl.py (new)
--- /dev/null
+++ b/LockControl.py
@@ -1,1 +1,31 @@
+import RPi.GPIO as GPIO
+import time
 
+def init():
+
+	GPIO.setup(16, GPIO.OUT)
+	GPIO.setup(18, GPIO.OUT)
+
+	GPIO.output(16, False)
+	GPIO.output(18, False)
+
+	print "Lock server set up"
+
+def command(cmd):
+
+	if cmd == "lock":
+		print "Locking"
+		GPIO.output(16, True)
+		time.sleep(0.2)
+		GPIO.output(16, False)
+
+	elif cmd == "unlock":
+		print "Unlocking"
+		GPIO.output(18, True)
+		time.sleep(0.2)
+		GPIO.output(18, False)
+
+	else:
+		print "Unrecognized Command..."
+
+

file:b/SendCommand.py (new)
--- /dev/null
+++ b/SendCommand.py
@@ -1,1 +1,18 @@
+from HashAuth import HashAuth
+from LockCommand import LockCommand
 
+import pickle
+import socket
+
+def send_command(command, secret_key, server, port):
+
+	sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+
+	auth_obj = HashAuth(secret_key)
+
+	lock_command = LockCommand(command, auth_obj);
+
+	sock.sendto(pickle.dumps(lock_command), (server, port))
+
+	print "Command " + command + " sent"
+

file:b/buildozer.spec (new)
--- /dev/null
+++ b/buildozer.spec
@@ -1,1 +1,186 @@
+[app]
 
+# (str) Title of your application
+title = Lock Control
+
+# (str) Package name
+package.name = lockiv
+
+# (str) Package domain (needed for android/ios packaging)
+package.domain = io.dryerzinia
+
+# (str) Source code where the main.py live
+source.dir = .
+
+# (list) Source files to include (let empty to include all the files)
+source.include_exts = py,png,jpg,kv,atlas
+
+# (list) Source files to exclude (let empty to not exclude anything)
+#source.exclude_exts = spec
+
+# (list) List of directory to exclude (let empty to not exclude anything)
+#source.exclude_dirs = tests, bin
+
+# (list) List of exclusions using pattern matching
+#source.exclude_patterns = license,images/*/*.jpg
+
+# (str) Application versioning (method 1)
+#version.regex = __version__ = ['"](.*)'['"]
+#version.filename = %(source.dir)s/main.py
+
+# (str) Application versioning (method 2)
+version = 1.0
+
+# (list) Application requirements
+requirements = kivy
+
+# (list) Garden requirements
+#garden_requirements =
+
+# (str) Presplash of the application
+#presplash.filename = %(source.dir)s/data/presplash.png
+
+# (str) Icon of the application
+#icon.filename = %(source.dir)s/data/icon.png
+
+# (str) Supported orientation (one of landscape, portrait or all)
+orientation = all
+
+# (bool) Indicate if the application should be fullscreen or not
+fullscreen = 1
+
+
+#
+# Android specific
+#
+
+# (list) Permissions
+android.permissions = INTERNET
+
+# (int) Android API to use
+android.api = 16
+
+# (int) Minimum API required (8 = Android 2.2 devices)
+#android.minapi = 8
+
+# (int) Android SDK version to use
+#android.sdk = 21
+
+# (str) Android NDK version to use
+android.ndk = 9d
+
+# (bool) Use --private data storage (True) or --dir public storage (False)
+#android.private_storage = True
+
+# (str) Android NDK directory (if empty, it will be automatically downloaded.)
+android.ndk_path = /opt/android-ndk
+
+# (str) Android SDK directory (if empty, it will be automatically downloaded.)
+android.sdk_path = /opt/android-sdk
+
+# (str) python-for-android git clone directory (if empty, it will be automatically cloned from github)
+#android.p4a_dir =
+
+# (list) python-for-android whitelist
+#android.p4a_whitelist =
+
+# (str) Android entry point, default is ok for Kivy-based app
+#android.entrypoint = org.renpy.android.PythonActivity
+
+# (list) List of Java .jar files to add to the libs so that pyjnius can access
+# their classes. Don't add jars that you do not need, since extra jars can slow
+# down the build process. Allows wildcards matching, for example:
+# OUYA-ODK/libs/*.jar
+#android.add_jars = foo.jar,bar.jar,path/to/more/*.jar
+
+# (list) List of Java files to add to the android project (can be java or a
+# directory containing the files)
+#android.add_src =
+
+# (str) python-for-android branch to use, if not master, useful to try
+# not yet merged features.
+#android.branch = master
+
+# (str) OUYA Console category. Should be one of GAME or APP
+# If you leave this blank, OUYA support will not be enabled
+#android.ouya.category = GAME
+
+# (str) Filename of OUYA Console icon. It must be a 732x412 png image.
+#android.ouya.icon.filename = %(source.dir)s/data/ouya_icon.png
+
+# (str) XML file to include as an intent filters in <activity> tag
+#android.manifest.intent_filters =
+
+# (list) Android additionnal libraries to copy into libs/armeabi
+#android.add_libs_armeabi = libs/android/*.so
+#android.add_libs_armeabi_v7a = libs/android-v7/*.so
+#android.add_libs_x86 = libs/android-x86/*.so
+#android.add_libs_mips = libs/android-mips/*.so
+
+# (bool) Indicate whether the screen should stay on
+# Don't forget to add the WAKE_LOCK permission if you set this to True
+#android.wakelock = False
+
+# (list) Android application meta-data to set (key=value format)
+#android.meta_data =
+
+# (list) Android library project to add (will be added in the
+# project.properties automatically.)
+#android.library_references =
+
+#
+# iOS specific
+#
+
+# (str) Name of the certificate to use for signing the debug version
+# Get a list of available identities: buildozer ios list_identities
+#ios.codesign.debug = "iPhone Developer: <lastname> <firstname> (<hexstring>)"
+
+# (str) Name of the certificate to use for signing the release version
+#ios.codesign.release = %(ios.codesign.debug)s
+
+
+[buildozer]
+
+# (int) Log level (0 = error only, 1 = info, 2 = debug (with command output))
+log_level = 1
+
+
+# -----------------------------------------------------------------------------
+# List as sections
+#
+# You can define all the "list" as [section:key].
+# Each line will be considered as a option to the list.
+# Let's take [app] / source.exclude_patterns.
+# Instead of doing:
+#
+#     [app]
+#     source.exclude_patterns = license,data/audio/*.wav,data/images/original/*
+#
+# This can be translated into:
+#
+#     [app:source.exclude_patterns]
+#     license
+#     data/audio/*.wav
+#     data/images/original/*
+#
+
+
+# -----------------------------------------------------------------------------
+# Profiles
+#
+# You can extend section / key with a profile
+# For example, you want to deploy a demo version of your application without
+# HD content. You could first change the title to add "(demo)" in the name
+# and extend the excluded directories to remove the HD content.
+#
+#     [app@demo]
+#     title = My Application (demo)
+#
+#     [app:source.exclude_patterns@demo]
+#     images/hd/*
+#
+# Then, invoke the command line with the "demo" profile:
+#
+#     buildozer --profile demo android debug
+

file:b/lock_config.py (new)
--- /dev/null
+++ b/lock_config.py
@@ -1,1 +1,5 @@
+SERVER_IP = "0.0.0.0"
+SERVER = "dryerzinia.mine.nu"
+PORT = 46657
+KEY = "Ldp{!~&k{M6=\"X$_<@4@&>itO$d^~K(4irTU>9l&>Z\"HXw?`yvYD`)+P,TFo/T+ki+9a$JC:(7glsP%QNT&GPiTH+ZOb_f4C;gkdJcF=r!kF%%vqsh%5P++uq{m=Y5)_"
 

file:b/lockiv.kv (new)
--- /dev/null
+++ b/lockiv.kv
@@ -1,1 +1,50 @@
+#:kivy 1.8
 
+<Lockiv>
+
+	key_pass: key_pass
+	key_pass_pop: key_pass_pop
+
+	BoxLayout:
+		orientation: 'vertical'
+		BoxLayout:
+			Button:
+				text: 'Lock'
+				on_press: root.lock()
+			Button:
+				text: 'Unlock'
+				on_press: root.unlock()
+		Button:
+			text: 'Settings'
+			on_press: root.open_settings()
+
+	Popup:
+
+		title: 'Key Password'
+
+		id: key_pass_pop
+
+		pos: (root.size[0]*0.125, root.size[1]/2 - 80)
+
+		size_hint: (None, None)
+		size: (root.size[0]*0.75, 160)
+
+		content:
+
+		BoxLayout:
+
+			orientation: 'vertical'
+
+			TextInput:
+				id: key_pass
+				password: True
+				multiline: False
+				size_hint: (None, None)
+				size: (key_pass_pop.size[0]*0.955, 40)
+			Button:
+				size_hint: (None, None)
+				size: (key_pass_pop.size[0]*0.955, 40)
+				text: 'Start'
+				on_press: root.set_password()
+
+

file:b/main.py (new)
--- /dev/null
+++ b/main.py
@@ -1,1 +1,64 @@
+from kivy.app import App
+from kivy.uix.popup import Popup
+from kivy.uix.label import Label
+from kivy.uix.button import Button
+from kivy.uix.floatlayout import FloatLayout
+from kivy.uix.boxlayout import BoxLayout
+from kivy.uix.settings import SettingsWithNoMenu
+from kivy.properties import ObjectProperty
 
+from SendCommand import send_command
+
+from functools import partial
+
+import hashlib
+
+import lock_config
+
+def close_settings(pair, extra):
+	pair[0].remove_widget(pair[1])
+
+class Lockiv(FloatLayout):
+
+	key_pass = ObjectProperty()
+	key_pass_pop = ObjectProperty()
+
+	def unlock(self):
+		send_command("unlock", self.secret_key, self.app.config.get("Lockiv", "SERVER"), int(self.app.config.get("Lockiv", "PORT")))
+
+	def lock(self):
+		send_command("lock", self.secret_key, self.app.config.get("Lockiv", "SERVER"), int(self.app.config.get("Lockiv", "PORT")))
+
+	def set_password(self):
+		password = self.key_pass.text
+		self.secret_key = hashlib.sha512( password + self.app.config.get("Lockiv", "KEY") ).digest()
+		self.remove_widget(self.key_pass_pop)
+
+	def open_settings(self):
+		bl = BoxLayout(orientation='vertical')
+		s = SettingsWithNoMenu()
+		s.add_json_panel('Settings', self.app.config, data='[{"type": "title","title": "Lock"},{"type": "string","title": "Lock Address","desc": "Address of the locking mechanism","section": "Lockiv","key": "SERVER"},{"type": "numeric","title": "Lock Port","desc": "Port of the locking mechanism","section": "Lockiv","key": "PORT"},{"type": "string","title": "Lock Key","desc": "Key to the lock","section": "Lockiv","key": "KEY"}]')
+		b = Button(text='Close', size_hint=(None, None), size=(self.size[0], 60))
+		b.bind(on_press=partial(close_settings, (self, bl)))
+		bl.add_widget(s)
+		bl.add_widget(b)
+		self.add_widget(bl)
+		pass
+
+
+class LockivApp(App):
+
+	def build(self):
+		l = Lockiv()
+		l.app = self
+		return l
+
+	def build_config(self, config):
+		config.setdefaults("Lockiv", {
+			"KEY": lock_config.KEY,
+			"SERVER": lock_config.SERVER,
+			"PORT": lock_config.PORT
+		})
+
+LockivApp().run()
+