[{"data":1,"prerenderedAt":1426},["ShallowReactive",2],{"navigation":3,"-jwt-jwe-ecdh-es":167,"-jwt-jwe-ecdh-es-surround":1423},[4,22,78,106,141,148],{"title":5,"path":6,"stem":7,"children":8},"Introduction","\u002Fgetting-started","0.Getting-Started\u002F0.index",[9,10,14,18],{"title":5,"path":6,"stem":7},{"title":11,"path":12,"stem":13},"Installation","\u002Fgetting-started\u002Finstallation","0.Getting-Started\u002F1.installation",{"title":15,"path":16,"stem":17},"Quickstart","\u002Fgetting-started\u002Fquickstart","0.Getting-Started\u002F2.quickstart",{"title":19,"path":20,"stem":21},"Core concepts","\u002Fgetting-started\u002Fcore-concepts","0.Getting-Started\u002F3.core-concepts",{"title":23,"path":24,"stem":25,"children":26,"icon":28},"JWT","\u002Fjwt","1.JWT\u002F0.index",[27,29,52],{"title":23,"path":24,"stem":25,"icon":28},"i-carbon-certificate",{"title":30,"path":31,"stem":32,"children":33,"icon":35},"JWS","\u002Fjwt\u002Fjws","1.JWT\u002F1.JWS\u002F0.index",[34,36,40,44,48],{"title":30,"path":31,"stem":32,"icon":35},"i-carbon-document-signed",{"title":37,"path":38,"stem":39},"Signing","\u002Fjwt\u002Fjws\u002Fsigning","1.JWT\u002F1.JWS\u002F1.signing",{"title":41,"path":42,"stem":43},"Verifying","\u002Fjwt\u002Fjws\u002Fverifying","1.JWT\u002F1.JWS\u002F2.verifying",{"title":45,"path":46,"stem":47},"Multi-signature","\u002Fjwt\u002Fjws\u002Fmulti-signature","1.JWT\u002F1.JWS\u002F3.multi-signature",{"title":49,"path":50,"stem":51},"Algorithms","\u002Fjwt\u002Fjws\u002Falgorithms","1.JWT\u002F1.JWS\u002F4.algorithms",{"title":53,"path":54,"stem":55,"children":56,"icon":58},"JWE","\u002Fjwt\u002Fjwe","1.JWT\u002F2.JWE\u002F0.index",[57,59,63,67,71,75],{"title":53,"path":54,"stem":55,"icon":58},"i-carbon-locked",{"title":60,"path":61,"stem":62},"Encrypting","\u002Fjwt\u002Fjwe\u002Fencrypting","1.JWT\u002F2.JWE\u002F1.encrypting",{"title":64,"path":65,"stem":66},"Decrypting","\u002Fjwt\u002Fjwe\u002Fdecrypting","1.JWT\u002F2.JWE\u002F2.decrypting",{"title":68,"path":69,"stem":70},"Multi-recipient","\u002Fjwt\u002Fjwe\u002Fmulti-recipient","1.JWT\u002F2.JWE\u002F3.multi-recipient",{"title":72,"path":73,"stem":74},"ECDH-ES and end-to-end encryption","\u002Fjwt\u002Fjwe\u002Fecdh-es","1.JWT\u002F2.JWE\u002F4.ecdh-es",{"title":49,"path":76,"stem":77},"\u002Fjwt\u002Fjwe\u002Falgorithms","1.JWT\u002F2.JWE\u002F5.algorithms",{"title":79,"path":80,"stem":81,"children":82,"icon":84},"Examples","\u002Fexamples","10.Examples\u002F0.index",[83,85,90,94,98,102],{"title":79,"path":80,"stem":81,"icon":84},"i-carbon-code-reference",{"title":86,"path":87,"stem":88,"icon":89},"Authentication basics","\u002Fexamples\u002Fauthentication-basics","10.Examples\u002F1.authentication-basics","i-lucide-code",{"title":91,"path":92,"stem":93,"icon":89},"Consuming a JWKS endpoint","\u002Fexamples\u002Fjwks-endpoint","10.Examples\u002F2.jwks-endpoint",{"title":95,"path":96,"stem":97,"icon":89},"Refresh token pattern","\u002Fexamples\u002Frefresh-token-pattern","10.Examples\u002F3.refresh-token-pattern",{"title":99,"path":100,"stem":101,"icon":89},"End-to-end encryption","\u002Fexamples\u002Fend-to-end-encryption","10.Examples\u002F4.end-to-end-encryption",{"title":103,"path":104,"stem":105,"icon":89},"Signed receipts","\u002Fexamples\u002Fsigned-receipts","10.Examples\u002F5.signed-receipts",{"title":107,"path":108,"stem":109,"children":110,"icon":112},"JWK","\u002Fjwk","2.JWK\u002F0.index",[111,113,117,121,125,129,133,137],{"title":107,"path":108,"stem":109,"icon":112},"i-carbon-two-factor-authentication",{"title":114,"path":115,"stem":116},"Generating keys","\u002Fjwk\u002Fgenerating","2.JWK\u002F1.generating",{"title":118,"path":119,"stem":120},"Importing and exporting","\u002Fjwk\u002Fimport-export","2.JWK\u002F2.import-export",{"title":122,"path":123,"stem":124},"PEM conversion","\u002Fjwk\u002Fpem","2.JWK\u002F3.pem",{"title":126,"path":127,"stem":128},"Key wrapping","\u002Fjwk\u002Fwrapping","2.JWK\u002F4.wrapping",{"title":130,"path":131,"stem":132},"Password derivation","\u002Fjwk\u002Fpassword-derivation","2.JWK\u002F5.password-derivation",{"title":134,"path":135,"stem":136},"JWK Sets","\u002Fjwk\u002Fjwk-sets","2.JWK\u002F6.jwk-sets",{"title":138,"path":139,"stem":140},"JWK cache","\u002Fjwk\u002Fcache","2.JWK\u002F7.cache",{"title":142,"path":143,"stem":144,"children":145,"icon":147},"Utilities","\u002Futilities","3.Utilities\u002F0.index",[146],{"title":142,"path":143,"stem":144,"icon":147},"i-carbon-tool-box",{"title":149,"path":150,"stem":151,"children":152,"icon":154},"Adapters","\u002Fadapters","99.Adapters\u002F0.index",[153,155,159,163],{"title":149,"path":150,"stem":151,"icon":154},"i-carbon-plug",{"title":156,"path":157,"stem":158},"H3 sessions","\u002Fadapters\u002Fh3-sessions","99.Adapters\u002F1.h3-sessions",{"title":160,"path":161,"stem":162},"Lifecycle hooks","\u002Fadapters\u002Fhooks","99.Adapters\u002F2.hooks",{"title":164,"path":165,"stem":166},"Lower-level functions","\u002Fadapters\u002Flower-level","99.Adapters\u002F3.lower-level",{"id":168,"title":72,"body":169,"description":330,"extension":1418,"meta":1419,"navigation":1420,"path":73,"seo":1421,"stem":74,"__hash__":1422},"content\u002F1.JWT\u002F2.JWE\u002F4.ecdh-es.md",{"type":170,"value":171,"toc":1401},"minimark",[172,177,197,207,212,220,293,303,306,316,320,323,417,424,428,431,538,549,552,556,562,673,676,680,687,695,768,779,786,803,917,920,933,936,1061,1083,1087,1098,1115,1200,1209,1216,1222,1254,1263,1267,1325,1328,1357,1361,1397],[173,174,176],"h1",{"id":175},"ecdh-es-end-to-end-encryption","ECDH-ES & end-to-end encryption",[178,179,180,184,185,188,189,192,193,196],"p",{},[181,182,183],"strong",{},"ECDH-ES"," (Elliptic-Curve Diffie-Hellman Ephemeral Static) is JWE's public-key encryption scheme. It's the workhorse for ",[181,186,187],{},"end-to-end encryption"," (E2EE) between participants who each hold a key pair: the ",[181,190,191],{},"private key never leaves its owner",", and the ",[181,194,195],{},"public key can be shared freely",".",[178,198,199,200,196],{},"Defined in ",[201,202,206],"a",{"href":203,"rel":204},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc7518#section-4.6",[205],"nofollow","RFC 7518 §4.6",[208,209,211],"h2",{"id":210},"the-two-ecdh-es-modes","The two ECDH-ES modes",[178,213,214,215,219],{},"Both appear in ",[216,217,218],"code",{},"alg",":",[221,222,223,243],"table",{},[224,225,226],"thead",{},[227,228,229,234,237],"tr",{},[230,231,232],"th",{},[216,233,218],{},[230,235,236],{},"Key flow",[230,238,239,242],{},[216,240,241],{},"enc"," behavior",[244,245,246,269],"tbody",{},[227,247,248,253,264],{},[249,250,251],"td",{},[216,252,183],{},[249,254,255,256,259,260,263],{},"Derived secret ",[181,257,258],{},"is"," the CEK directly (like ",[216,261,262],{},"dir",")",[249,265,266,268],{},[216,267,241],{}," required; content cipher consumes the secret",[227,270,271,283,290],{},[249,272,273,276,277,276,280],{},[216,274,275],{},"ECDH-ES+A128KW"," \u002F ",[216,278,279],{},"A192KW",[216,281,282],{},"A256KW",[249,284,285,286,289],{},"Derived secret becomes a KEK that ",[181,287,288],{},"wraps"," a random CEK",[249,291,292],{},"Shared CEK is wrapped per recipient",[178,294,295,296,298,299,302],{},"Plain ",[216,297,183],{}," is the simplest; the key-wrapped variants are required for ",[201,300,301],{"href":69},"multi-recipient"," scenarios.",[178,304,305],{},"For single-recipient flows, both work. The key-wrapped variants are slightly larger (there's a wrapped CEK segment) but they separate key agreement from content encryption cleanly.",[307,308,309],"tip",{},[178,310,311,312,315],{},"For single-recipient E2EE, use ",[216,313,314],{},"ECDH-ES+A256KW"," by default. It's compatible with the multi-recipient path if you later add more recipients.",[208,317,319],{"id":318},"one-time-key-setup","One-time key setup",[178,321,322],{},"Each participant generates a key pair once and persists it:",[324,325,331],"pre",{"className":326,"code":327,"filename":328,"language":329,"meta":330,"style":330},"language-ts shiki shiki-themes github-light github-dark github-dark","import { generateJWK } from \"unjwt\u002Fjwk\";\n\n\u002F\u002F Run once per participant at account creation \u002F app install\nconst keys = await generateJWK(\"ECDH-ES+A256KW\", { kid: \"alice-2025\" });\n\u002F\u002F keys.privateKey — keep in secure storage, never transmit\n\u002F\u002F keys.publicKey  — distribute freely (e.g., publish at \u002F.well-known\u002Fjwks.json)\n","key-setup.ts","ts","",[216,332,333,356,363,370,405,411],{"__ignoreMap":330},[334,335,338,342,346,349,353],"span",{"class":336,"line":337},"line",1,[334,339,341],{"class":340},"so5gQ","import",[334,343,345],{"class":344},"slsVL"," { generateJWK } ",[334,347,348],{"class":340},"from",[334,350,352],{"class":351},"sfrk1"," \"unjwt\u002Fjwk\"",[334,354,355],{"class":344},";\n",[334,357,359],{"class":336,"line":358},2,[334,360,362],{"emptyLinePlaceholder":361},true,"\n",[334,364,366],{"class":336,"line":365},3,[334,367,369],{"class":368},"sCsY4","\u002F\u002F Run once per participant at account creation \u002F app install\n",[334,371,373,376,380,383,386,390,393,396,399,402],{"class":336,"line":372},4,[334,374,375],{"class":340},"const",[334,377,379],{"class":378},"suiK_"," keys",[334,381,382],{"class":340}," =",[334,384,385],{"class":340}," await",[334,387,389],{"class":388},"shcOC"," generateJWK",[334,391,392],{"class":344},"(",[334,394,395],{"class":351},"\"ECDH-ES+A256KW\"",[334,397,398],{"class":344},", { kid: ",[334,400,401],{"class":351},"\"alice-2025\"",[334,403,404],{"class":344}," });\n",[334,406,408],{"class":336,"line":407},5,[334,409,410],{"class":368},"\u002F\u002F keys.privateKey — keep in secure storage, never transmit\n",[334,412,414],{"class":336,"line":413},6,[334,415,416],{"class":368},"\u002F\u002F keys.publicKey  — distribute freely (e.g., publish at \u002F.well-known\u002Fjwks.json)\n",[178,418,419,420,423],{},"Key distribution is ",[181,421,422],{},"out of band"," — registration, user profile page, a JWKS endpoint, a shared directory. It's not a cryptographic concern; you only need an authentic channel for the public key (otherwise a MITM could substitute their own).",[208,425,427],{"id":426},"sending-to-one-recipient","Sending to one recipient",[178,429,430],{},"The common case — one-to-one encrypted messaging:",[324,432,435],{"className":326,"code":433,"filename":434,"language":329,"meta":330,"style":330},"import { encrypt, decrypt } from \"unjwt\u002Fjwe\";\n\n\u002F\u002F Sender (Alice) — holds Bob's public key ——————————————————\nconst token = await encrypt({ message: \"Hello Bob!\" }, bobPublicKey);\n\u002F\u002F `token` is a compact JWE string — send via any channel\n\n\u002F\u002F Recipient (Bob) — holds his private key —————————————————\nconst { payload } = await decrypt(token, bobPrivateKey);\nconsole.log(payload.message); \u002F\u002F \"Hello Bob!\"\n","alice-to-bob.ts",[216,436,437,451,455,460,483,488,492,498,523],{"__ignoreMap":330},[334,438,439,441,444,446,449],{"class":336,"line":337},[334,440,341],{"class":340},[334,442,443],{"class":344}," { encrypt, decrypt } ",[334,445,348],{"class":340},[334,447,448],{"class":351}," \"unjwt\u002Fjwe\"",[334,450,355],{"class":344},[334,452,453],{"class":336,"line":358},[334,454,362],{"emptyLinePlaceholder":361},[334,456,457],{"class":336,"line":365},[334,458,459],{"class":368},"\u002F\u002F Sender (Alice) — holds Bob's public key ——————————————————\n",[334,461,462,464,467,469,471,474,477,480],{"class":336,"line":372},[334,463,375],{"class":340},[334,465,466],{"class":378}," token",[334,468,382],{"class":340},[334,470,385],{"class":340},[334,472,473],{"class":388}," encrypt",[334,475,476],{"class":344},"({ message: ",[334,478,479],{"class":351},"\"Hello Bob!\"",[334,481,482],{"class":344}," }, bobPublicKey);\n",[334,484,485],{"class":336,"line":407},[334,486,487],{"class":368},"\u002F\u002F `token` is a compact JWE string — send via any channel\n",[334,489,490],{"class":336,"line":413},[334,491,362],{"emptyLinePlaceholder":361},[334,493,495],{"class":336,"line":494},7,[334,496,497],{"class":368},"\u002F\u002F Recipient (Bob) — holds his private key —————————————————\n",[334,499,501,503,506,509,512,515,517,520],{"class":336,"line":500},8,[334,502,375],{"class":340},[334,504,505],{"class":344}," { ",[334,507,508],{"class":378},"payload",[334,510,511],{"class":344}," } ",[334,513,514],{"class":340},"=",[334,516,385],{"class":340},[334,518,519],{"class":388}," decrypt",[334,521,522],{"class":344},"(token, bobPrivateKey);\n",[334,524,526,529,532,535],{"class":336,"line":525},9,[334,527,528],{"class":344},"console.",[334,530,531],{"class":388},"log",[334,533,534],{"class":344},"(payload.message); ",[334,536,537],{"class":368},"\u002F\u002F \"Hello Bob!\"\n",[178,539,540,541,544,545,548],{},"That's it. unjwt generates a ",[181,542,543],{},"fresh ephemeral key pair per message"," internally on Alice's side, derives the shared secret, and writes the ephemeral public key (",[216,546,547],{},"epk",") into the JWE header so Bob can re-derive the same secret. Alice never holds Bob's private key; Bob never holds Alice's ephemeral private key (it's thrown away as soon as the message is encrypted).",[178,550,551],{},"Forward secrecy: even if Bob's long-term private key is later compromised, past ECDH-ES messages can't be decrypted because the ephemeral private key is gone.",[208,553,555],{"id":554},"sending-to-multiple-recipients-simple-fan-out","Sending to multiple recipients — simple fan-out",[178,557,558,561],{},[181,559,560],{},"Easiest approach:"," one independent token per recipient. Each is encrypted for exactly that person:",[324,563,566],{"className":326,"code":564,"filename":565,"language":329,"meta":330,"style":330},"const recipients = [\n  { name: \"bob\", publicKey: bobPublicKey },\n  { name: \"charlie\", publicKey: charliePublicKey },\n];\n\nconst tokens = await Promise.all(\n  recipients.map(({ publicKey }) => encrypt({ message: \"Hello team!\" }, publicKey)),\n);\n\u002F\u002F Deliver tokens[0] to Bob, tokens[1] to Charlie, etc.\n","fan-out.ts",[216,567,568,580,591,601,606,610,632,663,668],{"__ignoreMap":330},[334,569,570,572,575,577],{"class":336,"line":337},[334,571,375],{"class":340},[334,573,574],{"class":378}," recipients",[334,576,382],{"class":340},[334,578,579],{"class":344}," [\n",[334,581,582,585,588],{"class":336,"line":358},[334,583,584],{"class":344},"  { name: ",[334,586,587],{"class":351},"\"bob\"",[334,589,590],{"class":344},", publicKey: bobPublicKey },\n",[334,592,593,595,598],{"class":336,"line":365},[334,594,584],{"class":344},[334,596,597],{"class":351},"\"charlie\"",[334,599,600],{"class":344},", publicKey: charliePublicKey },\n",[334,602,603],{"class":336,"line":372},[334,604,605],{"class":344},"];\n",[334,607,608],{"class":336,"line":407},[334,609,362],{"emptyLinePlaceholder":361},[334,611,612,614,617,619,621,624,626,629],{"class":336,"line":413},[334,613,375],{"class":340},[334,615,616],{"class":378}," tokens",[334,618,382],{"class":340},[334,620,385],{"class":340},[334,622,623],{"class":378}," Promise",[334,625,196],{"class":344},[334,627,628],{"class":388},"all",[334,630,631],{"class":344},"(\n",[334,633,634,637,640,643,647,650,653,655,657,660],{"class":336,"line":494},[334,635,636],{"class":344},"  recipients.",[334,638,639],{"class":388},"map",[334,641,642],{"class":344},"(({ ",[334,644,646],{"class":645},"sQHwn","publicKey",[334,648,649],{"class":344}," }) ",[334,651,652],{"class":340},"=>",[334,654,473],{"class":388},[334,656,476],{"class":344},[334,658,659],{"class":351},"\"Hello team!\"",[334,661,662],{"class":344}," }, publicKey)),\n",[334,664,665],{"class":336,"line":500},[334,666,667],{"class":344},");\n",[334,669,670],{"class":336,"line":525},[334,671,672],{"class":368},"\u002F\u002F Deliver tokens[0] to Bob, tokens[1] to Charlie, etc.\n",[178,674,675],{},"For most use cases this is exactly what you want — it's clear, straightforward, and each token is independently verifiable in its delivery context. The only trade-off is bandwidth: N recipients means encrypting (and transmitting) the payload N times.",[208,677,679],{"id":678},"sending-to-multiple-recipients-shared-ciphertext","Sending to multiple recipients — shared ciphertext",[178,681,682,683,686],{},"When the payload is large or bandwidth matters, encrypt the payload ",[181,684,685],{},"once"," under a random CEK, then wrap that CEK individually per recipient. Everyone gets the same ciphertext plus their own wrapped key.",[178,688,689,690,219],{},"The high-level API for this is ",[201,691,692],{"href":69},[216,693,694],{},"encryptMulti",[324,696,699],{"className":326,"code":697,"filename":698,"language":329,"meta":330,"style":330},"import { encryptMulti, decryptMulti } from \"unjwt\u002Fjwe\";\n\nconst jwe = await encryptMulti(\n  { message: \"Hello team!\" },\n  [{ key: bobEcdhPublicJwk }, { key: charlieEcdhPublicJwk }],\n  { enc: \"A256GCM\" },\n);\n\u002F\u002F jwe is a JSON object — stringify and deliver\n","multi-recipient.ts",[216,700,701,714,718,734,744,749,759,763],{"__ignoreMap":330},[334,702,703,705,708,710,712],{"class":336,"line":337},[334,704,341],{"class":340},[334,706,707],{"class":344}," { encryptMulti, decryptMulti } ",[334,709,348],{"class":340},[334,711,448],{"class":351},[334,713,355],{"class":344},[334,715,716],{"class":336,"line":358},[334,717,362],{"emptyLinePlaceholder":361},[334,719,720,722,725,727,729,732],{"class":336,"line":365},[334,721,375],{"class":340},[334,723,724],{"class":378}," jwe",[334,726,382],{"class":340},[334,728,385],{"class":340},[334,730,731],{"class":388}," encryptMulti",[334,733,631],{"class":344},[334,735,736,739,741],{"class":336,"line":372},[334,737,738],{"class":344},"  { message: ",[334,740,659],{"class":351},[334,742,743],{"class":344}," },\n",[334,745,746],{"class":336,"line":407},[334,747,748],{"class":344},"  [{ key: bobEcdhPublicJwk }, { key: charlieEcdhPublicJwk }],\n",[334,750,751,754,757],{"class":336,"line":413},[334,752,753],{"class":344},"  { enc: ",[334,755,756],{"class":351},"\"A256GCM\"",[334,758,743],{"class":344},[334,760,761],{"class":336,"line":494},[334,762,667],{"class":344},[334,764,765],{"class":336,"line":500},[334,766,767],{"class":368},"\u002F\u002F jwe is a JSON object — stringify and deliver\n",[178,769,770,771,774,775,778],{},"Every recipient receives the same ",[216,772,773],{},"jwe.ciphertext",", and their own entry in ",[216,776,777],{},"jwe.recipients[]"," carries a CEK wrapped just for them.",[208,780,782,785],{"id":781},"derivesharedsecret-the-raw-kdf-step",[216,783,784],{},"deriveSharedSecret"," — the raw KDF step",[178,787,788,791,792,795,796,798,799,802],{},[216,789,790],{},"encrypt","\u002F",[216,793,794],{},"decrypt"," handle the full ECDH + Concat KDF + (optional) key-wrap cycle internally. For lower-level protocols, ",[216,797,784],{}," exposes ",[181,800,801],{},"just the KDF step"," — returning the raw derived bytes:",[324,804,807],{"className":326,"code":805,"filename":806,"language":329,"meta":330,"style":330},"import { deriveSharedSecret } from \"unjwt\u002Fjwk\";\n\n\u002F\u002F Both sides independently derive the exact same bytes\nconst aliceView = await deriveSharedSecret(\n  bobPublicKey,\n  aliceEphemeralPrivateKey,\n  \"ECDH-ES+A256KW\",\n);\nconst bobView = await deriveSharedSecret(\n  aliceEphemeralPublicKey, \u002F\u002F Bob gets this from the token's `epk` header\n  bobPrivateKey,\n  \"ECDH-ES+A256KW\",\n);\n\u002F\u002F aliceView and bobView are identical Uint8Arrays (32 bytes for A256KW)\n","raw-kdf.ts",[216,808,809,822,826,831,847,852,857,865,869,884,893,899,906,911],{"__ignoreMap":330},[334,810,811,813,816,818,820],{"class":336,"line":337},[334,812,341],{"class":340},[334,814,815],{"class":344}," { deriveSharedSecret } ",[334,817,348],{"class":340},[334,819,352],{"class":351},[334,821,355],{"class":344},[334,823,824],{"class":336,"line":358},[334,825,362],{"emptyLinePlaceholder":361},[334,827,828],{"class":336,"line":365},[334,829,830],{"class":368},"\u002F\u002F Both sides independently derive the exact same bytes\n",[334,832,833,835,838,840,842,845],{"class":336,"line":372},[334,834,375],{"class":340},[334,836,837],{"class":378}," aliceView",[334,839,382],{"class":340},[334,841,385],{"class":340},[334,843,844],{"class":388}," deriveSharedSecret",[334,846,631],{"class":344},[334,848,849],{"class":336,"line":407},[334,850,851],{"class":344},"  bobPublicKey,\n",[334,853,854],{"class":336,"line":413},[334,855,856],{"class":344},"  aliceEphemeralPrivateKey,\n",[334,858,859,862],{"class":336,"line":494},[334,860,861],{"class":351},"  \"ECDH-ES+A256KW\"",[334,863,864],{"class":344},",\n",[334,866,867],{"class":336,"line":500},[334,868,667],{"class":344},[334,870,871,873,876,878,880,882],{"class":336,"line":525},[334,872,375],{"class":340},[334,874,875],{"class":378}," bobView",[334,877,382],{"class":340},[334,879,385],{"class":340},[334,881,844],{"class":388},[334,883,631],{"class":344},[334,885,887,890],{"class":336,"line":886},10,[334,888,889],{"class":344},"  aliceEphemeralPublicKey, ",[334,891,892],{"class":368},"\u002F\u002F Bob gets this from the token's `epk` header\n",[334,894,896],{"class":336,"line":895},11,[334,897,898],{"class":344},"  bobPrivateKey,\n",[334,900,902,904],{"class":336,"line":901},12,[334,903,861],{"class":351},[334,905,864],{"class":344},[334,907,909],{"class":336,"line":908},13,[334,910,667],{"class":344},[334,912,914],{"class":336,"line":913},14,[334,915,916],{"class":368},"\u002F\u002F aliceView and bobView are identical Uint8Arrays (32 bytes for A256KW)\n",[178,918,919],{},"Use this when you need the derived bytes themselves — not a wrapped key. Applications include:",[921,922,923,927,930],"ul",{},[924,925,926],"li",{},"Custom hybrid protocols that use the shared secret as input to another KDF.",[924,928,929],{},"Non-JWE wrapping schemes.",[924,931,932],{},"Verifying the key-agreement step in isolation (interop testing, debugging).",[178,934,935],{},"Signature:",[324,937,939],{"className":326,"code":938,"language":329,"meta":330,"style":330},"deriveSharedSecret(\n  publicKey: CryptoKey | JWK_EC_Public,\n  privateKey: CryptoKey | JWK_EC_Private,\n  alg: JWK_ECDH_ES | ContentEncryptionAlgorithm,\n  options?: {\n    keyLength?: number;\n    partyUInfo?: Uint8Array\u003CArrayBuffer>;\n    partyVInfo?: Uint8Array\u003CArrayBuffer>;\n  },\n): Promise\u003CUint8Array\u003CArrayBuffer>>\n",[216,940,941,947,958,968,982,993,998,1019,1036,1041],{"__ignoreMap":330},[334,942,943,945],{"class":336,"line":337},[334,944,784],{"class":388},[334,946,631],{"class":344},[334,948,949,952,955],{"class":336,"line":358},[334,950,951],{"class":344},"  publicKey: CryptoKey ",[334,953,954],{"class":340},"|",[334,956,957],{"class":344}," JWK_EC_Public,\n",[334,959,960,963,965],{"class":336,"line":365},[334,961,962],{"class":344},"  privateKey: CryptoKey ",[334,964,954],{"class":340},[334,966,967],{"class":344}," JWK_EC_Private,\n",[334,969,970,973,976,979],{"class":336,"line":372},[334,971,972],{"class":344},"  alg: ",[334,974,975],{"class":378},"JWK_ECDH_ES",[334,977,978],{"class":340}," |",[334,980,981],{"class":344}," ContentEncryptionAlgorithm,\n",[334,983,984,987,990],{"class":336,"line":407},[334,985,986],{"class":344},"  options",[334,988,989],{"class":340},"?:",[334,991,992],{"class":344}," {\n",[334,994,995],{"class":336,"line":413},[334,996,997],{"class":344},"    keyLength?: number;\n",[334,999,1000,1003,1005,1008,1011,1014,1017],{"class":336,"line":494},[334,1001,1002],{"class":344},"    partyUInfo",[334,1004,989],{"class":340},[334,1006,1007],{"class":344}," Uint8Array",[334,1009,1010],{"class":340},"\u003C",[334,1012,1013],{"class":344},"ArrayBuffer",[334,1015,1016],{"class":340},">",[334,1018,355],{"class":344},[334,1020,1021,1024,1026,1028,1030,1032,1034],{"class":336,"line":500},[334,1022,1023],{"class":344},"    partyVInfo",[334,1025,989],{"class":340},[334,1027,1007],{"class":344},[334,1029,1010],{"class":340},[334,1031,1013],{"class":344},[334,1033,1016],{"class":340},[334,1035,355],{"class":344},[334,1037,1038],{"class":336,"line":525},[334,1039,1040],{"class":344},"  },\n",[334,1042,1043,1046,1049,1051,1054,1056,1058],{"class":336,"line":886},[334,1044,1045],{"class":344},"): ",[334,1047,1048],{"class":378},"Promise",[334,1050,1010],{"class":340},[334,1052,1053],{"class":344},"Uint8Array",[334,1055,1010],{"class":340},[334,1057,1013],{"class":344},[334,1059,1060],{"class":340},">>\n",[178,1062,1063,1064,1066,1067,1070,1071,1074,1075,1078,1079,1082],{},"When ",[216,1065,218],{}," is bare ",[216,1068,1069],{},"\"ECDH-ES\"",", the derived key length is ambiguous — pass ",[216,1072,1073],{},"options.keyLength"," explicitly (otherwise throws ",[216,1076,1077],{},"ERR_JWK_INVALID","). For ",[216,1080,1081],{},"\"ECDH-ES+A*KW\""," and content-encryption algs the length is inferred.",[208,1084,1086],{"id":1085},"parameters-to-know","Parameters to know",[1088,1089,1091,276,1094,1097],"h3",{"id":1090},"apu-apv-agreement-party-info",[216,1092,1093],{},"apu",[216,1095,1096],{},"apv"," — agreement party info",[178,1099,1100,1101,1106,1107,1110,1111,1114],{},"From ",[201,1102,1105],{"href":1103,"rel":1104},"https:\u002F\u002Fnvlpubs.nist.gov\u002Fnistpubs\u002FSpecialPublications\u002FNIST.SP.800-56Ar3.pdf",[205],"NIST SP 800-56A"," §5.8.1. Bind the derived key to specific sender (",[216,1108,1109],{},"PartyUInfo",") and recipient (",[216,1112,1113],{},"PartyVInfo",") identities so a key derived for \"alice→bob\" can't be reused as \"alice→mallory\":",[324,1116,1118],{"className":326,"code":1117,"language":329,"meta":330,"style":330},"const token = await encrypt({ data: \"x\" }, bobPublicKey, {\n  ecdh: {\n    partyUInfo: new TextEncoder().encode(\"alice@example.com\"),\n    partyVInfo: new TextEncoder().encode(\"bob@example.com\"),\n  },\n});\n",[216,1119,1120,1141,1146,1171,1191,1195],{"__ignoreMap":330},[334,1121,1122,1124,1126,1128,1130,1132,1135,1138],{"class":336,"line":337},[334,1123,375],{"class":340},[334,1125,466],{"class":378},[334,1127,382],{"class":340},[334,1129,385],{"class":340},[334,1131,473],{"class":388},[334,1133,1134],{"class":344},"({ data: ",[334,1136,1137],{"class":351},"\"x\"",[334,1139,1140],{"class":344}," }, bobPublicKey, {\n",[334,1142,1143],{"class":336,"line":358},[334,1144,1145],{"class":344},"  ecdh: {\n",[334,1147,1148,1151,1154,1157,1160,1163,1165,1168],{"class":336,"line":365},[334,1149,1150],{"class":344},"    partyUInfo: ",[334,1152,1153],{"class":340},"new",[334,1155,1156],{"class":388}," TextEncoder",[334,1158,1159],{"class":344},"().",[334,1161,1162],{"class":388},"encode",[334,1164,392],{"class":344},[334,1166,1167],{"class":351},"\"alice@example.com\"",[334,1169,1170],{"class":344},"),\n",[334,1172,1173,1176,1178,1180,1182,1184,1186,1189],{"class":336,"line":372},[334,1174,1175],{"class":344},"    partyVInfo: ",[334,1177,1153],{"class":340},[334,1179,1156],{"class":388},[334,1181,1159],{"class":344},[334,1183,1162],{"class":388},[334,1185,392],{"class":344},[334,1187,1188],{"class":351},"\"bob@example.com\"",[334,1190,1170],{"class":344},[334,1192,1193],{"class":336,"line":407},[334,1194,1040],{"class":344},[334,1196,1197],{"class":336,"line":413},[334,1198,1199],{"class":344},"});\n",[178,1201,1202,1203,1205,1206,1208],{},"unjwt writes these as ",[216,1204,1093],{}," and ",[216,1207,1096],{}," in the header. On decrypt, they're re-used in the KDF — a mismatch causes the wrong key to be derived and decryption to fail.",[1088,1210,1212,1215],{"id":1211},"ephemeralkey-deterministic-key-agreement",[216,1213,1214],{},"ephemeralKey"," — deterministic key agreement",[178,1217,1218,1219,1221],{},"By default unjwt generates a fresh ephemeral key pair on every ",[216,1220,790],{},". Override only for testing or specialized protocols where you need the ephemeral key to match an external artifact:",[324,1223,1225],{"className":326,"code":1224,"language":329,"meta":330,"style":330},"const token = await encrypt({ data: \"x\" }, bobPublicKey, {\n  ecdh: { ephemeralKey: myPrecomputedKeypair },\n});\n",[216,1226,1227,1245,1250],{"__ignoreMap":330},[334,1228,1229,1231,1233,1235,1237,1239,1241,1243],{"class":336,"line":337},[334,1230,375],{"class":340},[334,1232,466],{"class":378},[334,1234,382],{"class":340},[334,1236,385],{"class":340},[334,1238,473],{"class":388},[334,1240,1134],{"class":344},[334,1242,1137],{"class":351},[334,1244,1140],{"class":344},[334,1246,1247],{"class":336,"line":358},[334,1248,1249],{"class":344},"  ecdh: { ephemeralKey: myPrecomputedKeypair },\n",[334,1251,1252],{"class":336,"line":365},[334,1253,1199],{"class":344},[1255,1256,1257],"warning",{},[178,1258,1259,1262],{},[181,1260,1261],{},"Never reuse ephemeral keys across messages in production."," The \"E\" in ECDH-ES stands for \"ephemeral\" — reuse destroys forward secrecy and can leak the private key via side channels.",[208,1264,1266],{"id":1265},"supported-curves","Supported curves",[221,1268,1269,1279],{},[224,1270,1271],{},[227,1272,1273,1276],{},[230,1274,1275],{},"Curve",[230,1277,1278],{},"Notes",[244,1280,1281,1295,1305,1315],{},[227,1282,1283,1288],{},[249,1284,1285],{},[216,1286,1287],{},"P-256",[249,1289,1290,1291,1294],{},"Default for ",[216,1292,1293],{},"ECDH-ES*",". NIST curve. Widely supported.",[227,1296,1297,1302],{},[249,1298,1299],{},[216,1300,1301],{},"P-384",[249,1303,1304],{},"Larger, slower.",[227,1306,1307,1312],{},[249,1308,1309],{},[216,1310,1311],{},"P-521",[249,1313,1314],{},"Largest NIST curve available.",[227,1316,1317,1322],{},[249,1318,1319],{},[216,1320,1321],{},"X25519",[249,1323,1324],{},"Modern, constant-time, fast. Prefer for new systems.",[178,1326,1327],{},"Pick a curve at generation time:",[324,1329,1331],{"className":326,"code":1330,"language":329,"meta":330,"style":330},"const keys = await generateJWK(\"ECDH-ES+A256KW\", { namedCurve: \"X25519\" });\n",[216,1332,1333],{"__ignoreMap":330},[334,1334,1335,1337,1339,1341,1343,1345,1347,1349,1352,1355],{"class":336,"line":337},[334,1336,375],{"class":340},[334,1338,379],{"class":378},[334,1340,382],{"class":340},[334,1342,385],{"class":340},[334,1344,389],{"class":388},[334,1346,392],{"class":344},[334,1348,395],{"class":351},[334,1350,1351],{"class":344},", { namedCurve: ",[334,1353,1354],{"class":351},"\"X25519\"",[334,1356,404],{"class":344},[208,1358,1360],{"id":1359},"see-also","See also",[921,1362,1363,1373,1379,1384],{},[924,1364,1365,1368,1369,1372],{},[201,1366,1367],{"href":61},"Encrypting →"," — the full ",[216,1370,1371],{},"encrypt()"," surface.",[924,1374,1375,1378],{},[201,1376,1377],{"href":69},"Multi-recipient →"," — General JSON Serialization.",[924,1380,1381,196],{},[201,1382,1383],{"href":100},"Examples: end-to-end encryption →",[924,1385,1386,1389,1390,791,1393,1396],{},[201,1387,1388],{"href":127},"Key wrapping →"," — ",[216,1391,1392],{},"wrapKey",[216,1394,1395],{},"unwrapKey"," in isolation, for custom protocols.",[1398,1399,1400],"style",{},"html pre.shiki code .so5gQ, html code.shiki .so5gQ{--shiki-light:#D73A49;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .slsVL, html code.shiki .slsVL{--shiki-light:#24292E;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sfrk1, html code.shiki .sfrk1{--shiki-light:#032F62;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .sCsY4, html code.shiki .sCsY4{--shiki-light:#6A737D;--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .suiK_, html code.shiki .suiK_{--shiki-light:#005CC5;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .shcOC, html code.shiki .shcOC{--shiki-light:#6F42C1;--shiki-default:#B392F0;--shiki-dark:#B392F0}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sQHwn, html code.shiki .sQHwn{--shiki-light:#E36209;--shiki-default:#FFAB70;--shiki-dark:#FFAB70}",{"title":330,"searchDepth":358,"depth":358,"links":1402},[1403,1404,1405,1406,1407,1408,1410,1416,1417],{"id":210,"depth":358,"text":211},{"id":318,"depth":358,"text":319},{"id":426,"depth":358,"text":427},{"id":554,"depth":358,"text":555},{"id":678,"depth":358,"text":679},{"id":781,"depth":358,"text":1409},"deriveSharedSecret — the raw KDF step",{"id":1085,"depth":358,"text":1086,"children":1411},[1412,1414],{"id":1090,"depth":365,"text":1413},"apu \u002F apv — agreement party info",{"id":1211,"depth":365,"text":1415},"ephemeralKey — deterministic key agreement",{"id":1265,"depth":358,"text":1266},{"id":1359,"depth":358,"text":1360},"md",{},{},{"title":72,"description":330},"A5LK56odIdoqsAbH2hjK3rFqhv2jrox3MtpgsqDYVro",[1424,1425],{"title":68,"path":69,"stem":70,"description":330,"children":-1},{"title":49,"path":76,"stem":77,"description":330,"children":-1},1776888560076]