Files
rfc.vac.dev/assets/js/e2b5fcb9.dad7cd02.js
2025-08-01 18:13:23 +00:00

1 line
21 KiB
JavaScript

"use strict";(self.webpackChunklogos_docs_template=self.webpackChunklogos_docs_template||[]).push([[2799],{98526:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>l,toc:()=>p});var a=n(87462),i=(n(67294),n(3905));const r={title:"NOISE-X3DH-DOUBLE-RATCHET",name:"Secure 1-to-1 channel setup using X3DH and the double ratchet",status:"raw",category:"Standards Track",editor:"Ramses Fernandez &lt;ramses@status.im&gt;",contributors:null},o=void 0,l={unversionedId:"raw/noise-x3dh-double-ratchet",id:"raw/noise-x3dh-double-ratchet",title:"NOISE-X3DH-DOUBLE-RATCHET",description:"- Status: raw",source:"@site/vac/raw/noise-x3dh-double-ratchet.md",sourceDirName:"raw",slug:"/raw/noise-x3dh-double-ratchet",permalink:"/vac/raw/noise-x3dh-double-ratchet",draft:!1,tags:[],version:"current",frontMatter:{title:"NOISE-X3DH-DOUBLE-RATCHET",name:"Secure 1-to-1 channel setup using X3DH and the double ratchet",status:"raw",category:"Standards Track",editor:"Ramses Fernandez &lt;ramses@status.im&gt;",contributors:null},sidebar:"defaultSidebar",previous:{title:"LIBP2P-MIX",permalink:"/vac/raw/mix"},next:{title:"RLN-INTEREP-SPEC",permalink:"/vac/raw/rln-interep-spec"}},s={},p=[{value:"Motivation",id:"motivation",level:2},{value:"Theory",id:"theory",level:2},{value:"Syntax",id:"syntax",level:2},{value:"Cryptographic suite",id:"cryptographic-suite",level:3},{value:"X3DH initialization",id:"x3dh-initialization",level:3},{value:"Use of X3DH",id:"use-of-x3dh",level:3},{value:"Initialization of the double datchet",id:"initialization-of-the-double-datchet",level:3},{value:"Encryption",id:"encryption",level:3},{value:"Decryption",id:"decryption",level:3},{value:"Information retrieval",id:"information-retrieval",level:2},{value:"Static data",id:"static-data",level:3},{value:"Ephemeral data",id:"ephemeral-data",level:3},{value:"Copyright",id:"copyright",level:2},{value:"References",id:"references",level:2}],c={toc:p};function d(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Status: raw"),(0,i.kt)("li",{parentName:"ul"},"Category: Standards Track"),(0,i.kt)("li",{parentName:"ul"},"Editor: Ramses Fernandez ","<",(0,i.kt)("a",{parentName:"li",href:"mailto:ramses@status.im"},"ramses@status.im"),">")),(0,i.kt)("h2",{id:"motivation"},"Motivation"),(0,i.kt)("p",null,"The need for secure communications has become paramount.\nThis specification outlines a protocol describing a\nsecure 1-to-1 comunication channel between 2 users. The\nmain components are the X3DH key establishment mechanism,\ncombined with the double ratchet. The aim of this\ncombination of schemes is providing a protocol with both\nforward secrecy and post-compromise security."),(0,i.kt)("h2",{id:"theory"},"Theory"),(0,i.kt)("p",null,"The specification is based on the noise protocol framework.\nIt corresponds to the double ratchet scheme combined with\nthe X3DH algorithm, which will be used to initialize the former.\nWe chose to express the protocol in noise to be be able to use\nthe noise streamlined implementation and proving features.\nThe X3DH algorithm provides both authentication and forward\nsecrecy, as stated in the\n",(0,i.kt)("a",{parentName:"p",href:"https://signal.org/docs/specifications/x3dh/"},"X3DH specification"),"."),(0,i.kt)("p",null,"This protocol will consist of several stages:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Key setting for X3DH: this step will produce\nprekey bundles for Bob which will be fed into X3DH.\nIt will also allow Alice to generate the keys required\nto run the X3DH algorithm correctly."),(0,i.kt)("li",{parentName:"ol"},"Execution of X3DH: This step will output\na common secret key ",(0,i.kt)("inlineCode",{parentName:"li"},"SK")," together with an additional\ndata vector ",(0,i.kt)("inlineCode",{parentName:"li"},"AD"),". Both will be used in the double\nratchet algorithm initialization."),(0,i.kt)("li",{parentName:"ol"},"Execution of the double ratchet algorithm\nfor forward secure, authenticated communications,\nusing the common secret key ",(0,i.kt)("inlineCode",{parentName:"li"},"SK"),", obtained from X3DH, as a root key.")),(0,i.kt)("p",null,"The protocol assumes the following requirements:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Alice knows Bob\u2019s Ethereum address."),(0,i.kt)("li",{parentName:"ul"},"Bob is willing to participate in the protocol,\nand publishes his public key."),(0,i.kt)("li",{parentName:"ul"},"Bob\u2019s ownership of his public key is verifiable,"),(0,i.kt)("li",{parentName:"ul"},"Alice wants to send message M to Bob."),(0,i.kt)("li",{parentName:"ul"},"An eavesdropper cannot read M\u2019s content\neven if she is storing it or relaying it.")),(0,i.kt)("h2",{id:"syntax"},"Syntax"),(0,i.kt)("h3",{id:"cryptographic-suite"},"Cryptographic suite"),(0,i.kt)("p",null,"The following cryptographic functions MUST be used:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"X488")," as Diffie-Hellman function ",(0,i.kt)("inlineCode",{parentName:"li"},"DH"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"SHA256")," as KDF."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"AES256-GCM")," as AEAD algorithm."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"SHA512")," as hash function."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"XEd448")," for digital signatures.")),(0,i.kt)("h3",{id:"x3dh-initialization"},"X3DH initialization"),(0,i.kt)("p",null,"This scheme MUST work on the curve curve448.\nThe X3DH algorithm corresponds to the IX pattern in Noise."),(0,i.kt)("p",null,"Bob and Alice MUST define personal key pairs\n",(0,i.kt)("inlineCode",{parentName:"p"},"(ik_B, IK_B)")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"(ik_A, IK_A)")," respectively where:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"The key ",(0,i.kt)("inlineCode",{parentName:"li"},"ik")," must be kept secret,"),(0,i.kt)("li",{parentName:"ul"},"and the key ",(0,i.kt)("inlineCode",{parentName:"li"},"IK")," is public.")),(0,i.kt)("p",null,"Bob MUST generate new keys using\n",(0,i.kt)("inlineCode",{parentName:"p"},"(ik_B, IK_B) = GENERATE_KEYPAIR(curve = curve448)"),"."),(0,i.kt)("p",null,"Bob MUST also generate a public key pair\n",(0,i.kt)("inlineCode",{parentName:"p"},"(spk_B, SPK_B) = GENERATE_KEYPAIR(curve = curve448)"),"."),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"SPK")," is a public key generated and stored at medium-term.\nBoth signed prekey and the certificate MUST\nundergo periodic replacement.\nAfter replacing the key,\nBob keeps the old private key of ",(0,i.kt)("inlineCode",{parentName:"p"},"SPK"),"\nfor some interval, dependant on the implementation.\nThis allows Bob to decrypt delayed messages."),(0,i.kt)("p",null,"Bob MUST sign ",(0,i.kt)("inlineCode",{parentName:"p"},"SPK")," for authentication:\n",(0,i.kt)("inlineCode",{parentName:"p"},"SigSPK = XEd448(ik, Encode(SPK))")),(0,i.kt)("p",null,"A final step requires the definition of\n",(0,i.kt)("inlineCode",{parentName:"p"},"prekey_bundle = (IK, SPK, SigSPK, OPK_i)")),(0,i.kt)("p",null,"One-time keys ",(0,i.kt)("inlineCode",{parentName:"p"},"OPK")," MUST be generated as\n",(0,i.kt)("inlineCode",{parentName:"p"},"(opk_B, OPK_B) = GENERATE_KEYPAIR(curve = curve448)"),"."),(0,i.kt)("p",null,"Before sending an initial message to Bob,\nAlice MUST generate an AD: ",(0,i.kt)("inlineCode",{parentName:"p"},"AD = Encode(IK_A) || Encode(IK_B)"),"."),(0,i.kt)("p",null,"Alice MUST generate ephemeral key pairs\n",(0,i.kt)("inlineCode",{parentName:"p"},"(ek, EK) = GENERATE_KEYPAIR(curve = curve448)"),"."),(0,i.kt)("p",null,"The function ",(0,i.kt)("inlineCode",{parentName:"p"},"Encode()")," transforms a\ncurve448 public key into a byte sequence.\nThis is specified in the ",(0,i.kt)("a",{parentName:"p",href:"http://www.ietf.org/rfc/rfc7748.txt"},"RFC 7748"),"\non elliptic curves for security."),(0,i.kt)("p",null,"One MUST consider ",(0,i.kt)("inlineCode",{parentName:"p"},"q = 2^446 - 13818066809895115352007386748515426880336692474882178609894547503885"),"\nfor digital signatures with ",(0,i.kt)("inlineCode",{parentName:"p"},"(XEd448_sign, XEd448_verify)"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"XEd448_sign((ik, IK), message):\n Z = randbytes(64) \n r = SHA512(2^456 - 2 || ik || message ||\xa0Z )\n R = (r * convert_mont(5)) % q\n h = SHA512(R ||\xa0IK || M)\n s = (r + h * ik) % q\n return (R || s)\n")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"XEd448_verify(u, message, (R || s)):\n if (R.y >= 2^448) or (s >= 2^446): return FALSE\n h = (SHA512(R || 156326 || message)) % q\n R_check = s * convert_mont(5) - h * 156326\n if R == R_check: return TRUE\n return FALSE \n")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"convert_mont(u):\n u_masked = u % mod 2^448\n inv = ((1 - u_masked)^(2^448 - 2^224 - 3)) % (2^448 - 2^224 - 1)\n P.y = ((1 + u_masked) * inv)) % (2^448 - 2^224 - 1)\n P.s = 0\n return P\n")),(0,i.kt)("h3",{id:"use-of-x3dh"},"Use of X3DH"),(0,i.kt)("p",null,"This specification combines the double ratchet\nwith X3DH using the following data as initialization for the former:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"SK")," output from X3DH becomes the ",(0,i.kt)("inlineCode",{parentName:"li"},"SK"),"\ninput of the double ratchet. See section 3.3 of\n",(0,i.kt)("a",{parentName:"li",href:"https://signal.org/docs/specifications/doubleratchet/"},"Signal Specification"),"\nfor a detailed description."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"AD")," output from X3DH becomes the ",(0,i.kt)("inlineCode",{parentName:"li"},"AD"),"\ninput of the double ratchet. See sections 3.4 and 3.5 of\n",(0,i.kt)("a",{parentName:"li",href:"https://signal.org/docs/specifications/doubleratchet/"},"Signal Specification"),"\nfor a detailed description."),(0,i.kt)("li",{parentName:"ul"},"Bob\u2019s signed prekey ",(0,i.kt)("inlineCode",{parentName:"li"},"SigSPKB")," from X3DH is used as Bob\u2019s\ninitial ratchet public key of the double ratchet.")),(0,i.kt)("p",null,"X3DH has three phases:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Bob publishes his identity key and prekeys to a server,\na network, or dedicated smart contract."),(0,i.kt)("li",{parentName:"ol"},"Alice fetches a prekey bundle from the server,\nand uses it to send an initial message to Bob."),(0,i.kt)("li",{parentName:"ol"},"Bob receives and processes Alice's initial message.")),(0,i.kt)("p",null,"Alice MUST perform the following computations:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"dh1 = DH(IK_A, SPK_B, curve = curve448)\ndh2 = DH(EK_A, IK_B, curve = curve448)\ndh3 = DH(EK_A, SPK_B)\nSK = KDF(dh1 || dh2 || dh3)\n")),(0,i.kt)("p",null,"Alice MUST send to Bob a message containing:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"IK_A, EK_A"),"."),(0,i.kt)("li",{parentName:"ul"},"An identifier to Bob's prekeys used."),(0,i.kt)("li",{parentName:"ul"},"A message encrypted with AES256-GCM using ",(0,i.kt)("inlineCode",{parentName:"li"},"AD")," and ",(0,i.kt)("inlineCode",{parentName:"li"},"SK"),".")),(0,i.kt)("p",null,"Upon reception of the initial message, Bob MUST:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Perform the same computations above with the ",(0,i.kt)("inlineCode",{parentName:"li"},"DH()")," function."),(0,i.kt)("li",{parentName:"ol"},"Derive ",(0,i.kt)("inlineCode",{parentName:"li"},"SK")," and construct ",(0,i.kt)("inlineCode",{parentName:"li"},"AD"),"."),(0,i.kt)("li",{parentName:"ol"},"Decrypt the initial message encrypted with ",(0,i.kt)("inlineCode",{parentName:"li"},"AES256-GCM"),"."),(0,i.kt)("li",{parentName:"ol"},"If decryption fails, abort the protocol.")),(0,i.kt)("h3",{id:"initialization-of-the-double-datchet"},"Initialization of the double datchet"),(0,i.kt)("p",null,"In this stage Bob and Alice have generated key pairs\nand agreed a shared secret ",(0,i.kt)("inlineCode",{parentName:"p"},"SK")," using X3DH."),(0,i.kt)("p",null,"Alice calls ",(0,i.kt)("inlineCode",{parentName:"p"},"RatchetInitAlice()")," defined below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"RatchetInitAlice(SK, IK_B):\n state.DHs = GENERATE_KEYPAIR(curve = curve448)\n state.DHr = IK_B\n state.RK, state.CKs = HKDF(SK, DH(state.DHs, state.DHr)) \n state.CKr = None\n state.Ns, state.Nr, state.PN = 0\n state.MKSKIPPED = {}\n")),(0,i.kt)("p",null,"The HKDF function MUST be the proposal by\n",(0,i.kt)("a",{parentName:"p",href:"http://www.ietf.org/rfc/rfc5869.txt"},"Krawczyk and Eronen"),".\nIn this proposal ",(0,i.kt)("inlineCode",{parentName:"p"},"chaining_key")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"input_key_material"),"\nMUST be replaced with ",(0,i.kt)("inlineCode",{parentName:"p"},"SK")," and the output of ",(0,i.kt)("inlineCode",{parentName:"p"},"DH")," respectively."),(0,i.kt)("p",null,"Similarly, Bob calls the function ",(0,i.kt)("inlineCode",{parentName:"p"},"RatchetInitBob()")," defined below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"RatchetInitBob(SK, (ik_B,IK_B)):\n state.DHs = (ik_B, IK_B)\n state.Dhr = None\n state.RK = SK\n state.CKs, state.CKr = None\n state.Ns, state.Nr, state.PN = 0\n state.MKSKIPPED = {}\n")),(0,i.kt)("h3",{id:"encryption"},"Encryption"),(0,i.kt)("p",null,"This function performs the symmetric key ratchet."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"RatchetEncrypt(state, plaintext, AD):\n state.CKs, mk = HMAC-SHA256(state.CKs)\n header = HEADER(state.DHs, state.PN, state.Ns)\n state.Ns = state.Ns + 1\n return header, AES256-GCM_Enc(mk, plaintext, AD || header)\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"HEADER")," function creates a new message header\ncontaining the public key from the key pair output of the ",(0,i.kt)("inlineCode",{parentName:"p"},"DH"),"function.\nIt outputs the previous chain length ",(0,i.kt)("inlineCode",{parentName:"p"},"pn"),",\nand the message number ",(0,i.kt)("inlineCode",{parentName:"p"},"n"),".\nThe returned header object contains ratchet public key\n",(0,i.kt)("inlineCode",{parentName:"p"},"dh")," and integers ",(0,i.kt)("inlineCode",{parentName:"p"},"pn")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"n"),"."),(0,i.kt)("h3",{id:"decryption"},"Decryption"),(0,i.kt)("p",null,"The function ",(0,i.kt)("inlineCode",{parentName:"p"},"RatchetDecrypt()")," decrypts incoming messages:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"RatchetDecrypt(state, header, ciphertext, AD):\n plaintext = TrySkippedMessageKeys(state, header, ciphertext, AD)\n if plaintext != None:\n return plaintext\n if header.dh != state.DHr:\n SkipMessageKeys(state, header.pn)\n DHRatchet(state, header)\n SkipMessageKeys(state, header.n)\n state.CKr, mk = HMAC-SHA256(state.CKr)\n state.Nr = state.Nr + 1\n return AES256-GCM_Dec(mk, ciphertext, AD || header)\n")),(0,i.kt)("p",null,"Auxiliary functions follow:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"DHRatchet(state, header):\n state.PN = state.Ns\n state.Ns = state.Nr = 0\n state.DHr = header.dh\n state.RK, state.CKr = HKDF(state.RK, DH(state.DHs, state.DHr))\n state.DHs = GENERATE_KEYPAIR(curve = curve448)\n state.RK, state.CKs = HKDF(state.RK, DH(state.DHs, state.DHr))\n")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"SkipMessageKeys(state, until):\n if state.NR + MAX_SKIP < until:\n raise Error\n if state.CKr != none:\n while state.Nr < until:\n state.CKr, mk = HMAC-SHA256(state.CKr)\n state.MKSKIPPED[state.DHr, state.Nr] = mk\n state.Nr = state.Nr + 1\n")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"TrySkippedMessageKey(state, header, ciphertext, AD):\n if (header.dh, header.n) in state.MKSKIPPED:\n mk = state.MKSKIPPED[header.dh, header.n]\n delete state.MKSKIPPED[header.dh, header.n]\n return AES256-GCM_Dec(mk, ciphertext, AD || header)\n else: return None\n")),(0,i.kt)("h2",{id:"information-retrieval"},"Information retrieval"),(0,i.kt)("h3",{id:"static-data"},"Static data"),(0,i.kt)("p",null,"Some data, such as the key pairs ",(0,i.kt)("inlineCode",{parentName:"p"},"(ik, IK)")," for Alice and Bob,\nMAY NOT be regenerated after a period of time.\nTherefore the prekey bundle MAY be stored in long-term\nstorage solutions, such as a dedicated smart contract\nwhich outputs such a key pair when receiving an Ethereum wallet\naddress."),(0,i.kt)("p",null,"Storing static data is done using a dedicated\nsmart contract ",(0,i.kt)("inlineCode",{parentName:"p"},"PublicKeyStorage")," which associates\nthe Ethereum wallet address of a user with his public key.\nThis mapping is done by ",(0,i.kt)("inlineCode",{parentName:"p"},"PublicKeyStorage"),"\nusing a ",(0,i.kt)("inlineCode",{parentName:"p"},"publicKeys")," function, or a ",(0,i.kt)("inlineCode",{parentName:"p"},"setPublicKey")," function.\nThis mapping is done if the user passed an authorization process.\nA user who wants to retrieve a public key associated\nwith a specific wallet address calls a function ",(0,i.kt)("inlineCode",{parentName:"p"},"getPublicKey"),".\nThe user provides the wallet address as the only\ninput parameter for ",(0,i.kt)("inlineCode",{parentName:"p"},"getPublicKey"),".\nThe function outputs the associated public key\nfrom the smart contract."),(0,i.kt)("h3",{id:"ephemeral-data"},"Ephemeral data"),(0,i.kt)("p",null,"Storing ephemeral data on Ethereum MAY be done using\na combination of on-chain and off-chain solutions.\nThis approach provides an efficient solution to\nthe problem of storing updatable data in Ethereum."),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Ethereum stores a reference or a hash\nthat points to the off-chain data."),(0,i.kt)("li",{parentName:"ol"},"Off-chain solutions can include systems like IPFS,\ntraditional cloud storage solutions, or\ndecentralized storage networks such as a\n",(0,i.kt)("a",{parentName:"li",href:"https://www.ethswarm.org"},"Swarm"),".")),(0,i.kt)("p",null,"In any case, the user stores the associated\nIPFS hash, URL or reference in Ethereum."),(0,i.kt)("p",null,"The fact of a user not updating the ephemeral information\ncan be understood as Bob not willing to participate in any\ncommunication."),(0,i.kt)("h2",{id:"copyright"},"Copyright"),(0,i.kt)("p",null,"Copyright and related rights waived via ",(0,i.kt)("a",{parentName:"p",href:"https://creativecommons.org/publicdomain/zero/1.0/"},"CC0"),"."),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://signal.org/docs/specifications/doubleratchet/"},"The Double Ratchet Algorithm")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://signal.org/docs/specifications/x3dh/"},"The X3DH Key Agreement Protocol"))))}d.isMDXComponent=!0},3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var a=n(67294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),u=p(n),h=i,m=u["".concat(s,".").concat(h)]||u[h]||d[h]||r;return n?a.createElement(m,o(o({ref:t},c),{},{components:n})):a.createElement(m,o({ref:t},c))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var p=2;p<r;p++)o[p]=n[p];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}u.displayName="MDXCreateElement"}}]);