فهرست منبع

Start working on password

painor 5 سال پیش
والد
کامیت
44c7e72341
1فایلهای تغییر یافته به همراه222 افزوده شده و 0 حذف شده
  1. 222 0
      gramjs/Password.js

+ 222 - 0
gramjs/Password.js

@@ -0,0 +1,222 @@
+const Factorizator = require('./crypto/Factorizator')
+const { types } = require('./tl')
+const { readBigIntFromBuffer, readBufferFromBigInt, sha256, modExp } = require('./Helpers')
+const crypto = require('crypto')
+const SIZE_FOR_HASH = 256
+
+/**
+ *
+ *
+ * @param prime{BigInt}
+ * @param g{BigInt}
+ */
+function checkPrimeAndGoodCheck(prime, g) {
+    const goodPrimeBitsCount = 2048
+    if (prime < 0 || prime.toString('2').length !== goodPrimeBitsCount) {
+        throw new Error(`bad prime count ${prime.toString('2').length},expected ${goodPrimeBitsCount}`)
+    }
+    // TODO this is kinda slow
+    if (Factorizator.factorize(prime)[0] !== 1) {
+        throw new Error('give "prime" is not prime')
+    }
+    if (g === 2n) {
+        if (prime % 8n !== 7n) {
+            throw new Error(`bad g ${g}, mod8 ${prime % 8}`)
+        }
+    } else if (g === 3n) {
+        if (prime % 3n !== 2n) {
+            throw new Error(`bad g ${g}, mod3 ${prime % 3}`)
+        }
+        // eslint-disable-next-line no-empty
+    } else if (g === 4n) {
+
+    } else if (g === 5n) {
+        if (!([1n, 4n].includes(prime % 5n))) {
+            throw new Error(`bad g ${g}, mod8 ${prime % 5}`)
+        }
+    } else if (g === 6n) {
+        if (!([19n, 23n].includes(prime % 24n))) {
+            throw new Error(`bad g ${g}, mod8 ${prime % 24}`)
+        }
+    } else if (g === 7n) {
+        if (!([3n, 5n, 6n].includes(prime % 7n))) {
+            throw new Error(`bad g ${g}, mod8 ${prime % 7}`)
+        }
+    } else {
+        throw new Error(`bad g ${g}`)
+    }
+    const primeSub1Div2 = (prime - 1n) / 2n
+    if (Factorizator.factorize(primeSub1Div2)[0] !== 1) {
+        throw new Error('(prime - 1) // 2 is not prime')
+    }
+}
+
+/**
+ *
+ * @param primeBytes{Buffer}
+ * @param g{number}
+ */
+function checkPrimeAndGood(primeBytes, g) {
+    const goodPrime = Buffer.from([
+        0xC7, 0x1C, 0xAE, 0xB9, 0xC6, 0xB1, 0xC9, 0x04, 0x8E, 0x6C, 0x52, 0x2F, 0x70, 0xF1, 0x3F, 0x73,
+        0x98, 0x0D, 0x40, 0x23, 0x8E, 0x3E, 0x21, 0xC1, 0x49, 0x34, 0xD0, 0x37, 0x56, 0x3D, 0x93, 0x0F,
+        0x48, 0x19, 0x8A, 0x0A, 0xA7, 0xC1, 0x40, 0x58, 0x22, 0x94, 0x93, 0xD2, 0x25, 0x30, 0xF4, 0xDB,
+        0xFA, 0x33, 0x6F, 0x6E, 0x0A, 0xC9, 0x25, 0x13, 0x95, 0x43, 0xAE, 0xD4, 0x4C, 0xCE, 0x7C, 0x37,
+        0x20, 0xFD, 0x51, 0xF6, 0x94, 0x58, 0x70, 0x5A, 0xC6, 0x8C, 0xD4, 0xFE, 0x6B, 0x6B, 0x13, 0xAB,
+        0xDC, 0x97, 0x46, 0x51, 0x29, 0x69, 0x32, 0x84, 0x54, 0xF1, 0x8F, 0xAF, 0x8C, 0x59, 0x5F, 0x64,
+        0x24, 0x77, 0xFE, 0x96, 0xBB, 0x2A, 0x94, 0x1D, 0x5B, 0xCD, 0x1D, 0x4A, 0xC8, 0xCC, 0x49, 0x88,
+        0x07, 0x08, 0xFA, 0x9B, 0x37, 0x8E, 0x3C, 0x4F, 0x3A, 0x90, 0x60, 0xBE, 0xE6, 0x7C, 0xF9, 0xA4,
+        0xA4, 0xA6, 0x95, 0x81, 0x10, 0x51, 0x90, 0x7E, 0x16, 0x27, 0x53, 0xB5, 0x6B, 0x0F, 0x6B, 0x41,
+        0x0D, 0xBA, 0x74, 0xD8, 0xA8, 0x4B, 0x2A, 0x14, 0xB3, 0x14, 0x4E, 0x0E, 0xF1, 0x28, 0x47, 0x54,
+        0xFD, 0x17, 0xED, 0x95, 0x0D, 0x59, 0x65, 0xB4, 0xB9, 0xDD, 0x46, 0x58, 0x2D, 0xB1, 0x17, 0x8D,
+        0x16, 0x9C, 0x6B, 0xC4, 0x65, 0xB0, 0xD6, 0xFF, 0x9C, 0xA3, 0x92, 0x8F, 0xEF, 0x5B, 0x9A, 0xE4,
+        0xE4, 0x18, 0xFC, 0x15, 0xE8, 0x3E, 0xBE, 0xA0, 0xF8, 0x7F, 0xA9, 0xFF, 0x5E, 0xED, 0x70, 0x05,
+        0x0D, 0xED, 0x28, 0x49, 0xF4, 0x7B, 0xF9, 0x59, 0xD9, 0x56, 0x85, 0x0C, 0xE9, 0x29, 0x85, 0x1F,
+        0x0D, 0x81, 0x15, 0xF6, 0x35, 0xB1, 0x05, 0xEE, 0x2E, 0x4E, 0x15, 0xD0, 0x4B, 0x24, 0x54, 0xBF,
+        0x6F, 0x4F, 0xAD, 0xF0, 0x34, 0xB1, 0x04, 0x03, 0x11, 0x9C, 0xD8, 0xE3, 0xB9, 0x2F, 0xCC, 0x5B,
+    ])
+    console.log('the good prime is end')
+    if (goodPrime.equals(primeBytes)) {
+        if ([3, 4, 5, 7].includes(g)) {
+            return // It's good
+        }
+    }
+    console.log()
+    checkPrimeAndGoodCheck(readBigIntFromBuffer(primeBytes, false), g)
+}
+
+/**
+ *
+ * @param number{BigInt}
+ * @param p{BigInt}
+ * @returns {boolean}
+ */
+function isGoodLarge(number, p) {
+    return (number > 0n && (p - number) > 0n)
+}
+
+/**
+ *
+ * @param number {Buffer}
+ * @returns {Buffer}
+ */
+function numBytesForHash(number) {
+    return Buffer.concat([Buffer.from([SIZE_FOR_HASH - number.length]), number])
+}
+
+/**
+ *
+ * @param g {Buffer}
+ * @returns {Buffer}
+ */
+function bigNumForHash(g) {
+    return readBufferFromBigInt(g, SIZE_FOR_HASH, false)
+}
+
+/**
+ *
+ * @param modexp
+ * @param prime
+ * @returns {Boolean}
+ */
+function isGoodModExpFirst(modexp, prime) {
+    const diff = prime - modexp
+    const minDiffBitsCount = 2048 - 64
+    const maxModExpSize = 256
+    return !(diff < 0 || diff.toString('2').length < minDiffBitsCount ||
+        modexp.toString('2').length < minDiffBitsCount ||
+        Math.floor(modexp.toString('2').length + 7) / 8 > maxModExpSize)
+}
+
+function xor(a, b) {
+    const length = Math.min(a.length, b.length)
+
+    for (let i = 0; i < length; i++) {
+        a[i] = a[i] ^ b[i]
+    }
+
+    return a
+}
+
+/**
+ *
+ * @param password{Buffer}
+ * @param salt{Buffer}
+ * @param iterations{number}
+ * @returns {*}
+ */
+function pbkdf2sha512(password, salt, iterations) {
+    return crypto.pbkdf2Sync(password, salt, iterations, 64, 'sha512')
+}
+
+/**
+ *
+ * @param algo {types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow}
+ * @param password
+ * @returns {Buffer|*}
+ */
+function computeHash(algo, password) {
+    const hash1 = sha256(Buffer.concat([algo.salt1, Buffer.from(password, 'utf-8'), algo.salt1]))
+    const hash2 = sha256(Buffer.concat([algo.salt2, hash1, algo.salt2]))
+    const hash3 = pbkdf2sha512(hash2, algo.salt1, 100000)
+    return sha256(Buffer.concat([algo.salt2, hash3, algo.salt2]))
+}
+
+/**
+ *
+ * @param algo {types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow}
+ * @param password
+ */
+function computeDigest(algo, password) {
+    try {
+        console.log('checking good')
+        checkPrimeAndGood(algo.p, algo.g)
+        console.log('is good')
+    } catch (e) {
+        throw new Error('bad p/g in password')
+    }
+
+    const value = modExp(BigInt(algo.g),
+        readBigIntFromBuffer(computeHash(algo, password), false),
+        readBigIntFromBuffer(algo.p, false))
+    return bigNumForHash(value)
+}
+
+/**
+ *
+ * @param request {types.account.Password}
+ * @param password {string}
+ */
+function computeCheck(request, password) {
+    const algo = request.currentAlgo
+    if (!(algo instanceof types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow)) {
+        throw new Error(`Unsupported password algorithm ${algo.constructor.name}`)
+    }
+
+    const pwHash = computeHash(algo, password)
+
+    const p = readBigIntFromBuffer(algo.p, false)
+    const g = algo.g
+    const B = readBigIntFromBuffer(request.srp_B, false)
+    try {
+        checkPrimeAndGood(algo.p, g)
+    } catch (e) {
+        throw new Error('bad /g in password')
+    }
+    if (!isGoodLarge(B, p)) {
+        throw new Error('bad b in check')
+    }
+    const x = readBigIntFromBuffer(pwHash, false)
+    const pForHash = numBytesForHash(algo.p)
+    const gForHash = bigNumForHash(g)
+    const bForHash = numBytesForHash(request.srp_B)
+    const g_x = modExp(g, x, p)
+    const k = readBigIntFromBuffer(sha256(Buffer.concat([pForHash, gForHash])), false)
+    const kg_x = (k * g_x) % p
+
+}
+
+module.exports = {
+    computeHash,
+    computeDigest,
+}