@microsoft/dev-tunnels-ssh
Version:
SSH library for Dev Tunnels
212 lines • 8.59 kB
JavaScript
"use strict";
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebRsa = void 0;
const buffer_1 = require("buffer");
const publicKeyAlgorithm_1 = require("../publicKeyAlgorithm");
const webHmac_1 = require("./webHmac");
const sshData_1 = require("../../io/sshData");
const jsonWebKeyFormatter_1 = require("./jsonWebKeyFormatter");
// Note this is exposed as an inner-class property below: `WebRsa.KeyPair`.
// TypeScript requires that the class definition comes first.
class WebRsaKeyPair {
/* @internal */
constructor(hashAlgorithm) {
this.hashAlgorithm = hashAlgorithm;
this.comment = null;
}
get hasPublicKey() {
return !!this.publicKey;
}
get hasPrivateKey() {
return !!this.privateKey;
}
get keyAlgorithmName() {
return WebRsa.keyAlgorithmName;
}
async generate(keySizeInBits) {
keySizeInBits = keySizeInBits !== null && keySizeInBits !== void 0 ? keySizeInBits : WebRsaKeyPair.defaultKeySize;
try {
const keyGenParams = {
name: 'RSASSA-PKCS1-v1_5',
modulusLength: keySizeInBits,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: { name: this.hashAlgorithm },
};
const keyPair = (await crypto.subtle.generateKey(keyGenParams, true, ['sign', 'verify']));
this.publicKey = keyPair.publicKey;
this.privateKey = keyPair.privateKey;
}
catch (e) {
throw new Error('Failed to generate RSA key pair: ' + e);
}
}
async setPublicKeyBytes(keyBytes) {
if (!keyBytes) {
throw new TypeError('Buffer is required.');
}
// Read public key in SSH format.
const reader = new sshData_1.SshDataReader(keyBytes);
const algorithmName = reader.readString('ascii');
if (algorithmName !== this.keyAlgorithmName &&
algorithmName !== WebRsa.rsaWithSha256 &&
algorithmName !== WebRsa.rsaWithSha512) {
throw new Error(`Invalid RSA key algorithm: ${algorithmName}`);
}
const exponent = reader.readBigInt();
const modulus = reader.readBigInt();
// Import public key in JWK format.
const jwk = jsonWebKeyFormatter_1.JsonWebKeyFormatter.formatRsa({ modulus, exponent }, false);
jwk.alg = 'RS' + this.hashAlgorithm.replace('SHA-', '');
jwk.key_ops = ['verify'];
try {
const importParams = {
name: 'RSASSA-PKCS1-v1_5',
hash: { name: this.hashAlgorithm },
};
this.publicKey = await crypto.subtle.importKey('jwk', jwk, importParams, true, ['verify']);
}
catch (e) {
throw new Error('Failed to import RSA public key: ' + e);
}
}
async getPublicKeyBytes(algorithmName) {
if (!this.publicKey) {
return null;
}
if (!algorithmName) {
algorithmName = this.keyAlgorithmName;
}
// Export public key in JWK format.
let jwk;
try {
jwk = await crypto.subtle.exportKey('jwk', this.publicKey);
}
catch (e) {
throw new Error('Failed to export RSA public key: ' + e);
}
const { modulus, exponent } = jsonWebKeyFormatter_1.JsonWebKeyFormatter.parseRsa(jwk, false);
// Write public key in SSH format.
const keyBuffer = buffer_1.Buffer.alloc(512);
const keyWriter = new sshData_1.SshDataWriter(keyBuffer);
keyWriter.writeString(algorithmName, 'ascii');
keyWriter.writeBigInt(exponent);
keyWriter.writeBigInt(modulus);
const keyBytes = keyWriter.toBuffer();
return keyBytes;
}
async importParameters(parameters) {
const privateJwk = parameters.d ? jsonWebKeyFormatter_1.JsonWebKeyFormatter.formatRsa(parameters, true) : null;
const publicJwk = jsonWebKeyFormatter_1.JsonWebKeyFormatter.formatRsa(parameters, false);
const importParams = {
name: 'RSASSA-PKCS1-v1_5',
hash: { name: this.hashAlgorithm },
};
try {
this.publicKey = await crypto.subtle.importKey('jwk', publicJwk, importParams, true, [
'verify',
]);
if (privateJwk) {
this.privateKey = await crypto.subtle.importKey('jwk', privateJwk, importParams, true, [
'sign',
]);
}
else {
this.privateKey = undefined;
}
}
catch (e) {
if (!(e instanceof Error))
throw e;
throw new Error('Failed to import RSA key pair: ' + e.message);
}
}
async exportParameters() {
var _a;
if (!this.publicKey)
throw new Error('Public key not set.');
let jwk;
try {
jwk = await crypto.subtle.exportKey('jwk', (_a = this.privateKey) !== null && _a !== void 0 ? _a : this.publicKey);
}
catch (e) {
if (!(e instanceof Error))
throw e;
throw new Error('Failed to export RSA public key: ' + e.message);
}
return jsonWebKeyFormatter_1.JsonWebKeyFormatter.parseRsa(jwk, !!this.privateKey);
}
dispose() { }
}
WebRsaKeyPair.defaultKeySize = 2048;
class WebRsa extends publicKeyAlgorithm_1.PublicKeyAlgorithm {
constructor(name, hashAlgorithmName) {
super(name, WebRsa.keyAlgorithmName, hashAlgorithmName);
}
createKeyPair() {
const hashAlgorithm = WebRsa.convertHashAlgorithmName(this.hashAlgorithmName);
return new WebRsaKeyPair(hashAlgorithm);
}
async generateKeyPair(keySizeInBits) {
const hashAlgorithm = WebRsa.convertHashAlgorithmName(this.hashAlgorithmName);
const rsaKey = new WebRsaKeyPair(hashAlgorithm);
await rsaKey.generate(keySizeInBits);
return rsaKey;
}
createSigner(keyPair) {
if (!(keyPair instanceof WebRsaKeyPair)) {
throw new TypeError('RSA key pair object expected.');
}
const hashAlgorithm = WebRsa.convertHashAlgorithmName(this.hashAlgorithmName);
return new WebRsaSignerVerifier(keyPair, hashAlgorithm, webHmac_1.WebHmac.getHashDigestLength(this.hashAlgorithmName));
}
createVerifier(keyPair) {
if (!(keyPair instanceof WebRsaKeyPair)) {
throw new TypeError('RSA key pair object expected.');
}
const hashAlgorithm = WebRsa.convertHashAlgorithmName(this.hashAlgorithmName);
return new WebRsaSignerVerifier(keyPair, hashAlgorithm, webHmac_1.WebHmac.getHashDigestLength(this.hashAlgorithmName));
}
static convertHashAlgorithmName(hashAlgorithmName) {
return hashAlgorithmName.replace('SHA2-', 'SHA-');
}
}
exports.WebRsa = WebRsa;
WebRsa.keyAlgorithmName = 'ssh-rsa';
WebRsa.rsaWithSha256 = 'rsa-sha2-256';
WebRsa.rsaWithSha512 = 'rsa-sha2-512';
WebRsa.KeyPair = WebRsaKeyPair;
class WebRsaSignerVerifier {
constructor(keyPair, hashAlgorithm, digestLength) {
this.keyPair = keyPair;
this.hashAlgorithm = hashAlgorithm;
this.digestLength = digestLength;
}
async sign(data) {
if (!this.keyPair.privateKey) {
throw new Error('Private key not set.');
}
await this.convertKeyHashAlgorithm();
const signature = buffer_1.Buffer.from(await crypto.subtle.sign('RSASSA-PKCS1-v1_5', this.keyPair.privateKey, data));
return signature;
}
async verify(data, signature) {
if (!this.keyPair.publicKey) {
throw new Error('Public key not set.');
}
await this.convertKeyHashAlgorithm();
const result = await crypto.subtle.verify('RSASSA-PKCS1-v1_5', this.keyPair.publicKey, signature, data);
return result;
}
async convertKeyHashAlgorithm() {
if (this.keyPair.hashAlgorithm !== this.hashAlgorithm) {
const parameters = await this.keyPair.exportParameters();
this.keyPair = new WebRsaKeyPair(this.hashAlgorithm);
await this.keyPair.importParameters(parameters);
}
}
dispose() { }
}
//# sourceMappingURL=webRsa.js.map