| /** |
| * @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, PbKeyset, PbKeysetKey, PbKeyStatusType, PbOutputPrefixType, PbPointFormat} from './proto'; |
| |
| /** |
| * A type representing the constructor function for a given class. Unlike |
| * TypeScript's built-in `new` types, this works with abstract classes. It is |
| * used to describe the relationship between a primitive type object and its |
| * instances. |
| */ |
| export type Constructor<T> = Function&{prototype: T}; |
| |
| /** Like the `instanceof` operator, but works with `Constructor`. */ |
| export function isInstanceOf<T>( |
| value: unknown, ctor: Constructor<T>): value is T { |
| return value instanceof ctor; |
| } |
| |
| /** |
| * Validates the given key and throws SecurityException if it is invalid. |
| * |
| */ |
| export function validateKey(key: PbKeysetKey) { |
| 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: PbKeyset) { |
| 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: Uint8Array, sizeInBytes: number): Uint8Array { |
| 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: PbEllipticCurveType): |
| EllipticCurves.CurveType { |
| 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: PbHashType): string { |
| 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: PbPointFormat): |
| EllipticCurves.PointFormatType { |
| 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.'); |
| } |
| } |