Created
January 26, 2016 06:30
-
-
Save zougloub/3058d56857ba400b7ec3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 vi:noet | |
# Dump a clear-text version of an SSH key from gpg-agent | |
__author__ = "Jérôme Carretero <cJ-tub@zougloub.eu>" | |
__licence__ = "MIT" | |
import sys, io, subprocess, re, ctypes | |
libgcrypt = ctypes.CDLL("libgcrypt.so") | |
libgcrypt.gcry_cipher_open.argtypes = (ctypes.POINTER(ctypes.c_void_p), ctypes.c_int, ctypes.c_int, ctypes.c_int) | |
libgcrypt.gcry_cipher_open.restype = ctypes.c_int | |
libgcrypt.gcry_cipher_setkey.argtypes = (ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int) | |
libgcrypt.gcry_cipher_setkey.restype = ctypes.c_int | |
libgcrypt.gcry_cipher_decrypt.argtypes = (ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_char_p, ctypes.c_int) | |
libgcrypt.gcry_cipher_decrypt.restype = ctypes.c_int | |
libgcrypt.gcry_cipher_encrypt.argtypes = (ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_char_p, ctypes.c_int) | |
libgcrypt.gcry_cipher_encrypt.restype = ctypes.c_int | |
libgcrypt.gcry_cipher_close.argtypes = (ctypes.c_void_p,) | |
GCRY_CIPHER_AES128 = 7 | |
GCRY_CIPHER_MODE_AESWRAP = 7 | |
def unwrap_key(kek, crypted): | |
cipherhd = ctypes.c_void_p() | |
res = libgcrypt.gcry_cipher_open(ctypes.byref(cipherhd), GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0) | |
assert res == 0, res | |
res = libgcrypt.gcry_cipher_setkey(cipherhd, kek, len(kek)) | |
assert res == 0, res | |
xl = len(crypted)-8 | |
x = ctypes.create_string_buffer(xl) | |
res = libgcrypt.gcry_cipher_decrypt(cipherhd, x, xl, crypted, len(crypted)) | |
assert res == 0, res | |
libgcrypt.gcry_cipher_close(cipherhd) | |
return x.raw | |
def gpg_agent(*args): | |
cmd = ['gpg-connect-agent'] + list(args) + ['/bye'] | |
out = subprocess.check_output(cmd) | |
return out | |
if __name__ == '__main__': | |
if len(sys.argv) == 1: | |
print("Select a keygrab (after KEYINFO) from the following" \ | |
"and pass it as argument to this script") | |
cmd = ('ssh-add', '-l') | |
keys = subprocess.check_output(cmd).decode().splitlines() | |
keylist = gpg_agent("KEYINFO --ssh-list").decode().splitlines()[:-1] | |
for idx_key, (key_ssh, key_gpg) in enumerate(zip(keys, keylist)): | |
print("% 3d: %s %s" % (idx_key, key_gpg, key_ssh)) | |
raise SystemExit() | |
keygrab = sys.argv[1] | |
out = gpg_agent( | |
"OPTION lc-ctype=C", | |
"OPTION lc-messages=C", | |
"KEYWRAP_KEY --export", | |
"EXPORT_KEY %s" % keygrab, | |
) | |
pat = re.compile(br"^OK\nOK\nD (?P<keywrap_key>.*)\nOK\nD (?P<wrapped>.*)\nOK\n$", re.DOTALL) | |
m = re.match(pat, out) | |
assert m is not None | |
def deassuan(x): | |
out = [] | |
idx_c = 0 | |
while idx_c < len(x): | |
if x[idx_c] == b"%": | |
if x[idx_c:idx_c+3] == b"%0A": | |
out.append(b"\n") | |
elif x[idx_c:idx_c+3] == b"%0D": | |
out.append(b"\r") | |
elif x[idx_c:idx_c+3] == b"%25": | |
out.append(b"%") | |
else: | |
raise NotImplementedError() | |
idx_c += 3 | |
else: | |
out.append(x[idx_c]) | |
idx_c += 1 | |
return bytes(bytearray(out)) | |
keywrap_key = deassuan(m.group("keywrap_key")) | |
ciphertext = deassuan(m.group("wrapped")) | |
key = unwrap_key(keywrap_key, ciphertext) | |
with io.open("%s.key" % keygrab, "wb") as f: | |
f.write(key) | |
print("Wrote the key into %s.key.\n" \ | |
"Now you can add this key back to ~/.gnupg/private-keys-v1.d/" \ | |
" and execute the PASSWD command of ssh-connect-agent to protect it.") | |
Doesn't work for em.
I've three keys that are within private-keys-v1.d, but somehow are not within pubring.kbx. I can still use them to ssh into remote servers. But gpg won't allow me to export them, nor does it list them as even existing.
When I try this script on them I get:
Traceback (most recent call last):
File "/home/user/.gnupg/./a.py", line 94, in <module>
key = unwrap_key(keywrap_key, ciphertext)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/.gnupg/./a.py", line 35, in unwrap_key
assert res == 0, res
^^^^^^^^
AssertionError: 16777261
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Notice the missing % keygrab at the end...