Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

wyseman

Package Overview
Dependencies
Maintainers
1
Versions
36
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

wyseman - npm Package Compare versions

Comparing version 1.0.20 to 1.0.21

163

lib/client.js

@@ -6,8 +6,6 @@ #!/usr/bin/env node

// TODO:
//X- Make into a Wyseman library call
//X- How to abstract db=
//- Implement encryption/wrapping of the stored key (compatible with Wylib version of client)
//X- Implement encryption/wrapping of the stored key (compatible with Wylib version of client)
//-
const { Log } = require('wyclif')
const Crypto = require('crypto')
const Encrypt = require('wylib/src/encrypt')
const Https = require('https')

@@ -19,2 +17,14 @@ const Fetch = require('node-fetch') //Fetch via http

const UserAgent = "Wyseman Websocket Client API"
const Subtle = require('crypto').webcrypto.subtle
const KeyConfig = {
name: 'RSA-PSS',
modulusLength: 2048,
publicExponent: new Uint8Array([1,0,1]),
hash: 'SHA-256'
}
const SignConfig = { //For signing with RSA-PSS
name: 'RSA-PSS',
saltLength: 128
}
var encrypt = new Encrypt(require('crypto').webcrypto)

@@ -24,45 +34,88 @@ module.exports = class {

this.ca = conf.ca //Certificate Authority file
this.dbInfo = conf.dbInfo //Custom DB listen coes
this.dbInfo = conf.dbInfo //Custom DB listen codes
this.httpPort = conf.httpPort //Where clientinfo query goes
this.keyLength = conf.keyLength || defKeyLength //client's private key length
this.log = conf.log || Log('Wyseman-client')
this.ws = null //No websocket open yet
this.ws = null //No websocket open yet
}
connect(credential, openCB) { //Initiate an authenticate websocket connection, as a specified user
let { host, port, user, token, key } = credential //Grab properties from token or connecton key object
, authString //Will become part of ws URI
, myKey
text2json(text) { //Parse text to a JSON object
let json = {}
if (text) try {json = JSON.parse(text)} catch(e) {
this.log.error("Parsing ticket JSON: ", text)
}
return json
}
async connect(credential, openCB) { //Initiate an authenticate websocket connection, as a specified user
let host, port, user, token, key
if (credential.s && credential.i && credential.d) { //credentials are encrypted
let password = this.passwordCB ? this.passwordCB() : null
this.log.debug("Pre-decrypt:", credential)
encrypt.decrypt(password, JSON.stringify(credential)).then(d=>{
let plainObj = this.text2json(d)
this.log.debug("Post-decrypt:", plainObj)
if (!('s' in plainObj)) //Call recursively with decrpyted credentials
this.connect(plainObj, openCB)
}).catch(e => this.log.error("Decrypting credentials: ", e.message))
return
} else if ('login' in credential) {
({ host, port, user, token, key } = credential.login)
} else if ('host' in credential) {
({ host, port, user, token, key } = credential) //Grab properties from token or connecton key object
} else {
this.log.error("Can't find required information in credential: ", credential)
}
if (token) { //The caller has a connection token
let keyPair = Crypto.generateKeyPairSync('rsa',{ //We will build a new keypair for future connections
modulusLength: this.keyLength,
publicKeyEncoding: {type: 'spki', format: 'der'},
//Fixme: privateKeyEncoding: {type: 'pkcs8', format: 'der', cipher: 'aes-256-cbc', passphrase: ???}
privateKeyEncoding: {type: 'pkcs8', format: 'der'}
let origin = `https://${host}:${this.httpPort}` //Websocket runs within an http origin
, headers = {"user-agent": UserAgent, cookie: Math.random()}
, wsOptions = {origin, headers} //Used when opening websocket
, openWebSocket = (auth) => { //Initiate websocket connection
let dbHex = Buffer.from(JSON.stringify(this.dbInfo)).toString('base64url') //Make hex string of database listen codes
, query = `user=${user}&db=${dbHex}&${auth}` //Build arguments for our URI
, url = `wss://${host}:${port}/?${query}` //and then the full URI
this.log.debug("Ws URL:", url)
if (this.ca) wsOptions.ca = this.ca
this.ws = new Ws(url, wsOptions) //Launch connection
this.ws.on('open', () => openCB(this.ws)) //Invoke caller code when it opens
this.ws.on('error', err => {
if (!this.errCB) throw(err)
this.errCB(err, this.ws)
})
this.log.trace("Generated key:", user, keyPair.privateKey.toString('hex'))
if (this.keyCB) this.keyCB( //Let the caller store his new key
{login: {host, port, user, key:keyPair.privateKey.toString('hex')}}
)
authString = 'token=' + token + '&pub=' + keyPair.publicKey.toString('hex')
}
} else if (key) { //The caller already has a connection key
let keyData = Buffer.from(key, 'hex')
myKey = Crypto.createPrivateKey({key:keyData, format: 'der', type: 'pkcs8'})
if (token) { //The caller has a connection token
let keyPair = await Subtle.generateKey(KeyConfig, true, ['sign','verify'])
let exPriv = await Subtle.exportKey('jwk', keyPair.privateKey)
this.log.trace("Generated key:", user, keyPair.privateKey)
if (this.keyCB) { //Let the caller store the new key
let password = this.passwordCB ? this.passwordCB() : null
, saveKey = {login: {host, port, user, key:exPriv}}
if (password) { //Encrypt key with a password?
encrypt.encrypt(password, JSON.stringify(saveKey)).then(d=>this.keyCB(this.text2json(d)))
} else {
this.keyCB(saveKey)
}
}
} else {
// throw "Must specify a token or a key"
let exPub = await Subtle.exportKey('jwk', keyPair.publicKey)
, authString = 'token=' + token + '&pub=' + Buffer.from(JSON.stringify(exPub)).toString('base64url')
this.log.trace("Public:", exPub)
openWebSocket(authString)
return
}
let origin = `https://${host}:${this.httpPort}` //Websocket runs within an http origin
this.log.trace("key:", key)
if (!key) {this.log.error("Connection requested without token or key!"); return}
let myKey = await Subtle.importKey('jwk', key, KeyConfig, true, ['sign']) //The caller already has a connection key
, clientUri = origin + '/clientinfo' //Will grab some data here to encrypt for connection handshake
, headers = {"user-agent": UserAgent, cookie: Math.random()}
, fetchOptions = {headers} //Used in fetch of that data
, wsOptions = {origin,headers} //Used when opening websocket
this.log.trace("myKey:", myKey)
if (this.ca) { //Custom Certificate Authority provided
let agent = new Https.Agent({ca:this.ca})
fetchOptions.agent = agent //So fetch will recognize our site
wsOptions.ca = this.ca //So websocket will too
}

@@ -76,25 +129,11 @@ this.log.debug("Fetching client info from:", clientUri)

let db = Buffer.from(JSON.stringify(this.dbInfo)).toString('hex') //Make hex string of database listen codes
this.log.debug("DB:", db)
if (myKey) { //If we already have a connection key
let { ip, cookie, userAgent, date } = info //Fodder for what we will digitally sign
, message = JSON.stringify({ip, cookie, userAgent, date}) //Message object has to be built in exactly this order
, signer = Crypto.createSign('SHA256')
, enc = new TextEncoder
this.log.debug("message:", message) // Crypto.getHashes()
//this.log.trace("myKey:", myKey.export({type: 'pkcs8', format: 'pem'}))
signer.update(enc.encode(message))
signer.end()
let sign = signer.sign({key: myKey, padding: Crypto.constants.RSA_PKCS1_PSS_PADDING, saltLength: 128}, 'hex')
authString = 'sign=' + sign + '&date=' + date
}
let { ip, cookie, userAgent, date } = info //Fodder for what we will digitally sign
, message = JSON.stringify({ip, cookie, userAgent, date}) //Message object has to be built in exactly this order
, enc = new TextEncoder
this.log.debug("message:", message)
let query = `user=${user}&db=${db}&${authString}` //Build argumeents for our URI
, url = `wss://${host}:${port}/?${query}` //and the full URI
this.log.debug("Ws URL:", url)
this.ws = new Ws(url, wsOptions) //Instantiate the websocket connection
this.ws.on('open', () => openCB(this.ws)) //Invoke caller code when connection opens
this.ws.on('error', err => {
if (!this.errCB) throw(err)
this.errCB(err, this.ws)
Subtle.sign(SignConfig, myKey, enc.encode(message)).then(sign =>{
this.log.debug("sign:", sign)
let authString = 'sign=' + Buffer.from(sign).toString('base64url') + '&date=' + date
openWebSocket(authString)
})

@@ -108,10 +147,16 @@ }).catch(err => {

this.log.trace("Setting handler:", event)
if (event == 'key') { //When a new key is generated
this.keyCB = handler
} else if (event == 'error') {
this.errCB = handler
} else {
error('Unknown event:', event)
switch (event) {
case 'key': //When a new key is generated
this.keyCB = handler
break
case 'error':
this.errCB = handler
break
case 'password':
this.passwordCB = handler
break
default:
error('Unknown event:', event)
}
}
} //class Client

@@ -17,5 +17,5 @@ //Manage the connection between a User Interface and the backend database

const Url = require('url')
const Base64 = require('base64-js')
const Crypto = require('crypto')
const Net = require('net')
//const JWK2PEM = require('pem-jwk').jwk2pem //Shim until Node >= 16 mainstream

@@ -114,11 +114,12 @@ const PemHeader = "-----BEGIN PUBLIC KEY-----\n"

if (err) this.log.error("Error getting user connection key:", user, err)
let pubKey = (!err && res && res.rows && res.rows.length >= 1) ? res.rows[0].conn_pub : null
let pubString = (!err && res && res.rows && res.rows.length >= 1) ? res.rows[0].conn_pub : null
, pubKey = JSON.parse(pubString)
, valid = false //Assume failure
this.log.trace(" public key:", pubKey, res ? res.rows : null)
this.log.trace(" public key:", pubKey, typeof pubKey)
if (pubKey && sign) { //We have the public key from the DB and the signed hash from the client
let rawKey = Buffer.from(pubKey, 'hex') //Hex-to-binary
, rawSig = Buffer.from(sign, 'hex')
, key = PemHeader + Base64.fromByteArray(rawKey) + PemFooter //Raw-to-PEM
let rawKey = Crypto.createPublicKey({key:pubKey,format:'jwk',encoding:'utf-8'}) //JWK to raw
, key = rawKey.export({type:'spki', format: 'pem'}) //raw to PEM (Untested)
, rawSig = Buffer.from(sign, 'base64')
, verify = Crypto.createVerify('SHA256') //Make a verifier
this.log.trace(" user public:", user, key)
this.log.trace(" user public:", user, key, 'sign:', sign)
verify.update(message) //Give it our message

@@ -131,3 +132,3 @@ valid = verify.verify(Object.assign({key}, VerifyTpt), rawSig) //And check it

} catch (e) {
this.log.debug("Validating signature:", e)
this.log.debug("Error validating signature:", e.message)
}})

@@ -143,12 +144,12 @@ }

, { user, db, sign, date, token, pub } = query
, listen = db ? JSON.parse(Buffer.from(db,'hex').toString()) : null
, listen = db ? JSON.parse(Buffer.from(db,'base64').toString()) : null
, payload = req.WysemanPayload = {} //Custom Wyseman data to pass back to connection
this.log.trace("Checking client:", origin, "cb:", !!cb, "q:", query, "s:", secure, "IP:", req.connection.remoteAddress, "pub:", pub)
if (user && token && pub) //User connecting with a token
this.validateToken(user, token, pub, listen, payload, (valid)=>{
if (user && token && pub) { //User connecting with a token
let pubJSON = Buffer.from(pub,'base64').toString()
this.validateToken(user, token, pubJSON, listen, payload, (valid)=>{
cb(valid, 403, 'Invalid Login') //Tell websocket whether or not to connect
})
else if (user && sign && date) { //User has a signature
} else if (user && sign && date) { //User has a signature
let message = JSON.stringify({ip: req.connection.remoteAddress, cookie: req.headers.cookie, userAgent: req.headers['user-agent'], date})

@@ -155,0 +156,0 @@ , now = new Date()

{
"name": "wyseman",
"version": "1.0.20",
"version": "1.0.21",
"description": "PostgreSQL Schema Manager with Javascript, Ruby, TCL API",

@@ -30,10 +30,10 @@ "main": "lib/index.js",

"wyseman": "bin/wyseman",
"wmmkpkg": "bin/wmmkpkg",
"wysegi": "bin/wysegi"
},
"dependencies": {
"base64-js": "^1.5.1",
"node-fetch": "^2.6.1",
"pg": "^8.5.1",
"node-fetch": "^2.6.2",
"pg": "^8.7.1",
"pg-format": "^1.0.4",
"ws": "^7.4.3"
"ws": "^7.5.5"
},

@@ -40,0 +40,0 @@ "devDependencies": {

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc