gemini.git

going-flying.com gemini git repository

summary

tree

log

refs

ae879fbe5902b4951c86b1da630e7dabc2015c13 - Matthew Ernisse - 1612636964

totp qr creator!

view tree

view raw

diff --git a/cgi-bin/converter b/cgi-bin/converter
index 9a4b843..7aaf40d 100755
--- a/cgi-bin/converter
+++ b/cgi-bin/converter
@@ -29,7 +29,9 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 '''
 import base64
 import os
+import secrets
 import sys
+import qrcode
 from urllib.parse import parse_qs, quote, unquote, urlparse
 
 
@@ -54,6 +56,10 @@ Display a number in binary, decimal and hexadecimal.  If your input cannot be re
 => url/encode		URL Quote
 => url/decode		URL Unquote
 
+## Random Stuff
+
+=> random/base32	Random BASE32 secret
+
 ## Suggestions?
 Want to see something here?  Feel free to drop me a line.
 => mailto:matt@going-flying.com
@@ -130,10 +136,13 @@ class GeminiCGI(object):
 			
 
 
-def encode_response(input, result):
+def encode_response(cgi, input, result):
 	global BACK_LINK
 	cgi.Response.Ok('text/gemini')
 
+	if type(result) == bytes:
+		result = result.decode('utf-8')
+
 	output = f'# Input\n{input}\n\n'
 	output += f'# Output\n{result}\n\n'
 	output += f'=> {BACK_LINK}	Back\n'
@@ -181,6 +190,25 @@ def result_table(cgi, input, b, d, h):
 	print(output)
 
 
+def secret_response(cgi, val, qr=False):
+	global BACK_LINK
+	cgi.Response.Ok('text/gemini')
+
+	if type(val) == bytes:
+		val = val.decode('utf-8')
+
+	output = f'# Your secret\n'
+	output += f'{val}\n'
+
+	if qr:
+		e_val = base64.b64encode(val.encode('utf-8')).decode('utf-8')
+		output += f'=> {BACK_LINK}random/qr/{e_val}\n\n'
+
+	output += f'=> {BACK_LINK}	Back\n'
+	output += f'=> /	Home'
+	print(output)
+
+
 if __name__ == '__main__':
 	cgi = GeminiCGI()
 
@@ -213,6 +241,25 @@ if __name__ == '__main__':
 		elif cgi.path_info == 'url/decode':
 			cgi.Response.Input('String to decode?')
 
+		# Random Stuff
+		elif cgi.path_info == 'random/base32':
+			val = base64.b32encode(secrets.token_bytes(30))
+			secret_response(cgi, val)
+		
+		elif cgi.path_info == 'random/totp':
+			cgi.Response.Input('Account Label?')
+
+		elif cgi.path_info.startswith('random/qr'):
+			val = cgi.path_info.split('/')[-1]
+			val = base64.b64decode(val).decode('utf-8')
+			cgi.Response.Ok('image/png')
+			png = qrcode.make(val)
+
+			sys.stdout.flush()
+			png.save(sys.stdout.buffer)
+
+		else:
+			cgi.Response.Redirect(BACK_LINK)
 	else:
 		if not cgi.path_info:
 			cgi.Response.Redirect(BACK_LINK)
@@ -262,25 +309,23 @@ if __name__ == '__main__':
 			s = cgi.query_dequoted
 			try:
 				res = base64.b64encode(s.encode('utf-8'))
-				res = res.decode('utf-8')
 			except Exception as e:
 				sys.stderr.write(f'{e!s}\n')
 				sys.stderr.flush()
 				cgi.Response.Fail()
 				sys.exit()
 
-			encode_response(s, res)
+			encode_response(cgi, s, res)
 
 		elif cgi.path_info == 'base64/decode':
 			s = cgi.query_dequoted
 			try:
 				res = base64.b64decode(s, validate=True)
-				res = res.decode('utf-8')
 			except Exception:
 				cgi.Response.Fail()
 				sys.exit()
 
-			encode_response(s, res)
+			encode_response(cgi, s, res)
 
 		# urlencode/decode
 		elif cgi.path_info == 'url/encode':
@@ -291,7 +336,7 @@ if __name__ == '__main__':
 				cgi.Response.Fail()
 				sys.exit()
 
-			encode_response(s, res)
+			encode_response(cgi, s, res)
 
 		elif cgi.path_info == 'url/decode':
 			s = cgi.query_dequoted
@@ -301,7 +346,19 @@ if __name__ == '__main__':
 				cgi.Response.Fail()
 				sys.exit()
 
-			encode_response(s, res)
+			encode_response(cgi, s, res)
+
+		# Random Stuff
+		elif cgi.path_info == 'random/totp':
+			# Leave pre-urlencoded!
+			label = cgi.query_string
+			val = base64.b32encode(secrets.token_bytes(30))
+			val = val.decode('utf-8')
+			secret_response(
+				cgi,
+				f'otpauth://totp/{label}?secret={val}',
+				True
+			)
 
 		else:
 			cgi.Response.NotFound()