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)); |