npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@session.js/blinded-session-id

v1.0.8

Published

Utility JavaScript library with methods to work with Session's blinded Session ID

Readme

@session.js/blinded-session-id

Utility JavaScript library with methods to work with Session's blinded Session ID. Uses @noble/curves v2, @noble/hashes v2 and @session.js/scalars under the hood.

Example of unblinded Session ID (05-prefixed): 057aeb66e45660c3bdfb7c62706f6440226af43ec13f3b6f899c1dd4db1b8fce5b

Example of blinded Session ID (15-prefixed): 15d9fd3a6c3c5ddf7500b862174f205ab27164232d9b10fe31f145e61629b676e3

Example of blinded Session ID (25-prefixed): 253b991dcbba44cfdb45d5b38880d95cff723309e3ece6fd01415ad5fa1dccc7ac

Blinded IDs are used on Session SOGS to conceal identity of room's users.

Usage

Blinding

Blinding with Session ID or x25519 public key

To "blind" Session ID, we need the Sesion ID itself and the SOGS's public key (the part after ?public_key= in the SOGS url).

You have multiple choices on input format. All blindSessionId signatures require type property defining type of the output Session ID (15- or 25-prefixed). The simpliest format is passing both sessionId and sogsPublicKey as hex-encoded strings.

You'll get two variants of the blinded Session ID. Both outputs are valid and possible and there is absolutely no way to determine which one is correct (unless you have user's ed25519 key, more on that below).

import { blindSessionId } from "@session.js/blinded-session-id";

blindSessionId({
	type: "15",
	sessionId: "057aeb66e45660c3bdfb7c62706f6440226af43ec13f3b6f899c1dd4db1b8fce5b",
	sogsPublicKey: "cb4fd6199b84dc3664f0373354341a01007ecaa99a388496fe8775b9b76a253b",
});
// Either of those will be the blinded ID
// => [
//      "15383d0a3ba605abe3b5b7343102be3fc0026056b9812e06f6daee3be62a6a5663",
//      "15383d0a3ba605abe3b5b7343102be3fc0026056b9812e06f6daee3be62a6a56e3"
//    ]

You can also pass sogsPublicKey as Uint8Array, in case you already have it decoded in your app. You can also pass Session ID as x25519 public key as Uint8Array in case you already have it decoded in your app, but keep in mind that Session ID is 33 bytes long and x25519 public key is 32 bytes long, because Session ID has a prefix byte (0x05, 0x15 or 0x25).

import { blindSessionId } from "@session.js/blinded-session-id";

blindSessionId({
	type: "15",
	x25519PublicKey: new Uint8Array([
		122, 235, 102, 228, 86, 96, 195, 189, 251, 124, 98, 112, 111, 100, 64, 34, 106, 244, 62, 193,
		63, 59, 111, 137, 156, 29, 212, 219, 27, 143, 206, 91,
	]),
	sogsPublicKey: new Uint8Array([
		203, 79, 214, 25, 155, 132, 220, 54, 100, 240, 55, 51, 84, 52, 26, 1, 0, 126, 202, 169, 154, 56,
		132, 150, 254, 135, 117, 185, 183, 106, 37, 59,
	]),
});
// Either of those will be the blinded ID
// => [
//      "15383d0a3ba605abe3b5b7343102be3fc0026056b9812e06f6daee3be62a6a5663",
//      "15383d0a3ba605abe3b5b7343102be3fc0026056b9812e06f6daee3be62a6a56e3"
//    ]

When converting Session's x25519 (Session ID) to the ed25519 key (needed for blinding), the sign (+/-) information is lost, producing two equally valid possibilities. So unless you have user's ed25519 public key, you cannot have a guaranteed determenistic correct single output for blinding, you will always have to account for two outputs.

Hang on tight, we're diving into some real nerdy cryptography here!

You know how Session actually has two keypairs — x25519 (public, secret) and ed25519 (public, secret), all of which are derived from the main secret seed (which can be encoded into words with mnemonic/secret phrase) and Session ID is the public x25519 key?

In order to "blind" Session ID we first calculate a "blinding factor" k using modulo of SOGS public key and ed25519's ORDER constant. Then we calculate kA by multiplying user's ed25519 public key and k. Finally, we convert kA to hex and prepend it with 15 to mark it as a "blinded" Session ID.

But wait a second, where did "user's ed25519 public key" come from? Remember, Session ID is a x25519 public key, not ed25519 public key. These are two different keypairs, two different elliptic curves and they behave in very different ways!

