Skip to content

Instantly share code, notes, and snippets.

@applicato
Forked from giannitedesco/gash.py
Created September 26, 2018 13:01
Show Gist options
  • Select an option

  • Save applicato/f8bcfad3c8c9122570cc80eb685747a2 to your computer and use it in GitHub Desktop.

Select an option

Save applicato/f8bcfad3c8c9122570cc80eb685747a2 to your computer and use it in GitHub Desktop.
Send secret messages to someone using their public ssh key
#!/usr/bin/python3
__copyright__ = "Copyright (c) 2018 Gianni Tedesco"
__licence__ = "GPLv3"
__doc__ = "Tool for sending secret messages using ssh keys"
from argparse import ArgumentParser
try:
import nacl.utils
from nacl.signing import SigningKey,VerifyKey
from nacl.public import PrivateKey,PublicKey,Box
_nacl = True
except:
_nacl = False
from base64 import b64decode,b64encode
from struct import unpack
from sys import argv
from operator import itemgetter
import hashlib
def _get_string(buf):
sz = unpack('>I', buf[:4])[0]
s = buf[4:4 + sz]
buf = buf[4 + sz:]
return (s, buf)
def _get_u32(buf):
ret = unpack('>I', buf[:4])[0]
buf = buf[4:]
return (ret, buf)
def _get_strings(buf):
lst = list()
while len(buf) >= 4:
(s, buf) = _get_string(buf)
lst.append(s)
lst = tuple(lst)
return lst
class SshPublicKey(tuple):
__slots__ = ()
algo = property(itemgetter(0))
key = property(itemgetter(1))
def __new__(_cls, algo, key):
return super().__new__(_cls, (str(algo), bytes(key)))
def save_as_nacl(self, fn):
with open(fn, 'wb') as f:
f.write(self.key)
def as_nacl(self):
if not _nacl:
raise NotImplementedError
return VerifyKey(self.key).to_curve25519_public_key()
@classmethod
def load(_cls, buf):
cipher, key, *rest = buf.split()
kpem = b64decode(key)
cipher2, key2, *_ = _get_strings(kpem)
cipher2 = cipher2.decode('ascii')
assert(cipher == cipher2)
return _cls(cipher2, key2)
@classmethod
def fromfile(_cls, fn):
return _cls.load(open(fn).read())
class SshPrivateKey(tuple):
__slots__ = ()
algo = property(itemgetter(0))
key = property(itemgetter(1))
def __new__(_cls, algo, key):
return super().__new__(_cls, (str(algo), bytes(key)))
def save_as_nacl(self, fn):
with open(fn, 'wb') as f:
f.write(self.key)
def as_nacl(self):
if not _nacl:
raise NotImplementedError
return SigningKey(self.key).to_curve25519_private_key()
@classmethod
def load(_cls, buf):
begin, *middle, end, _ = buf.split('\n')
assert(not _)
assert(begin == '-----BEGIN OPENSSH PRIVATE KEY-----')
assert(end == '-----END OPENSSH PRIVATE KEY-----')
data = b64decode(''.join(middle))
tag = b'openssh-key-v1\x00'
begin, data = data[:len(tag)], data[len(tag):]
assert(begin == tag)
ciphername, data = _get_string(data)
kdfname, data = _get_string(data)
kdf, data = _get_string(data)
nkeys, data = _get_u32(data)
pubkey, data = _get_string(data)
encrypted_len, data = _get_u32(data)
assert(ciphername == b'none')
assert(kdfname == b'none')
assert(encrypted_len == len(data))
assert(encrypted_len >= 8)
check1, data = _get_u32(data)
check2, data = _get_u32(data)
assert(check1 == check2)
cipher, data = _get_string(data)
pubkey, data = _get_string(data)
privkey, data = _get_string(data)
comment, data = _get_string(data)
if _nacl:
#sk = SigningKey(privkey[:32]).to_curve25519_private_key()
#pk = VerifyKey(privkey[32:]).to_curve25519_public_key()
#print('sk', bytes(sk))
#print('pk', pk)
#print(bytes(seed.to_curve25519_private_key().public_key))
#assert(pk == sk)
pass
return _cls(cipher.decode('ascii'), privkey[:32])
@classmethod
def fromfile(_cls, fn):
return _cls.load(open(fn).read())
def main():
opts = ArgumentParser(description='gash - Gianni\'s Ad-Hoc Secure Hell')
opts.add_argument('message',
metavar = 'message',
nargs = '*',
help = 'UTF-8 text to encrypt/decrypt')
opts.add_argument('--secret', '-s',
metavar = 'path',
type = str,
default = 'id_ed25519',
help = 'Path to secret key')
opts.add_argument('--public', '-p',
metavar = 'path',
type = str,
default = 'id_ed25519.pub',
help = 'Path to public key')
opts.add_argument('--decrypt', '-d',
default = False,
action = 'store_true',
help = 'Decrypt')
args = opts.parse_args()
sk = SshPrivateKey.fromfile(args.secret).as_nacl()
pk = SshPublicKey.fromfile(args.public).as_nacl()
box = Box(sk, pk)
if args.decrypt:
for m in args.message:
ciphertext = b64decode(m)
plaintext = box.decrypt(ciphertext)
print(plaintext.decode('utf-8'))
else:
nonce = nacl.utils.random(Box.NONCE_SIZE)
plaintext = ' '.join(args.message).encode('utf-8')
ciphertext = box.encrypt(plaintext, nonce)
message = b64encode(ciphertext)
print(message.decode('utf-8'))
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment