blob: c748eab02853a26fdcfb4bbd7ecb152648d76f44 [file] [log] [blame]
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
goog.module('tink.subtle.Bytes');
const InvalidArgumentsException = goog.require('tink.exception.InvalidArgumentsException');
/**
* Does near constant time byte array comparison.
* @param {!Uint8Array} ba1 The first bytearray to check.
* @param {!Uint8Array} ba2 The second bytearray to check.
* @return {boolean} If the array are equal.
*/
const isEqual = function(ba1, ba2) {
if (ba1.length !== ba2.length) {
return false;
}
let result = 0;
for (let i = 0; i < ba1.length; i++) {
result |= ba1[i] ^ ba2[i];
}
return result == 0;
};
/**
* Returns a new array that is the result of joining the arguments.
* @param {...!Uint8Array} var_args
* @return {!Uint8Array}
*/
const concat = function(var_args) {
let length = 0;
for (let i = 0; i < arguments.length; i++) {
length += arguments[i].length;
}
let result = new Uint8Array(length);
let curOffset = 0;
for (let i = 0; i < arguments.length; i++) {
result.set(arguments[i], curOffset);
curOffset += arguments[i].length;
}
return result;
};
/**
* Converts a non-negative integer number to a 64-bit big-endian byte array.
* @param {number} value The number to convert.
* @return {!Uint8Array} The number as a big-endian byte array.
* @throws {InvalidArgumentsException}
* @static
*/
const fromNumber = function(value) {
if (isNaN(value) || value % 1 !== 0) {
throw new InvalidArgumentsException('cannot convert non-integer value');
}
if (value < 0) {
throw new InvalidArgumentsException('cannot convert negative number');
}
if (value > Number.MAX_SAFE_INTEGER) {
throw new InvalidArgumentsException(
'cannot convert number larger than ' + Number.MAX_SAFE_INTEGER);
}
const two_power_32 = 2**32;
let low = value % two_power_32;
let high = value / two_power_32;
const result = new Uint8Array(8);
for (let i = 7; i >= 4; i--) {
result[i] = low & 0xff;
low >>>= 8;
}
for (let i = 3; i >= 0; i--) {
result[i] = high & 0xff;
high >>>= 8;
}
return result;
};
/**
* Converts the hex string to a byte array.
*
* @param {string} hex the input
* @return {!Uint8Array} the byte array output
* @throws {!InvalidArgumentsException}
* @static
*/
const fromHex = function(hex) {
if (hex.length % 2 != 0) {
throw new InvalidArgumentsException(
'Hex string length must be multiple of 2');
}
var arr = new Uint8Array(hex.length / 2);
for (var i = 0; i < hex.length; i += 2) {
arr[i / 2] = parseInt(hex.substring(i, i + 2), 16);
}
return arr;
};
/**
* Converts a byte array to hex.
*
* @param {!Uint8Array} bytes the byte array input
* @return {string} hex the output
* @static
*/
const toHex = function(bytes) {
let result = '';
for (let i = 0; i < bytes.length; i++) {
let hexByte = bytes[i].toString(16);
result += hexByte.length > 1 ? hexByte : '0' + hexByte;
}
return result;
};
/**
* Converts the Base64 string to a byte array.
*
* @param {string} encoded the base64 string
* @param {boolean=} opt_webSafe True indicates we should use the alternative
* alphabet, which does not require escaping for use in URLs.
* @return {!Uint8Array} the byte array output
* @static
*/
const fromBase64 = function(encoded, opt_webSafe) {
if (opt_webSafe) {
const normalBase64 = encoded.replace(/-/g, '+').replace(/_/g, '/');
return fromByteString(window.atob(normalBase64));
}
return fromByteString(window.atob(encoded));
};
/**
* Base64 encode a byte array.
*
* @param {!Uint8Array} bytes the byte array input
* @param {boolean=} opt_webSafe True indicates we should use the alternative
* alphabet, which does not require escaping for use in URLs.
* @return {string} base64 output
* @static
*/
const toBase64 = function(bytes, opt_webSafe) {
let encoded =
window.btoa(toByteString(bytes)).replace(/=/g, '') /* padding */;
if (opt_webSafe) {
return encoded.replace(/\+/g, '-').replace(/\//g, '_');
}
return encoded;
};
/**
* Converts a byte string to a byte array. Only support ASCII and Latin-1
* strings, does not support multi-byte characters.
*
* @param {string} str the input
* @return {!Uint8Array} the byte array output
* @static
*/
const fromByteString = function(str) {
let output = [];
let p = 0;
for (let i = 0; i < str.length; i++) {
let c = str.charCodeAt(i);
output[p++] = c;
}
return new Uint8Array(output);
};
/**
* Turns a byte array into the string given by the concatenation of the
* characters to which the numbers correspond. Each byte is corresponding to a
* character. Does not support multi-byte characters.
*
* @param {!Uint8Array} bytes Array of numbers representing
* characters.
* @return {string} Stringification of the array.
*/
const toByteString = function(bytes) {
var str = '';
for (var i = 0; i < bytes.length; i += 1) {
str += String.fromCharCode(bytes[i]);
}
return str;
};
exports = {
concat,
fromBase64,
fromHex,
fromNumber,
fromByteString,
isEqual,
toBase64,
toHex,
toByteString,
};