git ssb

0+

Dominic / ssb-peer-invites



Tree: 3f14a6dcd8d9c4cb8d9fa48e216e5c4c1e91d52a

Files: 3f14a6dcd8d9c4cb8d9fa48e216e5c4c1e91d52a / valid.js

5693 bytesRaw
1var ssbKeys = require('ssb-keys')
2var isMsg = require('ssb-ref').isMsg
3var u = require('./util')
4
5var invite_key = require('./cap')
6
7function code(err, c) {
8 err.code = 'peer-invites:'+c
9 return err
10}
11
12function isObject (o) {
13 return o && 'object' === typeof o
14}
15
16function toBuffer(str) {
17 return Buffer.isBuffer(str) ? str : Buffer.from(str, 'base64')
18}
19
20//this should really be refactored out somewhere.
21function toMsgId(msg) {
22 return '%'+ssbKeys.hash(JSON.stringify(msg, null, 2))
23}
24
25//derive key for private field
26function hash (seed) {
27 if(!Buffer.isBuffer(seed)) throw new Error('expected seed as buffer')
28 return u.hash(seed)
29}
30
31//derive key for reveal field
32function hash2 (seed) {
33 if(!Buffer.isBuffer(seed)) throw new Error('expected seed as buffer')
34 return u.hash(u.hash(seed))
35}
36
37
38exports.createInvite = function (seed, host, reveal, private, caps) {
39 if(!isObject(caps)) throw new Error('caps *must* be provided')
40
41 seed = toBuffer(seed)
42 var keys = ssbKeys.generate(null, seed) //K
43 if(keys.id === host)
44 throw code(new Error('do not create invite with own public key'), 'peer-invites:no-own-goal')
45 return ssbKeys.signObj(keys, caps.peerInvite, {
46 type: 'peer-invite',
47 invite: keys.id,
48 host: host, //sign our own key, to prove we created K
49 reveal: reveal ? u.box(reveal, hash2(seed)) : undefined,
50 private: private ? u.box(private, hash(seed)) : undefined
51 })
52}
53
54exports.verifyInvitePublic = function (msg, caps) {
55 if(!isObject(caps)) throw new Error('caps *must* be provided')
56
57 if(msg.content.host != msg.author)
58 throw code(new Error('host did not match author'), 'host-must-match-author')
59
60 if(!ssbKeys.verifyObj(msg.content.invite, caps.peerInvite, msg.content))
61 throw code(new Error('invalid invite signature'), 'invite-signature-failed')
62
63 //an ordinary message so doesn't use special hmac_key, unless configed to.
64 if(!ssbKeys.verifyObj(msg.author, caps.sign, msg))
65 throw code(new Error('invalid host signature'), 'host-signature-failed')
66 return true
67}
68
69exports.verifyInvitePrivate = function (msg, seed, caps) {
70 if(!isObject(caps)) throw new Error('caps *must* be provided')
71
72 seed = toBuffer(seed)
73 exports.verifyInvitePublic(msg, caps)
74 if(msg.content.reveal) {
75 var reveal = u.unbox(msg.content.reveal, hash2(seed))
76 if(!reveal) throw code(new Error('could not decrypt reveal field'), 'decrypt-reveal-failed')
77 }
78 if(msg.content.private) {
79 var private = u.unbox(msg.content.private, hash(seed))
80 if(!private) throw code(new Error('could not decrypt private field'), 'decrypt-private-failed')
81 }
82
83 return {reveal: reveal, private: private}
84}
85
86exports.createAccept = function (msg, seed, id, caps) {
87 if(!isObject(caps)) throw new Error('caps *must* be provided')
88
89 seed = toBuffer(seed)
90 exports.verifyInvitePrivate(msg, seed, caps)
91 var keys = ssbKeys.generate(null, seed) //K
92 if(keys.id != msg.content.invite)
93 throw code(new Error('seed does not match invite'), 'seed-must-match-invite')
94 var inviteId = toMsgId(msg)
95 var content = {
96 type: 'peer-invite/accept',
97 receipt: inviteId,
98 id: id
99 }
100 if(msg.content.reveal)
101 content.key = hash2(seed).toString('base64')
102 return ssbKeys.signObj(keys, caps.peerInvite, content)
103}
104
105exports.verifyAcceptOnly = function (accept, caps) {
106 if(!isObject(caps)) throw new Error('caps *must* be provided')
107 if(accept.content.type !== 'peer-invite/accept')
108 throw code(new Error('accept must be type: "peer-invite/accept", was:'+JSON.stringify(accept.content.type)), 'accept-message-type')
109 if(!isMsg(accept.content.receipt))
110 throw code(new Error('accept must reference invite message id'), 'accept-reference-invite')
111 //verify signed as ordinary message.
112 if(!ssbKeys.verifyObj(accept.content.id, caps.sign, accept))
113 throw code(new Error('acceptance must be signed by claimed key'), 'accept-signature-failed')
114}
115
116exports.verifyAccept = function (accept, invite_msg, caps) {
117 if(!isObject(caps)) throw new Error('caps *must* be provided')
118 if(!invite_msg) throw new Error('invite must be provided')
119
120 exports.verifyAcceptOnly(accept, caps)
121
122 if(invite_msg.content.type !== 'peer-invite')
123 throw code(new Error('accept must be type: invite, was:'+accept.content.type), 'peer-invites:invite-message-type')
124
125 var invite_id = toMsgId(invite_msg)
126 var reveal
127
128 if(invite_id !== accept.content.receipt)
129 throw code(new Error('acceptance not matched to given invite, got:'+invite_id+' expected:'+accept.content.receipt), 'accept-wrong-invite')
130
131 if(accept.author === invite_msg.content.id)
132 throw code(new Error('guest must use a new key, not the same seed'), 'guest-key-reuse')
133 if(invite_msg.content.reveal) {
134 if(!accept.content.key)
135 throw code(new Error('accept missing reveal key, when invite has it'), 'accept-must-reveal-key')
136 reveal = u.unbox(invite_msg.content.reveal, toBuffer(accept.content.key))
137 if(!reveal) throw code(new Error('accept did not correctly reveal invite'), 'decrypt-accept-reveal-failed')
138 }
139
140 if(!ssbKeys.verifyObj(invite_msg.content.invite, caps.peerInvite, accept.content))
141 throw code(new Error('did not verify invite-acceptance contents'), 'accept-invite-signature-failed')
142 //an ordinary message, so does not use hmac_key
143 return reveal || true
144}
145
146exports.createConfirm = function (accept) {
147 return {
148 type: 'peer-invite/confirm',
149 embed: accept,
150 //second pointer back to receipt, so that links can find it
151 //(since it unfortunately does not handle links nested deeper
152 //inside objects. when we look up the message,
153 //confirm that content.embed.content.receipt is the same)
154 receipt: accept.content.receipt
155 }
156}
157
158

Built with git-ssb-web