Index: build/chrome/ssh-agent.js ================================================================== --- build/chrome/ssh-agent.js +++ build/chrome/ssh-agent.js @@ -132,15 +132,39 @@ function cackeySSHAgentEncodeArray(input) { var result; result = cackeySSHAgentEncodeInt(input.length); input.forEach(function(element) { - result = result.concat(element); + result = result.concat(cackeySSHAgentEncodeLV(element)); }); return(result); } + +function cackeySSHAgentDecodeArray(input) { + var items, info; + var itemCount; + + info = cackeySSHAgentDecodeInt(input); + input = info.output; + itemCount = info.value; + + items = []; + while (itemCount > 0) { + itemCount--; + + info = cackeySSHAgentDecodeLV(input); + input = info.output; + items.push(info.value); + } + + return({ + value: items, + output: input + }); +} + function cackeySSHAgentEncodeToUTF8Array(str) { var utf8 = []; if (typeof(str) === "string") { @@ -207,11 +231,11 @@ } return(buffer); } -function cackeySSHAgentEncodeCertToKeyAndID(cert, keyType) { +function cackeySSHAgentEncodeCertToKeyAndID(cert, sshKeyType) { var result = null, resultKey = null; var certObj, certBytes; var publicKey; certObj = new X509; @@ -223,20 +247,20 @@ certObj.readCertHex(cackeySSHAgentEncodeBinaryToHex(certBytes)); publicKey = certObj.getPublicKey(); - switch (keyType) { + switch (sshKeyType) { 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."); + console.log("[cackeySSH] Unsupported public key type:", sshKeyType, "/", publicKey.type, "-- ignoring."); break; } break; case "x509v3-sign": resultKey = certBytes; @@ -248,39 +272,70 @@ /* * Array of certificates */ resultKey = resultKey.concat(cackeySSHAgentEncodeArray([ - cackeySSHAgentEncodeLV(certBytes) + certBytes ])); /* * Array of OCSP responses */ resultKey = resultKey.concat(cackeySSHAgentEncodeArray([])); break; default: - console.log("[cackeySSH] Unsupported public key type:", keyType, "/", publicKey.type, "-- ignoring."); + console.log("[cackeySSH] Unsupported public key type:", sshKeyType, "/", publicKey.type, "-- ignoring."); break; } break; default: - console.log("[cackeySSH] Unsupported SSH key type:", keyType, "-- ignoring."); + console.log("[cackeySSH] Unsupported SSH key type:", sshKeyType, "-- ignoring."); break; } if (resultKey) { + var certLabel; + var certSAN; + var ignoreException; + + /* + * Set a default label + */ + certLabel = certObj.getSubjectString(); + + /* + * Try to find a better label from the certificate's + * Subject Alternative Name (SAN) extensions + */ + try { + certSAN = certObj.getExtSubjectAltName2(); + certSAN.forEach(function(itemPair) { + var itemType, itemValue; + + itemType = itemPair[0]; + itemValue = itemPair[1]; + + if (itemType === "MAIL") { + certLabel = itemValue; + } + }); + } catch (ignoreException) { + } + result = { - id: certObj.getSubjectString(), + label: certLabel, publicKeyType: publicKey.type, - sshKeyType: keyType, + sshKeyType: sshKeyType, key: resultKey }; } return(result); } + +function cackeySSHAgentDecodeCert(requestArray) { +} /* * Command Handlers */ async function cackeySSHAgentCommandRequestIdentity(request) { @@ -315,26 +370,34 @@ response.push(cackeySSHAgentMessage.SSH_AGENT_IDENTITIES_ANSWER); response = response.concat(cackeySSHAgentEncodeInt(keys.length)); keys.forEach(function(key) { response = response.concat(cackeySSHAgentEncodeLV(key.key)); - response = response.concat(cackeySSHAgentEncodeString("CACKey: " + key.id)); + response = response.concat(cackeySSHAgentEncodeString(key.label)); }); return(response); } async function cackeySSHAgentCommandSignRequest(request) { var keyInfo, data, flags; var certs, certToUse, certToUseType; var hashMethod, signedData, signedDataHeader, signRequest; - var response; + var decryptedData, decryptRequest; + var operation, response; var flagMeaning = { SSH_AGENT_RSA_SHA2_256: 2, - SSH_AGENT_RSA_SHA2_512: 4 + SSH_AGENT_RSA_SHA2_512: 4, + SSH_AGENT_RSA_RAW: 0x40000000, + SSH_AGENT_RSA_DECRYPT: 0x80000000 }; + /* + * Default mode is signing + */ + operation = "sign"; + /* * Strip off the command */ request = request.slice(1); @@ -395,10 +458,16 @@ hashMethod = "SHA512"; data = await crypto.subtle.digest("SHA-512", new Uint8Array(data)); } else if ((flags & flagMeaning.SSH_AGENT_RSA_SHA2_256) == flagMeaning.SSH_AGENT_RSA_SHA2_256) { hashMethod = "SHA256"; data = await crypto.subtle.digest("SHA-256", new Uint8Array(data)); + } else if (flags == (flagMeaning.SSH_AGENT_RSA_RAW | flagMeaning.SSH_AGENT_RSA_DECRYPT)) { + operation = "decrypt"; + data = new Uint8Array(data); + } else if (flags == flagMeaning.SSH_AGENT_RSA_RAW) { + hashMethod = "RAW"; + data = new Uint8Array(data); } else if (flags == 0) { hashMethod = "SHA1"; data = await crypto.subtle.digest("SHA-1", new Uint8Array(data)); } else { console.info("[cackeySSH] Sign request with flags set to", flags, "which is unsupported, failing the request."); @@ -405,10 +474,13 @@ return(null); } switch (hashMethod) { + case "RAW": + signedDataHeader = cackeySSHAgentEncodeString("rsa"); + break; case "SHA1": signedDataHeader = cackeySSHAgentEncodeString("ssh-rsa"); break; case "SHA256": signedDataHeader = cackeySSHAgentEncodeString("rsa-sha2-256"); @@ -429,18 +501,34 @@ return(null); break; } /* - * Sign the data + * Sign or decrypt the data */ - signRequest = { - hash: hashMethod, - digest: new Uint8Array(data) - }; - signedData = await cackeySignMessage(signRequest); - signedData = Array.from(new Uint8Array(signedData)); + switch (operation) { + case "sign": + signRequest = { + hash: hashMethod, + certificate: certToUse.certificate, + digest: new Uint8Array(data) + }; + + if (goog.DEBUG) { + console.log("[cackeySSH] Requesting CACKey sign message:", signRequest); + } + + signedData = await cackeySignMessage(signRequest); + signedData = Array.from(new Uint8Array(signedData)); + break; + case "decrypt": + /* XXX:TODO: Incomplete ! */ + decryptRequest = { + data: data + } + break; + } /* * Encode signature */ signedData = signedDataHeader.concat(cackeySSHAgentEncodeLV(signedData)); @@ -460,10 +548,11 @@ * Session handling */ async function cackeySSHAgentHandleMessage(socket, request) { var sshRequestID, sshRequest, response, sshResponse; var sshHandlerError; + var postMessageException; if (!request.type || request.type !== "auth-agent@openssh.com") { return; } @@ -507,11 +596,17 @@ if (goog.DEBUG) { console.log("[cackeySSH] Response: ", sshResponse); } - socket.postMessage(sshResponse); + try { + socket.postMessage(sshResponse); + } catch (postMessageException) { + if (goog.DEBUG) { + console.log("[cackeySSH] Failed to send response", postMessageException); + } + } return; } function cackeySSHAgentAcceptConnection(socket) {