Git Product home page Git Product logo

Comments (21)

vinliao avatar vinliao commented on August 11, 2024 4

Revision to the idea:

  • Instead of using a blank address, a Schnorr keypair is derived from the ECDH key
  • The shared Schnorr can be used to sign and can be used as the event's pubkey
  • Remove the "shared" tag (unnecessary)

Result:

  • Recipient's and sender's pubkey aren't present in the event
  • Event can now have a valid signature (the blank address couldn't)
  • Only both can pull, encrypt, sign, decrypt the private event

Below is the JS implementation of this revised idea. I've published an event to a relay, and it's actually accepted as a valid NIP-04 event.

async function generatePrivateEvent(priv, pub) {
  const unencryptedMessage = "supersecret"

  // `secp.getSharedSecret(senderPriv, "02" + recipientPub)`
  // and
  // `secp.getSharedSecret(recipientPriv, "02" + senderPub)`
  // produce the same value
  const sharedPointBytes = secp.getSharedSecret(priv, "02" + pub);
  const sharedPoint = toHexString(sharedPointBytes);
  const sharedX = sharedPoint.substr(2, 64)

  // not sure how safe this is, but sharedX is 32 bytes
  // which can be used as a privatekey, and which
  // a schnorr pubkey can be derived from
  const schnorrMutualKey = secp.schnorr.getPublicKey(sharedX)
  const schnorrMutualHex = toHexString(schnorrMutualKey)

  const iv = crypto.randomFillSync(new Uint8Array(16))
  const ivBase64 = Buffer.from(iv.buffer).toString('base64')
  const cipher = crypto.createCipheriv(
    'aes-256-cbc',
    Buffer.from(sharedX, 'hex'),
    iv
  )

  let encryptedMessage = cipher.update(JSON.stringify(unencryptedMessage), 'utf8', 'base64')
  encryptedMessage += cipher.final('base64')
  encryptedMessage += "?iv=" + ivBase64

  const unixTime = Math.floor(Date.now() / 1000);
  const data = [0, schnorrMutualHex, unixTime, 4, [], encryptedMessage];

  // event id is sha256 of data above
  // sig is schnorr sig of id
  const eventString = JSON.stringify(data);
  const eventByteArray = new TextEncoder().encode(eventString);
  const eventIdRaw = await secp.utils.sha256(eventByteArray);
  const eventId = toHexString(eventIdRaw);

  // sharedX is used as a private key here
  const signatureRaw = await secp.schnorr.sign(eventId, sharedX);
  const signature = toHexString(signatureRaw);

  return {
    id: eventId,
    pubkey: schnorrMutualHex,
    created_at: unixTime,
    kind: 4,
    tags: [],
    content: encryptedMessage,
    sig: signature
  }

  // example output
  /*
  {
    id: '24d6b6ba8b938c393ed13ee8fb1cfd639fc841f936dcbe28a039338340acda03',
    pubkey: '6646a4323f0e7234488579975d2dcaabfe87e8b086a91ddc3f08f4c9e978e82d',
    created_at: 1643932277,
    kind: 4,
    tags: [],
    content: 'lXDprQheAyTQ10AyKmYXcg==?iv=15FpmW00Nk+Fe+zdM+KCXA==',
    sig: '728b8dec8719cef760afd5a6b46ee02054441e9547c1c3af5c6fdd834b2c4ea7aeecb0b1c1584220c92278a1b987c1c8243586c5319fe5d76794e5c6341fc1c4'
  }
  */
}

async function decrypt(priv, pub) {
  const sharedPointBytes = secp.getSharedSecret(priv, "02" + pub);
  const sharedPoint = toHexString(sharedPointBytes);
  const sharedX = sharedPoint.substr(2, 64)

  const schnorrMutualKey = secp.schnorr.getPublicKey(sharedX)
  const schnorrMutualHex = toHexString(schnorrMutualKey)

  // request to relay: ["REQ", "foobar", [{"authors": schnorrMutualHex}]]
  // let's say we get back the data, then parse the encrypted message
  const encryptedMessage = "lXDprQheAyTQ10AyKmYXcg=="
  const iv = Buffer.from("15FpmW00Nk+Fe+zdM+KCXA==", "base64")

  const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(sharedX, 'hex'), iv)
  const message = Buffer.concat([decipher.update(encryptedMessage, 'base64'), decipher.final()]);
  console.log(message.toString()) // supersecret
}

I've sent a private message to @fiatjaf's known pubkey on Branle (3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d) with a dummy pubkey (ab1a33b0cf3d8f8896c433e6996744e48f1401e6fbc94aea6f84291074fb1b75) through wss://relayer.fiatjaf.com. Here's what the event looks like:

{
  id: '66337672d578940cf97ec84a8d82e547564ec527a26ac275b039d52c7a7cfe4b',
  pubkey: 'c00a84bd0c368c32a339e7200995c419ebd4a9fdc0b4230f82271eb9ea13d394',
  created_at: 1643934337,
  kind: 4,
  tags: [],
  content: 'KO8Cqvw5RIuynVkXjJze5g==?iv=n4YGW9p0L/RoFbs0t3vBYA==',
  sig: 'f0e0b1f54920c6fa636b6151c4268e807e7f8f5457042a2e318d46171edc89b1956697d1e7cda011ad074252675c95db734104f19a243351b63c9275062bee86'
}

Neither pubkeys are displayed on the event. With this improvement, NIP-04 only leaks timestamp.

from nostr.

fiatjaf avatar fiatjaf commented on August 11, 2024 3

I like this idea, but for a different NIP instead of NIP04.
And one can use random keys to sign instead of a blank key.

Maybe instead of using a shared key parties could instead send an initial message through NIP04 containing a random encryption key and then proceed using this new protocol using that?

from nostr.

fiatjaf avatar fiatjaf commented on August 11, 2024 3

By encryption key I meant the key that would be used to encrypt the messages using the same AES-CBC that NIP04 was using already, not RSA.

from nostr.

vinliao avatar vinliao commented on August 11, 2024 1

Or, instead of a blank key (inbox address), both parties use MuSig2 as the event's pubkey.

Ideally, no one can even surveil which pubkey talks to which pubkey. The problem here seems to be privately announcing sender's pubkey before both can talk privately using this new protocol.

Introducing "encryption key" to Nostr means future devs have to juggle between RSA and Schnorr, no?

I think loquaz and nostr.chat are suited to implement this idea, to experiment and see whether this new protocol actually works.

from nostr.

vinliao avatar vinliao commented on August 11, 2024 1

I like this idea, but for a different NIP instead of NIP04.

If this idea actually works in improving privacy, I agree that this should be a new NIP and a new kind.

from nostr.

HamishMacEwan avatar HamishMacEwan commented on August 11, 2024 1

Please poke holes in this idea (if you find any); I'd like to know whether this idea has merit or it's just full of hot air.

Wouldn't it be easier, on the client side, for the sender to create two disposable profiles, and NIP-04 a message to the receiver from the first profile with the second profile (and other information if desired, such as a private relay) as encrypted payload.

Receiver then creates a disposable profile and begins the conversation with the second profile from the sender (aka initiator).

It appears to have the advantage of being able to be implemented (🤞) using existing primitives, but could be formalised in a new NIP with a new kind.