That's where the major flaw of Session comes in. Technically, we could just settle on some way of converting value from one curve to another and vice verse. In fact, that's precisely what libsodium (and official Session clients) do using crypto_sign_curve25519_pk_to_ed25519. In the end, both curves are just mathematical curves and translating one to another should be determenistic.

There is just one problem though: x25519 does not have negative values, while ed25519 does have them. So ed25519's 123 converted to x25519 would be 456 and vice-verse, but what happens when we convert ed25519's -123 to x25519 curve is... 456 too! See the problem? Two different values producing same output.

And this wouldn't be bad if we only wanted x25519 for blinding, but as you saw earlier, we need ed25519 keys. Now you want to convert x25519 back to ed25519 and only have this x25519 value: 456. Was it 123 or -123 in ed25519 that produced that number?

So what happens when we try to convert x25519 public key (Session ID) -> ed25519 public key (for blinding) is that we end up with two keys since we don't know whether ed25519 should be positive or negative.

If only we had user's secret seed (mnemonic), we could easily calculate ed25519 and avoid x25519 -> ed25519 conversion issue at all... But alas, Session IDs are using x25519 and converting x25519 to ed25519 produces two results because of the lost signing bit.

More on blinding: “Blinded ID в Session и что с ними не так” by li0ard

Blinding with ed25519 public key

If you do have user's ed25519 public key (e.g. you control the secret seed, running from the user's client, etc), you can pass it directly instead of Session ID or x25519PublicKey and get a single, correct blinded ID.

import { blindSessionId } from "@session.js/blinded-session-id"

blindSessionId({
	type: "15",
	ed25519PublicKey: new Uint8Array([
		240, 55, 163, 9, 91, 114, 247, 212, 93, 217, 12, 245, 213, 114, 251, 182, 195, 95, 46, 4, 211,
		85, 177, 217, 201, 218, 3, 95, 202, 132, 158, 151,
	]),
	sogsPublicKey: "cb4fd6199b84dc3664f0373354341a01007ecaa99a388496fe8775b9b76a253b",
});
// => 15383d0a3ba605abe3b5b7343102be3fc0026056b9812e06f6daee3be62a6a56e3

There is no way to convert x25519 public key to ed25519 public key without losing the sign bit and thus getting two possible ed25519 keys and two possible blinded Session IDs.

Unblinding

Unblinding is a security vulnerability discovered by li0ard possible because 15-prefixed IDs use SOGS's public key as blake2b hash input, which is publicly known, making it possible to reverse 15-prefixed blinding by using the same sogsPublicKey.

In 25-prefixed IDs this issue was fixed by using both x25519 public key and SOGS's public key as blake2b hash input, so unless you know user's 05-prefixed Session ID it's impossible to unblind 25-prefixed Session ID, and if you do, there is no sense to unblind it anyway.

Unblinding 15-prefixed Session IDs

To unblind a Session ID, pass it as sessionId and SOGS's public key in either hex-encoded string or as Uint8Array.

import { unblindSessionId } from "@session.js/blinded-session-id"

unblindSessionId({
	sessionId: "15264c132e2e72a9c50b7a981eac11a48b3e51ae5a0ea45ea47deb519a3fa76612",
	sogsPublicKey: "ac9c872e525a58970df6971655abb944a30b38853442a793b29843d20795e840",
});
// => 057aeb66e45660c3bdfb7c62706f6440226af43ec13f3b6f899c1dd4db1b8fce5b

Unblinding 25-prefixed Session IDs

Unblinding 25-prefixed Session IDs is impossible.

Advanced usage

getBlindingK

generates a hash for blinding/unblinding using blake2b

import { getBlindingK } from "@session.js/blinded-session-id/utils";

const sogsPublicKey = new Uint8Array([
	203, 79, 214, 25, 155, 132, 220, 54, 100, 240, 55, 51, 84, 52, 26, 1, 0, 126, 202, 169, 154, 56,
	132, 150, 254, 135, 117, 185, 183, 106, 37, 59,
]);
getBlindingK(sogsPublicKey);
// => Uint8Array(32) [ 27, 203, 111, 10, 221, 88, 187, 146, 221, 11, 206, 55, 7, 86, 218, 223, 21, 123, 29, 214, 198, 182, 3, 40, 188, 123, 190, 73, 35, 122, 140, 13 ]

Made for Session.js

Use Session messenger programmatically with Session.js: Session bots, custom Session clients, and more.

Donate

hloth.dev/donate · Tor: hlothdevzkti6suoksy7lcy7hmpxnr3msu5waokzaslsi2mnx5ouu4qd.onion/donate

License

MIT