UNPKG

postgrejs

Version:

Professional PostgreSQL client NodeJS

109 lines (108 loc) 4.43 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SASL = void 0; const tslib_1 = require("tslib"); const crypto_1 = tslib_1.__importDefault(require("crypto")); var SASL; (function (SASL) { const CLIENT_KEY = 'Client Key'; const SERVER_KEY = 'Server Key'; const GS2_HEADER = 'n,,'; function createSession(username, mechanism) { const nonce = crypto_1.default.randomBytes(18).toString('base64'); const clientFirstMessage = `${GS2_HEADER}${firstMessageBare(username, nonce)}`; return { username, mechanism, nonce, clientFirstMessage, }; } SASL.createSession = createSession; function continueSession(session, password, data) { const s = data.toString(); const items = s.split(','); let nonce = ''; let salt = ''; let iteration = 0; for (const i of items) { switch (i[0]) { case 'r': nonce = i.substring(2); break; case 's': salt = i.substring(2); break; case 'i': iteration = parseInt(i.substring(2), 10); break; default: break; } } if (!nonce) throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce missing'); if (!salt) throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt missing'); if (!iteration) throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration missing'); if (!nonce.startsWith(session.nonce)) throw new Error('SASL: Server nonce does not start with client nonce'); const serverFirstMessage = `r=${nonce},s=${salt},i=${iteration}`; const clientFinalMessageWithoutProof = `c=${encode64(GS2_HEADER)},r=${nonce}`; const authMessage = `${firstMessageBare(session.username, session.nonce)},${serverFirstMessage},${clientFinalMessageWithoutProof}`; const saltPass = hi(password, salt, iteration); const clientKey = hmac(saltPass, CLIENT_KEY); const storedKey = hash(clientKey); const clientSignature = hmac(storedKey, authMessage); const clientProofBytes = xor(clientKey, clientSignature); const clientProof = clientProofBytes.toString('base64'); const serverKey = hmac(saltPass, SERVER_KEY); const serverSignatureBytes = hmac(serverKey, authMessage); session.serverSignature = serverSignatureBytes.toString('base64'); session.clientFinalMessage = clientFinalMessageWithoutProof + ',p=' + clientProof; } SASL.continueSession = continueSession; function finalizeSession(session, data) { let serverSignature = ''; const arr = data.split(','); for (const s of arr) { if (s[0] === 'v') serverSignature = s.substr(2); } if (serverSignature !== session.serverSignature) throw new Error('SASL: Server signature does not match'); } SASL.finalizeSession = finalizeSession; const firstMessageBare = function (username, nonce) { return `n=${username},r=${nonce}`; }; /** * Hi() is, essentially, PBKDF2 [RFC2898] with HMAC() as the * pseudorandom function (PRF) and with dkLen == output length of * HMAC() == output length of H() */ const hi = function (text, salt, iterations) { return crypto_1.default.pbkdf2Sync(text, Buffer.from(salt, 'base64'), iterations, 32, 'sha256'); }; const encode64 = (str) => Buffer.from(str).toString('base64'); const hmac = function (key, msg) { return crypto_1.default.createHmac('sha256', key).update(msg).digest(); }; const hash = function (data) { return crypto_1.default.createHash('sha256').update(data).digest(); }; const xor = function (a, b) { a = Buffer.isBuffer(a) ? a : Buffer.from(a); b = Buffer.isBuffer(b) ? b : Buffer.from(b); if (a.length !== b.length) throw new Error('Buffers must be of the same length'); const l = a.length; const out = Buffer.allocUnsafe(l); for (let i = 0; i < l; i++) { out[i] = a[i] ^ b[i]; } return out; }; })(SASL || (exports.SASL = SASL = {}));