Skip to content

Commit

Permalink
Remove msrCrypto in favor of native APIs (#12050)
Browse files Browse the repository at this point in the history
* Explicitly state computeHash type

* Remove msrCrypto in favor of native APIs

msrCrypto is a polyfill for the native APIs which are well supported in
browser and node currently. The module also recommends using native APIs
when they are available.

Fixes #12023
Part of #11996

* Fix compile issue in webTestReporter.js

* Mark node:crypto as an external

* require reflect-metadata in smoke tests earlier
  • Loading branch information
Tyriar authored Nov 21, 2022
1 parent d916d71 commit ee8804a
Show file tree
Hide file tree
Showing 12 changed files with 36 additions and 9,689 deletions.
11 changes: 10 additions & 1 deletion build/webTestReporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const colors = require('colors');
const core = require('@actions/core');
const glob = require('glob');
const { ExtensionRootDir } = require('./constants');
const { computeHash } = require('../src/platform/msrCrypto/hash');
const { webcrypto } = require('node:crypto');

const settingsFile = path.join(__dirname, '..', 'src', 'test', 'datascience', '.vscode', 'settings.json');
const webTestSummaryJsonFile = path.join(__dirname, '..', 'logs', 'testresults.json');
Expand Down Expand Up @@ -99,6 +99,15 @@ exports.startReportServer = async function () {
async function addCell(cells, output, failed, executionCount) {
const stackFrames = failed ? (output.err.stack || '').split(/\r?\n/) : [];
const line1 = stackFrames.shift() || '';

async function computeHash(data, algorithm) {
const inputBuffer = new TextEncoder().encode(data);
const hashBuffer = await webcrypto.subtle.digest({ name: algorithm }, inputBuffer);

// Turn into hash string (got this logic from https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest)
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
}
const fullTestNameHash = (await computeHash(output.fullTitle() || '', 'SHA-256')).substring(0, 10);
const fileNamePrefix = `${output.title}_${fullTestNameHash}`.replace(/[\W]+/g, '_');
const assertionError = failed
Expand Down
1 change: 0 additions & 1 deletion build/webpack/webpack.datascience-ui.config.builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@ function buildConfiguration(bundle) {
fallback: {
fs: false,
path: require.resolve('path-browserify'),
crypto: require.resolve(path.join(constants.ExtensionRootDir, 'src/platform/msrCrypto/msrCrypto.js')),
os: false
}
},
Expand Down
3 changes: 1 addition & 2 deletions build/webpack/webpack.extension.web.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ const config = {
}
]
},
externals: ['vscode', 'commonjs', 'electron'], // Don't bundle these
externals: ['vscode', 'commonjs', 'electron', 'node:crypto'], // Don't bundle these
plugins: [
...common.getDefaultPlugins('web'),
// Work around for Buffer is undefined:
Expand Down Expand Up @@ -141,7 +141,6 @@ const config = {
stream: require.resolve('stream-browserify'),
os: require.resolve('os-browserify'),
path: require.resolve('path-browserify'),
crypto: require.resolve(path.join(constants.ExtensionRootDir, 'src/platform/msrCrypto/msrCrypto.js')),
fs: false
}
},
Expand Down
31 changes: 18 additions & 13 deletions src/platform/common/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@

import { injectable } from 'inversify';
import type { ICryptoUtils } from './types';
import { computeHash as computeHashLib } from '../../platform/msrCrypto/hash';

/**
* Provides hashing functions. These hashing functions should only be used for non sensitive data. For sensitive data, use msrCrypto instead.
*/
@injectable()
export class CryptoUtils implements ICryptoUtils {
public async createHash(data: string, algorithm: 'SHA-512' | 'SHA-256' = 'SHA-256'): Promise<string> {
Expand All @@ -20,27 +16,27 @@ export class CryptoUtils implements ICryptoUtils {
const computedHashes: Record<string, string> = {};
let stopStoringHashes = false;

let cryptoProvider: Crypto =
// eslint-disable-next-line @typescript-eslint/no-explicit-any, local-rules/node-imports
typeof window === 'object' ? (window as any).crypto : require('node:crypto').webcrypto;

/**
* Computes a hash for a give string and returns hash as a hex value.
*
* @param {string} data
* @param {('SHA512' | 'SHA256' | 'SHA1')} algorithm
* @return {*}
*/
export async function computeHash(data: string, algorithm: 'SHA-512' | 'SHA-256' | 'SHA-1') {
export async function computeHash(data: string, algorithm: 'SHA-512' | 'SHA-256' | 'SHA-1'): Promise<string> {
// Save some CPU as this is called in a number of places.
// This will not get too large, will only grow by number of files per workspace, even if user has
// 1000s of files, this will not grow that large to cause any memory issues.
// Files get hashed a lot in a number of places within the extension (.interactive is the IW window Uri).
// Even things that include file paths like kernel id, which isn't a file path, but contains python executable path.
const isCandidateForCashing = data.includes('/') || data.includes('\\') || data.endsWith('.interactive');
if (isCandidateForCashing && computedHashes[data]) {
const isCandidateForCaching = data.includes('/') || data.includes('\\') || data.endsWith('.interactive');
if (isCandidateForCaching && computedHashes[data]) {
return computedHashes[data];
}

const hash = await computeHashLib(data, algorithm);
const hash = await computeHashInternal(data, algorithm);

if (isCandidateForCashing && !stopStoringHashes) {
if (isCandidateForCaching && !stopStoringHashes) {
// Just a simple fail safe, why 10_000, simple why not 10_000
// All we want to ensure is that we don't store too many hashes.
// The only way we can get there is if user never closes VS Code and our code
Expand All @@ -52,3 +48,12 @@ export async function computeHash(data: string, algorithm: 'SHA-512' | 'SHA-256'
}
return hash;
}

async function computeHashInternal(data: string, algorithm: 'SHA-512' | 'SHA-256' | 'SHA-1'): Promise<string> {
const inputBuffer = new TextEncoder().encode(data);
const hashBuffer = await cryptoProvider.subtle.digest({ name: algorithm }, inputBuffer);

// Turn into hash string (got this logic from https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest)
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
}
17 changes: 0 additions & 17 deletions src/platform/msrCrypto/LICENSE

This file was deleted.

7 changes: 0 additions & 7 deletions src/platform/msrCrypto/README.md

This file was deleted.

30 changes: 0 additions & 30 deletions src/platform/msrCrypto/hash.js

This file was deleted.

Loading

0 comments on commit ee8804a

Please sign in to comment.