mirror of
https://github.com/xfarrow/blink
synced 2025-06-27 09:03:02 +02:00
Change endpoint from persons to people
This commit is contained in:
121
backend/apis/nodejs/node_modules/pg/lib/crypto/cert-signatures.js
generated
vendored
Normal file
121
backend/apis/nodejs/node_modules/pg/lib/crypto/cert-signatures.js
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
function x509Error(msg, cert) {
|
||||
throw new Error('SASL channel binding: ' + msg + ' when parsing public certificate ' + cert.toString('base64'))
|
||||
}
|
||||
|
||||
function readASN1Length(data, index) {
|
||||
let length = data[index++]
|
||||
if (length < 0x80) return { length, index }
|
||||
|
||||
const lengthBytes = length & 0x7f
|
||||
if (lengthBytes > 4) x509Error('bad length', data)
|
||||
|
||||
length = 0
|
||||
for (let i = 0; i < lengthBytes; i++) {
|
||||
length = (length << 8) | data[index++]
|
||||
}
|
||||
|
||||
return { length, index }
|
||||
}
|
||||
|
||||
function readASN1OID(data, index) {
|
||||
if (data[index++] !== 0x6) x509Error('non-OID data', data) // 6 = OID
|
||||
|
||||
const { length: OIDLength, index: indexAfterOIDLength } = readASN1Length(data, index)
|
||||
index = indexAfterOIDLength
|
||||
lastIndex = index + OIDLength
|
||||
|
||||
const byte1 = data[index++]
|
||||
let oid = ((byte1 / 40) >> 0) + '.' + (byte1 % 40)
|
||||
|
||||
while (index < lastIndex) {
|
||||
// loop over numbers in OID
|
||||
let value = 0
|
||||
while (index < lastIndex) {
|
||||
// loop over bytes in number
|
||||
const nextByte = data[index++]
|
||||
value = (value << 7) | (nextByte & 0x7f)
|
||||
if (nextByte < 0x80) break
|
||||
}
|
||||
oid += '.' + value
|
||||
}
|
||||
|
||||
return { oid, index }
|
||||
}
|
||||
|
||||
function expectASN1Seq(data, index) {
|
||||
if (data[index++] !== 0x30) x509Error('non-sequence data', data) // 30 = Sequence
|
||||
return readASN1Length(data, index)
|
||||
}
|
||||
|
||||
function signatureAlgorithmHashFromCertificate(data, index) {
|
||||
// read this thread: https://www.postgresql.org/message-id/17760-b6c61e752ec07060%40postgresql.org
|
||||
if (index === undefined) index = 0
|
||||
index = expectASN1Seq(data, index).index
|
||||
const { length: certInfoLength, index: indexAfterCertInfoLength } = expectASN1Seq(data, index)
|
||||
index = indexAfterCertInfoLength + certInfoLength // skip over certificate info
|
||||
index = expectASN1Seq(data, index).index // skip over signature length field
|
||||
const { oid, index: indexAfterOID } = readASN1OID(data, index)
|
||||
switch (oid) {
|
||||
// RSA
|
||||
case '1.2.840.113549.1.1.4':
|
||||
return 'MD5'
|
||||
case '1.2.840.113549.1.1.5':
|
||||
return 'SHA-1'
|
||||
case '1.2.840.113549.1.1.11':
|
||||
return 'SHA-256'
|
||||
case '1.2.840.113549.1.1.12':
|
||||
return 'SHA-384'
|
||||
case '1.2.840.113549.1.1.13':
|
||||
return 'SHA-512'
|
||||
case '1.2.840.113549.1.1.14':
|
||||
return 'SHA-224'
|
||||
case '1.2.840.113549.1.1.15':
|
||||
return 'SHA512-224'
|
||||
case '1.2.840.113549.1.1.16':
|
||||
return 'SHA512-256'
|
||||
// ECDSA
|
||||
case '1.2.840.10045.4.1':
|
||||
return 'SHA-1'
|
||||
case '1.2.840.10045.4.3.1':
|
||||
return 'SHA-224'
|
||||
case '1.2.840.10045.4.3.2':
|
||||
return 'SHA-256'
|
||||
case '1.2.840.10045.4.3.3':
|
||||
return 'SHA-384'
|
||||
case '1.2.840.10045.4.3.4':
|
||||
return 'SHA-512'
|
||||
// RSASSA-PSS: hash is indicated separately
|
||||
case '1.2.840.113549.1.1.10':
|
||||
index = indexAfterOID
|
||||
index = expectASN1Seq(data, index).index
|
||||
if (data[index++] !== 0xa0) x509Error('non-tag data', data) // a0 = constructed tag 0
|
||||
index = readASN1Length(data, index).index // skip over tag length field
|
||||
index = expectASN1Seq(data, index).index // skip over sequence length field
|
||||
const { oid: hashOID } = readASN1OID(data, index)
|
||||
switch (hashOID) {
|
||||
// standalone hash OIDs
|
||||
case '1.2.840.113549.2.5':
|
||||
return 'MD5'
|
||||
case '1.3.14.3.2.26':
|
||||
return 'SHA-1'
|
||||
case '2.16.840.1.101.3.4.2.1':
|
||||
return 'SHA-256'
|
||||
case '2.16.840.1.101.3.4.2.2':
|
||||
return 'SHA-384'
|
||||
case '2.16.840.1.101.3.4.2.3':
|
||||
return 'SHA-512'
|
||||
}
|
||||
x509Error('unknown hash OID ' + hashOID, data)
|
||||
// Ed25519 -- see https: return//github.com/openssl/openssl/issues/15477
|
||||
case '1.3.101.110':
|
||||
case '1.3.101.112': // ph
|
||||
return 'SHA-512'
|
||||
// Ed448 -- still not in pg 17.2 (if supported, digest would be SHAKE256 x 64 bytes)
|
||||
case '1.3.101.111':
|
||||
case '1.3.101.113': // ph
|
||||
x509Error('Ed448 certificate channel binding is not currently supported by Postgres')
|
||||
}
|
||||
x509Error('unknown OID ' + oid, data)
|
||||
}
|
||||
|
||||
module.exports = { signatureAlgorithmHashFromCertificate }
|
212
backend/apis/nodejs/node_modules/pg/lib/crypto/sasl.js
generated
vendored
Normal file
212
backend/apis/nodejs/node_modules/pg/lib/crypto/sasl.js
generated
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
'use strict'
|
||||
const crypto = require('./utils')
|
||||
const { signatureAlgorithmHashFromCertificate } = require('./cert-signatures')
|
||||
|
||||
function startSession(mechanisms, stream) {
|
||||
const candidates = ['SCRAM-SHA-256']
|
||||
if (stream) candidates.unshift('SCRAM-SHA-256-PLUS') // higher-priority, so placed first
|
||||
|
||||
const mechanism = candidates.find((candidate) => mechanisms.includes(candidate))
|
||||
|
||||
if (!mechanism) {
|
||||
throw new Error('SASL: Only mechanism(s) ' + candidates.join(' and ') + ' are supported')
|
||||
}
|
||||
|
||||
if (mechanism === 'SCRAM-SHA-256-PLUS' && typeof stream.getPeerCertificate !== 'function') {
|
||||
// this should never happen if we are really talking to a Postgres server
|
||||
throw new Error('SASL: Mechanism SCRAM-SHA-256-PLUS requires a certificate')
|
||||
}
|
||||
|
||||
const clientNonce = crypto.randomBytes(18).toString('base64')
|
||||
const gs2Header = mechanism === 'SCRAM-SHA-256-PLUS' ? 'p=tls-server-end-point' : stream ? 'y' : 'n'
|
||||
|
||||
return {
|
||||
mechanism,
|
||||
clientNonce,
|
||||
response: gs2Header + ',,n=*,r=' + clientNonce,
|
||||
message: 'SASLInitialResponse',
|
||||
}
|
||||
}
|
||||
|
||||
async function continueSession(session, password, serverData, stream) {
|
||||
if (session.message !== 'SASLInitialResponse') {
|
||||
throw new Error('SASL: Last message was not SASLInitialResponse')
|
||||
}
|
||||
if (typeof password !== 'string') {
|
||||
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string')
|
||||
}
|
||||
if (password === '') {
|
||||
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a non-empty string')
|
||||
}
|
||||
if (typeof serverData !== 'string') {
|
||||
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: serverData must be a string')
|
||||
}
|
||||
|
||||
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')
|
||||
}
|
||||
|
||||
var clientFirstMessageBare = 'n=*,r=' + session.clientNonce
|
||||
var serverFirstMessage = 'r=' + sv.nonce + ',s=' + sv.salt + ',i=' + sv.iteration
|
||||
|
||||
// without channel binding:
|
||||
let channelBinding = stream ? 'eSws' : 'biws' // 'y,,' or 'n,,', base64-encoded
|
||||
|
||||
// override if channel binding is in use:
|
||||
if (session.mechanism === 'SCRAM-SHA-256-PLUS') {
|
||||
const peerCert = stream.getPeerCertificate().raw
|
||||
let hashName = signatureAlgorithmHashFromCertificate(peerCert)
|
||||
if (hashName === 'MD5' || hashName === 'SHA-1') hashName = 'SHA-256'
|
||||
const certHash = await crypto.hashByName(hashName, peerCert)
|
||||
const bindingData = Buffer.concat([Buffer.from('p=tls-server-end-point,,'), Buffer.from(certHash)])
|
||||
channelBinding = bindingData.toString('base64')
|
||||
}
|
||||
|
||||
var clientFinalMessageWithoutProof = 'c=' + channelBinding + ',r=' + sv.nonce
|
||||
var authMessage = clientFirstMessageBare + ',' + serverFirstMessage + ',' + clientFinalMessageWithoutProof
|
||||
|
||||
var saltBytes = Buffer.from(sv.salt, 'base64')
|
||||
var saltedPassword = await crypto.deriveKey(password, saltBytes, sv.iteration)
|
||||
var clientKey = await crypto.hmacSha256(saltedPassword, 'Client Key')
|
||||
var storedKey = await crypto.sha256(clientKey)
|
||||
var clientSignature = await crypto.hmacSha256(storedKey, authMessage)
|
||||
var clientProof = xorBuffers(Buffer.from(clientKey), Buffer.from(clientSignature)).toString('base64')
|
||||
var serverKey = await crypto.hmacSha256(saltedPassword, 'Server Key')
|
||||
var serverSignatureBytes = await crypto.hmacSha256(serverKey, authMessage)
|
||||
|
||||
session.message = 'SASLResponse'
|
||||
session.serverSignature = Buffer.from(serverSignatureBytes).toString('base64')
|
||||
session.response = clientFinalMessageWithoutProof + ',p=' + clientProof
|
||||
}
|
||||
|
||||
function finalizeSession(session, serverData) {
|
||||
if (session.message !== 'SASLResponse') {
|
||||
throw new Error('SASL: Last message was not SASLResponse')
|
||||
}
|
||||
if (typeof serverData !== 'string') {
|
||||
throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: serverData must be a string')
|
||||
}
|
||||
|
||||
const { serverSignature } = parseServerFinalMessage(serverData)
|
||||
|
||||
if (serverSignature !== session.serverSignature) {
|
||||
throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature does not match')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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))
|
||||
}
|
||||
|
||||
/**
|
||||
* 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')
|
||||
}
|
||||
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)
|
||||
|
||||
return {
|
||||
nonce,
|
||||
salt,
|
||||
iteration,
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
throw new TypeError('first argument must be a Buffer')
|
||||
}
|
||||
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]))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
startSession,
|
||||
continueSession,
|
||||
finalizeSession,
|
||||
}
|
43
backend/apis/nodejs/node_modules/pg/lib/crypto/utils-legacy.js
generated
vendored
Normal file
43
backend/apis/nodejs/node_modules/pg/lib/crypto/utils-legacy.js
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
'use strict'
|
||||
// This file contains crypto utility functions for versions of Node.js < 15.0.0,
|
||||
// which does not support the WebCrypto.subtle API.
|
||||
|
||||
const nodeCrypto = require('crypto')
|
||||
|
||||
function md5(string) {
|
||||
return nodeCrypto.createHash('md5').update(string, 'utf-8').digest('hex')
|
||||
}
|
||||
|
||||
// See AuthenticationMD5Password at https://www.postgresql.org/docs/current/static/protocol-flow.html
|
||||
function postgresMd5PasswordHash(user, password, salt) {
|
||||
var inner = md5(password + user)
|
||||
var outer = md5(Buffer.concat([Buffer.from(inner), salt]))
|
||||
return 'md5' + outer
|
||||
}
|
||||
|
||||
function sha256(text) {
|
||||
return nodeCrypto.createHash('sha256').update(text).digest()
|
||||
}
|
||||
|
||||
function hashByName(hashName, text) {
|
||||
hashName = hashName.replace(/(\D)-/, '$1') // e.g. SHA-256 -> SHA256
|
||||
return nodeCrypto.createHash(hashName).update(text).digest()
|
||||
}
|
||||
|
||||
function hmacSha256(key, msg) {
|
||||
return nodeCrypto.createHmac('sha256', key).update(msg).digest()
|
||||
}
|
||||
|
||||
async function deriveKey(password, salt, iterations) {
|
||||
return nodeCrypto.pbkdf2Sync(password, salt, iterations, 32, 'sha256')
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
postgresMd5PasswordHash,
|
||||
randomBytes: nodeCrypto.randomBytes,
|
||||
deriveKey,
|
||||
sha256,
|
||||
hashByName,
|
||||
hmacSha256,
|
||||
md5,
|
||||
}
|
88
backend/apis/nodejs/node_modules/pg/lib/crypto/utils-webcrypto.js
generated
vendored
Normal file
88
backend/apis/nodejs/node_modules/pg/lib/crypto/utils-webcrypto.js
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
const nodeCrypto = require('crypto')
|
||||
|
||||
module.exports = {
|
||||
postgresMd5PasswordHash,
|
||||
randomBytes,
|
||||
deriveKey,
|
||||
sha256,
|
||||
hashByName,
|
||||
hmacSha256,
|
||||
md5,
|
||||
}
|
||||
|
||||
/**
|
||||
* The Web Crypto API - grabbed from the Node.js library or the global
|
||||
* @type Crypto
|
||||
*/
|
||||
const webCrypto = nodeCrypto.webcrypto || globalThis.crypto
|
||||
/**
|
||||
* The SubtleCrypto API for low level crypto operations.
|
||||
* @type SubtleCrypto
|
||||
*/
|
||||
const subtleCrypto = webCrypto.subtle
|
||||
const textEncoder = new TextEncoder()
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} length
|
||||
* @returns
|
||||
*/
|
||||
function randomBytes(length) {
|
||||
return webCrypto.getRandomValues(Buffer.alloc(length))
|
||||
}
|
||||
|
||||
async function md5(string) {
|
||||
try {
|
||||
return nodeCrypto.createHash('md5').update(string, 'utf-8').digest('hex')
|
||||
} catch (e) {
|
||||
// `createHash()` failed so we are probably not in Node.js, use the WebCrypto API instead.
|
||||
// Note that the MD5 algorithm on WebCrypto is not available in Node.js.
|
||||
// This is why we cannot just use WebCrypto in all environments.
|
||||
const data = typeof string === 'string' ? textEncoder.encode(string) : string
|
||||
const hash = await subtleCrypto.digest('MD5', data)
|
||||
return Array.from(new Uint8Array(hash))
|
||||
.map((b) => b.toString(16).padStart(2, '0'))
|
||||
.join('')
|
||||
}
|
||||
}
|
||||
|
||||
// See AuthenticationMD5Password at https://www.postgresql.org/docs/current/static/protocol-flow.html
|
||||
async function postgresMd5PasswordHash(user, password, salt) {
|
||||
var inner = await md5(password + user)
|
||||
var outer = await md5(Buffer.concat([Buffer.from(inner), salt]))
|
||||
return 'md5' + outer
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a SHA-256 digest of the given data
|
||||
* @param {Buffer} data
|
||||
*/
|
||||
async function sha256(text) {
|
||||
return await subtleCrypto.digest('SHA-256', text)
|
||||
}
|
||||
|
||||
async function hashByName(hashName, text) {
|
||||
return await subtleCrypto.digest(hashName, text)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign the message with the given key
|
||||
* @param {ArrayBuffer} keyBuffer
|
||||
* @param {string} msg
|
||||
*/
|
||||
async function hmacSha256(keyBuffer, msg) {
|
||||
const key = await subtleCrypto.importKey('raw', keyBuffer, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'])
|
||||
return await subtleCrypto.sign('HMAC', key, textEncoder.encode(msg))
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a key from the password and salt
|
||||
* @param {string} password
|
||||
* @param {Uint8Array} salt
|
||||
* @param {number} iterations
|
||||
*/
|
||||
async function deriveKey(password, salt, iterations) {
|
||||
const key = await subtleCrypto.importKey('raw', textEncoder.encode(password), 'PBKDF2', false, ['deriveBits'])
|
||||
const params = { name: 'PBKDF2', hash: 'SHA-256', salt: salt, iterations: iterations }
|
||||
return await subtleCrypto.deriveBits(params, key, 32 * 8, ['deriveBits'])
|
||||
}
|
9
backend/apis/nodejs/node_modules/pg/lib/crypto/utils.js
generated
vendored
Normal file
9
backend/apis/nodejs/node_modules/pg/lib/crypto/utils.js
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
const useLegacyCrypto = parseInt(process.versions && process.versions.node && process.versions.node.split('.')[0]) < 15
|
||||
if (useLegacyCrypto) {
|
||||
// We are on an old version of Node.js that requires legacy crypto utilities.
|
||||
module.exports = require('./utils-legacy')
|
||||
} else {
|
||||
module.exports = require('./utils-webcrypto')
|
||||
}
|
Reference in New Issue
Block a user