Oddbean new post about | logout
 https://i.nostr.build/XC4YYPFWMeff9Q7A.jpg 
 What’s wrong with NIP-04? 
 nostr:nprofile1qqsd236myjzpu489zzr6pxcx0jtrn04pezjnqft23a2pykyuszvwr3qpzamhxue69uhhyetvv9ujumn0wd68ytnzv9hxgtcy2t490 
 NIP04 is great for OTP uses, be a lot cooler if we kept support of both. There is no single solution for all problems.🫡 
 Nip 17 is a lot better for OTP.  
 Any good examples on how I can reach a single person with the message without knowing anything about them but their npub only? And not being able to query any relays and just broadcast it as wide as possible? 
 Yes the CIA and NSA would like to know too 😆
nostr:nevent1qqstc2y76uxh4437thna535utwxe2sxvvfhxqa8t4nd7d3hdhxxu5xcpzemhxue69uhk2er9dchxummnw3ezumrpdejz7q3q37c5pd8gmhhe0njtsgwjgunc5xjr2vmzvglkgqs5sjeh972gqqxqxpqqqqqqzgad9sv 
 You can send the GiftWrap to all relays if you want. Same as Nip04.  
 So it means now I have to send two DMs, one with 04 and one with 17 😭😭😭 
 Maybe.. but just for now. Or just do one with NIP 17. People can use 0xchat to see it on iOS and Android, Snort/Coracle/Coop in the desktop, and Amethyst on Android. 

There are options available. 
 Can’t ask normal users to jump through the hoops, if they are on Damus or any other client that yet to support it, I’ll have a long list of people complaining about broken OTP 😭 
 This is why ie6 lasted so long, too scared to rip the bandaid

Nip04 is just bad it's better to fix it now while we're small and go through some short term pain imo 
 can it be blocked at the relay level? relays could just stop accepting them, that' would orce the hand of devs to remove it. 
 Good point, I will remove it from my relays  
 We still use it for otp, so i doubt we would just remove it unless someones wants to rewrite our otp code. It’s easy for people who don’t have existing code around to say just remove it. 
 Yes it's definitely easier to say than fix, but don't you agree it's bad for DMs? 
 Obviously 
 Always has been the case, people just make a blank statement “<thing> is bad” and have 0 regard for all that came before or relies on it. Plus “bad” is only based on use case, and nothing more than that. 
 Looking at nip17, I still have no idea how I can use nostr-tools and create a correct DM. One part of being secure is having less ways to shoot yourself into a foot, and I see a lot of landmines that will reap my legs off when I read it. I asked for a good example using nostr-tools and failed to find even one yet. 😭😭😭😭 
 https://github.com/vitorpamplona/lazereyes/blob/main/js/nip17.js 
 Just call createNIp17wraps with the message and keys.  
 async function createNip17Wraps(message, senderPubkey, receiverPubkey)

Where do I stick my SK into? And what do I do with the returned values that are in non-event format? 
 Humm.. you might need to tweak the code. This uses the browser's expression to sign via window.nostr... 

The returned values are two wraps for the sender and the receiver. All you need to do is to broadcast them to the relays each one uses.  
 Ok, I’ll take a look again and see if I can implement it. I assume that I do not need to send the “sender” part since it is OTP and nobody needs to check it as a sender 
 Yep, I guess it would be even better since the sender doesn't need a copy of the code. 
 I implemented it for the nostr.build just now but not sure if it even works. I still send 04 and now 17 as well. I have no way to verify it 😅 
 I will check tomorrow, but I only got the nip04. 

Also, you should ask for the nprofile in the login page. In that way, you will have the relay info to get the DM relays and send the DMs. :)  
 Is the code open somewhere?  
 😅 the code that does the sending is a complete mess and has a non-public info in it. I’ll try to clean it up and open the source for it. Need to parameterize some of the non-public (but not a high value secret or anything) info. 
 t-y Fishcake 
 This took me way longer than I would have liked, but here is the working Typescript code that makes those god damn GW DMs.

// Create DM event
export async function creatNIP17DMEvent(
  fromSk: string,
  toNpub: string,
  dm: string[],
): Promise<Array<Event>> {
  const skBuff = hexToBytes(fromSk)
  const events: Array<Event> = []

  while (dm.length > 0) {
    const msg = dm.shift()
    if (msg)
      events.push(createNip17Wraps(msg, toNpub, skBuff))
  }

  return events
}

