Skip to content

Commit 9665ce6

Browse files
committed
initial post quantum services
1 parent ee66740 commit 9665ce6

11 files changed

Lines changed: 332 additions & 65 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
},
6262
"dependencies": {
6363
"@dataparty/bouncer-db": "1.0.1",
64-
"@dataparty/crypto": "git://github.com/datapartyjs/dataparty-crypto.git",
64+
"@dataparty/crypto": "github:datapartyjs/dataparty-crypto#postquantum-bson",
6565
"@dataparty/tasker": "^0.0.3",
6666
"@diva.exchange/i2p-sam": "^4.1.8",
6767
"@markwylde/liferaft": "^1.3.4",

src/comms/isocket-comms.js

Lines changed: 72 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ const {Message, Routines} = require('@dataparty/crypto')
77

88
const AuthOp = require('./op/auth-op')
99
const RosShim = require('./ros-shim')
10-
const IParty = require('../party/iparty')
10+
//const IParty = require('../party/iparty')
11+
12+
const {Routines} = require('@dataparty/crypto')
1113

1214

1315
/**
@@ -38,6 +40,9 @@ class ISocketComms extends EventEmitter {
3840

3941
this.socket = undefined
4042

43+
//this.aesOffer = null
44+
this.aesStream = null
45+
4146
this._ros = undefined
4247
}
4348

@@ -80,49 +85,58 @@ class ISocketComms extends EventEmitter {
8085
op.run().then((status)=>{
8186
debug(status)
8287
debug('authed')
83-
this.emit('open')
88+
//this.aesOffer = op.offer
89+
this.aesStream = op.stream
8490
this.authed = true
91+
this.emit('open')
8592
}).catch(error=>{
8693
this.authed = false
8794
debug('auth error', error)
8895
this.emit('close')
8996
})
9097
}
9198

92-
decrypt(reply, sender){
99+
async decrypt(reply, sender){
100+
if(this.aesStream){
101+
debug('decrypting quantum aes')
102+
103+
const contentBSON = await this.aesStream.decrypt( reply.data )
104+
const content = Routines.BSON.parseObject(new Routines.BSON.BaseParser( contentBSON ))
105+
106+
return content
107+
108+
} else {
109+
debug('decrypting classic')
93110
const replyObj = JSON.parse(reply.data)
94-
let dataPromise = new Promise((resolve, reject)=>{
95-
if(replyObj.enc && replyObj.sig){
96-
let msg = new Message(replyObj)
97-
98-
return resolve(msg.decrypt(this.party._identity).then(content=>{
99-
const senderPub = Routines.extractPublicKeys(msg.enc)
100-
debug('sender', sender, '\tdiscover', this.discoverRemoteIdentity)
101-
if(this.discoverRemoteIdentity && !sender){
102-
debug('discovered remote identity', senderPub)
103-
this.remoteIdentity = {
104-
key: {
105-
public: senderPub
106-
}
107-
}
108-
sender = this.remoteIdentity
109-
}
110-
debug(`sender from - ${msg.from}`)
111-
debug(`senderPub - ${senderPub}`)
112-
113-
if(senderPub.box != sender.key.public.box || senderPub.sign != sender.key.public.sign){
114-
return Promise.reject('TRUST - reply is not from expected remote')
115-
}
116-
117-
debug('decrypted data')
118-
return content
119-
}))
120-
}
121-
122-
reject( Promise.reject('TRUST - reply is not encrypted') )
123-
})
124-
125-
return dataPromise
111+
112+
if(replyObj.enc && replyObj.sig){
113+
let msg = new Message(replyObj)
114+
115+
let content = await msg.decrypt(this.party.privateIdentity())
116+
117+
const senderPub = Routines.extractPublicKeys(msg.enc)
118+
debug('sender', sender, '\tdiscover', this.discoverRemoteIdentity)
119+
if(this.discoverRemoteIdentity && !sender){
120+
debug('discovered remote identity', senderPub)
121+
this.remoteIdentity = {
122+
key: {
123+
public: senderPub
124+
}
125+
}
126+
sender = this.remoteIdentity
127+
}
128+
debug(`sender from - ${msg.from}`)
129+
debug(`senderPub - ${senderPub}`)
130+
131+
if(senderPub.box != sender.key.public.box || senderPub.sign != sender.key.public.sign){
132+
throw new Error('TRUST - reply is not from expected remote')
133+
}
134+
135+
debug('decrypted data')
136+
return content
137+
}
138+
}
139+
126140
}
127141

128142
onmessage(message){
@@ -142,28 +156,37 @@ class ISocketComms extends EventEmitter {
142156
})
143157
}
144158

145-
send(input){
146-
debug('send - ', typeof input, input)
159+
async send(input){
160+
debug('send - ', typeof input, input)
147161

148-
if(typeof input != 'object'){
149-
input = JSON.parse(input)
150-
}
162+
if(typeof input != 'object'){
163+
input = JSON.parse(input)
164+
}
165+
166+
let content = null
151167

152-
const content = new Message({msg: input})
168+
if(this.aesStream){
169+
debug('sending quantum aes')
170+
const contentBSON = Routines.BSON.serializeBSONWithoutOptimiser( input )
171+
content = await this.aesStream.encrypt( contentBSON )
172+
} else {
153173

154-
return content.encrypt(this.party._identity, this.remoteIdentity.key)
155-
.then(JSON.stringify)
156-
.then(this.socket.send.bind(this.socket))
174+
debug('sending classic')
175+
const msg = new Message({msg: input})
176+
await msg.encrypt(this.party._identity, this.remoteIdentity.key)
177+
content = JSON.stringify(msg)
178+
}
157179

180+
await this.socket.send(content)
158181
}
159182

160183
get ros(){
161-
if(!this._ros){
162-
this._ros = new RosShim(this)
163-
this._ros.connect()
164-
}
184+
if(!this._ros){
185+
this._ros = new RosShim(this)
186+
this._ros.connect()
187+
}
165188

166-
return this._ros
189+
return this._ros
167190
}
168191
}
169192

src/comms/op/auth-op.js

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,48 @@
11
const debug = require('debug')('dataparty.op.auth-op')
22
const SocketOp = require('./socket-op')
33

4+
const {Routines} = require('@dataparty/crypto')
5+
46

57
class AuthOp extends SocketOp {
6-
constructor(socket){
7-
super('auth', {}, socket)
8+
constructor(socket){
9+
super('auth', {}, socket)
10+
11+
this.offer=null
12+
this.stream=null
13+
}
14+
15+
async run(){
16+
const actor = socket.party.privateIdentity()
17+
const aesStreamOffer = await actor.createStream( socket.remoteIdentity )
18+
19+
this.stream = aesStreamOffer.stream
20+
21+
const offer = {
22+
sender: aesStreamOffer.sender,
23+
pqCipherText: aesStreamOffer.pqCipherText,
24+
streamNonce: aesStreamOffer.streamNonce
25+
}
26+
27+
const offerBSON = Routines.BSON.serializeBSONWithoutOptimiser( offer )
28+
29+
const { timestamp, value, type } = await Routines.signDataPQ(actor, offerBSON, 'pqsign_ml')
30+
31+
const authPkt = {
32+
offer,
33+
signature: {
34+
timestamp, type,
35+
value: Routines.Utils.base64.encode(value)
36+
}
837
}
38+
39+
debug('created authPkt', authPkt)
40+
41+
this.offer = offer
42+
this.data = authPkt
43+
44+
return super.run()
45+
}
946
}
1047

1148
module.exports = AuthOp

src/comms/peer-comms.js

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -359,8 +359,49 @@ class PeerComms extends ISocketComms {
359359
}
360360

361361
async handleAuthOp(op){
362+
363+
debug('handleAuthOp -', op)
364+
365+
const offerBSON = Routines.BSON.serializeBSONWithoutOptimiser( op.input.offer )
366+
const offer = {
367+
sender: aesStreamOffer.sender,
368+
pqCipherText: aesStreamOffer.pqCipherText,
369+
streamNonce: aesStreamOffer.streamNonce
370+
}
371+
372+
const signature = {
373+
timestamp: op.input.signature.timestamp,
374+
type: op.input.signature.type,
375+
value: Routines.Utils.base64.decode( op.input.signature.value )
376+
}
377+
378+
const actor = await this.party.hostRunner.auth.lookupIdentity(offer.sender)
379+
const verified = await Routines.verifyDataPQ(actor, signature, offerBSON)
380+
381+
if(!verified){
382+
throw new Error('DENY - auth op signature is not valid')
383+
}
384+
385+
if(this.discoverRemoteIdentity){ this.remoteIdentity = actor }
386+
387+
const authorized = await this.party.hostRunner.auth.isSocketConnectionAllowed(actor)
388+
if(!authorized){
389+
390+
clearTimeout(this._host_auth_timeout)
391+
this._host_auth_timeout = null
392+
393+
this.authed = false
394+
this.setState(PeerComms.STATES.SERVER_CLOSED)
395+
op.setState(HostOp.STATES.Finished_Success)
396+
397+
await this.stop()
398+
399+
debug('DENY - client not allowed - ', this.remoteIdentity)
400+
}
401+
402+
this.aesStream = this.party.privateIdentity.recoverStream(offer, true)
362403

363-
debug('allowing client - ', this.remoteIdentity)
404+
debug('ALLOW - allowing client - ', this.remoteIdentity)
364405

365406
clearTimeout(this._host_auth_timeout)
366407
this._host_auth_timeout = null
@@ -380,7 +421,7 @@ class PeerComms extends ISocketComms {
380421

381422
debug('calling runner')
382423

383-
if(op.input.endpoint == 'api-v2-peer-bouncer'){
424+
if(op.input.endpoint == 'api-v2-peer-bouncer' && await this.party.hostRunner.auth.isAdmin(this.remoteIdentity)){
384425
debug('ask->', truncateString(op.input.data, 1024))
385426
op.result = {result: await this.party.handleCall(op.input.data) }
386427

@@ -418,7 +459,7 @@ class PeerComms extends ISocketComms {
418459
op.setState(HostOp.STATES.Finished_Success)
419460
return
420461

421-
} else if(op.input.endpoint == 'api-v2-peer-bouncer'){
462+
} else if(op.input.endpoint == 'api-v2-peer-bouncer' && await this.party.hostRunner.auth.isAdmin(this.remoteIdentity)){
422463

423464
debug('ask->',op.input.data)
424465
op.result = {result: await this.party.handleCall(op.input.data) }

src/service/iauth.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
const debug = require('debug')('dataparty.service.IAuth')
2+
3+
module.exports = class IAuth {
4+
5+
/**
6+
* Interface class for Authorization
7+
*
8+
* @interface module:Service.IAuth
9+
* @link module:Service
10+
*/
11+
constructor({context}){
12+
this.context = {
13+
party: context.party,
14+
serviceRunner: context.serviceRunner
15+
}
16+
}
17+
18+
/**
19+
* @type {string}
20+
* @member module:Service.ITask.Name
21+
*/
22+
static get Name(){
23+
throw new Error('not implemented')
24+
}
25+
26+
/**
27+
* @type {string}
28+
* @member module:Service.ITask.Description
29+
*/
30+
static get Description(){
31+
throw new Error('not implemented')
32+
}
33+
34+
async lookupIdentity(identity){
35+
return identity
36+
}
37+
38+
async isSocketConnectionAllowed(identity){
39+
//throw new Error('not implemented')
40+
return true
41+
}
42+
43+
async isInternal(identity){
44+
return false
45+
}
46+
47+
async isAdmin(identity){
48+
return false
49+
}
50+
51+
async canReadDb(identity){
52+
return false
53+
}
54+
55+
async canWriteDb(identity){
56+
return false
57+
}
58+
59+
async canAccessTopics(identity){
60+
return true
61+
}
62+
63+
/**
64+
* @typedef {Object} module:Service.IAuth.Info
65+
* @property {string} Name
66+
* @property {string} Description
67+
* @property {module:Service.IAuth.TaskConfig} Config
68+
*/
69+
70+
/**
71+
* Returns the task's `Name`, `Description`, and `Config`
72+
* @type module:Service.IAuth.Info
73+
* @member module:Service.IAuth.info
74+
*/
75+
static get info(){
76+
return {
77+
Name: this.Name,
78+
Description: this.Description
79+
}
80+
}
81+
}

src/service/index-browser.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const Path = require('path')
55
*/
66

77

8+
exports.IAuth = require('./iauth')
89
exports.IContext= require('./icontext')
910
exports.IService= require('./iservice')
1011
exports.IEndpoint= require('./iendpoint')

src/service/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const Path = require('path')
55
*/
66

77

8+
exports.IAuth = require('./iauth')
89
exports.IContext= require('./icontext')
910
exports.IService= require('./iservice')
1011
exports.IEndpoint= require('./iendpoint')

0 commit comments

Comments
 (0)