@@ -4,10 +4,53 @@ cackeySSHAgentApprovedApps = [ "pnhechapfaindjhompbnflcldabbghjo", "okddffdblfhhnmhodogpojmfkjmhinfp" ]; + +/* + * XXX:TODO: Expose UI for this + */ +cackeySSHAgentFeatures = { + enabled: true, + includeKeys: true, + includeCerts: true, + legacy: false +}; + +/* + * Feature support checking + */ +function cackeySSHAgentGetSSHKeyTypes() { + var types = []; + + if (cackeySSHAgentFeatures.includeKeys) { + types.push("ssh"); + } + + if (cackeySSHAgentFeatures.includeCerts) { + types.push("x509v3-ssh"); + + if (cackeySSHAgentFeatures.legacy) { + types.push("x509v3-sign"); + } + } + + return(types); +} + +async function cackeySSHAgentGetCertificates() { + var certs; + + if (!cackeySSHAgentFeatures.enabled) { + return([]); + } + + certs = await cackeyListCertificates(); + + return(certs); +} /* * SSH Element Encoding/Decoding */ function cackeySSHAgentEncodeInt(uint32) { @@ -48,14 +91,11 @@ bigInt = bigInt >> 8; } result.reverse(); break; case "object": - result = []; - new Uint8Array(bigInt.toByteArray()).forEach(function(e) { - result.push(e); - }); + result = Array.from(new Uint8Array(bigInt.toByteArray())); break; } result = cackeySSHAgentEncodeLV(result); @@ -86,10 +126,21 @@ return({ value: result, output: input.slice(info.value) }); } + +function cackeySSHAgentEncodeArray(input) { + var result; + + result = cackeySSHAgentEncodeInt(input.length); + input.forEach(function(element) { + result = result.concat(element); + }); + + return(result); +} function cackeySSHAgentEncodeToUTF8Array(str) { var utf8 = []; if (typeof(str) === "string") { @@ -156,38 +207,74 @@ } return(buffer); } -function cackeySSHAgentEncodeCertToKeyAndID(cert) { +function cackeySSHAgentEncodeCertToKeyAndID(cert, keyType) { var result = null, resultKey = null; - var certObj; + var certObj, certBytes; var publicKey; certObj = new X509; if (!certObj) { return(result); } - certObj.readCertHex(cackeySSHAgentEncodeBinaryToHex(cert)); + certBytes = Array.from(new Uint8Array(cert)); + + certObj.readCertHex(cackeySSHAgentEncodeBinaryToHex(certBytes)); publicKey = certObj.getPublicKey(); - switch (publicKey.type) { - case "RSA": - resultKey = cackeySSHAgentEncodeString("ssh-rsa"); - resultKey = resultKey.concat(cackeySSHAgentEncodeBigInt(publicKey.e)); - resultKey = resultKey.concat(cackeySSHAgentEncodeBigInt(publicKey.n)); + switch (keyType) { + case "ssh": + switch (publicKey.type) { + case "RSA": + resultKey = cackeySSHAgentEncodeString("ssh-rsa"); + resultKey = resultKey.concat(cackeySSHAgentEncodeBigInt(publicKey.e)); + resultKey = resultKey.concat(cackeySSHAgentEncodeBigInt(publicKey.n)); + break; + default: + console.log("[cackeySSH] Unsupported public key type:", keyType, "/", publicKey.type, "-- ignoring."); + break; + } + break; + case "x509v3-sign": + resultKey = certBytes; + break; + case "x509v3-ssh": + switch (publicKey.type) { + case "RSA": + resultKey = cackeySSHAgentEncodeString("x509v3-ssh-rsa"); + + /* + * Array of certificates + */ + resultKey = resultKey.concat(cackeySSHAgentEncodeArray([ + cackeySSHAgentEncodeLV(certBytes) + ])); + + /* + * Array of OCSP responses + */ + resultKey = resultKey.concat(cackeySSHAgentEncodeArray([])); + break; + default: + console.log("[cackeySSH] Unsupported public key type:", keyType, "/", publicKey.type, "-- ignoring."); + break; + } break; default: - console.log("[cackeySSH] Unsupported public key type:", publicKey.type, "-- ignoring."); + console.log("[cackeySSH] Unsupported SSH key type:", keyType, "-- ignoring."); + break; } if (resultKey) { result = { id: certObj.getSubjectString(), - type: publicKey.type, + publicKeyType: publicKey.type, + sshKeyType: keyType, key: resultKey }; } return(result); @@ -202,23 +289,25 @@ var keys = []; /* * Get a list of certificates */ - certs = await cackeyListCertificates(); + certs = await cackeySSHAgentGetCertificates(); /* * Convert each certificate to an SSH key blob */ - certs.forEach(function(cert) { - var key; - - key = cackeySSHAgentEncodeCertToKeyAndID(cert.certificate); - - if (key) { - keys.push(key); - } + cackeySSHAgentGetSSHKeyTypes().forEach(function(sshKeyType) { + certs.forEach(function(cert) { + var key; + + key = cackeySSHAgentEncodeCertToKeyAndID(cert.certificate, sshKeyType); + + if (key) { + keys.push(key); + } + }); }); /* * Encode response */ @@ -271,21 +360,23 @@ flags = flags.value; /* * Find the certificate that matches the requested key */ - certs = await cackeyListCertificates(); + certs = await cackeySSHAgentGetCertificates(); certToUse = null; - certs.forEach(function(cert) { - var key; - - key = cackeySSHAgentEncodeCertToKeyAndID(cert.certificate); - - if (key.key.join() == keyInfo.join()) { - certToUse = cert; - certToUseType = key.type; - } + cackeySSHAgentGetSSHKeyTypes().forEach(function(sshKeyType) { + certs.forEach(function(cert) { + var key; + + key = cackeySSHAgentEncodeCertToKeyAndID(cert.certificate, sshKeyType); + + if (key.key.join() == keyInfo.join()) { + certToUse = cert; + certToUseType = key.publicKeyType; + } + }); }); /* * If no certificate is found, return an error */