diff --git a/net/addr/app/templates/base.html b/net/addr/app/templates/base.html
index 5a5d7ab..3979454 100644
--- a/net/addr/app/templates/base.html
+++ b/net/addr/app/templates/base.html
@@ -27,7 +27,7 @@
{% block body %}{% endblock %}
diff --git a/net/addr/app/views.py b/net/addr/app/views.py
index 858ae63..f1f12e5 100644
--- a/net/addr/app/views.py
+++ b/net/addr/app/views.py
@@ -8,12 +8,17 @@ def index():
# First we define interactive browsers
_intbrowsers = {'camino': ['http://caminobrowser.org/', 'Camino'],
'chrome': ['https://www.google.com/chrome/', 'Google Chrome'],
+ 'edge': ['https://www.microsoft.com/en-us/windows/microsoft-edge',
+ 'Microsoft Edge'],
'firefox': ['https://www.mozilla.org/firefox/', 'Mozilla Firefox'],
'galeon': ['http://galeon.sourceforge.net/', 'Galeon'],
'kmeleon': ['http://kmeleonbrowser.org/', 'K-Meleon'],
'konqueror': ['https://konqueror.org/', 'Konqueror'],
'links': ['http://links.twibright.com/', 'Links'],
- 'lynx': ['http://lynx.browser.org/', 'Lynx']}
+ 'msie': ['https://en.wikipedia.org/wiki/Internet_Explorer',
+ 'Microsoft Internet Explorer'],
+ 'lynx': ['http://lynx.browser.org/', 'Lynx'],
+ 'safari': ['https://www.apple.com/safari/', 'Apple Safari']}
_os = {'aix': ['https://www.ibm.com/power/operating-systems/aix', 'AIX'],
'amiga': ['http://www.amiga.org/', 'Amiga'],
'android': ['https://www.android.com/', 'Android'],
@@ -30,6 +35,7 @@ def index():
'wii': ['http://wii.com/', 'Wii'],
'windows': ['https://www.microsoft.com/windows/', 'Windows']}
_alts = {'amiga': ' (have you tried AROS yet?)',
+ 'android': ' (have you tried LineageOS yet?)',
'macos': ' (have you tried ElementaryOS yet?)',
'sgi': ' (have you tried MaXX yet?)',
'windows': ' (have you tried ReactOS yet?)'}
@@ -92,4 +98,4 @@ def about():
@app.route('/usage', methods = ['GET'])
def usage():
- return(render_template('usage.html'))
\ No newline at end of file
+ return(render_template('usage.html'))
diff --git a/net/ssh/keygen/keygenstructs.py b/net/ssh/keygen/keygenstructs.py
new file mode 100644
index 0000000..f2b8af4
--- /dev/null
+++ b/net/ssh/keygen/keygenstructs.py
@@ -0,0 +1,7 @@
+#!/usr/bin/env python3
+
+# Generate a struct for generating keys.
+
+class constructor(object):
+ def __init__(self, keyblob, encrypted = False):
+ self.keyblob = keyblob
diff --git a/net/ssh/keygen/keystructs.py b/net/ssh/keygen/keystructs.py
new file mode 100644
index 0000000..74dca13
--- /dev/null
+++ b/net/ssh/keygen/keystructs.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+
+# Parse existing keys
+
+class constructor(object):
+ # These are various struct formats for the "new"-style OpenSSH private keys.
+ # REF1: https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?annotate=1.1
+ # REF2: https://github.com/openssh/openssh-portable/blob/94bc1e7ffba3cbdea8c7dcdab8376bf29283128f/sshkey.c
+ def __init__(self, ssh_keyblob):
+ # "keyblob" is the raw binary of an extracted (i.e. "---...---"-removed) and un-base64'd private key.
+ self.keyblob = ssh_keyblob
+ # 'encrypted' if it's encrypted, 'none' if plaintext. This is determined via processing.
+ self.enctype = 'none'
+ # This is the header. It is used by both encrypted and unencrypted keys.
+ self.header = ''.join((
+ '14cx', # "openssh-key-v1" and null byte (6f70656e7373682d6b65792d7631 00) ("magic bytes")
+ 'i' # separator, always 00000004
+ ))
+ # Only two cipher types that I know of that are used.
+ self.ciphertype = {
+ 'none': '4c', # 6e6f6e65
+ 'aes256-cbc': '10c'} # 6165733235362d636263 ("aes256-cbc")
+ # This separator is present in both.
+ self.sep1 = 'i'
+ # The name of the key encryption, if encrypted. These are the only two I know of that are used.
+ self.kdfname = {
+ 'none': '4c', # 6e6f6e65 ("none")
+ 'bcrypt': '6c'} # 626372797074 ("bcrypt")
+ ########################################### ENCRYPTED KEYS ONLY ################################################
+ # KDF options
+ self.kdfopts = {
+ 'none': '0i', # zero-length
+ 'encrypted': '24i'} # 24-length int
+ # The length of the salt. Default is 16 (REF2:67)
+ self.saltlen = {
+ 'none': '0i', # TODO: do unencrypted still have salts?
+ 'encrypted': '4i'} # 16 bytes length?
+ # The key needs to be parsed incrementally to have these lengths adjusted.
+ self.salt = {
+ 'none': '0c',
+ 'encrypted': '4c'} # This value may change based on self.saltlen['encrypted']'s value.
+ # The number of rounds for the key; default is 16 (REF2:69).
+ self.rounds = {
+ 'none': '0i', # TODO: do unencrypted still have rounds?
+ 'encrypted': '16i'}
+ ################################################################################################################
+ # Number of public keys.
+ self.numpub = 'i'
+ # That's all we can populate now. The rest is handled below via functions.
+ self._chkencrypt()
+
+ def _chkencrypt(self):
+ pass
+
+
+class legacy_constructor(object):
+ # These are various struct formats for the "old"-style OpenSSH private keys.
+ def __init__(self, keyblob):
+ self.keyblob = keyblob
diff --git a/net/ssh/puttyconv.py b/net/ssh/puttyconv.py
new file mode 100644
index 0000000..880ba52
--- /dev/null
+++ b/net/ssh/puttyconv.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+
+## INCOMPLETE ##
+# TODO
+
+import argparse
+
+def parseArgs():
+ args = argparse.ArgumentParser(description = 'Convert private and public SSH keys between PuTTY/OpenSSH format')
+ args.add_argument('-l', '--legacy',
+ dest = 'legacy_ssh',
+ action = 'store_true',
+ help = ('If specified, try to handle the OpenSSH key as the legacy format ("") rather than the '
+ 'newer '
+ 'more compact format ("")'))
+ ktype = args.add_mutually_exclusive_group()
+ ktype.add_argument('-s', '--ssh',
+ dest = 'ktype',
+ const = 'openssh',
+ help = ('Force the conversion to OpenSSH format'))
+ ktype.add_argument('-p', '--putty',
+ dest = 'ktype',
+ const = 'putty',
+ help = ('Force the conversion to PuTTY format'))