From e7c72dcbbf412d98c1e580b690bb78ecd88e54cc Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 12 Sep 2020 01:59:53 +0300 Subject: [PATCH] Extremely WIP cross-signing and SSSS support --- go.mod | 3 + go.sum | 50 +++--- interface/matrix.go | 1 + matrix/matrix.go | 4 +- matrix/uia-fallback.go | 114 +++++++++++++ ui/command-processor.go | 5 + ui/crypto-commands.go | 346 +++++++++++++++++++++++++++++++++++++++- ui/password-modal.go | 30 ++-- 8 files changed, 520 insertions(+), 33 deletions(-) create mode 100644 matrix/uia-fallback.go diff --git a/go.mod b/go.mod index 3048822..b8fde17 100644 --- a/go.mod +++ b/go.mod @@ -27,3 +27,6 @@ require ( maunium.net/go/mauview v0.1.1 maunium.net/go/tcell v0.2.0 ) + +//replace maunium.net/go/mautrix => ../mautrix-go +replace maunium.net/go/mautrix => github.com/nikofil/mautrix-go v0.5.2-0.20200911223256-b40ab761fadc diff --git a/go.sum b/go.sum index 4572c2b..948bff6 100644 --- a/go.sum +++ b/go.sum @@ -1,28 +1,42 @@ github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= -github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= github.com/alecthomas/chroma v0.8.0 h1:HS+HE97sgcqjQGu5uVr8jIE55Mmh5UeQ7kckAhHg2pY= github.com/alecthomas/chroma v0.8.0/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM= -github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE= -github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY= github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= +github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gabriel-vasile/mimetype v1.1.1 h1:qbN9MPuRf3bstHu9zkI9jDWNfH//9+9kHxr9oRBBBOA= github.com/gabriel-vasile/mimetype v1.1.1/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kyokomi/emoji v2.2.2+incompatible h1:gaQFbK2+uSxOR4iGZprJAbpmtqTrHhSdgOyIMD6Oidc= github.com/kyokomi/emoji v2.2.2+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA= github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= @@ -32,20 +46,21 @@ github.com/lithammer/fuzzysearch v1.1.0/go.mod h1:Bqx4wo8lTOFcJr3ckpY6HA9lEIOO0H github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= -github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= +github.com/nikofil/mautrix-go v0.5.2-0.20200911223256-b40ab761fadc h1:Un166sriSTE07vajPm+iv+oHmBIH3b4PW2LO1fhRyKc= +github.com/nikofil/mautrix-go v0.5.2-0.20200911223256-b40ab761fadc/go.mod h1:xd0D0ekVts/UDBbjeDSs4wGlBfcarJDg0MMhVgHbxhs= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -53,15 +68,12 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sasha-s/go-deadlock v0.2.0 h1:lMqc+fUb7RrFS3gQLtoQsJ7/6TV/pAIFvBsqX73DK8Y= github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10= -github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc= github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= @@ -74,25 +86,27 @@ github.com/tidwall/sjson v1.1.1 h1:7h1vk049Jnd5EH9NyzNiEuwYW4b5qgreBbqRC19AS3U= github.com/tidwall/sjson v1.1.1/go.mod h1:yvVuSnpEQv5cYIrO+AT6kw4QVfd5SDZoGIS7/5+fZFs= github.com/zyedidia/clipboard v0.0.0-20200421031010-7c45b8673834 h1:0nOfq3JwYRiY3+nwfWVQYEaXDmGCQgj3RKoqTifLzP4= github.com/zyedidia/clipboard v0.0.0-20200421031010-7c45b8673834/go.mod h1:zykFnZUXX0ErxqvYLUFEq7QDJKId8rmh2FgD0/Y8cjA= -github.com/zyedidia/poller v1.0.1 h1:Tt9S3AxAjXwWGNiC2TUdRJkQDZSzCBNVQ4xXiQ7440s= github.com/zyedidia/poller v1.0.1/go.mod h1:vZXJOHGDcuK08GXhF6IAY0ZFd2WcgOR5DOTp84Uk5eE= go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw= golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -106,19 +120,17 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2 h1:MZF6J7CV6s/h0HBkfqebrYfKCVEo5iN+wzE4QhV3Evo= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2/go.mod h1:s1Sn2yZos05Qfs7NKt867Xe18emOmtsO3eAKbDaon0o= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/vansante/go-ffprobe.v2 v2.0.2 h1:DdxSfFnlqeawPIVbIQEI6LR6OQHQNR7tNgWb2mWuC4w= gopkg.in/vansante/go-ffprobe.v2 v2.0.2/go.mod h1:qF0AlAjk7Nqzqf3y333Ly+KxN3cKF2JqA3JT5ZheUGE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= maunium.net/go/maulogger/v2 v2.1.1/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A= -maunium.net/go/mautrix v0.7.6 h1:jB9oCimPq0mVyolwQBC/9N1fu21AU+Ryq837cLf4gOo= -maunium.net/go/mautrix v0.7.6/go.mod h1:Va/74MijqaS0DQ3aUqxmFO54/PMfr1LVsCOcGRHbYmo= maunium.net/go/mauview v0.1.1 h1:wfTXyPx3LGAGpTskh+UbBv/QItUWnEpaneHmywoYnfY= maunium.net/go/mauview v0.1.1/go.mod h1:3QBUiuLct9moP1LgDhCGIg0Ovxn38Bd2sGndnUOuj4o= maunium.net/go/tcell v0.2.0 h1:1Q0kN3wCOGAIGu1r3QHADsjSUOPDylKREvCv3EzJpVg= diff --git a/interface/matrix.go b/interface/matrix.go index 870b80d..b2ae4a4 100644 --- a/interface/matrix.go +++ b/interface/matrix.go @@ -51,6 +51,7 @@ type MatrixContainer interface { Login(user, password string) error Logout() + UIAFallback(authType mautrix.AuthType, sessionID string) error SendPreferencesToMatrix() PrepareMarkdownMessage(roomID id.RoomID, msgtype event.MessageType, text, html string, relation *Relation) *muksevt.Event diff --git a/matrix/matrix.go b/matrix/matrix.go index d54ad82..c1a93cd 100644 --- a/matrix/matrix.go +++ b/matrix/matrix.go @@ -429,8 +429,8 @@ func (c *Container) Start() { return default: if err := c.client.Sync(); err != nil { - if httpErr, ok := err.(mautrix.HTTPError); ok && httpErr.Code == http.StatusUnauthorized { - debug.Print("Sync() errored with ", err, " -> logging out") + if errors.Is(err, mautrix.MUnknownToken) { + debug.Print("Sync() errored with", err, "-> logging out") c.Logout() } else { debug.Print("Sync() errored", err) diff --git a/matrix/uia-fallback.go b/matrix/uia-fallback.go new file mode 100644 index 0000000..1501ad9 --- /dev/null +++ b/matrix/uia-fallback.go @@ -0,0 +1,114 @@ +// gomuks - A terminal Matrix client written in Go. +// Copyright (C) 2020 Tulir Asokan +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package matrix + +import ( + "context" + "errors" + "net/http" + "net/url" + "time" + + "maunium.net/go/mautrix" + + "maunium.net/go/gomuks/debug" + "maunium.net/go/gomuks/lib/open" +) + +const uiaFallbackPage = ` + + + gomuks user-interactive auth + + + + +

