js-keygen/ssh-util.js

122 lines
3 KiB
JavaScript
Raw Normal View History

2018-02-13 16:41:00 +00:00
/* eslint no-bitwise: 0 */
/* global base64urlDecode */
2015-09-04 07:13:08 +00:00
2015-09-03 11:58:00 +00:00
function arrayToString(a) {
return String.fromCharCode.apply(null, a);
}
function stringToArray(s) {
2018-02-13 08:53:20 +00:00
return s.split("").map(c => c.charCodeAt());
2015-09-03 11:58:00 +00:00
}
function base64urlToArray(s) {
return stringToArray(base64urlDecode(s));
}
function pemToArray(pem) {
return stringToArray(window.atob(pem));
}
function arrayToPem(a) {
2018-02-13 08:53:20 +00:00
return window.btoa(a.map(c => String.fromCharCode(c)).join(""));
2015-09-03 11:58:00 +00:00
}
function arrayToLen(a) {
2018-02-13 16:41:00 +00:00
let result = 0;
for (let i = 0; i < a.length; i += 1) {
2015-09-03 11:58:00 +00:00
result = result * 256 + a[i];
}
return result;
}
function integerToOctet(n) {
2018-02-13 16:41:00 +00:00
const result = [];
for (let i = n; i > 0; i >>= 8) {
result.push(i & 0xff);
2015-09-03 11:58:00 +00:00
}
return result.reverse();
}
function lenToArray(n) {
2018-02-13 16:41:00 +00:00
const oct = integerToOctet(n);
let i;
2015-09-04 07:13:08 +00:00
for (i = oct.length; i < 4; i += 1) {
2015-09-03 11:58:00 +00:00
oct.unshift(0);
}
return oct;
}
function decodePublicKey(s) {
2018-02-13 16:41:00 +00:00
const split = s.split(" ");
const prefix = split[0];
2015-09-04 07:13:08 +00:00
if (prefix !== "ssh-rsa") {
2018-02-13 16:41:00 +00:00
throw new Error(`Unknown prefix: ${prefix}`);
2015-09-03 11:58:00 +00:00
}
2018-02-13 16:41:00 +00:00
const buffer = pemToArray(split[1]);
const nameLen = arrayToLen(buffer.splice(0, 4));
const type = arrayToString(buffer.splice(0, nameLen));
2015-09-04 07:13:08 +00:00
if (type !== "ssh-rsa") {
2018-02-13 16:41:00 +00:00
throw new Error(`Unknown key type: ${type}`);
2015-09-03 11:58:00 +00:00
}
2018-02-13 16:41:00 +00:00
const exponentLen = arrayToLen(buffer.splice(0, 4));
const exponent = buffer.splice(0, exponentLen);
const keyLen = arrayToLen(buffer.splice(0, 4));
const key = buffer.splice(0, keyLen);
return { type, exponent, key, name: split[2] };
2015-09-03 11:58:00 +00:00
}
2015-09-04 07:13:08 +00:00
function checkHighestBit(v) {
2018-02-13 08:30:29 +00:00
if (v[0] >> 7 === 1) {
// add leading zero if first bit is set
2015-09-04 07:13:08 +00:00
v.unshift(0);
}
return v;
}
2015-09-03 11:58:00 +00:00
function jwkToInternal(jwk) {
return {
type: "ssh-rsa",
exponent: checkHighestBit(stringToArray(base64urlDecode(jwk.e))),
2015-09-04 07:13:08 +00:00
name: "name",
2018-02-13 08:30:29 +00:00
key: checkHighestBit(stringToArray(base64urlDecode(jwk.n))),
2015-09-03 11:58:00 +00:00
};
}
function encodePublicKey(jwk, name) {
2018-02-13 16:41:00 +00:00
const k = jwkToInternal(jwk);
2015-09-03 11:58:00 +00:00
k.name = name;
2018-02-13 16:41:00 +00:00
const keyLenA = lenToArray(k.key.length);
const exponentLenA = lenToArray(k.exponent.length);
const typeLenA = lenToArray(k.type.length);
const array = [].concat(typeLenA, stringToArray(k.type), exponentLenA, k.exponent, keyLenA, k.key);
const encoding = arrayToPem(array);
return `${k.type} ${encoding} ${k.name}`;
2015-09-03 11:58:00 +00:00
}
function asnEncodeLen(n) {
2018-02-13 16:41:00 +00:00
let result = [];
2015-09-04 07:13:08 +00:00
if (n >> 7) {
2015-09-03 11:58:00 +00:00
result = integerToOctet(n);
result.unshift(0x80 + result.length);
} else {
result.push(n);
}
return result;
}
function encodePrivateKey(jwk) {
2018-02-13 16:41:00 +00:00
const order = ["n", "e", "d", "p", "q", "dp", "dq", "qi"];
const list = order.map(prop => {
const v = checkHighestBit(stringToArray(base64urlDecode(jwk[prop])));
const len = asnEncodeLen(v.length);
2015-09-03 11:58:00 +00:00
return [0x02].concat(len, v); // int tag is 0x02
});
2018-02-13 16:41:00 +00:00
let seq = [0x02, 0x01, 0x00]; // extra seq for SSH
seq = seq.concat(...list);
const len = asnEncodeLen(seq.length);
const a = [0x30].concat(len, seq); // seq is 0x30
2015-09-03 11:58:00 +00:00
return arrayToPem(a);
2015-09-04 07:13:08 +00:00
}
2018-02-13 08:45:06 +00:00
module.exports = { base64urlToArray, decodePublicKey, encodePublicKey, encodePrivateKey };