import type { CryptoByteCipherOptions, CryptoCodecKind, CryptoDesOptions, CryptoDigestKind, CryptoMacKind, CryptoRsaPair, CryptoRuntimeSnapshot, CryptoTextCipherOptions, CryptoTripleDesOptions, RsaOutputKind } from '../interface.uts' import { buildRuntimeSnapshot, createDefaultByteCipherOptions, createDefaultDesOptions, createDefaultTextCipherOptions, createDefaultTripleDesOptions, normalizeByteCipherOptions, normalizeDesOptions, normalizeTextCipherOptions, normalizeTripleDesOptions, resolveHexOutput } from '../shared/options.uts' import { NativeHarmonyRsaCore } from './native_rsa.ets' import { CryptoJS } from '@ohos/crypto-js' import type { CompatBridge, CompatCrypto } from '../shared/compat.uts' import { aesDecryptBytesCompat, aesDecryptCompat, aesEncryptBytesCompat, aesEncryptCompat, base64DecodeCompat, base64EncodeCompat, createCompatBridge, createCompatCrypto, desDecryptCompat, desEncryptCompat, generateRsaKeyPairCompat, hmacSha1Compat, hmacSha256Compat, hmacSha512Compat, md5Compat, rc4DecryptCompat, rc4EncryptCompat, rsaDecryptCompat, rsaEncryptCompat, sha1Compat, sha256Compat, sha512Compat } from '../shared/compat.uts' type WordValue = CryptoJS.lib.WordArray function bytesToWordArray(input : Uint8Array) : WordValue { const words = [] as number[] let index = 0 while (index < input.length) { let word = 0 let offset = 0 while (offset < 4 && index + offset < input.length) { word |= input[index + offset] << (24 - offset * 8) offset += 1 } words.push(word) index += 4 } return CryptoJS.lib.WordArray.create(words, input.length) } function wordArrayToBytes(input : WordValue) : Uint8Array { const output = new Uint8Array(input.sigBytes) let index = 0 while (index < input.sigBytes) { const wordIndex = Math.floor(index / 4) const offset = index % 4 output[index] = (input.words[wordIndex] >>> (24 - offset * 8)) & 0xff index += 1 } return output } function resolveAesMode(mode : string) : CryptoJS.BlockCipherMode { if (mode == 'CBC') { return CryptoJS.mode.CBC } if (mode == 'CFB') { return CryptoJS.mode.CFB } if (mode == 'CTR') { return CryptoJS.mode.CTR } if (mode == 'CTRGladman') { return CryptoJS.mode.CTRGladman } if (mode == 'ECB') { return CryptoJS.mode.ECB } return CryptoJS.mode.OFB } function resolvePadding(padding : string) : CryptoJS.Padding { if (padding == 'PKCS7') { return CryptoJS.pad.Pkcs7 } if (padding == 'ANSI_X923') { return CryptoJS.pad.AnsiX923 } if (padding == 'ISO_10126') { return CryptoJS.pad.Iso10126 } if (padding == 'ISO_97971') { return CryptoJS.pad.Iso97971 } if (padding == 'NONE') { return CryptoJS.pad.NoPadding } return CryptoJS.pad.ZeroPadding } function fitIvText(ivText : string | null, size : number) : WordValue { const bytes = new Uint8Array(size) if (ivText != null) { const parsed : WordValue = CryptoJS.enc.Utf8.parse(ivText) const source = wordArrayToBytes(parsed) let index = 0 while (index < size && index < source.length) { bytes[index] = source[index] index += 1 } } return bytesToWordArray(bytes) } function fitIvBytes(ivBytes : Uint8Array | null, size : number) : WordValue { const bytes = new Uint8Array(size) if (ivBytes != null) { let index = 0 while (index < size && index < ivBytes.length) { bytes[index] = ivBytes[index] index += 1 } } return bytesToWordArray(bytes) } function fitKeyWordFromText(secretText : string, keyLength : number | null) : WordValue { let target = 16 if (keyLength == 24 || keyLength == 32) { target = keyLength } const sourceWord : WordValue = CryptoJS.enc.Utf8.parse(secretText) const source = wordArrayToBytes(sourceWord) const output = new Uint8Array(target) let index = 0 while (index < target && index < source.length) { output[index] = source[index] index += 1 } return bytesToWordArray(output) } function fitKeyWordFromBytes(secretBytes : Uint8Array, keyLength : number | null) : WordValue { let target = 16 if (keyLength == 24 || keyLength == 32) { target = keyLength } const output = new Uint8Array(target) let index = 0 while (index < target && index < secretBytes.length) { output[index] = secretBytes[index] index += 1 } return bytesToWordArray(output) } export function defaultTextCipherOptions() : CryptoTextCipherOptions { return createDefaultTextCipherOptions() } export function defaultByteCipherOptions() : CryptoByteCipherOptions { return createDefaultByteCipherOptions() } export function defaultTripleDesOptions() : CryptoTripleDesOptions { return createDefaultTripleDesOptions() } export function defaultDesOptions() : CryptoDesOptions { return createDefaultDesOptions() } export function describeCryptoRuntime() : CryptoRuntimeSnapshot { return buildRuntimeSnapshot('Harmony', '@ohos/crypto-js', true, true) } export function encodeText(codec : CryptoCodecKind, plainText : string) : string { if (codec == 'utf8') { return plainText } const word : WordValue = CryptoJS.enc.Utf8.parse(plainText) if (codec == 'hex') { return CryptoJS.enc.Hex.stringify(word) } if (codec == 'base64') { return CryptoJS.enc.Base64.stringify(word) } if (codec == 'base64url') { return CryptoJS.enc.Base64url.stringify(word) } if (codec == 'latin1') { return CryptoJS.enc.Latin1.stringify(word) } return CryptoJS.enc.Utf16.stringify(word) } export function decodeText(codec : CryptoCodecKind, encodedText : string) : string { if (codec == 'utf8') { return encodedText } if (codec == 'hex') { return CryptoJS.enc.Hex.parse(encodedText).toString(CryptoJS.enc.Utf8) } if (codec == 'base64') { return CryptoJS.enc.Base64.parse(encodedText).toString(CryptoJS.enc.Utf8) } if (codec == 'base64url') { return CryptoJS.enc.Base64url.parse(encodedText).toString(CryptoJS.enc.Utf8) } if (codec == 'latin1') { return CryptoJS.enc.Latin1.parse(encodedText).toString(CryptoJS.enc.Utf8) } return CryptoJS.enc.Utf16.parse(encodedText).toString(CryptoJS.enc.Utf8) } export function digestText(kind : CryptoDigestKind, plainText : string) : string { if (kind == 'md5') { return CryptoJS.MD5(plainText).toString() } if (kind == 'sha1') { return CryptoJS.SHA1(plainText).toString() } if (kind == 'sha256') { return CryptoJS.SHA256(plainText).toString() } return CryptoJS.SHA512(plainText).toString() } export function signText(kind : CryptoMacKind, secretText : string, plainText : string) : string { if (kind == 'sha1') { return CryptoJS.HmacSHA1(plainText, secretText).toString() } if (kind == 'sha256') { return CryptoJS.HmacSHA256(plainText, secretText).toString() } return CryptoJS.HmacSHA512(plainText, secretText).toString() } export function aesEncryptText(secretText : string, plainText : string, options : CryptoTextCipherOptions | null) : string { const actual = normalizeTextCipherOptions(options) const encrypted : CryptoJS.lib.CipherParams = CryptoJS.AES.encrypt(plainText, fitKeyWordFromText(secretText, actual.keyLength), { iv: fitIvText(actual.ivText, 16), mode: resolveAesMode(actual.mode), padding: resolvePadding(actual.padding) }) return encrypted.toString() } export function aesDecryptText(secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) : string { const actual = normalizeTextCipherOptions(options) const decrypted : WordValue = CryptoJS.AES.decrypt(cipherText, fitKeyWordFromText(secretText, actual.keyLength), { iv: fitIvText(actual.ivText, 16), mode: resolveAesMode(actual.mode), padding: resolvePadding(actual.padding) }) return decrypted.toString(CryptoJS.enc.Utf8) } export function aesEncryptBytes(secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array { const actual = normalizeByteCipherOptions(options) const encrypted : CryptoJS.lib.CipherParams = CryptoJS.AES.encrypt(bytesToWordArray(plainBytes), fitKeyWordFromBytes(secretBytes, actual.keyLength), { iv: fitIvBytes(actual.ivBytes, 16), mode: resolveAesMode(actual.mode), padding: resolvePadding(actual.padding) }) return wordArrayToBytes(encrypted.ciphertext) } export function aesDecryptBytes(secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array { const actual = normalizeByteCipherOptions(options) const payload : UTSJSONObject = { ciphertext: bytesToWordArray(cipherBytes) } const decrypted : WordValue = CryptoJS.AES.decrypt(CryptoJS.lib.CipherParams.create(payload), fitKeyWordFromBytes(secretBytes, actual.keyLength), { iv: fitIvBytes(actual.ivBytes, 16), mode: resolveAesMode(actual.mode), padding: resolvePadding(actual.padding) }) return wordArrayToBytes(decrypted) } export function tripleDesEncryptText(secretText : string, plainText : string, options : CryptoTripleDesOptions | null) : string { const actual = normalizeTripleDesOptions(options) const encrypted : CryptoJS.lib.CipherParams = CryptoJS.TripleDES.encrypt(plainText, fitKeyWordFromText(secretText, 24), { iv: fitIvText(actual.ivText, 8), mode: actual.mode == 'ECB' ? CryptoJS.mode.ECB : CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }) return encrypted.toString() } export function tripleDesDecryptText(secretText : string, cipherText : string, options : CryptoTripleDesOptions | null) : string { const actual = normalizeTripleDesOptions(options) const decrypted : WordValue = CryptoJS.TripleDES.decrypt(cipherText, fitKeyWordFromText(secretText, 24), { iv: fitIvText(actual.ivText, 8), mode: actual.mode == 'ECB' ? CryptoJS.mode.ECB : CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }) return decrypted.toString(CryptoJS.enc.Utf8) } export function desEncryptText(secretText : string, plainText : string, options : CryptoDesOptions | null) : string { const actual = normalizeDesOptions(options) const encrypted : CryptoJS.lib.CipherParams = CryptoJS.DES.encrypt(plainText, fitKeyWordFromText(secretText, 8), { iv: fitIvText(actual.ivText, 8), mode: resolveAesMode(actual.mode), padding: resolvePadding(actual.padding) }) return encrypted.toString() } export function desDecryptText(secretText : string, cipherText : string, options : CryptoDesOptions | null) : string { const actual = normalizeDesOptions(options) const decrypted : WordValue = CryptoJS.DES.decrypt(cipherText, fitKeyWordFromText(secretText, 8), { iv: fitIvText(actual.ivText, 8), mode: resolveAesMode(actual.mode), padding: resolvePadding(actual.padding) }) return decrypted.toString(CryptoJS.enc.Utf8) } export function rc4EncryptText(secretText : string, plainText : string) : string { const encrypted : CryptoJS.lib.CipherParams = CryptoJS.RC4.encrypt(plainText, CryptoJS.enc.Utf8.parse(secretText)) return encrypted.ciphertext.toString(CryptoJS.enc.Hex) } export function rc4DecryptText(secretText : string, cipherHexText : string) : string { const payload : UTSJSONObject = { ciphertext: CryptoJS.enc.Hex.parse(cipherHexText) } const decrypted : WordValue = CryptoJS.RC4.decrypt(CryptoJS.lib.CipherParams.create(payload), CryptoJS.enc.Utf8.parse(secretText)) return decrypted.toString(CryptoJS.enc.Utf8) } export function createRsaPair(keySize : number) : CryptoRsaPair { const bundle = NativeHarmonyRsaCore.createRsaBundle(keySize) return { publicKey: bundle.publicKey, privateKey: bundle.privateKey } as CryptoRsaPair } export function rsaEncryptText(publicKey : string, plainText : string, outputKind : RsaOutputKind | null) : string { return NativeHarmonyRsaCore.rsaEncrypt(publicKey, plainText, resolveHexOutput(outputKind) ? 'hex' : 'base64') } export function rsaDecryptText(privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) : string { return NativeHarmonyRsaCore.rsaDecrypt(privateKey, cipherText, resolveHexOutput(outputKind) ? 'hex' : 'base64') } function compatBridge() : CompatBridge { return createCompatBridge( (codec : CryptoCodecKind, plainText : string) : string => encodeText(codec, plainText), (codec : CryptoCodecKind, encodedText : string) : string => decodeText(codec, encodedText), (kind : CryptoDigestKind, plainText : string) : string => digestText(kind, plainText), (kind : CryptoMacKind, secretText : string, plainText : string) : string => signText(kind, secretText, plainText), (secretText : string, plainText : string, options : CryptoTextCipherOptions | null) : string => aesEncryptText(secretText, plainText, options), (secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) : string => aesDecryptText(secretText, cipherText, options), (secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array => aesEncryptBytes(secretBytes, plainBytes, options), (secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array => aesDecryptBytes(secretBytes, cipherBytes, options), (secretText : string, plainText : string, options : CryptoDesOptions | null) : string => desEncryptText(secretText, plainText, options), (secretText : string, cipherText : string, options : CryptoDesOptions | null) : string => desDecryptText(secretText, cipherText, options), (keySize : number) : CryptoRsaPair => createRsaPair(keySize), (publicKey : string, plainText : string, outputKind : RsaOutputKind | null) : string => rsaEncryptText(publicKey, plainText, outputKind), (privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) : string => rsaDecryptText(privateKey, cipherText, outputKind), (secretText : string, plainText : string) : string => rc4EncryptText(secretText, plainText), (secretText : string, cipherHexText : string) : string => rc4DecryptText(secretText, cipherHexText) ) } export function base64Encode(input : string) : string { return base64EncodeCompat(compatBridge(), input) } export function base64Decode(input : string) : string { return base64DecodeCompat(compatBridge(), input) } export function md5(input : string) : string { return md5Compat(compatBridge(), input) } export function sha1(input : string) : string { return sha1Compat(compatBridge(), input) } export function sha256(input : string) : string { return sha256Compat(compatBridge(), input) } export function sha512(input : string) : string { return sha512Compat(compatBridge(), input) } export function hmacSha1(key : string, data : string) : string { return hmacSha1Compat(compatBridge(), key, data) } export function hmacSha256(key : string, data : string) : string { return hmacSha256Compat(compatBridge(), key, data) } export function hmacSha512(key : string, data : string) : string { return hmacSha512Compat(compatBridge(), key, data) } export function aesEncrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string { return aesEncryptCompat(compatBridge(), key, data, mode, iv, keySize) } export function aesDecrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string { return aesDecryptCompat(compatBridge(), key, data, mode, iv, keySize) } export function aesEncrypt2(key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array { return aesEncryptBytesCompat(compatBridge(), key, data, mode, iv, keySize) } export function aesDecrypt2(key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array { return aesDecryptBytesCompat(compatBridge(), key, data, mode, iv, keySize) } export function desEncrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string { return desEncryptCompat(compatBridge(), key, data, mode, iv) } export function desDecrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string { return desDecryptCompat(compatBridge(), key, data, mode, iv) } export function generateRSAKeyPair(keySize : number = 2048) : CryptoRsaPair { return generateRsaKeyPairCompat(compatBridge(), keySize) } export function rsaEncrypt(publicKey : string, data : string) : string { return rsaEncryptCompat(compatBridge(), publicKey, data) } export function rsaDecrypt(privateKey : string, data : string) : string { return rsaDecryptCompat(compatBridge(), privateKey, data) } export function rc4Encrypt(key : string, data : string) : string { return rc4EncryptCompat(compatBridge(), key, data) } export function rc4Decrypt(key : string, data : string) : string { return rc4DecryptCompat(compatBridge(), key, data) } export function useCrypto() : CompatCrypto { return createCompatCrypto(compatBridge()) }