function getNow(): number {
  return Math.floor(Date.now() / 1000)
}

function getRandomNow(): number {
  return getNow() - Math.floor(Math.random() * 172800)
}

function nip44Encrypt(rumor: Rumor, privateKey: Uint8Array, publicKey: string): string {
  const key = nip44.v2.utils.getConversationKey(privateKey, publicKey)
  return nip44.v2.encrypt(JSON.stringify(rumor), key)
}

function createWrap(event: Event, recipientPublicKey: string): Event {
  // Get random secret key to wrap the event
  const randomSK = generateSecretKey()
  return finalizeEvent({
    kind: 1059, // Gift wrapped event
    content: nip44Encrypt(event, randomSK, recipientPublicKey),
    created_at: getRandomNow(),
    tags: [["p", recipientPublicKey]]
  }, randomSK) as Event
}

function createSeal(event: Rumor, privateKey: Uint8Array, recipientPublicKey: string): Event {
  return finalizeEvent({
    kind: 13, // Seal
    content: nip44Encrypt(event, privateKey, recipientPublicKey),
    created_at: getRandomNow(),
    tags: []
  }, privateKey)
}

function createRumor(message: string, senderPubkey: string, receiverPubkey: string): Rumor {
  const rumor = {
    kind: 14, // As per specs, should be Kind 14 event
    content: message,
    pubkey: senderPubkey,
    created_at: getNow(),
    tags: [["p", receiverPubkey]],
  } as Rumor
  rumor.id = getEventHash(rumor)
  return rumor
}

function createNip17Wraps(message: string, recipientPublicKey: string, senderSk: Uint8Array): Event {
  const senderPubkey = getPublicKey(senderSk)
  return createWrap(
    createSeal(
      createRumor(message, senderPubkey, recipientPublicKey),
      senderSk, recipientPublicKey),
    recipientPublicKey)
} 
 One fix for types:

function nip44Encrypt(rumor: Rumor | VerifiedEvent, privateKey: Uint8Array, publicKey: string): string {
  const key = nip44.v2.utils.getConversationKey(privateKey, publicKey)
  return nip44.v2.encrypt(JSON.stringify(rumor), key)
}


It does not have any impact, just cosmetics 
 It works! :)  
 Yeah! I do suggest to make a clear documentation about how it should be done, I will contribute if needed. It is super confusing now and no single NIP to look at 🫂😭 
 Maybe send a PR with some of this code to nostr-tools?  
 I should add nip40 expiration to the wrap too I think, if the event is OTP 
 Great idea  
 I had some other issues, and didn’t have the messages actually sent anywhere, now they are but none of the clients see them only 04. Hence my point, if it is complex and close to impossible to implement even with examples, it will not be used or will be misused or misimplemented 😭😭😭 
 Very good boy
You can tag me in one and send to relay.utxo.one/chat I'll help you test 🐶🐾

Relay will give you an error right away if it didn't work
 https://i.nostr.build/lyNDHtFTkFQU6r0L.jpg 
 I am blasting the DMs, and they go out but none of them are seen by the client. My conclusion is that nip17 sucks ass, and only focuses on solving c2c but not s2c problem. I cannot have DMs sent to a specific relay, I don’t know who is using what and when, I need for DMs to reach the recipient and for me to do not need to know about their nprofile or anything else besides their npub, I cannot rely on any relay to have any info about any npub, I need to send it fast and with no WS fuss that is client oriented shit. So many complaints 😭😭😭😅 
 Thanks! This will help ! 🫂🙏🏻🫡 
 If you want to use something else to login in websites theres also the option to send an intent for a nostr signer like amber.
You send a client authentication for example and them ask the user to paste the signed event from the clipboard 
 Already have that, but not all people have or can have an extension 
 I like Amethyst's approach here. I didn't even know that the "incognito person" was a toggle button until someone told me about it. I just assumed that all private DMs were, well, private. NIP-17 by default FTW! 
 Almost have it done for nostr:nprofile1qqs2xugc5jyguqkj36rk0syv4tmnkjdtmtperttl7x9rqjy3ustdcvcppemhxue69uhkummn9ekx7mp0qy2hwumn8ghj7un9d3shjtnyv9kh2uewd9hj7qg6waehxw309aek2mnyd96zumn0wdnxcctjv5hxxmmd9uxa0uz8...

the conversation key requirement for nostr-tools is making it a pain, though. 😅