Skip to content

Commit

Permalink
[core] Add SHA256 functions for React-Native (#30080)
Browse files Browse the repository at this point in the history
### Packages impacted by this PR

- @azure/core-rest-pipeline
- @typespec/ts-http-runtime

### Issues associated with this PR

- #30065

### Describe the problem that is addressed by this PR

Adds web crypto APIs for React-Native calls. Requires the consumer to
polyfill the Web Crypto APIs in order for these modules to work.

### What are the possible designs available to address the problem? If
there are more than one possible design, why was the one in this PR
chosen?


### Are there test cases added in this PR? _(If not, why?)_


### Provide a list of related PRs _(if any)_


### Command used to generate this PR:**_(Applicable only to SDK release
request PRs)_

### Checklists
- [ ] Added impacted package name to the issue description
- [ ] Does this PR needs any fixes in the SDK Generator?** _(If so,
create an Issue in the
[Autorest/typescript](https://github.com/Azure/autorest.typescript)
repository and link it here)_
- [ ] Added a changelog (if necessary)
  • Loading branch information
mpodwysocki committed Jun 18, 2024
1 parent 9488fe4 commit 0934dfb
Show file tree
Hide file tree
Showing 21 changed files with 540 additions and 489 deletions.
2 changes: 2 additions & 0 deletions sdk/core/core-util/.tshy/browser.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"../src/**/*.tsx"
],
"exclude": [
".././src/bytesEncoding-react-native.mts",
".././src/sha256-react-native.mts",
".././src/uuidUtils-react-native.mts"
],
"compilerOptions": {
Expand Down
2 changes: 2 additions & 0 deletions sdk/core/core-util/.tshy/commonjs.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"../src/bytesEncoding-browser.mts",
"../src/sha256-browser.mts",
"../src/uuidUtils-browser.mts",
"../src/bytesEncoding-react-native.mts",
"../src/sha256-react-native.mts",
"../src/uuidUtils-react-native.mts"
],
"compilerOptions": {
Expand Down
2 changes: 2 additions & 0 deletions sdk/core/core-util/.tshy/esm.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
".././src/bytesEncoding-browser.mts",
".././src/sha256-browser.mts",
".././src/uuidUtils-browser.mts",
".././src/bytesEncoding-react-native.mts",
".././src/sha256-react-native.mts",
".././src/uuidUtils-react-native.mts"
],
"compilerOptions": {
Expand Down
129 changes: 1 addition & 128 deletions sdk/core/core-util/src/bytesEncoding-browser.mts
Original file line number Diff line number Diff line change
@@ -1,131 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

declare global {
// stub these out for the browser
function btoa(input: string): string;
function atob(input: string): string;
}

/** The supported character encoding type */
export type EncodingType = "utf-8" | "base64" | "base64url" | "hex";

/**
* The helper that transforms bytes with specific character encoding into string
* @param bytes - the uint8array bytes
* @param format - the format we use to encode the byte
* @returns a string of the encoded string
*/
export function uint8ArrayToString(bytes: Uint8Array, format: EncodingType): string {
switch (format) {
case "utf-8":
return uint8ArrayToUtf8String(bytes);
case "base64":
return uint8ArrayToBase64(bytes);
case "base64url":
return uint8ArrayToBase64Url(bytes);
case "hex":
return uint8ArrayToHexString(bytes);
}
}

/**
* The helper that transforms string to specific character encoded bytes array.
* @param value - the string to be converted
* @param format - the format we use to decode the value
* @returns a uint8array
*/
export function stringToUint8Array(value: string, format: EncodingType): Uint8Array {
switch (format) {
case "utf-8":
return utf8StringToUint8Array(value);
case "base64":
return base64ToUint8Array(value);
case "base64url":
return base64UrlToUint8Array(value);
case "hex":
return hexStringToUint8Array(value);
}
}

/**
* Decodes a Uint8Array into a Base64 string.
* @internal
*/
export function uint8ArrayToBase64(bytes: Uint8Array): string {
return btoa([...bytes].map((x) => String.fromCharCode(x)).join(""));
}

/**
* Decodes a Uint8Array into a Base64Url string.
* @internal
*/
export function uint8ArrayToBase64Url(bytes: Uint8Array): string {
return uint8ArrayToBase64(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}

/**
* Decodes a Uint8Array into a javascript string.
* @internal
*/
export function uint8ArrayToUtf8String(bytes: Uint8Array): string {
const decoder = new TextDecoder();
const dataString = decoder.decode(bytes);
return dataString;
}

/**
* Decodes a Uint8Array into a hex string
* @internal
*/
export function uint8ArrayToHexString(bytes: Uint8Array): string {
return [...bytes].map((x) => x.toString(16).padStart(2, "0")).join("");
}

/**
* Encodes a JavaScript string into a Uint8Array.
* @internal
*/
export function utf8StringToUint8Array(value: string): Uint8Array {
return new TextEncoder().encode(value);
}

/**
* Encodes a Base64 string into a Uint8Array.
* @internal
*/
export function base64ToUint8Array(value: string): Uint8Array {
return new Uint8Array([...atob(value)].map((x) => x.charCodeAt(0)));
}

/**
* Encodes a Base64Url string into a Uint8Array.
* @internal
*/
export function base64UrlToUint8Array(value: string): Uint8Array {
const base64String = value.replace(/-/g, "+").replace(/_/g, "/");
return base64ToUint8Array(base64String);
}

const hexDigits = new Set("0123456789abcdefABCDEF");

/**
* Encodes a hex string into a Uint8Array
* @internal
*/
export function hexStringToUint8Array(value: string): Uint8Array {
// If value has odd length, the last character will be ignored, consistent with NodeJS Buffer behavior
const bytes = new Uint8Array(value.length / 2);
for (let i = 0; i < value.length / 2; ++i) {
const highNibble = value[2 * i];
const lowNibble = value[2 * i + 1];
if (!hexDigits.has(highNibble) || !hexDigits.has(lowNibble)) {
// Replicate Node Buffer behavior by exiting early when we encounter an invalid byte
return bytes.slice(0, i);
}

bytes[i] = parseInt(`${highNibble}${lowNibble}`, 16);
}

return bytes;
}
export * from "./bytesEncoding.common.js";
4 changes: 4 additions & 0 deletions sdk/core/core-util/src/bytesEncoding-react-native.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

export * from "./bytesEncoding.common.js";
131 changes: 131 additions & 0 deletions sdk/core/core-util/src/bytesEncoding.common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

declare global {
// stub these out for the browser
function btoa(input: string): string;
function atob(input: string): string;
}

/** The supported character encoding type */
export type EncodingType = "utf-8" | "base64" | "base64url" | "hex";

/**
* The helper that transforms bytes with specific character encoding into string
* @param bytes - the uint8array bytes
* @param format - the format we use to encode the byte
* @returns a string of the encoded string
*/
export function uint8ArrayToString(bytes: Uint8Array, format: EncodingType): string {
switch (format) {
case "utf-8":
return uint8ArrayToUtf8String(bytes);
case "base64":
return uint8ArrayToBase64(bytes);
case "base64url":
return uint8ArrayToBase64Url(bytes);
case "hex":
return uint8ArrayToHexString(bytes);
}
}

/**
* The helper that transforms string to specific character encoded bytes array.
* @param value - the string to be converted
* @param format - the format we use to decode the value
* @returns a uint8array
*/
export function stringToUint8Array(value: string, format: EncodingType): Uint8Array {
switch (format) {
case "utf-8":
return utf8StringToUint8Array(value);
case "base64":
return base64ToUint8Array(value);
case "base64url":
return base64UrlToUint8Array(value);
case "hex":
return hexStringToUint8Array(value);
}
}

/**
* Decodes a Uint8Array into a Base64 string.
* @internal
*/
export function uint8ArrayToBase64(bytes: Uint8Array): string {
return btoa([...bytes].map((x) => String.fromCharCode(x)).join(""));
}

/**
* Decodes a Uint8Array into a Base64Url string.
* @internal
*/
export function uint8ArrayToBase64Url(bytes: Uint8Array): string {
return uint8ArrayToBase64(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}

/**
* Decodes a Uint8Array into a javascript string.
* @internal
*/
export function uint8ArrayToUtf8String(bytes: Uint8Array): string {
const decoder = new TextDecoder();
const dataString = decoder.decode(bytes);
return dataString;
}

/**
* Decodes a Uint8Array into a hex string
* @internal
*/
export function uint8ArrayToHexString(bytes: Uint8Array): string {
return [...bytes].map((x) => x.toString(16).padStart(2, "0")).join("");
}

/**
* Encodes a JavaScript string into a Uint8Array.
* @internal
*/
export function utf8StringToUint8Array(value: string): Uint8Array {
return new TextEncoder().encode(value);
}

/**
* Encodes a Base64 string into a Uint8Array.
* @internal
*/
export function base64ToUint8Array(value: string): Uint8Array {
return new Uint8Array([...atob(value)].map((x) => x.charCodeAt(0)));
}

/**
* Encodes a Base64Url string into a Uint8Array.
* @internal
*/
export function base64UrlToUint8Array(value: string): Uint8Array {
const base64String = value.replace(/-/g, "+").replace(/_/g, "/");
return base64ToUint8Array(base64String);
}

const hexDigits = new Set("0123456789abcdefABCDEF");

/**
* Encodes a hex string into a Uint8Array
* @internal
*/
export function hexStringToUint8Array(value: string): Uint8Array {
// If value has odd length, the last character will be ignored, consistent with NodeJS Buffer behavior
const bytes = new Uint8Array(value.length / 2);
for (let i = 0; i < value.length / 2; ++i) {
const highNibble = value[2 * i];
const lowNibble = value[2 * i + 1];
if (!hexDigits.has(highNibble) || !hexDigits.has(lowNibble)) {
// Replicate Node Buffer behavior by exiting early when we encounter an invalid byte
return bytes.slice(0, i);
}

bytes[i] = parseInt(`${highNibble}${lowNibble}`, 16);
}

return bytes;
}
Loading

0 comments on commit 0934dfb

Please sign in to comment.