feat(wasm): added sha1/256 to wasm, removed most of async in crypto
This commit is contained in:
parent
70f4e40ef5
commit
18178b438d
41 changed files with 813 additions and 472 deletions
|
@ -32,19 +32,19 @@ export class AuthKey {
|
||||||
return this.ready && buffersEqual(keyId, this.id)
|
return this.ready && buffersEqual(keyId, this.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
async setup(authKey?: Uint8Array | null): Promise<void> {
|
setup(authKey?: Uint8Array | null): void {
|
||||||
if (!authKey) return this.reset()
|
if (!authKey) return this.reset()
|
||||||
|
|
||||||
this.ready = true
|
this.ready = true
|
||||||
this.key = authKey
|
this.key = authKey
|
||||||
this.clientSalt = authKey.subarray(88, 120)
|
this.clientSalt = authKey.subarray(88, 120)
|
||||||
this.serverSalt = authKey.subarray(96, 128)
|
this.serverSalt = authKey.subarray(96, 128)
|
||||||
this.id = (await this._crypto.sha1(authKey)).subarray(-8)
|
this.id = this._crypto.sha1(authKey).subarray(-8)
|
||||||
|
|
||||||
this.log.verbose('auth key set up, id = %h', this.id)
|
this.log.verbose('auth key set up, id = %h', this.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
async encryptMessage(message: Uint8Array, serverSalt: Long, sessionId: Long): Promise<Uint8Array> {
|
encryptMessage(message: Uint8Array, serverSalt: Long, sessionId: Long): Uint8Array {
|
||||||
if (!this.ready) throw new MtcuteError('Keys are not set up!')
|
if (!this.ready) throw new MtcuteError('Keys are not set up!')
|
||||||
|
|
||||||
let padding = (16 /* header size */ + message.length + 12) /* min padding */ % 16
|
let padding = (16 /* header size */ + message.length + 12) /* min padding */ % 16
|
||||||
|
@ -60,18 +60,18 @@ export class AuthKey {
|
||||||
buf.set(message, 16)
|
buf.set(message, 16)
|
||||||
buf.set(randomBytes(padding), 16 + message.length)
|
buf.set(randomBytes(padding), 16 + message.length)
|
||||||
|
|
||||||
const messageKey = (await this._crypto.sha256(concatBuffers([this.clientSalt, buf]))).subarray(8, 24)
|
const messageKey = this._crypto.sha256(concatBuffers([this.clientSalt, buf])).subarray(8, 24)
|
||||||
const ige = await createAesIgeForMessage(this._crypto, this.key, messageKey, true)
|
const ige = createAesIgeForMessage(this._crypto, this.key, messageKey, true)
|
||||||
const encryptedData = ige.encrypt(buf)
|
const encryptedData = ige.encrypt(buf)
|
||||||
|
|
||||||
return concatBuffers([this.id, messageKey, encryptedData])
|
return concatBuffers([this.id, messageKey, encryptedData])
|
||||||
}
|
}
|
||||||
|
|
||||||
async decryptMessage(
|
decryptMessage(
|
||||||
data: Uint8Array,
|
data: Uint8Array,
|
||||||
sessionId: Long,
|
sessionId: Long,
|
||||||
callback: (msgId: tl.Long, seqNo: number, data: TlBinaryReader) => void,
|
callback: (msgId: tl.Long, seqNo: number, data: TlBinaryReader) => void,
|
||||||
): Promise<void> {
|
): void {
|
||||||
const messageKey = data.subarray(8, 24)
|
const messageKey = data.subarray(8, 24)
|
||||||
let encryptedData = data.subarray(24)
|
let encryptedData = data.subarray(24)
|
||||||
|
|
||||||
|
@ -84,10 +84,10 @@ export class AuthKey {
|
||||||
encryptedData = encryptedData.subarray(0, encryptedData.byteLength - mod16)
|
encryptedData = encryptedData.subarray(0, encryptedData.byteLength - mod16)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ige = await createAesIgeForMessage(this._crypto, this.key, messageKey, false)
|
const ige = createAesIgeForMessage(this._crypto, this.key, messageKey, false)
|
||||||
const innerData = ige.decrypt(encryptedData)
|
const innerData = ige.decrypt(encryptedData)
|
||||||
|
|
||||||
const msgKeySource = await this._crypto.sha256(concatBuffers([this.serverSalt, innerData]))
|
const msgKeySource = this._crypto.sha256(concatBuffers([this.serverSalt, innerData]))
|
||||||
const expectedMessageKey = msgKeySource.subarray(8, 24)
|
const expectedMessageKey = msgKeySource.subarray(8, 24)
|
||||||
|
|
||||||
if (!buffersEqual(messageKey, expectedMessageKey)) {
|
if (!buffersEqual(messageKey, expectedMessageKey)) {
|
||||||
|
|
|
@ -120,7 +120,7 @@ function checkDhPrime(log: Logger, dhPrime: bigint, g: number) {
|
||||||
log.debug('g = %d is safe to use with dh_prime', g)
|
log.debug('g = %d is safe to use with dh_prime', g)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function rsaPad(data: Uint8Array, crypto: ICryptoProvider, key: TlPublicKey): Promise<Uint8Array> {
|
function rsaPad(data: Uint8Array, crypto: ICryptoProvider, key: TlPublicKey): Uint8Array {
|
||||||
// since Summer 2021, they use "version of RSA with a variant of OAEP+ padding explained below"
|
// since Summer 2021, they use "version of RSA with a variant of OAEP+ padding explained below"
|
||||||
|
|
||||||
const keyModulus = BigInt(`0x${key.modulus}`)
|
const keyModulus = BigInt(`0x${key.modulus}`)
|
||||||
|
@ -137,13 +137,13 @@ async function rsaPad(data: Uint8Array, crypto: ICryptoProvider, key: TlPublicKe
|
||||||
|
|
||||||
const aesKey = randomBytes(32)
|
const aesKey = randomBytes(32)
|
||||||
|
|
||||||
const dataWithHash = concatBuffers([data, await crypto.sha256(concatBuffers([aesKey, data]))])
|
const dataWithHash = concatBuffers([data, crypto.sha256(concatBuffers([aesKey, data]))])
|
||||||
// we only need to reverse the data
|
// we only need to reverse the data
|
||||||
dataWithHash.subarray(0, 192).reverse()
|
dataWithHash.subarray(0, 192).reverse()
|
||||||
|
|
||||||
const aes = crypto.createAesIge(aesKey, aesIv)
|
const aes = crypto.createAesIge(aesKey, aesIv)
|
||||||
const encrypted = aes.encrypt(dataWithHash)
|
const encrypted = aes.encrypt(dataWithHash)
|
||||||
const encryptedHash = await crypto.sha256(encrypted)
|
const encryptedHash = crypto.sha256(encrypted)
|
||||||
|
|
||||||
xorBufferInPlace(aesKey, encryptedHash)
|
xorBufferInPlace(aesKey, encryptedHash)
|
||||||
const decryptedData = concatBuffers([aesKey, encrypted])
|
const decryptedData = concatBuffers([aesKey, encrypted])
|
||||||
|
@ -160,9 +160,9 @@ async function rsaPad(data: Uint8Array, crypto: ICryptoProvider, key: TlPublicKe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function rsaEncrypt(data: Uint8Array, crypto: ICryptoProvider, key: TlPublicKey): Promise<Uint8Array> {
|
function rsaEncrypt(data: Uint8Array, crypto: ICryptoProvider, key: TlPublicKey): Uint8Array {
|
||||||
const toEncrypt = concatBuffers([
|
const toEncrypt = concatBuffers([
|
||||||
await crypto.sha1(data),
|
crypto.sha1(data),
|
||||||
data,
|
data,
|
||||||
// sha1 is always 20 bytes, so we're left with 255 - 20 - x padding
|
// sha1 is always 20 bytes, so we're left with 255 - 20 - x padding
|
||||||
randomBytes(235 - data.length),
|
randomBytes(235 - data.length),
|
||||||
|
@ -271,8 +271,8 @@ export async function doAuthorization(
|
||||||
const pqInnerData = TlBinaryWriter.serializeObject(writerMap, _pqInnerData)
|
const pqInnerData = TlBinaryWriter.serializeObject(writerMap, _pqInnerData)
|
||||||
|
|
||||||
const encryptedData = publicKey.old ?
|
const encryptedData = publicKey.old ?
|
||||||
await rsaEncrypt(pqInnerData, crypto, publicKey) :
|
rsaEncrypt(pqInnerData, crypto, publicKey) :
|
||||||
await rsaPad(pqInnerData, crypto, publicKey)
|
rsaPad(pqInnerData, crypto, publicKey)
|
||||||
|
|
||||||
log.debug('requesting DH params')
|
log.debug('requesting DH params')
|
||||||
|
|
||||||
|
@ -303,7 +303,7 @@ export async function doAuthorization(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: complete DH exchange
|
// Step 3: complete DH exchange
|
||||||
const [key, iv] = await generateKeyAndIvFromNonce(crypto, resPq.serverNonce, newNonce)
|
const [key, iv] = generateKeyAndIvFromNonce(crypto, resPq.serverNonce, newNonce)
|
||||||
const ige = crypto.createAesIge(key, iv)
|
const ige = crypto.createAesIge(key, iv)
|
||||||
|
|
||||||
const plainTextAnswer = ige.decrypt(serverDhParams.encryptedAnswer)
|
const plainTextAnswer = ige.decrypt(serverDhParams.encryptedAnswer)
|
||||||
|
@ -311,7 +311,7 @@ export async function doAuthorization(
|
||||||
const serverDhInnerReader = new TlBinaryReader(readerMap, plainTextAnswer, 20)
|
const serverDhInnerReader = new TlBinaryReader(readerMap, plainTextAnswer, 20)
|
||||||
const serverDhInner = serverDhInnerReader.object() as mtp.TlObject
|
const serverDhInner = serverDhInnerReader.object() as mtp.TlObject
|
||||||
|
|
||||||
if (!buffersEqual(innerDataHash, await crypto.sha1(plainTextAnswer.subarray(20, serverDhInnerReader.pos)))) {
|
if (!buffersEqual(innerDataHash, crypto.sha1(plainTextAnswer.subarray(20, serverDhInnerReader.pos)))) {
|
||||||
throw new MtSecurityError('Step 3: invalid inner data hash')
|
throw new MtSecurityError('Step 3: invalid inner data hash')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,7 +340,7 @@ export async function doAuthorization(
|
||||||
const gB = bigIntModPow(g, b, dhPrime)
|
const gB = bigIntModPow(g, b, dhPrime)
|
||||||
|
|
||||||
const authKey = bigIntToBuffer(bigIntModPow(gA, b, dhPrime))
|
const authKey = bigIntToBuffer(bigIntModPow(gA, b, dhPrime))
|
||||||
const authKeyAuxHash = (await crypto.sha1(authKey)).subarray(0, 8)
|
const authKeyAuxHash = crypto.sha1(authKey).subarray(0, 8)
|
||||||
|
|
||||||
// validate DH params
|
// validate DH params
|
||||||
if (g <= 1 || g >= dhPrime - 1n) {
|
if (g <= 1 || g >= dhPrime - 1n) {
|
||||||
|
@ -377,7 +377,7 @@ export async function doAuthorization(
|
||||||
const clientDhInnerWriter = TlBinaryWriter.alloc(writerMap, innerLength)
|
const clientDhInnerWriter = TlBinaryWriter.alloc(writerMap, innerLength)
|
||||||
clientDhInnerWriter.pos = 20
|
clientDhInnerWriter.pos = 20
|
||||||
clientDhInnerWriter.object(clientDhInner)
|
clientDhInnerWriter.object(clientDhInner)
|
||||||
const clientDhInnerHash = await crypto.sha1(clientDhInnerWriter.uint8View.subarray(20, clientDhInnerWriter.pos))
|
const clientDhInnerHash = crypto.sha1(clientDhInnerWriter.uint8View.subarray(20, clientDhInnerWriter.pos))
|
||||||
clientDhInnerWriter.pos = 0
|
clientDhInnerWriter.pos = 0
|
||||||
clientDhInnerWriter.raw(clientDhInnerHash)
|
clientDhInnerWriter.raw(clientDhInnerHash)
|
||||||
|
|
||||||
|
@ -412,7 +412,7 @@ export async function doAuthorization(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dhGen._ === 'mt_dh_gen_retry') {
|
if (dhGen._ === 'mt_dh_gen_retry') {
|
||||||
const expectedHash = await crypto.sha1(concatBuffers([newNonce, new Uint8Array([2]), authKeyAuxHash]))
|
const expectedHash = crypto.sha1(concatBuffers([newNonce, new Uint8Array([2]), authKeyAuxHash]))
|
||||||
|
|
||||||
if (!buffersEqual(expectedHash.subarray(4, 20), dhGen.newNonceHash2)) {
|
if (!buffersEqual(expectedHash.subarray(4, 20), dhGen.newNonceHash2)) {
|
||||||
throw Error('Step 4: invalid retry nonce hash from server')
|
throw Error('Step 4: invalid retry nonce hash from server')
|
||||||
|
@ -423,7 +423,7 @@ export async function doAuthorization(
|
||||||
|
|
||||||
if (dhGen._ !== 'mt_dh_gen_ok') throw new Error() // unreachable
|
if (dhGen._ !== 'mt_dh_gen_ok') throw new Error() // unreachable
|
||||||
|
|
||||||
const expectedHash = await crypto.sha1(concatBuffers([newNonce, new Uint8Array([1]), authKeyAuxHash]))
|
const expectedHash = crypto.sha1(concatBuffers([newNonce, new Uint8Array([1]), authKeyAuxHash]))
|
||||||
|
|
||||||
if (!buffersEqual(expectedHash.subarray(4, 20), dhGen.newNonceHash1)) {
|
if (!buffersEqual(expectedHash.subarray(4, 20), dhGen.newNonceHash1)) {
|
||||||
throw Error('Step 4: invalid nonce hash from server')
|
throw Error('Step 4: invalid nonce hash from server')
|
||||||
|
|
|
@ -243,14 +243,14 @@ export class MtprotoSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Encrypt a single MTProto message using session's keys */
|
/** Encrypt a single MTProto message using session's keys */
|
||||||
async encryptMessage(message: Uint8Array): Promise<Uint8Array> {
|
encryptMessage(message: Uint8Array): Uint8Array {
|
||||||
const key = this._authKeyTemp.ready ? this._authKeyTemp : this._authKey
|
const key = this._authKeyTemp.ready ? this._authKeyTemp : this._authKey
|
||||||
|
|
||||||
return key.encryptMessage(message, this.serverSalt, this._sessionId)
|
return key.encryptMessage(message, this.serverSalt, this._sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Decrypt a single MTProto message using session's keys */
|
/** Decrypt a single MTProto message using session's keys */
|
||||||
async decryptMessage(data: Uint8Array, callback: Parameters<AuthKey['decryptMessage']>[2]): Promise<void> {
|
decryptMessage(data: Uint8Array, callback: Parameters<AuthKey['decryptMessage']>[2]): void {
|
||||||
if (!this._authKey.ready) throw new MtcuteError('Keys are not set up!')
|
if (!this._authKey.ready) throw new MtcuteError('Keys are not set up!')
|
||||||
|
|
||||||
const authKeyId = data.subarray(0, 8)
|
const authKeyId = data.subarray(0, 8)
|
||||||
|
|
|
@ -237,10 +237,10 @@ export class MultiSessionConnection extends EventEmitter {
|
||||||
this.connect()
|
this.connect()
|
||||||
}
|
}
|
||||||
|
|
||||||
async setAuthKey(authKey: Uint8Array | null, temp = false, idx = 0): Promise<void> {
|
setAuthKey(authKey: Uint8Array | null, temp = false, idx = 0): void {
|
||||||
const session = this._sessions[idx]
|
const session = this._sessions[idx]
|
||||||
const key = temp ? session._authKeyTemp : session._authKey
|
const key = temp ? session._authKeyTemp : session._authKey
|
||||||
await key.setup(authKey)
|
key.setup(authKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
resetAuthKeys(): void {
|
resetAuthKeys(): void {
|
||||||
|
|
|
@ -330,12 +330,10 @@ export class DcConnectionManager {
|
||||||
async loadKeys(): Promise<boolean> {
|
async loadKeys(): Promise<boolean> {
|
||||||
const permanent = await this.manager._storage.getAuthKeyFor(this.dcId)
|
const permanent = await this.manager._storage.getAuthKeyFor(this.dcId)
|
||||||
|
|
||||||
await Promise.all([
|
this.main.setAuthKey(permanent)
|
||||||
this.main.setAuthKey(permanent),
|
this.upload.setAuthKey(permanent)
|
||||||
this.upload.setAuthKey(permanent),
|
this.download.setAuthKey(permanent)
|
||||||
this.download.setAuthKey(permanent),
|
this.downloadSmall.setAuthKey(permanent)
|
||||||
this.downloadSmall.setAuthKey(permanent),
|
|
||||||
])
|
|
||||||
|
|
||||||
if (!permanent) {
|
if (!permanent) {
|
||||||
return false
|
return false
|
||||||
|
@ -345,14 +343,12 @@ export class DcConnectionManager {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
this.main._sessions.map(async (_, i) => {
|
this.main._sessions.map(async (_, i) => {
|
||||||
const temp = await this.manager._storage.getAuthKeyFor(this.dcId, i)
|
const temp = await this.manager._storage.getAuthKeyFor(this.dcId, i)
|
||||||
await this.main.setAuthKey(temp, true, i)
|
this.main.setAuthKey(temp, true, i)
|
||||||
|
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
await Promise.all([
|
this.upload.setAuthKey(temp, true)
|
||||||
this.upload.setAuthKey(temp, true),
|
this.download.setAuthKey(temp, true)
|
||||||
this.download.setAuthKey(temp, true),
|
this.downloadSmall.setAuthKey(temp, true)
|
||||||
this.downloadSmall.setAuthKey(temp, true),
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
@ -267,8 +267,8 @@ export class SessionConnection extends PersistentConnection {
|
||||||
this.emit('auth-begin')
|
this.emit('auth-begin')
|
||||||
|
|
||||||
doAuthorization(this, this._crypto)
|
doAuthorization(this, this._crypto)
|
||||||
.then(async ([authKey, serverSalt, timeOffset]) => {
|
.then(([authKey, serverSalt, timeOffset]) => {
|
||||||
await this._session._authKey.setup(authKey)
|
this._session._authKey.setup(authKey)
|
||||||
this._session.serverSalt = serverSalt
|
this._session.serverSalt = serverSalt
|
||||||
this._session._timeOffset = timeOffset
|
this._session._timeOffset = timeOffset
|
||||||
|
|
||||||
|
@ -322,7 +322,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
const tempKey = this._session._authKeyTempSecondary
|
const tempKey = this._session._authKeyTempSecondary
|
||||||
await tempKey.setup(tempAuthKey)
|
tempKey.setup(tempAuthKey)
|
||||||
|
|
||||||
const msgId = this._session.getMessageId()
|
const msgId = this._session.getMessageId()
|
||||||
|
|
||||||
|
@ -358,10 +358,10 @@ export class SessionConnection extends PersistentConnection {
|
||||||
writer.raw(randomBytes(8))
|
writer.raw(randomBytes(8))
|
||||||
const msgWithPadding = writer.result()
|
const msgWithPadding = writer.result()
|
||||||
|
|
||||||
const hash = await this._crypto.sha1(msgWithoutPadding)
|
const hash = this._crypto.sha1(msgWithoutPadding)
|
||||||
const msgKey = hash.subarray(4, 20)
|
const msgKey = hash.subarray(4, 20)
|
||||||
|
|
||||||
const ige = await createAesIgeForMessageOld(this._crypto, this._session._authKey.key, msgKey, true)
|
const ige = createAesIgeForMessageOld(this._crypto, this._session._authKey.key, msgKey, true)
|
||||||
const encryptedData = ige.encrypt(msgWithPadding)
|
const encryptedData = ige.encrypt(msgWithPadding)
|
||||||
const encryptedMessage = concatBuffers([this._session._authKey.id, msgKey, encryptedData])
|
const encryptedMessage = concatBuffers([this._session._authKey.id, msgKey, encryptedData])
|
||||||
|
|
||||||
|
@ -399,7 +399,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
reqWriter.object(request)
|
reqWriter.object(request)
|
||||||
|
|
||||||
// we can now send it as is
|
// we can now send it as is
|
||||||
const requestEncrypted = await tempKey.encryptMessage(
|
const requestEncrypted = tempKey.encryptMessage(
|
||||||
reqWriter.result(),
|
reqWriter.result(),
|
||||||
tempServerSalt,
|
tempServerSalt,
|
||||||
this._session._sessionId,
|
this._session._sessionId,
|
||||||
|
@ -476,7 +476,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
return promise
|
return promise
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async onMessage(data: Uint8Array): Promise<void> {
|
protected onMessage(data: Uint8Array): void {
|
||||||
if (this._pendingWaitForUnencrypted.length) {
|
if (this._pendingWaitForUnencrypted.length) {
|
||||||
const int32 = new Int32Array(data.buffer, data.byteOffset, 2)
|
const int32 = new Int32Array(data.buffer, data.byteOffset, 2)
|
||||||
|
|
||||||
|
@ -501,7 +501,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this._session.decryptMessage(data, this._handleRawMessage)
|
this._session.decryptMessage(data, this._handleRawMessage)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.log.error('failed to decrypt message: %s\ndata: %h', err, data)
|
this.log.error('failed to decrypt message: %s\ndata: %h', err, data)
|
||||||
}
|
}
|
||||||
|
@ -1793,17 +1793,15 @@ export class SessionConnection extends PersistentConnection {
|
||||||
rootMsgId,
|
rootMsgId,
|
||||||
)
|
)
|
||||||
|
|
||||||
this._session
|
const enc = this._session.encryptMessage(result)
|
||||||
.encryptMessage(result)
|
this.send(enc).catch((err: Error) => {
|
||||||
.then((enc) => this.send(enc))
|
this.log.error('error while sending pending messages (root msg_id = %l): %s', rootMsgId, err.stack)
|
||||||
.catch((err: Error) => {
|
|
||||||
this.log.error('error while sending pending messages (root msg_id = %l): %s', rootMsgId, err.stack)
|
|
||||||
|
|
||||||
// put acks in the front so they are the first to be sent
|
// put acks in the front so they are the first to be sent
|
||||||
if (ackMsgIds) {
|
if (ackMsgIds) {
|
||||||
this._session.queuedAcks.splice(0, 0, ...ackMsgIds)
|
this._session.queuedAcks.splice(0, 0, ...ackMsgIds)
|
||||||
}
|
}
|
||||||
this._onMessageFailed(rootMsgId!, 'unknown error')
|
this._onMessageFailed(rootMsgId!, 'unknown error')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,8 +73,8 @@ export class ObfuscatedPacketCodec extends WrappedCodec implements IPacketCodec
|
||||||
const decryptIv = randomRev.subarray(32, 48)
|
const decryptIv = randomRev.subarray(32, 48)
|
||||||
|
|
||||||
if (this._proxy) {
|
if (this._proxy) {
|
||||||
encryptKey = await this._crypto.sha256(concatBuffers([encryptKey, this._proxy.secret]))
|
encryptKey = this._crypto.sha256(concatBuffers([encryptKey, this._proxy.secret]))
|
||||||
decryptKey = await this._crypto.sha256(concatBuffers([decryptKey, this._proxy.secret]))
|
decryptKey = this._crypto.sha256(concatBuffers([decryptKey, this._proxy.secret]))
|
||||||
}
|
}
|
||||||
|
|
||||||
this._encryptor = this._crypto.createAesCtr(encryptKey, encryptIv, true)
|
this._encryptor = this._crypto.createAesCtr(encryptKey, encryptIv, true)
|
||||||
|
|
|
@ -14,9 +14,9 @@ export interface IAesCtr {
|
||||||
export interface ICryptoProvider {
|
export interface ICryptoProvider {
|
||||||
initialize?(): MaybeAsync<void>
|
initialize?(): MaybeAsync<void>
|
||||||
|
|
||||||
sha1(data: Uint8Array): MaybeAsync<Uint8Array>
|
sha1(data: Uint8Array): Uint8Array
|
||||||
|
|
||||||
sha256(data: Uint8Array): MaybeAsync<Uint8Array>
|
sha256(data: Uint8Array): Uint8Array
|
||||||
|
|
||||||
pbkdf2(
|
pbkdf2(
|
||||||
password: Uint8Array,
|
password: Uint8Array,
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { ICryptoProvider } from './abstract.js'
|
||||||
* @param key PEM-encoded RSA public key
|
* @param key PEM-encoded RSA public key
|
||||||
* @param old Whether this is an "old" key
|
* @param old Whether this is an "old" key
|
||||||
*/
|
*/
|
||||||
export async function parsePublicKey(crypto: ICryptoProvider, key: string, old = false): Promise<TlPublicKey> {
|
export function parsePublicKey(crypto: ICryptoProvider, key: string, old = false): TlPublicKey {
|
||||||
const asn1 = parseAsn1(parsePemContents(key))
|
const asn1 = parseAsn1(parsePemContents(key))
|
||||||
const modulus = asn1.children?.[0].value
|
const modulus = asn1.children?.[0].value
|
||||||
const exponent = asn1.children?.[1].value
|
const exponent = asn1.children?.[1].value
|
||||||
|
@ -26,7 +26,7 @@ export async function parsePublicKey(crypto: ICryptoProvider, key: string, old =
|
||||||
writer.bytes(exponent)
|
writer.bytes(exponent)
|
||||||
|
|
||||||
const data = writer.result()
|
const data = writer.result()
|
||||||
const sha = await crypto.sha1(data)
|
const sha = crypto.sha1(data)
|
||||||
const fp = hexEncode(sha.slice(-8).reverse())
|
const fp = hexEncode(sha.slice(-8).reverse())
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -44,8 +44,8 @@ export async function parsePublicKey(crypto: ICryptoProvider, key: string, old =
|
||||||
* @param key PEM-encoded RSA public key
|
* @param key PEM-encoded RSA public key
|
||||||
* @param old Whether this is an "old" key
|
* @param old Whether this is an "old" key
|
||||||
*/
|
*/
|
||||||
export async function addPublicKey(crypto: ICryptoProvider, key: string, old = false): Promise<void> {
|
export function addPublicKey(crypto: ICryptoProvider, key: string, old = false): void {
|
||||||
const parsed = await parsePublicKey(crypto, key, old)
|
const parsed = parsePublicKey(crypto, key, old)
|
||||||
keysIndex[parsed.fingerprint] = parsed
|
keysIndex[parsed.fingerprint] = parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,14 @@ import { ICryptoProvider, IEncryptionScheme } from './abstract.js'
|
||||||
* @param newNonce New nonce
|
* @param newNonce New nonce
|
||||||
* @returns Tuple: `[key, iv]`
|
* @returns Tuple: `[key, iv]`
|
||||||
*/
|
*/
|
||||||
export async function generateKeyAndIvFromNonce(
|
export function generateKeyAndIvFromNonce(
|
||||||
crypto: ICryptoProvider,
|
crypto: ICryptoProvider,
|
||||||
serverNonce: Uint8Array,
|
serverNonce: Uint8Array,
|
||||||
newNonce: Uint8Array,
|
newNonce: Uint8Array,
|
||||||
): Promise<[Uint8Array, Uint8Array]> {
|
): [Uint8Array, Uint8Array] {
|
||||||
const hash1 = await crypto.sha1(concatBuffers([newNonce, serverNonce]))
|
const hash1 = crypto.sha1(concatBuffers([newNonce, serverNonce]))
|
||||||
const hash2 = await crypto.sha1(concatBuffers([serverNonce, newNonce]))
|
const hash2 = crypto.sha1(concatBuffers([serverNonce, newNonce]))
|
||||||
const hash3 = await crypto.sha1(concatBuffers([newNonce, newNonce]))
|
const hash3 = crypto.sha1(concatBuffers([newNonce, newNonce]))
|
||||||
|
|
||||||
const key = concatBuffers([hash1, hash2.subarray(0, 12)])
|
const key = concatBuffers([hash1, hash2.subarray(0, 12)])
|
||||||
const iv = concatBuffers([hash2.subarray(12, 20), hash3, newNonce.subarray(0, 4)])
|
const iv = concatBuffers([hash2.subarray(12, 20), hash3, newNonce.subarray(0, 4)])
|
||||||
|
@ -34,15 +34,15 @@ export async function generateKeyAndIvFromNonce(
|
||||||
* @param messageKey Message key
|
* @param messageKey Message key
|
||||||
* @param client Whether this is a client to server message
|
* @param client Whether this is a client to server message
|
||||||
*/
|
*/
|
||||||
export async function createAesIgeForMessage(
|
export function createAesIgeForMessage(
|
||||||
crypto: ICryptoProvider,
|
crypto: ICryptoProvider,
|
||||||
authKey: Uint8Array,
|
authKey: Uint8Array,
|
||||||
messageKey: Uint8Array,
|
messageKey: Uint8Array,
|
||||||
client: boolean,
|
client: boolean,
|
||||||
): Promise<IEncryptionScheme> {
|
): IEncryptionScheme {
|
||||||
const x = client ? 0 : 8
|
const x = client ? 0 : 8
|
||||||
const sha256a = await crypto.sha256(concatBuffers([messageKey, authKey.subarray(x, 36 + x)]))
|
const sha256a = crypto.sha256(concatBuffers([messageKey, authKey.subarray(x, 36 + x)]))
|
||||||
const sha256b = await crypto.sha256(concatBuffers([authKey.subarray(40 + x, 76 + x), messageKey]))
|
const sha256b = crypto.sha256(concatBuffers([authKey.subarray(40 + x, 76 + x), messageKey]))
|
||||||
|
|
||||||
const key = concatBuffers([sha256a.subarray(0, 8), sha256b.subarray(8, 24), sha256a.subarray(24, 32)])
|
const key = concatBuffers([sha256a.subarray(0, 8), sha256b.subarray(8, 24), sha256a.subarray(24, 32)])
|
||||||
const iv = concatBuffers([sha256b.subarray(0, 8), sha256a.subarray(8, 24), sha256b.subarray(24, 32)])
|
const iv = concatBuffers([sha256b.subarray(0, 8), sha256a.subarray(8, 24), sha256b.subarray(24, 32)])
|
||||||
|
@ -59,19 +59,19 @@ export async function createAesIgeForMessage(
|
||||||
* @param messageKey Message key
|
* @param messageKey Message key
|
||||||
* @param client Whether this is a client to server message
|
* @param client Whether this is a client to server message
|
||||||
*/
|
*/
|
||||||
export async function createAesIgeForMessageOld(
|
export function createAesIgeForMessageOld(
|
||||||
crypto: ICryptoProvider,
|
crypto: ICryptoProvider,
|
||||||
authKey: Uint8Array,
|
authKey: Uint8Array,
|
||||||
messageKey: Uint8Array,
|
messageKey: Uint8Array,
|
||||||
client: boolean,
|
client: boolean,
|
||||||
): Promise<IEncryptionScheme> {
|
): IEncryptionScheme {
|
||||||
const x = client ? 0 : 8
|
const x = client ? 0 : 8
|
||||||
const sha1a = await crypto.sha1(concatBuffers([messageKey, authKey.subarray(x, 32 + x)]))
|
const sha1a = crypto.sha1(concatBuffers([messageKey, authKey.subarray(x, 32 + x)]))
|
||||||
const sha1b = await crypto.sha1(
|
const sha1b = crypto.sha1(
|
||||||
concatBuffers([authKey.subarray(32 + x, 48 + x), messageKey, authKey.subarray(48 + x, 64 + x)]),
|
concatBuffers([authKey.subarray(32 + x, 48 + x), messageKey, authKey.subarray(48 + x, 64 + x)]),
|
||||||
)
|
)
|
||||||
const sha1c = await crypto.sha1(concatBuffers([authKey.subarray(64 + x, 96 + x), messageKey]))
|
const sha1c = crypto.sha1(concatBuffers([authKey.subarray(64 + x, 96 + x), messageKey]))
|
||||||
const sha1d = await crypto.sha1(concatBuffers([messageKey, authKey.subarray(96 + x, 128 + x)]))
|
const sha1d = crypto.sha1(concatBuffers([messageKey, authKey.subarray(96 + x, 128 + x)]))
|
||||||
|
|
||||||
const key = concatBuffers([sha1a.subarray(0, 8), sha1b.subarray(8, 20), sha1c.subarray(4, 16)])
|
const key = concatBuffers([sha1a.subarray(0, 8), sha1b.subarray(8, 20), sha1c.subarray(4, 16)])
|
||||||
const iv = concatBuffers([
|
const iv = concatBuffers([
|
||||||
|
|
|
@ -63,7 +63,6 @@ export abstract class BaseNodeCryptoProvider extends BaseCryptoProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
gunzip(data: Uint8Array): Uint8Array {
|
gunzip(data: Uint8Array): Uint8Array {
|
||||||
// todo: test if wasm impl is better fit here
|
|
||||||
return gunzipSync(data)
|
return gunzipSync(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,9 @@ export async function computePasswordHash(
|
||||||
salt2: Uint8Array,
|
salt2: Uint8Array,
|
||||||
): Promise<Uint8Array> {
|
): Promise<Uint8Array> {
|
||||||
const SH = (data: Uint8Array, salt: Uint8Array) => crypto.sha256(concatBuffers([salt, data, salt]))
|
const SH = (data: Uint8Array, salt: Uint8Array) => crypto.sha256(concatBuffers([salt, data, salt]))
|
||||||
const PH1 = async (pwd: Uint8Array, salt1: Uint8Array, salt2: Uint8Array) => SH(await SH(pwd, salt1), salt2)
|
const PH1 = (pwd: Uint8Array, salt1: Uint8Array, salt2: Uint8Array) => SH(SH(pwd, salt1), salt2)
|
||||||
|
|
||||||
return SH(await crypto.pbkdf2(await PH1(password, salt1, salt2), salt1, 100000), salt2)
|
return SH(await crypto.pbkdf2(PH1(password, salt1, salt2), salt1, 100000), salt2)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -95,12 +95,9 @@ export async function computeSrpParams(
|
||||||
|
|
||||||
const H = (data: Uint8Array) => crypto.sha256(data)
|
const H = (data: Uint8Array) => crypto.sha256(data)
|
||||||
|
|
||||||
const [_k, _u, _x] = await Promise.all([
|
const _k = crypto.sha256(concatBuffers([algo.p, _g]))
|
||||||
// maybe, just maybe this will be a bit faster with some crypto providers
|
const _u = crypto.sha256(concatBuffers([_gA, request.srpB]))
|
||||||
/* k = */ crypto.sha256(concatBuffers([algo.p, _g])),
|
const _x = await computePasswordHash(crypto, utf8EncodeToBuffer(password), algo.salt1, algo.salt2)
|
||||||
/* u = */ crypto.sha256(concatBuffers([_gA, request.srpB])),
|
|
||||||
/* x = */ computePasswordHash(crypto, utf8EncodeToBuffer(password), algo.salt1, algo.salt2),
|
|
||||||
])
|
|
||||||
const k = bufferToBigInt(_k)
|
const k = bufferToBigInt(_k)
|
||||||
const u = bufferToBigInt(_u)
|
const u = bufferToBigInt(_u)
|
||||||
const x = bufferToBigInt(_x)
|
const x = bufferToBigInt(_x)
|
||||||
|
@ -111,18 +108,9 @@ export async function computeSrpParams(
|
||||||
let t = gB - kV
|
let t = gB - kV
|
||||||
if (t < 0n) t += p
|
if (t < 0n) t += p
|
||||||
const sA = bigIntModPow(t, a + u * x, p)
|
const sA = bigIntModPow(t, a + u * x, p)
|
||||||
const _kA = await H(bigIntToBuffer(sA, 256))
|
const _kA = H(bigIntToBuffer(sA, 256))
|
||||||
|
|
||||||
const _M1 = await H(
|
const _M1 = H(concatBuffers([xorBuffer(H(algo.p), H(_g)), H(algo.salt1), H(algo.salt2), _gA, request.srpB, _kA]))
|
||||||
concatBuffers([
|
|
||||||
xorBuffer(await H(algo.p), await H(_g)),
|
|
||||||
await H(algo.salt1),
|
|
||||||
await H(algo.salt2),
|
|
||||||
_gA,
|
|
||||||
request.srpB,
|
|
||||||
_kA,
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_: 'inputCheckPasswordSRP',
|
_: 'inputCheckPasswordSRP',
|
||||||
|
|
69
packages/core/src/utils/crypto/wasm.ts
Normal file
69
packages/core/src/utils/crypto/wasm.ts
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import {
|
||||||
|
createCtr256,
|
||||||
|
ctr256,
|
||||||
|
deflateMaxSize,
|
||||||
|
freeCtr256,
|
||||||
|
gunzip,
|
||||||
|
ige256Decrypt,
|
||||||
|
ige256Encrypt,
|
||||||
|
initAsync,
|
||||||
|
InitInput,
|
||||||
|
sha1,
|
||||||
|
sha256,
|
||||||
|
} from '@mtcute/wasm'
|
||||||
|
|
||||||
|
import { BaseCryptoProvider, IAesCtr, ICryptoProvider, IEncryptionScheme } from './abstract.js'
|
||||||
|
|
||||||
|
export interface WasmCryptoProviderOptions {
|
||||||
|
/**
|
||||||
|
* WASM blob to use for crypto operations.
|
||||||
|
*
|
||||||
|
* Must conform to `@mtcute/wasm` interface.
|
||||||
|
*/
|
||||||
|
wasmInput?: InitInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WasmCryptoProvider extends BaseCryptoProvider implements Partial<ICryptoProvider> {
|
||||||
|
readonly wasmInput?: InitInput
|
||||||
|
|
||||||
|
constructor(params?: WasmCryptoProviderOptions) {
|
||||||
|
super()
|
||||||
|
this.wasmInput = params?.wasmInput
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize(): Promise<void> {
|
||||||
|
return initAsync(this.wasmInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
sha1(data: Uint8Array): Uint8Array {
|
||||||
|
return sha1(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
sha256(data: Uint8Array): Uint8Array {
|
||||||
|
return sha256(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
createAesCtr(key: Uint8Array, iv: Uint8Array): IAesCtr {
|
||||||
|
const ctx = createCtr256(key, iv)
|
||||||
|
|
||||||
|
return {
|
||||||
|
process: (data) => ctr256(ctx, data),
|
||||||
|
close: () => freeCtr256(ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createAesIge(key: Uint8Array, iv: Uint8Array): IEncryptionScheme {
|
||||||
|
return {
|
||||||
|
encrypt: (data) => ige256Encrypt(data, key, iv),
|
||||||
|
decrypt: (data) => ige256Decrypt(data, key, iv),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gzip(data: Uint8Array, maxSize: number): Uint8Array | null {
|
||||||
|
return deflateMaxSize(data, maxSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
gunzip(data: Uint8Array): Uint8Array {
|
||||||
|
return gunzip(data)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,5 @@
|
||||||
import {
|
import { ICryptoProvider } from './abstract.js'
|
||||||
createCtr256,
|
import { WasmCryptoProvider, WasmCryptoProviderOptions } from './wasm.js'
|
||||||
ctr256,
|
|
||||||
deflateMaxSize,
|
|
||||||
freeCtr256,
|
|
||||||
gunzip,
|
|
||||||
ige256Decrypt,
|
|
||||||
ige256Encrypt,
|
|
||||||
initAsync,
|
|
||||||
InitInput,
|
|
||||||
} from '@mtcute/wasm'
|
|
||||||
|
|
||||||
import { MaybeAsync } from '../../index.js'
|
|
||||||
import { BaseCryptoProvider, IAesCtr, ICryptoProvider, IEncryptionScheme } from './abstract.js'
|
|
||||||
|
|
||||||
const ALGO_TO_SUBTLE: Record<string, string> = {
|
const ALGO_TO_SUBTLE: Record<string, string> = {
|
||||||
sha256: 'SHA-256',
|
sha256: 'SHA-256',
|
||||||
|
@ -19,13 +7,11 @@ const ALGO_TO_SUBTLE: Record<string, string> = {
|
||||||
sha512: 'SHA-512',
|
sha512: 'SHA-512',
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WebCryptoProvider extends BaseCryptoProvider implements ICryptoProvider {
|
export class WebCryptoProvider extends WasmCryptoProvider implements ICryptoProvider {
|
||||||
readonly subtle: SubtleCrypto
|
readonly subtle: SubtleCrypto
|
||||||
readonly wasmInput?: InitInput
|
|
||||||
|
|
||||||
constructor(params?: { wasmInput?: InitInput; subtle?: SubtleCrypto }) {
|
constructor(params?: WasmCryptoProviderOptions & { subtle?: SubtleCrypto }) {
|
||||||
super()
|
super(params)
|
||||||
this.wasmInput = params?.wasmInput
|
|
||||||
const subtle = params?.subtle ?? globalThis.crypto?.subtle
|
const subtle = params?.subtle ?? globalThis.crypto?.subtle
|
||||||
|
|
||||||
if (!subtle) {
|
if (!subtle) {
|
||||||
|
@ -34,18 +20,6 @@ export class WebCryptoProvider extends BaseCryptoProvider implements ICryptoProv
|
||||||
this.subtle = subtle
|
this.subtle = subtle
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize(): Promise<void> {
|
|
||||||
return initAsync(this.wasmInput)
|
|
||||||
}
|
|
||||||
|
|
||||||
sha1(data: Uint8Array): MaybeAsync<Uint8Array> {
|
|
||||||
return this.subtle.digest('SHA-1', data).then((result) => new Uint8Array(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
sha256(data: Uint8Array): MaybeAsync<Uint8Array> {
|
|
||||||
return this.subtle.digest('SHA-256', data).then((result) => new Uint8Array(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
async pbkdf2(
|
async pbkdf2(
|
||||||
password: Uint8Array,
|
password: Uint8Array,
|
||||||
salt: Uint8Array,
|
salt: Uint8Array,
|
||||||
|
@ -82,28 +56,4 @@ export class WebCryptoProvider extends BaseCryptoProvider implements ICryptoProv
|
||||||
|
|
||||||
return new Uint8Array(res)
|
return new Uint8Array(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
createAesCtr(key: Uint8Array, iv: Uint8Array): IAesCtr {
|
|
||||||
const ctx = createCtr256(key, iv)
|
|
||||||
|
|
||||||
return {
|
|
||||||
process: (data) => ctr256(ctx, data),
|
|
||||||
close: () => freeCtr256(ctx),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createAesIge(key: Uint8Array, iv: Uint8Array): IEncryptionScheme {
|
|
||||||
return {
|
|
||||||
encrypt: (data) => ige256Encrypt(data, key, iv),
|
|
||||||
decrypt: (data) => ige256Decrypt(data, key, iv),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gzip(data: Uint8Array, maxSize: number): Uint8Array | null {
|
|
||||||
return deflateMaxSize(data, maxSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
gunzip(data: Uint8Array): Uint8Array {
|
|
||||||
return gunzip(data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,9 @@ describe('AuthKey', () => {
|
||||||
const logger = new LogManager()
|
const logger = new LogManager()
|
||||||
const readerMap: TlReaderMap = {}
|
const readerMap: TlReaderMap = {}
|
||||||
|
|
||||||
it('should correctly calculate derivatives', async () => {
|
it('should correctly calculate derivatives', () => {
|
||||||
const key = new AuthKey(crypto, logger, readerMap)
|
const key = new AuthKey(crypto, logger, readerMap)
|
||||||
await key.setup(authKey)
|
key.setup(authKey)
|
||||||
|
|
||||||
expect(key.key).to.eql(authKey)
|
expect(key.key).to.eql(authKey)
|
||||||
expect(key.clientSalt).to.eql(
|
expect(key.clientSalt).to.eql(
|
||||||
|
|
|
@ -11,20 +11,20 @@ import { ICryptoProvider } from '../src/utils/index.js'
|
||||||
export function testCryptoProvider(c: ICryptoProvider): void {
|
export function testCryptoProvider(c: ICryptoProvider): void {
|
||||||
before(() => c.initialize?.())
|
before(() => c.initialize?.())
|
||||||
|
|
||||||
it('should calculate sha1', async () => {
|
it('should calculate sha1', () => {
|
||||||
expect(hexEncode(await c.sha1(utf8EncodeToBuffer('')))).to.eq('da39a3ee5e6b4b0d3255bfef95601890afd80709')
|
expect(hexEncode(c.sha1(utf8EncodeToBuffer('')))).to.eq('da39a3ee5e6b4b0d3255bfef95601890afd80709')
|
||||||
expect(hexEncode(await c.sha1(utf8EncodeToBuffer('hello')))).to.eq('aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d')
|
expect(hexEncode(c.sha1(utf8EncodeToBuffer('hello')))).to.eq('aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d')
|
||||||
expect(hexEncode(await c.sha1(hexDecodeToBuffer('aebb1f')))).to.eq('62849d15c5dea495916c5eea8dba5f9551288850')
|
expect(hexEncode(c.sha1(hexDecodeToBuffer('aebb1f')))).to.eq('62849d15c5dea495916c5eea8dba5f9551288850')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should calculate sha256', async () => {
|
it('should calculate sha256', () => {
|
||||||
expect(hexEncode(await c.sha256(utf8EncodeToBuffer('')))).to.eq(
|
expect(hexEncode(c.sha256(utf8EncodeToBuffer('')))).to.eq(
|
||||||
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
|
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
|
||||||
)
|
)
|
||||||
expect(hexEncode(await c.sha256(utf8EncodeToBuffer('hello')))).to.eq(
|
expect(hexEncode(c.sha256(utf8EncodeToBuffer('hello')))).to.eq(
|
||||||
'2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
|
'2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
|
||||||
)
|
)
|
||||||
expect(hexEncode(await c.sha256(hexDecodeToBuffer('aebb1f')))).to.eq(
|
expect(hexEncode(c.sha256(hexDecodeToBuffer('aebb1f')))).to.eq(
|
||||||
'2d29658aba48f2b286fe8bbddb931b7ad297e5adb5b9a6fc3aab67ef7fbf4e80',
|
'2d29658aba48f2b286fe8bbddb931b7ad297e5adb5b9a6fc3aab67ef7fbf4e80',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -7,9 +7,9 @@ import { parsePublicKey } from '../src/utils/index.js'
|
||||||
const crypto = new NodeCryptoProvider()
|
const crypto = new NodeCryptoProvider()
|
||||||
|
|
||||||
describe('parsePublicKey', () => {
|
describe('parsePublicKey', () => {
|
||||||
it('should parse telegram public keys', async () => {
|
it('should parse telegram public keys', () => {
|
||||||
expect(
|
expect(
|
||||||
await parsePublicKey(
|
parsePublicKey(
|
||||||
crypto,
|
crypto,
|
||||||
`-----BEGIN RSA PUBLIC KEY-----
|
`-----BEGIN RSA PUBLIC KEY-----
|
||||||
MIIBCgKCAQEAruw2yP/BCcsJliRoW5eBVBVle9dtjJw+OYED160Wybum9SXtBBLX
|
MIIBCgKCAQEAruw2yP/BCcsJliRoW5eBVBVle9dtjJw+OYED160Wybum9SXtBBLX
|
||||||
|
|
|
@ -20,11 +20,11 @@ const authKey = Buffer.alloc(
|
||||||
const messageKey = Buffer.from('25d701f2a29205526757825a99eb2d32')
|
const messageKey = Buffer.from('25d701f2a29205526757825a99eb2d32')
|
||||||
|
|
||||||
describe('mtproto 2.0', () => {
|
describe('mtproto 2.0', () => {
|
||||||
it('should correctly derive message key and iv for client', async () => {
|
it('should correctly derive message key and iv for client', () => {
|
||||||
const crypto = new NodeCryptoProvider()
|
const crypto = new NodeCryptoProvider()
|
||||||
const spy = chai.spy.on(crypto, 'createAesIge')
|
const spy = chai.spy.on(crypto, 'createAesIge')
|
||||||
|
|
||||||
await createAesIgeForMessage(crypto, authKey, messageKey, true)
|
createAesIgeForMessage(crypto, authKey, messageKey, true)
|
||||||
|
|
||||||
expect(spy).to.have.been.called.with.exactly(
|
expect(spy).to.have.been.called.with.exactly(
|
||||||
Buffer.from('7acac59ab48cd370e478daf6c64545ab9f32d5c9197f25febe052110f61875ca', 'hex'),
|
Buffer.from('7acac59ab48cd370e478daf6c64545ab9f32d5c9197f25febe052110f61875ca', 'hex'),
|
||||||
|
@ -32,11 +32,11 @@ describe('mtproto 2.0', () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should correctly derive message key and iv for server', async () => {
|
it('should correctly derive message key and iv for server', () => {
|
||||||
const crypto = new NodeCryptoProvider()
|
const crypto = new NodeCryptoProvider()
|
||||||
const spy = chai.spy.on(crypto, 'createAesIge')
|
const spy = chai.spy.on(crypto, 'createAesIge')
|
||||||
|
|
||||||
await createAesIgeForMessage(crypto, authKey, messageKey, false)
|
createAesIgeForMessage(crypto, authKey, messageKey, false)
|
||||||
|
|
||||||
expect(spy).to.have.been.called.with.exactly(
|
expect(spy).to.have.been.called.with.exactly(
|
||||||
Buffer.from('c7cf179e7ebab144ba87de05415db4157d2fc66df4790b2fd405a6c8cbe4c0b3', 'hex'),
|
Buffer.from('c7cf179e7ebab144ba87de05415db4157d2fc66df4790b2fd405a6c8cbe4c0b3', 'hex'),
|
||||||
|
@ -46,11 +46,11 @@ describe('mtproto 2.0', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('mtproto 1.0', () => {
|
describe('mtproto 1.0', () => {
|
||||||
it('should correctly derive message key and iv for client', async () => {
|
it('should correctly derive message key and iv for client', () => {
|
||||||
const crypto = new NodeCryptoProvider()
|
const crypto = new NodeCryptoProvider()
|
||||||
const spy = chai.spy.on(crypto, 'createAesIge')
|
const spy = chai.spy.on(crypto, 'createAesIge')
|
||||||
|
|
||||||
await createAesIgeForMessageOld(crypto, authKey, messageKey, true)
|
createAesIgeForMessageOld(crypto, authKey, messageKey, true)
|
||||||
|
|
||||||
expect(spy).to.have.been.called.with.exactly(
|
expect(spy).to.have.been.called.with.exactly(
|
||||||
Buffer.from('aad61cb5b7be5e8435174d74665f8a978e85806d0970ad4958642ca49e3c8834', 'hex'),
|
Buffer.from('aad61cb5b7be5e8435174d74665f8a978e85806d0970ad4958642ca49e3c8834', 'hex'),
|
||||||
|
@ -58,11 +58,11 @@ describe('mtproto 1.0', () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should correctly derive message key and iv for server', async () => {
|
it('should correctly derive message key and iv for server', () => {
|
||||||
const crypto = new NodeCryptoProvider()
|
const crypto = new NodeCryptoProvider()
|
||||||
const spy = chai.spy.on(crypto, 'createAesIge')
|
const spy = chai.spy.on(crypto, 'createAesIge')
|
||||||
|
|
||||||
await createAesIgeForMessageOld(crypto, authKey, messageKey, false)
|
createAesIgeForMessageOld(crypto, authKey, messageKey, false)
|
||||||
|
|
||||||
expect(spy).to.have.been.called.with.exactly(
|
expect(spy).to.have.been.called.with.exactly(
|
||||||
Buffer.from('d57682a17105e43b92bc5025ea80e88ef708240fc19450dfe072a8760f9534da', 'hex'),
|
Buffer.from('d57682a17105e43b92bc5025ea80e88ef708240fc19450dfe072a8760f9534da', 'hex'),
|
||||||
|
@ -72,10 +72,10 @@ describe('mtproto 1.0', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('mtproto key/iv from nonce', () => {
|
describe('mtproto key/iv from nonce', () => {
|
||||||
it('should correctly derive message key and iv for given nonces', async () => {
|
it('should correctly derive message key and iv for given nonces', () => {
|
||||||
const crypto = new NodeCryptoProvider()
|
const crypto = new NodeCryptoProvider()
|
||||||
|
|
||||||
const res = await generateKeyAndIvFromNonce(
|
const res = generateKeyAndIvFromNonce(
|
||||||
crypto,
|
crypto,
|
||||||
Buffer.from('8af24c551836e5ed7002f5857e6e71b2', 'hex'),
|
Buffer.from('8af24c551836e5ed7002f5857e6e71b2', 'hex'),
|
||||||
Buffer.from('3bf48b2d3152f383d82d1f2b32ac7fb5', 'hex'),
|
Buffer.from('3bf48b2d3152f383d82d1f2b32ac7fb5', 'hex'),
|
||||||
|
|
|
@ -50,7 +50,7 @@ async function main() {
|
||||||
const obj: Record<string, TlPublicKey> = {}
|
const obj: Record<string, TlPublicKey> = {}
|
||||||
|
|
||||||
for await (const key of parseInputFile()) {
|
for await (const key of parseInputFile()) {
|
||||||
const parsed = await parsePublicKey(crypto, key.pem, key.kind === 'old')
|
const parsed = parsePublicKey(crypto, key.pem, key.kind === 'old')
|
||||||
obj[parsed.fingerprint] = parsed
|
obj[parsed.fingerprint] = parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,15 +5,20 @@
|
||||||
Highly optimized for size & speed WASM implementation of common algorithms used in Telegram.
|
Highly optimized for size & speed WASM implementation of common algorithms used in Telegram.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- **Super lightweight**: Only 45 KB raw, 22 KB gzipped
|
- **Super lightweight**: Only 47 KB raw, 24 KB gzipped
|
||||||
- **Blazingly fast**: Up to 10x faster than pure JS implementations
|
- **Blazingly fast**: Up to 10x faster than pure JS implementations
|
||||||
- Implements AES IGE and Deflate (zlib compression + gunzip), which are not available in some environments (e.g. web)
|
- **Ready to use**: Implements almost all algos used in MTProto:
|
||||||
|
- AES IGE
|
||||||
|
- Deflate (zlib compression + gunzip)
|
||||||
|
- SHA-1, SHA-256
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
- Deflate is implemented through a modified version of [libdeflate](https://github.com/ebiggers/libdeflate), MIT license.
|
- Deflate is implemented through a modified version of [libdeflate](https://github.com/ebiggers/libdeflate), MIT license.
|
||||||
- Modified by [kamillaova](https://github.com/kamillaova) to support WASM and improve bundle size
|
- Modified by [kamillaova](https://github.com/kamillaova) to support WASM and improve bundle size
|
||||||
- AES IGE code is mostly based on [tgcrypto](https://github.com/pyrogram/tgcrypto), LGPL-3.0 license.
|
- AES IGE code is mostly based on [tgcrypto](https://github.com/pyrogram/tgcrypto), LGPL-3.0 license.
|
||||||
- To comply with LGPL-3.0, the source code of the modified tgcrypto is available [here](./lib/crypto/) under LGPL-3.0 license.
|
- To comply with LGPL-3.0, the source code of the modified tgcrypto is available [here](./lib/crypto/) under LGPL-3.0 license.
|
||||||
|
- SHA1 is based on [teeny-sha1](https://github.com/CTrabant/teeny-sha1)
|
||||||
|
- SHA256 is based on [lekkit/sha256](https://github.com/LekKit/sha256)
|
||||||
|
|
||||||
## Benchmarks
|
## Benchmarks
|
||||||
See https://github.com/mtcute/benchmarks
|
See https://github.com/mtcute/benchmarks
|
|
@ -6,9 +6,10 @@ RUN apk add --no-cache lld make clang16 binaryen
|
||||||
|
|
||||||
COPY crypto /src/crypto
|
COPY crypto /src/crypto
|
||||||
COPY libdeflate /src/libdeflate
|
COPY libdeflate /src/libdeflate
|
||||||
COPY *.h *.c Makefile /src/
|
COPY utils /src/utils
|
||||||
|
COPY wasm.h Makefile /src/
|
||||||
|
|
||||||
RUN ZLIB_COMPRESSION_API=1 GZIP_DECOMPRESSION_API=1 IGE_API=1 CTR_API=1 make
|
RUN make
|
||||||
|
|
||||||
FROM scratch AS binaries
|
FROM scratch AS binaries
|
||||||
COPY --from=build /src/mtcute.wasm /
|
COPY --from=build /src/mtcute.wasm /
|
||||||
|
|
|
@ -1,39 +1,17 @@
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
|
||||||
DEFAULT_API ?= 0
|
SOURCES = utils/allocator.c \
|
||||||
|
libdeflate/allocator.c \
|
||||||
DEFLATE_COMPRESSION_API ?= $(DEFAULT_API)
|
libdeflate/deflate_compress.c \
|
||||||
DEFLATE_DECOMPRESSION_API ?= $(DEFAULT_API)
|
libdeflate/deflate_decompress.c \
|
||||||
GZIP_COMPRESSION_API ?= $(DEFAULT_API)
|
libdeflate/gzip_decompress.c \
|
||||||
GZIP_DECOMPRESSION_API ?= $(DEFAULT_API)
|
libdeflate/zlib_compress.c \
|
||||||
ZLIB_COMPRESSION_API ?= $(DEFAULT_API)
|
libdeflate/adler32.c \
|
||||||
ZLIB_DECOMPRESSION_API ?= $(DEFAULT_API)
|
crypto/aes256.c \
|
||||||
CRC32_API ?= $(DEFAULT_API)
|
crypto/ige256.c \
|
||||||
ADLER32_API ?= $(DEFAULT_API)
|
crypto/ctr256.c \
|
||||||
IGE_API ?= $(DEFAULT_API)
|
hash/sha256.c \
|
||||||
CTR_API ?= $(DEFAULT_API)
|
hash/sha1.c
|
||||||
|
|
||||||
CRC32 ?= 0
|
|
||||||
|
|
||||||
LOGGING ?= 0
|
|
||||||
|
|
||||||
_DEFLATE_COMPRESSION := 1
|
|
||||||
_DEFLATE_DECOMPRESSION := 1
|
|
||||||
_ADLER32 := $(findstring 1, $(ZLIB_COMPRESSION_API)$(ZLIB_DECOMPRESSION_API))
|
|
||||||
_AES := $(findstring 1, $(IGE_API)$(CTR_API))
|
|
||||||
|
|
||||||
SOURCES = utils.c \
|
|
||||||
$(if $(filter 1, $(_DEFLATE_COMPRESSION)), libdeflate/deflate_compress.c) \
|
|
||||||
$(if $(filter 1, $(_DEFLATE_DECOMPRESSION)), libdeflate/deflate_decompress.c) \
|
|
||||||
$(if $(filter 1, $(GZIP_COMPRESSION_API)), libdeflate/gzip_compress.c) \
|
|
||||||
$(if $(filter 1, $(GZIP_DECOMPRESSION_API)), libdeflate/gzip_decompress.c) \
|
|
||||||
$(if $(filter 1, $(ZLIB_COMPRESSION_API)), libdeflate/zlib_compress.c) \
|
|
||||||
$(if $(filter 1, $(ZLIB_DECOMPRESSION_API)), libdeflate/zlib_decompress.c) \
|
|
||||||
$(if $(filter 1, $(CRC32)), libdeflate/crc32.c) \
|
|
||||||
$(if $(filter 1, $(_ADLER32)), libdeflate/adler32.c) \
|
|
||||||
$(if $(filter 1, $(_AES)), crypto/aes256.c) \
|
|
||||||
$(if $(filter 1, $(IGE_API)), crypto/ige256.c) \
|
|
||||||
$(if $(filter 1, $(CTR_API)), crypto/ctr256.c)
|
|
||||||
|
|
||||||
WASM_CC ?= clang
|
WASM_CC ?= clang
|
||||||
CC := $(WASM_CC)
|
CC := $(WASM_CC)
|
||||||
|
@ -41,7 +19,6 @@ CC := $(WASM_CC)
|
||||||
CFLAGS_WASM := \
|
CFLAGS_WASM := \
|
||||||
-target wasm32-unknown-unknown \
|
-target wasm32-unknown-unknown \
|
||||||
-nostdlib -ffreestanding -DFREESTANDING \
|
-nostdlib -ffreestanding -DFREESTANDING \
|
||||||
$(if $(filter 1, $(LOGGING)), -DLOGGING) \
|
|
||||||
-mbulk-memory \
|
-mbulk-memory \
|
||||||
-Wl,--no-entry,--export-dynamic,--lto-O3
|
-Wl,--no-entry,--export-dynamic,--lto-O3
|
||||||
|
|
||||||
|
@ -69,7 +46,7 @@ endif
|
||||||
OUT := mtcute.wasm
|
OUT := mtcute.wasm
|
||||||
|
|
||||||
$(OUT): $(SOURCES)
|
$(OUT): $(SOURCES)
|
||||||
$(CC) $(CFLAGS) -I . -o $@ $^
|
$(CC) $(CFLAGS) -I . -I utils -o $@ $^
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OUT)
|
rm -f $(OUT)
|
||||||
|
|
|
@ -6,6 +6,17 @@
|
||||||
#define GET(p) SWAP(*((uint32_t *)(p)))
|
#define GET(p) SWAP(*((uint32_t *)(p)))
|
||||||
#define PUT(ct, st) (*((uint32_t *)(ct)) = SWAP((st)))
|
#define PUT(ct, st) (*((uint32_t *)(ct)) = SWAP((st)))
|
||||||
|
|
||||||
|
uint8_t aes_shared_key_buffer[32];
|
||||||
|
uint8_t aes_shared_iv_buffer[32];
|
||||||
|
|
||||||
|
WASM_EXPORT uint8_t* __get_shared_key_buffer() {
|
||||||
|
return aes_shared_key_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
WASM_EXPORT uint8_t* __get_shared_iv_buffer() {
|
||||||
|
return aes_shared_iv_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
static const uint32_t Te0[256] = {
|
static const uint32_t Te0[256] = {
|
||||||
0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554,
|
0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554,
|
||||||
0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a,
|
0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a,
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
#include "lib_common.h"
|
#include "wasm.h"
|
||||||
|
|
||||||
#ifndef AES256_H
|
#ifndef AES256_H
|
||||||
#define AES256_H
|
#define AES256_H
|
||||||
|
|
||||||
#define AES_BLOCK_SIZE 16
|
#define AES_BLOCK_SIZE 16
|
||||||
#define EXPANDED_KEY_SIZE 60
|
#define EXPANDED_KEY_SIZE 60
|
||||||
#define AES_EXPORT __attribute__((visibility("default")))
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
extern uint8_t aes_shared_key_buffer[32];
|
||||||
extern "C" {
|
extern uint8_t aes_shared_iv_buffer[32];
|
||||||
#endif
|
|
||||||
|
|
||||||
void aes256_set_encryption_key(uint8_t* key, uint32_t* expandedKey);
|
void aes256_set_encryption_key(uint8_t* key, uint32_t* expandedKey);
|
||||||
void aes256_set_decryption_key(uint8_t* key, uint32_t* expandedKey);
|
void aes256_set_decryption_key(uint8_t* key, uint32_t* expandedKey);
|
||||||
|
@ -17,9 +15,4 @@ void aes256_set_decryption_key(uint8_t* key, uint32_t* expandedKey);
|
||||||
void aes256_encrypt(uint8_t* in, uint8_t* out, uint32_t* expandedKey);
|
void aes256_encrypt(uint8_t* in, uint8_t* out, uint32_t* expandedKey);
|
||||||
void aes256_decrypt(uint8_t* in, uint8_t* out, uint32_t* expandedKey);
|
void aes256_decrypt(uint8_t* in, uint8_t* out, uint32_t* expandedKey);
|
||||||
|
|
||||||
|
#endif // AES256_H
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // AES256_H
|
|
|
@ -2,27 +2,25 @@
|
||||||
|
|
||||||
struct ctr256_ctx {
|
struct ctr256_ctx {
|
||||||
uint32_t expandedKey[EXPANDED_KEY_SIZE];
|
uint32_t expandedKey[EXPANDED_KEY_SIZE];
|
||||||
uint8_t* iv;
|
uint8_t iv[AES_BLOCK_SIZE];
|
||||||
uint8_t state;
|
uint8_t state;
|
||||||
};
|
};
|
||||||
|
|
||||||
AES_EXPORT struct ctr256_ctx* ctr256_alloc(uint8_t* key, uint8_t* iv) {
|
WASM_EXPORT struct ctr256_ctx* ctr256_alloc() {
|
||||||
struct ctr256_ctx *state = (struct ctr256_ctx *) __malloc(sizeof(struct ctr256_ctx));
|
struct ctr256_ctx *state = (struct ctr256_ctx *) __malloc(sizeof(struct ctr256_ctx));
|
||||||
aes256_set_encryption_key(key, state->expandedKey);
|
aes256_set_encryption_key(aes_shared_key_buffer, state->expandedKey);
|
||||||
__free(key);
|
|
||||||
|
|
||||||
state->iv = iv;
|
memcpy(state->iv, aes_shared_iv_buffer, AES_BLOCK_SIZE);
|
||||||
state->state = 0;
|
state->state = 0;
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
AES_EXPORT void ctr256_free(struct ctr256_ctx* ctx) {
|
WASM_EXPORT void ctr256_free(struct ctr256_ctx* ctx) {
|
||||||
__free(ctx->iv);
|
|
||||||
__free(ctx);
|
__free(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
AES_EXPORT void ctr256(struct ctr256_ctx* ctx, uint8_t* in, uint32_t length, uint8_t *out) {
|
WASM_EXPORT void ctr256(struct ctr256_ctx* ctx, uint8_t* in, uint32_t length, uint8_t *out) {
|
||||||
uint8_t chunk[AES_BLOCK_SIZE];
|
uint8_t chunk[AES_BLOCK_SIZE];
|
||||||
uint32_t* expandedKey = ctx->expandedKey;
|
uint32_t* expandedKey = ctx->expandedKey;
|
||||||
uint8_t* iv = ctx->iv;
|
uint8_t* iv = ctx->iv;
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
#ifndef CTR256_H
|
|
||||||
#define CTR256_H
|
|
||||||
|
|
||||||
uint8_t *ctr256(const uint8_t in[], uint32_t length, const uint8_t key[32], uint8_t iv[16], uint8_t *state);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "aes256.h"
|
#include "aes256.h"
|
||||||
|
|
||||||
AES_EXPORT void ige256_encrypt(uint8_t* in, uint32_t length, uint8_t* key, uint8_t* iv, uint8_t* out) {
|
WASM_EXPORT void ige256_encrypt(uint8_t* in, uint32_t length, uint8_t* out) {
|
||||||
uint32_t expandedKey[EXPANDED_KEY_SIZE];
|
uint32_t expandedKey[EXPANDED_KEY_SIZE];
|
||||||
uint32_t i, j;
|
uint32_t i, j;
|
||||||
|
|
||||||
|
@ -8,10 +8,10 @@ AES_EXPORT void ige256_encrypt(uint8_t* in, uint32_t length, uint8_t* key, uint8
|
||||||
uint8_t* iv2;
|
uint8_t* iv2;
|
||||||
uint8_t* block;
|
uint8_t* block;
|
||||||
|
|
||||||
iv1 = &iv[0];
|
iv1 = &aes_shared_iv_buffer[0];
|
||||||
iv2 = &iv[16];
|
iv2 = &aes_shared_iv_buffer[16];
|
||||||
|
|
||||||
aes256_set_encryption_key(key, expandedKey);
|
aes256_set_encryption_key(aes_shared_key_buffer, expandedKey);
|
||||||
|
|
||||||
for (i = 0; i < length; i += AES_BLOCK_SIZE) {
|
for (i = 0; i < length; i += AES_BLOCK_SIZE) {
|
||||||
block = &out[i];
|
block = &out[i];
|
||||||
|
@ -29,7 +29,7 @@ AES_EXPORT void ige256_encrypt(uint8_t* in, uint32_t length, uint8_t* key, uint8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AES_EXPORT void ige256_decrypt(uint8_t* in, uint32_t length, uint8_t* key, uint8_t* iv, uint8_t* out) {
|
WASM_EXPORT void ige256_decrypt(uint8_t* in, uint32_t length, uint8_t* out) {
|
||||||
uint32_t expandedKey[EXPANDED_KEY_SIZE];
|
uint32_t expandedKey[EXPANDED_KEY_SIZE];
|
||||||
uint32_t i, j;
|
uint32_t i, j;
|
||||||
|
|
||||||
|
@ -37,10 +37,10 @@ AES_EXPORT void ige256_decrypt(uint8_t* in, uint32_t length, uint8_t* key, uint8
|
||||||
uint8_t* iv2;
|
uint8_t* iv2;
|
||||||
uint8_t* block;
|
uint8_t* block;
|
||||||
|
|
||||||
iv1 = &iv[16];
|
iv1 = &aes_shared_iv_buffer[16];
|
||||||
iv2 = &iv[0];
|
iv2 = &aes_shared_iv_buffer[0];
|
||||||
|
|
||||||
aes256_set_decryption_key(key, expandedKey);
|
aes256_set_decryption_key(aes_shared_key_buffer, expandedKey);
|
||||||
|
|
||||||
for (i = 0; i < length; i += AES_BLOCK_SIZE) {
|
for (i = 0; i < length; i += AES_BLOCK_SIZE) {
|
||||||
block = &out[i];
|
block = &out[i];
|
||||||
|
@ -56,4 +56,4 @@ AES_EXPORT void ige256_decrypt(uint8_t* in, uint32_t length, uint8_t* key, uint8
|
||||||
iv1 = block;
|
iv1 = block;
|
||||||
iv2 = &in[i];
|
iv2 = &in[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,17 +0,0 @@
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#ifndef IGE256_H
|
|
||||||
#define IGE256_H
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void ige256_encrypt(uint8_t* in, uint32_t length, uint8_t* key, uint8_t* iv, uint8_t* out);
|
|
||||||
void ige256_decrypt(uint8_t* in, uint32_t length, uint8_t* key, uint8_t* iv, uint8_t* out);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // IGE256_H
|
|
179
packages/wasm/lib/hash/sha1.c
Normal file
179
packages/wasm/lib/hash/sha1.c
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Teeny SHA-1
|
||||||
|
*
|
||||||
|
* The below sha1digest() calculates a SHA-1 hash value for a
|
||||||
|
* specified data buffer and generates a hex representation of the
|
||||||
|
* result. This implementation is a re-forming of the SHA-1 code at
|
||||||
|
* https://github.com/jinqiangshou/EncryptionLibrary.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017 CTrabant
|
||||||
|
*
|
||||||
|
* License: MIT, see included LICENSE file for details.
|
||||||
|
*
|
||||||
|
* To use the sha1digest() function either copy it into an existing
|
||||||
|
* project source code file or include this file in a project and put
|
||||||
|
* the declaration (example below) in the sources files where needed.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "wasm.h"
|
||||||
|
|
||||||
|
/* Declaration:
|
||||||
|
extern int sha1digest(uint8_t *digest, char *hexdigest, const uint8_t *data, size_t databytes);
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* sha1digest: https://github.com/CTrabant/teeny-sha1
|
||||||
|
*
|
||||||
|
* Calculate the SHA-1 value for supplied data buffer and generate a
|
||||||
|
* text representation in hexadecimal.
|
||||||
|
*
|
||||||
|
* Based on https://github.com/jinqiangshou/EncryptionLibrary, credit
|
||||||
|
* goes to @jinqiangshou, all new bugs are mine.
|
||||||
|
*
|
||||||
|
* @input:
|
||||||
|
* data -- data to be hashed
|
||||||
|
* databytes -- bytes in data buffer to be hashed
|
||||||
|
*
|
||||||
|
* @output:
|
||||||
|
* digest -- the result, MUST be at least 20 bytes
|
||||||
|
* hexdigest -- the result in hex, MUST be at least 41 bytes
|
||||||
|
*
|
||||||
|
* At least one of the output buffers must be supplied. The other, if not
|
||||||
|
* desired, may be set to NULL.
|
||||||
|
*
|
||||||
|
* @return: 0 on success and non-zero on error.
|
||||||
|
******************************************************************************/
|
||||||
|
WASM_EXPORT void sha1(const uint8_t *data, size_t databytes) {
|
||||||
|
#define SHA1ROTATELEFT(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||||
|
|
||||||
|
uint32_t W[80];
|
||||||
|
uint32_t H[] = {0x67452301,
|
||||||
|
0xEFCDAB89,
|
||||||
|
0x98BADCFE,
|
||||||
|
0x10325476,
|
||||||
|
0xC3D2E1F0};
|
||||||
|
uint32_t a;
|
||||||
|
uint32_t b;
|
||||||
|
uint32_t c;
|
||||||
|
uint32_t d;
|
||||||
|
uint32_t e;
|
||||||
|
uint32_t f = 0;
|
||||||
|
uint32_t k = 0;
|
||||||
|
|
||||||
|
uint32_t idx;
|
||||||
|
uint32_t lidx;
|
||||||
|
uint32_t widx;
|
||||||
|
uint32_t didx = 0;
|
||||||
|
|
||||||
|
int32_t wcount;
|
||||||
|
uint32_t temp;
|
||||||
|
uint64_t databits = ((uint64_t)databytes) * 8;
|
||||||
|
uint32_t loopcount = (databytes + 8) / 64 + 1;
|
||||||
|
uint32_t tailbytes = 64 * loopcount - databytes;
|
||||||
|
uint8_t datatail[128] = {0};
|
||||||
|
|
||||||
|
/* Pre-processing of data tail (includes padding to fill out 512-bit chunk):
|
||||||
|
Add bit '1' to end of message (big-endian)
|
||||||
|
Add 64-bit message length in bits at very end (big-endian) */
|
||||||
|
datatail[0] = 0x80;
|
||||||
|
datatail[tailbytes - 8] = (uint8_t) (databits >> 56 & 0xFF);
|
||||||
|
datatail[tailbytes - 7] = (uint8_t) (databits >> 48 & 0xFF);
|
||||||
|
datatail[tailbytes - 6] = (uint8_t) (databits >> 40 & 0xFF);
|
||||||
|
datatail[tailbytes - 5] = (uint8_t) (databits >> 32 & 0xFF);
|
||||||
|
datatail[tailbytes - 4] = (uint8_t) (databits >> 24 & 0xFF);
|
||||||
|
datatail[tailbytes - 3] = (uint8_t) (databits >> 16 & 0xFF);
|
||||||
|
datatail[tailbytes - 2] = (uint8_t) (databits >> 8 & 0xFF);
|
||||||
|
datatail[tailbytes - 1] = (uint8_t) (databits >> 0 & 0xFF);
|
||||||
|
|
||||||
|
/* Process each 512-bit chunk */
|
||||||
|
for (lidx = 0; lidx < loopcount; lidx++)
|
||||||
|
{
|
||||||
|
/* Compute all elements in W */
|
||||||
|
memset (W, 0, 80 * sizeof (uint32_t));
|
||||||
|
|
||||||
|
/* Break 512-bit chunk into sixteen 32-bit, big endian words */
|
||||||
|
for (widx = 0; widx <= 15; widx++)
|
||||||
|
{
|
||||||
|
wcount = 24;
|
||||||
|
|
||||||
|
/* Copy byte-per byte from specified buffer */
|
||||||
|
while (didx < databytes && wcount >= 0)
|
||||||
|
{
|
||||||
|
W[widx] += (((uint32_t)data[didx]) << wcount);
|
||||||
|
didx++;
|
||||||
|
wcount -= 8;
|
||||||
|
}
|
||||||
|
/* Fill out W with padding as needed */
|
||||||
|
while (wcount >= 0)
|
||||||
|
{
|
||||||
|
W[widx] += (((uint32_t)datatail[didx - databytes]) << wcount);
|
||||||
|
didx++;
|
||||||
|
wcount -= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extend the sixteen 32-bit words into eighty 32-bit words, with potential optimization from:
|
||||||
|
"Improving the Performance of the Secure Hash Algorithm (SHA-1)" by Max Locktyukhin */
|
||||||
|
for (widx = 16; widx <= 31; widx++)
|
||||||
|
{
|
||||||
|
W[widx] = SHA1ROTATELEFT ((W[widx - 3] ^ W[widx - 8] ^ W[widx - 14] ^ W[widx - 16]), 1);
|
||||||
|
}
|
||||||
|
for (widx = 32; widx <= 79; widx++)
|
||||||
|
{
|
||||||
|
W[widx] = SHA1ROTATELEFT ((W[widx - 6] ^ W[widx - 16] ^ W[widx - 28] ^ W[widx - 32]), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main loop */
|
||||||
|
a = H[0];
|
||||||
|
b = H[1];
|
||||||
|
c = H[2];
|
||||||
|
d = H[3];
|
||||||
|
e = H[4];
|
||||||
|
|
||||||
|
for (idx = 0; idx <= 79; idx++)
|
||||||
|
{
|
||||||
|
if (idx <= 19)
|
||||||
|
{
|
||||||
|
f = (b & c) | ((~b) & d);
|
||||||
|
k = 0x5A827999;
|
||||||
|
}
|
||||||
|
else if (idx >= 20 && idx <= 39)
|
||||||
|
{
|
||||||
|
f = b ^ c ^ d;
|
||||||
|
k = 0x6ED9EBA1;
|
||||||
|
}
|
||||||
|
else if (idx >= 40 && idx <= 59)
|
||||||
|
{
|
||||||
|
f = (b & c) | (b & d) | (c & d);
|
||||||
|
k = 0x8F1BBCDC;
|
||||||
|
}
|
||||||
|
else if (idx >= 60 && idx <= 79)
|
||||||
|
{
|
||||||
|
f = b ^ c ^ d;
|
||||||
|
k = 0xCA62C1D6;
|
||||||
|
}
|
||||||
|
temp = SHA1ROTATELEFT (a, 5) + f + e + k + W[idx];
|
||||||
|
e = d;
|
||||||
|
d = c;
|
||||||
|
c = SHA1ROTATELEFT (b, 30);
|
||||||
|
b = a;
|
||||||
|
a = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
H[0] += a;
|
||||||
|
H[1] += b;
|
||||||
|
H[2] += c;
|
||||||
|
H[3] += d;
|
||||||
|
H[4] += e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store binary digest in supplied buffer */
|
||||||
|
for (idx = 0; idx < 5; idx++) {
|
||||||
|
shared_out[idx * 4 + 0] = (uint8_t) (H[idx] >> 24);
|
||||||
|
shared_out[idx * 4 + 1] = (uint8_t) (H[idx] >> 16);
|
||||||
|
shared_out[idx * 4 + 2] = (uint8_t) (H[idx] >> 8);
|
||||||
|
shared_out[idx * 4 + 3] = (uint8_t) (H[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef SHA1ROTATELEFT
|
||||||
|
} /* End of sha1digest() */
|
167
packages/wasm/lib/hash/sha256.c
Normal file
167
packages/wasm/lib/hash/sha256.c
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 LekKit https://github.com/LekKit
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "wasm.h"
|
||||||
|
|
||||||
|
struct lekkit_sha256_buff {
|
||||||
|
uint64_t data_size;
|
||||||
|
uint32_t h[8];
|
||||||
|
uint8_t last_chunk[64];
|
||||||
|
uint8_t chunk_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
void lekkit_sha256_init(struct lekkit_sha256_buff* buff) {
|
||||||
|
buff->h[0] = 0x6a09e667;
|
||||||
|
buff->h[1] = 0xbb67ae85;
|
||||||
|
buff->h[2] = 0x3c6ef372;
|
||||||
|
buff->h[3] = 0xa54ff53a;
|
||||||
|
buff->h[4] = 0x510e527f;
|
||||||
|
buff->h[5] = 0x9b05688c;
|
||||||
|
buff->h[6] = 0x1f83d9ab;
|
||||||
|
buff->h[7] = 0x5be0cd19;
|
||||||
|
buff->data_size = 0;
|
||||||
|
buff->chunk_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint32_t lekkit_k[64] = {
|
||||||
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||||
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||||
|
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||||
|
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||||
|
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||||
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||||
|
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||||
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||||
|
};
|
||||||
|
|
||||||
|
#define rotate_r(val, bits) (val >> bits | val << (32 - bits))
|
||||||
|
|
||||||
|
static void lekkit_sha256_calc_chunk(struct lekkit_sha256_buff* buff, const uint8_t* chunk) {
|
||||||
|
uint32_t w[64];
|
||||||
|
uint32_t tv[8];
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for (i=0; i<16; ++i){
|
||||||
|
w[i] = (uint32_t) chunk[0] << 24 | (uint32_t) chunk[1] << 16 | (uint32_t) chunk[2] << 8 | (uint32_t) chunk[3];
|
||||||
|
chunk += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=16; i<64; ++i){
|
||||||
|
uint32_t s0 = rotate_r(w[i-15], 7) ^ rotate_r(w[i-15], 18) ^ (w[i-15] >> 3);
|
||||||
|
uint32_t s1 = rotate_r(w[i-2], 17) ^ rotate_r(w[i-2], 19) ^ (w[i-2] >> 10);
|
||||||
|
w[i] = w[i-16] + s0 + w[i-7] + s1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 8; ++i)
|
||||||
|
tv[i] = buff->h[i];
|
||||||
|
|
||||||
|
for (i=0; i<64; ++i){
|
||||||
|
uint32_t S1 = rotate_r(tv[4], 6) ^ rotate_r(tv[4], 11) ^ rotate_r(tv[4], 25);
|
||||||
|
uint32_t ch = (tv[4] & tv[5]) ^ (~tv[4] & tv[6]);
|
||||||
|
uint32_t temp1 = tv[7] + S1 + ch + lekkit_k[i] + w[i];
|
||||||
|
uint32_t S0 = rotate_r(tv[0], 2) ^ rotate_r(tv[0], 13) ^ rotate_r(tv[0], 22);
|
||||||
|
uint32_t maj = (tv[0] & tv[1]) ^ (tv[0] & tv[2]) ^ (tv[1] & tv[2]);
|
||||||
|
uint32_t temp2 = S0 + maj;
|
||||||
|
|
||||||
|
tv[7] = tv[6];
|
||||||
|
tv[6] = tv[5];
|
||||||
|
tv[5] = tv[4];
|
||||||
|
tv[4] = tv[3] + temp1;
|
||||||
|
tv[3] = tv[2];
|
||||||
|
tv[2] = tv[1];
|
||||||
|
tv[1] = tv[0];
|
||||||
|
tv[0] = temp1 + temp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 8; ++i)
|
||||||
|
buff->h[i] += tv[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void lekkit_sha256_update(struct lekkit_sha256_buff* buff, const void* data, uint32_t size) {
|
||||||
|
const uint8_t* ptr = (const uint8_t*)data;
|
||||||
|
buff->data_size += size;
|
||||||
|
/* If there is data left in buff, concatenate it to process as new chunk */
|
||||||
|
if (size + buff->chunk_size >= 64) {
|
||||||
|
uint8_t tmp_chunk[64];
|
||||||
|
__builtin_memcpy(tmp_chunk, buff->last_chunk, buff->chunk_size);
|
||||||
|
__builtin_memcpy(tmp_chunk + buff->chunk_size, ptr, 64 - buff->chunk_size);
|
||||||
|
ptr += (64 - buff->chunk_size);
|
||||||
|
size -= (64 - buff->chunk_size);
|
||||||
|
buff->chunk_size = 0;
|
||||||
|
lekkit_sha256_calc_chunk(buff, tmp_chunk);
|
||||||
|
}
|
||||||
|
/* Run over data chunks */
|
||||||
|
while (size >= 64) {
|
||||||
|
lekkit_sha256_calc_chunk(buff, ptr);
|
||||||
|
ptr += 64;
|
||||||
|
size -= 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save remaining data in buff, will be reused on next call or finalize */
|
||||||
|
__builtin_memcpy(buff->last_chunk + buff->chunk_size, ptr, size);
|
||||||
|
buff->chunk_size += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lekkit_sha256_finalize(struct lekkit_sha256_buff* buff) {
|
||||||
|
buff->last_chunk[buff->chunk_size] = 0x80;
|
||||||
|
buff->chunk_size++;
|
||||||
|
__builtin_memset(buff->last_chunk + buff->chunk_size, 0, 64 - buff->chunk_size);
|
||||||
|
|
||||||
|
/* If there isn't enough space to fit int64, pad chunk with zeroes and prepare next chunk */
|
||||||
|
if (buff->chunk_size > 56) {
|
||||||
|
lekkit_sha256_calc_chunk(buff, buff->last_chunk);
|
||||||
|
__builtin_memset(buff->last_chunk, 0, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add total size as big-endian int64 x8 */
|
||||||
|
uint64_t size = buff->data_size * 8;
|
||||||
|
int i;
|
||||||
|
for (i = 8; i > 0; --i) {
|
||||||
|
buff->last_chunk[55+i] = size & 255;
|
||||||
|
size >>= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
lekkit_sha256_calc_chunk(buff, buff->last_chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lekkit_sha256_read(const struct lekkit_sha256_buff* buff, uint8_t* hash) {
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
hash[i*4] = (buff->h[i] >> 24) & 255;
|
||||||
|
hash[i*4 + 1] = (buff->h[i] >> 16) & 255;
|
||||||
|
hash[i*4 + 2] = (buff->h[i] >> 8) & 255;
|
||||||
|
hash[i*4 + 3] = buff->h[i] & 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct lekkit_sha256_buff lekkit_shared_ctx;
|
||||||
|
|
||||||
|
WASM_EXPORT void sha256(const void* data, uint32_t size) {
|
||||||
|
lekkit_sha256_init(&lekkit_shared_ctx);
|
||||||
|
lekkit_sha256_update(&lekkit_shared_ctx, data, size);
|
||||||
|
lekkit_sha256_finalize(&lekkit_shared_ctx);
|
||||||
|
lekkit_sha256_read(&lekkit_shared_ctx, shared_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef rotate_r
|
21
packages/wasm/lib/libdeflate/allocator.c
Normal file
21
packages/wasm/lib/libdeflate/allocator.c
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#include "wasm.h"
|
||||||
|
|
||||||
|
void *
|
||||||
|
libdeflate_aligned_malloc(size_t alignment, size_t size)
|
||||||
|
{
|
||||||
|
void *ptr = __malloc(sizeof(void *) + alignment - 1 + size);
|
||||||
|
|
||||||
|
if (ptr) {
|
||||||
|
void *orig_ptr = ptr;
|
||||||
|
|
||||||
|
ptr = (void *)ALIGN((uintptr_t)ptr + sizeof(void *), alignment);
|
||||||
|
((void **)ptr)[-1] = orig_ptr;
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
libdeflate_aligned_free(void *ptr)
|
||||||
|
{
|
||||||
|
__free((((void **)ptr)[-1]));
|
||||||
|
}
|
|
@ -5,14 +5,6 @@
|
||||||
#ifndef LIB_LIB_COMMON_H
|
#ifndef LIB_LIB_COMMON_H
|
||||||
#define LIB_LIB_COMMON_H
|
#define LIB_LIB_COMMON_H
|
||||||
|
|
||||||
#ifdef LIBDEFLATE_H
|
|
||||||
/*
|
|
||||||
* When building the library, LIBDEFLATEAPI needs to be defined properly before
|
|
||||||
* including libdeflate.h.
|
|
||||||
*/
|
|
||||||
# error "lib_common.h must always be included before libdeflate.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(LIBDEFLATE_DLL) && (defined(_WIN32) || defined(__CYGWIN__))
|
#if defined(LIBDEFLATE_DLL) && (defined(_WIN32) || defined(__CYGWIN__))
|
||||||
# define LIBDEFLATE_EXPORT_SYM __declspec(dllexport)
|
# define LIBDEFLATE_EXPORT_SYM __declspec(dllexport)
|
||||||
#elif defined(__GNUC__)
|
#elif defined(__GNUC__)
|
||||||
|
@ -38,9 +30,8 @@
|
||||||
#define LIBDEFLATEAPI LIBDEFLATE_EXPORT_SYM LIBDEFLATE_ALIGN_STACK
|
#define LIBDEFLATEAPI LIBDEFLATE_EXPORT_SYM LIBDEFLATE_ALIGN_STACK
|
||||||
|
|
||||||
#include "common_defs.h"
|
#include "common_defs.h"
|
||||||
|
#include "libdeflate.h"
|
||||||
extern void* __malloc(size_t size);
|
#include "wasm.h"
|
||||||
extern void __free(void* ptr);
|
|
||||||
|
|
||||||
void *libdeflate_aligned_malloc(size_t alignment, size_t size);
|
void *libdeflate_aligned_malloc(size_t alignment, size_t size);
|
||||||
void libdeflate_aligned_free(void *ptr);
|
void libdeflate_aligned_free(void *ptr);
|
||||||
|
@ -49,14 +40,4 @@ void libdeflate_aligned_free(void *ptr);
|
||||||
#define CONCAT_IMPL(a, b) a##b
|
#define CONCAT_IMPL(a, b) a##b
|
||||||
#define CONCAT(a, b) CONCAT_IMPL(a, b)
|
#define CONCAT(a, b) CONCAT_IMPL(a, b)
|
||||||
#define ADD_SUFFIX(name) CONCAT(name, SUFFIX)
|
#define ADD_SUFFIX(name) CONCAT(name, SUFFIX)
|
||||||
|
|
||||||
#ifdef LOGGING
|
|
||||||
void __debug(char* str);
|
|
||||||
|
|
||||||
#define DEBUG(str) __debug(str);
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define DEBUG(str)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* LIB_LIB_COMMON_H */
|
#endif /* LIB_LIB_COMMON_H */
|
|
@ -16,20 +16,6 @@ extern "C" {
|
||||||
#define LIBDEFLATE_VERSION_MINOR 19
|
#define LIBDEFLATE_VERSION_MINOR 19
|
||||||
#define LIBDEFLATE_VERSION_STRING "1.19"
|
#define LIBDEFLATE_VERSION_STRING "1.19"
|
||||||
|
|
||||||
/*
|
|
||||||
* Users of libdeflate.dll on Windows can define LIBDEFLATE_DLL to cause
|
|
||||||
* __declspec(dllimport) to be used. This should be done when it's easy to do.
|
|
||||||
* Otherwise it's fine to skip it, since it is a very minor performance
|
|
||||||
* optimization that is irrelevant for most use cases of libdeflate.
|
|
||||||
*/
|
|
||||||
#ifndef LIBDEFLATEAPI
|
|
||||||
# if defined(LIBDEFLATE_DLL) && (defined(_WIN32) || defined(__CYGWIN__))
|
|
||||||
# define LIBDEFLATEAPI __declspec(dllimport)
|
|
||||||
# else
|
|
||||||
# define LIBDEFLATEAPI
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* ========================================================================== */
|
/* ========================================================================== */
|
||||||
/* Compression */
|
/* Compression */
|
||||||
/* ========================================================================== */
|
/* ========================================================================== */
|
Binary file not shown.
|
@ -1,137 +0,0 @@
|
||||||
/*
|
|
||||||
* utils.c - utility functions for libdeflate
|
|
||||||
*
|
|
||||||
* Copyright 2016 Eric Biggers
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person
|
|
||||||
* obtaining a copy of this software and associated documentation
|
|
||||||
* files (the "Software"), to deal in the Software without
|
|
||||||
* restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the
|
|
||||||
* Software is furnished to do so, subject to the following
|
|
||||||
* conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
||||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
||||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "lib_common.h"
|
|
||||||
|
|
||||||
extern unsigned char __heap_base;
|
|
||||||
static size_t __heap_tail = (size_t) &__heap_base;
|
|
||||||
static size_t __heap_mark = (size_t) &__heap_base;
|
|
||||||
|
|
||||||
#define memory_size() __builtin_wasm_memory_size(0)
|
|
||||||
|
|
||||||
#define memory_grow(delta) __builtin_wasm_memory_grow(0, delta)
|
|
||||||
|
|
||||||
enum {
|
|
||||||
_mem_flag_used = 0xbf82583a,
|
|
||||||
_mem_flag_free = 0xab34d705
|
|
||||||
};
|
|
||||||
|
|
||||||
__attribute__((visibility("default"))) void* __malloc(size_t n) {
|
|
||||||
n += (8 - (n % 4)) % 4;
|
|
||||||
// check if size is enough
|
|
||||||
size_t total = __heap_tail + n + 3 * sizeof(size_t);
|
|
||||||
size_t size = memory_size() << 16;
|
|
||||||
if (total > size) {
|
|
||||||
memory_grow((total >> 16) - (size >> 16) + 1);
|
|
||||||
}
|
|
||||||
unsigned int r = __heap_tail;
|
|
||||||
*((size_t*) r) = n;
|
|
||||||
r += sizeof(size_t);
|
|
||||||
*((size_t*) r) =_mem_flag_used;
|
|
||||||
r += sizeof(size_t);
|
|
||||||
__heap_tail = r + n;
|
|
||||||
*((size_t*) __heap_tail) = n;
|
|
||||||
__heap_tail += sizeof(size_t);
|
|
||||||
return (void*) r;
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((visibility("default"))) void __free(void* p) {
|
|
||||||
size_t n;
|
|
||||||
// null case
|
|
||||||
if (!p) return;
|
|
||||||
size_t r=(size_t)p;
|
|
||||||
r -= sizeof(size_t);
|
|
||||||
// already free
|
|
||||||
if (*((size_t*) r) != _mem_flag_used) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// mark it as free
|
|
||||||
size_t flag = _mem_flag_free;
|
|
||||||
*((size_t*) r) = flag;
|
|
||||||
// calc ptr_tail
|
|
||||||
r -= sizeof(size_t);
|
|
||||||
n = *(size_t*) r; // size of current block
|
|
||||||
size_t ptr_tail = ((size_t) p) + n + sizeof(size_t);
|
|
||||||
// if not at tail return without moving __heap_tail
|
|
||||||
if (__heap_tail != ptr_tail) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
__heap_tail = r;
|
|
||||||
while (r > (size_t) &__heap_base) {
|
|
||||||
r -= sizeof(size_t);
|
|
||||||
n = *(size_t*) r; // size of previous block
|
|
||||||
r -= n;
|
|
||||||
r -= sizeof(size_t);
|
|
||||||
flag = *((size_t*) r);
|
|
||||||
if (flag != _mem_flag_free) break;
|
|
||||||
r -= sizeof(size_t);
|
|
||||||
n = *(size_t*) r; // size of current block
|
|
||||||
__heap_tail = r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void *
|
|
||||||
libdeflate_aligned_malloc(size_t alignment, size_t size)
|
|
||||||
{
|
|
||||||
void *ptr = __malloc(sizeof(void *) + alignment - 1 + size);
|
|
||||||
|
|
||||||
if (ptr) {
|
|
||||||
void *orig_ptr = ptr;
|
|
||||||
|
|
||||||
ptr = (void *)ALIGN((uintptr_t)ptr + sizeof(void *), alignment);
|
|
||||||
((void **)ptr)[-1] = orig_ptr;
|
|
||||||
}
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
libdeflate_aligned_free(void *ptr)
|
|
||||||
{
|
|
||||||
__free((((void **)ptr)[-1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef LOGGING
|
|
||||||
char* __debug_log = 0;
|
|
||||||
char __debug_log_pos = 0;
|
|
||||||
__attribute__((visibility("default"))) char* __get_debug_log() {
|
|
||||||
return __debug_log;
|
|
||||||
}
|
|
||||||
|
|
||||||
void __debug(char* str) {
|
|
||||||
if (!__debug_log) {
|
|
||||||
__debug_log = __malloc(1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
while (str[i] != '\0') {
|
|
||||||
__debug_log[__debug_log_pos++] = str[i++];
|
|
||||||
}
|
|
||||||
__debug_log[__debug_log_pos++] = '\n';
|
|
||||||
__debug_log[__debug_log_pos] = '\0';
|
|
||||||
}
|
|
||||||
#endif
|
|
74
packages/wasm/lib/utils/allocator.c
Normal file
74
packages/wasm/lib/utils/allocator.c
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#include "wasm.h"
|
||||||
|
|
||||||
|
extern unsigned char __heap_base;
|
||||||
|
static size_t __heap_tail = (size_t) &__heap_base;
|
||||||
|
static size_t __heap_mark = (size_t) &__heap_base;
|
||||||
|
|
||||||
|
#define memory_size() __builtin_wasm_memory_size(0)
|
||||||
|
|
||||||
|
#define memory_grow(delta) __builtin_wasm_memory_grow(0, delta)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
_mem_flag_used = 0xbf82583a,
|
||||||
|
_mem_flag_free = 0xab34d705
|
||||||
|
};
|
||||||
|
|
||||||
|
WASM_EXPORT void* __malloc(size_t n) {
|
||||||
|
n += (8 - (n % 4)) % 4;
|
||||||
|
// check if size is enough
|
||||||
|
size_t total = __heap_tail + n + 3 * sizeof(size_t);
|
||||||
|
size_t size = memory_size() << 16;
|
||||||
|
if (total > size) {
|
||||||
|
memory_grow((total >> 16) - (size >> 16) + 1);
|
||||||
|
}
|
||||||
|
unsigned int r = __heap_tail;
|
||||||
|
*((size_t*) r) = n;
|
||||||
|
r += sizeof(size_t);
|
||||||
|
*((size_t*) r) =_mem_flag_used;
|
||||||
|
r += sizeof(size_t);
|
||||||
|
__heap_tail = r + n;
|
||||||
|
*((size_t*) __heap_tail) = n;
|
||||||
|
__heap_tail += sizeof(size_t);
|
||||||
|
return (void*) r;
|
||||||
|
}
|
||||||
|
|
||||||
|
WASM_EXPORT void __free(void* p) {
|
||||||
|
size_t n;
|
||||||
|
// null case
|
||||||
|
if (!p) return;
|
||||||
|
size_t r=(size_t)p;
|
||||||
|
r -= sizeof(size_t);
|
||||||
|
// already free
|
||||||
|
if (*((size_t*) r) != _mem_flag_used) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// mark it as free
|
||||||
|
size_t flag = _mem_flag_free;
|
||||||
|
*((size_t*) r) = flag;
|
||||||
|
// calc ptr_tail
|
||||||
|
r -= sizeof(size_t);
|
||||||
|
n = *(size_t*) r; // size of current block
|
||||||
|
size_t ptr_tail = ((size_t) p) + n + sizeof(size_t);
|
||||||
|
// if not at tail return without moving __heap_tail
|
||||||
|
if (__heap_tail != ptr_tail) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
__heap_tail = r;
|
||||||
|
while (r > (size_t) &__heap_base) {
|
||||||
|
r -= sizeof(size_t);
|
||||||
|
n = *(size_t*) r; // size of previous block
|
||||||
|
r -= n;
|
||||||
|
r -= sizeof(size_t);
|
||||||
|
flag = *((size_t*) r);
|
||||||
|
if (flag != _mem_flag_free) break;
|
||||||
|
r -= sizeof(size_t);
|
||||||
|
n = *(size_t*) r; // size of current block
|
||||||
|
__heap_tail = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t shared_out[256];
|
||||||
|
|
||||||
|
WASM_EXPORT uint8_t* __get_shared_out() {
|
||||||
|
return shared_out;
|
||||||
|
}
|
|
@ -28,8 +28,6 @@
|
||||||
#ifndef COMMON_DEFS_H
|
#ifndef COMMON_DEFS_H
|
||||||
#define COMMON_DEFS_H
|
#define COMMON_DEFS_H
|
||||||
|
|
||||||
#include "libdeflate.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h> /* for size_t */
|
#include <stddef.h> /* for size_t */
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
18
packages/wasm/lib/wasm.h
Normal file
18
packages/wasm/lib/wasm.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef MTCUTE_WASM_H
|
||||||
|
#define MTCUTE_WASM_H
|
||||||
|
|
||||||
|
#include "common_defs.h"
|
||||||
|
|
||||||
|
#define WASM_EXPORT __attribute__((visibility("default")))
|
||||||
|
|
||||||
|
// see utils/allocator.c
|
||||||
|
extern void* __malloc(size_t size);
|
||||||
|
extern void __free(void* ptr);
|
||||||
|
|
||||||
|
#define memset(p,v,n) __builtin_memset(p,v,n)
|
||||||
|
#define memcpy(d,s,n) __builtin_memcpy(d,s,n)
|
||||||
|
|
||||||
|
// more than enough for most of our cases
|
||||||
|
extern uint8_t shared_out[256];
|
||||||
|
|
||||||
|
#endif // MTCUTE_WASM_H
|
|
@ -6,11 +6,17 @@ export * from './types.js'
|
||||||
let wasm!: MtcuteWasmModule
|
let wasm!: MtcuteWasmModule
|
||||||
let compressor!: number
|
let compressor!: number
|
||||||
let decompressor!: number
|
let decompressor!: number
|
||||||
|
let sharedOutPtr!: number
|
||||||
|
let sharedKeyPtr!: number
|
||||||
|
let sharedIvPtr!: number
|
||||||
let cachedUint8Memory: Uint8Array | null = null
|
let cachedUint8Memory: Uint8Array | null = null
|
||||||
|
|
||||||
function initCommon() {
|
function initCommon() {
|
||||||
compressor = wasm.libdeflate_alloc_compressor(6)
|
compressor = wasm.libdeflate_alloc_compressor(6)
|
||||||
decompressor = wasm.libdeflate_alloc_decompressor()
|
decompressor = wasm.libdeflate_alloc_decompressor()
|
||||||
|
sharedOutPtr = wasm.__get_shared_out()
|
||||||
|
sharedKeyPtr = wasm.__get_shared_key_buffer()
|
||||||
|
sharedIvPtr = wasm.__get_shared_iv_buffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUint8Memory() {
|
function getUint8Memory() {
|
||||||
|
@ -58,7 +64,9 @@ export async function initAsync(input?: InitInput): Promise<void> {
|
||||||
export function deflateMaxSize(bytes: Uint8Array, size: number): Uint8Array | null {
|
export function deflateMaxSize(bytes: Uint8Array, size: number): Uint8Array | null {
|
||||||
const outputPtr = wasm.__malloc(size)
|
const outputPtr = wasm.__malloc(size)
|
||||||
const inputPtr = wasm.__malloc(bytes.length)
|
const inputPtr = wasm.__malloc(bytes.length)
|
||||||
getUint8Memory().set(bytes, inputPtr)
|
|
||||||
|
const mem = getUint8Memory()
|
||||||
|
mem.set(bytes, inputPtr)
|
||||||
|
|
||||||
const written = wasm.libdeflate_zlib_compress(compressor, inputPtr, bytes.length, outputPtr, size)
|
const written = wasm.libdeflate_zlib_compress(compressor, inputPtr, bytes.length, outputPtr, size)
|
||||||
wasm.__free(inputPtr)
|
wasm.__free(inputPtr)
|
||||||
|
@ -69,7 +77,7 @@ export function deflateMaxSize(bytes: Uint8Array, size: number): Uint8Array | nu
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = getUint8Memory().slice(outputPtr, outputPtr + written)
|
const result = mem.slice(outputPtr, outputPtr + written)
|
||||||
wasm.__free(outputPtr)
|
wasm.__free(outputPtr)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -109,20 +117,18 @@ export function gunzip(bytes: Uint8Array): Uint8Array {
|
||||||
* @param iv initialization vector (32 bytes)
|
* @param iv initialization vector (32 bytes)
|
||||||
*/
|
*/
|
||||||
export function ige256Encrypt(data: Uint8Array, key: Uint8Array, iv: Uint8Array): Uint8Array {
|
export function ige256Encrypt(data: Uint8Array, key: Uint8Array, iv: Uint8Array): Uint8Array {
|
||||||
const ptr = wasm.__malloc(key.length + iv.length + data.length + data.length)
|
const ptr = wasm.__malloc(data.length + data.length)
|
||||||
|
|
||||||
const keyPtr = ptr
|
const inputPtr = ptr
|
||||||
const ivPtr = ptr + key.length
|
|
||||||
const inputPtr = ivPtr + iv.length
|
|
||||||
const outputPtr = inputPtr + data.length
|
const outputPtr = inputPtr + data.length
|
||||||
|
|
||||||
const mem = getUint8Memory()
|
const mem = getUint8Memory()
|
||||||
mem.set(data, inputPtr)
|
mem.set(data, inputPtr)
|
||||||
mem.set(key, keyPtr)
|
mem.set(key, sharedKeyPtr)
|
||||||
mem.set(iv, ivPtr)
|
mem.set(iv, sharedIvPtr)
|
||||||
|
|
||||||
wasm.ige256_encrypt(inputPtr, data.length, keyPtr, ivPtr, outputPtr)
|
wasm.ige256_encrypt(inputPtr, data.length, outputPtr)
|
||||||
const result = getUint8Memory().slice(outputPtr, outputPtr + data.length)
|
const result = mem.slice(outputPtr, outputPtr + data.length)
|
||||||
|
|
||||||
wasm.__free(ptr)
|
wasm.__free(ptr)
|
||||||
|
|
||||||
|
@ -137,21 +143,19 @@ export function ige256Encrypt(data: Uint8Array, key: Uint8Array, iv: Uint8Array)
|
||||||
* @param iv initialization vector (32 bytes)
|
* @param iv initialization vector (32 bytes)
|
||||||
*/
|
*/
|
||||||
export function ige256Decrypt(data: Uint8Array, key: Uint8Array, iv: Uint8Array): Uint8Array {
|
export function ige256Decrypt(data: Uint8Array, key: Uint8Array, iv: Uint8Array): Uint8Array {
|
||||||
const ptr = wasm.__malloc(key.length + iv.length + data.length + data.length)
|
const ptr = wasm.__malloc(data.length + data.length)
|
||||||
|
|
||||||
const keyPtr = ptr
|
const inputPtr = ptr
|
||||||
const ivPtr = ptr + key.length
|
|
||||||
const inputPtr = ivPtr + iv.length
|
|
||||||
const outputPtr = inputPtr + data.length
|
const outputPtr = inputPtr + data.length
|
||||||
|
|
||||||
const mem = getUint8Memory()
|
const mem = getUint8Memory()
|
||||||
mem.set(data, inputPtr)
|
mem.set(data, inputPtr)
|
||||||
mem.set(key, keyPtr)
|
mem.set(key, sharedKeyPtr)
|
||||||
mem.set(iv, ivPtr)
|
mem.set(iv, sharedIvPtr)
|
||||||
|
|
||||||
wasm.ige256_decrypt(inputPtr, data.length, keyPtr, ivPtr, outputPtr)
|
wasm.ige256_decrypt(inputPtr, data.length, outputPtr)
|
||||||
|
const result = mem.slice(outputPtr, outputPtr + data.length)
|
||||||
|
|
||||||
const result = getUint8Memory().slice(outputPtr, outputPtr + data.length)
|
|
||||||
wasm.__free(ptr)
|
wasm.__free(ptr)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -163,15 +167,10 @@ export function ige256Decrypt(data: Uint8Array, key: Uint8Array, iv: Uint8Array)
|
||||||
* > **Note**: `freeCtr256` must be called on the returned context when it's no longer needed
|
* > **Note**: `freeCtr256` must be called on the returned context when it's no longer needed
|
||||||
*/
|
*/
|
||||||
export function createCtr256(key: Uint8Array, iv: Uint8Array) {
|
export function createCtr256(key: Uint8Array, iv: Uint8Array) {
|
||||||
const keyPtr = wasm.__malloc(key.length)
|
getUint8Memory().set(key, sharedKeyPtr)
|
||||||
const ivPtr = wasm.__malloc(iv.length)
|
getUint8Memory().set(iv, sharedIvPtr)
|
||||||
getUint8Memory().set(key, keyPtr)
|
|
||||||
getUint8Memory().set(iv, ivPtr)
|
|
||||||
|
|
||||||
const ctx = wasm.ctr256_alloc(keyPtr, ivPtr)
|
return wasm.ctr256_alloc()
|
||||||
// pointers are "moved" and will be handled by c code
|
|
||||||
|
|
||||||
return ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -203,6 +202,42 @@ export function ctr256(ctx: number, data: Uint8Array): Uint8Array {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate a SHA-256 hash
|
||||||
|
*
|
||||||
|
* @param data data to hash
|
||||||
|
*/
|
||||||
|
export function sha256(data: Uint8Array): Uint8Array {
|
||||||
|
const { __malloc, __free } = wasm
|
||||||
|
const inputPtr = __malloc(data.length)
|
||||||
|
|
||||||
|
const mem = getUint8Memory()
|
||||||
|
mem.set(data, inputPtr)
|
||||||
|
|
||||||
|
wasm.sha256(inputPtr, data.length)
|
||||||
|
__free(inputPtr)
|
||||||
|
|
||||||
|
return mem.slice(sharedOutPtr, sharedOutPtr + 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate a SHA-1 hash
|
||||||
|
*
|
||||||
|
* @param data data to hash
|
||||||
|
*/
|
||||||
|
export function sha1(data: Uint8Array): Uint8Array {
|
||||||
|
const { __malloc, __free } = wasm
|
||||||
|
const inputPtr = __malloc(data.length)
|
||||||
|
|
||||||
|
const mem = getUint8Memory()
|
||||||
|
mem.set(data, inputPtr)
|
||||||
|
|
||||||
|
wasm.sha1(inputPtr, data.length)
|
||||||
|
__free(inputPtr)
|
||||||
|
|
||||||
|
return mem.slice(sharedOutPtr, sharedOutPtr + 20)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the WASM module instance.
|
* Get the WASM module instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -2,6 +2,11 @@ export interface MtcuteWasmModule {
|
||||||
memory: WebAssembly.Memory
|
memory: WebAssembly.Memory
|
||||||
__malloc: (size: number) => number
|
__malloc: (size: number) => number
|
||||||
__free: (ptr: number) => void
|
__free: (ptr: number) => void
|
||||||
|
|
||||||
|
__get_shared_out: () => number
|
||||||
|
__get_shared_key_buffer: () => number
|
||||||
|
__get_shared_iv_buffer: () => number
|
||||||
|
|
||||||
libdeflate_alloc_decompressor: () => number
|
libdeflate_alloc_decompressor: () => number
|
||||||
libdeflate_alloc_compressor: (level: number) => number
|
libdeflate_alloc_compressor: (level: number) => number
|
||||||
|
|
||||||
|
@ -11,13 +16,16 @@ export interface MtcuteWasmModule {
|
||||||
|
|
||||||
libdeflate_zlib_compress: (ctx: number, src: number, srcLen: number, dst: number, dstLen: number) => number
|
libdeflate_zlib_compress: (ctx: number, src: number, srcLen: number, dst: number, dstLen: number) => number
|
||||||
|
|
||||||
ige256_encrypt: (data: number, dataLen: number, key: number, iv: number, out: number) => void
|
ige256_encrypt: (data: number, dataLen: number, out: number) => void
|
||||||
|
|
||||||
ige256_decrypt: (data: number, dataLen: number, key: number, iv: number, out: number) => void
|
ige256_decrypt: (data: number, dataLen: number, out: number) => void
|
||||||
|
|
||||||
ctr256_alloc: (key: number, iv: number) => number
|
ctr256_alloc: () => number
|
||||||
ctr256_free: (ctx: number) => void
|
ctr256_free: (ctx: number) => void
|
||||||
ctr256: (ctx: number, data: number, dataLen: number, out: number) => number
|
ctr256: (ctx: number, data: number, dataLen: number, out: number) => number
|
||||||
|
|
||||||
|
sha256: (data: number, dataLen: number) => void
|
||||||
|
sha1: (data: number, dataLen: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SyncInitInput = BufferSource | WebAssembly.Module
|
export type SyncInitInput = BufferSource | WebAssembly.Module
|
||||||
|
|
49
packages/wasm/tests/hash.spec.ts
Normal file
49
packages/wasm/tests/hash.spec.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/* eslint-disable no-restricted-globals */
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import { before, describe } from 'mocha'
|
||||||
|
|
||||||
|
import { __getWasm, initAsync, sha1, sha256 } from '../src/index.js'
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await initAsync()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('sha256', () => {
|
||||||
|
it('should correctly calculate sha-256 hash', () => {
|
||||||
|
const hash = sha256(Buffer.from('abc'))
|
||||||
|
|
||||||
|
expect(Buffer.from(hash).toString('hex')).to.equal(
|
||||||
|
'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not leak memory', () => {
|
||||||
|
const mem = __getWasm().memory.buffer
|
||||||
|
const memSize = mem.byteLength
|
||||||
|
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
sha256(Buffer.from('abc'))
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(mem.byteLength).to.equal(memSize)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('sha1', () => {
|
||||||
|
it('should correctly calculate sha-1 hash', () => {
|
||||||
|
const hash = sha1(Buffer.from('abc'))
|
||||||
|
|
||||||
|
expect(Buffer.from(hash).toString('hex')).to.equal('a9993e364706816aba3e25717850c26c9cd0d89d')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not leak memory', () => {
|
||||||
|
const mem = __getWasm().memory.buffer
|
||||||
|
const memSize = mem.byteLength
|
||||||
|
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
sha1(Buffer.from('abc'))
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(mem.byteLength).to.equal(memSize)
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in a new issue