I'm trying to use hyperdb in browser with swarming via webrtc and signalhub. The code is pretty strait forward, but there is some issue with hyperdb replicate where the connecting is killed because of a sameKey check in hypercore. So, I'm thinking ... I'm not properly juggling my discovery keys and id keys so the peers know they should be sync'd. Here is some sample code, it is a bit of a mess but the relevant bits are the hyperdb initialization and the webrtc/signalhub stuff (I think) ... the key at the top is the discovery key of the other peer:
const crypto = require('crypto'),
sha = crypto.createHash('sha1'),
hyperdb = require('hyperdb'),
hyperdiscovery = require('hyperdiscovery'),
cms = require('random-access-idb')('cms'),
webrtc = require('webrtc-swarm'),
signalhub = require('signalhub'),
hyperdrive = require('hyperdrive'),
pump = require('pump');
// Discovery key of other peer/signalhub channel
var key = "cbffda913dabfe73cbd45f64466ffda845383965e66b2aef5f3b716ee6c06528";
const db = hyperdb(filename => {
return cms(filename);
}, { valueEncoding: 'utf-8' });
var DEFAULT_SIGNALHUBS = 'https://signalhub-jccqtwhdwc.now.sh';
db.on('ready', function () {
const swarm = webrtc(signalhub(key, DEFAULT_SIGNALHUBS));
swarm.on('peer', function (conn) {
console.log("PEER!!!!!!!");
const peer = db.replicate({
upload: true,
download: true
});
pump(conn, peer, conn)
});
});
I put up a working example here: https://github.com/joehand/hyperdb-web-example/blob/master/index.js
I think you are getting that error because you are not initializing the db with the key:
var db = hyperdb(storage, key)
Once you do that, you can get the discovery key. Generally, you don't need to be copying the discovery key around because that is always generated from the public key.
If that does not work, please include only the relevant code or a minimum example, so it is easier to debug =). Thanks!
Related
What is a correct way to generate session and than validate it on every request?
Registration and initial authentication (user identity check for generating session) is handled by external service, so it's out of the question.
To simplify a question, what is the native secure way to generate and encrypt session with secret.
Requirements (alternatives is welcomed):
Session should be of two parts, one stored in cookies, second in database.
User check handled by server using database session part, cookies part and validate function.
Session generating and validating functions stored on server side and not accessible to user.
If database session part or functions is compromised, hacker couldn't make request pretending to be user. For this he will need to steal user cookies or session generate function and database session part.
Multiple device support with the same database session part.
JWT is not usable as logout is needed on server side (database session part will be deleted and all devices wouldn't be able to login with old cookies session part). User had some trust level that can change and it's will require JWT invalidation, so sessions is better choice.
I was thinking of using Crypto AES for this, but after asking "is it ok?" - answer was no, i'm not an expert in crypto, so i didn't fully understood a reason.
Here is my initial idea of implementation:
/**
* #param {string} data dummy
* #param {string} userKey from database or create new
* #return {object} {iv, key, encryptedData}
*/
function encrypt(data, userKey) {
let key = userKey ? Buffer.from(userKey, 'hex') : crypto.randomBytes(32)
let iv = crypto.randomBytes(16)
let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv)
let encrypted = cipher.update(data)
encrypted = Buffer.concat([encrypted, cipher.final()])
return { iv: iv.toString('hex'), key: key.toString('hex'), encryptedData: encrypted.toString('hex') }
}
/**
* #param {string} iv
* #param {string} key
* #param {string} encryptedData
* #return {string} decrupted dummy data
*/
function decrypt(iv, key, encryptedData) {
try {
iv = Buffer.from(iv, 'hex')
key = Buffer.from(key, 'hex')
encryptedData = Buffer.from(encryptedData, 'hex')
let decipher = crypto.createDecipheriv('aes-256-cbc', key, iv)
let decrypted = decipher.update(encryptedData)
decrypted = Buffer.concat([decrypted, decipher.final()])
return decrypted.toString()
} catch (err) {
return false
}
}
A way to make it, is that user generate RSA and use crypto with public and private keys, but the public key must always be sent by the user('device')
'use strict';
const express = require('express');
const fs = require('fs');
const nodersa = require('node-rsa');
const bodyParser = require('body-parser');
let app = express();
app.use(bodyParser.urlencoded({ extended : false }));
app.use(bodyParser.json());
// req.body.value is public key, this never write in database or file
app.post('/', (req, res)=>{
let value = req.body.value;
const privateKey = fs.readFileSync('./store/privateKey.pem', 'utf8');
const original = new nodersa(privateKey).decrypt(value, 'utf8');
res.end(original);
});
app.listen(8000, ()=>{
console.log('on 8000');
});
if you use a public certificate authority with node, use aditional file called "certification file", node example here, this file is issue by C.A. you could work as C.A. and generarte this file, but It is recommended for closed systems, if you not need a gubernamental allow
I'm trying to get the sessionID from express-session when a new WebSocket connection comes in from a user. I'm able to find the sessionID, I just have a question about its format.
When I make a HTTP request to my messenger page say I get 'X' as a sessionID, if I then made a WebSocket connection I can find the session ID 'AXB', the session ID X is in there, but also surrounded with other information.
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var session = require('express-session');
var io = require('socket.io')(server);
var store = new MemoryStore({
checkPeriod: 86400000
});
app.use(session({
store: store,
secret: 'jpcs-0001080900DRXPXL',
saveUninitialized: false,
resave: true
}));
// ...
app.get('/messenger/:uid', authorizationRedirect, (req, res) => {
console.log(req.sessionID);
// prints "EIVUudPTckmojrkv6FN9Cdb5NAQq5oQU"
// ...
});
io.set('authorization', (data, accept) => {
if (data && data.headers && data.headers.cookie) {
console.log(data.headers.cookie);
cookies_str = data.headers.cookie;
cookies_arr = cookies_str.split(';');
cookies = {};
for (index in cookies_arr) {
cookie = cookies_arr[index].split('=');
key = cookie[0].replace(/ /g,'');
val = cookie[1];
cookies[key] = val;
}
sessionId = cookies['connect.sid'].split('.')[0];
console.log(sessionId);
// prints "s%3AEIVUudPTckmojrkv6FN9Cdb5NAQq5oQU.AQkvP..."
// ...
});
So basically, in io.set('authorization', ...) I get:
s%3AEIVUudPTckmojrkv6FN9Cdb5NAQq5oQU.AQkvPsfoxieH3EAs8laFWN28dr1C%2B9zIT%2BMXtKTRPBg
But in app.get('/...', ...) I get:
EIVUudPTckmojrkv6FN9Cdb5NAQq5oQU
You can notice that the string from socket.io does contain the session id in this format: "s%3A" + sessionID + ".xxxxxxxxxxx..."
So obviously I can get the sessionID from here, but I'm curious why the sessionID is shown like this when I get socket connections? Will it ALWAYS be shown like this regardless of browser, WebSocket implementations, etc? What does the other information contained mean? I mostly want to make sure that this is a reliable way to get the sessionID. Thanks!
I would first like to clarify that io.set('authorization',...) has been deprecated. Here's the updated version Documentation
So obviously I can get the sessionID from here, but I'm curious why the sessionID is shown like this when I get socket connections? Will it ALWAYS be shown like this regardless of browser, WebSocket implementations, etc?
It's not reserved for socket connections at all. That is simply how it is fixed on the browser. So yes, it will always be shown like that.
What does the other information contained mean? I mostly want to make sure that this is a reliable way to get the sessionID. (s%3AEIVUudPTckmojrkv6FN9Cdb5NAQq5oQU.AQkvPsfoxieH3EAs8laFWN28dr1C%2B9zIT%2BMXtKTRPBg)
The first three characters are just encoded, and I believe every sessionID containts that. DecodedURIComponent("s%3A") = "s:"
After that is the sessionID itself (EIVUudPTckmojrkv6FN9Cdb5NAQq5oQU)
Now, after the dot(AQkvPsfoxieH3EAs8laFWN28dr1C%2B9zIT%2BMXtKTRPBg) is the signature portion. That verifies the authenticity of the cookie and is actually given when you sign the cookie. AND yes, I would say it is a trusted and reliable way.
I'm having difficulty identifying which track belongs to which media source on the receiving end. In unified plan is there any way to define custom properties on a transceiver?
I'm having difficulty identifying which track belongs to which media source on the receiving end.
Use transceiver.mid or the stream.id of an associated stream to correlate tracks.
A transceiver has a mid, which is a unique id that is the same on both sides of the connection after initial negotiation. It is exposed here:
pc.ontrack = event => {
const track = event.track;
const mid = event.transceiver.mid;
}
Alternatively, use addTransceiver(track, {streams: [stream]}) or addTrack(track, stream) and use the stream.id:
pc.ontrack = event => {
const track = event.track;
const id = event.streams[0].id;
}
In unified plan is there any way to define custom properties on a transceiver?
Any JS object can have a property defined on it. But I suspect that's not what you mean.
mid and stream.ids are the only metadata negotiated over to the remote peer connection, and there's no official way to add custom ones.
Once a connection has been established, you can of course use a datachannel to send whatever data you want over.
How to hack custom metadata
OK there is a way, but I hesitate to show it, since you haven't said what you'd use it for. Please consider the above options before resorting to this. Use at your own risk!
You can add any number of stream.ids and replace them in the SPD with whatever you want:
const config = {sdpSemantics: "unified-plan"};
const pc1 = new RTCPeerConnection(config), pc2 = new RTCPeerConnection(config);
const stream = new MediaStream();
pc1.addTransceiver("video", {streams: [stream]});
pc1.msg = "Hello";
pc2.ontrack = event => {
pc2.msg = event.streams[0].id;
console.log(pc2.msg);
};
pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);
pc1.onnegotiationneeded = async e => {
await pc1.setLocalDescription(await pc1.createOffer());
let sdp = pc1.localDescription.sdp.replace(new RegExp(stream.id, 'g'), pc1.msg);
await pc2.setRemoteDescription({type: "offer", sdp});
await pc2.setLocalDescription(await pc2.createAnswer());
await pc1.setRemoteDescription(pc2.localDescription);
}
I'm not actually recommending this, just showing it can be done. Any message you put in here is subject to SDP parsing rules, so be careful.
Using kurento tutorials java samples. I Want to handle stream events like onended etc on the webrtcpeer object. Following is my sample code from where i want to fetch the stream object.
var options = {
localVideo: video,
mediaConstraints: constraints,
onicecandidate: participant.onIceCandidate.bind(participant)
};
var peer = new kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options, function(error) {
if (error) {
return console.error(error);
}
this.generateOffer(participant.offerToReceiveVideo.bind(participant));
});
I want to handle events in a way similar to as mentions in this question
How should I proceed? Please Help
You can bind to those events two ways
Passing a onstreamended listener in the options bag
var options = {
localVideo: video,
mediaConstraints: constraints,
onicecandidate: participant.onIceCandidate.bind(participant),
onstreamended: myOnStreamEnded,
};
Accessing directly the RTCPeerConnection object wrapped inside the WebRtcPeer, and binding to events directly.
var rtcPeerConnection = peer.peerConnection
The latter gives you full access to the peer connection object, so you can work as if you would with that object.
I am a newbie to WebRTC. I am building an application that enables users to view each other's video stream, as well as exchange files. The audio/video part is implemented and working. The problem is I need to add the ability to exchange files now. I am using the below code to initialize the PeerConnection object
var connection = _getConnection(partnerId);
console.log("Initiate offer")
// Add our audio/video stream
connection.addStream(stream);
// Send an offer for a connection
connection.createOffer(function (desc) { _createOfferSuccess(connection, partnerId, desc) }, function (error) { console.log('Error creating session description: ' + error); });
_getConnection creates a new RTCPeerConnection object using
var connection = new RTCPeerConnection(iceconfig);
i.e., with no explicit constraints. It also initializes the different event handlers on it. Right after this, I attach the audio/video stream to this connection. I also cache these connections using the partner id, so I can use it later.
The question is, can I later recall the connection object from the cache, add a data channel to it using something like
connection.createDataChannel("DataChannel", dataChannelOptions);
And use it to share files, or do I have to create a new RTCPeerConnection object and attach the data channel to it?
You certainly do not have to create a another PeerConnection for file transfer alone. Existing PeerConnection can utilize RTCDatachannel with behaves like traditional websocket mechanism ( ie 2 way communication without a central server )
`var PC = new RTCPeerConnection();
//specifying options for my datachannel
var dataChannelOptions = {
ordered: false, // unguaranted sequence
maxRetransmitTime: 2000, // 2000 miliseconds is the maximum time to try and retrsanmit failed messages
maxRetransmits : 5 // 5 is the number of times to try to retransmit failed messages , other options are negotiated , id , protocol
};
// createing data channel using RTC datachannel API name DC1
var dataChannel = PC.createDataChannel("DC1", dataChannelOptions);
dataChannel.onerror = function (error) {
console.log("DC Error:", error);
};
dataChannel.onmessage = function (event) {
console.log("DC Message:", event.data);
};
dataChannel.onopen = function () {
dataChannel.send(" Sending 123 "); // you can add file here in either strings/blob/array bufers almost anyways
};
dataChannel.onclose = function () {
console.log("DC is Closed");
};
`
PS : while sending files over datachannel API , it is advisable to break down the files into small chunks beforehand . I suggest chunk size of almost 10 - 15 KB .