121 lines
2.9 KiB
JavaScript
121 lines
2.9 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
;(function (exports) {
|
|
'use strict';
|
|
|
|
if (!exports.Enc) { exports.Enc = {}; }
|
|
if (!exports.SSH) { exports.SSH = {}; }
|
|
|
|
var Enc = exports.Enc;
|
|
var SSH = exports.SSH;
|
|
|
|
SSH.pack = function (opts) {
|
|
var jwk = opts.jwk;
|
|
var els = [];
|
|
var ssh = {
|
|
type: ''
|
|
, _elements: els
|
|
, comment: opts.comment || ''
|
|
};
|
|
var len;
|
|
|
|
if ("RSA" === jwk.kty) {
|
|
ssh.type = 'ssh-rsa';
|
|
els.push(Enc.binToHex(ssh.type));
|
|
els.push(SSH._padRsa(Enc.base64ToHex(jwk.e)));
|
|
els.push(SSH._padRsa(Enc.base64ToHex(jwk.n)));
|
|
return SSH._packElements(ssh);
|
|
}
|
|
|
|
if ("P-256" === jwk.crv) {
|
|
ssh.type = 'ecdsa-sha2-nistp256';
|
|
els.push(Enc.binToHex(ssh.type));
|
|
els.push(Enc.binToHex('nistp256'));
|
|
len = 32;
|
|
} else if ("P-384" === jwk.crv) {
|
|
ssh.type = 'ecdsa-sha2-nistp384';
|
|
els.push(Enc.binToHex(ssh.type));
|
|
els.push(Enc.binToHex('nistp384'));
|
|
len = 48;
|
|
} else {
|
|
throw new Error("unknown key type " + (jwk.crv || jwk.kty));
|
|
}
|
|
|
|
els.push('04'
|
|
+ SSH._padEc(Enc.base64ToHex(jwk.x), len)
|
|
+ SSH._padEc(Enc.base64ToHex(jwk.y), len)
|
|
);
|
|
return SSH._packElements(ssh);
|
|
};
|
|
|
|
SSH._packElements = function (ssh) {
|
|
var hex = ssh._elements.map(function (hex) {
|
|
console.log(hex);
|
|
return SSH._numToUint32Hex(hex.length/2) + hex;
|
|
}).join('');
|
|
return [ ssh.type, Enc.hexToBase64(hex), ssh.comment ].join(' ');
|
|
};
|
|
|
|
SSH._numToUint32Hex = function (num) {
|
|
var hex = num.toString(16);
|
|
while (hex.length < 8) {
|
|
hex = '0' + hex;
|
|
}
|
|
console.log('length', hex);
|
|
return hex;
|
|
};
|
|
|
|
SSH._padRsa = function (hex) {
|
|
// BigInt is negative if the high order bit 0x80 is set,
|
|
// so ASN1, SSH, and many other formats pad with '0x00'
|
|
// to signifiy a positive number.
|
|
var i = parseInt(hex.slice(0, 2), 16);
|
|
if (0x80 & i) {
|
|
return '00' + hex;
|
|
}
|
|
return hex;
|
|
};
|
|
|
|
SSH._padEc = function (hex, len) {
|
|
while (hex.length < len * 2) {
|
|
hex = '00' + hex;
|
|
}
|
|
return hex;
|
|
};
|
|
|
|
Enc.base64ToHex = function (b64) {
|
|
var bin = atob(Enc.urlBase64ToBase64(b64));
|
|
return Enc.binToHex(bin);
|
|
};
|
|
|
|
Enc.binToHex = function (bin) {
|
|
return bin.split('').map(function (ch) {
|
|
var h = ch.charCodeAt(0).toString(16);
|
|
if (h.length % 2) { h = '0' + h; }
|
|
return h;
|
|
}).join('');
|
|
};
|
|
|
|
Enc.hexToBase64 = function (hex) {
|
|
return btoa(Enc.hexToBin(hex));
|
|
};
|
|
|
|
Enc.hexToBin = function (hex) {
|
|
return hex.match(/.{2}/g).map(function (h) {
|
|
return String.fromCharCode(parseInt(h, 16));
|
|
}).join('');
|
|
};
|
|
|
|
Enc.urlBase64ToBase64 = function urlsafeBase64ToBase64(str) {
|
|
var r = str % 4;
|
|
if (2 === r) {
|
|
str += '==';
|
|
} else if (3 === r) {
|
|
str += '=';
|
|
}
|
|
return str.replace(/-/g, '+').replace(/_/g, '/');
|
|
};
|
|
|
|
}('undefined' !== typeof window ? window : module.exports));
|