Please complete the login in the popup window

+ + + + + +` + +func (c *Container) UIAFallback(loginType mautrix.AuthType, sessionID string) error { + errChan := make(chan error, 1) + server := &http.Server{Addr: ":29325"} + server.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + w.Header().Add("Content-Type", "text/html") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(uiaFallbackPage)) + } else if r.Method == "POST" || r.Method == "DELETE" { + w.Header().Add("Content-Type", "text/html") + w.WriteHeader(http.StatusOK) + + go func() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + err := server.Shutdown(ctx) + if err != nil { + debug.Printf("Failed to shut down SSO server: %v\n", err) + } + if r.Method == "DELETE" { + errChan <- errors.New("login cancelled") + } else { + errChan <- nil + } + }() + } else { + w.WriteHeader(http.StatusMethodNotAllowed) + } + }) + go server.ListenAndServe() + defer server.Close() + authURL := c.client.BuildURLWithQuery(mautrix.URLPath{"auth", loginType, "fallback", "web"}, map[string]string{ + "session": sessionID, + }) + link := url.URL{ + Scheme: "http", + Host: "localhost:29325", + Path: "/", + Fragment: authURL, + } + err := open.Open(link.String()) + if err != nil { + return err + } + err = <-errChan + return err +} diff --git a/ui/command-processor.go b/ui/command-processor.go index fcec03e..da8388a 100644 --- a/ui/command-processor.go +++ b/ui/command-processor.go @@ -104,6 +104,9 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor { "e": {"edit"}, "dl": {"download"}, "o": {"open"}, + "4s": {"ssss"}, + "s4": {"ssss"}, + "cs": {"cross-signing"}, }, autocompleters: map[string]CommandAutocompleter{ "devices": autocompleteDevice, @@ -172,6 +175,8 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor { "import": cmdImportKeys, "export": cmdExportKeys, "export-room": cmdExportRoomKeys, + "ssss": cmdSSSS, + "cross-signing": cmdCrossSigning, }, } } diff --git a/ui/crypto-commands.go b/ui/crypto-commands.go index 6c8896f..04be052 100644 --- a/ui/crypto-commands.go +++ b/ui/crypto-commands.go @@ -19,6 +19,7 @@ package ui import ( + "errors" "fmt" "io/ioutil" "path/filepath" @@ -26,7 +27,10 @@ import ( "time" "unicode" + ifc "maunium.net/go/gomuks/interface" + "maunium.net/go/mautrix" "maunium.net/go/mautrix/crypto" + "maunium.net/go/mautrix/crypto/ssss" "maunium.net/go/mautrix/id" ) @@ -243,7 +247,7 @@ func cmdImportKeys(cmd *Command) { cmd.Reply("Failed to read %s: %v", path, err) return } - passphrase, ok := cmd.MainView.AskPassword("Key import", false) + passphrase, ok := cmd.MainView.AskPassword("Key import", "passphrase", "", false) if !ok { cmd.Reply("Passphrase entry cancelled") return @@ -263,7 +267,7 @@ func exportKeys(cmd *Command, sessions []*crypto.InboundGroupSession) { cmd.Reply("Failed to get absolute path: %v", err) return } - passphrase, ok := cmd.MainView.AskPassword("Key export", true) + passphrase, ok := cmd.MainView.AskPassword("Key export", "passphrase", "", true) if !ok { cmd.Reply("Passphrase entry cancelled") return @@ -299,3 +303,341 @@ func cmdExportRoomKeys(cmd *Command) { } exportKeys(cmd, sessions) } + +const ssssHelp = `Usage: /%s [...] + +Subcommands: +* status [key ID] - Check the status of your SSSS. +* generate [--set-default] - Generate a SSSS key and optionally set it as the default. +* set-default - Set a SSSS key as the default.` + +func cmdSSSS(cmd *Command) { + if len(cmd.Args) == 0 { + cmd.Reply(ssssHelp, cmd.OrigCommand) + return + } + + mach := cmd.Matrix.Crypto().(*crypto.OlmMachine) + + switch strings.ToLower(cmd.Args[0]) { + case "status": + keyID := "" + if len(cmd.Args) > 1 { + keyID = cmd.Args[1] + } + cmdS4Status(cmd, mach, keyID) + case "generate": + setDefault := len(cmd.Args) > 1 && strings.ToLower(cmd.Args[1]) == "--set-default" + cmdS4Generate(cmd, mach, setDefault) + case "set-default": + if len(cmd.Args) < 2 { + cmd.Reply("Usage: /%s set-default ", cmd.OrigCommand) + return + } + cmdS4SetDefault(cmd, mach, cmd.Args[1]) + default: + cmd.Reply(ssssHelp, cmd.OrigCommand) + } +} + +func cmdS4Status(cmd *Command, mach *crypto.OlmMachine, keyID string) { + var keyData *ssss.KeyMetadata + var err error + if len(keyID) == 0 { + keyID, keyData, err = mach.SSSS.GetDefaultKeyData() + } else { + keyData, err = mach.SSSS.GetKeyData(keyID) + } + if errors.Is(err, ssss.ErrNoDefaultKeyAccountDataEvent) { + cmd.Reply("SSSS is not set up: no default key set") + } else if err != nil { + cmd.Reply("Failed to get key data: %v", err) + return + } + hasPassphrase := "no" + if keyData.Passphrase != nil { + hasPassphrase = fmt.Sprintf("yes (alg=%s,bits=%d,iter=%d)", keyData.Passphrase.Algorithm, keyData.Passphrase.Bits, keyData.Passphrase.Iterations) + } + algorithm := keyData.Algorithm + if algorithm != ssss.AlgorithmAESHMACSHA2 { + algorithm += " (not supported!)" + } + cmd.Reply("Default key is set.\n Key ID: %s\n Has passphrase: %s\n Algorithm: %s", keyID, hasPassphrase, algorithm) +} + +func cmdS4Generate(cmd *Command, mach *crypto.OlmMachine, setDefault bool) { + passphrase, ok := cmd.MainView.AskPassword("Passphrase", "", "", false) + if !ok { + return + } + + key, err := ssss.NewKey(passphrase) + if err != nil { + cmd.Reply("Failed to generate new key: %v", err) + return + } + + err = mach.SSSS.SetKeyData(key.ID, key.Metadata) + if err != nil { + cmd.Reply("Failed to upload key metadata: %v", err) + return + } + + // TODO if we start persisting command replies, the recovery key needs to be moved into a popup + cmd.Reply("Successfully generated key %s\nRecovery key: %s", key.ID, key.RecoveryKey()) + + if setDefault { + err = mach.SSSS.SetDefaultKeyID(key.ID) + if err != nil { + cmd.Reply("Failed to set key as default: %v", err) + } + } else { + cmd.Reply("You can use `/%s set-default %s` to set it as the default", cmd.OrigCommand, key.ID) + } +} + +func cmdS4SetDefault(cmd *Command, mach *crypto.OlmMachine, keyID string) { + _, err := mach.SSSS.GetKeyData(keyID) + if err != nil { + if errors.Is(err, mautrix.MNotFound) { + cmd.Reply("Couldn't find key data on server") + } else { + cmd.Reply("Failed to fetch key data: %v", err) + } + return + } + + err = mach.SSSS.SetDefaultKeyID(keyID) + if err != nil { + cmd.Reply("Failed to set key as default: %v", err) + } else { + cmd.Reply("Successfully set key %s as default", keyID) + } +} + +const crossSigningHelp = `Usage: /%s [...] + +Subcommands: +* status + Check the status of your own cross-signing keys. +* generate [--force] + Generate and upload new cross-signing keys. + This will prompt you to enter your account password. + If you already have existing keys, --force is required. +* fetch [--save-to-disk] + Fetch your cross-signing keys from SSSS and decrypt them. + If --save-to-disk is specified, the keys are saved to disk. +* upload + Upload your cross-signing keys to SSSS.` + +func cmdCrossSigning(cmd *Command) { + if len(cmd.Args) == 0 { + cmd.Reply(crossSigningHelp, cmd.OrigCommand) + return + } + + client := cmd.Matrix.Client() + mach := cmd.Matrix.Crypto().(*crypto.OlmMachine) + + switch strings.ToLower(cmd.Args[0]) { + case "status": + cmdCrossSigningStatus(cmd, mach, client) + case "generate": + force := len(cmd.Args) > 1 && strings.ToLower(cmd.Args[1]) == "--force" + cmdCrossSigningGenerate(cmd, cmd.Matrix, mach, client, force) + case "fetch": + saveToDisk := len(cmd.Args) > 1 && strings.ToLower(cmd.Args[1]) == "--save-to-disk" + cmdCrossSigningFetch(cmd, mach, saveToDisk) + case "upload": + cmdCrossSigningUpload(cmd, mach) + default: + cmd.Reply(crossSigningHelp, cmd.OrigCommand) + } +} + +func parseKeyResp(keys *mautrix.RespQueryKeys, userID id.UserID) (id.Ed25519, id.Ed25519, id.Ed25519, bool) { + masterKeys, ok := keys.MasterKeys[userID] + if !ok { + return "", "", "", false + } + selfSigningKeys, ok := keys.SelfSigningKeys[userID] + if !ok { + return "", "", "", false + } + userSigningKeys, ok := keys.UserSigningKeys[userID] + if !ok { + return masterKeys.FirstKey(), selfSigningKeys.FirstKey(), "", true + } + return masterKeys.FirstKey(), userSigningKeys.FirstKey(), selfSigningKeys.FirstKey(), true +} + +func cmdCrossSigningStatus(cmd *Command, mach *crypto.OlmMachine, client *mautrix.Client) { + if mach.CrossSigningKeys != nil { + cmd.Reply("Cross-signing is set up and private keys are cached") + cmd.Reply("Master key: %s", mach.CrossSigningKeys.MasterKey.PublicKey) + cmd.Reply("User signing key: %s", mach.CrossSigningKeys.UserSigningKey.PublicKey) + cmd.Reply("Self-signing key: %s", mach.CrossSigningKeys.SelfSigningKey.PublicKey) + return + } + keys, err := client.QueryKeys(&mautrix.ReqQueryKeys{ + DeviceKeys: mautrix.DeviceKeysRequest{ + client.UserID: mautrix.DeviceIDList{client.DeviceID}, + }, + }) + if err != nil { + cmd.Reply("Failed to query own keys: %v", err) + return + } + masterKey, selfSigningKey, userSigningKey, ok := parseKeyResp(keys, client.UserID) + if !ok { + cmd.Reply("Didn't find published cross-signing keys") + return + } + cmd.Reply("Cross-signing is set up, but private keys are not cached") + cmd.Reply("Master key: %s", masterKey) + cmd.Reply("User signing key: %s", userSigningKey) + cmd.Reply("Self-signing key: %s", selfSigningKey) +} + +func cmdCrossSigningFetch(cmd *Command, mach *crypto.OlmMachine, saveToDisk bool) { + key := getSSSS(cmd, mach) + if key == nil { + return + } + + err := mach.FetchCrossSigningKeysFromSSSS(key) + if err != nil { + cmd.Reply("Error fetching cross-signing keys: %v", err) + return + } + if saveToDisk { + cmd.Reply("Saving keys to disk is not yet implemented") + } + cmd.Reply("Successfully unlocked cross-signing keys") +} + +func cmdCrossSigningGenerate(cmd *Command, container ifc.MatrixContainer, mach *crypto.OlmMachine, client *mautrix.Client, force bool) { + if !force { + keys, err := client.QueryKeys(&mautrix.ReqQueryKeys{ + DeviceKeys: mautrix.DeviceKeysRequest{ + client.UserID: mautrix.DeviceIDList{client.DeviceID}, + }, + }) + if err == nil { + _, _, _, ok := parseKeyResp(keys, client.UserID) + if ok { + cmd.Reply("Found existing cross-signing keys. Use `--force` if you want to overwrite them.") + return + } + } + } + + keys, err := mach.GenerateCrossSigningKeys() + if err != nil { + cmd.Reply("Failed to generate cross-signing keys: %v", err) + return + } + + err = mach.PublishCrossSigningKeys(keys, func(uia *mautrix.RespUserInteractive) interface{} { + if !uia.HasSingleStageFlow(mautrix.AuthTypePassword) { + for _, flow := range uia.Flows { + if len(flow.Stages) != 1 { + return nil + } + cmd.Reply("Opening browser for authentication") + err := container.UIAFallback(flow.Stages[0], uia.Session) + if err != nil { + cmd.Reply("Authentication failed: %v", err) + return nil + } + return &mautrix.BaseAuthData{ + Type: flow.Stages[0], + Session: uia.Session, + } + } + cmd.Reply("No supported authentication mechanisms found") + return nil + } + password, ok := cmd.MainView.AskPassword("Account password", "", "correct horse battery staple", false) + if !ok { + return nil + } + return &mautrix.ReqUIAuthLogin{ + BaseAuthData: mautrix.BaseAuthData{ + Type: mautrix.AuthTypePassword, + Session: uia.Session, + }, + User: mach.Client.UserID.String(), + Password: password, + } + }) + if err != nil { + cmd.Reply("Failed to publish cross-signing keys: %v", err) + return + } + + mach.CrossSigningKeys = keys +} + +func getSSSS(cmd *Command, mach *crypto.OlmMachine) *ssss.Key { + _, keyData, err := mach.SSSS.GetDefaultKeyData() + if err != nil { + if errors.Is(err, mautrix.MNotFound) { + cmd.Reply("SSSS not set up, use `!ssss generate --set-default` first") + } else { + cmd.Reply("Failed to fetch default SSSS key data: %v", err) + } + return nil + } + + var key *ssss.Key + if keyData.Passphrase != nil && keyData.Passphrase.Algorithm == ssss.PassphraseAlgorithmPBKDF2 { + passphrase, ok := cmd.MainView.AskPassword("Passphrase", "", "correct horse battery staple", false) + if !ok { + return nil + } + key, err = keyData.VerifyPassphrase(passphrase) + if errors.Is(err, ssss.ErrIncorrectSSSSKey) { + cmd.Reply("Incorrect passphrase") + return nil + } + } else { + recoveryKey, ok := cmd.MainView.AskPassword("Recovery key", "", "tDAK LMRH PiYE bdzi maCe xLX5 wV6P Nmfd c5mC wLef 15Fs VVSc", false) + if !ok { + return nil + } + key, err = keyData.VerifyRecoveryKey(recoveryKey) + if errors.Is(err, ssss.ErrInvalidRecoveryKey) { + cmd.Reply("Malformed recovery key") + return nil + } else if errors.Is(err, ssss.ErrIncorrectSSSSKey) { + cmd.Reply("Incorrect recovery key") + return nil + } + } + // All the errors should already be handled above, this is just for backup + if err != nil { + cmd.Reply("Failed to get SSSS key: %v", err) + return nil + } + return key +} + +func cmdCrossSigningUpload(cmd *Command, mach *crypto.OlmMachine) { + if mach.CrossSigningKeys == nil { + cmd.Reply("Cross-signing keys not cached, use `!%s generate` first", cmd.OrigCommand) + return + } + + key := getSSSS(cmd, mach) + if key == nil { + return + } + + err := mach.UploadCrossSigningKeysToSSSS(key, mach.CrossSigningKeys) + if err != nil { + cmd.Reply("Failed to upload keys to SSSS: %v", err) + } else { + cmd.Reply("Successfully uploaded cross-signing keys to SSSS") + } +} diff --git a/ui/password-modal.go b/ui/password-modal.go index afda7b5..4a9f25f 100644 --- a/ui/password-modal.go +++ b/ui/password-modal.go @@ -17,6 +17,9 @@ package ui import ( + "fmt" + "strings" + "maunium.net/go/mauview" "maunium.net/go/tcell" ) @@ -41,13 +44,20 @@ type PasswordModal struct { parent *MainView } -func (view *MainView) AskPassword(title string, isNew bool) (string, bool) { - pwm := NewPasswordModal(view, title, isNew) +func (view *MainView) AskPassword(title, thing, placeholder string, isNew bool) (string, bool) { + pwm := NewPasswordModal(view, title, thing, placeholder, isNew) view.ShowModal(pwm) + view.parent.Render() return pwm.Wait() } -func NewPasswordModal(parent *MainView, title string, isNew bool) *PasswordModal { +func NewPasswordModal(parent *MainView, title, thing, placeholder string, isNew bool) *PasswordModal { + if placeholder == "" { + placeholder = "correct horse battery staple" + } + if thing == "" { + thing = strings.ToLower(title) + } pwm := &PasswordModal{ parent: parent, form: mauview.NewForm(), @@ -64,13 +74,13 @@ func NewPasswordModal(parent *MainView, title string, isNew bool) *PasswordModal pwm.text = mauview.NewTextField() if isNew { - pwm.text.SetText("Create a passphrase") + pwm.text.SetText(fmt.Sprintf("Create a %s", thing)) } else { - pwm.text.SetText("Enter the passphrase") + pwm.text.SetText(fmt.Sprintf("Enter the %s", thing)) } pwm.input = mauview.NewInputField(). SetMaskCharacter('*'). - SetPlaceholder("correct horse battery staple") + SetPlaceholder(placeholder) pwm.form.AddComponent(pwm.text, 1, 1, 3, 1) pwm.form.AddFormItem(pwm.input, 1, 2, 3, 1) @@ -78,10 +88,10 @@ func NewPasswordModal(parent *MainView, title string, isNew bool) *PasswordModal height += 3 pwm.confirmInput = mauview.NewInputField(). SetMaskCharacter('*'). - SetPlaceholder("correct horse battery staple"). + SetPlaceholder(placeholder). SetChangedFunc(pwm.HandleChange) pwm.input.SetChangedFunc(pwm.HandleChange) - pwm.confirmText = mauview.NewTextField().SetText("Confirm passphrase") + pwm.confirmText = mauview.NewTextField().SetText(fmt.Sprintf("Confirm %s", thing)) pwm.form.SetRow(3, 1).SetRow(4, 1).SetRow(5, 1) pwm.form.AddComponent(pwm.confirmText, 1, 4, 3, 1) @@ -125,9 +135,9 @@ func (pwm *PasswordModal) ClickSubmit() { func (pwm *PasswordModal) Wait() (string, bool) { select { - case result := <- pwm.outputChan: + case result := <-pwm.outputChan: return result, true - case <- pwm.cancelChan: + case <-pwm.cancelChan: return "", false } }