tink-crypto
Version:
A multi-language, cross-platform library that provides cryptographic APIs that are secure, easy to use correctly, and hard(er) to misuse.
120 lines • 15.7 kB
JavaScript
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { SecurityException } from '../exception/security_exception';
import * as Bytes from '../subtle/bytes';
import * as EllipticCurves from '../subtle/elliptic_curves';
import { PbEllipticCurveType, PbHashType, PbKeyStatusType, PbOutputPrefixType, PbPointFormat } from './proto';
/** Like the `instanceof` operator, but works with `Constructor`. */
export function isInstanceOf(value, ctor) {
return value instanceof ctor;
}
/**
* Validates the given key and throws SecurityException if it is invalid.
*
*/
export function validateKey(key) {
if (!key) {
throw new SecurityException('Key should be non null.');
}
if (!key.getKeyData()) {
throw new SecurityException('Key data are missing for key ' + key.getKeyId() + '.');
}
if (key.getOutputPrefixType() === PbOutputPrefixType.UNKNOWN_PREFIX) {
throw new SecurityException('Key ' + key.getKeyId() + ' has unknown output prefix type.');
}
if (key.getStatus() === PbKeyStatusType.UNKNOWN_STATUS) {
throw new SecurityException('Key ' + key.getKeyId() + ' has unknown status.');
}
}
/**
* Validates the given keyset and throws SecurityException if it is invalid.
*
*/
export function validateKeyset(keyset) {
if (!keyset || !keyset.getKeyList() || keyset.getKeyList().length < 1) {
throw new SecurityException('Keyset should be non null and must contain at least one key.');
}
let hasPrimary = false;
const numberOfKeys = keyset.getKeyList().length;
for (let i = 0; i < numberOfKeys; i++) {
const key = keyset.getKeyList()[i];
validateKey(key);
if (keyset.getPrimaryKeyId() === key.getKeyId() &&
key.getStatus() === PbKeyStatusType.ENABLED) {
if (hasPrimary) {
throw new SecurityException('Primary key has to be unique.');
}
hasPrimary = true;
}
}
if (!hasPrimary) {
throw new SecurityException('Primary key has to be in the keyset and ' +
'has to be enabled.');
}
}
// Functions which are useful for implementation of
// private and public EC keys.
/**
* Either prolong or shrinks the array representing number in BigEndian encoding
* to have the specified size. As webcrypto API assumes that x, y and d values
* has exactly the supposed number of bytes, whereas corresponding x, y and
* keyValue values in proto might either have some leading zeros or the leading
* zeros might be missing.
*
*/
export function bigEndianNumberToCorrectLength(bigEndianNumber, sizeInBytes) {
const numberLen = bigEndianNumber.length;
if (numberLen < sizeInBytes) {
const zeros = new Uint8Array(sizeInBytes - numberLen);
return Bytes.concat(zeros, bigEndianNumber);
}
if (numberLen > sizeInBytes) {
for (let i = 0; i < numberLen - sizeInBytes; i++) {
if (bigEndianNumber[i] != 0) {
throw new SecurityException('Number needs more bytes to be represented.');
}
}
return bigEndianNumber.slice(numberLen - sizeInBytes, numberLen);
}
return bigEndianNumber;
}
export function curveTypeProtoToSubtle(curveTypeProto) {
switch (curveTypeProto) {
case PbEllipticCurveType.NIST_P256:
return EllipticCurves.CurveType.P256;
case PbEllipticCurveType.NIST_P384:
return EllipticCurves.CurveType.P384;
case PbEllipticCurveType.NIST_P521:
return EllipticCurves.CurveType.P521;
default:
throw new SecurityException('Unknown curve type.');
}
}
export function hashTypeProtoToString(hashTypeProto) {
switch (hashTypeProto) {
case PbHashType.SHA1:
return 'SHA-1';
case PbHashType.SHA256:
return 'SHA-256';
case PbHashType.SHA512:
return 'SHA-512';
default:
throw new SecurityException('Unknown hash type.');
}
}
export function pointFormatProtoToSubtle(pointFormatProto) {
switch (pointFormatProto) {
case PbPointFormat.UNCOMPRESSED:
return EllipticCurves.PointFormatType.UNCOMPRESSED;
case PbPointFormat.COMPRESSED:
return EllipticCurves.PointFormatType.COMPRESSED;
case PbPointFormat.DO_NOT_USE_CRUNCHY_UNCOMPRESSED:
return EllipticCurves.PointFormatType.DO_NOT_USE_CRUNCHY_UNCOMPRESSED;
default:
throw new SecurityException('Unknown point format.');
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2ludGVybmFsL3V0aWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7R0FJRztBQUVILE9BQU8sRUFBQyxpQkFBaUIsRUFBQyxNQUFNLGlDQUFpQyxDQUFDO0FBQ2xFLE9BQU8sS0FBSyxLQUFLLE1BQU0saUJBQWlCLENBQUM7QUFDekMsT0FBTyxLQUFLLGNBQWMsTUFBTSwyQkFBMkIsQ0FBQztBQUU1RCxPQUFPLEVBQUMsbUJBQW1CLEVBQUUsVUFBVSxFQUF5QixlQUFlLEVBQUUsa0JBQWtCLEVBQUUsYUFBYSxFQUFDLE1BQU0sU0FBUyxDQUFDO0FBVW5JLG9FQUFvRTtBQUNwRSxNQUFNLFVBQVUsWUFBWSxDQUN4QixLQUFjLEVBQUUsSUFBb0I7SUFDdEMsT0FBTyxLQUFLLFlBQVksSUFBSSxDQUFDO0FBQy9CLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUsV0FBVyxDQUFDLEdBQWdCO0lBQzFDLElBQUksQ0FBQyxHQUFHLEVBQUU7UUFDUixNQUFNLElBQUksaUJBQWlCLENBQUMseUJBQXlCLENBQUMsQ0FBQztLQUN4RDtJQUNELElBQUksQ0FBQyxHQUFHLENBQUMsVUFBVSxFQUFFLEVBQUU7UUFDckIsTUFBTSxJQUFJLGlCQUFpQixDQUN2QiwrQkFBK0IsR0FBRyxHQUFHLENBQUMsUUFBUSxFQUFFLEdBQUcsR0FBRyxDQUFDLENBQUM7S0FDN0Q7SUFDRCxJQUFJLEdBQUcsQ0FBQyxtQkFBbUIsRUFBRSxLQUFLLGtCQUFrQixDQUFDLGNBQWMsRUFBRTtRQUNuRSxNQUFNLElBQUksaUJBQWlCLENBQ3ZCLE1BQU0sR0FBRyxHQUFHLENBQUMsUUFBUSxFQUFFLEdBQUcsa0NBQWtDLENBQUMsQ0FBQztLQUNuRTtJQUNELElBQUksR0FBRyxDQUFDLFNBQVMsRUFBRSxLQUFLLGVBQWUsQ0FBQyxjQUFjLEVBQUU7UUFDdEQsTUFBTSxJQUFJLGlCQUFpQixD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USxhQUFhLEVBQUU7UUFDckIsS0FBSyxVQUFVLENBQUMsSUFBSTtZQUNsQixPQUFPLE9BQU8sQ0FBQztRQUNqQixLQUFLLFVBQVUsQ0FBQyxNQUFNO1lBQ3BCLE9BQU8sU0FBUyxDQUFDO1FBQ25CLEtBQUssVUFBVSxDQUFDLE1BQU07WUFDcEIsT0FBTyxTQUFTLENBQUM7UUFDbkI7WUFDRSxNQUFNLElBQUksaUJBQWlCLENBQUMsb0JBQW9CLENBQUMsQ0FBQztLQUNyRDtBQUNILENBQUM7QUFFRCxNQUFNLFVBQVUsd0JBQXdCLENBQUMsZ0JBQStCO0lBRXRFLFFBQVEsZ0JBQWdCLEVBQUU7UUFDeEIsS0FBSyxhQUFhLENBQUMsWUFBWTtZQUM3QixPQUFPLGNBQWMsQ0FBQyxlQUFlLENBQUMsWUFBWSxDQUFDO1FBQ3JELEtBQUssYUFBYSxDQUFDLFVBQVU7WUFDM0IsT0FBTyxjQUFjLENBQUMsZUFBZSxDQUFDLFVBQVUsQ0FBQztRQUNuRCxLQUFLLGFBQWEsQ0FBQywrQkFBK0I7WUFDaEQsT0FBTyxjQUFjLENBQUMsZUFBZSxDQUFDLCtCQUErQixDQUFDO1FBQ3hFO1lBQ0UsTUFBTSxJQUFJLGlCQUFpQixDQUFDLHVCQUF1QixDQUFDLENBQUM7S0FDeEQ7QUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IDIwMjAgR29vZ2xlIExMQ1xuICogU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcbiAqL1xuXG5pbXBvcnQge1NlY3VyaXR5RXhjZXB0aW9ufSBmcm9tICcuLi9leGNlcHRpb24vc2VjdXJpdHlfZXhjZXB0aW9uJztcbmltcG9ydCAqIGFzIEJ5dGVzIGZyb20gJy4uL3N1YnRsZS9ieXRlcyc7XG5pbXBvcnQgKiBhcyBFbGxpcHRpY0N1cnZlcyBmcm9tICcuLi9zdWJ0bGUvZWxsaXB0aWNfY3VydmVzJztcblxuaW1wb3J0IHtQYkVsbGlwdGljQ3VydmVUeXBlLCBQYkhhc2hUeXBlLCBQYktleXNldCwgUGJLZXlzZXRLZXksIFBiS2V5U3RhdHVzVHlwZSwgUGJPdXRwdXRQcmVmaXhUeXBlLCBQYlBvaW50Rm9ybWF0fSBmcm9tICcuL3Byb3RvJztcblxuLyoqXG4gKiBBIHR5cGUgcmVwcmVzZW50aW5nIHRoZSBjb25zdHJ1Y3RvciBmdW5jdGlvbiBmb3IgYSBnaXZlbiBjbGFzcy4gVW5saWtlXG4gKiBUeXBlU2NyaXB0J3MgYnVpbHQtaW4gYG5ld2AgdHlwZXMsIHRoaXMgd29ya3Mgd2l0aCBhYnN0cmFjdCBjbGFzc2VzLiBJdCBpc1xuICogdXNlZCB0byBkZXNjcmliZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYSBwcmltaXRpdmUgdHlwZSBvYmplY3QgYW5kIGl0c1xuICogaW5zdGFuY2VzLlxuICovXG5leHBvcnQgdHlwZSBDb25zdHJ1Y3RvcjxUPiA9IEZ1bmN0aW9uJntwcm90b3R5cGU6IFR9O1xuXG4vKiogTGlrZSB0aGUgYGluc3RhbmNlb2ZgIG9wZXJhdG9yLCBidXQgd29ya3Mgd2l0aCBgQ29uc3RydWN0b3JgLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzSW5zdGFuY2VPZjxUPihcbiAgICB2YWx1ZTogdW5rbm93biwgY3RvcjogQ29uc3RydWN0b3I8VD4pOiB2YWx1ZSBpcyBUIHtcbiAgcmV0dXJuIHZhbHVlIGluc3RhbmNlb2YgY3Rvcjtcbn1cblxuLyoqXG4gKiBWYWxpZGF0ZXMgdGhlIGdpdmVuIGtleSBhbmQgdGhyb3dzIFNlY3VyaXR5RXhjZXB0aW9uIGlmIGl0IGlzIGludmFsaWQuXG4gKlxuICovXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRhdGVLZXkoa2V5OiBQYktleXNldEtleSkge1xuICBpZiAoIWtleSkge1xuICAgIHRocm93IG5ldyBTZWN1cml0eUV4Y2VwdGlvbignS2V5IHNob3VsZCBiZSBub24gbnVsbC4nKTtcbiAgfVxuICBpZiAoIWtleS5nZXRLZXlEYXRhKCkpIHtcbiAgICB0aHJvdyBuZXcgU2VjdXJpdHlFeGNlcHRpb24oXG4gICAgICAgICdLZXkgZGF0YSBhcmUgbWlzc2luZyBmb3Iga2V5ICcgKyBrZXkuZ2V0S2V5SWQoKSArICcuJyk7XG4gIH1cbiAgaWYgKGtleS5nZXRPdXRwdXRQcmVmaXhUeXBlKCkgPT09IFBiT3V0cHV0UHJlZml4VHlwZS5VTktOT1dOX1BSRUZJWCkge1xuICAgIHRocm93IG5ldyBTZWN1cml0eUV4Y2VwdGlvbihcbiAgICAgICAgJ0tleSAnICsga2V5LmdldEtleUlkKCkgKyAnIGhhcyB1bmtub3duIG91dHB1dCBwcmVmaXggdHlwZS4nKTtcbiAgfVxuICBpZiAoa2V5LmdldFN0YXR1cygpID09PSBQYktleVN0YXR1c1R5cGUuVU5LTk9XTl9TVEFUVVMpIHtcbiAgICB0aHJvdyBuZXcgU2VjdXJpdHlFeGNlcHRpb24oXG4gICAgICAgICdLZXkgJyArIGtleS5nZXRLZXlJZCgpICsgJyBoYXMgdW5rbm93biBzdGF0dXMuJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBWYWxpZGF0ZXMgdGhlIGdpdmVuIGtleXNldCBhbmQgdGhyb3dzIFNlY3VyaXR5RXhjZXB0aW9uIGlmIGl0IGlzIGludmFsaWQuXG4gKlxuICovXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRhdGVLZXlzZXQoa2V5c2V0OiBQYktleXNldCkge1xuICBpZiAoIWtleXNldCB8fCAha2V5c2V0LmdldEtleUxpc3QoKSB8fCBrZXlzZXQuZ2V0S2V5TGlzdCgpLmxlbmd0aCA8IDEpIHtcbiAgICB0aHJvdyBuZXcgU2VjdXJpdHlFeGNlcHRpb24oXG4gICAgICAgICdLZXlzZXQgc2hvdWxkIGJlIG5vbiBudWxsIGFuZCBtdXN0IGNvbnRhaW4gYXQgbGVhc3Qgb25lIGtleS4nKTtcbiAgfVxuICBsZXQgaGFzUHJpbWFyeSA9IGZhbHNlO1xuICBjb25zdCBudW1iZXJPZktleXMgPSBrZXlzZXQuZ2V0S2V5TGlzdCgpLmxlbmd0aDtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBudW1iZXJPZktleXM7IGkrKykge1xuICAgIGNvbnN0IGtleSA9IGtleXNldC5nZXRLZXlMaXN0KClbaV07XG4gICAgdmFsaWRhdGVLZXkoa2V5KTtcbiAgICBpZiAoa2V5c2V0LmdldFByaW1hcnlLZXlJZCgpID09PSBrZXkuZ2V0S2V5SWQoKSAmJlxuICAgICAgICBrZXkuZ2V0U3RhdHVzKCkgPT09IFBiS2V5U3RhdHVzVHlwZS5FTkFCTEVEKSB7XG4gICAgICBpZiAoaGFzUHJpbWFyeSkge1xuICAgICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFeGNlcHRpb24oJ1ByaW1hcnkga2V5IGhhcyB0byBiZSB1bmlxdWUuJyk7XG4gICAgICB9XG4gICAgICBoYXNQcmltYXJ5ID0gdHJ1ZTtcbiAgICB9XG4gIH1cbiAgaWYgKCFoYXNQcmltYXJ5KSB7XG4gICAgdGhyb3cgbmV3IFNlY3VyaXR5RXhjZXB0aW9uKFxuICAgICAgICAnUHJpbWFyeSBrZXkgaGFzIHRvIGJlIGluIHRoZSBrZXlzZXQgYW5kICcgK1xuICAgICAgICAnaGFzIHRvIGJlIGVuYWJsZWQuJyk7XG4gIH1cbn1cblxuLy8gRnVuY3Rpb25zIHdoaWNoIGFyZSB1c2VmdWwgZm9yIGltcGxlbWVudGF0aW9uIG9mXG4vLyBwcml2YXRlIGFuZCBwdWJsaWMgRUMga2V5cy5cblxuLyoqXG4gKiBFaXRoZXIgcHJvbG9uZyBvciBzaHJpbmtzIHRoZSBhcnJheSByZXByZXNlbnRpbmcgbnVtYmVyIGluIEJpZ0VuZGlhbiBlbmNvZGluZ1xuICogdG8gaGF2ZSB0aGUgc3BlY2lmaWVkIHNpemUuIEFzIHdlYmNyeXB0byBBUEkgYXNzdW1lcyB0aGF0IHgsIHkgYW5kIGQgdmFsdWVzXG4gKiBoYXMgZXhhY3RseSB0aGUgc3VwcG9zZWQgbnVtYmVyIG9mIGJ5dGVzLCB3aGVyZWFzIGNvcnJlc3BvbmRpbmcgeCwgeSBhbmRcbiAqIGtleVZhbHVlIHZhbHVlcyBpbiBwcm90byBtaWdodCBlaXRoZXIgaGF2ZSBzb21lIGxlYWRpbmcgemVyb3Mgb3IgdGhlIGxlYWRpbmdcbiAqIHplcm9zIG1pZ2h0IGJlIG1pc3NpbmcuXG4gKlxuICovXG5leHBvcnQgZnVuY3Rpb24gYmlnRW5kaWFuTnVtYmVyVG9Db3JyZWN0TGVuZ3RoKFxuICAgIGJpZ0VuZGlhbk51bWJlcjogVWludDhBcnJheSwgc2l6ZUluQnl0ZXM6IG51bWJlcik6IFVpbnQ4QXJyYXkge1xuICBjb25zdCBudW1iZXJMZW4gPSBiaWdFbmRpYW5OdW1iZXIubGVuZ3RoO1xuICBpZiAobnVtYmVyTGVuIDwgc2l6ZUluQnl0ZXMpIHtcbiAgICBjb25zdCB6ZXJvcyA9IG5ldyBVaW50OEFycmF5KHNpemVJbkJ5dGVzIC0gbnVtYmVyTGVuKTtcbiAgICByZXR1cm4gQnl0ZXMuY29uY2F0KHplcm9zLCBiaWdFbmRpYW5OdW1iZXIpO1xuICB9XG4gIGlmIChudW1iZXJMZW4gPiBzaXplSW5CeXRlcykge1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbnVtYmVyTGVuIC0gc2l6ZUluQnl0ZXM7IGkrKykge1xuICAgICAgaWYgKGJpZ0VuZGlhbk51bWJlcltpXSAhPSAwKSB7XG4gICAgICAgIHRocm93IG5ldyBTZWN1cml0eUV4Y2VwdGlvbihcbiAgICAgICAgICAgICdOdW1iZXIgbmVlZHMgbW9yZSBieXRlcyB0byBiZSByZXByZXNlbnRlZC4nKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGJpZ0VuZGlhbk51bWJlci5zbGljZShudW1iZXJMZW4gLSBzaXplSW5CeXRlcywgbnVtYmVyTGVuKTtcbiAgfVxuICByZXR1cm4gYmlnRW5kaWFuTnVtYmVyO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3VydmVUeXBlUHJvdG9Ub1N1YnRsZShjdXJ2ZVR5cGVQcm90bzogUGJFbGxpcHRpY0N1cnZlVHlwZSk6XG4gICAgRWxsaXB0aWNDdXJ2ZXMuQ3VydmVUeXBlIHtcbiAgc3dpdGNoIChjdXJ2ZVR5cGVQcm90bykge1xuICAgIGNhc2UgUGJFbGxpcHRpY0N1cnZlVHlwZS5OSVNUX1AyNTY6XG4gICAgICByZXR1cm4gRWxsaXB0aWNDdXJ2ZXMuQ3VydmVUeXBlLlAyNTY7XG4gICAgY2FzZSBQYkVsbGlwdGljQ3VydmVUeXBlLk5JU1RfUDM4NDpcbiAgICAgIHJldHVybiBFbGxpcHRpY0N1cnZlcy5DdXJ2ZVR5cGUuUDM4NDtcbiAgICBjYXNlIFBiRWxsaXB0aWNDdXJ2ZVR5cGUuTklTVF9QNTIxOlxuICAgICAgcmV0dXJuIEVsbGlwdGljQ3VydmVzLkN1cnZlVHlwZS5QNTIxO1xuICAgIGRlZmF1bHQ6XG4gICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFeGNlcHRpb24oJ1Vua25vd24gY3VydmUgdHlwZS4nKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gaGFzaFR5cGVQcm90b1RvU3RyaW5nKGhhc2hUeXBlUHJvdG86IFBiSGFzaFR5cGUpOiBzdHJpbmcge1xuICBzd2l0Y2ggKGhhc2hUeXBlUHJvdG8pIHtcbiAgICBjYXNlIFBiSGFzaFR5cGUuU0hBMTpcbiAgICAgIHJldHVybiAnU0hBLTEnO1xuICAgIGNhc2UgUGJIYXNoVHlwZS5TSEEyNTY6XG4gICAgICByZXR1cm4gJ1NIQS0yNTYnO1xuICAgIGNhc2UgUGJIYXNoVHlwZS5TSEE1MTI6XG4gICAgICByZXR1cm4gJ1NIQS01MTInO1xuICAgIGRlZmF1bHQ6XG4gICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFeGNlcHRpb24oJ1Vua25vd24gaGFzaCB0eXBlLicpO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwb2ludEZvcm1hdFByb3RvVG9TdWJ0bGUocG9pbnRGb3JtYXRQcm90bzogUGJQb2ludEZvcm1hdCk6XG4gICAgRWxsaXB0aWNDdXJ2ZXMuUG9pbnRGb3JtYXRUeXBlIHtcbiAgc3dpdGNoIChwb2ludEZvcm1hdFByb3RvKSB7XG4gICAgY2FzZSBQYlBvaW50Rm9ybWF0LlVOQ09NUFJFU1NFRDpcbiAgICAgIHJldHVybiBFbGxpcHRpY0N1cnZlcy5Qb2ludEZvcm1hdFR5cGUuVU5DT01QUkVTU0VEO1xuICAgIGNhc2UgUGJQb2ludEZvcm1hdC5DT01QUkVTU0VEOlxuICAgICAgcmV0dXJuIEVsbGlwdGljQ3VydmVzLlBvaW50Rm9ybWF0VHlwZS5DT01QUkVTU0VEO1xuICAgIGNhc2UgUGJQb2ludEZvcm1hdC5ET19OT1RfVVNFX0NSVU5DSFlfVU5DT01QUkVTU0VEOlxuICAgICAgcmV0dXJuIEVsbGlwdGljQ3VydmVzLlBvaW50Rm9ybWF0VHlwZS5ET19OT1RfVVNFX0NSVU5DSFlfVU5DT01QUkVTU0VEO1xuICAgIGRlZmF1bHQ6XG4gICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFeGNlcHRpb24oJ1Vua25vd24gcG9pbnQgZm9ybWF0LicpO1xuICB9XG59XG4iXX0=