30817:sigil
SIGIL
- signed by
- npub1melv6...rt5c24
- published
- 2025-07-12
- event
- cdb01ee420...46b5
NIP-XX: SIGIL – Secure Interoperable Generic JSON Layer
| Field | Value |
|---|---|
| NIP | (TBD – to be assigned by editors) |
| Title | SIGIL – Secure Interoperable Generic JSON Layer |
| Author | @melvincarvalho |
| Status | Draft |
| Type | Standards Track |
| Created | 2025‑07‑11 |
| License | CC0‑1.0 |
1 Abstract
SIGIL defines a deterministic envelope that lets any JSON object be hashed and authenticated with a Nostr key‑pair using RFC 8785 canonicalisation and BIP‑340 Schnorr signatures. The envelope keeps the familiar id and pubkey/sig fields while removing the 6‑item Nostr signing tuple, making the format suitable for off‑relay storage, cross‑protocol bridges and application‑specific events.
2 Motivation
- **Flexibility ** – Applications often need to sign richer JSON than the simple
[0, pubkey, created_at, kind, tags, content]tuple. - **Determinism ** – JSON must be turned into a single, byte‑for‑byte stable representation before hashing.
- **Interoperability ** – By re‑using Schnorr/BIP‑340 and the existing Nostr key‑space, libraries, wallets and relays can adopt SIGIL with minimal changes.
3 Terminology
- Event – a JSON object that must contain at least the fields in §4.
- Canonical JSON – the result of applying RFC 8785 (JCS) to an Event.
- SIGIL ID – the 32‑byte SHA‑256 digest of Canonical JSON with the
sigfield empty.
4 Envelope Format
{
"created_at": 1688540400, // Unix seconds
"kind": 1,
"tags": [["p","npub1…"]],
"content": "Hello, SIGIL!",
"pubkey": "ab…cd", // 32‑byte compressed SEC
"sig": "", // 64‑byte hex Schnorr
"id": "" // 32‑byte hex – derived, see §5
}
Additional application keys are allowed but MUST NOT begin with sig, pubkey or id.
5 Signing Procedure
- Prepare an Event object with all keys except
sigandid. - Insert
sig:""(empty string) temporarily. - Canonicalise the Event using RFC 8785.
- Compute
id = sha256(canonical_bytes). - Sign the canonical bytes with the private key per BIP‑340.
- Encode the 64‑byte signature and the 33‑byte public key as lower‑case hexadecimal.
- Store
id,pubkey,sigback into the Event.
6 Verification
To verify a candidate Event:
- Ensure required fields exist and are hex‑encoded of the correct length.
- Copy the Event; set its
sigfield to the empty string. - Canonicalise the copy using RFC 8785.
- Re‑hash; the digest must equal the
idfield. - Verify the Schnorr signature (
sig) over the canonical bytes withpubkey.
7 Reference Implementation (Node 18+)
// npm i canonicalize @noble/secp256k1
import canonicalize from 'canonicalize';
import { schnorr } from '@noble/secp256k1';
import { createHash, randomBytes } from 'crypto';
const canonBytes = (obj) => Buffer.from(canonicalize(obj), 'utf8');
const hash = (buf) => createHash('sha256').update(buf).digest();
export async function signEvent(event, sk) {
const ev = { ...event, sig: '' };
const can = canonBytes(ev);
ev.id = hash(can).toString('hex');
ev.pubkey = Buffer.from(await schnorr.getPublicKey(sk, true)).toString('hex');
ev.sig = Buffer.from(await schnorr.sign(can, sk)).toString('hex');
return ev;
}
export async function verifyEvent(ev) {
const { sig, ...copy } = ev;
copy.sig = '';
const can = canonBytes(copy);
if (hash(can).toString('hex') !== ev.id) return false;
return schnorr.verify(ev.sig, can, ev.pubkey);
}
// quick demo
(async () => {
const sk = randomBytes(32);
const evt = await signEvent({
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags: [],
content: 'Hello SIGIL!'
}, sk);
console.log('verified?', await verifyEvent(evt));
})();
8 Rationale
- RFC 8785 delivers a battle‑tested, language‑agnostic canonical form suitable for cryptographic transforms.
- BIP‑340 Schnorr offers smaller signatures and easier security proofs than ECDSA.
9 Compatibility
Existing relays that ignore unknown keys can forward SIGIL events unchanged. Clients that understand SIGIL can validate and display them; others can treat content as opaque text.
10 Security Considerations
- Private keys must be generated with high‑entropy sources.
- A failed signature verification MUST invalidate the whole Event.
- When embedding binary data, always use base64/hex – raw control characters are disallowed by RFC 8785.
11 Test Vectors
TBD (include BIP‑340 test vectors serialised as SIGIL).
12 References
- RFC 8785 – JSON Canonicalisation Scheme (JCS)
- BIP 340 – Schnorr Signatures for secp256k1
End of specification.
Discussion
Connect a key to comment.