Last active
March 30, 2017 15:29
-
-
Save alibitek/6fa778086b4fcadafe57118f79b84c43 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Pretty printing styles. Used with prettify.js. */ | |
.str { color: #080; } | |
.kwd { color: #008; } | |
.com { color: #800; } | |
.typ { color: #606; } | |
.lit { color: #066; } | |
.pun { color: #660; } | |
.pln { color: #000; } | |
.tag { color: #008; } | |
.atn { color: #606; } | |
.atv { color: #080; } | |
.dec { color: #606; } | |
@media print { | |
.str { color: #060; } | |
.kwd { color: #006; font-weight: bold; } | |
.com { color: #600; font-style: italic; } | |
.typ { color: #404; font-weight: bold; } | |
.lit { color: #044; } | |
.pun { color: #440; } | |
.pln { color: #000; } | |
.tag { color: #006; font-weight: bold; } | |
.atn { color: #404; } | |
.atv { color: #060; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(){ | |
var o=true,r=null,z=false;window.PR_SHOULD_USE_CONTINUATION=o;window.PR_TAB_WIDTH=8;window.PR_normalizedHtml=window.PR=window.prettyPrintOne=window.prettyPrint=void 0;window._pr_isIE6=function(){var N=navigator&&navigator.userAgent&&/\bMSIE 6\./.test(navigator.userAgent);window._pr_isIE6=function(){return N};return N}; | |
var aa="!",ba="!=",ca="!==",F="#",da="%",ea="%=",G="&",fa="&&",ja="&&=",ka="&=",H="(",la="*",ma="*=",na="+=",oa=",",pa="-=",qa="->",ra="/",sa="/=",ta=":",ua="::",va=";",I="<",wa="<<",xa="<<=",ya="<=",za="=",Aa="==",Ba="===",J=">",Ca=">=",Da=">>",Ea=">>=",Fa=">>>",Ga=">>>=",Ha="?",Ia="@",L="[",M="^",Ta="^=",Ua="^^",Va="^^=",Wa="{",O="|",Xa="|=",Ya="||",Za="||=",$a="~",ab="break",bb="case",cb="continue",db="delete",eb="do",fb="else",gb="finally",hb="instanceof",ib="return",jb="throw",kb="try",lb="typeof", | |
mb="(?:^^|[+-]",nb="\\$1",ob=")\\s*",pb="&",qb="<",rb=">",sb=""",tb="&#",ub="x",vb="'",wb='"',xb=" ",yb="XMP",zb="</",Ab='="',P="",Q="\\",Bb="b",Cb="t",Db="n",Eb="v",Fb="f",Gb="r",Hb="u",Ib="0",Jb="1",Kb="2",Lb="3",Mb="4",Nb="5",Ob="6",Pb="7",Qb="\\x0",Rb="\\x",Sb="-",Tb="]",Ub="\\\\u[0-9A-Fa-f]{4}|\\\\x[0-9A-Fa-f]{2}|\\\\[0-3][0-7]{0,2}|\\\\[0-7]{1,2}|\\\\[\\s\\S]|-|[^-\\\\]",R="g",Vb="\\B",Wb="\\b",Xb="\\D",Yb="\\d",Zb="\\S",$b="\\s",ac="\\W",bc="\\w",cc="(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)", | |
dc="(?:",ec=")",fc="gi",gc="PRE",hc='<!DOCTYPE foo PUBLIC "foo bar">\n<foo />',ic="\t",jc="\n",kc="[^<]+|<!--[\\s\\S]*?--\>|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>|</?[a-zA-Z][^>]*>|<",lc="nocode",mc=' $1="$2$3$4"',S="pln",nc="string",T="lang-",oc="src",U="str",pc="'\"",qc="'\"`",rc="\"'",V="com",sc="lang-regex",tc="(/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/)",uc="kwd",vc="^(?:",wc=")\\b",xc=" \r\n\t\u00a0",yc="lit",zc="typ",Ac="0123456789",Y="pun",Bc="break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try alignof align_union asm axiom bool concept concept_map const_cast constexpr decltype dynamic_cast explicit export friend inline late_check mutable namespace nullptr reinterpret_cast static_assert static_cast template typeid typename typeof using virtual wchar_t where break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient as base by checked decimal delegate descending event fixed foreach from group implicit in interface internal into is lock object out override orderby params partial readonly ref sbyte sealed stackalloc string select uint ulong unchecked unsafe ushort var break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try debugger eval export function get null set undefined var with Infinity NaN caller delete die do dump elsif eval exit foreach for goto if import last local my next no our print package redo require sub undef unless until use wantarray while BEGIN END break continue do else for if return while and as assert class def del elif except exec finally from global import in is lambda nonlocal not or pass print raise try with yield False True None break continue do else for if return while alias and begin case class def defined elsif end ensure false in module next nil not or redo rescue retry self super then true undef unless until when yield BEGIN END break continue do else for if return while case done elif esac eval fi function in local set then until ", | |
Cc="</span>",Dc='<span class="',Ec='">',Fc="$1 ",Gc=" <br />",Hc="<br />",Ic="console",Jc="cannot override language handler %s",Kc="default-markup",Lc="default-code",Mc="dec",Z="lang-js",$="lang-css",Nc="lang-in.tag",Oc="htm",Pc="html",Qc="mxml",Rc="xhtml",Sc="xml",Tc="xsl",Uc=" \t\r\n",Vc="atv",Wc="tag",Xc="atn",Yc="lang-uq.val",Zc="in.tag",$c="uq.val",ad="break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try alignof align_union asm axiom bool concept concept_map const_cast constexpr decltype dynamic_cast explicit export friend inline late_check mutable namespace nullptr reinterpret_cast static_assert static_cast template typeid typename typeof using virtual wchar_t where ", | |
bd="c",cd="cc",dd="cpp",ed="cxx",fd="cyc",gd="m",hd="null true false",id="json",jd="break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient as base by checked decimal delegate descending event fixed foreach from group implicit in interface internal into is lock object out override orderby params partial readonly ref sbyte sealed stackalloc string select uint ulong unchecked unsafe ushort var ", | |
kd="cs",ld="break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient ",md="java",nd="break continue do else for if return while case done elif esac eval fi function in local set then until ", | |
od="bsh",pd="csh",qd="sh",rd="break continue do else for if return while and as assert class def del elif except exec finally from global import in is lambda nonlocal not or pass print raise try with yield False True None ",sd="cv",td="py",ud="caller delete die do dump elsif eval exit foreach for goto if import last local my next no our print package redo require sub undef unless until use wantarray while BEGIN END ",vd="perl",wd="pl",xd="pm",yd="break continue do else for if return while alias and begin case class def defined elsif end ensure false in module next nil not or redo rescue retry self super then true undef unless until when yield BEGIN END ", | |
zd="rb",Ad="break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try debugger eval export function get null set undefined var with Infinity NaN ",Bd="js",Cd="regex",Dd="pre",Ed="code",Fd="xmp",Gd="prettyprint",Hd="class",Id="br",Jd="\r"; | |
(function(){var N=function(){for(var a=[aa,ba,ca,F,da,ea,G,fa,ja,ka,H,la,ma,na,oa,pa,qa,ra,sa,ta,ua,va,I,wa,xa,ya,za,Aa,Ba,J,Ca,Da,Ea,Fa,Ga,Ha,Ia,L,M,Ta,Ua,Va,Wa,O,Xa,Ya,Za,$a,ab,bb,cb,db,eb,fb,gb,hb,ib,jb,kb,lb],b=mb,c=0;c<a.length;++c)b+=O+a[c].replace(/([^=<>:&a-z])/g,nb);b+=ob;return b}(),Ja=/&/g,Ka=/</g,La=/>/g,Kd=/\"/g;function Ld(a){return a.replace(Ja,pb).replace(Ka,qb).replace(La,rb).replace(Kd,sb)}function ga(a){return a.replace(Ja,pb).replace(Ka,qb).replace(La,rb)}var Md=/</g,Nd=/>/g, | |
Od=/'/g,Pd=/"/g,Qd=/&/g,Rd=/ /g;function Sd(a){var b=a.indexOf(G);if(b<0)return a;for(--b;(b=a.indexOf(tb,b+1))>=0;){var c=a.indexOf(va,b);if(c>=0){var d=a.substring(b+3,c),g=10;if(d&&d.charAt(0)===ub){d=d.substring(1);g=16}var i=parseInt(d,g);isNaN(i)||(a=a.substring(0,b)+String.fromCharCode(i)+a.substring(c+1))}}return a.replace(Md,I).replace(Nd,J).replace(Od,vb).replace(Pd,wb).replace(Qd,G).replace(Rd,xb)}function Ma(a){return yb===a.tagName}function W(a,b){switch(a.nodeType){case 1:var c= | |
a.tagName.toLowerCase();b.push(I,c);for(var d=0;d<a.attributes.length;++d){var g=a.attributes[d];if(g.specified){b.push(xb);W(g,b)}}b.push(J);for(var i=a.firstChild;i;i=i.nextSibling)W(i,b);if(a.firstChild||!/^(?:br|link|img)$/.test(c))b.push(zb,c,J);break;case 2:b.push(a.name.toLowerCase(),Ab,Ld(a.value),wb);break;case 3:case 4:b.push(ga(a.nodeValue));break}}function Na(a){for(var b=0,c=z,d=z,g=0,i=a.length;g<i;++g){var m=a[g];if(m.ignoreCase)d=o;else if(/[a-z]/i.test(m.source.replace(/\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, | |
P))){c=o;d=z;break}}function l(j){if(j.charAt(0)!==Q)return j.charCodeAt(0);switch(j.charAt(1)){case Bb:return 8;case Cb:return 9;case Db:return 10;case Eb:return 11;case Fb:return 12;case Gb:return 13;case Hb:case ub:return parseInt(j.substring(2),16)||j.charCodeAt(1);case Ib:case Jb:case Kb:case Lb:case Mb:case Nb:case Ob:case Pb:return parseInt(j.substring(1),8);default:return j.charCodeAt(1)}}function n(j){if(j<32)return(j<16?Qb:Rb)+j.toString(16);var f=String.fromCharCode(j);if(f===Q||f===Sb|| | |
f===L||f===Tb)f=Q+f;return f}function q(j){for(var f=j.substring(1,j.length-1).match(new RegExp(Ub,R)),s=[],k=[],h=f[0]===M,e=h?1:0,p=f.length;e<p;++e){var t=f[e];switch(t){case Vb:case Wb:case Xb:case Yb:case Zb:case $b:case ac:case bc:s.push(t);continue}var u=l(t),x;if(e+2<p&&Sb===f[e+1]){x=l(f[e+2]);e+=2}else x=u;k.push([u,x]);if(!(x<65||u>122)){x<65||u>90||k.push([Math.max(65,u)|32,Math.min(x,90)|32]);x<97||u>122||k.push([Math.max(97,u)&-33,Math.min(x,122)&-33])}}k.sort(function(Oa,Pa){return Oa[0]- | |
Pa[0]||Pa[1]-Oa[1]});var B=[],E=[NaN,NaN];for(e=0;e<k.length;++e){var A=k[e];if(A[0]<=E[1]+1)E[1]=Math.max(E[1],A[1]);else B.push(E=A)}var D=[L];h&&D.push(M);D.push.apply(D,s);for(e=0;e<B.length;++e){A=B[e];D.push(n(A[0]));if(A[1]>A[0]){A[1]+1>A[0]&&D.push(Sb);D.push(n(A[1]))}}D.push(Tb);return D.join(P)}function v(j){var f=j.source.match(new RegExp(cc,R)),s=f.length,k=[],h,e=0;for(h=0;e<s;++e){var p=f[e];if(p===H)++h;else if(Q===p.charAt(0)){var t=+p.substring(1);if(t&&t<=h)k[t]=-1}}for(e=1;e<k.length;++e)if(-1=== | |
k[e])k[e]=++b;for(h=e=0;e<s;++e){p=f[e];if(p===H){++h;if(k[h]===undefined)f[e]=dc}else if(Q===p.charAt(0))if((t=+p.substring(1))&&t<=h)f[e]=Q+k[h]}for(h=e=0;e<s;++e)if(M===f[e]&&M!==f[e+1])f[e]=P;if(j.ignoreCase&&c)for(e=0;e<s;++e){p=f[e];var u=p.charAt(0);if(p.length>=2&&u===L)f[e]=q(p);else if(u!==Q)f[e]=p.replace(/[a-zA-Z]/g,function(x){var B=x.charCodeAt(0);return L+String.fromCharCode(B&-33,B|32)+Tb})}return f.join(P)}var w=[];g=0;for(i=a.length;g<i;++g){m=a[g];if(m.global||m.multiline)throw new Error(P+ | |
m);w.push(dc+v(m)+ec)}return new RegExp(w.join(O),d?fc:R)}var ha=r;function Td(a){if(r===ha){var b=document.createElement(gc);b.appendChild(document.createTextNode(hc));ha=!/</.test(b.innerHTML)}if(ha){var c=a.innerHTML;if(Ma(a))c=ga(c);return c}for(var d=[],g=a.firstChild;g;g=g.nextSibling)W(g,d);return d.join(P)}function Ud(a){var b=0;return function(c){for(var d=r,g=0,i=0,m=c.length;i<m;++i){var l=c.charAt(i);switch(l){case ic:d||(d=[]);d.push(c.substring(g,i));var n=a-b%a;for(b+=n;n>=0;n-=" ".length)d.push(" ".substring(0, | |
n));g=i+1;break;case jc:b=0;break;default:++b}}if(!d)return c;d.push(c.substring(g));return d.join(P)}}var Vd=new RegExp(kc,R),Wd=/^<\!--/,Xd=/^<\[CDATA\[/,Yd=/^<br\b/i,Qa=/^<(\/?)([a-zA-Z]+)/;function Zd(a){var b=a.match(Vd),c=[],d=0,g=[];if(b)for(var i=0,m=b.length;i<m;++i){var l=b[i];if(l.length>1&&l.charAt(0)===I){if(!Wd.test(l))if(Xd.test(l)){c.push(l.substring(9,l.length-3));d+=l.length-12}else if(Yd.test(l)){c.push(jc);++d}else if(l.indexOf(lc)>=0&&$d(l)){var n=l.match(Qa)[2],q=1,v;v=i+1;a:for(;v< | |
m;++v){var w=b[v].match(Qa);if(w&&w[2]===n)if(w[1]===ra){if(--q===0)break a}else++q}if(v<m){g.push(d,b.slice(i,v+1).join(P));i=v}else g.push(d,l)}else g.push(d,l)}else{var j=Sd(l);c.push(j);d+=j.length}}return{source:c.join(P),tags:g}}function $d(a){return!!a.replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,mc).match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/)}function ia(a,b,c,d){if(b){var g={source:b,b:a};c(g);d.push.apply(d,g.c)}}function K(a,b){var c={},d;(function(){for(var m=a.concat(b), | |
l=[],n={},q=0,v=m.length;q<v;++q){var w=m[q],j=w[3];if(j)for(var f=j.length;--f>=0;)c[j.charAt(f)]=w;var s=w[1],k=P+s;if(!n.hasOwnProperty(k)){l.push(s);n[k]=r}}l.push(/[\0-\uffff]/);d=Na(l)})();var g=b.length,i=function(m){for(var l=m.source,n=m.b,q=[n,S],v=0,w=l.match(d)||[],j={},f=0,s=w.length;f<s;++f){var k=w[f],h=j[k],e,p;if(typeof h===nc)p=z;else{var t=c[k.charAt(0)];if(t){e=k.match(t[1]);h=t[0]}else{for(var u=0;u<g;++u){t=b[u];if(e=k.match(t[1])){h=t[0];break}}e||(h=S)}if((p=h.length>=5&&T=== | |
h.substring(0,5))&&!(e&&e[1])){p=z;h=oc}p||(j[k]=h)}var x=v;v+=k.length;if(p){var B=e[1],E=k.indexOf(B),A=E+B.length,D=h.substring(5);ia(n+x,k.substring(0,E),i,q);ia(n+x+E,B,Ra(D,B),q);ia(n+x+A,k.substring(A),i,q)}else q.push(n+x,h)}m.c=q};return i}function C(a){var b=[],c=[];if(a.tripleQuotedStrings)b.push([U,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,r,pc]); | |
else a.multiLineStrings?b.push([U,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,r,qc]):b.push([U,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,r,rc]);if(a.hashComments)a.cStyleComments?b.push([V,/^#(?:[^\r\n\/]|\/(?!\*)|\/\*[^\r\n]*?\*\/)*/,r,F]):b.push([V,/^#[^\r\n]*/,r,F]);if(a.cStyleComments){c.push([V,/^\/\/[^\r\n]*/,r]);c.push([V,/^\/\*[\s\S]*?(?:\*\/|$)/,r])}a.regexLiterals&&c.push([sc,new RegExp(M+N+tc)]);var d= | |
a.keywords.replace(/^\s+|\s+$/g,P);d.length&&c.push([uc,new RegExp(vc+d.replace(/\s+/g,O)+wc),r]);b.push([S,/^\s+/,r,xc]);c.push([yc,/^@[a-z_$][a-z_$@0-9]*/i,r,Ia],[zc,/^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/,r],[S,/^[a-z_$][a-z_$@0-9]*/i,r],[yc,/^(?:0x[a-f0-9]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+\-]?\d+)?)[a-z]*/i,r,Ac],[Y,/^.[^\s\w\.$@\'\"\`\/\#]*/,r]);return K(b,c)}var ae=C({keywords:Bc,hashComments:o,cStyleComments:o,multiLineStrings:o,regexLiterals:o});function be(a){var b=a.source,c=a.f,d=a.c, | |
g=[],i=0,m=r,l=r,n=0,q=0,v=Ud(window.PR_TAB_WIDTH),w=/([\r\n ]) /g,j=/(^| ) /gm,f=/\r\n?|\n/g,s=/[ \r\n]$/,k=o;function h(p){if(p>i){if(m&&m!==l){g.push(Cc);m=r}if(!m&&l){m=l;g.push(Dc,m,Ec)}var t=ga(v(b.substring(i,p))).replace(k?j:w,Fc);k=s.test(t);var u=window._pr_isIE6()?Gc:Hc;g.push(t.replace(f,u));i=p}}for(;1;){var e;if(e=n<c.length?q<d.length?c[n]<=d[q]:o:z){h(c[n]);if(m){g.push(Cc);m=r}g.push(c[n+1]);n+=2}else if(q<d.length){h(d[q]);l=d[q+1];q+=2}else break}h(b.length);m&&g.push(Cc);a.a=g.join(P)} | |
var X={};function y(a,b){for(var c=b.length;--c>=0;){var d=b[c];if(X.hasOwnProperty(d))Ic in window&&console.i(Jc,d);else X[d]=a}}function Ra(a,b){a&&X.hasOwnProperty(a)||(a=/^\s*</.test(b)?Kc:Lc);return X[a]}y(ae,[Lc]);y(K([],[[S,/^[^<?]+/],[Mc,/^<!\w[^>]*(?:>|$)/],[V,/^<\!--[\s\S]*?(?:-\->|$)/],[T,/^<\?([\s\S]+?)(?:\?>|$)/],[T,/^<%([\s\S]+?)(?:%>|$)/],[Y,/^(?:<[%?]|[%?]>)/],[T,/^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],[Z,/^<script\b[^>]*>([\s\S]+?)<\/script\b[^>]*>/i],[$,/^<style\b[^>]*>([\s\S]+?)<\/style\b[^>]*>/i], | |
[Nc,/^(<\/?[a-z][^<>]*>)/i]]),[Kc,Oc,Pc,Qc,Rc,Sc,Tc]);y(K([[S,/^[\s]+/,r,Uc],[Vc,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,r,rc]],[[Wc,/^^<\/?[a-z](?:[\w:-]*\w)?|\/?>$/],[Xc,/^(?!style\b|on)[a-z](?:[\w:-]*\w)?/],[Yc,/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[Y,/^[=<>\/]+/],[Z,/^on\w+\s*=\s*\"([^\"]+)\"/i],[Z,/^on\w+\s*=\s*\'([^\']+)\'/i],[Z,/^on\w+\s*=\s*([^\"\'>\s]+)/i],[$,/^sty\w+\s*=\s*\"([^\"]+)\"/i],[$,/^sty\w+\s*=\s*\'([^\']+)\'/i],[$,/^sty\w+\s*=\s*([^\"\'>\s]+)/i]]),[Zc]);y(K([],[[Vc,/^[\s\S]+/]]), | |
[$c]);y(C({keywords:ad,hashComments:o,cStyleComments:o}),[bd,cd,dd,ed,fd,gd]);y(C({keywords:hd}),[id]);y(C({keywords:jd,hashComments:o,cStyleComments:o}),[kd]);y(C({keywords:ld,cStyleComments:o}),[md]);y(C({keywords:nd,hashComments:o,multiLineStrings:o}),[od,pd,qd]);y(C({keywords:rd,hashComments:o,multiLineStrings:o,tripleQuotedStrings:o}),[sd,td]);y(C({keywords:ud,hashComments:o,multiLineStrings:o,regexLiterals:o}),[vd,wd,xd]);y(C({keywords:yd,hashComments:o,multiLineStrings:o,regexLiterals:o}), | |
[zd]);y(C({keywords:Ad,cStyleComments:o,regexLiterals:o}),[Bd]);y(K([],[[U,/^[\s\S]+/]]),[Cd]);function Sa(a){var b=a.e,c=a.d;a.a=b;try{var d=Zd(b),g=d.source;a.source=g;a.b=0;a.f=d.tags;Ra(c,g)(a);be(a)}catch(i){if(Ic in window){console.log(i);console.h()}}}function ce(a,b){var c={e:a,d:b};Sa(c);return c.a}function de(a){for(var b=window._pr_isIE6(),c=[document.getElementsByTagName(Dd),document.getElementsByTagName(Ed),document.getElementsByTagName(Fd)],d=[],g=0;g<c.length;++g)for(var i=0,m=c[g].length;i< | |
m;++i)d.push(c[g][i]);c=r;var l=Date;l.now||(l={now:function(){return(new Date).getTime()}});var n=0,q;function v(){for(var j=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;n<d.length&&l.now()<j;n++){var f=d[n];if(f.className&&f.className.indexOf(Gd)>=0){var s=f.className.match(/\blang-(\w+)\b/);if(s)s=s[1];for(var k=z,h=f.parentNode;h;h=h.parentNode)if((h.tagName===Dd||h.tagName===Ed||h.tagName===Fd)&&h.className&&h.className.indexOf(Gd)>=0){k=o;break}if(!k){var e=Td(f);e=e.replace(/(?:\r\n?|\n)$/, | |
P);q={e:e,d:s,g:f};Sa(q);w()}}}if(n<d.length)setTimeout(v,250);else a&&a()}function w(){var j=q.a;if(j){var f=q.g;if(Ma(f)){for(var s=document.createElement(gc),k=0;k<f.attributes.length;++k){var h=f.attributes[k];if(h.specified){var e=h.name.toLowerCase();if(e===Hd)s.className=h.value;else s.setAttribute(h.name,h.value)}}s.innerHTML=j;f.parentNode.replaceChild(s,f);f=s}else f.innerHTML=j;if(b&&f.tagName===gc)for(var p=f.getElementsByTagName(Id),t=p.length;--t>=0;){var u=p[t];u.parentNode.replaceChild(document.createTextNode(Jd), | |
u)}}}v()}window.PR_normalizedHtml=W;window.prettyPrintOne=ce;window.prettyPrint=de;window.PR={combinePrefixPatterns:Na,createSimpleLexer:K,registerLangHandler:y,sourceDecorator:C,PR_ATTRIB_NAME:Xc,PR_ATTRIB_VALUE:Vc,PR_COMMENT:V,PR_DECLARATION:Mc,PR_KEYWORD:uc,PR_LITERAL:yc,PR_NOCODE:lc,PR_PLAIN:S,PR_PUNCTUATION:Y,PR_SOURCE:oc,PR_STRING:U,PR_TAG:Wc,PR_TYPE:zc}})(); | |
})() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0"?> | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>XEP-0384: OMEMO Encryption</title><link rel="stylesheet" type="text/css" href="https://cdn.rawgit.com/mnemonicflow/6fa778086b4fcadafe57118f79b84c43/raw/b13faa72055c5dc2ce1e13068a3adc6a82d83de6/xmpp.css" /><link href="https://cdn.rawgit.com/mnemonicflow/6fa778086b4fcadafe57118f79b84c43/raw/b13faa72055c5dc2ce1e13068a3adc6a82d83de6/prettify.css" type="text/css" rel="stylesheet" /><link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" /><script type="text/javascript" src="https://cdn.rawgit.com/mnemonicflow/6fa778086b4fcadafe57118f79b84c43/raw/b13faa72055c5dc2ce1e13068a3adc6a82d83de6/prettify.js"></script><meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=2.0" /><meta name="DC.Title" content="OMEMO Encryption" /><meta name="DC.Creator" content="Andreas Straub" /><meta name="DC.Description" content="This specification defines a protocol for end-to-end encryption in one-on-one chats that may have multiple clients per account." /><meta name="DC.Publisher" content="XMPP Standards Foundation" /><meta name="DC.Contributor" content="XMPP Extensions Editor" /><meta name="DC.Date" content="2017-03-25" /><meta name="DC.Type" content="XMPP Extension Protocol" /><meta name="DC.Format" content="XHTML" /><meta name="DC.Identifier" content="XEP-0384" /><meta name="DC.Language" content="en" /><meta name="DC.Rights" content="This XMPP Extension Protocol is copyright © 1999 – 2017 by the XMPP Standards Foundation (XSF)." /></head><body onload="prettyPrint()"><h1>XEP-0384: OMEMO Encryption</h1><table><tr valign="top"><td><strong>Abstract:</strong></td><td>This specification defines a protocol for end-to-end encryption in one-on-one chats that may have multiple clients per account.</td></tr><tr valign="top"><td><strong>Author:</strong></td><td>Andreas Straub</td></tr><tr valign="top"><td><strong>Copyright:</strong></td><td>© 1999 – 2017 XMPP Standards Foundation. <a href="#appendix-legal">SEE LEGAL NOTICES</a>.</td></tr><tr valign="top"><td><strong>Status:</strong></td><td>Experimental</td></tr><tr valign="top"><td><strong>Type:</strong></td><td>Standards Track</td></tr><tr valign="top"><td><strong>Version:</strong></td><td>0.2</td></tr><tr valign="top"><td><strong>Last Updated:</strong></td><td>2017-03-25</td></tr></table><hr /><p style="color:red">WARNING: This Standards-Track document is Experimental. Publication as an XMPP Extension Protocol does not imply approval of this proposal by the XMPP Standards Foundation. Implementation of the protocol described herein is encouraged in exploratory implementations, but production systems are advised to carefully consider whether it is appropriate to deploy implementations of this protocol before it advances to a status of Draft.</p><hr /><h2>Table of Contents</h2><div class="indent"><p><br />1. <a href="#intro">Introduction</a><br /> | |
1.1. <a href="#intro-motivation">Motivation</a><br /> | |
1.2. <a href="#intro-overview">Overview</a><br />2. <a href="#reqs">Requirements</a><br />3. <a href="#glossary">Glossary</a><br /> | |
3.1. <a href="#glossary-general">General Terms</a><br /> | |
3.2. <a href="#glossary-odr">ODR-specific</a><br />4. <a href="#odr">OMEMO Double Ratchet</a><br /> | |
4.1. <a href="#odr-parameters">Parameters</a><br /> | |
4.2. <a href="#odr-wire">Wire format</a><br />5. <a href="#usecases">Use Cases</a><br /> | |
5.1. <a href="#usecases-setup">Setup</a><br /> | |
5.2. <a href="#usecases-discovering">Discovering peer support</a><br /> | |
5.3. <a href="#usecases-announcing">Announcing support</a><br /> | |
5.4. <a href="#usecases-building">Building a session</a><br /> | |
5.5. <a href="#usecases-messagesend">Sending a message</a><br /> | |
5.6. <a href="#usecases-keysend">Sending a key</a><br /> | |
5.7. <a href="#usecases-receiving">Receiving a message</a><br /> | |
5.7.1. <a href="#usecase-receiving-keys">Processing the matching elements</a><br /> | |
5.8. <a href="#uninstall">Removing OMEMO support</a><br />6. <a href="#rules">Business Rules</a><br />7. <a href="#impl">Implementation Notes</a><br />8. <a href="#security">Security Considerations</a><br />9. <a href="#iana">IANA Considerations</a><br />10. <a href="#registrar">XMPP Registrar Considerations</a><br /> | |
10.1. <a href="#namespaces">Protocol Namespaces</a><br /> | |
10.2. <a href="#versioning">Protocol Versioning</a><br />11. <a href="#schema">XML Schema</a><br />12. <a href="#ack">Acknowledgements</a></p><p><a href="#appendices">Appendices</a><br /> <a href="#appendix-docinfo">A: Document Information</a><br /> <a href="#appendix-authorinfo">B: Author Information</a><br /> <a href="#appendix-legal">C: Legal Notices</a><br /> <a href="#appendix-xmpp">D: Relation to XMPP</a><br /> <a href="#appendix-discuss">E: Discussion Venue</a><br /> <a href="#appendix-conformance">F: Requirements Conformance</a><br /> <a href="#appendix-notes">G: Notes</a><br /> <a href="#appendix-revs">H: Revision History</a></p></div><hr /><h2>1. | |
<a name="intro" id="intro">Introduction</a></h2> | |
<div class="indent"><h3>1.1 <a name="intro-motivation" id="intro-motivation">Motivation</a></h3> | |
<p class="" style=""> | |
There are two main end-to-end encryption schemes in common use in the XMPP | |
ecosystem, Off-the-Record (OTR) messaging (<span class="ref" style=""><a href="https://xmpp.org/extensions/xep-0364.html">Current Off-the-Record Messaging Usage (XEP-0364)</a></span> [<a href="#nt-idm46032767451904">1</a>]) and OpenPGP | |
(<span class="ref" style=""><a href="https://xmpp.org/extensions/xep-0027.html">Current Jabber OpenPGP Usage (XEP-0027)</a></span> [<a href="#nt-idm46032766467360">2</a>]). OTR has significant usability drawbacks for inter-client | |
mobility. As OTR sessions exist between exactly two clients, the chat | |
history will not be synchronized across other clients of the involved | |
parties. Furthermore, OTR chats are only possible if both participants are | |
currently online, due to how the rolling key agreement scheme of OTR | |
works. OpenPGP, while not suffering from these mobility issues, does not | |
provide any kind of forward secrecy and is vulnerable to replay attacks. | |
Additionally, PGP over XMPP uses a custom wireformat which is defined by | |
convention rather than standardization, and involves quite a bit of | |
external complexity. | |
</p> | |
<p class="" style=""> | |
This XEP defines a protocol that leverages <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/doubleratchet/doubleratchet.pdf">Double Ratchet</a></span> [<a href="#nt-idm46032766461408">3</a>] encryption | |
with <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/x3dh/x3dh.pdf">X3DH</a></span> [<a href="#nt-idm46032766452112">4</a>] Key Agreement to provide | |
multi-end to multi-end encryption, allowing messages to be synchronized | |
securely across multiple clients, even if some of them are offline. <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/doubleratchet/doubleratchet.pdf">Double Ratchet</a></span> [<a href="#nt-idm46032766461408">3</a>] | |
and <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/x3dh/x3dh.pdf">X3DH</a></span> [<a href="#nt-idm46032766452112">4</a>] are cryptographic protocols designed by Trevor Perrin | |
and Moxie Marlinspike and were first published under the name Axolotl. | |
</p> | |
</div> | |
<div class="indent"><h3>1.2 <a name="intro-overview" id="intro-overview">Overview</a></h3> | |
<p class="" style=""> | |
The general idea behind this protocol is to maintain separate, | |
long-standing <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/doubleratchet/doubleratchet.pdf">Double Ratchet</a></span> [<a href="#nt-idm46032766461408">3</a>] sessions with each device of each contact | |
(as well as with each of our own other devices), which are used as secure key | |
transport channels. In this scheme, each message is encrypted with a | |
fresh, randomly generated encryption key. An encrypted header is added to | |
the message for each device that is supposed to receive it. These headers | |
simply contain the key that the payload message is encrypted with, and | |
they are separately encrypted using the session corresponding to the | |
counterpart device. The encrypted payload is sent together with the | |
headers as a <message> stanza. Individual recipient devices can | |
decrypt the header item intended for them, and use the contained payload | |
key to decrypt the payload message. | |
</p> | |
<p class="" style=""> | |
As the encrypted payload is common to all recipients, it only has to be | |
included once, reducing overhead. Furthermore, <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/doubleratchet/doubleratchet.pdf">Double Ratchet</a></span> [<a href="#nt-idm46032766461408">3</a>]'s transparent handling | |
of messages that were lost or received out of order, as well as those sent | |
while the recipient was offline, is maintained by this protocol. As a | |
result, in combination with <span class="ref" style=""><a href="https://xmpp.org/extensions/xep-0280.html">Message Carbons (XEP-0280)</a></span> [<a href="#nt-idm46032766437136">5</a>] and <span class="ref" style=""><a href="https://xmpp.org/extensions/xep-0313.html">Message Archive Management (XEP-0313)</a></span> [<a href="#nt-idm46032766431856">6</a>], the desired property | |
of inter-client history synchronization is achieved. | |
</p> | |
<p class="" style=""> | |
The cryptographic algorithms used by OMEMO are collectively referred to as | |
the OMEMO Double Ratchet (ODR). In order to make the protocol independent | |
from specific server-side support, <span class="ref" style=""><a href="https://xmpp.org/extensions/xep-0163.html">Personal Eventing Protocol (XEP-0163)</a></span> [<a href="#nt-idm46032766425856">7</a>] (PEP) is used to fill the 'server' | |
role of <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/x3dh/x3dh.pdf">X3DH</a></span> [<a href="#nt-idm46032766452112">4</a>]. | |
</p> | |
</div> | |
<h2>2. | |
<a name="reqs" id="reqs">Requirements</a></h2> | |
<ul class="" style=""> | |
<li class="" style="">Provide forward secrecy</li> | |
<li class="" style="">Ensure chat messages can be deciphered by all (capable) clients of both parties</li> | |
<li class="" style="">Be usable regardless of the participants' online statuses</li> | |
<li class="" style="">Provide a method to exchange auxilliary keying material. This could for example be used to secure encrypted file transfers.</li> | |
</ul> | |
<h2>3. | |
<a name="glossary" id="glossary">Glossary</a></h2> | |
<div class="indent"><h3>3.1 <a name="glossary-general" id="glossary-general">General Terms</a></h3> | |
<div class="indent"><dl> | |
<di> | |
<dt><strong> | |
Device | |
</strong></dt><dd> | |
A communication end point, i.e. a specific client instance | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
OMEMO element | |
</strong></dt><dd> | |
An <encrypted> element in the urn:xmpp:omemo:1 namespace. Can be | |
either MessageElement or a KeyTransportElement | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
OMEMO Message Element | |
</strong></dt><dd> | |
An OMEMO Element that contains a chat message. Its <payload>, | |
when decrypted, corresponds to a <message>'s <body>. | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
OMEMO Key Transport Element | |
</strong></dt><dd> | |
An OMEMO Element that does not have a <payload>. It contains a | |
fresh encryption key, which can be used for purposes external to this | |
XEP. | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
OMEMO Ratchet Update Message | |
</strong></dt><dd> | |
A <message> containing an OMEMO Key Transport Element but no | |
other payload or reference to a payload. | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
matching element | |
</strong></dt><dd> | |
An <envelope> or <init-envelope> element with an rid matching our own | |
device ID | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
Bundle | |
</strong></dt><dd> | |
A collection of publicly accessible data that can be used to build a | |
session with a device, namely its public Identity-key, a signed-pre-key | |
with corresponding signature, and a list of (single use) pre-keys. | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
active device | |
</strong></dt><dd> | |
Any device whose ID is currently announced in the devicelist | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
inactive device | |
</strong></dt><dd> | |
Any device whose ID is NOT currently announced in the devicelist | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
rid | |
</strong></dt><dd> | |
The device id of the intended recipient of the containing envelope | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
sid | |
</strong></dt><dd> | |
The device id of the sender of the containing OMEMO element | |
</dd> | |
</di> | |
</dl></div> | |
</div> | |
<div class="indent"><h3>3.2 <a name="glossary-odr" id="glossary-odr">ODR-specific</a></h3> | |
<p class="" style=""> | |
The terminology of ODR is chosen to be analogous to the <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/doubleratchet/doubleratchet.pdf">Double Ratchet</a></span> [<a href="#nt-idm46032766461408">3</a>] | |
and <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/x3dh/x3dh.pdf">X3DH</a></span> [<a href="#nt-idm46032766452112">4</a>] specifications wherever possible. ODR protocol messages are | |
referred to as envelopes in order to distinguish them from XMPP | |
<message>s. | |
</p> | |
<div class="indent"><dl> | |
<di> | |
<dt><strong> | |
identity-key | |
</strong></dt><dd> | |
Per-device public/private key pair used to authenticate communications | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
pre-key | |
</strong></dt><dd> | |
A Diffie-Hellman public key, published in bulk and ahead of time | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
signed-pre-key | |
</strong></dt><dd> | |
A Diffie-Hellman public key and a signature on this key (using the | |
identity-key), published ahead of time and regularly rotated | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
omemo-envelope | |
</strong></dt><dd> | |
An ODR-encrypted envelope containing key material | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
init-omemo-envelope | |
</strong></dt><dd> | |
An ODR-encrypted envelope containing key material. This envelope | |
includes the initial key exchange which is used to transparently build | |
sessions with the first exchanged envelope. | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
pkid | |
</strong></dt><dd> | |
The ID of the counterpart's pre-key that was selected for an | |
init-omemo-envelope | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
spkid | |
</strong></dt><dd> | |
The ID of the counterpart's signed-pre-key that was selected for an | |
init-omemo-envelope | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
ek | |
</strong></dt><dd> | |
The own ephemeral key that was used for the init-omemo-envelope | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
ik | |
</strong></dt><dd> | |
The own identity-key | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
dhs | |
</strong></dt><dd> | |
The own sending ratchet DH public key | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
pn | |
</strong></dt><dd> | |
The previous chain length for a (init-)omemo-envelope | |
</dd> | |
</di> | |
<di> | |
<dt><strong> | |
n | |
</strong></dt><dd> | |
The envelope number of a (init-)omemo-envelope | |
</dd> | |
</di> | |
</dl></div> | |
</div> | |
<h2>4. | |
<a name="odr" id="odr">OMEMO Double Ratchet</a></h2> | |
<p class="" style=""> | |
The OMEMO Double Ratchet (ODR) uses the <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/doubleratchet/doubleratchet.pdf">Double Ratchet</a></span> [<a href="#nt-idm46032766461408">3</a>] algorithm (WITHOUT | |
header encryption) with <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/x3dh/x3dh.pdf">X3DH</a></span> [<a href="#nt-idm46032766452112">4</a>] key agreement. For details on these, see the | |
respective specifications. | |
</p> | |
<div class="indent"><h3>4.1 <a name="odr-parameters" id="odr-parameters">Parameters</a></h3> | |
<p class="" style=""> | |
ODR uses <span class="ref" style=""><a href="http://cr.yp.to/ecdh/curve25519-20060209.pdf">Curve25519</a></span> [<a href="#nt-idm46032766368000">8</a>], SHA-256, HKDF, and AES-256-CBC-HMAC as suggested in | |
Section 5.2 of the <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/doubleratchet/doubleratchet.pdf">Double Ratchet</a></span> [<a href="#nt-idm46032766461408">3</a>] specification. The same primitives are | |
used for <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/x3dh/x3dh.pdf">X3DH</a></span> [<a href="#nt-idm46032766452112">4</a>] where applicable. The info string for the key agreement is | |
'OMEMO X3DH' and the info string for the ratchet is 'OMEMO Double | |
Ratchet'. | |
</p> | |
</div> | |
<div class="indent"><h3>4.2 <a name="odr-wire" id="odr-wire">Wire format</a></h3> | |
<p class="" style=""> | |
ODR envelopes are serialized into an XML structure. These make up the | |
headers of OMEMO messages. | |
</p> | |
<p class="caption"><a name="example-1" id="example-1"></a>Example 1. An ODR element for an omemo-envelope</p><div class="indent"><pre class="prettyprint"> | |
<envelope rid='123' n='2' pn='1' dhs='BASE64ENCODED...'>BASE64ENCODED</envelope> | |
</pre></div> | |
<p class="" style=""> | |
Init-omemo-envelopes include additional attributes for pkid, spkid, ek, | |
and ik. | |
</p> | |
<p class="caption"><a name="example-2" id="example-2"></a>Example 2. An ODR element for a init-omemo-envelope</p><div class="indent"><pre class="prettyprint"> | |
<init-envelope rid='123' pkid='1' spkid='1' ek='BASE64ENCODED...' ik='BASE64ENCODED' n='2' pn='0' dhs='BASE64ENCODED...'>BASE64ENCODED</init-envelope> | |
</pre></div> | |
</div> | |
<h2>5. | |
<a name="usecases" id="usecases">Use Cases</a></h2> | |
<div class="indent"><h3>5.1 <a name="usecases-setup" id="usecases-setup">Setup</a></h3> | |
<p class="" style=""> | |
The first thing that needs to happen if a client wants to start using | |
OMEMO is they need to generate an IdentityKey and a Device ID. The | |
IdentityKey is a <span class="ref" style=""><a href="http://cr.yp.to/ecdh/curve25519-20060209.pdf">Curve25519</a></span> [<a href="#nt-idm46032766368000">8</a>] public/private Key pair. The Device ID is a | |
randomly generated integer between 1 and 2^31 - 1. | |
</p> | |
</div> | |
<div class="indent"><h3>5.2 <a name="usecases-discovering" id="usecases-discovering">Discovering peer support</a></h3> | |
<p class="" style=""> | |
In order to determine whether a given contact has devices that support | |
OMEMO, the devicelist node in PEP is consulted. Devices MUST subscribe to | |
'urn:xmpp:omemo:1:devicelist' via PEP, so that they are informed whenever | |
their contacts add a new device. They MUST cache the most up-to-date | |
version of the devicelist. | |
</p> | |
<p class="caption"><a name="example-3" id="example-3"></a>Example 3. Devicelist update received by subscribed clients</p><div class="indent"><pre class="prettyprint"> | |
<message from='[email protected]' | |
to='[email protected]' | |
type='headline' | |
id='update_01'> | |
<event xmlns='http://jabber.org/protocol/pubsub#event'> | |
<items node='urn:xmpp:omemo:1:devicelist'> | |
<item> | |
<list xmlns='urn:xmpp:omemo:1'> | |
<device id='12345' /> | |
<device id='4223' /> | |
</list> | |
</item> | |
</items> | |
</event> | |
</message></pre></div> | |
</div> | |
<div class="indent"><h3>5.3 <a name="usecases-announcing" id="usecases-announcing">Announcing support</a></h3> | |
<p class="" style=""> | |
In order for other devices to be able to initiate a session with a given | |
device, it first has to announce itself by adding its device ID to the | |
devicelist PEP node. | |
</p> | |
<p class="caption"><a name="example-4" id="example-4"></a>Example 4. Adding the own device ID to the list</p><div class="indent"><pre class="prettyprint"> | |
<iq from='[email protected]' type='set' id='announce1'> | |
<pubsub xmlns='http://jabber.org/protocol/pubsub'> | |
<publish node='urn:xmpp:omemo:1:devicelist'> | |
<item> | |
<list xmlns='urn:xmpp:omemo:1'> | |
<device id='12345' /> | |
<device id='4223' /> | |
<device id='31415' /> | |
</list> | |
</item> | |
</publish> | |
</pubsub> | |
</iq></pre></div> | |
<p class="" style=""> | |
Furthermore, a device MUST announce it's identity-key, a signed-pre-key, | |
and a list of pre-keys in a separate, per-device PEP node. | |
</p> | |
<p class="caption"><a name="example-5" id="example-5"></a>Example 5. Announcing bundle information</p><div class="indent"><pre class="prettyprint"> | |
<iq from='[email protected]' type='set' id='announce2'> | |
<pubsub xmlns='http://jabber.org/protocol/pubsub'> | |
<publish node='urn:xmpp:omemo:1:bundles:31415'> | |
<item> | |
<bundle xmlns='urn:xmpp:omemo:1'> | |
<signed-pre-key-public spkid='1'> | |
BASE64ENCODED... | |
</signed-pre-key-public> | |
<signed-pre-key-signature> | |
BASE64ENCODED... | |
</signed-pre-key-signature> | |
<identity-key> | |
BASE64ENCODED... | |
</identity-key> | |
<pre-keys> | |
<pre-key-public pkid='1'> | |
BASE64ENCODED... | |
</pre-key-public> | |
<pre-key-public pkid='2'> | |
BASE64ENCODED... | |
</pre-key-public> | |
<pre-key-public pkid='3'> | |
BASE64ENCODED... | |
</pre-key-public> | |
<!-- ... --> | |
</pre-keys> | |
</bundle> | |
</item> | |
</publish> | |
</pubsub> | |
</iq></pre></div> | |
</div> | |
<div class="indent"><h3>5.4 <a name="usecases-building" id="usecases-building">Building a session</a></h3> | |
<p class="" style="">In order to build a session with a device, their bundle information is fetched.</p> | |
<p class="caption"><a name="example-6" id="example-6"></a>Example 6. Fetching a device's bundle information</p><div class="indent"><pre class="prettyprint"> | |
<iq type='get' | |
from='[email protected]' | |
to='[email protected]' | |
id='fetch1'> | |
<pubsub xmlns='http://jabber.org/protocol/pubsub'> | |
<items node='urn:xmpp:omemo:1:bundles:31415'/> | |
</pubsub> | |
</iq></pre></div> | |
<p class="" style=""> | |
A random pre-key-public entry is selected, and used to build an ODR session. | |
</p> | |
</div> | |
<div class="indent"><h3>5.5 <a name="usecases-messagesend" id="usecases-messagesend">Sending a message</a></h3> | |
<p class="" style=""> | |
In order to send a chat message, its <body> first has to be | |
encrypted. The client MUST generate a fresh 32-byte secret. This secret is | |
used in an AES-256-CBC and HMAC-SHA-256 based AEAD scheme identical to the | |
encryption in the ODR. The HKDF info string is 'OMEMO Payload'. For | |
details see Section 5.2 of the <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/doubleratchet/doubleratchet.pdf">Double Ratchet</a></span> [<a href="#nt-idm46032766461408">3</a>] specification. However the | |
HMAC output is NOT appended to the ciphertext. Rather, the HMAC output is | |
appended to the 32-byte secret. For each intended recipient device, i.e. | |
both own trusted devices as well as trusted devices associated with the | |
contact, this secret/HMAC combination is encrypted using the | |
corresponding long-standing ODR session. Each encrypted secret/HMAC | |
combination is tagged with the recipient device's ID. This is all | |
serialized into an OMEMO Message Element, which is transmitted in a | |
<message> as follows: | |
</p> | |
<p class="caption"><a name="example-7" id="example-7"></a>Example 7. Sending a message</p><div class="indent"><pre class="prettyprint"> | |
<message to='[email protected]' from='[email protected]' id='send1'> | |
<encrypted xmlns='urn:xmpp:omemo:1'> | |
<header sid='27183'>` | |
<envelope rid='31415' n='20' pn='5' dhs='BASE64ENCODED...'>BASE64ENCODED</envelope> | |
<init-envelope rid='12321' pkid='1' spkid='1' ek='BASE64ENCODED...' ik='BASE64ENCODED' n='2' pn='0' dhs='BASE64ENCODED...'>BASE64ENCODED</init-enveope> | |
<!-- ... --> | |
<iv>BASE64ENCODED...</iv> | |
</header> | |
<payload>BASE64ENCODED</payload> | |
</encrypted> | |
<store xmlns='urn:xmpp:hints'/> | |
</message></pre></div> | |
</div> | |
<div class="indent"><h3>5.6 <a name="usecases-keysend" id="usecases-keysend">Sending a key</a></h3> | |
<p class="" style=""> | |
The client may wish to transmit keying material to the contact for | |
external use (e.g. file transfer). This works analogously to sending a | |
regular message. A secret is generated and keys are derived (using the | |
info string 'OMEMO Payload') as before. The externally transmitted data | |
is encrypted and authenticated using the same CBC-HMAC AEAD scheme. The | |
HMAC is then appended to the secret, and they are separately encrypted | |
for each intended recipient device using the corresponding long-standing | |
ODR sessions and tagged with the respective rids. The resulting | |
collection of envelopes is serialized into a OMEMO Key Transport Element | |
which omits the <payload>. | |
</p> | |
<p class="caption"><a name="example-8" id="example-8"></a>Example 8. Sending a key</p><div class="indent"><pre class="prettyprint"> | |
<encrypted xmlns='urn:xmpp:omemo:1'> | |
<header sid='27183'> | |
<envelope rid='31415' n='21' pn='5' dhs='BASE64ENCODED...'>BASE64ENCODED</envelope> | |
<init-envelope rid='12321' pkid='1' spkid='1' ek='BASE64ENCODED...' ik='BASE64ENCODED' n='3' pn='0' dhs='BASE64ENCODED...'>BASE64ENCODED</init-envelope> | |
<!-- ... --> | |
<iv>BASE64ENCODED...</iv> | |
</header> | |
</encrypted></pre></div> | |
<p class="" style="">This OMEMO Key Transport Element can then be sent over any applicable transport mechanism.</p> | |
</div> | |
<div class="indent"><h3>5.7 <a name="usecases-receiving" id="usecases-receiving">Receiving a message</a></h3> | |
<p class="" style=""> | |
When an OMEMO element is received, the client first processes the | |
<header>. It MUST check whether there is at least one envelope with | |
an rid attribute matching its own device ID ("matching elements"). If | |
this is not the case, the element MUST be silently discarded. There might | |
also be more than one matching element, as device IDs are only unique on | |
a per-account basis, so the recipient MUST perform the following steps | |
for each matching element until one has been processed successfully. | |
</p> | |
<div class="indent"><h3>5.7.1 <a name="usecase-receiving-keys" id="usecase-receiving-keys">Processing the matching elements</a></h3> | |
<p class="" style=""> | |
If the element is an init-omemo-envelope, a new session is built from | |
this received element. If this fails (e.g. because the pkid is not | |
valid), the client MUST try to continue with the next matching element. | |
If it succeeds, the client SHOULD then republish their bundle | |
information, replacing the used pre-key, such that it won't be used | |
again by a different client. The client MUST delete the private key | |
belonging to the pre-key after use. If the client already had an ODR | |
session with the sender's device, it MUST replace this session with the | |
newly built session. | |
</p> | |
<p class="" style=""> | |
If the element is an omemo-envelope, and the client has a session | |
with the sender's device, it tries to decrypt the omemo-envelope using this | |
session. If the decryption fails, the client MUST try to continue with the | |
next matching element. | |
</p> | |
<p class="" style=""> | |
If there are no more matching elements left to try, the OMEMO element | |
MUST be silently discarded. | |
</p> | |
</div> | |
<p class="" style=""> | |
After having successfully processed one matching element, the client | |
extracts the secret and HMAC. It then derives 32-byte authentication and | |
encryption keys as well as the 16-byte IV from the secret using | |
HKDF-SHA-256 as before. It is now done processing the <header>, and | |
can continue with the rest of the OMEMO element. | |
</p> | |
<p class="" style=""> | |
If the OMEMO element contains a <payload>, it is an OMEMO Message | |
Element. The client MUST first try to verify the HMAC on the base64 encoded | |
contents with HMAC-SHA-256 using the authentication key. If this is | |
successful, the client can then attempt to decrypt the contents with | |
AES-256-CBC using the IV and encryption key. If either authentication or | |
decryption fails, the client MUST silently discard the OMEMO Message | |
Element. If both succeed, the decrypted contents are treated as the | |
<body> of the received message. | |
</p> | |
<p class="" style=""> | |
If the OMEMO element does not contain a <payload>, the client has | |
received a OMEMO Key Transport Element. The key material derived from the | |
matching element can then be used for other purposes (e.g. encrypted | |
file transfer). If the OMEMO Key Transport Element was received in an | |
otherwise empty <message>, i.e. this is an OMEMO Ratchet Update | |
Message, and therefore is of no use by itself, the client MUST ensure | |
that the ODR header is neverthelesss processed locally before the Omemo | |
element is silently discarded. This ensures that the ratchet is still | |
advanced (or, in the case of a init-omemo-envelope, the session | |
replaced entirely). | |
</p> | |
</div> | |
<div class="indent"><h3>5.8 <a name="uninstall" id="uninstall">Removing OMEMO support</a></h3> | |
<p class="" style=""> | |
If a device wishes to remove OMEMO support, e.g. because the user is | |
removing the account from the client, it MUST remove itself from the | |
devicelist and SHOULD remove its bundle. This will turn it into an | |
inactive device, which implicitly notifies all counterparts not to address | |
messages to it any more. | |
</p> | |
</div> | |
<h2>6. | |
<a name="rules" id="rules">Business Rules</a></h2> | |
<p class="" style=""> | |
Before publishing a freshly generated Device ID for the first time, a client | |
MUST check whether that Device ID already exists for this account by | |
consulting its own devicelist, and if so, generate a new one. | |
</p> | |
<p class="" style=""> | |
A client MUST ensure that its bundle information is correct at least every time | |
it logs in. To this end, it SHOULD simply re-publish the entire bundle, as it would | |
otherwise have to query the node and then also verify all the information contained | |
within. | |
</p> | |
<p class="" style=""> | |
The announcement step presents the risk of introducing a race condition: Two | |
devices might simultaneously try to announce themselves in the device list, | |
unaware of the other's existence. The second device would overwrite the | |
first one. To mitigate this, devices MUST check that their own device ID is | |
contained in the list whenever they receive a PEP update from their own | |
account. If they have been removed, they MUST reannounce themselves. | |
Furthermore, devices MAY also want to re-publish their own bundle at this | |
point in order to deal with cases where PEP was purged entirely (either | |
accidentally or maliciously). This part is OPTIONAL, however, as the | |
additional overhead in traffic caused by this mechanism might be undesirable | |
for constrained devices (e.g. mobile). | |
</p> | |
<p class="" style=""> | |
If the device is repeatedly removed from the devicelist, or its bundle is | |
repeatedly removed or corrupted, the client SHOULD notify the user of this fact. | |
It MAY also stop attempting to correct the PEP records in order to conserve | |
resources on constrained devices. | |
</p> | |
<p class="" style=""> | |
The size of the list of pre-keys in the bundle is a tradeoff between | |
preventing potential collisions and reducing overhead. If the list is very | |
small, multiple contacts have a higher chance of selecting the same pre-key | |
to initiate a session with this client (e.g. if they both build sessions | |
simultaneously, before the client has a chance to replace the used pre-keys, | |
or if the client is offline for a prolonged period of time). However, as the | |
bundle has to be re-published regularly, a large list of pre-keys would | |
increase the overhead, which might be disadvantageous for constrained | |
devices. The impact of pre-key collisions can further be reduced by | |
postponing removal of pre-key private keys during <span class="ref" style=""><a href="https://xmpp.org/extensions/xep-0313.html">Message Archive Management (XEP-0313)</a></span> [<a href="#nt-idm46032766431856">6</a>] catch-up phases | |
as described further down. It is therefore RECOMMENDED to include 20 | |
pre-keys in the list, but implementors should make their own determination | |
in light of the targeted use-case of their client. | |
</p> | |
<p class="" style=""> | |
Clients MUST ensure that their signed-pre-key is replaced regularly, e.g. by | |
publishing a new one once a week. It is RECOMMENDED to (additionally) | |
replace it every time a bundle is published, i.e. after log-ins and having | |
received init-omemo-envelopes. The reason for this is that the client has | |
to transmit its signed-pre-key anyway, so it might as well push a new one. | |
</p> | |
<p class="" style=""> | |
Clients SHOULD NOT immediately fetch the bundle and build a session as soon | |
as a new device is announced. Before the first message is exchanged, the | |
recipient does not know which pre-key has been used (or, in fact, that any | |
pre-key was used at all). As they have not had a chance to remove the used | |
pre-key from their bundle announcement, this could lead to collisions where | |
two (or more) different other devices pick the same pre-key to build a | |
session with a specific device. As each pre-key SHOULD only be used once, | |
the party that sends their initial init-omemo-envelope later loses this | |
race condition. This means that they think they have a valid session with | |
the contact, when in reality their messages MAY be ignored by the other | |
end. By postponing building sessions, the chance of such issues occurring | |
can be drastically reduced. It is RECOMMENDED to construct new sessions only | |
immediately before sending a message. | |
</p> | |
<p class="" style=""> | |
As there are no explicit error messages in this protocol, if a client does | |
receive a init-omemo-envelope using an invalid pre-key, they SHOULD | |
respond with an OMEMO Ratchet Update Message using a | |
init-omemo-envelope. By building a new session with the original sender | |
this way, the invalid session of the original sender will be overwritten on | |
their end with this newly created, valid session. | |
</p> | |
<p class="" style=""> | |
If a init-omemo-envelope is received as part of a <span class="ref" style=""><a href="https://xmpp.org/extensions/xep-0313.html">Message Archive Management (XEP-0313)</a></span> [<a href="#nt-idm46032766431856">6</a>] catch-up and | |
used to establish a new session with the sender, the client SHOULD postpone | |
deletion of the private key corresponding to the used pre-key until after | |
MAM catch-up is completed. After completing catch-up, the client MUST then | |
also send an OMEMO Ratchet Update Message using a init-omemo-envelope | |
before sending any payloads using this session, to trigger re-keying. (as | |
above) This practice can mitigate the previously mentioned race condition | |
by preventing message loss. | |
</p> | |
<p class="" style=""> | |
As the asynchronous nature of OMEMO enables currently offline devices to | |
decrypt received messages at a later time, clients MUST include a <span class="ref" style=""><a href="https://xmpp.org/extensions/xep-0334.html">Message Processing Hints (XEP-0334)</a></span> [<a href="#nt-idm46032766280016">9</a>] | |
<store /> hint in their OMEMO messages. Otherwise, server | |
implementations of <span class="ref" style=""><a href="https://xmpp.org/extensions/xep-0313.html">Message Archive Management (XEP-0313)</a></span> [<a href="#nt-idm46032766431856">6</a>] will generally not retain OMEMO messages, | |
since they do not contain a <body /> | |
</p> | |
<p class="" style=""> | |
When sending messages, clients SHOULD only add envelopes for active | |
devices. They MAY also include devices that have only "recently" (e.g. the | |
past 24 hours or the past week) been marked as inactive in order to give | |
those devices a chance to reannounce themselves and not lose any history in | |
the meantime. Clients SHOULD retain ODR sessions for inactive devices, but | |
they SHOULD eventually delete them after a certain period of inactivity | |
(e.g. a month). Additionally, clients MAY regard devices from which they | |
have not received any envelopes, including OMEMO Ratchet Update Messages | |
("heartbeats"), in a long time as inactive. | |
</p> | |
<p class="" style=""> | |
Clients MAY offer the functionality of removing other devices from the PEP | |
devicelist to the user. This could be used to clean up the devicelist | |
periodically, as all active devices would reannounce themselves, while stale | |
entries belonging to devices that are not in use anymore would not reappear. | |
However, clients MUST NOT interfere with other devices' bundles. | |
</p> | |
<h2>7. | |
<a name="impl" id="impl">Implementation Notes</a></h2> | |
<p class="" style=""> | |
The OMEMO Double Ratchet deliberately uses mostly identical parameters for | |
its <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/doubleratchet/doubleratchet.pdf">Double Ratchet</a></span> [<a href="#nt-idm46032766461408">3</a>] and <span class="ref" style=""><a href="https://whispersystems.org/docs/specifications/x3dh/x3dh.pdf">X3DH</a></span> [<a href="#nt-idm46032766452112">4</a>] as the Signal protocol does so that Signal | |
protocol implementations can be reused with only minor modifications. Most | |
notably, the HKDF info strings are different for ODR. Additionally, as ODR | |
has its own, XML-based wire format, the protobuf serialization of the Signal | |
protocol can be removed. Thus, implementors will most likely want to fork an | |
implementation of the Signal protocol if one exists for their platform and | |
make the necessary changes. Reusing well-known code should reduce the | |
amount of work needed to implement OMEMO drastically, while also bolstering | |
security. | |
</p> | |
<p class="" style=""> | |
The payload key derivation and encryption schemes employed by OMEMO are | |
identical to the ones employed by ODR, and therefore also identical to the | |
ones in the Signal protocol. Implementors that make use of existing Signal | |
protocol libraries SHOULD re-use the relevant library-internal code if at | |
all possible. In some cases this might necessitate some refactoring of the | |
code in order to expose an interface to these primitives, but it is worth | |
the effort, because it reduces the attack surface and limits the risk of | |
critical errors slipping in during re-implementation. | |
</p> | |
<p class="" style=""> | |
The trust handling that is carried out internally by the Signal protocol | |
reference implementations (and presumably ports thereof to various other | |
platforms) doesn't mesh very well with the multi-end paradigm of OMEMO. For | |
this reason it may be desirable to have the library consider all keys | |
trusted, effectively disabling its internal trust management. This in turn | |
of course makes it necessary to implement trust handling oneself. | |
</p> | |
<p class="" style=""> | |
If implementors using Signal protocol implementations for ODR choose to | |
implement the RECOMMENDED mechanism postponing deletion of pre-key private | |
keys until after <span class="ref" style=""><a href="https://xmpp.org/extensions/xep-0313.html">Message Archive Management (XEP-0313)</a></span> [<a href="#nt-idm46032766431856">6</a>] catch-up, they need to be aware of the fact that | |
these libraries generally take care of removing pre-key private keys | |
themselves after they were used. Therefore, implementors may wish to suspend | |
this mechanism in their implementation of the backing PreKeyStore, create a | |
copy of the PreKeyStore contents to draw from during catch-up, or devise | |
their own scheme to preseve the pre-key private keys during this phase. In any | |
case, implementors MUST ensure that the pre-key private keys are nonetheless | |
deleted after catch-up is completed. | |
</p> | |
<h2>8. | |
<a name="security" id="security">Security Considerations</a></h2> | |
<p class="" style=""> | |
Clients MUST NOT include themselves in the list of recipients for outgoing | |
OMEMO messages as this would result in an ODR session where only one of the | |
two chains would ever be used for sending, leading to a complete loss of | |
forward secrecy. | |
</p> | |
<p class="" style=""> | |
Clients MUST NOT use a newly built session to transmit data without user | |
consent. If a client were to opportunistically start using new sessions for | |
sending without the user's knowledge, an attacker could publish a fake | |
device for this user, which would then silently receive copies of all | |
messages sent by/to this user. It is RECOMMENDED to apply Blind Trust | |
Before Verification (BTBV) | |
[<a href="#nt-idm46032766254384">10</a>], but more conservative clients may instead e.g. choose to | |
prompt the user for a trust decision on every new key. | |
</p> | |
<p class="" style=""> | |
A client MAY use "not (yet) trusted" sessions for decryption of received | |
messages, but in that case it SHOULD indicate the untrusted nature of such | |
messages to the user. | |
</p> | |
<p class="" style=""> | |
When prompting the user for a trust decision regarding a key, the client | |
SHOULD present the user with a fingerprint in the form of a hex string, QR | |
code, or other unique representation, such that it can be compared by the | |
user. | |
</p> | |
<p class="" style=""> | |
While it is RECOMMENDED that clients postpone private key deletion until | |
after MAM catch-up and this standards mandates that clients MUST NOT use | |
duplicate-pre-key sessions for sending, clients MAY delete such keys | |
immediately for security reasons. For additional information on potential | |
security impacts of this decision, refer to | |
[<a href="#nt-idm46032766249680">11</a>]. | |
</p> | |
<p class="" style=""> | |
Clients SHOULD send OMEMO Ratchet Update Messages in regular intervals. These | |
act as "heartbeats", ensuring that the ratchet is advanced even during | |
prolonged periods of inactivity of the sending chain. | |
</p> | |
<p class="" style=""> | |
Conversely, if a client has not received any (valid) ODR envelopes in a | |
session, the client MAY suspend sending on this specific session until an | |
envelope that advances this ratchet is received. This way clients can enforce a | |
certain bare minimum of forward secrecy. This is however NOT RECOMMENDED for | |
typical use, as it will cause very noticeable service degradation. | |
</p> | |
<p class="" style=""> | |
In order to be able to handle out-of-order messages, the ODR stack has to | |
cache the keys belonging to "skipped" messages that have not been seen yet. | |
It is up to the implementor to decide how long and how many of such keys to | |
keep around. | |
</p> | |
<h2>9. | |
<a name="iana" id="iana">IANA Considerations</a></h2> | |
<p class="" style="">This document requires no interaction with the Internet Assigned Numbers Authority (IANA). </p> | |
<h2>10. | |
<a name="registrar" id="registrar">XMPP Registrar Considerations</a></h2> | |
<div class="indent"><h3>10.1 <a name="namespaces" id="namespaces">Protocol Namespaces</a></h3> | |
<p class="" style="">This specification defines the following XMPP namespaces:</p> | |
<ul class="" style=""> | |
<li class="" style="">urn:xmpp:omemo:1</li> | |
</ul> | |
<p class="" style="">The <span class="ref" style=""><a href="https://xmpp.org/registrar/">XMPP Registrar</a></span> [<a href="#nt-idm46032766235760">12</a>] shall include the foregoing namespace in its registry at <<a href="https://xmpp.org/registrar/namespaces.html">https://xmpp.org/registrar/namespaces.html</a>>, as goverened by <span class="ref" style=""><a href="https://xmpp.org/extensions/xep-0053.html">XMPP Registrar Function (XEP-0053)</a></span> [<a href="#nt-idm46032766231120">13</a>].</p> | |
</div> | |
<div class="indent"><h3>10.2 <a name="versioning" id="versioning">Protocol Versioning</a></h3> | |
<p class="" style="">If the protocol defined in this specification undergoes a revision that is not fully backwards-compatible with an older version, the XMPP Registrar shall increment the protocol version number found at the end of the XML namespaces defined herein, as described in Section 4 of <span class="ref">XEP-0053</span>.</p> | |
</div> | |
<h2>11. | |
<a name="schema" id="schema">XML Schema</a></h2> | |
<p class="caption"></p><div class="indent"><pre class="prettyprint"> | |
<xml version="1.0" encoding="utf8"> | |
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" | |
targetNamespace="urn:xmpp:omemo:1" | |
xmlns="urn:xmpp:omemo:1"> | |
<xs:element name="encrypted"> | |
<xs:complexType> | |
<xs:sequence> | |
<xs:element name="header"> | |
<xs:complexType> | |
<xs:sequence> | |
<xs:choice maxOccurs="unbounded"> | |
<xs:element name="envelope" type="xs:base64Binary"> | |
<xs:attribute name="rid" type="xs:integer" use="required"/> | |
<xs:attribute name="n" type="xs:integer" use="required"/> | |
<xs:attribute name="pn" type="xs:integer" use="required"/> | |
<xs:attribute name="dhs" type="xs:base64Binary" use="required"/> | |
</xs:element> | |
<xs:element name="init-envelope" type="xs:base64Binary"> | |
<xs:attribute name="rid" type="xs:integer" use="required"/> | |
<xs:attribute name="n" type="xs:integer" use="required"/> | |
<xs:attribute name="pn" type="xs:integer" use="required"/> | |
<xs:attribute name="dhs" type="xs:base64Binary" use="required"/> | |
<xs:attribute name="pkid" type="xs:integer" use="required"/> | |
<xs:attribute name="spkid" type="xs:integer" use="required"/> | |
<xs:attribute name="ek" type="xs:base64Binary" use="required"/> | |
<xs:attribute name="ik" type="xs:base64Binary" use="required"/> | |
</xs:element> | |
</xs:choice> | |
<xs:element name="iv" type="xs:base64Binary" maxOccurs="1"/> | |
</xs:sequence> | |
<xs:attribute name="sid" type="xs:integer"/> | |
</xs:complexType> | |
</xs:element> | |
<xs:element name="payload" type="xs:base64Binary" minOccurs="0"/> | |
</xs:sequence> | |
</xs:complexType> | |
</xs:element> | |
<xs:element name="list"> | |
<xs:complexType> | |
<xs:sequence> | |
<xs:element name="device" maxOccurs="unbounded"> | |
<xs:attribute name="id" type="integer" use="required"/> | |
</xs:element> | |
</xs:sequence> | |
</xs:complexType> | |
</xs:element> | |
<xs:element name="bundle"> | |
<xs:complexType> | |
<xs:sequence> | |
<xs:element name="signed-pre-key-public" type="base64Binary"> | |
<xs:attribute name="spkid" type="integer" use="required"/> | |
</xs:element> | |
<xs:element name="signed-pre-key-signature" type="base64Binary"/> | |
<xs:element name="identity-key" type="base64Binary"/> | |
<xs:element name="pre-keys"> | |
<xs:complexType> | |
<xs:sequence> | |
<xs:element name="pre-key-public" type="base64Binary" maxOccurs="unbounded"> | |
<xs:attribute name="pkid" type="integer" use="required"/> | |
</xs:element> | |
</xs:sequence> | |
</xs:complexType> | |
</xs:element> | |
</xs:sequence> | |
</xs:complexType> | |
</xs:element> | |
</xs:schema> | |
</pre></div> | |
<h2>12. | |
<a name="ack" id="ack">Acknowledgements</a></h2> | |
<p class="" style=""> | |
Big thanks to Daniel Gultsch for mentoring me during the development of this | |
protocol. Thanks to Thijs Alkemade and Cornelius Aschermann for talking | |
through some of the finer points of the protocol with me, and to Sebastian | |
Verschoor for auditing a previous version of the standard and giving valuable | |
recommendations towards improving it. Lastly I would also like to thank Sam | |
Whited, Holger Weiss, Florian Schmaus, René Calles, Germán Márquez Mejía, | |
Paul Schaub, and Richard Bayerle for their input on the standard. | |
</p> | |
<hr /><a name="appendices" id="appendices"></a><h2>Appendices</h2><hr /><a name="appendix-docinfo" id="appendix-docinfo"></a><h3>Appendix A: Document Information</h3><p class="indent"> | |
Series: <a href="http://xmpp.org/extensions/">XEP</a><br /> | |
Number: 0384<br /> | |
Publisher: <a href="/xsf/">XMPP Standards Foundation</a><br /> | |
Status: | |
<a href="http://xmpp.org/extensions/xep-0001.html#states-Experimental">Experimental</a><br /> | |
Type: | |
<a href="http://xmpp.org/extensions/xep-0001.html#types-Standards Track">Standards Track</a><br /> | |
Version: 0.2<br /> | |
Last Updated: 2017-03-25<br /> | |
Approving Body: <a href="http://xmpp.org/council/">XMPP Council</a><br />Dependencies: XMPP Core, XEP-0163<br /> | |
Supersedes: None<br /> | |
Superseded By: None<br /> | |
Short Name: OMEMO<br /> | |
Source Control: | |
<a class="standardsButton" href="https://github.com/xsf/xeps/blob/master/xep-0384.xml">HTML</a><br /> | |
This document in other formats: | |
<a class="standardsButton" href="http://xmpp.org/extensions/xep-0384.xml">XML</a> | |
<a class="standardsButton" href="http://xmpp.org/extensions/xep-0384.pdf">PDF</a></p><hr /><a name="appendix-authorinfo" id="appendix-authorinfo"></a><h3>Appendix B: Author Information</h3><div class="indent"><h3>Andreas Straub</h3><p class="indent"> | |
Email: | |
<a href="mailto:[email protected]">[email protected]</a><br /> | |
JabberID: | |
<a href="xmpp:[email protected]">[email protected]</a><br /></p></div><hr /><a name="appendix-legal" id="appendix-legal"></a><h3>Appendix C: Legal Notices</h3><div class="indent"><h4>Copyright</h4>This XMPP Extension Protocol is copyright © 1999 – 2017 by the <a href="https://xmpp.org/">XMPP Standards Foundation</a> (XSF).<h4>Permissions</h4>Permission is hereby granted, free of charge, to any person obtaining a copy of this specification (the "Specification"), to make use of the Specification without restriction, including without limitation the rights to implement the Specification in a software program, deploy the Specification in a network service, and copy, modify, merge, publish, translate, distribute, sublicense, or sell copies of the Specification, and to permit persons to whom the Specification is furnished to do so, subject to the condition that the foregoing copyright notice and this permission notice shall be included in all copies or substantial portions of the Specification. Unless separate permission is granted, modified works that are redistributed shall not contain misleading information regarding the authors, title, number, or publisher of the Specification, and shall not claim endorsement of the modified works by the authors, any organization or project to which the authors belong, or the XMPP Standards Foundation.<h4>Disclaimer of Warranty</h4><span style="font-weight: bold">## NOTE WELL: This Specification is provided on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. ##</span><h4>Limitation of Liability</h4>In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall the XMPP Standards Foundation or any author of this Specification be liable for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising from, out of, or in connection with the Specification or the implementation, deployment, or other use of the Specification (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if the XMPP Standards Foundation or such author has been advised of the possibility of such damages.<h4>IPR Conformance</h4>This XMPP Extension Protocol has been contributed in full conformance with the XSF's Intellectual Property Rights Policy (a copy of which can be found at <<a href="https://xmpp.org/about/xsf/ipr-policy">https://xmpp.org/about/xsf/ipr-policy</a>> or obtained by writing to XMPP Standards Foundation, P.O. Box 787, Parker, CO 80134 USA).</div><hr /><a name="appendix-xmpp" id="appendix-xmpp"></a><h3>Appendix D: Relation to XMPP</h3><p class="indent">The Extensible Messaging and Presence Protocol (XMPP) is defined in the XMPP Core (RFC 6120) and XMPP IM (RFC 6121) specifications contributed by the XMPP Standards Foundation to the Internet Standards Process, which is managed by the Internet Engineering Task Force in accordance with RFC 2026. Any protocol defined in this document has been developed outside the Internet Standards Process and is to be understood as an extension to XMPP rather than as an evolution, development, or modification of XMPP itself.</p><hr /><a name="appendix-discuss" id="appendix-discuss"></a><h3>Appendix E: Discussion Venue</h3><p class="indent">The primary venue for discussion of XMPP Extension Protocols is the <<a href="http://mail.jabber.org/mailman/listinfo/standards">[email protected]</a>> discussion list.</p><p class="indent">Discussion on other xmpp.org discussion lists might also be appropriate; see <<a href="http://xmpp.org/about/discuss.shtml">http://xmpp.org/about/discuss.shtml</a>> for a complete list.</p><p class="indent">Errata can be sent to <<a href="mailto:[email protected]">[email protected]</a>>.</p><hr /><a name="appendix-conformance" id="appendix-conformance"></a><h3>Appendix F: Requirements Conformance</h3><p class="indent">The following requirements keywords as used in this document are to be interpreted as described in <a href="http://www.ietf.org/rfc/rfc2119.txt">RFC 2119</a>: "MUST", "SHALL", "REQUIRED"; "MUST NOT", "SHALL NOT"; "SHOULD", "RECOMMENDED"; "SHOULD NOT", "NOT RECOMMENDED"; "MAY", "OPTIONAL".</p><hr /><a name="appendix-notes" id="appendix-notes"></a><h3>Appendix G: Notes</h3><div class="indent"><p><a name="nt-idm46032767451904" id="nt-idm46032767451904">1</a>. XEP-0364: Current Off-the-Record Messaging Usage <<a href="https://xmpp.org/extensions/xep-0364.html">https://xmpp.org/extensions/xep-0364.html</a>>.</p><p><a name="nt-idm46032766467360" id="nt-idm46032766467360">2</a>. XEP-0027: Current Jabber OpenPGP Usage <<a href="https://xmpp.org/extensions/xep-0027.html">https://xmpp.org/extensions/xep-0027.html</a>>.</p><p><a name="nt-idm46032766461408" id="nt-idm46032766461408">3</a>. The Double Ratchet Algorithm <<a href="https://whispersystems.org/docs/specifications/doubleratchet/doubleratchet.pdf">https://whispersystems.org/docs/specifications/doubleratchet/doubleratchet.pdf</a>>.</p><p><a name="nt-idm46032766452112" id="nt-idm46032766452112">4</a>. The X3DH Key Agreement Protocol <<a href="https://whispersystems.org/docs/specifications/x3dh/x3dh.pdf">https://whispersystems.org/docs/specifications/x3dh/x3dh.pdf</a>>.</p><p><a name="nt-idm46032766437136" id="nt-idm46032766437136">5</a>. XEP-0280: Message Carbons <<a href="https://xmpp.org/extensions/xep-0280.html">https://xmpp.org/extensions/xep-0280.html</a>>.</p><p><a name="nt-idm46032766431856" id="nt-idm46032766431856">6</a>. XEP-0313: Message Archive Management <<a href="https://xmpp.org/extensions/xep-0313.html">https://xmpp.org/extensions/xep-0313.html</a>>.</p><p><a name="nt-idm46032766425856" id="nt-idm46032766425856">7</a>. XEP-0163: Personal Eventing Protocol <<a href="https://xmpp.org/extensions/xep-0163.html">https://xmpp.org/extensions/xep-0163.html</a>>.</p><p><a name="nt-idm46032766368000" id="nt-idm46032766368000">8</a>. Curve25519: new Diffie-Hellman speed records <<a href="http://cr.yp.to/ecdh/curve25519-20060209.pdf">http://cr.yp.to/ecdh/curve25519-20060209.pdf</a>>.</p><p><a name="nt-idm46032766280016" id="nt-idm46032766280016">9</a>. XEP-0334: Message Processing Hints <<a href="https://xmpp.org/extensions/xep-0334.html">https://xmpp.org/extensions/xep-0334.html</a>>.</p><p><a name="nt-idm46032766254384" id="nt-idm46032766254384">10</a>. Blind Trust Before Verification | |
<<a href="https://gultsch.de/trust.html">https://gultsch.de/trust.html</a>> | |
</p><p><a name="nt-idm46032766249680" id="nt-idm46032766249680">11</a>. Menezes, Alfred, and Berkant Ustaoglu. "On reusing ephemeral | |
keys in Diffie-Hellman key agreement protocols." International Journal | |
of Applied Cryptography 2, no. 2 (2010): 154-158. </p><p><a name="nt-idm46032766235760" id="nt-idm46032766235760">12</a>. The XMPP Registrar maintains a list of reserved protocol namespaces as well as registries of parameters used in the context of XMPP extension protocols approved by the XMPP Standards Foundation. For further information, see <<a href="https://xmpp.org/registrar/">https://xmpp.org/registrar/</a>>.</p><p><a name="nt-idm46032766231120" id="nt-idm46032766231120">13</a>. XEP-0053: XMPP Registrar Function <<a href="https://xmpp.org/extensions/xep-0053.html">https://xmpp.org/extensions/xep-0053.html</a>>.</p></div><hr /><a name="appendix-revs" id="appendix-revs"></a><h3>Appendix H: Revision History</h3><p>Note: Older versions of this specification might be available at <a href="http://xmpp.org/extensions/attic/">http://xmpp.org/extensions/attic/</a></p><div class="indent"><h4>Version 0.2 (2017-03-25)</h4><div class="indent"> | |
<p class="" style="">Use Double Ratchet and X3DH</p> | |
<p class="" style="">Describe use-cases more precisely</p> | |
<p class="" style="">Add clarifying business rules</p> | |
<p class="" style="">Use kebab-case throughout</p> | |
(as) | |
</div><h4>Version 0.1 (2016-12-07)</h4><div class="indent"><p class="" style="">Initial version approved by the council.</p> (XEP Editor: ssw) | |
</div><h4>Version 0.0.2 (2016-09-22)</h4><div class="indent"><p class="" style="">Depend on Olm instead of Axolotl.</p> (ssw, dg) | |
</div><h4>Version 0.0.1 (2015-10-25)</h4><div class="indent"><p class="" style="">First draft.</p> (as) | |
</div></div><hr /><p>END</p></body></html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
BODY { | |
background-color: #ffffff; | |
color: #000000; | |
font-family: Verdana, Arial, Helvetica, Geneva, sans-serif; | |
font-size: small; | |
line-height: 130%; | |
margin-left: 7%; | |
margin-right: 7%; | |
} | |
#left { | |
border: 0px; | |
border-color: #aaaaaa; | |
border-right: 1px; | |
border-style: solid; | |
float: left; | |
margin: 0% 0% 5% 0%; | |
padding: 4px 2% 10px 2%; | |
width: 12%; | |
} | |
#main { | |
border: 0px; | |
color: #000000; | |
font-weight: normal; | |
margin: 0% 0% 5% 17%; | |
padding: 4px 2% 10px 2%; | |
text-decoration: none; | |
} | |
#foot { | |
border-top: 0px solid; | |
clear: both; | |
color: #666666; | |
font-size: x-small; | |
font-weight: normal; | |
text-align: center; | |
text-decoration: none; | |
} | |
A:link { | |
color: #336699; | |
} | |
A.standardsButton { | |
background-color: #ff6600; | |
border: 1px solid; | |
border-color: #ffc8a4 #7d3302 #3f1a01 #ff9a57; | |
color: #ffffff; | |
padding: 0px 3px 0px 3px; | |
text-decoration: none; | |
margin: 0px; | |
} | |
A:visited { | |
color: #663399; | |
} | |
H1, H2, H3, H4, H5, H6 { | |
color: #336699; | |
line-height: 0.8; | |
} | |
pre { | |
font-family: Courier, monospace; | |
background-color: #e0e9f2; | |
padding: 0.4em; | |
border: 1px solid #369; | |
} | |
ul { | |
list-style: disc outside; | |
} | |
.body { | |
font-family: Verdana, Arial, Helvetica, Geneva, sans-serif; | |
font-size: small; | |
} | |
.bold { | |
font-weight: bold; | |
} | |
.box { | |
border: thin dotted; | |
padding-bottom: 1em; | |
padding-left: 2em; | |
padding-right: 2em; | |
padding-top: 1em; | |
} | |
.caption { | |
font-weight: bold; | |
} | |
.code { | |
font-family: "Courier New", Courier, monospace; | |
white-space: pre; | |
} | |
.def { | |
text-indent: -6.3em; | |
padding-bottom: 1em; | |
padding-left: 6.5em; | |
padding-right: 10em; | |
padding-top: 1em; | |
} | |
.em { | |
font-style: italic; | |
} | |
.example { | |
background-color: #f2ee6e; | |
margin: 0.4em 5%; | |
border: 1px solid #999633; | |
padding: 0 0.4em; | |
} | |
.indent { | |
padding-left: 5%; | |
padding-right: 5%; | |
} | |
.head { | |
color: #336699; | |
font-weight: bold; | |
} | |
.highlight { | |
color: #336699; | |
} | |
.nav { | |
font-size: small; | |
line-height: 45%; | |
text-decoration: none; | |
white-space: nowrap; | |
} | |
.navhead { | |
color: #336699; | |
font-size: medium; | |
line-height: 90%; | |
padding-left: 0px; | |
text-decoration: none; | |
} | |
.pagehead { | |
color: #336699; | |
text-decoration: none; | |
} | |
A:visited.pagehead { | |
color: #336699; | |
text-decoration: none; | |
} | |
.ref { | |
font-weight: bold; | |
} | |
.strong { | |
font-weight: bold; | |
} | |
.subhead { | |
font-style: italic; | |
} | |
.sub { | |
font-size: xx-small; | |
vertical-align: sub; | |
} | |
.super { | |
font-size: xx-small; | |
vertical-align: super; | |
} | |
.tablebody { | |
font-family: Verdana, Arial, Helvetica, Geneva, sans-serif; | |
font-size: small; | |
white-space: nowrap; | |
} | |
.event { | |
color: #336699; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment