blob: 65489ede5eca74e42d2ca77fd8212d230d18ec96 [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.signature.EcdsaPrivateKeyManager');
const Bytes = goog.require('tink.subtle.Bytes');
const EcdsaPublicKeyManager = goog.require('tink.signature.EcdsaPublicKeyManager');
const EcdsaSign = goog.require('tink.subtle.EcdsaSign');
const EcdsaUtil = goog.require('tink.signature.EcdsaUtil');
const EllipticCurves = goog.require('tink.subtle.EllipticCurves');
const KeyManager = goog.require('tink.KeyManager');
const PbEcdsaKeyFormat = goog.require('proto.google.crypto.tink.EcdsaKeyFormat');
const PbEcdsaParams = goog.require('proto.google.crypto.tink.EcdsaParams');
const PbEcdsaPrivateKey = goog.require('proto.google.crypto.tink.EcdsaPrivateKey');
const PbEcdsaPublicKey = goog.require('proto.google.crypto.tink.EcdsaPublicKey');
const PbKeyData = goog.require('proto.google.crypto.tink.KeyData');
const PbMessage = goog.require('jspb.Message');
const PublicKeySign = goog.require('tink.PublicKeySign');
const SecurityException = goog.require('tink.exception.SecurityException');
const Util = goog.require('tink.Util');
/**
* @implements {KeyManager.PrivateKeyFactory}
* @final
*/
class EcdsaPrivateKeyFactory {
/**
* @override
* @return {!Promise<!PbEcdsaPrivateKey>}
*/
async newKey(keyFormat) {
if (!keyFormat) {
throw new SecurityException('Key format has to be non-null.');
}
const keyFormatProto = EcdsaPrivateKeyFactory.getKeyFormatProto_(keyFormat);
EcdsaUtil.validateKeyFormat(keyFormatProto);
return await EcdsaPrivateKeyFactory.newKeyImpl_(keyFormatProto);
}
/**
* @override
* @return {!Promise<!PbKeyData>}
*/
async newKeyData(serializedKeyFormat) {
const key = await this.newKey(serializedKeyFormat);
const keyData =
new PbKeyData()
.setTypeUrl(EcdsaPrivateKeyManager.KEY_TYPE)
.setValue(key.serializeBinary())
.setKeyMaterialType(PbKeyData.KeyMaterialType.ASYMMETRIC_PRIVATE);
return keyData;
}
/** @override */
getPublicKeyData(serializedPrivateKey) {
const privateKey =
EcdsaPrivateKeyManager.deserializePrivateKey_(serializedPrivateKey);
const publicKeyData =
new PbKeyData()
.setValue(privateKey.getPublicKey().serializeBinary())
.setTypeUrl(EcdsaPublicKeyManager.KEY_TYPE)
.setKeyMaterialType(PbKeyData.KeyMaterialType.ASYMMETRIC_PUBLIC);
return publicKeyData;
}
/**
* Generates key corresponding to the given key format.
* WARNING: This function assumes that the keyFormat has been validated.
*
* @private
* @param {!PbEcdsaKeyFormat} keyFormat
* @return {!Promise<!PbEcdsaPrivateKey>}
*/
static async newKeyImpl_(keyFormat) {
const params =
/** @type {!PbEcdsaParams} */ (keyFormat.getParams());
const curveTypeProto = params.getCurve();
const curveTypeSubtle = Util.curveTypeProtoToSubtle(curveTypeProto);
const curveName = EllipticCurves.curveToString(curveTypeSubtle);
const keyPair = await EllipticCurves.generateKeyPair('ECDSA', curveName);
const jsonPublicKey =
await EllipticCurves.exportCryptoKey(/** @type {?} */ (keyPair).publicKey);
const jsonPrivateKey =
await EllipticCurves.exportCryptoKey(/** @type {?} */ (keyPair).privateKey);
return EcdsaPrivateKeyFactory.jsonToProtoKey_(
jsonPrivateKey, jsonPublicKey, params);
}
/**
* Creates a private key proto corresponding to given JSON key pair and with
* the given params.
*
* @private
* @param {!webCrypto.JsonWebKey} jsonPrivateKey
* @param {!webCrypto.JsonWebKey} jsonPublicKey
* @param {!PbEcdsaParams} params
* @return {!PbEcdsaPrivateKey}
*/
static jsonToProtoKey_(jsonPrivateKey, jsonPublicKey, params) {
const publicKeyProto =
new PbEcdsaPublicKey()
.setVersion(EcdsaPublicKeyManager.VERSION)
.setParams(params)
.setX(Bytes.fromBase64(jsonPublicKey['x'], true))
.setY(Bytes.fromBase64(jsonPublicKey['y'], true));
const privateKeyProto =
new PbEcdsaPrivateKey()
.setVersion(EcdsaPrivateKeyManager.VERSION_)
.setPublicKey(publicKeyProto)
.setKeyValue(Bytes.fromBase64(jsonPrivateKey['d'], true));
return privateKeyProto;
}
/**
* The input keyFormat is either deserialized (in case that the input is
* Uint8Array) or checked to be an EcdsaKeyFormat-proto (otherwise).
*
* @private
* @param {!PbMessage|!Uint8Array} keyFormat
* @return {!PbEcdsaKeyFormat}
*/
static getKeyFormatProto_(keyFormat) {
if (keyFormat instanceof Uint8Array) {
return EcdsaPrivateKeyFactory.deserializeKeyFormat_(keyFormat);
} else {
if (keyFormat instanceof PbEcdsaKeyFormat) {
return keyFormat;
} else {
throw new SecurityException(
'Expected ' + EcdsaPrivateKeyManager.KEY_TYPE +
' key format proto.');
}
}
}
/**
* @private
* @param {!Uint8Array} keyFormat
* @return {!PbEcdsaKeyFormat}
*/
static deserializeKeyFormat_(keyFormat) {
let /** !PbEcdsaKeyFormat */ keyFormatProto;
try {
keyFormatProto = PbEcdsaKeyFormat.deserializeBinary(keyFormat);
} catch (e) {
throw new SecurityException(
'Input cannot be parsed as ' + EcdsaPrivateKeyManager.KEY_TYPE +
' key format proto.');
}
if (!keyFormatProto.getParams()) {
throw new SecurityException(
'Input cannot be parsed as ' + EcdsaPrivateKeyManager.KEY_TYPE +
' key format proto.');
}
return keyFormatProto;
}
}
/**
* @implements {KeyManager.KeyManager<PublicKeySign>}
* @final
*/
class EcdsaPrivateKeyManager {
constructor() {
this.keyFactory = new EcdsaPrivateKeyFactory();
}
/** @override */
async getPrimitive(primitiveType, key) {
if (primitiveType !== this.getPrimitiveType()) {
throw new SecurityException(
'Requested primitive type which is not ' +
'supported by this key manager.');
}
const keyProto = EcdsaPrivateKeyManager.getKeyProto_(key);
EcdsaUtil.validatePrivateKey(
keyProto, EcdsaPrivateKeyManager.VERSION_,
EcdsaPublicKeyManager.VERSION);
const recepientPrivateKey = EcdsaUtil.getJsonWebKeyFromProto(keyProto);
const params =
/** @type {!PbEcdsaParams} */ (keyProto.getPublicKey().getParams());
const hash = Util.hashTypeProtoToString(params.getHashType());
const encoding = EcdsaUtil.encodingTypeProtoToEnum(params.getEncoding());
return await EcdsaSign.newInstance(recepientPrivateKey, hash, encoding);
}
/** @override */
doesSupport(keyType) {
return keyType === this.getKeyType();
}
/** @override */
getKeyType() {
return EcdsaPrivateKeyManager.KEY_TYPE;
}
/** @override */
getPrimitiveType() {
return EcdsaPrivateKeyManager.SUPPORTED_PRIMITIVE_;
}
/** @override */
getVersion() {
return EcdsaPrivateKeyManager.VERSION_;
}
/** @override */
getKeyFactory() {
return this.keyFactory;
}
/**
* @private
* @param {!PbKeyData|!PbMessage} keyMaterial
* @return {!PbEcdsaPrivateKey}
*/
static getKeyProto_(keyMaterial) {
if (keyMaterial instanceof PbKeyData) {
return EcdsaPrivateKeyManager.getKeyProtoFromKeyData_(keyMaterial);
}
if (keyMaterial instanceof PbEcdsaPrivateKey) {
return keyMaterial;
}
throw new SecurityException(
'Key type is not supported. This key ' +
'manager supports ' + EcdsaPrivateKeyManager.KEY_TYPE + '.');
}
/**
* @private
* @param {!PbKeyData} keyData
* @return {!PbEcdsaPrivateKey}
*/
static getKeyProtoFromKeyData_(keyData) {
if (keyData.getTypeUrl() !== EcdsaPrivateKeyManager.KEY_TYPE) {
throw new SecurityException(
'Key type ' + keyData.getTypeUrl() +
' is not supported. This key manager supports ' +
EcdsaPrivateKeyManager.KEY_TYPE + '.');
}
return EcdsaPrivateKeyManager.deserializePrivateKey_(
keyData.getValue_asU8());
}
/**
* @private
* @param {!Uint8Array} serializedPrivateKey
* @return {!PbEcdsaPrivateKey}
*/
static deserializePrivateKey_(serializedPrivateKey) {
let /** !PbEcdsaPrivateKey */ key;
try {
key = PbEcdsaPrivateKey.deserializeBinary(serializedPrivateKey);
} catch (e) {
throw new SecurityException(
'Input cannot be parsed as ' + EcdsaPrivateKeyManager.KEY_TYPE +
' key-proto.');
}
if (!key.getPublicKey() || !key.getKeyValue()) {
throw new SecurityException(
'Input cannot be parsed as ' + EcdsaPrivateKeyManager.KEY_TYPE +
' key-proto.');
}
return key;
}
}
/** @const @private {!Object} */
EcdsaPrivateKeyManager.SUPPORTED_PRIMITIVE_ = PublicKeySign;
/** @const @public {string} */
EcdsaPrivateKeyManager.KEY_TYPE =
'type.googleapis.com/google.crypto.tink.EcdsaPrivateKey';
/** @const @private {number} */
EcdsaPrivateKeyManager.VERSION_ = 0;
exports = EcdsaPrivateKeyManager;