(I see similarities to the concept you describe here #70 (comment))

from nostr.

Giszmo avatar Giszmo commented on August 11, 2024 1

@fiatjaf you are derailing this issue a bit.

I would hope to see the above to be pushed forward to replace nip-4 over some lets-use-telegram or lets-trust-relays-pinky-swear. The metadata leak of what I last described above is minimal and way better than nip-4 which sadly now is the default in all clients.

Not all group chats are public but for public group chats you could use kind a new "kind" but apply most of kind-1 to it. Clients would not show those in the feed but only show those groups that you subscribed to. A group is simply tagged like a hashtag. Most importantly for this would be to make abundantly clear that these chats are public.

from nostr.

fiatjaf avatar fiatjaf commented on August 11, 2024 1

What I was saying is that NIP-04 should not be replaced at all. These ideas to make DMs better belong to different NIPs.

Also since there are multiple different approaches being talked about by different people and none of them seems to be obviously the best possible way to do anything (they all look hacky and ugly to me), my suggestion was that interested parties experiment with them.

from nostr.

vinliao avatar vinliao commented on August 11, 2024

I've implemented a half-baked POC to test this idea, and it's pretty... bad - it's a super-mega-complex stuff added on top of NIP-04 with marginal benefit. Not worth the headache.

from nostr.

fiatjaf avatar fiatjaf commented on August 11, 2024

Hahaha, thank you for your honesty and for actually trying to implement. I was indeed afraid it would be too complex, but I thought that was just me being lazy. Since you, the creator of the idea, say it's bad super-mega-complex, then I'll follow you.

from nostr.

vinliao avatar vinliao commented on August 11, 2024

It seems to me that using two disposable profiles makes impersonation easy. I think the alias system described on #70 is good enough to obfuscate NIP-04 pubkeys, and it's also extensible enough for other purposes.

from nostr.

HamishMacEwan avatar HamishMacEwan commented on August 11, 2024

It seems to me that using two disposable profiles makes impersonation easy.

Three disposable profiles are involved, and how do you see any third party gaining access to the private keys of any of them (required for impersonation), or linking them to either of the main profiles?

from nostr.

vinliao avatar vinliao commented on August 11, 2024

I can create two pubkeys, send one to you (encrypted), claiming to be some popular guy, telling you to trust me that I'm that popular guy, then tell you to chat with me through the pubkey I've encrypted to you. That counts as impersonation to me.

from nostr.

HamishMacEwan avatar HamishMacEwan commented on August 11, 2024

telling you to trust me that I'm that popular guy

This popular guy's public key is not well known?

Telling me to do something, particularly to trust, isn't the same as me doing it.

This may be an attempt at impersonation but it isn't easy to succeed with this approach.

from nostr.

vinliao avatar vinliao commented on August 11, 2024

Thanks to the comment above by @fiatjaf and @HamishMacEwan - I took some inspiration from their ideas. The ideas thrown around in this issue have merit. I've built a working POC: https://github.com/vinliao/clust. NIP-04 is more private now.

It's still clunky as hell, but it works. I'm gonna iron out a few UX details before sharing it to the Telegram group. Let me know if the explanation in the readme is lacking, or if there's a bug in the CLI app.

from nostr.

Giszmo avatar Giszmo commented on August 11, 2024

So what is the state of this? Please update the original description as we all agree that events with publicly known private keys are not cool. Heck, random others could even delete them as they appeared.

So do I understand right the last iteration was:

  1. Alice derives kind-4 shared secret with Bob's pubKey
  2. Alice derives key pair from secret
  3. Alice sends events keypair's pubkey
  4. Bob, knowing the privKey can decrypt events
  • Only Alice and Bob know sender and recipient of each message
  • For outside observers, these events are not attributable
  • Alice might have to poke Bob. For example any kind-4 DM or opening the "DM with Bob" view could trigger clients to check kind-[to be announced] DMs, too.
  • These DMs have plausible deniability as the recipient can forge messages to himself from the sender.
  • Additional signatures in the content could attest actual authorship.

I find this protocol quite straight-forward and if I understood it correctly, could implement and try it in NostrPostrLib, too.

from nostr.

fiatjaf avatar fiatjaf commented on August 11, 2024

I don't know the state of this, we're free to experiment with many things. So let me just throw some random thoughts:

NIP-04 was never supposed to be the perfect solution, just the simplest possible protocol until someone else made a good thing later (I was actually opposed to NIP-04 since the beginning).

My personal preferences are, in order:

  1. Use NIP-04 to signal another transport mechanism outside of Nostr (like, I have no idea, direct Tor connections between the peers, or something else);
  2. Use NIP-04 with relays that promise to not reveal messages to anyone except people who know a "room secret" or something like that.

Another unrelated thing I think is that we should try to address other use cases before DMs. It's very hard to gain users for DMs, the market is too crowded, and Nostr doesn't excel in things that are needed in DMs -- for example, phone notifications. I think group chats (which are public by design) could benefit more from Nostr (and benefit Nostr more).

from nostr.

Giszmo avatar Giszmo commented on August 11, 2024

So I think I came up with something simple enough to gain traction. I just updated the referenced nips PR to a completely different approach which just removes the leaky data from the messages. A summary is here.

The upside over nip-4:

  • Not leaking recipient data in events at all if they are following us anyway or we can poke them out of band to check their inbox or they can just check all DMs (currently this is trivially doable).
  • Good anonymity set against timestamp correlation analysis as events only reveal that pubkey sent a DM.
  • Simple to implement if you already support nip-4

Downside:

  • For privacy, the recipient has to query more events and check if they can be decrypted
  • Once nostr traffic picks up and querying all kind-18 events becomes hard, recipient requires defence against relays spying on his events of interest.
  • Replying to an event is not covered yet. I'm not sure it's needed. Let clients simply process an inline [](e/<event id>). Of course such event linking could lead to clients leaking interest in kind-18 events again.

The linked explanation:

All mentioned issues are addressed by a massive simplification of the protocol. The new protocol is:

  • Pretend it's nip-04
  • Put recipient only on first messages or not if they are following you already. False recipients are encouraged on subsequent messages.
  • Query all your follows' kind-18 events regardless of the advertised recipient.
  • Query all kind-18 events that advertised you as recipient.
  • Query all events from pubkeys that previously sent you events using some privacy tools such as TOR.
  • Or ... query all kind-18 events as long as it's low volume.

from nostr.

tclementdev avatar tclementdev commented on August 11, 2024

Alternatively, would there be any interest in allowing relays to require REQ messages for kind 4 events to include a pubkey and signature? So that relays only deliver events if the REQ signature is valid and pubkey matches the recipient for the kind 4 events. I know it doesn't help if you do not trust the relays, but could be a simple change towards improving the situation.

from nostr.

fiatjaf avatar fiatjaf commented on August 11, 2024

@tclementdev that's what NIP-42 is doing, and is being implemented in multiple relays and clients already.

from nostr.

tclementdev avatar tclementdev commented on August 11, 2024

@fiatjaf ah thank you, I missed that. Now one would need to make sure to only use relays that restrict kind 4 access for DMs.

from nostr.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.