Curve Encryptor added hybrid (support only working with ml_dsa so far)

This commit is contained in:
itscutaru 2025-08-06 12:44:12 +03:00
parent 71f4e69be0
commit 6b01d64da5
10 changed files with 49 additions and 15 deletions

View File

@ -19,6 +19,10 @@ Core.isValidPublicKey = function (owner) {
return typeof(owner) === 'string' && owner.length === 44;
};
Core.isValidKemPublicKey = function (kemPublic) {
return typeof(kemPublic) === 'string' && kemPublic.length === 800;
};
var makeToken = Core.makeToken = function () {
return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
.toString(16);

View File

@ -264,14 +264,16 @@ commands.DISABLE_APPS = function (Env, args) {
commands.SET_SUPPORT_KEYS = function (Env, args) {
const curvePublic = args[0]; // Support mailbox key
const edPublic = args[1]; // Support pin log
const kemPublic = args[2]; // Support KEM key
let validated = typeof(curvePublic) === "string" &&
(Core.isValidPublicKey(curvePublic) || !curvePublic) &&
typeof(edPublic) === "string" &&
(Core.isValidPublicKey(edPublic) || !edPublic);
if (!validated) { throw new Error('INVALID_ARGS'); }
if (Env.supportMailboxKey === curvePublic && Env.supportPinKey === edPublic) { return false; }
if (Env.supportMailboxKey === curvePublic && Env.supportPinKey === edPublic && Env.supportMailboxKemKey === kemPublic) { return false; }
Env.supportMailboxKey = curvePublic;
Env.supportPinKey = edPublic;
Env.supportMailboxKemKey = kemPublic;
return true;
};

View File

@ -156,6 +156,7 @@ module.exports.create = function (config) {
adminEmail: config.adminEmail,
supportMailbox: config.supportMailboxPublicKey,
supportMailboxKey: undefined,
supportMailboxKemKey: undefined,
metadata_cache: {},
channel_cache: {},

View File

@ -620,6 +620,7 @@ var serveConfig = makeRouteCache(function () {
inactiveTime: Env.inactiveTime,
supportMailbox: Env.supportMailbox,
supportMailboxKey: Env.supportMailboxKey,
supportMailboxKemKey: Env.supportMailboxKemKey,
defaultStorageLimit: Env.defaultStorageLimit,
maxUploadSize: Env.maxUploadSize,
premiumUploadSize: Env.premiumUploadSize,

View File

@ -45,6 +45,7 @@ Stats.instanceData = function (Env) {
// we expect that you enable your support mailbox
data.supportMailbox = Boolean(Env.supportMailbox);
data.supportMailboxKey = Boolean(Env.supportMailboxKey);
data.supportMailboxKemKey = Boolean(Env.supportMailboxKemKey);
// do you allow registration?
data.restrictRegistration = Boolean(Env.restrictRegistration);

View File

@ -297,7 +297,7 @@ const factory = (Feedback, Hash, Util,
* 3.b. No ==> post our mailbox data to the messenger channel
*/
network.join(friend.channel).then(function (wc) {
var keys = Crypto.Curve.deriveKeys(friend.curvePublic, userObject.curvePrivate);
var keys = Crypto.Curve.deriveKeys(friend.curvePublic, userObject.curvePrivate, friend.kemPublic, userObject.kemPublic);
var encryptor = Crypto.Curve.createEncryptor(keys);
channels[friend.channel] = {
wc: wc,

View File

@ -721,7 +721,7 @@ const factory = (Crypto, Hash, Util, Realtime, Messaging,
}
var proxy = ctx.store.proxy;
var keys = Curve.deriveKeys(friend.curvePublic, proxy.curvePrivate);
var keys = Curve.deriveKeys(friend.curvePublic, proxy.curvePrivate, friend.kemPublic, proxy.kemPublic);
var data = {
keys: keys,
channel: friend.channel,

View File

@ -39,13 +39,18 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
return void cb('EFORBIDDEN');
}
var supportKemKey = NewConfig.supportMailboxKemKey;
if (isAdmin) {
return ctx.adminRdyEvt.reg(() => {
cb(null, {
supportKey: supportKey,
supportKemKey: supportKemKey,
myCurve: data.adminCurvePrivate || Util.find(ctx.store.proxy, [
'mailboxes', 'supportteam', 'keys', 'curvePrivate']),
theirPublic: data.curvePublic,
myKem: ctx.store.proxy.kemPublic,
theirKem: data.kemPublic,
notifKey: data.curvePublic
});
});
@ -53,8 +58,11 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
cb(null, {
supportKey: supportKey,
supportKemKey: supportKemKey,
myCurve: ctx.store.proxy.curvePrivate,
theirPublic: data.curvePublic || supportKey, // old tickets may use deprecated key
myKem: ctx.store.proxy.kemPublic,
theirKem: data.kemPublic, // || supportKemKey, // Needed for PQC, currently making messages unreadable
notifKey: supportKey
});
});
@ -63,7 +71,7 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
// Get the content of a ticket mailbox and close it
var getContent = function (ctx, data, isAdmin, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var theirPublic, myCurve;
var theirPublic, myCurve, theirKem, myKem;
nThen((waitFor) => {
getKeys(ctx, isAdmin, data, waitFor((err, obj) => {
if (err) {
@ -72,9 +80,11 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
}
theirPublic = obj.theirPublic;
myCurve = obj.myCurve;
theirKem = obj.theirKem;
myKem = obj.myKem;
}));
}).nThen(() => {
var keys = Crypto.Curve.deriveKeys(theirPublic, myCurve);
var keys = Crypto.Curve.deriveKeys(theirPublic, myCurve, theirKem, myKem);
var crypto = Crypto.Curve.createEncryptor(keys);
var cfg = {
network: ctx.store.network,
@ -121,7 +131,7 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
var channel = data.channel;
var title = data.title;
var ticket = data.ticket;
var supportKey, theirPublic, myCurve;
var supportKey, theirPublic, myCurve, theirKem, myKem;
var time = +new Date();
nThen((waitFor) => {
// Send ticket to the admins and call back
@ -132,13 +142,15 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
}
supportKey = obj.supportKey;
theirPublic = obj.theirPublic;
theirKem = obj.theirKem;
myKem = obj.myKem;
myCurve = obj.myCurve;
// No need for notifKey here: users can only create tickets for the
// currently used key
}));
}).nThen((waitFor) => {
// Create ticket mailbox
var keys = Crypto.Curve.deriveKeys(theirPublic, myCurve);
var keys = Crypto.Curve.deriveKeys(theirPublic, myCurve, theirKem, myKem);
var crypto = Crypto.Curve.createEncryptor(keys);
var text = JSON.stringify(ticket);
var ciphertext = crypto.encrypt(text);
@ -227,7 +239,7 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
if (!mailbox) { return void cb('E_NOT_READY'); }
if (!anonRpc) { return void cb("anonymous rpc session not ready"); }
if (!data?.ticket) { return void cb('E_NO_DATA'); }
var theirPublic, myCurve, notifKey;
var theirPublic, myCurve, notifKey, myKem, theirKem;
var time;
nThen((waitFor) => {
// Get correct keys
@ -238,11 +250,13 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
}
theirPublic = obj.theirPublic;
myCurve = obj.myCurve;
theirKem = obj.theirKem;
myKem = obj.myKem;
notifKey = obj.notifKey;
}));
}).nThen((waitFor) => {
// Send message
var keys = Crypto.Curve.deriveKeys(theirPublic, myCurve);
var keys = Crypto.Curve.deriveKeys(theirPublic, myCurve, theirKem, myKem);
var crypto = Crypto.Curve.createEncryptor(keys);
var text = JSON.stringify(data.ticket);
var ciphertext = crypto.encrypt(text);
@ -417,7 +431,8 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
var t = Util.clone(ctx.supportData[ticket]);
getContent(ctx, {
channel: ticket,
curvePublic: t.curvePublic
curvePublic: t.curvePublic,
kemPublic: t.kemPublic
}, false, waitFor((err, messages) => {
if (err) {
if (err.type === 'EDELETED') {
@ -829,6 +844,7 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
name: Util.find(first, ['sender', 'name']),
notifications: Util.find(first, ['sender', 'notifications']),
curvePublic: Util.find(first, ['sender', 'curvePublic']),
kemPublic: Util.find(first, ['sender', 'kemPublic']),
channel: Hash.createChannelId(),
title: first.title,
time: last.time,
@ -995,7 +1011,7 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
// ADMIN COMMANDS
let updateServerKey = (ctx, curvePublic, curvePrivate, cb) => {
let updateServerKey = (ctx, curvePublic, curvePrivate, kemPublic, cb) => {
let edPublic;
try {
let pair = Crypto.CryptoAgility.signKeyPairFromSeed(Util.decodeBase64(curvePrivate));
@ -1005,7 +1021,7 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
}
ctx.Store.adminRpc(null, {
cmd: 'ADMIN_DECREE',
data: ['SET_SUPPORT_KEYS', [curvePublic, edPublic]]
data: ['SET_SUPPORT_KEYS', [curvePublic, edPublic, kemPublic]]
}, cb);
};
let getModerators = (ctx, data, cId, cb) => {
@ -1021,11 +1037,14 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
let edPublic = proxy.edPublic;
const keyPair = Crypto.CryptoAgility.curveKeyPair();
const kemPair = Crypto.PQC.ml_kem.ml_kem512.keygen();
const newKeyPub = Util.encodeBase64(keyPair.publicKey);
const newKey = Util.encodeBase64(keyPair.secretKey);
const newKemPublic = Util.encodeBase64(kemPair.publicKey);
const oldKey = Util.find(proxy, ['mailboxes', 'supportteam', 'keys', 'curvePrivate']);
const oldKeyPub = Util.find(proxy, ['mailboxes', 'supportteam', 'keys', 'curvePublic']);
const oldKemPublic = Util.find(proxy, ['mailboxes', 'supportteam', 'keys', 'kemPublic']);
if (!newKey || !newKeyPub) { return void cb({ error: 'INVALID_KEY' }); }
let oldAdminChan;
@ -1091,7 +1110,7 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
});
}).nThen((waitFor) => {
// Send new key to server
updateServerKey(ctx, newKeyPub, newKey, waitFor((obj) => {
updateServerKey(ctx, newKeyPub, newKey, newKemPublic, waitFor((obj) => {
if (obj && obj.error) {
waitFor.abort();
return void cb(obj);
@ -1122,7 +1141,7 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
waitFor.abort();
if (oldSupportKey) {
// If we weren't able to store the new key, abort and restore old keys
return updateServerKey(ctx, oldKeyPub, oldKey, () => {
return updateServerKey(ctx, oldKeyPub, oldKey, oldKemPublic,() => {
return void cb(obj);
});
}
@ -1204,7 +1223,7 @@ const factory = (Util, Hash, Realtime, Pinpad, Crypt,
}).nThen((waitFor) => {
ctx.Store.adminRpc(null, {
cmd: 'ADMIN_DECREE',
data: ['SET_SUPPORT_KEYS', ['', '']]
data: ['SET_SUPPORT_KEYS', ['', '', '']]
}, waitFor(function (obj) {
if (obj && obj.error) {
waitFor.abort();

View File

@ -1681,6 +1681,10 @@ const factory = (Util, Hash, Constants, Realtime, ProxyManager,
edPrivate: ephemeralKeys.edPrivate,
curvePublic: ephemeralKeys.curvePublic,
curvePrivate: ephemeralKeys.curvePrivate,
kemPublic: ephemeralKeys.kemPublic,
kemPrivate: ephemeralKeys.kemPrivate,
dsaPublic: ephemeralKeys.dsaPublic,
dsaPrivate: ephemeralKeys.dsaPrivate,
},
};

View File

@ -105,6 +105,7 @@ define([
APP.supportModule.execCommand('CLOSE_TICKET', {
channel: channel,
curvePublic: data.curvePublic, // Support curve public for this ticket
kemPublic: data.kemPublic, // Support kem public for this ticket
ticket: APP.support.getDebuggingData({ close: true })
}, function (obj) {
if (obj && obj.error) { return void UI.warn(Messages.error); }
@ -116,6 +117,7 @@ define([
APP.supportModule.execCommand('REPLY_TICKET', {
channel: channel,
curvePublic: data.curvePublic, // Support curve public for this ticket
kemPublic: data.kemPublic, // Support kem public for this ticket
ticket: formData
}, function (obj) {
if (obj && obj.error) { return void UI.warn(Messages.error); }