1 module libssh.key; 2 3 import core.stdc..string : memcpy; 4 5 import libssh.c_bindings.libssh; 6 import libssh.errors; 7 import libssh.utils; 8 9 enum PublicKeyHashType : int { 10 SHA1 = ssh_publickey_hash_type.SSH_PUBLICKEY_HASH_SHA1, 11 MD5 = ssh_publickey_hash_type.SSH_PUBLICKEY_HASH_MD5, 12 } 13 14 enum KeyComparePart : ssh_keycmp_e { 15 Public = ssh_keycmp_e.SSH_KEY_CMP_PUBLIC, 16 Private = ssh_keycmp_e.SSH_KEY_CMP_PRIVATE, 17 } 18 19 enum KeyType : ssh_keytypes_e { 20 Unknown = ssh_keytypes_e.SSH_KEYTYPE_UNKNOWN, 21 DSS = ssh_keytypes_e.SSH_KEYTYPE_DSS, 22 RSA = ssh_keytypes_e.SSH_KEYTYPE_RSA, 23 RSA1 = ssh_keytypes_e.SSH_KEYTYPE_RSA1, 24 ECDSA = ssh_keytypes_e.SSH_KEYTYPE_ECDSA, 25 ED25519 = ssh_keytypes_e.SSH_KEYTYPE_ED25519, 26 } 27 28 enum PublicKeyState : int { 29 Error = ssh_publickey_state_e.SSH_PUBLICKEY_STATE_ERROR, 30 None = ssh_publickey_state_e.SSH_PUBLICKEY_STATE_NONE, 31 Valid = ssh_publickey_state_e.SSH_PUBLICKEY_STATE_VALID, 32 Wrong = ssh_publickey_state_e.SSH_PUBLICKEY_STATE_WRONG, 33 } 34 35 class SSHKey : Disposable { 36 alias AuthCallback = string delegate(string prompt, bool echo, bool verify); 37 38 @property bool isPrivate() { 39 return ssh_key_is_private(this._key) == 0 ? false : true; 40 } 41 42 @property bool isPublic() { 43 return ssh_key_is_public(this._key) == 0 ? false : true; 44 } 45 46 @property KeyType keyType() { 47 return cast(KeyType) ssh_key_type(this._key); 48 } 49 50 @property string ecdsaName() { 51 return fromStrZ(ssh_pki_key_ecdsa_name(this._key)); 52 } 53 54 ubyte[] getHash(PublicKeyHashType hashType) { 55 ubyte* hash; 56 size_t hashLength; 57 auto rc = ssh_get_publickey_hash(this._key, cast(ssh_publickey_hash_type) hashType, &hash, &hashLength); 58 checkForRCError(rc, rc); 59 scope(exit) ssh_clean_pubkey_hash(&hash); 60 61 ubyte[] result = new ubyte[hashLength]; 62 memcpy(result.ptr, hash, hashLength); 63 return result; 64 } 65 66 void exportPrivateKeyToFile(string passPhrase, string fileName, AuthCallback authFn = null) { 67 auto rc = ssh_pki_export_privkey_file(this._key, toStrZ(passPhrase), 68 authFn is null ? null : &nativeAuthCallback, authFn.ptr, toStrZ(fileName)); 69 checkForRCError(rc, rc); 70 } 71 72 SSHKey exportPrivateKeyToPublicKey() { 73 ssh_key result; 74 auto rc = ssh_pki_export_privkey_to_pubkey(this._key, &result); 75 checkForRCError(rc, rc); 76 checkForNullError(result, rc); 77 return new SSHKey(result); 78 } 79 80 string exportPrivateKeyToBase64() { 81 char* result; 82 auto rc = ssh_pki_export_pubkey_base64(this._key, &result); 83 checkForNullError(result, rc); 84 scope(exit) ssh_string_free_char(result); 85 checkForRCError(rc, rc); 86 return copyFromStrZ(result); 87 } 88 89 override bool opEquals(Object o) { 90 auto b = cast(SSHKey) o; 91 if (b is null) { 92 return false; 93 } 94 return isKeysEqual(this, b, KeyComparePart.Private) && 95 isKeysEqual(this, b, KeyComparePart.Public); 96 } 97 98 static bool isKeysEqual(const SSHKey a, const SSHKey b, KeyComparePart comparePart) { 99 return ssh_key_cmp(a._key, b._key, comparePart) == 0 ? true : false; 100 } 101 102 static KeyType keyTypeFromString(string name) { 103 return cast(KeyType) ssh_key_type_from_name(toStrZ(name)); 104 } 105 106 static string keyTypeToString(KeyType kt) { 107 return copyFromStrZ(ssh_key_type_to_char(kt)); 108 } 109 110 static SSHKey importPrivateKeyFromBase64(string b64, string passPhrase, 111 AuthCallback authFn = null) { 112 ssh_key result; 113 auto rc = ssh_pki_import_privkey_base64(toStrZ(b64), toStrZ(passPhrase), 114 authFn is null ? null : &nativeAuthCallback, authFn.ptr, &result); 115 checkForRCError(rc, rc); 116 checkForNullError(result, rc); 117 return new SSHKey(result); 118 } 119 120 static SSHKey importPrivateKeyFromFile(string fileName, string passPhrase, 121 AuthCallback authFn = null) { 122 ssh_key result; 123 auto rc = ssh_pki_import_privkey_file(toStrZ(fileName), toStrZ(passPhrase), 124 authFn is null ? null : &nativeAuthCallback, authFn.ptr, &result); 125 checkForRCError(rc, rc); 126 checkForNullError(result, rc); 127 return new SSHKey(result); 128 } 129 130 static SSHKey importPublicKeyFromBase64(string b64, KeyType keyType) { 131 ssh_key result; 132 auto rc = ssh_pki_import_pubkey_base64(toStrZ(b64), cast(ssh_keytypes_e) keyType, 133 &result); 134 checkForRCError(rc, rc); 135 checkForNullError(result, rc); 136 return new SSHKey(result); 137 } 138 139 static SSHKey importPublicKeyFromFile(string fileName) { 140 ssh_key result; 141 auto rc = ssh_pki_import_pubkey_file(toStrZ(fileName), &result); 142 checkForRCError(rc, rc); 143 checkForNullError(result, rc); 144 return new SSHKey(result); 145 } 146 147 static SSHKey generate(KeyType keyType, int bitsLength) { 148 ssh_key result; 149 auto rc = ssh_pki_generate(cast(ssh_keytypes_e) keyType, bitsLength, &result); 150 checkForRCError(rc, rc); 151 checkForNullError(result, rc); 152 return new SSHKey(result); 153 } 154 155 override void dispose() { 156 this._dispose(false); 157 } 158 159 this() { 160 auto key = ssh_key_new(); 161 checkForNullError(key, "Error while creating ssh key"); 162 this(key); 163 } 164 165 ~this() { 166 this._dispose(true); 167 } 168 169 package { 170 this(ssh_key key) { 171 this._key = key; 172 } 173 174 ssh_key _key; 175 } 176 177 private { 178 void _dispose(bool fromDtor) { 179 if (this._key !is null) { 180 ssh_key_free(this._key); 181 this._key = null; 182 } 183 } 184 } 185 } 186 187 188 private { 189 extern(C) int nativeAuthCallback(const char *prompt, char *buf, size_t len, 190 int echo, int verify, void *userdata) { 191 auto cb = cast(SSHKey.AuthCallback*) userdata; 192 193 if (cb is null) { 194 return SSH_ERROR; 195 } 196 197 try { 198 auto result = (*cb)(fromStrZ(prompt), echo == 0 ? false : true, 199 verify == 0 ? false : true); 200 if (result is null) { 201 return SSH_ERROR; 202 } 203 204 if (len < result.length + 1) { 205 return SSH_ERROR; 206 } 207 208 import core.stdc..string : memcpy; 209 memcpy(buf, result.ptr, result.length); 210 buf[result.length] = 0; 211 212 return SSH_OK; 213 } catch (Exception) { 214 return SSH_ERROR; 215 } 216 } 217 }