Develop crypto library

This commit is contained in:
Vladimir Privezenov
2025-12-19 19:38:56 +03:00
parent ee56819e63
commit 4a51b18279
8 changed files with 233 additions and 176 deletions

View File

@ -1,56 +1,10 @@
import {WebEncryptKeyPair, WebSignKeyPair, WebSymmetricKey} from "./keys";
import {EncryptData, WebEncryptKeyPair, WebSignKeyPair, WebSymmetricKey} from "./keys";
import {AesGcmGenParams} from "./params";
import {c_oAscDigestType, c_oAscExportKeyFormat} from "./defines";
import {writeBuffer, writeLong} from "./serialize/writer";
import {BinaryWriter, writeBuffer, writeLong} from "./serialize/writer";
import {readBuffer, readLong} from "./serialize/reader";
import {initClass} from "./utils";
const PBKDFSaltLength = 16;
function PBKDF2Params() {
this.version = 1;
this.iterations = 600000;
this.hash = c_oAscDigestType.SHA256;
this.salt = null;
}
PBKDF2Params.import = function(reader) {
const params = new PBKDF2Params();
params.setVerison(readLong(reader));
switch (params.version) {
case 1: {
this.setIterations(readLong(reader));
this.setHash(readLong(reader));
this.setSalt(readBuffer(reader));
break;
}
default: {
return null;
}
}
return params;
}
PBKDF2Params.prototype.export = function(writer) {
writeLong(this.version);
switch (this.version) {
case 1: {
writeLong(writer, this.iterations);
writeLong(writer, this.hash);
writeBuffer(writer, this.salt);
break;
}
default: {
break;
}
}
}
PBKDF2Params.prototype.getCryptoParams = function() {
return {
name: 'PBKDF2',
salt: this.salt,
iterations: this.iterations,
hash: getCryptoHash(this.hash)
};
};
function CCryptoBase() {
}
@ -60,8 +14,6 @@ CCryptoBase.prototype.verify = function(key, signature, data) {};
CCryptoBase.prototype.decrypt = function(key, data) {};
CCryptoBase.prototype.encrypt = function(key, data) {};
CCryptoBase.prototype.generateKey = function(params) {};
CCryptoBase.prototype.wrapKey = function(format, key, masterPassword, salt, aesParams, keyUsage) {};
CCryptoBase.prototype.unwrapKey = function(format, key, masterPassword, salt, aesParams, keyParams, keyUsage) {};
CCryptoBase.prototype.getRandomValues = function(length) {};
CCryptoBase.prototype.randomUUID = function() {};
@ -95,26 +47,8 @@ CWebCrypto.prototype.getAesCryptoKey = function(masterPassword, salt) {
);
}).then(function(aesKey) {
return WebSymmetricKey.fromCryptoKey(aesKey, pbkdfParams);
});
};
CWebCrypto.prototype.wrapKey = function (format, key, masterPassword, salt, aesParams) {
const oThis = this;
const cryptoKey = key.getCryptoKey();
return Promise.all([this.getAesCryptoKey(masterPassword, salt), cryptoKey]).then(function(cryptoKeys) {
return oThis.subtle.wrapKey(format, cryptoKeys[1], cryptoKeys[0], aesParams);
});
}
CWebCrypto.prototype.unwrapKey = function(format, key, masterPassword, salt, aesParams, keyParams) {
const oThis = this;
return this.getAesCryptoKey(masterPassword, salt).then(function(cryptoAesKey) {
return oThis.subtle.unwrapKey(format, key, cryptoAesKey, aesParams, keyParams, true, /*this.getKeyUsages(keyUsages)*/["sign"]);
}).then(function(cryptoKey) {
return oThis.subtle.exportKey(format, cryptoKey);
});
}
CWebCrypto.prototype.sign = function(key, data) {
const oThis = this;
const cryptoKey = key.getCryptoKey();
@ -132,13 +66,18 @@ CWebCrypto.prototype.verify = function(key, signature, data) {
CWebCrypto.prototype.decrypt = function(key, data) {
const oThis = this;
const cryptoKey = key.getCryptoKey();
const algorithm = key.getCryptoAlgrotihm();
return oThis.subtle.decrypt(algorithm, cryptoKey, data);
const algorithm = data.getEncryptParams();
return oThis.subtle.decrypt(algorithm, cryptoKey, data.getEncryptData());
}
CWebCrypto.prototype.encrypt = function(key, data) {
const cryptoKey = key.getCryptoKey();
const algorithm = key.getCryptoAlgorithm();
return this.subtle.encrypt(algorithm, cryptoKey, data);
const algorithm = key.getEncryptParams();
return this.subtle.encrypt(algorithm, cryptoKey, data).then(function (encryptedData) {
const data = new EncryptData(encryptedData, algorithm);
const writer = new BinaryWriter();
data.export(writer);
return writer.GetData();
});
}
CWebCrypto.prototype.exportKey = function(key) {
const cryptoKey = key.getCryptoKey();
@ -153,12 +92,14 @@ CWebCrypto.prototype.generateKey = function(params, password, salt) {
return this.subtle.generateKey(cryptoParams, true, cryptoUsages).then(function(cryptoKey) {
if (cryptoKey.privateKey && cryptoKey.publicKey) {
const publicKey = oThis.subtle.exportKey(c_oAscExportKeyFormat.spki, cryptoKey.publicKey);
const privateKey = aesKey.export(c_oAscExportKeyFormat.pkcs8, cryptoKey.privateKey).then(function(data) {
const privateKey = oThis.subtle.exportKey(c_oAscExportKeyFormat.pkcs8, cryptoKey.privateKey).then(function(data) {
return aesKey.encrypt(data);
});
return Promise.all([publicKey,privateKey]);
return Promise.all([publicKey, privateKey]);
}
return oThis.subtle.exportKey(c_oAscExportKeyFormat.raw, cryptoKey);
return oThis.subtle.exportKey(c_oAscExportKeyFormat.raw, cryptoKey).then(function (data) {
return aesKey.encrypt(data);
});
}).then(function(exportedKeys) {
const importParams = params.getImportParams();
if (Array.isArray(exportedKeys)) {

View File

@ -25,4 +25,10 @@ export const c_oAscKeyStorageType = {
WebPublicKey: 4,
WebPrivateKey: 5,
Ed25519ImportParams: 6,
EncryptData: 7,
RSAOAEPImportParams: 8,
RSAOAEPKeyGenParams: 9,
Ed25519KeyGenParams: 10,
AesGCMCryptoParams: 11,
AesGCMKeyGenParams: 12
};

View File

@ -1,9 +1,23 @@
import {c_oAscKeyStorageType} from "./defines";
import {WebEncryptKeyPair, WebPrivateKey, WebPublicKey, WebSignKeyPair, WebSymmetricKey} from "./keys";
import {EncryptData, WebEncryptKeyPair, WebPrivateKey, WebPublicKey, WebSignKeyPair, WebSymmetricKey} from "./keys";
import {
AesGcmCryptoParams, AesGcmKeyGenParams,
Ed25519ImportParams,
Ed25519KeyGenParams,
RsaOAEPImportParams,
RsaOAEPKeyGenParams
} from "./params";
export const c_oAscObjectFactory = {};
c_oAscObjectFactory[c_oAscKeyStorageType.WebPublicKey] = WebPublicKey;
c_oAscObjectFactory[c_oAscKeyStorageType.WebPrivateKey] = WebPrivateKey;
c_oAscObjectFactory[c_oAscKeyStorageType.WebSymmetricKey] = WebSymmetricKey;
c_oAscObjectFactory[c_oAscKeyStorageType.WebSignKeyPair] = WebSignKeyPair;
c_oAscObjectFactory[c_oAscKeyStorageType.WebEncryptKeyPair] = WebEncryptKeyPair;
c_oAscObjectFactory[c_oAscKeyStorageType.WebPublicKey] = WebPublicKey;
c_oAscObjectFactory[c_oAscKeyStorageType.WebPrivateKey] = WebPrivateKey;
c_oAscObjectFactory[c_oAscKeyStorageType.Ed25519ImportParams] = Ed25519ImportParams;
c_oAscObjectFactory[c_oAscKeyStorageType.EncryptData] = EncryptData;
c_oAscObjectFactory[c_oAscKeyStorageType.RSAOAEPImportParams] = RsaOAEPImportParams;
c_oAscObjectFactory[c_oAscKeyStorageType.RSAOAEPKeyGenParams] = RsaOAEPKeyGenParams;
c_oAscObjectFactory[c_oAscKeyStorageType.Ed25519KeyGenParams] = Ed25519KeyGenParams;
c_oAscObjectFactory[c_oAscKeyStorageType.AesGCMCryptoParams] = AesGcmCryptoParams;
c_oAscObjectFactory[c_oAscKeyStorageType.AesGCMKeyGenParams] = AesGcmKeyGenParams;

View File

@ -1,52 +1,11 @@
import {initClass} from "./utils";
import {CryptoBase, initClass} from "./utils";
import {c_oAscKeyStorageType} from "./defines";
import {writeBool, writeBuffer, writeString} from "./serialize/writer";
import {BinaryWriter, writeBool, writeBuffer, writeLong, writeObject, writeString} from "./serialize/writer";
import {getCrypto} from "./crypto";
import {readBool, readBuffer, readLong, readString} from "./serialize/reader";
function PromiseManager(initPromise) {
this.data = null;
this.error = null;
this.isResolved = false;
this.isRejected = false;
this.resolvers = [];
const oThis= this;
initPromise.then(function(data) {
oThis.isResolved = true;
oThis.data = data;
}).catch(function(error) {
oThis.isRejected = true;
oThis.error = error;
}).finally(function() {
oThis.handleResolvers();
});
}
PromiseManager.prototype.getPromise = function() {
const oThis = this;
return new Promise(function(resolve, reject) {
if (oThis.isResolved) {
resolve(oThis.data);
} else if (oThis.isRejected) {
reject(oThis.error);
} else {
oThis.resolvers.push({resolve: resolve, reject: reject});
}
});
};
PromiseManager.prototype.handleResolvers = function() {
while (this.resolvers.length) {
const resolver = this.resolvers.pop();
if (this.isResolved) {
resolver.resolve(this.data);
} else if (this.isRejected) {
resolver.reject(this.error);
}
}
};
import {BinaryReader, readBool, readBuffer, readLong, readObject, readString} from "./serialize/reader";
function CryptoKeyBase() {
CryptoBase.call(this);
this.date = null;
this.isValid = false;
this.uid = null;
@ -54,6 +13,7 @@ function CryptoKeyBase() {
this.params = null;
this.type = c_oAscKeyStorageType.NoType;
}
initClass(CryptoBase, CryptoKeyBase);
CryptoKeyBase.import = function(reader, version, symmetricKey) {};
CryptoKeyBase.prototype.init = function() {
const crypto = getCrypto();
@ -154,21 +114,31 @@ WebKeyPair.fromCryptoBuffer = function(publicKeyBuffer, privateKeyBuffer, import
export function WebSignKeyPair() {
WebKeyPair.call(this);
this.type = c_oAscKeyStorageType.WebSignKeyPair;
}
initClass(WebSignKeyPair, WebKeyPair);
initClass(WebSignKeyPair, WebKeyPair, c_oAscKeyStorageType.WebSignKeyPair);
WebSignKeyPair.import = WebKeyPair.import;
WebSignKeyPair.fromCryptoBuffer = WebKeyPair.fromCryptoBuffer;
WebEncryptKeyPair.prototype.verify = function (data) {
return this.publicKey.verify(data);
};
WebEncryptKeyPair.prototype.sign = function (data) {
return this.privateKey.sign(data);
};
export function WebEncryptKeyPair() {
WebKeyPair.call(this);
this.privateKey = null;
this.publicKey = null;
this.type = c_oAscKeyStorageType.WebEncryptKeyPair;
}
initClass(WebEncryptKeyPair, WebKeyPair);
initClass(WebEncryptKeyPair, WebKeyPair, c_oAscKeyStorageType.WebEncryptKeyPair);
WebEncryptKeyPair.import = WebKeyPair.import;
WebEncryptKeyPair.fromCryptoBuffer = WebKeyPair.fromCryptoBuffer;
WebEncryptKeyPair.prototype.encrypt = function (data) {
return this.publicKey.encrypt(data);
};
WebEncryptKeyPair.prototype.decrypt = function (data) {
return this.privateKey.decrypt(data);
};
function AsymmetricKey() {
this.binaryKey = null;
@ -189,10 +159,9 @@ AsymmetricKey.prototype.changeMasterPassword = function(oldMasterPassword, newMa
export function WebPrivateKey() {
AsymmetricKey.call(this);
this.type = c_oAscKeyStorageType.WebPrivateKey;
this.salt = null;
}
initClass(WebPrivateKey, AsymmetricKey);
initClass(WebPrivateKey, AsymmetricKey, c_oAscKeyStorageType.WebPrivateKey);
WebPrivateKey.import = function(reader) {
const key = new WebPrivateKey();
@ -226,8 +195,15 @@ WebPrivateKey.prototype.setSalt = function(salt) {
};
WebPrivateKey.prototype.changeMasterPassword = function(oldMasterPassword, newMasterPassword) {
const oldPasswordKey = WebSymmetricKey.getFromPassword(oldMasterPassword, this.salt);
}
WebPrivateKey.prototype.decrypt = function(data) {
const crypto = getCrypto();
return crypto.decrypt(this, data);
};
WebPrivateKey.prototype.sign = function(data) {
const crypto = getCrypto();
return crypto.sign(this, data);
};
@ -236,7 +212,7 @@ export function WebPublicKey() {
this.binaryKey = null;
this.cryptoKey = null;
}
initClass(WebPublicKey, AsymmetricKey);
initClass(WebPublicKey, AsymmetricKey, c_oAscKeyStorageType.WebPublicKey);
WebPublicKey.import = function(reader) {
const key = new WebPublicKey();
@ -263,15 +239,22 @@ WebPublicKey.prototype.export = function(writer) {
}
}
};
WebPublicKey.prototype.encrypt = function(data) {
const crypto = getCrypto();
return crypto.encrypt(this, data);
};
WebPublicKey.prototype.verify = function(data) {
const crypto = getCrypto();
return crypto.verify(this, data);
};
export function WebSymmetricKey() {
CryptoKeyBase.call(this);
this.version = 1;
this.type = c_oAscKeyStorageType.WebSymmetricKey;
this.cryptoKey = null;
this.binaryKey = null;
}
initClass(WebSymmetricKey, AsymmetricKey);
initClass(WebSymmetricKey, AsymmetricKey, c_oAscKeyStorageType.WebSymmetricKey);
WebSymmetricKey.import = function(reader) {
const symmetricKey = new WebSymmetricKey();
@ -330,6 +313,61 @@ WebSymmetricKey.prototype.export = function(writer) {
}
}
};
WebSymmetricKey.prototype.encrypt = function(data) {
const crypto = getCrypto();
crypto.encrypt(this, data);
};
WebSymmetricKey.prototype.decrypt = function(data) {
const crypto = getCrypto();
crypto.decrypt(this, EncryptData.import(data));
};
export function EncryptData(encryptData, params) {
CryptoBase.call(this);
this.version = 1;
this.encryptData = encryptData || null;
this.params = params || null;
}
initClass(EncryptData, CryptoBase, c_oAscKeyStorageType.EncryptData);
EncryptData.import = function(binaryData) {
const data = new EncryptData();
const reader = new BinaryReader(binaryData);
data.setVersion(readLong(reader));
switch (this.version) {
case 1: {
data.setEncryptParams(readObject(reader));
data.setEncryptData(readBuffer(reader));
break;
}
default: {
return null;
}
}
return data;
};
EncryptData.prototype.export = function (writer) {
writeLong(writer, this.version);
switch (this.version) {
case 1: {
writeObject(writer, this.params);
writeBuffer(writer, this.encryptData);
break;
}
default: {
break;
}
}
};
EncryptData.prototype.setEncryptData = function (encryptData) {
this.encryptData = encryptData;
};
EncryptData.prototype.setEncryptParams = function (params) {
this.params = params;
};
EncryptData.prototype.getEncryptParams = function () {
return this.params;
};
EncryptData.prototype.getEncryptData = function () {
return this.encryptData;
};

View File

@ -1,11 +1,13 @@
import {c_oAscAlgorithmType} from "./defines";
import {c_oAscAlgorithmType, c_oAscKeyStorageType} from "./defines";
import {readLong, readBuffer} from "./serialize/reader";
import {writeLong} from "./serialize/writer";
import {writeBuffer, writeLong} from "./serialize/writer";
import {CryptoBase, initClass} from "./utils";
function AlgorithmParams(type) {
this.type = type;
function AlgorithmParams() {
CryptoBase.call(this);
this.version = 1;
}
initClass(AlgorithmParams, CryptoBase);
AlgorithmParams.import = function(reader) {
const params = new this();
params.setVersion(readLong(reader));
@ -35,12 +37,11 @@ AlgorithmParams.prototype.export = function(writer) {
}
};
function RsaImportParams(name, hash) {
AlgorithmParams.call(this, name);
function RsaImportParams(hash) {
AlgorithmParams.call(this);
this.hash = hash;
}
RsaImportParams.prototype = Object.create(AlgorithmParams.prototype);
RsaImportParams.prototype.constructor = RsaImportParams;
initClass(RsaImportParams, AlgorithmParams);
RsaImportParams.import = function(reader) {
const params = new RsaImportParams();
params.setVersion(readLong(reader));
@ -69,42 +70,47 @@ RsaImportParams.prototype.export = function(writer) {
};
RsaImportParams.prototype.setHash = function (hash) {
this.hash = hash;
};
export function RsaOAEPImportParams(hash) {
RsaImportParams.call(this, hash);
}
initClass(RsaOAEPImportParams, RsaImportParams, c_oAscKeyStorageType.RSAOAEPImportParams);
function RSAKeyGenParams(name, hash, modulusLength, publicExponent) {
RsaImportParams.call(this, name, hash);
function RSAKeyGenParams(hash, modulusLength, publicExponent) {
RsaImportParams.call(this, hash);
this.modulusLength = typeof modulusLength === "number" ? modulusLength : 2048;
this.publicExponent = publicExponent || new Uint8Array([0x01, 0x00, 0x01]);
}
RSAKeyGenParams.prototype = Object.create(RsaImportParams.prototype);
RSAKeyGenParams.prototype.constructor = RSAKeyGenParams;
initClass(RSAKeyGenParams, RsaImportParams);
RSAKeyGenParams.prototype.getImportParams = function() {
return new RsaImportParams(this.name, this.hash);
return new RsaImportParams(this.hash);
};
function Ed25519ImportParams() {
AlgorithmParams.call(this, c_oAscAlgorithmType.ED25519);
export function RsaOAEPKeyGenParams(hash, modulusLength, publicExponent) {
RSAKeyGenParams.call(this, hash, modulusLength, publicExponent);
}
Ed25519ImportParams.prototype = Object.create(AlgorithmParams.prototype);
Ed25519ImportParams.prototype.constructor = Ed25519ImportParams;
initClass(RsaOAEPImportParams, RsaImportParams, c_oAscKeyStorageType.RSAOAEPKeyGenParams);
export function Ed25519ImportParams() {
AlgorithmParams.call(this);
}
initClass(Ed25519ImportParams, AlgorithmParams, c_oAscKeyStorageType.Ed25519ImportParams);
Ed25519ImportParams.import = AlgorithmParams.import;
function Ed25519KeyGenParams() {
export function Ed25519KeyGenParams() {
Ed25519ImportParams.call(this);
}
Ed25519KeyGenParams.prototype = Object.create(AlgorithmParams.prototype);
Ed25519KeyGenParams.prototype.constructor = Ed25519KeyGenParams;
initClass(Ed25519KeyGenParams, Ed25519ImportParams, c_oAscKeyStorageType.Ed25519KeyGenParams);
Ed25519KeyGenParams.prototype.getImportParams = function() {
return new Ed25519ImportParams();
};
function AesGcmCryptoParams(iv, tagLength) {
AlgorithmParams.call(this, c_oAscAlgorithmType.AES_GCM);
export function AesGcmCryptoParams(iv, tagLength) {
AlgorithmParams.call(this);
this.iv = iv || null;
this.tagLength = typeof tagLength === "number" ? tagLength : null;
}
AesGcmCryptoParams.prototype = Object.create(AlgorithmParams.prototype);
AesGcmCryptoParams.prototype.constructor = AesGcmCryptoParams;
initClass(AesGcmCryptoParams, AlgorithmParams, c_oAscKeyStorageType.AesGCMCryptoParams);
AesGcmCryptoParams.import = function (reader) {
const params = new AesGcmCryptoParams();
params.setVersion(readLong(reader));
@ -145,21 +151,65 @@ AesGcmCryptoParams.prototype.init = function () {
};
function AesKeyGenParams(name, length) {
AlgorithmParams.call(this, name);
function AesKeyGenParams(length) {
AlgorithmParams.call(this);
this.length = length;
}
AesKeyGenParams.prototype = Object.create(AlgorithmParams.prototype);
AesKeyGenParams.prototype.constructor = AesKeyGenParams;
initClass(AesKeyGenParams, AlgorithmParams);
AesKeyGenParams.prototype.getImportParams = function() {
return new AlgorithmParams(this.name);
return new AlgorithmParams();
};
export function AesGcmGenParams() {
AesKeyGenParams.call(this, c_oAscAlgorithmType.AES_GCM, 256);
export function AesGcmKeyGenParams() {
AesKeyGenParams.call(this, 256);
}
AesGcmGenParams.prototype = Object.create(AlgorithmParams.prototype);
AesGcmGenParams.prototype.constructor = AesGcmGenParams;
initClass(AesGcmKeyGenParams, AlgorithmParams, c_oAscKeyStorageType.AesGCMKeyGenParams);
const PBKDFSaltLength = 16;
export function PBKDF2Params() {
AlgorithmParams.call(this);
this.iterations = 600000;
this.hash = c_oAscDigestType.SHA256;
this.salt = null;
}
initClass(PBKDF2Params, AlgorithmParams, );
PBKDF2Params.import = function(reader) {
const params = new PBKDF2Params();
params.setVerison(readLong(reader));
switch (params.version) {
case 1: {
this.setIterations(readLong(reader));
this.setHash(readLong(reader));
this.setSalt(readBuffer(reader));
break;
}
default: {
return null;
}
}
return params;
}
PBKDF2Params.prototype.export = function(writer) {
writeLong(this.version);
switch (this.version) {
case 1: {
writeLong(writer, this.iterations);
writeLong(writer, this.hash);
writeBuffer(writer, this.salt);
break;
}
default: {
break;
}
}
}
PBKDF2Params.prototype.getCryptoParams = function() {
return {
name: 'PBKDF2',
salt: this.salt,
iterations: this.iterations,
hash: getCryptoHash(this.hash)
};
};

View File

@ -118,16 +118,15 @@ export function readBuffer(reader) {
export function readObject(reader) {
const isWrite = reader.GetBool();
let object = null;
if (isWrite) {
const type = reader.GetUChar();
const type = reader.GetLong();
var nStart = reader.cur;
var nEnd = nStart + reader.GetLong() + 4;
if (c_oAscObjectFactory[type]) {
const object = new c_oAscObjectFactory[type];
object.import(reader);
return object;
object = c_oAscObjectFactory[type].import(reader);
}
reader.Seek2(nEnd);
}
return null;
return object;
}

View File

@ -106,7 +106,7 @@ BinaryWriter.prototype.WriteBuffer = function(data, _pos, count)
}
BinaryWriter.prototype.WriteItem = function(type, fWrite)
{
this.WriteByte(type);
this.WriteLong(type);
this.WriteItemWithLength(fWrite);
};
BinaryWriter.prototype.WriteItemWithLength = function(fWrite)
@ -160,7 +160,7 @@ export function writeBuffer(writer, buffer) {
}
export function writeObject(writer, object) {
let isWrite = false;
const type = object && object.getType();
const type = object && object.getObjectType();
if (c_oAscObjectFactory[type]) {
isWrite = true;
}

View File

@ -1,5 +1,14 @@
import {c_oAscKeyStorageType} from "./defines";
export function CryptoBase() {
this.objectType = c_oAscKeyStorageType.NoType;
}
CryptoBase.prototype.getObjectType = function () {
return this.objectType;
};
export function initClass(fClass, fBase, type) {
fClass.prototype = Object.create(fBase.prototype);
fClass.prototype.constructor = fClass;
fClass.prototype.objectType = type;
fClass.prototype.objectType = type || c_oAscKeyStorageType.NoType;
}