Comparing version 8.5.1 to 8.6.0
@@ -5,3 +5,2 @@ 'use strict' | ||
var EventEmitter = require('events').EventEmitter | ||
var util = require('util') | ||
@@ -8,0 +7,0 @@ const { parse, serialize } = require('pg-protocol') |
@@ -7,2 +7,3 @@ 'use strict' | ||
var Pool = require('pg-pool') | ||
const { DatabaseError } = require('pg-protocol') | ||
@@ -25,2 +26,3 @@ const poolFactory = (Client) => { | ||
this.types = require('pg-types') | ||
this.DatabaseError = DatabaseError | ||
} | ||
@@ -27,0 +29,0 @@ |
158
lib/sasl.js
@@ -23,7 +23,15 @@ 'use strict' | ||
} | ||
if (typeof password !== 'string') { | ||
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string') | ||
} | ||
if (typeof serverData !== 'string') { | ||
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: serverData must be a string') | ||
} | ||
const sv = extractVariablesFromFirstServerMessage(serverData) | ||
const sv = parseServerFirstMessage(serverData) | ||
if (!sv.nonce.startsWith(session.clientNonce)) { | ||
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce does not start with client nonce') | ||
} else if (sv.nonce.length === session.clientNonce.length) { | ||
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce is too short') | ||
} | ||
@@ -35,4 +43,4 @@ | ||
var clientKey = createHMAC(saltedPassword, 'Client Key') | ||
var storedKey = crypto.createHash('sha256').update(clientKey).digest() | ||
var clientKey = hmacSha256(saltedPassword, 'Client Key') | ||
var storedKey = sha256(clientKey) | ||
@@ -46,8 +54,8 @@ var clientFirstMessageBare = 'n=*,r=' + session.clientNonce | ||
var clientSignature = createHMAC(storedKey, authMessage) | ||
var clientSignature = hmacSha256(storedKey, authMessage) | ||
var clientProofBytes = xorBuffers(clientKey, clientSignature) | ||
var clientProof = clientProofBytes.toString('base64') | ||
var serverKey = createHMAC(saltedPassword, 'Server Key') | ||
var serverSignatureBytes = createHMAC(serverKey, authMessage) | ||
var serverKey = hmacSha256(saltedPassword, 'Server Key') | ||
var serverSignatureBytes = hmacSha256(serverKey, authMessage) | ||
@@ -63,15 +71,8 @@ session.message = 'SASLResponse' | ||
} | ||
if (typeof serverData !== 'string') { | ||
throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: serverData must be a string') | ||
} | ||
var serverSignature | ||
const { serverSignature } = parseServerFinalMessage(serverData) | ||
String(serverData) | ||
.split(',') | ||
.forEach(function (part) { | ||
switch (part[0]) { | ||
case 'v': | ||
serverSignature = part.substr(2) | ||
break | ||
} | ||
}) | ||
if (serverSignature !== session.serverSignature) { | ||
@@ -82,32 +83,72 @@ throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature does not match') | ||
function extractVariablesFromFirstServerMessage(data) { | ||
var nonce, salt, iteration | ||
/** | ||
* printable = %x21-2B / %x2D-7E | ||
* ;; Printable ASCII except ",". | ||
* ;; Note that any "printable" is also | ||
* ;; a valid "value". | ||
*/ | ||
function isPrintableChars(text) { | ||
if (typeof text !== 'string') { | ||
throw new TypeError('SASL: text must be a string') | ||
} | ||
return text | ||
.split('') | ||
.map((_, i) => text.charCodeAt(i)) | ||
.every((c) => (c >= 0x21 && c <= 0x2b) || (c >= 0x2d && c <= 0x7e)) | ||
} | ||
String(data) | ||
.split(',') | ||
.forEach(function (part) { | ||
switch (part[0]) { | ||
case 'r': | ||
nonce = part.substr(2) | ||
break | ||
case 's': | ||
salt = part.substr(2) | ||
break | ||
case 'i': | ||
iteration = parseInt(part.substr(2), 10) | ||
break | ||
/** | ||
* base64-char = ALPHA / DIGIT / "/" / "+" | ||
* | ||
* base64-4 = 4base64-char | ||
* | ||
* base64-3 = 3base64-char "=" | ||
* | ||
* base64-2 = 2base64-char "==" | ||
* | ||
* base64 = *base64-4 [base64-3 / base64-2] | ||
*/ | ||
function isBase64(text) { | ||
return /^(?:[a-zA-Z0-9+/]{4})*(?:[a-zA-Z0-9+/]{2}==|[a-zA-Z0-9+/]{3}=)?$/.test(text) | ||
} | ||
function parseAttributePairs(text) { | ||
if (typeof text !== 'string') { | ||
throw new TypeError('SASL: attribute pairs text must be a string') | ||
} | ||
return new Map( | ||
text.split(',').map((attrValue) => { | ||
if (!/^.=/.test(attrValue)) { | ||
throw new Error('SASL: Invalid attribute pair entry') | ||
} | ||
const name = attrValue[0] | ||
const value = attrValue.substring(2) | ||
return [name, value] | ||
}) | ||
) | ||
} | ||
function parseServerFirstMessage(data) { | ||
const attrPairs = parseAttributePairs(data) | ||
const nonce = attrPairs.get('r') | ||
if (!nonce) { | ||
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce missing') | ||
} else if (!isPrintableChars(nonce)) { | ||
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce must only contain printable characters') | ||
} | ||
const salt = attrPairs.get('s') | ||
if (!salt) { | ||
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt missing') | ||
} else if (!isBase64(salt)) { | ||
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt must be base64') | ||
} | ||
if (!iteration) { | ||
const iterationText = attrPairs.get('i') | ||
if (!iterationText) { | ||
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration missing') | ||
} else if (!/^[1-9][0-9]*$/.test(iterationText)) { | ||
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: invalid iteration count') | ||
} | ||
const iteration = parseInt(iterationText, 10) | ||
@@ -121,19 +162,36 @@ return { | ||
function parseServerFinalMessage(serverData) { | ||
const attrPairs = parseAttributePairs(serverData) | ||
const serverSignature = attrPairs.get('v') | ||
if (!serverSignature) { | ||
throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature is missing') | ||
} else if (!isBase64(serverSignature)) { | ||
throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature must be base64') | ||
} | ||
return { | ||
serverSignature, | ||
} | ||
} | ||
function xorBuffers(a, b) { | ||
if (!Buffer.isBuffer(a)) a = Buffer.from(a) | ||
if (!Buffer.isBuffer(b)) b = Buffer.from(b) | ||
var res = [] | ||
if (a.length > b.length) { | ||
for (var i = 0; i < b.length; i++) { | ||
res.push(a[i] ^ b[i]) | ||
} | ||
} else { | ||
for (var j = 0; j < a.length; j++) { | ||
res.push(a[j] ^ b[j]) | ||
} | ||
if (!Buffer.isBuffer(a)) { | ||
throw new TypeError('first argument must be a Buffer') | ||
} | ||
return Buffer.from(res) | ||
if (!Buffer.isBuffer(b)) { | ||
throw new TypeError('second argument must be a Buffer') | ||
} | ||
if (a.length !== b.length) { | ||
throw new Error('Buffer lengths must match') | ||
} | ||
if (a.length === 0) { | ||
throw new Error('Buffers cannot be empty') | ||
} | ||
return Buffer.from(a.map((_, i) => a[i] ^ b[i])) | ||
} | ||
function createHMAC(key, msg) { | ||
function sha256(text) { | ||
return crypto.createHash('sha256').update(text).digest() | ||
} | ||
function hmacSha256(key, msg) { | ||
return crypto.createHmac('sha256', key).update(msg).digest() | ||
@@ -143,6 +201,6 @@ } | ||
function Hi(password, saltBytes, iterations) { | ||
var ui1 = createHMAC(password, Buffer.concat([saltBytes, Buffer.from([0, 0, 0, 1])])) | ||
var ui1 = hmacSha256(password, Buffer.concat([saltBytes, Buffer.from([0, 0, 0, 1])])) | ||
var ui = ui1 | ||
for (var i = 0; i < iterations - 1; i++) { | ||
ui1 = createHMAC(password, ui1) | ||
ui1 = hmacSha256(password, ui1) | ||
ui = xorBuffers(ui, ui1) | ||
@@ -149,0 +207,0 @@ } |
{ | ||
"name": "pg", | ||
"version": "8.5.1", | ||
"version": "8.6.0", | ||
"description": "PostgreSQL client - pure javascript & libpq with the same API", | ||
@@ -17,3 +17,4 @@ "keywords": [ | ||
"type": "git", | ||
"url": "git://github.com/brianc/node-postgres.git" | ||
"url": "git://github.com/brianc/node-postgres.git", | ||
"directory": "packages/pg" | ||
}, | ||
@@ -25,5 +26,5 @@ "author": "Brian Carlson <brian.m.carlson@gmail.com>", | ||
"packet-reader": "1.0.0", | ||
"pg-connection-string": "^2.4.0", | ||
"pg-pool": "^3.2.2", | ||
"pg-protocol": "^1.4.0", | ||
"pg-connection-string": "^2.5.0", | ||
"pg-pool": "^3.3.0", | ||
"pg-protocol": "^1.5.0", | ||
"pg-types": "^2.1.0", | ||
@@ -57,3 +58,3 @@ "pgpass": "1.x" | ||
}, | ||
"gitHead": "0b9bb349dcb10f6473737001062082b65efc74be" | ||
"gitHead": "d45947938263bec30a1e3252452f04177b785f66" | ||
} |
@@ -56,2 +56,7 @@ # node-postgres | ||
</a> | ||
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAABCAQAAAB0m0auAAAADElEQVR42mNkIBIAAABSAAI2VLqiAAAAAElFTkSuQmCC" /> | ||
<a href="https://www.eaze.com" target="_blank"> | ||
<img height="80" src="https://node-postgres.com/eaze.png" /> | ||
</a> | ||
</div> | ||
@@ -58,0 +63,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
71301
2009
102
Updatedpg-connection-string@^2.5.0
Updatedpg-pool@^3.3.0
Updatedpg-protocol@^1.5.0