Path: blob/main/extensions/copilot/src/util/common/crypto.ts
13397 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { encodeHex, VSBuffer } from '../vs/base/common/buffer';6import * as strings from '../vs/base/common/strings';78export async function createRequestHMAC(hmacSecret: string | undefined): Promise<string | undefined> {9// If we don't have the right env variables this could happen10if (!hmacSecret) {11return undefined;12}1314const key = await crypto.subtle.importKey(15'raw',16new TextEncoder().encode(hmacSecret),17{ name: 'HMAC', hash: 'SHA-256' },18false,19['sign']20);2122const current = Math.floor(Date.now() / 1000).toString();23const textEncoder = new TextEncoder();24const data = textEncoder.encode(current);2526const signature = await crypto.subtle.sign('HMAC', key, data);27const signatureArray = Array.from(new Uint8Array(signature));28const signatureHex = signatureArray.map(b => b.toString(16).padStart(2, '0')).join('');2930return `${current}.${signatureHex}`;31}3233export async function createSha256Hash(data: string | Uint8Array): Promise<string> {34const dataUint8 = typeof data === 'string' ? new TextEncoder().encode(data) : data;35const hashBuffer = await crypto.subtle.digest('SHA-256', dataUint8);36const hashArray = new Uint8Array(hashBuffer);37let hashHex = '';38for (const byte of hashArray) {39hashHex += byte.toString(16).padStart(2, '0');40}4142return hashHex;43}4445const _cachedSha256Hashes = new Map<string, string>();46export function getCachedSha256Hash(text: string): string {47if (_cachedSha256Hashes.has(text)) {48return _cachedSha256Hashes.get(text)!;49}5051const hash = createSha256HashSyncInsecure(text);52_cachedSha256Hashes.set(text, hash);53return hash;54}555657function createSha256HashSyncInsecure(data: string): string {58const sha256 = new StringSHA256Insecure();59sha256.update(data);60return sha256.digest();61}6263const enum SHA256Constant {64BLOCK_SIZE = 64, // 512 / 865UNICODE_REPLACEMENT = 0xFFFD,66}6768function toHexString(buffer: ArrayBuffer): string;69function toHexString(value: number, bitsize?: number): string;70function toHexString(bufferOrValue: ArrayBuffer | number, bitsize: number = 32): string {71if (bufferOrValue instanceof ArrayBuffer) {72return encodeHex(VSBuffer.wrap(new Uint8Array(bufferOrValue)));73}7475return (bufferOrValue >>> 0).toString(16).padStart(bitsize / 4, '0');76}7778function rightRotate(value: number, bits: number): number {79return ((value >>> bits) | (value << (32 - bits))) >>> 0;80}8182/**83* A simple, synchronous implementation of SHA-256 for strings.84* Only to be used in non-security-critical paths where synchronous operation is required.85*/86class StringSHA256Insecure {87private static _k = [880x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,890xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,900xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,910x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,920x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,930xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,940x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,950x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f296];9798private static _bigBlock32 = new DataView(new ArrayBuffer(256)); // 64 * 4 = 25699100private _h0 = 0x6a09e667;101private _h1 = 0xbb67ae85;102private _h2 = 0x3c6ef372;103private _h3 = 0xa54ff53a;104private _h4 = 0x510e527f;105private _h5 = 0x9b05688c;106private _h6 = 0x1f83d9ab;107private _h7 = 0x5be0cd19;108109private readonly _buff: Uint8Array;110private readonly _buffDV: DataView;111private _buffLen: number;112private _totalLen: number;113private _leftoverHighSurrogate: number;114private _finished: boolean;115116constructor() {117this._buff = new Uint8Array(SHA256Constant.BLOCK_SIZE + 3 /* to fit any utf-8 */);118this._buffDV = new DataView(this._buff.buffer);119this._buffLen = 0;120this._totalLen = 0;121this._leftoverHighSurrogate = 0;122this._finished = false;123}124125public update(str: string): void {126const strLen = str.length;127if (strLen === 0) {128return;129}130131const buff = this._buff;132let buffLen = this._buffLen;133let leftoverHighSurrogate = this._leftoverHighSurrogate;134let charCode: number;135let offset: number;136137if (leftoverHighSurrogate !== 0) {138charCode = leftoverHighSurrogate;139offset = -1;140leftoverHighSurrogate = 0;141} else {142charCode = str.charCodeAt(0);143offset = 0;144}145146while (true) {147let codePoint = charCode;148if (strings.isHighSurrogate(charCode)) {149if (offset + 1 < strLen) {150const nextCharCode = str.charCodeAt(offset + 1);151if (strings.isLowSurrogate(nextCharCode)) {152offset++;153codePoint = strings.computeCodePoint(charCode, nextCharCode);154} else {155// illegal => unicode replacement character156codePoint = SHA256Constant.UNICODE_REPLACEMENT;157}158} else {159// last character is a surrogate pair160leftoverHighSurrogate = charCode;161break;162}163} else if (strings.isLowSurrogate(charCode)) {164// illegal => unicode replacement character165codePoint = SHA256Constant.UNICODE_REPLACEMENT;166}167168buffLen = this._push(buff, buffLen, codePoint);169offset++;170if (offset < strLen) {171charCode = str.charCodeAt(offset);172} else {173break;174}175}176177this._buffLen = buffLen;178this._leftoverHighSurrogate = leftoverHighSurrogate;179}180181private _push(buff: Uint8Array, buffLen: number, codePoint: number): number {182if (codePoint < 0x0080) {183buff[buffLen++] = codePoint;184} else if (codePoint < 0x0800) {185buff[buffLen++] = 0b11000000 | ((codePoint & 0b00000000000000000000011111000000) >>> 6);186buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000000000111111) >>> 0);187} else if (codePoint < 0x10000) {188buff[buffLen++] = 0b11100000 | ((codePoint & 0b00000000000000001111000000000000) >>> 12);189buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000111111000000) >>> 6);190buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000000000111111) >>> 0);191} else {192buff[buffLen++] = 0b11110000 | ((codePoint & 0b00000000000111000000000000000000) >>> 18);193buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000111111000000000000) >>> 12);194buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000111111000000) >>> 6);195buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000000000111111) >>> 0);196}197198if (buffLen >= SHA256Constant.BLOCK_SIZE) {199this._step();200buffLen -= SHA256Constant.BLOCK_SIZE;201this._totalLen += SHA256Constant.BLOCK_SIZE;202// take last 3 in case of UTF8 overflow203buff[0] = buff[SHA256Constant.BLOCK_SIZE + 0];204buff[1] = buff[SHA256Constant.BLOCK_SIZE + 1];205buff[2] = buff[SHA256Constant.BLOCK_SIZE + 2];206}207208return buffLen;209}210211public digest(): string {212if (!this._finished) {213this._finished = true;214if (this._leftoverHighSurrogate) {215// illegal => unicode replacement character216this._leftoverHighSurrogate = 0;217this._buffLen = this._push(this._buff, this._buffLen, SHA256Constant.UNICODE_REPLACEMENT);218}219this._totalLen += this._buffLen;220this._wrapUp();221}222223return toHexString(this._h0) + toHexString(this._h1) + toHexString(this._h2) + toHexString(this._h3) + toHexString(this._h4) + toHexString(this._h5) + toHexString(this._h6) + toHexString(this._h7);224}225226private _wrapUp(): void {227this._buff[this._buffLen++] = 0x80;228this._buff.subarray(this._buffLen).fill(0);229230if (this._buffLen > 56) {231this._step();232this._buff.fill(0);233}234235// this will fit because the mantissa can cover up to 52 bits236const ml = 8 * this._totalLen;237238this._buffDV.setUint32(56, Math.floor(ml / 4294967296), false);239this._buffDV.setUint32(60, ml % 4294967296, false);240241this._step();242}243244private _step(): void {245const bigBlock32 = StringSHA256Insecure._bigBlock32;246const data = this._buffDV;247const k = StringSHA256Insecure._k;248249// Copy chunk into first 16 words of message schedule250for (let j = 0; j < 64 /* 16*4 */; j += 4) {251bigBlock32.setUint32(j, data.getUint32(j, false), false);252}253254// Extend the first 16 words into the remaining 48 words of the message schedule255for (let j = 16; j < 64; j++) {256const offset = j * 4;257const w15 = bigBlock32.getUint32((j - 15) * 4, false);258const w2 = bigBlock32.getUint32((j - 2) * 4, false);259const s0 = rightRotate(w15, 7) ^ rightRotate(w15, 18) ^ (w15 >>> 3);260const s1 = rightRotate(w2, 17) ^ rightRotate(w2, 19) ^ (w2 >>> 10);261const w16 = bigBlock32.getUint32((j - 16) * 4, false);262const w7 = bigBlock32.getUint32((j - 7) * 4, false);263bigBlock32.setUint32(offset, (w16 + s0 + w7 + s1) >>> 0, false);264}265266// Initialize working variables267let a = this._h0;268let b = this._h1;269let c = this._h2;270let d = this._h3;271let e = this._h4;272let f = this._h5;273let g = this._h6;274let h = this._h7;275276// Compression function main loop277for (let j = 0; j < 64; j++) {278const S1 = rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25);279const ch = (e & f) ^ ((~e) & g);280const temp1 = (h + S1 + ch + k[j] + bigBlock32.getUint32(j * 4, false)) >>> 0;281const S0 = rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22);282const maj = (a & b) ^ (a & c) ^ (b & c);283const temp2 = (S0 + maj) >>> 0;284285h = g;286g = f;287f = e;288e = (d + temp1) >>> 0;289d = c;290c = b;291b = a;292a = (temp1 + temp2) >>> 0;293}294295// Add the compressed chunk to the current hash value296this._h0 = (this._h0 + a) >>> 0;297this._h1 = (this._h1 + b) >>> 0;298this._h2 = (this._h2 + c) >>> 0;299this._h3 = (this._h3 + d) >>> 0;300this._h4 = (this._h4 + e) >>> 0;301this._h5 = (this._h5 + f) >>> 0;302this._h6 = (this._h6 + g) >>> 0;303this._h7 = (this._h7 + h) >>> 0;304}305}306307308