diff --git a/Gemfile b/Gemfile index 241dc3f..44bc70e 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,5 @@ source "https://rubygems.org" gem 'jekyll-compose', group: [:jekyll_plugins] + +gem 'jekyll-target-blank' diff --git a/Gemfile.lock b/Gemfile.lock index 38841f9..3a30d30 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -31,6 +31,9 @@ GEM jekyll (~> 3.0) jekyll-sass-converter (1.5.2) sass (~> 3.4) + jekyll-target-blank (1.1.1) + jekyll (~> 3.0) + nokogiri (~> 1.8.2) jekyll-watch (2.1.2) listen (~> 3.0) kramdown (1.17.0) @@ -40,6 +43,9 @@ GEM rb-inotify (~> 0.9, >= 0.9.7) ruby_dep (~> 1.2) mercenary (0.3.6) + mini_portile2 (2.3.0) + nokogiri (1.8.5) + mini_portile2 (~> 2.3.0) pathutil (0.16.2) forwardable-extended (~> 2.6) public_suffix (3.0.3) @@ -59,6 +65,7 @@ PLATFORMS DEPENDENCIES jekyll-compose + jekyll-target-blank BUNDLED WITH 2.0.1 diff --git a/_config.yml b/_config.yml index 3dbd025..b6ba11f 100644 --- a/_config.yml +++ b/_config.yml @@ -30,3 +30,6 @@ livereload: true jekyll_compose: auto_open: true + +plugins: + - jekyll-target-blank diff --git a/_includes/externals/styling.html b/_includes/externals/styling.html index f5147ac..fe1447b 100644 --- a/_includes/externals/styling.html +++ b/_includes/externals/styling.html @@ -5,12 +5,12 @@ {% endif %} - - - - - - + + + + + + diff --git a/_includes/header.html b/_includes/header.html index 34385d6..ebb6250 100644 --- a/_includes/header.html +++ b/_includes/header.html @@ -5,30 +5,24 @@
  • About
  • Contact
  • Blog
  • -
  • Friends
  • +
  • Privacy
  • - + diff --git a/_includes/page/explore.html b/_includes/page/explore.html index f9f7060..a8aca61 100644 --- a/_includes/page/explore.html +++ b/_includes/page/explore.html @@ -7,7 +7,7 @@ {% endfor %}
  • All
  • -
    Website source available here
    -
    All content is licensed under CC-BY 4.0
    +
    Website source available on Gitea
    +
    All content is licensed under CC-BY 4.0
    (unless otherwise stated)
    diff --git a/_includes/page/footer.html b/_includes/page/footer.html index 92560d9..7efc47c 100644 --- a/_includes/page/footer.html +++ b/_includes/page/footer.html @@ -1,8 +1,8 @@
    *****
    -
    Website source available on Gitea
    -
    Made with ❤️ and Jekyll by Amolith
    -
    All content is licensed under CC-BY 4.0
    +
    Website source available on Gitea
    +
    Made with ❤️ and Jekyll by Amolith
    +
    All content is licensed under CC-BY 4.0
    (unless otherwise stated)
    l4qlywnpwqsluw65ts7md3khrivpirse744un3x7mlskqauz5pyuzgqd.onion
    diff --git a/_layouts/blog.html b/_layouts/blog.html index 7972cad..5642485 100644 --- a/_layouts/blog.html +++ b/_layouts/blog.html @@ -1,5 +1,6 @@ --- layout: default +description: Amolith's blog on various tech-related topics --- {% include page/title.html %} diff --git a/_layouts/default.html b/_layouts/default.html index 5b8fae6..72bc9c1 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -1,5 +1,5 @@ - + {% include meta_tags/base.html %} {% include meta_tags/sharing.html %} diff --git a/_layouts/home.html b/_layouts/home.html new file mode 100644 index 0000000..6396bd7 --- /dev/null +++ b/_layouts/home.html @@ -0,0 +1,8 @@ +--- +layout: default +--- +{% include page/title.html %} + +{{ content }} + +{% include page/footer.html %} diff --git a/_layouts/page.html b/_layouts/page.html index 6396bd7..62c621b 100644 --- a/_layouts/page.html +++ b/_layouts/page.html @@ -2,7 +2,8 @@ layout: default --- {% include page/title.html %} - +
    {{ content }} +
    {% include page/footer.html %} diff --git a/_posts/2018-12-14-forecasting-with-a-command.md b/_posts/2018-12-14-forecasting-with-a-command.md index 6f82dcd..059b736 100644 --- a/_posts/2018-12-14-forecasting-with-a-command.md +++ b/_posts/2018-12-14-forecasting-with-a-command.md @@ -21,4 +21,4 @@ A great way to make this faster and simpler is to put an alias in your shell's ` echo "alias weather='curl sky.webionite.com/f/location'" >> ~/.bashrc ``` If you ever forget how to use the tool, just run `curl sky.webionite.com` and you'll be given instructions. -![](/assets/weather.png) +![screenshot of the output of the command. it shows two rows of four boxes with ascii art depicting rain, overcast clouds, and the sun obscured by clouds. it shows the temperature and the time of day. everything is coloured and looks very attractive.](/assets/weather.png) diff --git a/_posts/2019-02-05-dns-and-root-certificates-what-you-need-to-know.md b/_posts/2019-02-05-dns-and-root-certificates-what-you-need-to-know.md index 82946d4..1c3d9c7 100644 --- a/_posts/2019-02-05-dns-and-root-certificates-what-you-need-to-know.md +++ b/_posts/2019-02-05-dns-and-root-certificates-what-you-need-to-know.md @@ -6,8 +6,8 @@ description: Protecting yourself from malicious third parties leveraging DNS and cover: /assets/posts/privacy.png date: 2019-02-05 10:53 -0500 --- -

    This post was mirrored from Privacy Today on Telegram.

    -

    t.me/PrivacyToday

    +

    This post was mirrored from Privacy Today on Telegram.

    +

    t.me/PrivacyToday


    Due to recent events we felt compelled to write an impromptu article on this matter. It's intended for all audiences so it will be kept simple - technical details may be posted later. @@ -72,7 +72,7 @@ This site will look absolutely fine to you; it has https in the URL and, if you It now receives all the communication you intended to send to the original. This bypasses the checks created to avoid it. You won't receive error messages, your browser won't complain. -All your data is compromised! +All your data is compromised! ## 4. Conclusion @@ -85,7 +85,7 @@ All your data is compromised! **Do not ever install a 3rd party root certificate!** There are very few exceptions why you would want to do so and none of them are applicable to general end users. -Do not fall for clever marketing that ensures "ad blocking", "military grade security", or something similar. There are methods of using DNS resolvers on their own to enhance your privacy but installing a 3rd party root certificate never makes sense. You are opening yourself up to extreme abuse. +Do not fall for clever marketing that ensures "ad blocking", "military grade security", or something similar. There are methods of using DNS resolvers on their own to enhance your privacy but installing a 3rd party root certificate never makes sense. You are opening yourself up to extreme abuse. ## 5. Seeing It Live @@ -111,8 +111,8 @@ Here is the link: [https-interception.info.tm](http://https-interception.info.tm ## 6. Further Information -If you are interested in more technical details, let us know. If there is enough interest, we might write a more in-depth article but, for now, the important part is sharing the basics so you can make an informed decision and not fall for marketing and straight up fraud. Feel free to suggest other topics that are important to you. +If you are interested in more technical details, let us know. If there is enough interest, we might write a more in-depth article but, for now, the important part is sharing the basics so you can make an informed decision and not fall for marketing and straight up fraud. Feel free to suggest other topics that are important to you.

    -

    For more information/feedback/corrections, join Privacy Today on Telegram.

    -

    This post is licensed under CC BY-NC-SA 4.0 and was mirrored with permission.

    +

    For more information/feedback/corrections, join Privacy Today on Telegram.

    +

    This post is licensed under CC BY-NC-SA 4.0 and was mirrored with permission.

    diff --git a/_posts/2019-06-03-excluding-your-site-from-the-wayback-machine-keybase-only.md b/_posts/2019-06-03-excluding-your-site-from-the-wayback-machine-keybase-only.md index 486790a..8922b30 100644 --- a/_posts/2019-06-03-excluding-your-site-from-the-wayback-machine-keybase-only.md +++ b/_posts/2019-06-03-excluding-your-site-from-the-wayback-machine-keybase-only.md @@ -16,7 +16,7 @@ If that's all you intend to do, this should be sufficient and there's no need to # For domains you *don't* own There are a few things I used that worked in harmony to verify my other accounts. [Keybase](https://keybase.io/) was the most useful for this purpose. It is a proprietary service but I deem the level of proof it facilitates worth compromising for. -The other tool I used was [GPG](https://gnupg.org/). For the sake of keeping it simple, *this* guide will just deal with GPG from within Keybase. I ***really*** recommend actually learning to use GPG on its own; it's wonderful for protecting your privacy and verifying your identity in a multitude of situations. The next post will be on using GPG *outside* of Keybase for this so [stay tuned](nixnet.xyz/feed.xml). +The other tool I used was [GPG](https://gnupg.org/). For the sake of keeping it simple, *this* guide will just deal with GPG from within Keybase. I ***really*** recommend actually learning to use GPG on its own; it's wonderful for protecting your privacy and verifying your identity in a multitude of situations. The next post will be on using GPG *outside* of Keybase for this so [stay tuned](/feed.xml). ## Generating your key After creating your Keybase account, click "add a PGP key", "I need a public key", then enter the requisite information. You should use whatever name is associated with the account you'll be emailing Archive.org from as well as that address. Wait a bit while it generates the key . . . diff --git a/_sass/_default.scss b/_sass/_default.scss index 5718f7e..b7ca4e1 100644 --- a/_sass/_default.scss +++ b/_sass/_default.scss @@ -1,6 +1,7 @@ ; @font-face { font-family: 'Open Sans'; + font-display: auto; font-style: normal; font-weight: 300; src: url('assets/fonts/open-sans-v15-latin-300.eot'); /* IE9 Compat Modes */ @@ -13,6 +14,7 @@ } @font-face { font-family: 'Open Sans'; + font-display: auto; font-style: italic; font-weight: 300; src: url('/assets/fonts/open-sans-v15-latin-300italic.eot'); /* IE9 Compat Modes */ @@ -25,6 +27,7 @@ } @font-face { font-family: 'Inconsolata'; + font-display: auto; font-style: normal; font-weight: 400; src: url('/assets/fonts/inconsolata-v16-latin-regular.eot'); /* IE9 Compat Modes */ @@ -37,6 +40,7 @@ } @font-face { font-family: 'Inconsolata'; + font-display: auto; font-style: normal; font-weight: 700; src: url('/assets/fonts/inconsolata-v16-latin-700.eot'); /* IE9 Compat Modes */ @@ -49,6 +53,7 @@ } @font-face { font-family: 'Overpass'; + font-display: auto; font-style: normal; font-weight: 400; src: url('/assets/fonts/overpass-v2-latin-regular.eot'); /* IE9 Compat Modes */ diff --git a/about.html b/about.html index 351373d..f7caca5 100644 --- a/about.html +++ b/about.html @@ -1,5 +1,5 @@ --- -layout: page +layout: home title: About subtitle: 'A little bit about NixNet.xyz and some financial information' description: A little bit about NixNet.xyz and some financial information @@ -14,28 +14,25 @@ permalink: /about/

    Finance

    -

    At the moment, all the sites I host are running on netcup GmbH's 500 G8 VPS. I pay $6.10/mo in lump sums of $36.60 every six months. The domain nixnet.xyz is registered with gandi.net and it renews at $13.63/yr. I use Gandi because they have WHOIS privacy by default, every domain comes with two inboxes and unlimited aliases for both. One is amolith@nixnet.xyz and the other uses my real name rather than my pseudonym. My plans are to upgrade to the 1000 G8 (on the same page) and add a Tariff A storage server. This would cost $4.07/mo. I've also added the domain, nixnet.xyz to the goal, which is $13/year ($0.27/mo). My goal is $4.34/week exactly so I can upgrade VPSs, add a storage server, and not have anything left over.

    +

    At the moment, all the sites I host are running on netcup GmbH's 500 G8 VPS. I pay $6.10/mo in lump sums of $36.60 every six months. The domain nixnet.xyz is registered with gandi.net and it renews at $13.63/yr. I use Gandi because they have WHOIS privacy by default, every domain comes with two inboxes and unlimited aliases for both. One is amolith@nixnet.xyz and the other uses my real name rather than my pseudonym. My plans are to upgrade to the 1000 G8 (on the same page) and add a Tariff A storage server. This would cost $4.07/mo. I've also added the domain, nixnet.xyz to the goal, which is $13/year ($0.27/mo). My goal is $4.34/week exactly so I can upgrade VPSs, add a storage server, and not have anything left over.

    - +
    - - - - +
    -

    If you want to donate in crypto currencies, I accept Bitcoin and Litecoin though I do prefer Litecoin because of its speed. If you do crypto on mobile, you can scan the QR codes with your preferred app or tap them to open the appropriate app. The address is also below for other methods.

    +

    If you want to donate in crypto currencies, I accept Bitcoin and Litecoin though I do prefer Litecoin because of its speed. If you do crypto on mobile, you can scan the QR codes with your preferred app or tap them to open the appropriate app. The address is also below for other methods.

    -

    +

    Bitcoin QR code

    Bitcoin

    1Q3o8Wtji2QS566BExdcPGMk76NjJgHrcz

    -

    +

    Litecoin QR code

    Litecoin

    LUUbRvipXwcf3pFAXLVJV4jYmK3uBYBJcq

    diff --git a/amolith.txt b/amolith.txt index a840dd9..98ebbda 100644 --- a/amolith.txt +++ b/amolith.txt @@ -71,184 +71,245 @@ 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 -=YfBo +VTxxovbOsVPAwbsx381A2i3U/W859gIYna3gHokDfgQTAQgBaAIbAwULCQgHAwUV +CgkICwQWAgEAWxSAAAAAABIAQHByb29mQG1ldGFjb2RlLmJpemh0dHBzOi8vZ2lz +dC5naXRodWIuY29tL0Ftb2xpdGgvZDMwMTZkN2E1MzI5NDRhZjhhN2RkYzA5ODk4 +ZDUzMGMyFIAAAAAAEgAXcHJvb2ZAbWV0YWNvZGUuYml6ZG5zOm5peG5ldC54eXo/ +dHlwZT1UWFQ8FIAAAAAAEgAhcHJvb2ZAbWV0YWNvZGUuYml6aHR0cHM6Ly9tYXN0 +by5uaXhuZXQueHl6L0BhbW9saXRoZBSAAAAAABIASXByb29mQG1ldGFjb2RlLmJp +emh0dHBzOi8vd3d3LnJlZGRpdC5jb20vdXNlci9BbW9saXRoL2NvbW1lbnRzL2J4 +enJvei9ncGdfa2V5X3ZlcmlmaWNhdGlvbi8WIQR13TLKG/7qauQNgjxR/UCTbbAG +WwUCXQXUOQUJBrcyfgAKCRBR/UCTbbAGWzezEAC07EO0+OfUkZj1ev1YbxEcC7kL +J9WnDa1sFe7x2EpH9Z8SyUkBDXW8YktrBPg4fWls3AVSyfe7U5/TtA5M6WcRt4UR +YbHAS8FFS20BPRWK6FoSexSy1dtkOBXMT3hn9X0RQoDDTB3w9/bAjq8r4zMvb1+n +H1UGJh3g5tYAjyCXfbSz30bfZlkbhc57tMiPWG3RyOiiyhUEfXkMnlNK5ugj1RLQ +qFxKWo8B6y0MJOxSNaFrGwJbbJtkN6f3AavzIFPwyhemKfT/rEDGvigWr7G7CfAr +1pqurTY+RTlUUhQYG1VLb05yc0D1AXRjFcZWiRw6D2BxNi5MuEwbWnYhNmSom6H/ +FSThxn/hxAgQpapjXkC8jqy6/gTpqRigvdlICGzlpmKix61Cb0laV+7+P81kr8x9 +wfjZWNwVIlTZ/BuOAYGJstxKvkAiEG3qUF8oh36vM5xGnzfK/Ewz/JJHoes0SkX6 +XIxEwuJP8qAUus18snN599tXmtgYLU51QqUrJh+MC8+tlkWHj+x9VQMAV5jlAioq +d2aZPJg+cC2P+a9BB15c6j5Kyx3cYRJwHzKcr2G/nbvfGAL3ECr1CuC0My4ae9Rl +9OMzOkV9J/QIDZPBFNfZTjWCMDyCLG92F318GJ+vi2EquX+Qc6Z2aS3/5BITPX61 +ck/reZ5eezs8RNGQbIkCMwQQAQgAHRYhBISGxudX+toKDyexPBylW+peXLKWBQJd +BqP9AAoJEBylW+peXLKWFr8P/2COjRl2dasxAhVVcAQ3f+79T593rP5enajOSg6M +Ll3eBUakctM3HBI3Mj9v+auHN8pv75B5z6lA1Br5ON64LsbwGF4LJ/DDSCWTkR8h +OeWoisIhr+OmHwyu0YgFUOiBRk9BuTCWMKUFRILFb3TKKevzUrdVOBR5Kry1oUla +6aTlX4XRtUmu0sFgN0ShiNzgLS8T/9uC32Elrx6P1oqRI1tcEmxXgIVbWozbGqzJ +CTq29lTwqeI1teZFGTKcj1Fus7IRQbPyc/CO6DOJrkZd26pNXqsCKd2oaCzE80cV +KGu5gQ9aUkigUWDnPN75OdoPma2QiAK32Cc/OtUY2yFPPRmjKX/wSyEKFLw2b8N4 +RiqwdjttIuT2zLxcenIqA7+5e07xaVzrKbfH67xL8RBho86UiN7vxbUCHTLTWc+Z +lfkvssvKyRidP8zpwEU1F8J/b/IVMjMwvQqtenC00sMc+mMJJ4viRCxMlfPlU3q3 +QRDsNBV/VHScXgW+gHtVF28LQ/zyKUa1odevoLnvB3ax126LjC0t6dfwVZ1mxzbZ +gwUbGmSXNp0oGweL1aCnrhyntQqO+lWurazoNWmc3vjGPS/V26uMp3DsW8hml9jo +zqCV87E2XVnvG1ZLeWVBVU4Klu0H1ajGPHfOoQn7w/+S9UsqR5zE9xU5ofIKtYNa +WyQfiQIzBBABCgAdFiEEfplkbhoPhN70gcW0mRL6Tr+9i7cFAl0GciIACgkQmRL6 +Tr+9i7eTFw/8CKuVHMMUGbwkAgOaZC8mCu1mcKWJDYBcHdyfTqKCkfol9L2OuYD+ +Tufupb+PMDCswGrT7iXOR+f55d4vpTqyHF/wVTHEhXAkWnxiIr7Lhq1G8JktCGK/ +LY/28k3GeVIO/wqL5vkeixWk1G8HGnEtYLq+J+V66LF+lDn+e6V6czH876YxO8sH +rVZI5MbheLD6m4LqOTmmbqhGlSrqUxJtk/3F7ai3+nqikz6fGJdY2huajKvSSVBo +DXDo492tzHw7PaMpyr0d6u5YsJV+gIv9EuiYM3EXbtCoA1NbitsKsfBUv15q30WH +M9ktx5rjY2eqTEUrE5azAfaxHC/NT96dJwjuKr5hcMEYxk2mdeTLaDGd3D/CGqie +hE4b7NLbi98N0ODGxSLavPlbfsaXPI4ZNd6H47YVTQYdxuTBnA22UKnJ6u5/KWtR +o3qIkpzVEhRSMI5c7h7JSO55avc/kpbU+y+18u0GH/20ZT6fClHJIcCZ3WSgAcNU +9ry/1LVhhKSwg5zVC8sJctOEGTQuZxqFabmSz8zS47YKvW0GYfpMrN5DheGE+M4N +L/fToOiWg2PR1d/QM+uB+wXB+6mgo3lHtQmuzjQAwCs8VV7CqFpzbIvZV10V8ABc +0kesczWm0RRdNDZUIVc9xxts1Fm+WFD7rwvMMZTVlW3iNS9hAgJKeRaJAhwEEwEC +AAYFAl0IMNEACgkQxdX0vtJKSgKjhxAAlFmOVWimaEfRtfcXfwhGEfzCwAYCNEd4 +6iqSyb6A94joztqpBqnRHvcKyfgZq1J/o2jIGmVWZOAdNjc5Ot1Oor8bI4TnwKCU +TVNgvn6rnI4Bq+yuH1dczy81GmJVy8P/N6g1CRtvYmNyZ43lRsK83yQ09whyO11j +Kg/sGq8PPoRxilWFr2WJ/2p4FzE/rwMc3wHsNUt00T6sS47pTpk6/LoN+680JAjN +pIec+rBT1TuViMDK2sfyYnMg1t5dsE4lKZS1I/JQ2E0JH7Oh5GKC54yntSZgucJ7 +9/1/Ei2h1XJsbPRWJzwvE6nWnGV2deTgf5x+YWMjB0b+W7kVjPds+5B+dH/Ytk4C +C5SpmR6A7eCpkJblAzY6SuGO/f4Zkh1mnkZZFU1/87sYiGBybS42st8//21qbKC3 +O/DxO6LGaIF20mtt1IloFpznko9CiZtJyQdiElKH9bJfx94uCYTA1bcp2j1+VpDj +kwFOxafHXcY5WyPVxe2SLmQKhl7J+X4Mrh+YUEsqK0ZiT+D1Fb2YCcCRzCnAMI9D +CA8hyIK+2+toVRUBcKuWmDF2ahqBfmj3zf7dUmFW4HeSIJFHyeEEJV82m+RNwTZc +RrK5FrstSCe5rwLmKgNGyu68swcAT2+gHLeKEQgsEgGwXHjidb0AAgVYju3G4V2j +1aJi7H9GI1+JA34EEwEIAWgCGwMFCQFOncUFCwkIBwMFFQoJCAsEFgIBABYhBHXd +Msob/upq5A2CPFH9QJNtsAZbBQJc+tE7WxSAAAAAABIAQHByb29mQG1ldGFjb2Rl +LmJpemh0dHBzOi8vZ2lzdC5naXRodWIuY29tL0Ftb2xpdGgvZDMwMTZkN2E1MzI5 +NDRhZjhhN2RkYzA5ODk4ZDUzMGMyFIAAAAAAEgAXcHJvb2ZAbWV0YWNvZGUuYml6 +ZG5zOm5peG5ldC54eXo/dHlwZT1UWFQ8FIAAAAAAEgAhcHJvb2ZAbWV0YWNvZGUu +Yml6aHR0cHM6Ly9tYXN0by5uaXhuZXQueHl6L0BhbW9saXRoZBSAAAAAABIASXBy +b29mQG1ldGFjb2RlLmJpemh0dHBzOi8vd3d3LnJlZGRpdC5jb20vdXNlci9BbW9s +aXRoL2NvbW1lbnRzL2J4enJvei9ncGdfa2V5X3ZlcmlmaWNhdGlvbi8ACgkQUf1A +k22wBluVXA/+KMdsHcfNZEFRDXoCYxEA24PPqLcaObz7ounUBYdjuljpdi+prPWs +FT7AR214mC5UbOUtfCHrZ5bbkMD+Si6UDKoyiu8joZa6GKr5ddTkbdo0QJxQHCT+ +cZAqLY097UCXKhHjwD71hma4FuWKBCNh6ix3XDP5vZ5rRVajZMTADFZP044mRH4O +Q+aW0vBhYaWgXa0BAnNhBw70lz/oHdM1zi8XqiB4pNBsmdHd3GbH7tIwCqbXw9gi +s/dFGeWIERdCLrxR+45akWJm+uSUlFbipGX/zBG/5oJYjPXnRU973n0mX5MWFd3a +vLJskoWyVBvrlKNKJqf6tU59umo2ocwEkKLtZJ0V3YG5lWF6dPA1svmpKvA/ufnc +T83PiJgZGBR+yTwGsoH3fMbsvAo1uS76+ZWo3ulYgMWEDuI4kI/rLxqd9Ya0e/tG +JVVuPtUWdx1+eebBSWTJU98KB+QKeaO52vIkUPsPFx8y029Dvx4TD7wK9jbzKJZB +uPDIQSQXaqH3LsbvsGIG3bkTlECIQlTZhFHg1M5SJENRzp2Z4GfLkv2KONQAPEkJ +BKcHKZYXZbK8hetpy74O+Jj2c8MSPUrrbI5iC8NwhcwF2gfkow8H/rAQhuEt2xRK +9zO9ISkOukLccyczlEb7hP1NKIoVSzJrN+E55PIMxjSr1jQXQ4SMRJi5Ag0EW/I8 +OwEQALsWpBxmWOrcNOJjlAlqVW+JZS2MebKFnN0vC2sBzs0UyZcLGZCSMd3GxQM7 +rIMbkcsmqF4lFs/gU2CEHALhJzCGvOsKHCiOevO/qwQy4bicMur8DwSD+N3ZA7sj +g5x9MiTJmYkmPUC9qKvq0ZVCK38fyD39MdtTCe+UYclOkXzc3dGHmwLzDYmmXpbj +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 +OHc= +=nOej -----END PGP PUBLIC KEY BLOCK----- diff --git a/contact.html b/contact.html index 61fd73b..e6f7257 100644 --- a/contact.html +++ b/contact.html @@ -1,5 +1,5 @@ --- -layout: page +layout: home title: Contact subtitle: Where to find me description: Various ways to contact me @@ -11,6 +11,12 @@ These are some of my more public accounts; feel free to contact me at any of the

    @@ -40,9 +40,10 @@ These are some of my more public accounts; feel free to contact me at any of the


    +

    - I also have Briar if we meet IRL and I'm on Wire as @amolith. I can be found on my XMPP server at amolith@nixnet.xyz as well. + I also have Briar if we meet IRL and I'm on Wire as @amolith.

    Despite the many ways to contact me, email and XMPP are and always will be my most preferrered methods (as well as the most reliable). If you email me, please be sure to encrypt it with my GPG key. If you message me over XMPP, please make sure it's with a client that has OMEMO encryption. diff --git a/contact2.html b/contact2.html new file mode 100644 index 0000000..af9e700 --- /dev/null +++ b/contact2.html @@ -0,0 +1,78 @@ +--- +layout: page +title: Contact +subtitle: Where to find me +description: Various ways to contact me +cover: /assets/pages/contact.png +permalink: /contact2/ +--- + + + + + + +

    +
    +
    + OpenPGP +
    + +
    +
    +
    +
    +
    +
    Loading key...
    + +
    +
    +

    Note: contents of this page are generated purely from the OpenPGP key in your browser. + If you want to add social proofs to your key see OpenPGP Proofs page.

    +

    If you want to adjust the keyserver used or the key being displayed just edit this HTML page.

    +
    +
    diff --git a/css/main.scss b/css/main.scss index 63425a0..37aa30a 100755 --- a/css/main.scss +++ b/css/main.scss @@ -8,7 +8,7 @@ sitemap: /* =Colors */ $dorian: #323234; $iron: #ffffff; -$cloudy: #828282; +$cloudy: #9d9d9d; @import "mixins", diff --git a/friends.html b/friends.html deleted file mode 100644 index 24f1360..0000000 --- a/friends.html +++ /dev/null @@ -1,23 +0,0 @@ ---- -layout: page -title: Friends of NixNet -subtitle: A list of some like-minded people and their websites -description: A list of some like-minded people and their websites -cover: /assets/pages/friends.png -permalink: /friends/ ---- -
    -

    People

    - -

    Organisations

    -
      -
    • LibreHo.st - a network of average people who host FLOSS services available for anyone to use
    • -
    • Lelux.fi - really cool guy who hosts some services like I do
    • -
    • Snopyta.org - Snopyta provides online services based on freedom, privacy and decentralization. Part of LibreHosters.
    • -
    • LibreDesigners.org - a website for learning how to create art, interfaces, websites, etc. with free/libre and open source software (Portuguese-only right now)
    • -
    • CYGONetwork.com - alternative to Facebook. Better features, better community, and enhanced privacy/security
    • -
    -
    diff --git a/img.py b/img.py old mode 100644 new mode 100755 diff --git a/index.html b/index.html index 5afd67a..678e9cd 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,5 @@ --- -layout: page +layout: home title: "NixNet" subtitle: "Freedom served with a slice of liberty" description: "A network of websites and services hosted by Amolith available for anyone to use free of charge." @@ -11,7 +11,7 @@ priority: 0.9

    I host a variety of services that are available for anyone to use free of charge. Below is a list with descriptions of each.

    - +

    Clone // +// // +////////////////////////////// + + +/** + * Create a packetlist from the correspoding object types. + * @param {Object} options the object passed to and from the web worker + * @returns {Object} a mutated version of the options optject + */ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2015 Tankred Hase +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @fileoverview This module implements packet list cloning required to + * pass certain object types between the web worker and main thread using + * the structured cloning algorithm. + * @module packet/clone + */ + +function clonePackets(options) { + if (options.publicKeys) { + options.publicKeys = options.publicKeys.map(key => key.toPacketlist()); + } + if (options.privateKeys) { + options.privateKeys = options.privateKeys.map(key => key.toPacketlist()); + } + if (options.privateKey) { + options.privateKey = options.privateKey.toPacketlist(); + } + if (options.key) { + options.key = options.key.toPacketlist(); + } + if (options.message) { + //could be either a Message or CleartextMessage object + if (options.message instanceof _message.Message) { + options.message = options.message.packets; + } else if (options.message instanceof _cleartext.CleartextMessage) { + options.message = { text: options.message.text, signature: options.message.signature.packets }; + } + } + if (options.signature && options.signature instanceof _signature.Signature) { + options.signature = options.signature.packets; + } + if (options.signatures) { + options.signatures.forEach(verificationObjectToClone); + } + return options; +} + +function verificationObjectToClone(verObject) { + const verified = verObject.verified; + verObject.verified = _webStreamTools2.default.fromAsync(() => verified); + if (verObject.signature instanceof Promise) { + const signature = verObject.signature; + verObject.signature = _webStreamTools2.default.fromAsync(async () => { + const packets = (await signature).packets; + try { + await verified; + delete packets[0].signature; + } catch (e) {} + return packets; + }); + } else { + verObject.signature = verObject.signature.packets; + } + if (verObject.error) { + verObject.error = verObject.error.message; + } + return verObject; +} + +////////////////////////////// +// // +// Clone --> List // +// // +////////////////////////////// + + +/** + * Creates an object with the correct prototype from a corresponding packetlist. + * @param {Object} options the object passed to and from the web worker + * @param {String} method the public api function name to be delegated to the worker + * @returns {Object} a mutated version of the options optject + */ +function parseClonedPackets(options) { + if (options.publicKeys) { + options.publicKeys = options.publicKeys.map(packetlistCloneToKey); + } + if (options.privateKeys) { + options.privateKeys = options.privateKeys.map(packetlistCloneToKey); + } + if (options.privateKey) { + options.privateKey = packetlistCloneToKey(options.privateKey); + } + if (options.key) { + options.key = packetlistCloneToKey(options.key); + } + if (options.message && options.message.signature) { + options.message = packetlistCloneToCleartextMessage(options.message); + } else if (options.message) { + options.message = packetlistCloneToMessage(options.message); + } + if (options.signatures) { + options.signatures = options.signatures.map(packetlistCloneToSignatures); + } + if (options.signature) { + options.signature = packetlistCloneToSignature(options.signature); + } + return options; +} + +function packetlistCloneToKey(clone) { + const packetlist = _packetlist2.default.fromStructuredClone(clone); + return new _key.Key(packetlist); +} + +function packetlistCloneToMessage(clone) { + const packetlist = _packetlist2.default.fromStructuredClone(clone); + return new _message.Message(packetlist); +} + +function packetlistCloneToCleartextMessage(clone) { + const packetlist = _packetlist2.default.fromStructuredClone(clone.signature); + return new _cleartext.CleartextMessage(clone.text, new _signature.Signature(packetlist)); +} + +//verification objects +function packetlistCloneToSignatures(clone) { + clone.keyid = _keyid2.default.fromClone(clone.keyid); + if (_util2.default.isStream(clone.signature)) { + clone.signature = _webStreamTools2.default.readToEnd(clone.signature, ([signature]) => new _signature.Signature(_packetlist2.default.fromStructuredClone(signature))); + clone.signature.catch(() => {}); + } else { + clone.signature = new _signature.Signature(_packetlist2.default.fromStructuredClone(clone.signature)); + } + clone.verified = _webStreamTools2.default.readToEnd(clone.verified, ([verified]) => verified); + clone.verified.catch(() => {}); + if (clone.error) { + clone.error = new Error(clone.error); + } + return clone; +} + +function packetlistCloneToSignature(clone) { + if (_util2.default.isString(clone) || _util2.default.isStream(clone)) { + //signature is armored + return clone; + } + const packetlist = _packetlist2.default.fromStructuredClone(clone); + return new _signature.Signature(packetlist); +} + +},{"../cleartext":78,"../key":117,"../message":121,"../signature":146,"../type/keyid":149,"../util":153,"./packetlist":131,"web-stream-tools":76}],125:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _pako = require('pako'); + +var _pako2 = _interopRequireDefault(_pako); + +var _seekBzip = require('seek-bzip'); + +var _seekBzip2 = _interopRequireDefault(_seekBzip); + +var _webStreamTools = require('web-stream-tools'); + +var _webStreamTools2 = _interopRequireDefault(_webStreamTools); + +var _config = require('../config'); + +var _config2 = _interopRequireDefault(_config); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Implementation of the Compressed Data Packet (Tag 8) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.6|RFC4880 5.6}: + * The Compressed Data packet contains compressed data. Typically, + * this packet is found as the contents of an encrypted packet, or following + * a Signature or One-Pass Signature packet, and contains a literal data packet. + * @memberof module:packet + * @constructor + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires web-stream-tools + * @requires pako + * @requires config + * @requires enums + * @requires util + * @requires compression/bzip2 + */ + +function Compressed() { + /** + * Packet type + * @type {module:enums.packet} + */ + this.tag = _enums2.default.packet.compressed; + /** + * List of packets + * @type {module:packet.List} + */ + this.packets = null; + /** + * Compression algorithm + * @type {compression} + */ + this.algorithm = 'zip'; + + /** + * Compressed packet data + * @type {Uint8Array | ReadableStream} + */ + this.compressed = null; +} + +/** + * Parsing function for the packet. + * @param {Uint8Array | ReadableStream} bytes Payload of a tag 8 packet + */ +Compressed.prototype.read = async function (bytes, streaming) { + await _webStreamTools2.default.parse(bytes, async reader => { + + // One octet that gives the algorithm used to compress the packet. + this.algorithm = _enums2.default.read(_enums2.default.compression, (await reader.readByte())); + + // Compressed data, which makes up the remainder of the packet. + this.compressed = reader.remainder(); + + await this.decompress(streaming); + }); +}; + +/** + * Return the compressed packet. + * @returns {Uint8Array | ReadableStream} binary compressed packet + */ +Compressed.prototype.write = function () { + if (this.compressed === null) { + this.compress(); + } + + return _util2.default.concat([new Uint8Array([_enums2.default.write(_enums2.default.compression, this.algorithm)]), this.compressed]); +}; + +/** + * Decompression method for decompressing the compressed data + * read by read_packet + */ +Compressed.prototype.decompress = async function (streaming) { + + if (!decompress_fns[this.algorithm]) { + throw new Error(this.algorithm + ' decompression not supported'); + } + + await this.packets.read(decompress_fns[this.algorithm](this.compressed), streaming); +}; + +/** + * Compress the packet data (member decompressedData) + */ +Compressed.prototype.compress = function () { + + if (!compress_fns[this.algorithm]) { + throw new Error(this.algorithm + ' compression not supported'); + } + + this.compressed = compress_fns[this.algorithm](this.packets.write()); +}; + +exports.default = Compressed; + +////////////////////////// +// // +// Helper functions // +// // +////////////////////////// + + +const nodeZlib = _util2.default.getNodeZlib(); + +function node_zlib(func, options = {}) { + return function (data) { + return _webStreamTools2.default.nodeToWeb(_webStreamTools2.default.webToNode(data).pipe(func(options))); + }; +} + +function pako_zlib(constructor, options = {}) { + return function (data) { + const obj = new constructor(options); + return _webStreamTools2.default.transform(data, value => { + if (value.length) { + obj.push(value, _pako2.default.Z_SYNC_FLUSH); + return obj.result; + } + }, () => { + if (constructor === _pako2.default.Deflate) { + obj.push([], _pako2.default.Z_FINISH); + return obj.result; + } + }); + }; +} + +function bzip2(func) { + return function (data) { + return _webStreamTools2.default.fromAsync(async () => func((await _webStreamTools2.default.readToEnd(data)))); + }; +} + +let compress_fns; +let decompress_fns; +if (nodeZlib) { + // Use Node native zlib for DEFLATE compression/decompression + compress_fns = { + zip: node_zlib(nodeZlib.createDeflateRaw, { level: _config2.default.deflate_level }), + zlib: node_zlib(nodeZlib.createDeflate, { level: _config2.default.deflate_level }) + }; + + decompress_fns = { + zip: node_zlib(nodeZlib.createInflateRaw), + zlib: node_zlib(nodeZlib.createInflate), + bzip2: bzip2(_seekBzip2.default.decode) + }; +} else { + // Use JS fallbacks + compress_fns = { + zip: pako_zlib(_pako2.default.Deflate, { raw: true, level: _config2.default.deflate_level }), + zlib: pako_zlib(_pako2.default.Deflate, { level: _config2.default.deflate_level }) + }; + + decompress_fns = { + zip: pako_zlib(_pako2.default.Inflate, { raw: true }), + zlib: pako_zlib(_pako2.default.Inflate), + bzip2: bzip2(_seekBzip2.default.decode) + }; +} + +},{"../config":80,"../enums":114,"../util":153,"pako":51,"seek-bzip":70,"web-stream-tools":76}],126:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _all_packets = require('./all_packets'); + +var packets = _interopRequireWildcard(_all_packets); + +var _clone = require('./clone'); + +var clone = _interopRequireWildcard(_clone); + +var _packetlist = require('./packetlist'); + +var _packetlist2 = _interopRequireDefault(_packetlist); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +const mod = { + List: _packetlist2.default, + clone +}; /** + * @fileoverview OpenPGP packet types + * @see module:packet/all_packets + * @see module:packet/clone + * @see module:packet.List + * @module packet + */ + +Object.assign(mod, packets); + +exports.default = mod; + +},{"./all_packets":123,"./clone":124,"./packetlist":131}],127:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _webStreamTools = require('web-stream-tools'); + +var _webStreamTools2 = _interopRequireDefault(_webStreamTools); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Implementation of the Literal Data Packet (Tag 11) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.9|RFC4880 5.9}: + * A Literal Data packet contains the body of a message; data that is not to be + * further interpreted. + * @param {Date} date the creation date of the literal package + * @memberof module:packet + * @constructor + */ +function Literal(date = new Date()) { + this.tag = _enums2.default.packet.literal; + this.format = 'utf8'; // default format for literal data packets + this.date = _util2.default.normalizeDate(date); + this.text = null; // textual data representation + this.data = null; // literal data representation + this.filename = 'msg.txt'; +} + +/** + * Set the packet data to a javascript native string, end of line + * will be normalized to \r\n and by default text is converted to UTF8 + * @param {String | ReadableStream} text Any native javascript string + * @param {utf8|binary|text|mime} format (optional) The format of the string of bytes + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires web-stream-tools + * @requires enums + * @requires util + */ + +Literal.prototype.setText = function (text, format = 'utf8') { + this.format = format; + this.text = text; + this.data = null; +}; + +/** + * Returns literal data packets as native JavaScript string + * with normalized end of line to \n + * @param {Boolean} clone (optional) Whether to return a clone so that getBytes/getText can be called again + * @returns {String | ReadableStream} literal data as text + */ +Literal.prototype.getText = function (clone = false) { + if (this.text === null || _util2.default.isStream(this.text)) { + // Assume that this.text has been read + this.text = _util2.default.nativeEOL(_util2.default.decode_utf8(this.getBytes(clone))); + } + return this.text; +}; + +/** + * Set the packet data to value represented by the provided string of bytes. + * @param {Uint8Array | ReadableStream} bytes The string of bytes + * @param {utf8|binary|text|mime} format The format of the string of bytes + */ +Literal.prototype.setBytes = function (bytes, format) { + this.format = format; + this.data = bytes; + this.text = null; +}; + +/** + * Get the byte sequence representing the literal packet data + * @param {Boolean} clone (optional) Whether to return a clone so that getBytes/getText can be called again + * @returns {Uint8Array | ReadableStream} A sequence of bytes + */ +Literal.prototype.getBytes = function (clone = false) { + if (this.data === null) { + // normalize EOL to \r\n and encode UTF8 + this.data = _util2.default.encode_utf8(_util2.default.canonicalizeEOL(this.text)); + } + if (clone) { + return _webStreamTools2.default.passiveClone(this.data); + } + return this.data; +}; + +/** + * Sets the filename of the literal packet data + * @param {String} filename Any native javascript string + */ +Literal.prototype.setFilename = function (filename) { + this.filename = filename; +}; + +/** + * Get the filename of the literal packet data + * @returns {String} filename + */ +Literal.prototype.getFilename = function () { + return this.filename; +}; + +/** + * Parsing function for a literal data packet (tag 11). + * + * @param {Uint8Array | ReadableStream} input Payload of a tag 11 packet + * @returns {module:packet.Literal} object representation + */ +Literal.prototype.read = async function (bytes) { + await _webStreamTools2.default.parse(bytes, async reader => { + // - A one-octet field that describes how the data is formatted. + const format = _enums2.default.read(_enums2.default.literal, (await reader.readByte())); + + const filename_len = await reader.readByte(); + this.filename = _util2.default.decode_utf8((await reader.readBytes(filename_len))); + + this.date = _util2.default.readDate((await reader.readBytes(4))); + + const data = reader.remainder(); + + this.setBytes(data, format); + }); +}; + +/** + * Creates a string representation of the packet + * + * @returns {Uint8Array | ReadableStream} Uint8Array representation of the packet + */ +Literal.prototype.write = function () { + const filename = _util2.default.encode_utf8(this.filename); + const filename_length = new Uint8Array([filename.length]); + + const format = new Uint8Array([_enums2.default.write(_enums2.default.literal, this.format)]); + const date = _util2.default.writeDate(this.date); + const data = this.getBytes(); + + return _util2.default.concat([format, filename_length, filename, date, data]); +}; + +exports.default = Literal; + +},{"../enums":114,"../util":153,"web-stream-tools":76}],128:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Implementation of the strange "Marker packet" (Tag 10) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.8|RFC4880 5.8}: + * An experimental version of PGP used this packet as the Literal + * packet, but no released version of PGP generated Literal packets with this + * tag. With PGP 5.x, this packet has been reassigned and is reserved for use as + * the Marker packet. + * + * Such a packet MUST be ignored when received. + * @memberof module:packet + * @constructor + */ +function Marker() { + this.tag = _enums2.default.packet.marker; +} + +/** + * Parsing function for a literal data packet (tag 10). + * + * @param {String} input Payload of a tag 10 packet + * @param {Integer} position + * Position to start reading from the input string + * @param {Integer} len + * Length of the packet or the remaining length of + * input at position + * @returns {module:packet.Marker} Object representation + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires enums + */ + +Marker.prototype.read = function (bytes) { + if (bytes[0] === 0x50 && // P + bytes[1] === 0x47 && // G + bytes[2] === 0x50) { + // P + return true; + } + // marker packet does not contain "PGP" + return false; +}; + +exports.default = Marker; + +},{"../enums":114}],129:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _signature = require('./signature'); + +var _signature2 = _interopRequireDefault(_signature); + +var _keyid = require('../type/keyid'); + +var _keyid2 = _interopRequireDefault(_keyid); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Implementation of the One-Pass Signature Packets (Tag 4) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.4|RFC4880 5.4}: + * The One-Pass Signature packet precedes the signed data and contains + * enough information to allow the receiver to begin calculating any + * hashes needed to verify the signature. It allows the Signature + * packet to be placed at the end of the message, so that the signer + * can compute the entire signed message in one pass. + * @memberof module:packet + * @constructor + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires packet/signature + * @requires type/keyid + * @requires enums + * @requires util + */ + +function OnePassSignature() { + /** + * Packet type + * @type {module:enums.packet} + */ + this.tag = _enums2.default.packet.onePassSignature; + /** A one-octet version number. The current version is 3. */ + this.version = null; + /** + * A one-octet signature type. + * Signature types are described in + * {@link https://tools.ietf.org/html/rfc4880#section-5.2.1|RFC4880 Section 5.2.1}. + */ + this.signatureType = null; + /** + * A one-octet number describing the hash algorithm used. + * @see {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC4880 9.4} + */ + this.hashAlgorithm = null; + /** + * A one-octet number describing the public-key algorithm used. + * @see {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC4880 9.1} + */ + this.publicKeyAlgorithm = null; + /** An eight-octet number holding the Key ID of the signing key. */ + this.issuerKeyId = null; + /** + * A one-octet number holding a flag showing whether the signature is nested. + * A zero value indicates that the next packet is another One-Pass Signature packet + * that describes another signature to be applied to the same message data. + */ + this.flags = null; +} + +/** + * parsing function for a one-pass signature packet (tag 4). + * @param {Uint8Array} bytes payload of a tag 4 packet + * @returns {module:packet.OnePassSignature} object representation + */ +OnePassSignature.prototype.read = function (bytes) { + let mypos = 0; + // A one-octet version number. The current version is 3. + this.version = bytes[mypos++]; + + // A one-octet signature type. Signature types are described in + // Section 5.2.1. + this.signatureType = bytes[mypos++]; + + // A one-octet number describing the hash algorithm used. + this.hashAlgorithm = bytes[mypos++]; + + // A one-octet number describing the public-key algorithm used. + this.publicKeyAlgorithm = bytes[mypos++]; + + // An eight-octet number holding the Key ID of the signing key. + this.issuerKeyId = new _keyid2.default(); + this.issuerKeyId.read(bytes.subarray(mypos, mypos + 8)); + mypos += 8; + + // A one-octet number holding a flag showing whether the signature + // is nested. A zero value indicates that the next packet is + // another One-Pass Signature packet that describes another + // signature to be applied to the same message data. + this.flags = bytes[mypos++]; + return this; +}; + +/** + * creates a string representation of a one-pass signature packet + * @returns {Uint8Array} a Uint8Array representation of a one-pass signature packet + */ +OnePassSignature.prototype.write = function () { + const start = new Uint8Array([3, _enums2.default.write(_enums2.default.signature, this.signatureType), _enums2.default.write(_enums2.default.hash, this.hashAlgorithm), _enums2.default.write(_enums2.default.publicKey, this.publicKeyAlgorithm)]); + + const end = new Uint8Array([this.flags]); + + return _util2.default.concatUint8Array([start, this.issuerKeyId.write(), end]); +}; + +/** + * Fix custom types after cloning + */ +OnePassSignature.prototype.postCloneTypeFix = function () { + this.issuerKeyId = _keyid2.default.fromClone(this.issuerKeyId); +}; + +OnePassSignature.prototype.hash = function () { + const version = this.version; + this.version = 4; + try { + return _signature2.default.prototype.hash.apply(this, arguments); + } finally { + this.version = version; + } +}; +OnePassSignature.prototype.toHash = _signature2.default.prototype.toHash; +OnePassSignature.prototype.toSign = _signature2.default.prototype.toSign; +OnePassSignature.prototype.calculateTrailer = _signature2.default.prototype.calculateTrailer; + +OnePassSignature.prototype.verify = async function () { + const correspondingSig = await this.correspondingSig; + if (!correspondingSig || correspondingSig.tag !== _enums2.default.packet.signature) { + throw new Error('Corresponding signature packet missing'); + } + if (correspondingSig.signatureType !== this.signatureType || correspondingSig.hashAlgorithm !== this.hashAlgorithm || correspondingSig.publicKeyAlgorithm !== this.publicKeyAlgorithm || !correspondingSig.issuerKeyId.equals(this.issuerKeyId)) { + throw new Error('Corresponding signature packet does not match one-pass signature packet'); + } + correspondingSig.hashed = this.hashed; + return correspondingSig.verify.apply(correspondingSig, arguments); +}; + +exports.default = OnePassSignature; + +},{"../enums":114,"../type/keyid":149,"../util":153,"./signature":137}],130:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); // GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/* eslint-disable callback-return */ + +/** + * @fileoverview Functions for reading and writing packets + * @requires web-stream-tools + * @requires enums + * @requires util + * @module packet/packet + */ + +var _webStreamTools = require('web-stream-tools'); + +var _webStreamTools2 = _interopRequireDefault(_webStreamTools); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +exports.default = { + readSimpleLength: function readSimpleLength(bytes) { + let len = 0; + let offset; + const type = bytes[0]; + + if (type < 192) { + var _bytes = _slicedToArray(bytes, 1); + + len = _bytes[0]; + + offset = 1; + } else if (type < 255) { + len = (bytes[0] - 192 << 8) + bytes[1] + 192; + offset = 2; + } else if (type === 255) { + len = _util2.default.readNumber(bytes.subarray(1, 1 + 4)); + offset = 5; + } + + return { + len: len, + offset: offset + }; + }, + + /** + * Encodes a given integer of length to the openpgp length specifier to a + * string + * + * @param {Integer} length The length to encode + * @returns {Uint8Array} String with openpgp length representation + */ + writeSimpleLength: function writeSimpleLength(length) { + if (length < 192) { + return new Uint8Array([length]); + } else if (length > 191 && length < 8384) { + /* + * let a = (total data packet length) - 192 let bc = two octet + * representation of a let d = b + 192 + */ + return new Uint8Array([(length - 192 >> 8) + 192, length - 192 & 0xFF]); + } + return _util2.default.concatUint8Array([new Uint8Array([255]), _util2.default.writeNumber(length, 4)]); + }, + + writePartialLength: function writePartialLength(power) { + if (power < 0 || power > 30) { + throw new Error('Partial Length power must be between 1 and 30'); + } + return new Uint8Array([224 + power]); + }, + + writeTag: function writeTag(tag_type) { + /* we're only generating v4 packet headers here */ + return new Uint8Array([0xC0 | tag_type]); + }, + + /** + * Writes a packet header version 4 with the given tag_type and length to a + * string + * + * @param {Integer} tag_type Tag type + * @param {Integer} length Length of the payload + * @returns {String} String of the header + */ + writeHeader: function writeHeader(tag_type, length) { + /* we're only generating v4 packet headers here */ + return _util2.default.concatUint8Array([this.writeTag(tag_type), this.writeSimpleLength(length)]); + }, + + /** + * Writes a packet header Version 3 with the given tag_type and length to a + * string + * + * @param {Integer} tag_type Tag type + * @param {Integer} length Length of the payload + * @returns {String} String of the header + */ + writeOldHeader: function writeOldHeader(tag_type, length) { + if (length < 256) { + return new Uint8Array([0x80 | tag_type << 2, length]); + } else if (length < 65536) { + return _util2.default.concatUint8Array([new Uint8Array([0x80 | tag_type << 2 | 1]), _util2.default.writeNumber(length, 2)]); + } + return _util2.default.concatUint8Array([new Uint8Array([0x80 | tag_type << 2 | 2]), _util2.default.writeNumber(length, 4)]); + }, + + /** + * Whether the packet type supports partial lengths per RFC4880 + * @param {Integer} tag_type Tag type + * @returns {Boolean} String of the header + */ + supportsStreaming: function supportsStreaming(tag_type) { + return [_enums2.default.packet.literal, _enums2.default.packet.compressed, _enums2.default.packet.symmetricallyEncrypted, _enums2.default.packet.symEncryptedIntegrityProtected, _enums2.default.packet.symEncryptedAEADProtected].includes(tag_type); + }, + + /** + * Generic static Packet Parser function + * + * @param {Uint8Array | ReadableStream} input Input stream as string + * @param {Function} callback Function to call with the parsed packet + * @returns {Boolean} Returns false if the stream was empty and parsing is done, and true otherwise. + */ + read: async function read(input, streaming, callback) { + const reader = _webStreamTools2.default.getReader(input); + let writer; + try { + const peekedBytes = await reader.peekBytes(2); + // some sanity checks + if (!peekedBytes || peekedBytes.length < 2 || (peekedBytes[0] & 0x80) === 0) { + throw new Error("Error during parsing. This message / key probably does not conform to a valid OpenPGP format."); + } + const headerByte = await reader.readByte(); + let tag = -1; + let format = -1; + let packet_length; + + format = 0; // 0 = old format; 1 = new format + if ((headerByte & 0x40) !== 0) { + format = 1; + } + + let packet_length_type; + if (format) { + // new format header + tag = headerByte & 0x3F; // bit 5-0 + } else { + // old format header + tag = (headerByte & 0x3F) >> 2; // bit 5-2 + packet_length_type = headerByte & 0x03; // bit 1-0 + } + + const supportsStreaming = this.supportsStreaming(tag); + let packet = null; + let callbackReturned; + if (streaming && supportsStreaming) { + const transform = new TransformStream(); + writer = _webStreamTools2.default.getWriter(transform.writable); + packet = transform.readable; + callbackReturned = callback({ tag, packet }); + } else { + packet = []; + } + + let wasPartialLength; + do { + if (!format) { + // 4.2.1. Old Format Packet Lengths + switch (packet_length_type) { + case 0: + // The packet has a one-octet length. The header is 2 octets + // long. + packet_length = await reader.readByte(); + break; + case 1: + // The packet has a two-octet length. The header is 3 octets + // long. + packet_length = (await reader.readByte()) << 8 | (await reader.readByte()); + break; + case 2: + // The packet has a four-octet length. The header is 5 + // octets long. + packet_length = (await reader.readByte()) << 24 | (await reader.readByte()) << 16 | (await reader.readByte()) << 8 | (await reader.readByte()); + break; + default: + // 3 - The packet is of indeterminate length. The header is 1 + // octet long, and the implementation must determine how long + // the packet is. If the packet is in a file, this means that + // the packet extends until the end of the file. In general, + // an implementation SHOULD NOT use indeterminate-length + // packets except where the end of the data will be clear + // from the context, and even then it is better to use a + // definite length, or a new format header. The new format + // headers described below have a mechanism for precisely + // encoding data of indeterminate length. + packet_length = Infinity; + break; + } + } else { + // 4.2.2. New Format Packet Lengths + // 4.2.2.1. One-Octet Lengths + const lengthByte = await reader.readByte(); + wasPartialLength = false; + if (lengthByte < 192) { + packet_length = lengthByte; + // 4.2.2.2. Two-Octet Lengths + } else if (lengthByte >= 192 && lengthByte < 224) { + packet_length = (lengthByte - 192 << 8) + (await reader.readByte()) + 192; + // 4.2.2.4. Partial Body Lengths + } else if (lengthByte > 223 && lengthByte < 255) { + packet_length = 1 << (lengthByte & 0x1F); + wasPartialLength = true; + if (!supportsStreaming) { + throw new TypeError('This packet type does not support partial lengths.'); + } + // 4.2.2.3. Five-Octet Lengths + } else { + packet_length = (await reader.readByte()) << 24 | (await reader.readByte()) << 16 | (await reader.readByte()) << 8 | (await reader.readByte()); + } + } + if (packet_length > 0) { + let bytesRead = 0; + while (true) { + if (writer) await writer.ready; + + var _ref = await reader.read(); + + const done = _ref.done, + value = _ref.value; + + if (done) { + if (packet_length === Infinity) break; + throw new Error('Unexpected end of packet'); + } + const chunk = packet_length === Infinity ? value : value.subarray(0, packet_length - bytesRead); + if (writer) await writer.write(chunk);else packet.push(chunk); + bytesRead += value.length; + if (bytesRead >= packet_length) { + reader.unshift(value.subarray(packet_length - bytesRead + value.length)); + break; + } + } + } + } while (wasPartialLength); + + if (!writer) { + packet = _util2.default.concatUint8Array(packet); + await callback({ tag, packet }); + } + const nextPacket = await reader.peekBytes(2); + if (writer) { + await writer.ready; + await writer.close(); + await callbackReturned; + } + return !nextPacket || !nextPacket.length; + } catch (e) { + if (writer) { + await writer.abort(e); + return true; + } else { + throw e; + } + } finally { + reader.releaseLock(); + } + } +}; + +},{"../enums":114,"../util":153,"web-stream-tools":76}],131:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _webStreamTools = require('web-stream-tools'); + +var _webStreamTools2 = _interopRequireDefault(_webStreamTools); + +var _all_packets = require('./all_packets'); + +var packets = _interopRequireWildcard(_all_packets); + +var _packet = require('./packet'); + +var _packet2 = _interopRequireDefault(_packet); + +var _config = require('../config'); + +var _config2 = _interopRequireDefault(_config); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * This class represents a list of openpgp packets. + * Take care when iterating over it - the packets themselves + * are stored as numerical indices. + * @memberof module:packet + * @constructor + * @extends Array + */ +/* eslint-disable callback-return */ +/** + * @requires web-stream-tools + * @requires packet/all_packets + * @requires packet/packet + * @requires config + * @requires enums + * @requires util + */ + +function List() { + /** + * The number of packets contained within the list. + * @readonly + * @type {Integer} + */ + this.length = 0; +} + +List.prototype = []; + +/** + * Reads a stream of binary data and interprents it as a list of packets. + * @param {Uint8Array | ReadableStream} A Uint8Array of bytes. + */ +List.prototype.read = async function (bytes, streaming) { + this.stream = _webStreamTools2.default.transformPair(bytes, async (readable, writable) => { + const writer = _webStreamTools2.default.getWriter(writable); + try { + while (true) { + await writer.ready; + const done = await _packet2.default.read(readable, streaming, async parsed => { + try { + const tag = _enums2.default.read(_enums2.default.packet, parsed.tag); + const packet = packets.newPacketFromTag(tag); + packet.packets = new List(); + packet.fromStream = _util2.default.isStream(parsed.packet); + await packet.read(parsed.packet, streaming); + await writer.write(packet); + } catch (e) { + if (!_config2.default.tolerant || _packet2.default.supportsStreaming(parsed.tag)) { + // The packets that support streaming are the ones that contain + // message data. Those are also the ones we want to be more strict + // about and throw on parse errors for. + await writer.abort(e); + } + _util2.default.print_debug_error(e); + } + }); + if (done) { + await writer.ready; + await writer.close(); + return; + } + } + } catch (e) { + await writer.abort(e); + } + }); + + // Wait until first few packets have been read + const reader = _webStreamTools2.default.getReader(this.stream); + while (true) { + var _ref = await reader.read(); + + const done = _ref.done, + value = _ref.value; + + if (!done) { + this.push(value); + } else { + this.stream = null; + } + if (done || _packet2.default.supportsStreaming(value.tag)) { + break; + } + } + reader.releaseLock(); +}; + +/** + * Creates a binary representation of openpgp objects contained within the + * class instance. + * @returns {Uint8Array} A Uint8Array containing valid openpgp packets. + */ +List.prototype.write = function () { + const arr = []; + + for (let i = 0; i < this.length; i++) { + const packetbytes = this[i].write(); + if (_util2.default.isStream(packetbytes) && _packet2.default.supportsStreaming(this[i].tag)) { + let buffer = []; + let bufferLength = 0; + const minLength = 512; + arr.push(_packet2.default.writeTag(this[i].tag)); + arr.push(_webStreamTools2.default.transform(packetbytes, value => { + buffer.push(value); + bufferLength += value.length; + if (bufferLength >= minLength) { + const powerOf2 = Math.min(Math.log(bufferLength) / Math.LN2 | 0, 30); + const chunkSize = 2 ** powerOf2; + const bufferConcat = _util2.default.concat([_packet2.default.writePartialLength(powerOf2)].concat(buffer)); + buffer = [bufferConcat.subarray(1 + chunkSize)]; + bufferLength = buffer[0].length; + return bufferConcat.subarray(0, 1 + chunkSize); + } + }, () => _util2.default.concat([_packet2.default.writeSimpleLength(bufferLength)].concat(buffer)))); + } else { + if (_util2.default.isStream(packetbytes)) { + let length = 0; + arr.push(_webStreamTools2.default.transform(_webStreamTools2.default.clone(packetbytes), value => { + length += value.length; + }, () => _packet2.default.writeHeader(this[i].tag, length))); + } else { + arr.push(_packet2.default.writeHeader(this[i].tag, packetbytes.length)); + } + arr.push(packetbytes); + } + } + + return _util2.default.concat(arr); +}; + +/** + * Adds a packet to the list. This is the only supported method of doing so; + * writing to packetlist[i] directly will result in an error. + * @param {Object} packet Packet to push + */ +List.prototype.push = function (packet) { + if (!packet) { + return; + } + + packet.packets = packet.packets || new List(); + + this[this.length] = packet; + this.length++; +}; + +/** + * Creates a new PacketList with all packets from the given types + */ +List.prototype.filterByTag = function (...args) { + const filtered = new List(); + + const handle = tag => packetType => tag === packetType; + + for (let i = 0; i < this.length; i++) { + if (args.some(handle(this[i].tag))) { + filtered.push(this[i]); + } + } + + return filtered; +}; + +/** + * Traverses packet tree and returns first matching packet + * @param {module:enums.packet} type The packet type + * @returns {module:packet/packet|undefined} + */ +List.prototype.findPacket = function (type) { + return this.find(packet => packet.tag === type); +}; + +/** + * Returns array of found indices by tag + */ +List.prototype.indexOfTag = function (...args) { + const tagIndex = []; + const that = this; + + const handle = tag => packetType => tag === packetType; + + for (let i = 0; i < this.length; i++) { + if (args.some(handle(that[i].tag))) { + tagIndex.push(i); + } + } + return tagIndex; +}; + +/** + * Concatenates packetlist or array of packets + */ +List.prototype.concat = function (packetlist) { + if (packetlist) { + for (let i = 0; i < packetlist.length; i++) { + this.push(packetlist[i]); + } + } + return this; +}; + +/** + * Allocate a new packetlist from structured packetlist clone + * See {@link https://w3c.github.io/html/infrastructure.html#safe-passing-of-structured-data} + * @param {Object} packetClone packetlist clone + * @returns {Object} new packetlist object with data from packetlist clone + */ +List.fromStructuredClone = function (packetlistClone) { + const packetlist = new List(); + for (let i = 0; i < packetlistClone.length; i++) { + const packet = packets.fromStructuredClone(packetlistClone[i]); + packetlist.push(packet); + if (packet.embeddedSignature) { + packet.embeddedSignature = packets.fromStructuredClone(packet.embeddedSignature); + } + if (packet.packets.length !== 0) { + packet.packets = this.fromStructuredClone(packet.packets); + } else { + packet.packets = new List(); + } + } + if (packetlistClone.stream) { + packetlist.stream = _webStreamTools2.default.transform(packetlistClone.stream, packet => packets.fromStructuredClone(packet)); + } + return packetlist; +}; + +exports.default = List; + +},{"../config":80,"../enums":114,"../util":153,"./all_packets":123,"./packet":130,"web-stream-tools":76}],132:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _sha = require('asmcrypto.js/dist_es5/hash/sha1/sha1'); + +var _sha2 = require('asmcrypto.js/dist_es5/hash/sha256/sha256'); + +var _keyid = require('../type/keyid'); + +var _keyid2 = _interopRequireDefault(_keyid); + +var _mpi = require('../type/mpi'); + +var _mpi2 = _interopRequireDefault(_mpi); + +var _config = require('../config'); + +var _config2 = _interopRequireDefault(_config); + +var _crypto = require('../crypto'); + +var _crypto2 = _interopRequireDefault(_crypto); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Implementation of the Key Material Packet (Tag 5,6,7,14) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.5|RFC4480 5.5}: + * A key material packet contains all the information about a public or + * private key. There are four variants of this packet type, and two + * major versions. + * + * A Public-Key packet starts a series of packets that forms an OpenPGP + * key (sometimes called an OpenPGP certificate). + * @memberof module:packet + * @constructor + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires type/keyid + * @requires type/mpi + * @requires config + * @requires crypto + * @requires enums + * @requires util + */ + +function PublicKey(date = new Date()) { + /** + * Packet type + * @type {module:enums.packet} + */ + this.tag = _enums2.default.packet.publicKey; + /** + * Packet version + * @type {Integer} + */ + this.version = _config2.default.aead_protect && _config2.default.aead_protect_version === 4 ? 5 : 4; + /** + * Key creation date. + * @type {Date} + */ + this.created = _util2.default.normalizeDate(date); + /** + * Public key algorithm. + * @type {String} + */ + this.algorithm = null; + /** + * Algorithm specific params + * @type {Array} + */ + this.params = []; + /** + * Time until expiration in days (V3 only) + * @type {Integer} + */ + this.expirationTimeV3 = 0; + /** + * Fingerprint in lowercase hex + * @type {String} + */ + this.fingerprint = null; + /** + * Keyid + * @type {module:type/keyid} + */ + this.keyid = null; +} + +/** + * Internal Parser for public keys as specified in {@link https://tools.ietf.org/html/rfc4880#section-5.5.2|RFC 4880 section 5.5.2 Public-Key Packet Formats} + * called by read_tag<num> + * @param {Uint8Array} bytes Input array to read the packet from + * @returns {Object} This object with attributes set by the parser + */ +PublicKey.prototype.read = function (bytes) { + let pos = 0; + // A one-octet version number (3, 4 or 5). + this.version = bytes[pos++]; + + if (this.version === 4 || this.version === 5) { + // - A four-octet number denoting the time that the key was created. + this.created = _util2.default.readDate(bytes.subarray(pos, pos + 4)); + pos += 4; + + // - A one-octet number denoting the public-key algorithm of this key. + this.algorithm = _enums2.default.read(_enums2.default.publicKey, bytes[pos++]); + const algo = _enums2.default.write(_enums2.default.publicKey, this.algorithm); + + if (this.version === 5) { + // - A four-octet scalar octet count for the following key material. + pos += 4; + } + + // - A series of values comprising the key material. This is + // algorithm-specific and described in section XXXX. + const types = _crypto2.default.getPubKeyParamTypes(algo); + this.params = _crypto2.default.constructParams(types); + + for (let i = 0; i < types.length && pos < bytes.length; i++) { + pos += this.params[i].read(bytes.subarray(pos, bytes.length)); + if (pos > bytes.length) { + throw new Error('Error reading MPI @:' + pos); + } + } + + return pos; + } + throw new Error('Version ' + this.version + ' of the key packet is unsupported.'); +}; + +/** + * Alias of read() + * @see module:packet.PublicKey#read + */ +PublicKey.prototype.readPublicKey = PublicKey.prototype.read; + +/** + * Same as write_private_key, but has less information because of + * public key. + * @returns {Uint8Array} OpenPGP packet body contents, + */ +PublicKey.prototype.write = function () { + const arr = []; + // Version + arr.push(new Uint8Array([this.version])); + arr.push(_util2.default.writeDate(this.created)); + // A one-octet number denoting the public-key algorithm of this key + const algo = _enums2.default.write(_enums2.default.publicKey, this.algorithm); + arr.push(new Uint8Array([algo])); + + const paramCount = _crypto2.default.getPubKeyParamTypes(algo).length; + const params = _util2.default.concatUint8Array(this.params.slice(0, paramCount).map(param => param.write())); + if (this.version === 5) { + // A four-octet scalar octet count for the following key material + arr.push(_util2.default.writeNumber(params.length, 4)); + } + // Algorithm-specific params + arr.push(params); + return _util2.default.concatUint8Array(arr); +}; + +/** + * Alias of write() + * @see module:packet.PublicKey#write + */ +PublicKey.prototype.writePublicKey = PublicKey.prototype.write; + +/** + * Write an old version packet - it's used by some of the internal routines. + */ +PublicKey.prototype.writeOld = function () { + const bytes = this.writePublicKey(); + + return _util2.default.concatUint8Array([new Uint8Array([0x99]), _util2.default.writeNumber(bytes.length, 2), bytes]); +}; + +/** + * Check whether secret-key data is available in decrypted form. Returns null for public keys. + * @returns {Boolean|null} + */ +PublicKey.prototype.isDecrypted = function () { + return null; +}; + +/** + * Returns the creation time of the key + * @returns {Date} + */ +PublicKey.prototype.getCreationTime = function () { + return this.created; +}; + +/** + * Calculates the key id of the key + * @returns {String} A 8 byte key id + */ +PublicKey.prototype.getKeyId = function () { + if (this.keyid) { + return this.keyid; + } + this.keyid = new _keyid2.default(); + if (this.version === 5) { + this.keyid.read(_util2.default.hex_to_Uint8Array(this.getFingerprint()).subarray(0, 8)); + } else if (this.version === 4) { + this.keyid.read(_util2.default.hex_to_Uint8Array(this.getFingerprint()).subarray(12, 20)); + } + return this.keyid; +}; + +/** + * Calculates the fingerprint of the key + * @returns {Uint8Array} A Uint8Array containing the fingerprint + */ +PublicKey.prototype.getFingerprintBytes = function () { + if (this.fingerprint) { + return this.fingerprint; + } + let toHash; + if (this.version === 5) { + const bytes = this.writePublicKey(); + toHash = _util2.default.concatUint8Array([new Uint8Array([0x9A]), _util2.default.writeNumber(bytes.length, 4), bytes]); + this.fingerprint = _sha2.Sha256.bytes(toHash); + } else if (this.version === 4) { + toHash = this.writeOld(); + this.fingerprint = _sha.Sha1.bytes(toHash); + } + return this.fingerprint; +}; + +/** + * Calculates the fingerprint of the key + * @returns {String} A string containing the fingerprint in lowercase hex + */ +PublicKey.prototype.getFingerprint = function () { + return _util2.default.Uint8Array_to_hex(this.getFingerprintBytes()); +}; + +/** + * Calculates whether two keys have the same fingerprint without actually calculating the fingerprint + * @returns {Boolean} Whether the two keys have the same version and public key data + */ +PublicKey.prototype.hasSameFingerprintAs = function (other) { + return this.version === other.version && _util2.default.equalsUint8Array(this.writePublicKey(), other.writePublicKey()); +}; + +/** + * Returns algorithm information + * @returns {Object} An object of the form {algorithm: String, bits:int, curve:String} + */ +PublicKey.prototype.getAlgorithmInfo = function () { + const result = {}; + result.algorithm = this.algorithm; + if (this.params[0] instanceof _mpi2.default) { + result.bits = this.params[0].byteLength() * 8; + } else { + result.curve = this.params[0].getName(); + } + return result; +}; + +/** + * Fix custom types after cloning + */ +PublicKey.prototype.postCloneTypeFix = function () { + const algo = _enums2.default.write(_enums2.default.publicKey, this.algorithm); + const types = _crypto2.default.getPubKeyParamTypes(algo); + for (let i = 0; i < types.length; i++) { + const param = this.params[i]; + this.params[i] = types[i].fromClone(param); + } + if (this.keyid) { + this.keyid = _keyid2.default.fromClone(this.keyid); + } +}; + +exports.default = PublicKey; + +},{"../config":80,"../crypto":95,"../enums":114,"../type/keyid":149,"../type/mpi":150,"../util":153,"asmcrypto.js/dist_es5/hash/sha1/sha1":12,"asmcrypto.js/dist_es5/hash/sha256/sha256":14}],133:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _keyid = require('../type/keyid'); + +var _keyid2 = _interopRequireDefault(_keyid); + +var _mpi = require('../type/mpi'); + +var _mpi2 = _interopRequireDefault(_mpi); + +var _crypto = require('../crypto'); + +var _crypto2 = _interopRequireDefault(_crypto); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Public-Key Encrypted Session Key Packets (Tag 1) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.1|RFC4880 5.1}: + * A Public-Key Encrypted Session Key packet holds the session key + * used to encrypt a message. Zero or more Public-Key Encrypted Session Key + * packets and/or Symmetric-Key Encrypted Session Key packets may precede a + * Symmetrically Encrypted Data Packet, which holds an encrypted message. The + * message is encrypted with the session key, and the session key is itself + * encrypted and stored in the Encrypted Session Key packet(s). The + * Symmetrically Encrypted Data Packet is preceded by one Public-Key Encrypted + * Session Key packet for each OpenPGP key to which the message is encrypted. + * The recipient of the message finds a session key that is encrypted to their + * public key, decrypts the session key, and then uses the session key to + * decrypt the message. + * @memberof module:packet + * @constructor + */ +function PublicKeyEncryptedSessionKey() { + this.tag = _enums2.default.packet.publicKeyEncryptedSessionKey; + this.version = 3; + + this.publicKeyId = new _keyid2.default(); + this.publicKeyAlgorithm = null; + + this.sessionKey = null; + this.sessionKeyAlgorithm = null; + + /** @type {Array} */ + this.encrypted = []; +} + +/** + * Parsing function for a publickey encrypted session key packet (tag 1). + * + * @param {Uint8Array} input Payload of a tag 1 packet + * @param {Integer} position Position to start reading from the input string + * @param {Integer} len Length of the packet or the remaining length of + * input at position + * @returns {module:packet.PublicKeyEncryptedSessionKey} Object representation + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires type/keyid + * @requires type/mpi + * @requires crypto + * @requires enums + * @requires util + */ + +PublicKeyEncryptedSessionKey.prototype.read = function (bytes) { + this.version = bytes[0]; + this.publicKeyId.read(bytes.subarray(1, bytes.length)); + this.publicKeyAlgorithm = _enums2.default.read(_enums2.default.publicKey, bytes[9]); + + let i = 10; + + const algo = _enums2.default.write(_enums2.default.publicKey, this.publicKeyAlgorithm); + const types = _crypto2.default.getEncSessionKeyParamTypes(algo); + this.encrypted = _crypto2.default.constructParams(types); + + for (let j = 0; j < types.length; j++) { + i += this.encrypted[j].read(bytes.subarray(i, bytes.length)); + } +}; + +/** + * Create a string representation of a tag 1 packet + * + * @returns {Uint8Array} The Uint8Array representation + */ +PublicKeyEncryptedSessionKey.prototype.write = function () { + const arr = [new Uint8Array([this.version]), this.publicKeyId.write(), new Uint8Array([_enums2.default.write(_enums2.default.publicKey, this.publicKeyAlgorithm)])]; + + for (let i = 0; i < this.encrypted.length; i++) { + arr.push(this.encrypted[i].write()); + } + + return _util2.default.concatUint8Array(arr); +}; + +/** + * Encrypt session key packet + * @param {module:packet.PublicKey} key Public key + * @returns {Promise} + * @async + */ +PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) { + let data = String.fromCharCode(_enums2.default.write(_enums2.default.symmetric, this.sessionKeyAlgorithm)); + + data += _util2.default.Uint8Array_to_str(this.sessionKey); + data += _util2.default.Uint8Array_to_str(_util2.default.write_checksum(this.sessionKey)); + + let toEncrypt; + const algo = _enums2.default.write(_enums2.default.publicKey, this.publicKeyAlgorithm); + if (algo === _enums2.default.publicKey.ecdh) { + toEncrypt = new _mpi2.default(_crypto2.default.pkcs5.encode(data)); + } else { + toEncrypt = new _mpi2.default((await _crypto2.default.pkcs1.eme.encode(data, key.params[0].byteLength()))); + } + + this.encrypted = await _crypto2.default.publicKeyEncrypt(algo, key.params, toEncrypt, key.getFingerprintBytes()); + return true; +}; + +/** + * Decrypts the session key (only for public key encrypted session key + * packets (tag 1) + * + * @param {module:packet.SecretKey} key + * Private key with secret params unlocked + * @returns {Promise} + * @async + */ +PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) { + const algo = _enums2.default.write(_enums2.default.publicKey, this.publicKeyAlgorithm); + const result = new _mpi2.default((await _crypto2.default.publicKeyDecrypt(algo, key.params, this.encrypted, key.getFingerprintBytes()))); + + let checksum; + let decoded; + if (algo === _enums2.default.publicKey.ecdh) { + decoded = _crypto2.default.pkcs5.decode(result.toString()); + checksum = _util2.default.str_to_Uint8Array(decoded.substr(decoded.length - 2)); + } else { + decoded = _crypto2.default.pkcs1.eme.decode(result.toString()); + checksum = result.toUint8Array().slice(result.byteLength() - 2); + } + + key = _util2.default.str_to_Uint8Array(decoded.substring(1, decoded.length - 2)); + + if (!_util2.default.equalsUint8Array(checksum, _util2.default.write_checksum(key))) { + throw new Error('Decryption error'); + } else { + this.sessionKey = key; + this.sessionKeyAlgorithm = _enums2.default.read(_enums2.default.symmetric, decoded.charCodeAt(0)); + } + return true; +}; + +/** + * Fix custom types after cloning + */ +PublicKeyEncryptedSessionKey.prototype.postCloneTypeFix = function () { + this.publicKeyId = _keyid2.default.fromClone(this.publicKeyId); + const algo = _enums2.default.write(_enums2.default.publicKey, this.publicKeyAlgorithm); + const types = _crypto2.default.getEncSessionKeyParamTypes(algo); + for (let i = 0; i < this.encrypted.length; i++) { + this.encrypted[i] = types[i].fromClone(this.encrypted[i]); + } +}; + +exports.default = PublicKeyEncryptedSessionKey; + +},{"../crypto":95,"../enums":114,"../type/keyid":149,"../type/mpi":150,"../util":153}],134:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _public_key = require('./public_key'); + +var _public_key2 = _interopRequireDefault(_public_key); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * A Public-Subkey packet (tag 14) has exactly the same format as a + * Public-Key packet, but denotes a subkey. One or more subkeys may be + * associated with a top-level key. By convention, the top-level key + * provides signature services, and the subkeys provide encryption + * services. + * @memberof module:packet + * @constructor + * @extends module:packet.PublicKey + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires packet/public_key + * @requires enums + */ + +function PublicSubkey() { + _public_key2.default.call(this); + this.tag = _enums2.default.packet.publicSubkey; +} + +PublicSubkey.prototype = new _public_key2.default(); +PublicSubkey.prototype.constructor = PublicSubkey; + +exports.default = PublicSubkey; + +},{"../enums":114,"./public_key":132}],135:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _public_key = require('./public_key'); + +var _public_key2 = _interopRequireDefault(_public_key); + +var _keyid = require('../type/keyid.js'); + +var _keyid2 = _interopRequireDefault(_keyid); + +var _s2k = require('../type/s2k'); + +var _s2k2 = _interopRequireDefault(_s2k); + +var _crypto = require('../crypto'); + +var _crypto2 = _interopRequireDefault(_crypto); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * A Secret-Key packet contains all the information that is found in a + * Public-Key packet, including the public-key material, but also + * includes the secret-key material after all the public-key fields. + * @memberof module:packet + * @constructor + * @extends module:packet.PublicKey + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires packet/public_key + * @requires type/keyid + * @requires type/s2k + * @requires crypto + * @requires enums + * @requires util + */ + +function SecretKey(date = new Date()) { + _public_key2.default.call(this, date); + /** + * Packet type + * @type {module:enums.packet} + */ + this.tag = _enums2.default.packet.secretKey; + /** + * Encrypted secret-key data + */ + this.encrypted = null; + /** + * Indicator if secret-key data is encrypted. `this.isEncrypted === false` means data is available in decrypted form. + */ + this.isEncrypted = null; +} + +SecretKey.prototype = new _public_key2.default(); +SecretKey.prototype.constructor = SecretKey; + +// Helper function + +function parse_cleartext_params(cleartext, algorithm) { + const algo = _enums2.default.write(_enums2.default.publicKey, algorithm); + const types = _crypto2.default.getPrivKeyParamTypes(algo); + const params = _crypto2.default.constructParams(types); + let p = 0; + + for (let i = 0; i < types.length && p < cleartext.length; i++) { + p += params[i].read(cleartext.subarray(p, cleartext.length)); + if (p > cleartext.length) { + throw new Error('Error reading param @:' + p); + } + } + + return params; +} + +function write_cleartext_params(params, algorithm) { + const arr = []; + const algo = _enums2.default.write(_enums2.default.publicKey, algorithm); + const numPublicParams = _crypto2.default.getPubKeyParamTypes(algo).length; + + for (let i = numPublicParams; i < params.length; i++) { + arr.push(params[i].write()); + } + + return _util2.default.concatUint8Array(arr); +} + +// 5.5.3. Secret-Key Packet Formats + +/** + * Internal parser for private keys as specified in + * {@link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-04#section-5.5.3|RFC4880bis-04 section 5.5.3} + * @param {String} bytes Input string to read the packet from + */ +SecretKey.prototype.read = function (bytes) { + // - A Public-Key or Public-Subkey packet, as described above. + const len = this.readPublicKey(bytes); + + bytes = bytes.subarray(len, bytes.length); + + // - One octet indicating string-to-key usage conventions. Zero + // indicates that the secret-key data is not encrypted. 255 or 254 + // indicates that a string-to-key specifier is being given. Any + // other value is a symmetric-key encryption algorithm identifier. + const isEncrypted = bytes[0]; + + if (isEncrypted) { + this.encrypted = bytes; + this.isEncrypted = true; + } else { + // - Plain or encrypted multiprecision integers comprising the secret + // key data. These algorithm-specific fields are as described + // below. + const cleartext = bytes.subarray(1, -2); + if (!_util2.default.equalsUint8Array(_util2.default.write_checksum(cleartext), bytes.subarray(-2))) { + throw new Error('Key checksum mismatch'); + } + const privParams = parse_cleartext_params(cleartext, this.algorithm); + this.params = this.params.concat(privParams); + this.isEncrypted = false; + } +}; + +/** + * Creates an OpenPGP key packet for the given key. + * @returns {String} A string of bytes containing the secret key OpenPGP packet + */ +SecretKey.prototype.write = function () { + const arr = [this.writePublicKey()]; + + if (!this.encrypted) { + arr.push(new Uint8Array([0])); + const cleartextParams = write_cleartext_params(this.params, this.algorithm); + arr.push(cleartextParams); + arr.push(_util2.default.write_checksum(cleartextParams)); + } else { + arr.push(this.encrypted); + } + + return _util2.default.concatUint8Array(arr); +}; + +/** + * Check whether secret-key data is available in decrypted form. Returns null for public keys. + * @returns {Boolean|null} + */ +SecretKey.prototype.isDecrypted = function () { + return this.isEncrypted === false; +}; + +/** + * Encrypt the payload. By default, we use aes256 and iterated, salted string + * to key specifier. If the key is in a decrypted state (isEncrypted === false) + * and the passphrase is empty or undefined, the key will be set as not encrypted. + * This can be used to remove passphrase protection after calling decrypt(). + * @param {String} passphrase + * @returns {Promise} + * @async + */ +SecretKey.prototype.encrypt = async function (passphrase) { + if (this.isDecrypted() && this.encrypted) { + // gnu-dummy + this.isEncrypted = true; + return false; + } + + if (this.isDecrypted() && !passphrase) { + this.encrypted = null; + return false; + } else if (!passphrase) { + throw new Error('The key must be decrypted before removing passphrase protection.'); + } + + const s2k = new _s2k2.default(); + s2k.salt = await _crypto2.default.random.getRandomBytes(8); + const symmetric = 'aes256'; + const cleartext = write_cleartext_params(this.params, this.algorithm); + const key = await produceEncryptionKey(s2k, passphrase, symmetric); + const blockLen = _crypto2.default.cipher[symmetric].blockSize; + const iv = await _crypto2.default.random.getRandomBytes(blockLen); + + let arr; + + if (this.version === 5) { + const aead = 'eax'; + const optionalFields = _util2.default.concatUint8Array([new Uint8Array([_enums2.default.write(_enums2.default.symmetric, symmetric), _enums2.default.write(_enums2.default.aead, aead)]), s2k.write(), iv]); + arr = [new Uint8Array([253, optionalFields.length])]; + arr.push(optionalFields); + const mode = _crypto2.default[aead]; + const modeInstance = await mode(symmetric, key); + const encrypted = await modeInstance.encrypt(cleartext, iv.subarray(0, mode.ivLength), new Uint8Array()); + arr.push(_util2.default.writeNumber(encrypted.length, 4)); + arr.push(encrypted); + } else { + arr = [new Uint8Array([254, _enums2.default.write(_enums2.default.symmetric, symmetric)])]; + arr.push(s2k.write()); + arr.push(iv); + arr.push(_crypto2.default.cfb.encrypt(symmetric, key, _util2.default.concatUint8Array([cleartext, await _crypto2.default.hash.sha1(cleartext)]), iv)); + } + + this.encrypted = _util2.default.concatUint8Array(arr); + return true; +}; + +async function produceEncryptionKey(s2k, passphrase, algorithm) { + return s2k.produce_key(passphrase, _crypto2.default.cipher[algorithm].keySize); +} + +/** + * Decrypts the private key params which are needed to use the key. + * {@link module:packet.SecretKey.isDecrypted} should be false, as + * otherwise calls to this function will throw an error. + * @param {String} passphrase The passphrase for this private key as string + * @returns {Promise} + * @async + */ +SecretKey.prototype.decrypt = async function (passphrase) { + if (this.isDecrypted()) { + throw new Error('Key packet is already decrypted.'); + } + + let i = 0; + let symmetric; + let aead; + let key; + + const s2k_usage = this.encrypted[i++]; + + // - Only for a version 5 packet, a one-octet scalar octet count of + // the next 4 optional fields. + if (this.version === 5) { + i++; + } + + // - [Optional] If string-to-key usage octet was 255, 254, or 253, a + // one-octet symmetric encryption algorithm. + if (s2k_usage === 255 || s2k_usage === 254 || s2k_usage === 253) { + symmetric = this.encrypted[i++]; + symmetric = _enums2.default.read(_enums2.default.symmetric, symmetric); + + // - [Optional] If string-to-key usage octet was 253, a one-octet + // AEAD algorithm. + if (s2k_usage === 253) { + aead = this.encrypted[i++]; + aead = _enums2.default.read(_enums2.default.aead, aead); + } + + // - [Optional] If string-to-key usage octet was 255, 254, or 253, a + // string-to-key specifier. The length of the string-to-key + // specifier is implied by its type, as described above. + const s2k = new _s2k2.default(); + i += s2k.read(this.encrypted.subarray(i, this.encrypted.length)); + + if (s2k.type === 'gnu-dummy') { + this.isEncrypted = false; + return false; + } + key = await produceEncryptionKey(s2k, passphrase, symmetric); + } else { + symmetric = s2k_usage; + symmetric = _enums2.default.read(_enums2.default.symmetric, symmetric); + key = await _crypto2.default.hash.md5(passphrase); + } + + // - [Optional] If secret data is encrypted (string-to-key usage octet + // not zero), an Initial Vector (IV) of the same length as the + // cipher's block size. + const iv = this.encrypted.subarray(i, i + _crypto2.default.cipher[symmetric].blockSize); + + i += iv.length; + + // - Only for a version 5 packet, a four-octet scalar octet count for + // the following key material. + if (this.version === 5) { + i += 4; + } + + const ciphertext = this.encrypted.subarray(i, this.encrypted.length); + let cleartext; + if (aead) { + const mode = _crypto2.default[aead]; + try { + const modeInstance = await mode(symmetric, key); + cleartext = await modeInstance.decrypt(ciphertext, iv.subarray(0, mode.ivLength), new Uint8Array()); + } catch (err) { + if (err.message === 'Authentication tag mismatch') { + throw new Error('Incorrect key passphrase: ' + err.message); + } + } + } else { + const cleartextWithHash = await _crypto2.default.cfb.decrypt(symmetric, key, ciphertext, iv); + + let hash; + let hashlen; + if (s2k_usage === 255) { + hashlen = 2; + cleartext = cleartextWithHash.subarray(0, -hashlen); + hash = _util2.default.write_checksum(cleartext); + } else { + hashlen = 20; + cleartext = cleartextWithHash.subarray(0, -hashlen); + hash = await _crypto2.default.hash.sha1(cleartext); + } + + if (!_util2.default.equalsUint8Array(hash, cleartextWithHash.subarray(-hashlen))) { + throw new Error('Incorrect key passphrase'); + } + } + + const privParams = parse_cleartext_params(cleartext, this.algorithm); + this.params = this.params.concat(privParams); + this.isEncrypted = false; + this.encrypted = null; + + return true; +}; + +SecretKey.prototype.generate = async function (bits, curve) { + const algo = _enums2.default.write(_enums2.default.publicKey, this.algorithm); + this.params = await _crypto2.default.generateParams(algo, bits, curve); + this.isEncrypted = false; +}; + +/** + * Clear private params, return to initial state + */ +SecretKey.prototype.clearPrivateParams = function () { + if (!this.encrypted) { + throw new Error('If secret key is not encrypted, clearing private params is irreversible.'); + } + const algo = _enums2.default.write(_enums2.default.publicKey, this.algorithm); + this.params = this.params.slice(0, _crypto2.default.getPubKeyParamTypes(algo).length); + this.isEncrypted = true; +}; + +/** + * Fix custom types after cloning + */ +SecretKey.prototype.postCloneTypeFix = function () { + const algo = _enums2.default.write(_enums2.default.publicKey, this.algorithm); + const types = [].concat(_crypto2.default.getPubKeyParamTypes(algo), _crypto2.default.getPrivKeyParamTypes(algo)); + for (let i = 0; i < this.params.length; i++) { + const param = this.params[i]; + this.params[i] = types[i].fromClone(param); + } + if (this.keyid) { + this.keyid = _keyid2.default.fromClone(this.keyid); + } +}; + +exports.default = SecretKey; + +},{"../crypto":95,"../enums":114,"../type/keyid.js":149,"../type/s2k":152,"../util":153,"./public_key":132}],136:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _secret_key = require('./secret_key'); + +var _secret_key2 = _interopRequireDefault(_secret_key); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * A Secret-Subkey packet (tag 7) is the subkey analog of the Secret + * Key packet and has exactly the same format. + * @memberof module:packet + * @constructor + * @extends module:packet.SecretKey + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires packet/secret_key + * @requires enums + */ + +function SecretSubkey(date = new Date()) { + _secret_key2.default.call(this, date); + this.tag = _enums2.default.packet.secretSubkey; +} + +SecretSubkey.prototype = new _secret_key2.default(); +SecretSubkey.prototype.constructor = SecretSubkey; + +exports.default = SecretSubkey; + +},{"../enums":114,"./secret_key":135}],137:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _webStreamTools = require('web-stream-tools'); + +var _webStreamTools2 = _interopRequireDefault(_webStreamTools); + +var _packet = require('./packet'); + +var _packet2 = _interopRequireDefault(_packet); + +var _keyid = require('../type/keyid.js'); + +var _keyid2 = _interopRequireDefault(_keyid); + +var _mpi = require('../type/mpi.js'); + +var _mpi2 = _interopRequireDefault(_mpi); + +var _crypto = require('../crypto'); + +var _crypto2 = _interopRequireDefault(_crypto); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Implementation of the Signature Packet (Tag 2) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.2|RFC4480 5.2}: + * A Signature packet describes a binding between some public key and + * some data. The most common signatures are a signature of a file or a + * block of text, and a signature that is a certification of a User ID. + * @memberof module:packet + * @constructor + * @param {Date} date the creation date of the signature + */ +function Signature(date = new Date()) { + this.tag = _enums2.default.packet.signature; + this.version = 4; + this.signatureType = null; + this.hashAlgorithm = null; + this.publicKeyAlgorithm = null; + + this.signatureData = null; + this.unhashedSubpackets = []; + this.signedHashValue = null; + + this.created = _util2.default.normalizeDate(date); + this.signatureExpirationTime = null; + this.signatureNeverExpires = true; + this.exportable = null; + this.trustLevel = null; + this.trustAmount = null; + this.regularExpression = null; + this.revocable = null; + this.keyExpirationTime = null; + this.keyNeverExpires = null; + this.preferredSymmetricAlgorithms = null; + this.revocationKeyClass = null; + this.revocationKeyAlgorithm = null; + this.revocationKeyFingerprint = null; + this.issuerKeyId = new _keyid2.default(); + this.notations = []; + this.preferredHashAlgorithms = null; + this.preferredCompressionAlgorithms = null; + this.keyServerPreferences = null; + this.preferredKeyServer = null; + this.isPrimaryUserID = null; + this.policyURI = null; + this.keyFlags = null; + this.signersUserId = null; + this.reasonForRevocationFlag = null; + this.reasonForRevocationString = null; + this.features = null; + this.signatureTargetPublicKeyAlgorithm = null; + this.signatureTargetHashAlgorithm = null; + this.signatureTargetHash = null; + this.embeddedSignature = null; + this.issuerKeyVersion = null; + this.issuerFingerprint = null; + this.preferredAeadAlgorithms = null; + + this.verified = null; + this.revoked = null; +} + +/** + * parsing function for a signature packet (tag 2). + * @param {String} bytes payload of a tag 2 packet + * @param {Integer} position position to start reading from the bytes string + * @param {Integer} len length of the packet or the remaining length of bytes at position + * @returns {module:packet.Signature} object representation + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires web-stream-tools + * @requires packet/packet + * @requires type/keyid + * @requires type/mpi + * @requires crypto + * @requires enums + * @requires util + */ + +Signature.prototype.read = function (bytes) { + let i = 0; + this.version = bytes[i++]; + + if (this.version !== 4) { + throw new Error('Version ' + this.version + ' of the signature is unsupported.'); + } + + this.signatureType = bytes[i++]; + this.publicKeyAlgorithm = bytes[i++]; + this.hashAlgorithm = bytes[i++]; + + // hashed subpackets + i += this.read_sub_packets(bytes.subarray(i, bytes.length), true); + + // A V4 signature hashes the packet body + // starting from its first field, the version number, through the end + // of the hashed subpacket data. Thus, the fields hashed are the + // signature version, the signature type, the public-key algorithm, the + // hash algorithm, the hashed subpacket length, and the hashed + // subpacket body. + this.signatureData = bytes.subarray(0, i); + + // unhashed subpackets + i += this.read_sub_packets(bytes.subarray(i, bytes.length), false); + + // Two-octet field holding left 16 bits of signed hash value. + this.signedHashValue = bytes.subarray(i, i + 2); + i += 2; + + this.signature = bytes.subarray(i, bytes.length); +}; + +Signature.prototype.write = function () { + const arr = []; + arr.push(this.signatureData); + arr.push(this.write_unhashed_sub_packets()); + arr.push(this.signedHashValue); + arr.push(_webStreamTools2.default.clone(this.signature)); + return _util2.default.concat(arr); +}; + +/** + * Signs provided data. This needs to be done prior to serialization. + * @param {module:packet.SecretKey} key private key used to sign the message. + * @param {Object} data Contains packets to be signed. + * @returns {Promise} + * @async + */ +Signature.prototype.sign = async function (key, data) { + const signatureType = _enums2.default.write(_enums2.default.signature, this.signatureType); + const publicKeyAlgorithm = _enums2.default.write(_enums2.default.publicKey, this.publicKeyAlgorithm); + const hashAlgorithm = _enums2.default.write(_enums2.default.hash, this.hashAlgorithm); + + const arr = [new Uint8Array([4, signatureType, publicKeyAlgorithm, hashAlgorithm])]; + + if (key.version === 5) { + // We could also generate this subpacket for version 4 keys, but for + // now we don't. + this.issuerKeyVersion = key.version; + this.issuerFingerprint = key.getFingerprintBytes(); + } + + this.issuerKeyId = key.getKeyId(); + + // Add hashed subpackets + arr.push(this.write_hashed_sub_packets()); + + this.signatureData = _util2.default.concat(arr); + + const toHash = this.toHash(signatureType, data); + const hash = await this.hash(signatureType, data, toHash); + + this.signedHashValue = _webStreamTools2.default.slice(_webStreamTools2.default.clone(hash), 0, 2); + + const params = key.params; + this.signature = _webStreamTools2.default.fromAsync(async () => _crypto2.default.signature.sign(publicKeyAlgorithm, hashAlgorithm, params, toHash, (await _webStreamTools2.default.readToEnd(hash)))); + + // Store the fact that this signature is valid, e.g. for when we call `await + // getLatestValidSignature(this.revocationSignatures, key, data)` later. Note + // that this only holds up if the key and data passed to verify are the same + // as the ones passed to sign. + this.verified = true; + return true; +}; + +/** + * Creates Uint8Array of bytes of all subpacket data except Issuer and Embedded Signature subpackets + * @returns {Uint8Array} subpacket data + */ +Signature.prototype.write_hashed_sub_packets = function () { + const sub = _enums2.default.signatureSubpacket; + const arr = []; + let bytes; + if (this.created !== null) { + arr.push(write_sub_packet(sub.signature_creation_time, _util2.default.writeDate(this.created))); + } + if (this.signatureExpirationTime !== null) { + arr.push(write_sub_packet(sub.signature_expiration_time, _util2.default.writeNumber(this.signatureExpirationTime, 4))); + } + if (this.exportable !== null) { + arr.push(write_sub_packet(sub.exportable_certification, new Uint8Array([this.exportable ? 1 : 0]))); + } + if (this.trustLevel !== null) { + bytes = new Uint8Array([this.trustLevel, this.trustAmount]); + arr.push(write_sub_packet(sub.trust_signature, bytes)); + } + if (this.regularExpression !== null) { + arr.push(write_sub_packet(sub.regular_expression, this.regularExpression)); + } + if (this.revocable !== null) { + arr.push(write_sub_packet(sub.revocable, new Uint8Array([this.revocable ? 1 : 0]))); + } + if (this.keyExpirationTime !== null) { + arr.push(write_sub_packet(sub.key_expiration_time, _util2.default.writeNumber(this.keyExpirationTime, 4))); + } + if (this.preferredSymmetricAlgorithms !== null) { + bytes = _util2.default.str_to_Uint8Array(_util2.default.Uint8Array_to_str(this.preferredSymmetricAlgorithms)); + arr.push(write_sub_packet(sub.preferred_symmetric_algorithms, bytes)); + } + if (this.revocationKeyClass !== null) { + bytes = new Uint8Array([this.revocationKeyClass, this.revocationKeyAlgorithm]); + bytes = _util2.default.concat([bytes, this.revocationKeyFingerprint]); + arr.push(write_sub_packet(sub.revocation_key, bytes)); + } + this.notations.forEach(([name, value]) => { + bytes = [new Uint8Array([0x80, 0, 0, 0])]; + // 2 octets of name length + bytes.push(_util2.default.writeNumber(name.length, 2)); + // 2 octets of value length + bytes.push(_util2.default.writeNumber(value.length, 2)); + bytes.push(_util2.default.str_to_Uint8Array(name + value)); + bytes = _util2.default.concat(bytes); + arr.push(write_sub_packet(sub.notation_data, bytes)); + }); + if (this.preferredHashAlgorithms !== null) { + bytes = _util2.default.str_to_Uint8Array(_util2.default.Uint8Array_to_str(this.preferredHashAlgorithms)); + arr.push(write_sub_packet(sub.preferred_hash_algorithms, bytes)); + } + if (this.preferredCompressionAlgorithms !== null) { + bytes = _util2.default.str_to_Uint8Array(_util2.default.Uint8Array_to_str(this.preferredCompressionAlgorithms)); + arr.push(write_sub_packet(sub.preferred_compression_algorithms, bytes)); + } + if (this.keyServerPreferences !== null) { + bytes = _util2.default.str_to_Uint8Array(_util2.default.Uint8Array_to_str(this.keyServerPreferences)); + arr.push(write_sub_packet(sub.key_server_preferences, bytes)); + } + if (this.preferredKeyServer !== null) { + arr.push(write_sub_packet(sub.preferred_key_server, _util2.default.str_to_Uint8Array(this.preferredKeyServer))); + } + if (this.isPrimaryUserID !== null) { + arr.push(write_sub_packet(sub.primary_user_id, new Uint8Array([this.isPrimaryUserID ? 1 : 0]))); + } + if (this.policyURI !== null) { + arr.push(write_sub_packet(sub.policy_uri, _util2.default.str_to_Uint8Array(this.policyURI))); + } + if (this.keyFlags !== null) { + bytes = _util2.default.str_to_Uint8Array(_util2.default.Uint8Array_to_str(this.keyFlags)); + arr.push(write_sub_packet(sub.key_flags, bytes)); + } + if (this.signersUserId !== null) { + arr.push(write_sub_packet(sub.signers_user_id, _util2.default.str_to_Uint8Array(this.signersUserId))); + } + if (this.reasonForRevocationFlag !== null) { + bytes = _util2.default.str_to_Uint8Array(String.fromCharCode(this.reasonForRevocationFlag) + this.reasonForRevocationString); + arr.push(write_sub_packet(sub.reason_for_revocation, bytes)); + } + if (this.features !== null) { + bytes = _util2.default.str_to_Uint8Array(_util2.default.Uint8Array_to_str(this.features)); + arr.push(write_sub_packet(sub.features, bytes)); + } + if (this.signatureTargetPublicKeyAlgorithm !== null) { + bytes = [new Uint8Array([this.signatureTargetPublicKeyAlgorithm, this.signatureTargetHashAlgorithm])]; + bytes.push(_util2.default.str_to_Uint8Array(this.signatureTargetHash)); + bytes = _util2.default.concat(bytes); + arr.push(write_sub_packet(sub.signature_target, bytes)); + } + if (this.preferredAeadAlgorithms !== null) { + bytes = _util2.default.str_to_Uint8Array(_util2.default.Uint8Array_to_str(this.preferredAeadAlgorithms)); + arr.push(write_sub_packet(sub.preferred_aead_algorithms, bytes)); + } + + const result = _util2.default.concat(arr); + const length = _util2.default.writeNumber(result.length, 2); + + return _util2.default.concat([length, result]); +}; + +/** + * Creates Uint8Array of bytes of Issuer and Embedded Signature subpackets + * @returns {Uint8Array} subpacket data + */ +Signature.prototype.write_unhashed_sub_packets = function () { + const sub = _enums2.default.signatureSubpacket; + const arr = []; + let bytes; + if (!this.issuerKeyId.isNull() && this.issuerKeyVersion !== 5) { + // If the version of [the] key is greater than 4, this subpacket + // MUST NOT be included in the signature. + arr.push(write_sub_packet(sub.issuer, this.issuerKeyId.write())); + } + if (this.embeddedSignature !== null) { + arr.push(write_sub_packet(sub.embedded_signature, this.embeddedSignature.write())); + } + if (this.issuerFingerprint !== null) { + bytes = [new Uint8Array([this.issuerKeyVersion]), this.issuerFingerprint]; + bytes = _util2.default.concat(bytes); + arr.push(write_sub_packet(sub.issuer_fingerprint, bytes)); + } + this.unhashedSubpackets.forEach(data => { + arr.push(_packet2.default.writeSimpleLength(data.length)); + arr.push(data); + }); + + const result = _util2.default.concat(arr); + const length = _util2.default.writeNumber(result.length, 2); + + return _util2.default.concat([length, result]); +}; + +/** + * Creates a string representation of a sub signature packet + * @see {@link https://tools.ietf.org/html/rfc4880#section-5.2.3.1|RFC4880 5.2.3.1} + * @see {@link https://tools.ietf.org/html/rfc4880#section-5.2.3.2|RFC4880 5.2.3.2} + * @param {Integer} type subpacket signature type. + * @param {String} data data to be included + * @returns {String} a string-representation of a sub signature packet + * @private + */ +function write_sub_packet(type, data) { + const arr = []; + arr.push(_packet2.default.writeSimpleLength(data.length + 1)); + arr.push(new Uint8Array([type])); + arr.push(data); + return _util2.default.concat(arr); +} + +// V4 signature sub packets + +Signature.prototype.read_sub_packet = function (bytes, trusted = true) { + let mypos = 0; + + const read_array = (prop, bytes) => { + this[prop] = []; + + for (let i = 0; i < bytes.length; i++) { + this[prop].push(bytes[i]); + } + }; + + // The leftmost bit denotes a "critical" packet + const critical = bytes[mypos] & 0x80; + const type = bytes[mypos] & 0x7F; + + // GPG puts the Issuer and Signature subpackets in the unhashed area. + // Tampering with those invalidates the signature, so we can trust them. + // Ignore all other unhashed subpackets. + if (!trusted && ![_enums2.default.signatureSubpacket.issuer, _enums2.default.signatureSubpacket.issuer_fingerprint, _enums2.default.signatureSubpacket.embedded_signature].includes(type)) { + this.unhashedSubpackets.push(bytes.subarray(mypos, bytes.length)); + return; + } + + mypos++; + + // subpacket type + switch (type) { + case 2: + // Signature Creation Time + this.created = _util2.default.readDate(bytes.subarray(mypos, bytes.length)); + break; + case 3: + { + // Signature Expiration Time in seconds + const seconds = _util2.default.readNumber(bytes.subarray(mypos, bytes.length)); + + this.signatureNeverExpires = seconds === 0; + this.signatureExpirationTime = seconds; + + break; + } + case 4: + // Exportable Certification + this.exportable = bytes[mypos++] === 1; + break; + case 5: + // Trust Signature + this.trustLevel = bytes[mypos++]; + this.trustAmount = bytes[mypos++]; + break; + case 6: + // Regular Expression + this.regularExpression = bytes[mypos]; + break; + case 7: + // Revocable + this.revocable = bytes[mypos++] === 1; + break; + case 9: + { + // Key Expiration Time in seconds + const seconds = _util2.default.readNumber(bytes.subarray(mypos, bytes.length)); + + this.keyExpirationTime = seconds; + this.keyNeverExpires = seconds === 0; + + break; + } + case 11: + // Preferred Symmetric Algorithms + read_array('preferredSymmetricAlgorithms', bytes.subarray(mypos, bytes.length)); + break; + case 12: + // Revocation Key + // (1 octet of class, 1 octet of public-key algorithm ID, 20 + // octets of + // fingerprint) + this.revocationKeyClass = bytes[mypos++]; + this.revocationKeyAlgorithm = bytes[mypos++]; + this.revocationKeyFingerprint = bytes.subarray(mypos, mypos + 20); + break; + + case 16: + // Issuer + this.issuerKeyId.read(bytes.subarray(mypos, bytes.length)); + break; + + case 20: + // Notation Data + // We don't know how to handle anything but a text flagged data. + if (bytes[mypos] === 0x80) { + // We extract key/value tuple from the byte stream. + mypos += 4; + const m = _util2.default.readNumber(bytes.subarray(mypos, mypos + 2)); + mypos += 2; + const n = _util2.default.readNumber(bytes.subarray(mypos, mypos + 2)); + mypos += 2; + + const name = _util2.default.Uint8Array_to_str(bytes.subarray(mypos, mypos + m)); + const value = _util2.default.Uint8Array_to_str(bytes.subarray(mypos + m, mypos + m + n)); + + this.notations.push([name, value]); + } else { + _util2.default.print_debug("Unsupported notation flag " + bytes[mypos]); + } + break; + case 21: + // Preferred Hash Algorithms + read_array('preferredHashAlgorithms', bytes.subarray(mypos, bytes.length)); + break; + case 22: + // Preferred Compression Algorithms + read_array('preferredCompressionAlgorithms', bytes.subarray(mypos, bytes.length)); + break; + case 23: + // Key Server Preferences + read_array('keyServerPreferences', bytes.subarray(mypos, bytes.length)); + break; + case 24: + // Preferred Key Server + this.preferredKeyServer = _util2.default.Uint8Array_to_str(bytes.subarray(mypos, bytes.length)); + break; + case 25: + // Primary User ID + this.isPrimaryUserID = bytes[mypos++] !== 0; + break; + case 26: + // Policy URI + this.policyURI = _util2.default.Uint8Array_to_str(bytes.subarray(mypos, bytes.length)); + break; + case 27: + // Key Flags + read_array('keyFlags', bytes.subarray(mypos, bytes.length)); + break; + case 28: + // Signer's User ID + this.signersUserId = _util2.default.Uint8Array_to_str(bytes.subarray(mypos, bytes.length)); + break; + case 29: + // Reason for Revocation + this.reasonForRevocationFlag = bytes[mypos++]; + this.reasonForRevocationString = _util2.default.Uint8Array_to_str(bytes.subarray(mypos, bytes.length)); + break; + case 30: + // Features + read_array('features', bytes.subarray(mypos, bytes.length)); + break; + case 31: + { + // Signature Target + // (1 octet public-key algorithm, 1 octet hash algorithm, N octets hash) + this.signatureTargetPublicKeyAlgorithm = bytes[mypos++]; + this.signatureTargetHashAlgorithm = bytes[mypos++]; + + const len = _crypto2.default.getHashByteLength(this.signatureTargetHashAlgorithm); + + this.signatureTargetHash = _util2.default.Uint8Array_to_str(bytes.subarray(mypos, mypos + len)); + break; + } + case 32: + // Embedded Signature + this.embeddedSignature = new Signature(); + this.embeddedSignature.read(bytes.subarray(mypos, bytes.length)); + break; + case 33: + // Issuer Fingerprint + this.issuerKeyVersion = bytes[mypos++]; + this.issuerFingerprint = bytes.subarray(mypos, bytes.length); + if (this.issuerKeyVersion === 5) { + this.issuerKeyId.read(this.issuerFingerprint); + } else { + this.issuerKeyId.read(this.issuerFingerprint.subarray(-8)); + } + break; + case 34: + // Preferred AEAD Algorithms + read_array.call(this, 'preferredAeadAlgorithms', bytes.subarray(mypos, bytes.length)); + break; + default: + { + const err = new Error("Unknown signature subpacket type " + type + " @:" + mypos); + if (critical) { + throw err; + } else { + _util2.default.print_debug(err); + } + } + } +}; + +Signature.prototype.read_sub_packets = function (bytes, trusted = true) { + // Two-octet scalar octet count for following subpacket data. + const subpacket_length = _util2.default.readNumber(bytes.subarray(0, 2)); + + let i = 2; + + // subpacket data set (zero or more subpackets) + while (i < 2 + subpacket_length) { + const len = _packet2.default.readSimpleLength(bytes.subarray(i, bytes.length)); + i += len.offset; + + this.read_sub_packet(bytes.subarray(i, i + len.len), trusted); + + i += len.len; + } + + return i; +}; + +// Produces data to produce signature on +Signature.prototype.toSign = function (type, data) { + const t = _enums2.default.signature; + + switch (type) { + case t.binary: + if (data.text !== null) { + return _util2.default.str_to_Uint8Array(data.getText(true)); + } + return data.getBytes(true); + + case t.text: + { + let text = data.getText(true); + // normalize EOL to \r\n + text = _util2.default.canonicalizeEOL(text); + // encode UTF8 + return _util2.default.encode_utf8(text); + } + case t.standalone: + return new Uint8Array(0); + + case t.cert_generic: + case t.cert_persona: + case t.cert_casual: + case t.cert_positive: + case t.cert_revocation: + { + let packet; + let tag; + + if (data.userId) { + tag = 0xB4; + packet = data.userId; + } else if (data.userAttribute) { + tag = 0xD1; + packet = data.userAttribute; + } else { + throw new Error('Either a userId or userAttribute packet needs to be ' + 'supplied for certification.'); + } + + const bytes = packet.write(); + + return _util2.default.concat([this.toSign(t.key, data), new Uint8Array([tag]), _util2.default.writeNumber(bytes.length, 4), bytes]); + } + case t.subkey_binding: + case t.subkey_revocation: + case t.key_binding: + return _util2.default.concat([this.toSign(t.key, data), this.toSign(t.key, { + key: data.bind + })]); + + case t.key: + if (data.key === undefined) { + throw new Error('Key packet is required for this signature.'); + } + return data.key.writeOld(); + + case t.key_revocation: + return this.toSign(t.key, data); + case t.timestamp: + return new Uint8Array(0); + case t.third_party: + throw new Error('Not implemented'); + default: + throw new Error('Unknown signature type.'); + } +}; + +Signature.prototype.calculateTrailer = function () { + let length = 0; + return _webStreamTools2.default.transform(_webStreamTools2.default.clone(this.signatureData), value => { + length += value.length; + }, () => { + const first = new Uint8Array([4, 0xFF]); //Version, ? + return _util2.default.concat([first, _util2.default.writeNumber(length, 4)]); + }); +}; + +Signature.prototype.toHash = function (signatureType, data) { + const bytes = this.toSign(signatureType, data); + + return _util2.default.concat([bytes, this.signatureData, this.calculateTrailer()]); +}; + +Signature.prototype.hash = async function (signatureType, data, toHash, streaming = true) { + const hashAlgorithm = _enums2.default.write(_enums2.default.hash, this.hashAlgorithm); + if (!toHash) toHash = this.toHash(signatureType, data); + if (!streaming && _util2.default.isStream(toHash)) { + return _webStreamTools2.default.fromAsync(async () => this.hash(signatureType, data, (await _webStreamTools2.default.readToEnd(toHash)))); + } + return _crypto2.default.hash.digest(hashAlgorithm, toHash); +}; + +/** + * verifys the signature packet. Note: not signature types are implemented + * @param {module:packet.PublicSubkey|module:packet.PublicKey| + * module:packet.SecretSubkey|module:packet.SecretKey} key the public key to verify the signature + * @param {module:enums.signature} signatureType expected signature type + * @param {String|Object} data data which on the signature applies + * @returns {Promise} True if message is verified, else false. + * @async + */ +Signature.prototype.verify = async function (key, signatureType, data) { + const publicKeyAlgorithm = _enums2.default.write(_enums2.default.publicKey, this.publicKeyAlgorithm); + const hashAlgorithm = _enums2.default.write(_enums2.default.hash, this.hashAlgorithm); + + if (publicKeyAlgorithm !== _enums2.default.write(_enums2.default.publicKey, key.algorithm)) { + throw new Error('Public key algorithm used to sign signature does not match issuer key algorithm.'); + } + + let toHash; + let hash; + if (this.hashed) { + hash = this.hashed; + } else { + toHash = this.toHash(signatureType, data); + hash = await this.hash(signatureType, data, toHash); + } + hash = await _webStreamTools2.default.readToEnd(hash); + + if (this.signedHashValue[0] !== hash[0] || this.signedHashValue[1] !== hash[1]) { + this.verified = false; + } else { + let mpicount = 0; + // Algorithm-Specific Fields for RSA signatures: + // - multiprecision number (MPI) of RSA signature value m**d mod n. + if (publicKeyAlgorithm > 0 && publicKeyAlgorithm < 4) { + mpicount = 1; + + // Algorithm-Specific Fields for DSA, ECDSA, and EdDSA signatures: + // - MPI of DSA value r. + // - MPI of DSA value s. + } else if (publicKeyAlgorithm === _enums2.default.publicKey.dsa || publicKeyAlgorithm === _enums2.default.publicKey.ecdsa || publicKeyAlgorithm === _enums2.default.publicKey.eddsa) { + mpicount = 2; + } + + // EdDSA signature parameters are encoded in little-endian format + // https://tools.ietf.org/html/rfc8032#section-5.1.2 + const endian = publicKeyAlgorithm === _enums2.default.publicKey.eddsa ? 'le' : 'be'; + const mpi = []; + let i = 0; + this.signature = await _webStreamTools2.default.readToEnd(this.signature); + for (let j = 0; j < mpicount; j++) { + mpi[j] = new _mpi2.default(); + i += mpi[j].read(this.signature.subarray(i, this.signature.length), endian); + } + + this.verified = await _crypto2.default.signature.verify(publicKeyAlgorithm, hashAlgorithm, mpi, key.params, toHash, hash); + } + return this.verified; +}; + +/** + * Verifies signature expiration date + * @param {Date} date (optional) use the given date for verification instead of the current time + * @returns {Boolean} true if expired + */ +Signature.prototype.isExpired = function (date = new Date()) { + const normDate = _util2.default.normalizeDate(date); + if (normDate !== null) { + const expirationTime = this.getExpirationTime(); + return !(this.created <= normDate && normDate <= expirationTime); + } + return false; +}; + +/** + * Returns the expiration time of the signature or Infinity if signature does not expire + * @returns {Date} expiration time + */ +Signature.prototype.getExpirationTime = function () { + return !this.signatureNeverExpires ? new Date(this.created.getTime() + this.signatureExpirationTime * 1000) : Infinity; +}; + +/** + * Fix custom types after cloning + */ +Signature.prototype.postCloneTypeFix = function () { + this.issuerKeyId = _keyid2.default.fromClone(this.issuerKeyId); +}; + +exports.default = Signature; + +},{"../crypto":95,"../enums":114,"../type/keyid.js":149,"../type/mpi.js":150,"../util":153,"./packet":130,"web-stream-tools":76}],138:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _webStreamTools = require('web-stream-tools'); + +var _webStreamTools2 = _interopRequireDefault(_webStreamTools); + +var _config = require('../config'); + +var _config2 = _interopRequireDefault(_config); + +var _crypto = require('../crypto'); + +var _crypto2 = _interopRequireDefault(_crypto); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const VERSION = 1; // A one-octet version number of the data packet. + +/** + * Implementation of the Symmetrically Encrypted Authenticated Encryption with + * Additional Data (AEAD) Protected Data Packet + * + * {@link https://tools.ietf.org/html/draft-ford-openpgp-format-00#section-2.1}: + * AEAD Protected Data Packet + * @memberof module:packet + * @constructor + */ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2016 Tankred Hase +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires web-stream-tools + * @requires config + * @requires crypto + * @requires enums + * @requires util + */ + +function SymEncryptedAEADProtected() { + this.tag = _enums2.default.packet.symEncryptedAEADProtected; + this.version = VERSION; + this.cipherAlgo = null; + this.aeadAlgorithm = 'eax'; + this.aeadAlgo = null; + this.chunkSizeByte = null; + this.iv = null; + this.encrypted = null; + this.packets = null; +} + +exports.default = SymEncryptedAEADProtected; + +/** + * Parse an encrypted payload of bytes in the order: version, IV, ciphertext (see specification) + * @param {Uint8Array | ReadableStream} bytes + */ + +SymEncryptedAEADProtected.prototype.read = async function (bytes) { + await _webStreamTools2.default.parse(bytes, async reader => { + if ((await reader.readByte()) !== VERSION) { + // The only currently defined value is 1. + throw new Error('Invalid packet version.'); + } + if (_config2.default.aead_protect_version === 4) { + this.cipherAlgo = await reader.readByte(); + this.aeadAlgo = await reader.readByte(); + this.chunkSizeByte = await reader.readByte(); + } else { + this.aeadAlgo = _enums2.default.aead.experimental_gcm; + } + const mode = _crypto2.default[_enums2.default.read(_enums2.default.aead, this.aeadAlgo)]; + this.iv = await reader.readBytes(mode.ivLength); + this.encrypted = reader.remainder(); + }); +}; + +/** + * Write the encrypted payload of bytes in the order: version, IV, ciphertext (see specification) + * @returns {Uint8Array | ReadableStream} The encrypted payload + */ +SymEncryptedAEADProtected.prototype.write = function () { + if (_config2.default.aead_protect_version === 4) { + return _util2.default.concat([new Uint8Array([this.version, this.cipherAlgo, this.aeadAlgo, this.chunkSizeByte]), this.iv, this.encrypted]); + } + return _util2.default.concat([new Uint8Array([this.version]), this.iv, this.encrypted]); +}; + +/** + * Decrypt the encrypted payload. + * @param {String} sessionKeyAlgorithm The session key's cipher algorithm e.g. 'aes128' + * @param {Uint8Array} key The session key used to encrypt the payload + * @param {Boolean} streaming Whether the top-level function will return a stream + * @returns {Boolean} + * @async + */ +SymEncryptedAEADProtected.prototype.decrypt = async function (sessionKeyAlgorithm, key, streaming) { + if (_config2.default.aead_protect_version !== 4) { + this.cipherAlgo = _enums2.default.write(_enums2.default.symmetric, sessionKeyAlgorithm); + } + await this.packets.read((await this.crypt('decrypt', key, _webStreamTools2.default.clone(this.encrypted), streaming)), streaming); + return true; +}; + +/** + * Encrypt the packet list payload. + * @param {String} sessionKeyAlgorithm The session key's cipher algorithm e.g. 'aes128' + * @param {Uint8Array} key The session key used to encrypt the payload + * @param {Boolean} streaming Whether the top-level function will return a stream + * @async + */ +SymEncryptedAEADProtected.prototype.encrypt = async function (sessionKeyAlgorithm, key, streaming) { + this.cipherAlgo = _enums2.default.write(_enums2.default.symmetric, sessionKeyAlgorithm); + this.aeadAlgo = _config2.default.aead_protect_version === 4 ? _enums2.default.write(_enums2.default.aead, this.aeadAlgorithm) : _enums2.default.aead.experimental_gcm; + const mode = _crypto2.default[_enums2.default.read(_enums2.default.aead, this.aeadAlgo)]; + this.iv = await _crypto2.default.random.getRandomBytes(mode.ivLength); // generate new random IV + this.chunkSizeByte = _config2.default.aead_chunk_size_byte; + const data = this.packets.write(); + this.encrypted = await this.crypt('encrypt', key, data, streaming); +}; + +/** + * En/decrypt the payload. + * @param {encrypt|decrypt} fn Whether to encrypt or decrypt + * @param {Uint8Array} key The session key used to en/decrypt the payload + * @param {Uint8Array | ReadableStream} data The data to en/decrypt + * @param {Boolean} streaming Whether the top-level function will return a stream + * @returns {Uint8Array | ReadableStream} + * @async + */ +SymEncryptedAEADProtected.prototype.crypt = async function (fn, key, data, streaming) { + const cipher = _enums2.default.read(_enums2.default.symmetric, this.cipherAlgo); + const mode = _crypto2.default[_enums2.default.read(_enums2.default.aead, this.aeadAlgo)]; + const modeInstance = await mode(cipher, key); + if (_config2.default.aead_protect_version === 4) { + const tagLengthIfDecrypting = fn === 'decrypt' ? mode.tagLength : 0; + const chunkSize = 2 ** (this.chunkSizeByte + 6) + tagLengthIfDecrypting; // ((uint64_t)1 << (c + 6)) + const adataBuffer = new ArrayBuffer(21); + const adataArray = new Uint8Array(adataBuffer, 0, 13); + const adataTagArray = new Uint8Array(adataBuffer); + const adataView = new DataView(adataBuffer); + const chunkIndexArray = new Uint8Array(adataBuffer, 5, 8); + adataArray.set([0xC0 | this.tag, this.version, this.cipherAlgo, this.aeadAlgo, this.chunkSizeByte], 0); + let chunkIndex = 0; + let latestPromise = Promise.resolve(); + let cryptedBytes = 0; + let queuedBytes = 0; + const iv = this.iv; + return _webStreamTools2.default.transformPair(data, async (readable, writable) => { + const reader = _webStreamTools2.default.getReader(readable); + const buffer = new TransformStream({}, { + highWaterMark: streaming ? _util2.default.getHardwareConcurrency() * 2 ** (_config2.default.aead_chunk_size_byte + 6) : Infinity, + size: array => array.length + }); + _webStreamTools2.default.pipe(buffer.readable, writable); + const writer = _webStreamTools2.default.getWriter(buffer.writable); + try { + while (true) { + let chunk = (await reader.readBytes(chunkSize + tagLengthIfDecrypting)) || new Uint8Array(); + const finalChunk = chunk.subarray(chunk.length - tagLengthIfDecrypting); + chunk = chunk.subarray(0, chunk.length - tagLengthIfDecrypting); + let cryptedPromise; + let done; + if (!chunkIndex || chunk.length) { + reader.unshift(finalChunk); + cryptedPromise = modeInstance[fn](chunk, mode.getNonce(iv, chunkIndexArray), adataArray); + } else { + // After the last chunk, we either encrypt a final, empty + // data chunk to get the final authentication tag or + // validate that final authentication tag. + adataView.setInt32(13 + 4, cryptedBytes); // Should be setInt64(13, ...) + cryptedPromise = modeInstance[fn](finalChunk, mode.getNonce(iv, chunkIndexArray), adataTagArray); + done = true; + } + cryptedBytes += chunk.length - tagLengthIfDecrypting; + queuedBytes += chunk.length - tagLengthIfDecrypting; + // eslint-disable-next-line no-loop-func + latestPromise = latestPromise.then(() => cryptedPromise).then(async crypted => { + await writer.ready; + await writer.write(crypted); + queuedBytes -= chunk.length; + }).catch(err => writer.abort(err)); + if (done || queuedBytes > writer.desiredSize) { + await latestPromise; // Respect backpressure + } + if (!done) { + adataView.setInt32(5 + 4, ++chunkIndex); // Should be setInt64(5, ...) + } else { + await writer.close(); + break; + } + } + } catch (e) { + await writer.abort(e); + } + }); + } else { + return modeInstance[fn]((await _webStreamTools2.default.readToEnd(data)), this.iv); + } +}; + +},{"../config":80,"../crypto":95,"../enums":114,"../util":153,"web-stream-tools":76}],139:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _webStreamTools = require('web-stream-tools'); + +var _webStreamTools2 = _interopRequireDefault(_webStreamTools); + +var _config = require('../config'); + +var _config2 = _interopRequireDefault(_config); + +var _crypto = require('../crypto'); + +var _crypto2 = _interopRequireDefault(_crypto); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const VERSION = 1; // A one-octet version number of the data packet. + +/** + * Implementation of the Sym. Encrypted Integrity Protected Data Packet (Tag 18) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.13|RFC4880 5.13}: + * The Symmetrically Encrypted Integrity Protected Data packet is + * a variant of the Symmetrically Encrypted Data packet. It is a new feature + * created for OpenPGP that addresses the problem of detecting a modification to + * encrypted data. It is used in combination with a Modification Detection Code + * packet. + * @memberof module:packet + * @constructor + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires asmcrypto.js + * @requires web-stream-tools + * @requires config + * @requires crypto + * @requires enums + * @requires util + */ + +function SymEncryptedIntegrityProtected() { + this.tag = _enums2.default.packet.symEncryptedIntegrityProtected; + this.version = VERSION; + /** The encrypted payload. */ + this.encrypted = null; // string + /** + * If after decrypting the packet this is set to true, + * a modification has been detected and thus the contents + * should be discarded. + * @type {Boolean} + */ + this.modification = false; + this.packets = null; +} + +SymEncryptedIntegrityProtected.prototype.read = async function (bytes) { + await _webStreamTools2.default.parse(bytes, async reader => { + + // - A one-octet version number. The only currently defined value is 1. + if ((await reader.readByte()) !== VERSION) { + throw new Error('Invalid packet version.'); + } + + // - Encrypted data, the output of the selected symmetric-key cipher + // operating in Cipher Feedback mode with shift amount equal to the + // block size of the cipher (CFB-n where n is the block size). + this.encrypted = reader.remainder(); + }); +}; + +SymEncryptedIntegrityProtected.prototype.write = function () { + return _util2.default.concat([new Uint8Array([VERSION]), this.encrypted]); +}; + +/** + * Encrypt the payload in the packet. + * @param {String} sessionKeyAlgorithm The selected symmetric encryption algorithm to be used e.g. 'aes128' + * @param {Uint8Array} key The key of cipher blocksize length to be used + * @param {Boolean} streaming Whether to set this.encrypted to a stream + * @returns {Promise} + * @async + */ +SymEncryptedIntegrityProtected.prototype.encrypt = async function (sessionKeyAlgorithm, key, streaming) { + let bytes = this.packets.write(); + if (!streaming) bytes = await _webStreamTools2.default.readToEnd(bytes); + const prefix = await _crypto2.default.getPrefixRandom(sessionKeyAlgorithm); + const mdc = new Uint8Array([0xD3, 0x14]); // modification detection code packet + + const tohash = _util2.default.concat([prefix, bytes, mdc]); + const hash = await _crypto2.default.hash.sha1(_webStreamTools2.default.passiveClone(tohash)); + const plaintext = _util2.default.concat([tohash, hash]); + + this.encrypted = await _crypto2.default.cfb.encrypt(sessionKeyAlgorithm, key, plaintext, new Uint8Array(_crypto2.default.cipher[sessionKeyAlgorithm].blockSize)); + return true; +}; + +/** + * Decrypts the encrypted data contained in the packet. + * @param {String} sessionKeyAlgorithm The selected symmetric encryption algorithm to be used e.g. 'aes128' + * @param {Uint8Array} key The key of cipher blocksize length to be used + * @param {Boolean} streaming Whether to read this.encrypted as a stream + * @returns {Promise} + * @async + */ +SymEncryptedIntegrityProtected.prototype.decrypt = async function (sessionKeyAlgorithm, key, streaming) { + if (!streaming) this.encrypted = await _webStreamTools2.default.readToEnd(this.encrypted); + const encrypted = _webStreamTools2.default.clone(this.encrypted); + const decrypted = await _crypto2.default.cfb.decrypt(sessionKeyAlgorithm, key, encrypted, new Uint8Array(_crypto2.default.cipher[sessionKeyAlgorithm].blockSize)); + + // there must be a modification detection code packet as the + // last packet and everything gets hashed except the hash itself + const realHash = _webStreamTools2.default.slice(_webStreamTools2.default.passiveClone(decrypted), -20); + const tohash = _webStreamTools2.default.slice(decrypted, 0, -20); + const verifyHash = Promise.all([_webStreamTools2.default.readToEnd((await _crypto2.default.hash.sha1(_webStreamTools2.default.passiveClone(tohash)))), _webStreamTools2.default.readToEnd(realHash)]).then(([hash, mdc]) => { + if (!_util2.default.equalsUint8Array(hash, mdc)) { + throw new Error('Modification detected.'); + } + return new Uint8Array(); + }); + const bytes = _webStreamTools2.default.slice(tohash, _crypto2.default.cipher[sessionKeyAlgorithm].blockSize + 2); // Remove random prefix + let packetbytes = _webStreamTools2.default.slice(bytes, 0, -2); // Remove MDC packet + packetbytes = _webStreamTools2.default.concat([packetbytes, _webStreamTools2.default.fromAsync(() => verifyHash)]); + if (!_util2.default.isStream(encrypted) || !_config2.default.allow_unauthenticated_stream) { + packetbytes = await _webStreamTools2.default.readToEnd(packetbytes); + } + await this.packets.read(packetbytes, streaming); + return true; +}; + +exports.default = SymEncryptedIntegrityProtected; + +},{"../config":80,"../crypto":95,"../enums":114,"../util":153,"web-stream-tools":76}],140:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _s2k = require('../type/s2k'); + +var _s2k2 = _interopRequireDefault(_s2k); + +var _config = require('../config'); + +var _config2 = _interopRequireDefault(_config); + +var _crypto = require('../crypto'); + +var _crypto2 = _interopRequireDefault(_crypto); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Public-Key Encrypted Session Key Packets (Tag 1) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.1|RFC4880 5.1}: + * A Public-Key Encrypted Session Key packet holds the session key + * used to encrypt a message. Zero or more Public-Key Encrypted Session Key + * packets and/or Symmetric-Key Encrypted Session Key packets may precede a + * Symmetrically Encrypted Data Packet, which holds an encrypted message. The + * message is encrypted with the session key, and the session key is itself + * encrypted and stored in the Encrypted Session Key packet(s). The + * Symmetrically Encrypted Data Packet is preceded by one Public-Key Encrypted + * Session Key packet for each OpenPGP key to which the message is encrypted. + * The recipient of the message finds a session key that is encrypted to their + * public key, decrypts the session key, and then uses the session key to + * decrypt the message. + * @memberof module:packet + * @constructor + */ +function SymEncryptedSessionKey() { + this.tag = _enums2.default.packet.symEncryptedSessionKey; + this.version = _config2.default.aead_protect && _config2.default.aead_protect_version === 4 ? 5 : 4; + this.sessionKey = null; + this.sessionKeyEncryptionAlgorithm = null; + this.sessionKeyAlgorithm = 'aes256'; + this.aeadAlgorithm = _enums2.default.read(_enums2.default.aead, _config2.default.aead_mode); + this.encrypted = null; + this.s2k = null; + this.iv = null; +} + +/** + * Parsing function for a symmetric encrypted session key packet (tag 3). + * + * @param {Uint8Array} input Payload of a tag 1 packet + * @param {Integer} position Position to start reading from the input string + * @param {Integer} len + * Length of the packet or the remaining length of + * input at position + * @returns {module:packet.SymEncryptedSessionKey} Object representation + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires type/s2k + * @requires config + * @requires crypto + * @requires enums + * @requires util + */ + +SymEncryptedSessionKey.prototype.read = function (bytes) { + let offset = 0; + + // A one-octet version number. The only currently defined version is 4. + this.version = bytes[offset++]; + + // A one-octet number describing the symmetric algorithm used. + const algo = _enums2.default.read(_enums2.default.symmetric, bytes[offset++]); + + if (this.version === 5) { + // A one-octet AEAD algorithm. + this.aeadAlgorithm = _enums2.default.read(_enums2.default.aead, bytes[offset++]); + } + + // A string-to-key (S2K) specifier, length as defined above. + this.s2k = new _s2k2.default(); + offset += this.s2k.read(bytes.subarray(offset, bytes.length)); + + if (this.version === 5) { + const mode = _crypto2.default[this.aeadAlgorithm]; + + // A starting initialization vector of size specified by the AEAD + // algorithm. + this.iv = bytes.subarray(offset, offset += mode.ivLength); + } + + // The encrypted session key itself, which is decrypted with the + // string-to-key object. This is optional in version 4. + if (this.version === 5 || offset < bytes.length) { + this.encrypted = bytes.subarray(offset, bytes.length); + this.sessionKeyEncryptionAlgorithm = algo; + } else { + this.sessionKeyAlgorithm = algo; + } +}; + +SymEncryptedSessionKey.prototype.write = function () { + const algo = this.encrypted === null ? this.sessionKeyAlgorithm : this.sessionKeyEncryptionAlgorithm; + + let bytes; + + if (this.version === 5) { + bytes = _util2.default.concatUint8Array([new Uint8Array([this.version, _enums2.default.write(_enums2.default.symmetric, algo), _enums2.default.write(_enums2.default.aead, this.aeadAlgorithm)]), this.s2k.write(), this.iv, this.encrypted]); + } else { + bytes = _util2.default.concatUint8Array([new Uint8Array([this.version, _enums2.default.write(_enums2.default.symmetric, algo)]), this.s2k.write()]); + + if (this.encrypted !== null) { + bytes = _util2.default.concatUint8Array([bytes, this.encrypted]); + } + } + + return bytes; +}; + +/** + * Decrypts the session key + * @param {String} passphrase The passphrase in string form + * @returns {Promise} + * @async + */ +SymEncryptedSessionKey.prototype.decrypt = async function (passphrase) { + const algo = this.sessionKeyEncryptionAlgorithm !== null ? this.sessionKeyEncryptionAlgorithm : this.sessionKeyAlgorithm; + + const length = _crypto2.default.cipher[algo].keySize; + const key = await this.s2k.produce_key(passphrase, length); + + if (this.version === 5) { + const mode = _crypto2.default[this.aeadAlgorithm]; + const adata = new Uint8Array([0xC0 | this.tag, this.version, _enums2.default.write(_enums2.default.symmetric, this.sessionKeyEncryptionAlgorithm), _enums2.default.write(_enums2.default.aead, this.aeadAlgorithm)]); + const modeInstance = await mode(algo, key); + this.sessionKey = await modeInstance.decrypt(this.encrypted, this.iv, adata); + } else if (this.encrypted !== null) { + const decrypted = await _crypto2.default.cfb.decrypt(algo, key, this.encrypted, new Uint8Array(_crypto2.default.cipher[algo].blockSize)); + + this.sessionKeyAlgorithm = _enums2.default.read(_enums2.default.symmetric, decrypted[0]); + this.sessionKey = decrypted.subarray(1, decrypted.length); + } else { + this.sessionKey = key; + } + + return true; +}; + +/** + * Encrypts the session key + * @param {String} passphrase The passphrase in string form + * @returns {Promise} + * @async + */ +SymEncryptedSessionKey.prototype.encrypt = async function (passphrase) { + const algo = this.sessionKeyEncryptionAlgorithm !== null ? this.sessionKeyEncryptionAlgorithm : this.sessionKeyAlgorithm; + + this.sessionKeyEncryptionAlgorithm = algo; + + this.s2k = new _s2k2.default(); + this.s2k.salt = await _crypto2.default.random.getRandomBytes(8); + + const length = _crypto2.default.cipher[algo].keySize; + const key = await this.s2k.produce_key(passphrase, length); + + if (this.sessionKey === null) { + this.sessionKey = await _crypto2.default.generateSessionKey(this.sessionKeyAlgorithm); + } + + if (this.version === 5) { + const mode = _crypto2.default[this.aeadAlgorithm]; + this.iv = await _crypto2.default.random.getRandomBytes(mode.ivLength); // generate new random IV + const adata = new Uint8Array([0xC0 | this.tag, this.version, _enums2.default.write(_enums2.default.symmetric, this.sessionKeyEncryptionAlgorithm), _enums2.default.write(_enums2.default.aead, this.aeadAlgorithm)]); + const modeInstance = await mode(algo, key); + this.encrypted = await modeInstance.encrypt(this.sessionKey, this.iv, adata); + } else { + const algo_enum = new Uint8Array([_enums2.default.write(_enums2.default.symmetric, this.sessionKeyAlgorithm)]); + const private_key = _util2.default.concatUint8Array([algo_enum, this.sessionKey]); + this.encrypted = await _crypto2.default.cfb.encrypt(algo, key, private_key, new Uint8Array(_crypto2.default.cipher[algo].blockSize)); + } + + return true; +}; + +/** + * Fix custom types after cloning + */ +SymEncryptedSessionKey.prototype.postCloneTypeFix = function () { + this.s2k = _s2k2.default.fromClone(this.s2k); +}; + +exports.default = SymEncryptedSessionKey; + +},{"../config":80,"../crypto":95,"../enums":114,"../type/s2k":152,"../util":153}],141:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _webStreamTools = require('web-stream-tools'); + +var _webStreamTools2 = _interopRequireDefault(_webStreamTools); + +var _config = require('../config'); + +var _config2 = _interopRequireDefault(_config); + +var _crypto = require('../crypto'); + +var _crypto2 = _interopRequireDefault(_crypto); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Implementation of the Symmetrically Encrypted Data Packet (Tag 9) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.7|RFC4880 5.7}: + * The Symmetrically Encrypted Data packet contains data encrypted with a + * symmetric-key algorithm. When it has been decrypted, it contains other + * packets (usually a literal data packet or compressed data packet, but in + * theory other Symmetrically Encrypted Data packets or sequences of packets + * that form whole OpenPGP messages). + * @memberof module:packet + * @constructor + */ +function SymmetricallyEncrypted() { + /** + * Packet type + * @type {module:enums.packet} + */ + this.tag = _enums2.default.packet.symmetricallyEncrypted; + /** + * Encrypted secret-key data + */ + this.encrypted = null; + /** + * Decrypted packets contained within. + * @type {module:packet.List} + */ + this.packets = null; + /** + * When true, decrypt fails if message is not integrity protected + * @see module:config.ignore_mdc_error + */ + this.ignore_mdc_error = _config2.default.ignore_mdc_error; +} // GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires web-stream-tools + * @requires config + * @requires crypto + * @requires enums + * @requires util + */ + +SymmetricallyEncrypted.prototype.read = function (bytes) { + this.encrypted = bytes; +}; + +SymmetricallyEncrypted.prototype.write = function () { + return this.encrypted; +}; + +/** + * Decrypt the symmetrically-encrypted packet data + * See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms. + * @param {module:enums.symmetric} sessionKeyAlgorithm Symmetric key algorithm to use + * @param {Uint8Array} key The key of cipher blocksize length to be used + * @returns {Promise} + * @async + */ +SymmetricallyEncrypted.prototype.decrypt = async function (sessionKeyAlgorithm, key) { + // If MDC errors are not being ignored, all missing MDC packets in symmetrically encrypted data should throw an error + if (!this.ignore_mdc_error) { + throw new Error('Decryption failed due to missing MDC.'); + } + + this.encrypted = await _webStreamTools2.default.readToEnd(this.encrypted); + const decrypted = await _crypto2.default.cfb.decrypt(sessionKeyAlgorithm, key, this.encrypted.subarray(_crypto2.default.cipher[sessionKeyAlgorithm].blockSize + 2), this.encrypted.subarray(2, _crypto2.default.cipher[sessionKeyAlgorithm].blockSize + 2)); + + await this.packets.read(decrypted); + + return true; +}; + +/** + * Encrypt the symmetrically-encrypted packet data + * See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms. + * @param {module:enums.symmetric} sessionKeyAlgorithm Symmetric key algorithm to use + * @param {Uint8Array} key The key of cipher blocksize length to be used + * @returns {Promise} + * @async + */ +SymmetricallyEncrypted.prototype.encrypt = async function (algo, key) { + const data = this.packets.write(); + + const prefix = await _crypto2.default.getPrefixRandom(algo); + const FRE = await _crypto2.default.cfb.encrypt(algo, key, prefix, new Uint8Array(_crypto2.default.cipher[algo].blockSize)); + const ciphertext = await _crypto2.default.cfb.encrypt(algo, key, data, FRE.subarray(2)); + this.encrypted = _util2.default.concat([FRE, ciphertext]); + + return true; +}; + +exports.default = SymmetricallyEncrypted; + +},{"../config":80,"../crypto":95,"../enums":114,"../util":153,"web-stream-tools":76}],142:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Implementation of the Trust Packet (Tag 12) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.10|RFC4880 5.10}: + * The Trust packet is used only within keyrings and is not normally + * exported. Trust packets contain data that record the user's + * specifications of which key holders are trustworthy introducers, + * along with other information that implementing software uses for + * trust information. The format of Trust packets is defined by a given + * implementation. + * + * Trust packets SHOULD NOT be emitted to output streams that are + * transferred to other users, and they SHOULD be ignored on any input + * other than local keyring files. + * @memberof module:packet + * @constructor + */ +function Trust() { + this.tag = _enums2.default.packet.trust; +} + +/** + * Parsing function for a trust packet (tag 12). + * Currently not implemented as we ignore trust packets + * @param {String} byptes payload of a tag 12 packet + */ +/** + * @requires enums + */ + +Trust.prototype.read = function () {}; // TODO + +exports.default = Trust; + +},{"../enums":114}],143:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _packet = require('./packet'); + +var _packet2 = _interopRequireDefault(_packet); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Implementation of the User Attribute Packet (Tag 17) + * + * The User Attribute packet is a variation of the User ID packet. It + * is capable of storing more types of data than the User ID packet, + * which is limited to text. Like the User ID packet, a User Attribute + * packet may be certified by the key owner ("self-signed") or any other + * key owner who cares to certify it. Except as noted, a User Attribute + * packet may be used anywhere that a User ID packet may be used. + * + * While User Attribute packets are not a required part of the OpenPGP + * standard, implementations SHOULD provide at least enough + * compatibility to properly handle a certification signature on the + * User Attribute packet. A simple way to do this is by treating the + * User Attribute packet as a User ID packet with opaque contents, but + * an implementation may use any method desired. + * @memberof module:packet + * @constructor + */ +function UserAttribute() { + this.tag = _enums2.default.packet.userAttribute; + this.attributes = []; +} + +/** + * parsing function for a user attribute packet (tag 17). + * @param {Uint8Array} input payload of a tag 17 packet + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires packet + * @requires enums + * @requires util + */ + +UserAttribute.prototype.read = function (bytes) { + let i = 0; + while (i < bytes.length) { + const len = _packet2.default.readSimpleLength(bytes.subarray(i, bytes.length)); + i += len.offset; + + this.attributes.push(_util2.default.Uint8Array_to_str(bytes.subarray(i, i + len.len))); + i += len.len; + } +}; + +/** + * Creates a binary representation of the user attribute packet + * @returns {Uint8Array} string representation + */ +UserAttribute.prototype.write = function () { + const arr = []; + for (let i = 0; i < this.attributes.length; i++) { + arr.push(_packet2.default.writeSimpleLength(this.attributes[i].length)); + arr.push(_util2.default.str_to_Uint8Array(this.attributes[i])); + } + return _util2.default.concatUint8Array(arr); +}; + +/** + * Compare for equality + * @param {module:packet.UserAttribute} usrAttr + * @returns {Boolean} true if equal + */ +UserAttribute.prototype.equals = function (usrAttr) { + if (!usrAttr || !(usrAttr instanceof UserAttribute)) { + return false; + } + return this.attributes.every(function (attr, index) { + return attr === usrAttr.attributes[index]; + }); +}; + +exports.default = UserAttribute; + +},{"../enums":114,"../util":153,"./packet":130}],144:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Implementation of the User ID Packet (Tag 13) + * + * A User ID packet consists of UTF-8 text that is intended to represent + * the name and email address of the key holder. By convention, it + * includes an RFC 2822 [RFC2822] mail name-addr, but there are no + * restrictions on its content. The packet length in the header + * specifies the length of the User ID. + * @memberof module:packet + * @constructor + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires enums + * @requires util + */ + +function Userid() { + this.tag = _enums2.default.packet.userid; + /** A string containing the user id. Usually in the form + * John Doe + * @type {String} + */ + this.userid = ''; + + this.name = ''; + this.email = ''; + this.comment = ''; +} + +/** + * Parsing function for a user id packet (tag 13). + * @param {Uint8Array} input payload of a tag 13 packet + */ +Userid.prototype.read = function (bytes) { + this.parse(_util2.default.decode_utf8(bytes)); +}; + +/** + * Parse userid string, e.g. 'John Doe ' + */ +Userid.prototype.parse = function (userid) { + try { + Object.assign(this, _util2.default.parseUserId(userid)); + } catch (e) {} + this.userid = userid; +}; + +/** + * Creates a binary representation of the user id packet + * @returns {Uint8Array} binary representation + */ +Userid.prototype.write = function () { + return _util2.default.encode_utf8(this.userid); +}; + +/** + * Set userid string from object, e.g. { name:'Phil Zimmermann', email:'phil@openpgp.org' } + */ +Userid.prototype.format = function (userid) { + if (_util2.default.isString(userid)) { + userid = _util2.default.parseUserId(userid); + } + Object.assign(this, userid); + this.userid = _util2.default.formatUserId(userid); +}; + +exports.default = Userid; + +},{"../enums":114,"../util":153}],145:[function(require,module,exports){ +(function (global){ +'use strict'; + +var _util = require('./util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +if (typeof window !== 'undefined') { + /******************************************************************** + * NOTE: This list is duplicated in Gruntfile.js, * + * so that these polyfills are only included in the compat bundle. * + ********************************************************************/ + + try { + if (typeof window.fetch === 'undefined') { + require('whatwg-fetch'); + } + if (typeof Array.prototype.fill === 'undefined') { + require('core-js/fn/array/fill'); + } + if (typeof Array.prototype.find === 'undefined') { + require('core-js/fn/array/find'); + } + if (typeof Array.prototype.includes === 'undefined') { + require('core-js/fn/array/includes'); + } + if (typeof Array.from === 'undefined') { + require('core-js/fn/array/from'); + } + + // No if-statement on Promise because of IE11. Otherwise Promise is undefined in the service worker. + require('core-js/fn/promise'); + + if (typeof Uint8Array.from === 'undefined') { + require('core-js/fn/typed/uint8-array'); + } + if (typeof String.prototype.repeat === 'undefined') { + require('core-js/fn/string/repeat'); + } + if (typeof Symbol === 'undefined') { + require('core-js/fn/symbol'); + } + if (typeof Object.assign === 'undefined') { + require('core-js/fn/object/assign'); + } + } catch (e) {} +} /** + * @fileoverview Old browser polyfills + * All are listed as dev dependencies because Node does not need them + * and for browser babel will take care of it + * @requires util + * @module polyfills + */ + +if (typeof TransformStream === 'undefined') { + require('@mattiasbuelens/web-streams-polyfill/es6'); +} +if (typeof TextEncoder === 'undefined') { + const nodeUtil = _util2.default.nodeRequire('util') || {}; + global.TextEncoder = nodeUtil.TextEncoder; + global.TextDecoder = nodeUtil.TextDecoder; +} +if (typeof TextEncoder === 'undefined') { + const textEncoding = require('text-encoding-utf-8'); + global.TextEncoder = textEncoding.TextEncoder; + global.TextDecoder = textEncoding.TextDecoder; +} + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./util":153,"@mattiasbuelens/web-streams-polyfill/es6":1,"core-js/fn/array/fill":"core-js/fn/array/fill","core-js/fn/array/find":"core-js/fn/array/find","core-js/fn/array/from":"core-js/fn/array/from","core-js/fn/array/includes":"core-js/fn/array/includes","core-js/fn/object/assign":"core-js/fn/object/assign","core-js/fn/promise":"core-js/fn/promise","core-js/fn/string/repeat":"core-js/fn/string/repeat","core-js/fn/symbol":"core-js/fn/symbol","core-js/fn/typed/uint8-array":"core-js/fn/typed/uint8-array","text-encoding-utf-8":72,"whatwg-fetch":"whatwg-fetch"}],146:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Signature = Signature; +exports.readArmored = readArmored; +exports.read = read; + +var _armor = require('./encoding/armor'); + +var _armor2 = _interopRequireDefault(_armor); + +var _packet = require('./packet'); + +var _packet2 = _interopRequireDefault(_packet); + +var _enums = require('./enums'); + +var _enums2 = _interopRequireDefault(_enums); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * @class + * @classdesc Class that represents an OpenPGP signature. + * @param {module:packet.List} packetlist The signature packets + */ +function Signature(packetlist) { + if (!(this instanceof Signature)) { + return new Signature(packetlist); + } + this.packets = packetlist || new _packet2.default.List(); +} + +/** + * Returns ASCII armored text of signature + * @returns {ReadableStream} ASCII armor + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires encoding/armor + * @requires packet + * @requires enums + * @module signature + */ + +Signature.prototype.armor = function () { + return _armor2.default.encode(_enums2.default.armor.signature, this.packets.write()); +}; + +/** + * reads an OpenPGP armored signature and returns a signature object + * @param {String | ReadableStream} armoredText text to be parsed + * @returns {Signature} new signature object + * @async + * @static + */ +async function readArmored(armoredText) { + const input = await _armor2.default.decode(armoredText); + return read(input.data); +} + +/** + * reads an OpenPGP signature as byte array and returns a signature object + * @param {Uint8Array | ReadableStream} input binary signature + * @returns {Signature} new signature object + * @async + * @static + */ +async function read(input) { + const packetlist = new _packet2.default.List(); + await packetlist.read(input); + return new Signature(packetlist); +} + +},{"./encoding/armor":112,"./enums":114,"./packet":126}],147:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * @constructor + */ +function ECDHSymmetricKey(data) { + if (typeof data === 'undefined') { + data = new Uint8Array([]); + } else if (_util2.default.isString(data)) { + data = _util2.default.str_to_Uint8Array(data); + } else { + data = new Uint8Array(data); + } + this.data = data; +} + +/** + * Read an ECDHSymmetricKey from an Uint8Array + * @param {Uint8Array} input Where to read the encoded symmetric key from + * @returns {Number} Number of read bytes + */ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2015-2016 Decentral +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Encoded symmetric key for ECDH + * + * @requires util + * @module type/ecdh_symkey + */ + +ECDHSymmetricKey.prototype.read = function (input) { + if (input.length >= 1) { + const length = input[0]; + if (input.length >= 1 + length) { + this.data = input.subarray(1, 1 + length); + return 1 + this.data.length; + } + } + throw new Error('Invalid symmetric key'); +}; + +/** + * Write an ECDHSymmetricKey as an Uint8Array + * @returns {Uint8Array} An array containing the value + */ +ECDHSymmetricKey.prototype.write = function () { + return _util2.default.concatUint8Array([new Uint8Array([this.data.length]), this.data]); +}; + +ECDHSymmetricKey.fromClone = function (clone) { + return new ECDHSymmetricKey(clone.data); +}; + +exports.default = ECDHSymmetricKey; + +},{"../util":153}],148:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _enums = require('../enums.js'); + +var _enums2 = _interopRequireDefault(_enums); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * @constructor + * @param {enums.hash} hash Hash algorithm + * @param {enums.symmetric} cipher Symmetric algorithm + */ +function KDFParams(data) { + if (data && data.length === 2) { + this.hash = data[0]; + this.cipher = data[1]; + } else { + this.hash = _enums2.default.hash.sha1; + this.cipher = _enums2.default.symmetric.aes128; + } +} + +/** + * Read KDFParams from an Uint8Array + * @param {Uint8Array} input Where to read the KDFParams from + * @returns {Number} Number of read bytes + */ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2015-2016 Decentral +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Implementation of type KDF parameters + * + * {@link https://tools.ietf.org/html/rfc6637#section-7|RFC 6637 7}: + * A key derivation function (KDF) is necessary to implement the EC + * encryption. The Concatenation Key Derivation Function (Approved + * Alternative 1) [NIST-SP800-56A] with the KDF hash function that is + * SHA2-256 [FIPS-180-3] or stronger is REQUIRED. + * @requires enums + * @module type/kdf_params + */ + +KDFParams.prototype.read = function (input) { + if (input.length < 4 || input[0] !== 3 || input[1] !== 1) { + throw new Error('Cannot read KDFParams'); + } + this.hash = input[2]; + this.cipher = input[3]; + return 4; +}; + +/** + * Write KDFParams to an Uint8Array + * @returns {Uint8Array} Array with the KDFParams value + */ +KDFParams.prototype.write = function () { + return new Uint8Array([3, 1, this.hash, this.cipher]); +}; + +KDFParams.fromClone = function (clone) { + return new KDFParams([clone.hash, clone.cipher]); +}; + +exports.default = KDFParams; + +},{"../enums.js":114}],149:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _util = require('../util.js'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * @constructor + */ +function Keyid() { + this.bytes = ''; +} + +/** + * Parsing method for a key id + * @param {Uint8Array} input Input to read the key id from + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Implementation of type key id + * + * {@link https://tools.ietf.org/html/rfc4880#section-3.3|RFC4880 3.3}: + * A Key ID is an eight-octet scalar that identifies a key. + * Implementations SHOULD NOT assume that Key IDs are unique. The + * section "Enhanced Key Formats" below describes how Key IDs are + * formed. + * @requires util + * @module type/keyid + */ + +Keyid.prototype.read = function (bytes) { + this.bytes = _util2.default.Uint8Array_to_str(bytes.subarray(0, 8)); +}; + +Keyid.prototype.write = function () { + return _util2.default.str_to_Uint8Array(this.bytes); +}; + +Keyid.prototype.toHex = function () { + return _util2.default.str_to_hex(this.bytes); +}; + +/** + * Checks equality of Key ID's + * @param {Keyid} keyid + * @param {Boolean} matchWildcard Indicates whether to check if either keyid is a wildcard + */ +Keyid.prototype.equals = function (keyid, matchWildcard = false) { + return matchWildcard && (keyid.isWildcard() || this.isWildcard()) || this.bytes === keyid.bytes; +}; + +Keyid.prototype.isNull = function () { + return this.bytes === ''; +}; + +Keyid.prototype.isWildcard = function () { + return (/^0+$/.test(this.toHex()) + ); +}; + +Keyid.mapToHex = function (keyId) { + return keyId.toHex(); +}; + +Keyid.fromClone = function (clone) { + const keyid = new Keyid(); + keyid.bytes = clone.bytes; + return keyid; +}; + +Keyid.fromId = function (hex) { + const keyid = new Keyid(); + keyid.read(_util2.default.hex_to_Uint8Array(hex)); + return keyid; +}; + +Keyid.wildcard = function () { + const keyid = new Keyid(); + keyid.read(new Uint8Array(8)); + return keyid; +}; + +exports.default = Keyid; + +},{"../util.js":153}],150:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _bn = require('bn.js'); + +var _bn2 = _interopRequireDefault(_bn); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * @constructor + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +// Hint: We hold our MPIs as an array of octets in big endian format preceding a two +// octet scalar: MPI: [a,b,c,d,e,f] +// - MPI size: (a << 8) | b +// - MPI = c | d << 8 | e << ((MPI.length -2)*8) | f ((MPI.length -2)*8) + +/** + * Implementation of type MPI ({@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2}) + * Multiprecision integers (also called MPIs) are unsigned integers used + * to hold large integers such as the ones used in cryptographic + * calculations. + * An MPI consists of two pieces: a two-octet scalar that is the length + * of the MPI in bits followed by a string of octets that contain the + * actual integer. + * @requires bn.js + * @requires util + * @module type/mpi + */ + +function MPI(data) { + /** An implementation dependent integer */ + if (data instanceof MPI) { + this.data = data.data; + } else if (_bn2.default.isBN(data)) { + this.fromBN(data); + } else if (_util2.default.isUint8Array(data)) { + this.fromUint8Array(data); + } else if (_util2.default.isString(data)) { + this.fromString(data); + } else { + this.data = null; + } +} + +/** + * Parsing function for a MPI ({@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC 4880 3.2}). + * @param {Uint8Array} input Payload of MPI data + * @param {String} endian Endianness of the data; 'be' for big-endian or 'le' for little-endian + * @returns {Integer} Length of data read + */ +MPI.prototype.read = function (bytes, endian = 'be') { + if (_util2.default.isString(bytes)) { + bytes = _util2.default.str_to_Uint8Array(bytes); + } + + const bits = bytes[0] << 8 | bytes[1]; + const bytelen = bits + 7 >>> 3; + const payload = bytes.subarray(2, 2 + bytelen); + + this.fromUint8Array(payload, endian); + + return 2 + bytelen; +}; + +/** + * Converts the mpi object to a bytes as specified in + * {@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2} + * @param {String} endian Endianness of the payload; 'be' for big-endian or 'le' for little-endian + * @param {Integer} length Length of the data part of the MPI + * @returns {Uint8Aray} mpi Byte representation + */ +MPI.prototype.write = function (endian, length) { + return _util2.default.Uint8Array_to_MPI(this.toUint8Array(endian, length)); +}; + +MPI.prototype.bitLength = function () { + return (this.data.length - 1) * 8 + _util2.default.nbits(this.data[0]); +}; + +MPI.prototype.byteLength = function () { + return this.data.length; +}; + +MPI.prototype.toUint8Array = function (endian, length) { + endian = endian || 'be'; + length = length || this.data.length; + + const payload = new Uint8Array(length); + const start = length - this.data.length; + if (start < 0) { + throw new Error('Payload is too large.'); + } + + payload.set(this.data, start); + if (endian === 'le') { + payload.reverse(); + } + + return payload; +}; + +MPI.prototype.fromUint8Array = function (bytes, endian = 'be') { + this.data = new Uint8Array(bytes.length); + this.data.set(bytes); + + if (endian === 'le') { + this.data.reverse(); + } +}; + +MPI.prototype.toString = function () { + return _util2.default.Uint8Array_to_str(this.toUint8Array()); +}; + +MPI.prototype.fromString = function (str, endian = 'be') { + this.fromUint8Array(_util2.default.str_to_Uint8Array(str), endian); +}; + +MPI.prototype.toBN = function () { + return new _bn2.default(this.toUint8Array()); +}; + +MPI.prototype.fromBN = function (bn) { + this.data = bn.toArrayLike(Uint8Array); +}; + +MPI.fromClone = function (clone) { + return new MPI(clone.data); +}; + +exports.default = MPI; + +},{"../util":153,"bn.js":17}],151:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _util = require('../util'); + +var _util2 = _interopRequireDefault(_util); + +var _enums = require('../enums'); + +var _enums2 = _interopRequireDefault(_enums); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * @constructor + */ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2015-2016 Decentral +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Wrapper to an OID value + * + * {@link https://tools.ietf.org/html/rfc6637#section-11|RFC6637, section 11}: + * The sequence of octets in the third column is the result of applying + * the Distinguished Encoding Rules (DER) to the ASN.1 Object Identifier + * with subsequent truncation. The truncation removes the two fields of + * encoded Object Identifier. The first omitted field is one octet + * representing the Object Identifier tag, and the second omitted field + * is the length of the Object Identifier body. For example, the + * complete ASN.1 DER encoding for the NIST P-256 curve OID is "06 08 2A + * 86 48 CE 3D 03 01 07", from which the first entry in the table above + * is constructed by omitting the first two octets. Only the truncated + * sequence of octets is the valid representation of a curve OID. + * @requires util + * @requires enums + * @module type/oid + */ + +function OID(oid) { + if (oid instanceof OID) { + this.oid = oid.oid; + } else if (_util2.default.isArray(oid) || _util2.default.isUint8Array(oid)) { + oid = new Uint8Array(oid); + if (oid[0] === 0x06) { + // DER encoded oid byte array + if (oid[1] !== oid.length - 2) { + throw new Error('Length mismatch in DER encoded oid'); + } + oid = oid.subarray(2); + } + this.oid = oid; + } else { + this.oid = ''; + } +} + +/** + * Method to read an OID object + * @param {Uint8Array} input Where to read the OID from + * @returns {Number} Number of read bytes + */ +OID.prototype.read = function (input) { + if (input.length >= 1) { + const length = input[0]; + if (input.length >= 1 + length) { + this.oid = input.subarray(1, 1 + length); + return 1 + this.oid.length; + } + } + throw new Error('Invalid oid'); +}; + +/** + * Serialize an OID object + * @returns {Uint8Array} Array with the serialized value the OID + */ +OID.prototype.write = function () { + return _util2.default.concatUint8Array([new Uint8Array([this.oid.length]), this.oid]); +}; + +/** + * Serialize an OID object as a hex string + * @returns {string} String with the hex value of the OID + */ +OID.prototype.toHex = function () { + return _util2.default.Uint8Array_to_hex(this.oid); +}; + +/** + * If a known curve object identifier, return the canonical name of the curve + * @returns {string} String with the canonical name of the curve + */ +OID.prototype.getName = function () { + const hex = this.toHex(); + if (_enums2.default.curve[hex]) { + return _enums2.default.write(_enums2.default.curve, hex); + } else { + throw new Error('Unknown curve object identifier.'); + } +}; + +OID.fromClone = function (clone) { + return new OID(clone.oid); +}; + +exports.default = OID; + +},{"../enums":114,"../util":153}],152:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _config = require('../config'); + +var _config2 = _interopRequireDefault(_config); + +var _crypto = require('../crypto'); + +var _crypto2 = _interopRequireDefault(_crypto); + +var _enums = require('../enums.js'); + +var _enums2 = _interopRequireDefault(_enums); + +var _util = require('../util.js'); + +var _util2 = _interopRequireDefault(_util); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * @constructor + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Implementation of the String-to-key specifier + * + * {@link https://tools.ietf.org/html/rfc4880#section-3.7|RFC4880 3.7}: + * String-to-key (S2K) specifiers are used to convert passphrase strings + * into symmetric-key encryption/decryption keys. They are used in two + * places, currently: to encrypt the secret part of private keys in the + * private keyring, and to convert passphrases to encryption keys for + * symmetrically encrypted messages. + * @requires config + * @requires crypto + * @requires enums + * @requires util + * @module type/s2k + */ + +function S2K() { + /** @type {module:enums.hash} */ + this.algorithm = 'sha256'; + /** @type {module:enums.s2k} */ + this.type = 'iterated'; + /** @type {Integer} */ + this.c = _config2.default.s2k_iteration_count_byte; + /** Eight bytes of salt in a binary string. + * @type {String} + */ + this.salt = null; +} + +S2K.prototype.get_count = function () { + // Exponent bias, defined in RFC4880 + const expbias = 6; + + return 16 + (this.c & 15) << (this.c >> 4) + expbias; +}; + +/** + * Parsing function for a string-to-key specifier ({@link https://tools.ietf.org/html/rfc4880#section-3.7|RFC 4880 3.7}). + * @param {String} input Payload of string-to-key specifier + * @returns {Integer} Actual length of the object + */ +S2K.prototype.read = function (bytes) { + let i = 0; + this.type = _enums2.default.read(_enums2.default.s2k, bytes[i++]); + this.algorithm = bytes[i++]; + if (this.type !== 'gnu') { + this.algorithm = _enums2.default.read(_enums2.default.hash, this.algorithm); + } + + switch (this.type) { + case 'simple': + break; + + case 'salted': + this.salt = bytes.subarray(i, i + 8); + i += 8; + break; + + case 'iterated': + this.salt = bytes.subarray(i, i + 8); + i += 8; + + // Octet 10: count, a one-octet, coded value + this.c = bytes[i++]; + break; + + case 'gnu': + if (_util2.default.Uint8Array_to_str(bytes.subarray(i, i + 3)) === "GNU") { + i += 3; // GNU + const gnuExtType = 1000 + bytes[i++]; + if (gnuExtType === 1001) { + this.type = 'gnu-dummy'; + // GnuPG extension mode 1001 -- don't write secret key at all + } else { + throw new Error("Unknown s2k gnu protection mode."); + } + } else { + throw new Error("Unknown s2k type."); + } + break; + + default: + throw new Error("Unknown s2k type."); + } + + return i; +}; + +/** + * Serializes s2k information + * @returns {Uint8Array} binary representation of s2k + */ +S2K.prototype.write = function () { + const arr = [new Uint8Array([_enums2.default.write(_enums2.default.s2k, this.type), _enums2.default.write(_enums2.default.hash, this.algorithm)])]; + + switch (this.type) { + case 'simple': + break; + case 'salted': + arr.push(this.salt); + break; + case 'iterated': + arr.push(this.salt); + arr.push(new Uint8Array([this.c])); + break; + case 'gnu': + throw new Error("GNU s2k type not supported."); + default: + throw new Error("Unknown s2k type."); + } + + return _util2.default.concatUint8Array(arr); +}; + +/** + * Produces a key using the specified passphrase and the defined + * hashAlgorithm + * @param {String} passphrase Passphrase containing user input + * @returns {Uint8Array} Produced key with a length corresponding to + * hashAlgorithm hash length + */ +S2K.prototype.produce_key = async function (passphrase, numBytes) { + passphrase = _util2.default.encode_utf8(passphrase); + + async function round(prefix, s2k) { + const algorithm = _enums2.default.write(_enums2.default.hash, s2k.algorithm); + + switch (s2k.type) { + case 'simple': + return _crypto2.default.hash.digest(algorithm, _util2.default.concatUint8Array([prefix, passphrase])); + + case 'salted': + return _crypto2.default.hash.digest(algorithm, _util2.default.concatUint8Array([prefix, s2k.salt, passphrase])); + + case 'iterated': + { + const count = s2k.get_count(); + const data = _util2.default.concatUint8Array([s2k.salt, passphrase]); + const datalen = data.length; + const isp = new Uint8Array(prefix.length + count + datalen); + isp.set(prefix); + for (let pos = prefix.length; pos < count; pos += datalen) { + isp.set(data, pos); + } + return _crypto2.default.hash.digest(algorithm, isp.subarray(0, prefix.length + count)); + } + case 'gnu': + throw new Error("GNU s2k type not supported."); + + default: + throw new Error("Unknown s2k type."); + } + } + + const arr = []; + let rlength = 0; + const prefix = new Uint8Array(numBytes); + + for (let i = 0; i < numBytes; i++) { + prefix[i] = 0; + } + + let i = 0; + while (rlength < numBytes) { + const result = await round(prefix.subarray(0, i), this); + arr.push(result); + rlength += result.length; + i++; + } + + return _util2.default.concatUint8Array(arr).subarray(0, numBytes); +}; + +S2K.fromClone = function (clone) { + const s2k = new S2K(); + s2k.algorithm = clone.algorithm; + s2k.type = clone.type; + s2k.c = clone.c; + s2k.salt = clone.salt; + return s2k; +}; + +exports.default = S2K; + +},{"../config":80,"../crypto":95,"../enums.js":114,"../util.js":153}],153:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); // GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/* eslint-disable no-console */ + +/** + * This object contains utility functions + * @requires address-rfc2822 + * @requires web-stream-tools + * @requires config + * @requires encoding/base64 + * @module util + */ + +// re-import module to access util functions + + +var _addressRfc = require('address-rfc2822'); + +var _addressRfc2 = _interopRequireDefault(_addressRfc); + +var _webStreamTools = require('web-stream-tools'); + +var _webStreamTools2 = _interopRequireDefault(_webStreamTools); + +var _config = require('./config'); + +var _config2 = _interopRequireDefault(_config); + +var _util = require('./util'); + +var _util2 = _interopRequireDefault(_util); + +var _base = require('./encoding/base64'); + +var _base2 = _interopRequireDefault(_base); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +exports.default = { + isString: function isString(data) { + return typeof data === 'string' || String.prototype.isPrototypeOf(data); + }, + + isArray: function isArray(data) { + return Array.prototype.isPrototypeOf(data); + }, + + isUint8Array: _webStreamTools2.default.isUint8Array, + + isStream: _webStreamTools2.default.isStream, + + /** + * Get transferable objects to pass buffers with zero copy (similar to "pass by reference" in C++) + * See: https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage + * Also, convert ReadableStreams to MessagePorts + * @param {Object} obj the options object to be passed to the web worker + * @returns {Array} an array of binary data to be passed + */ + getTransferables: function getTransferables(obj, zero_copy) { + const transferables = []; + _util2.default.collectTransferables(obj, transferables, zero_copy); + return transferables.length ? transferables : undefined; + }, + + collectTransferables: function collectTransferables(obj, collection, zero_copy) { + if (!obj) { + return; + } + + if (_util2.default.isUint8Array(obj)) { + if (zero_copy && collection.indexOf(obj.buffer) === -1 && !(navigator.userAgent.indexOf('Version/11.1') !== -1 || // Safari 11.1 + (navigator.userAgent.match(/Chrome\/(\d+)/) || [])[1] < 56 && navigator.userAgent.indexOf('Edge') === -1 // Chrome < 56 + )) { + collection.push(obj.buffer); + } + return; + } + if (Object.prototype.isPrototypeOf(obj)) { + Object.entries(obj).forEach(([key, value]) => { + // recursively search all children + if (_util2.default.isStream(value)) { + if (value.locked) { + obj[key] = null; + } else { + const transformed = _webStreamTools2.default.transformPair(value, async readable => { + const reader = _webStreamTools2.default.getReader(readable); + + var _ref = new MessageChannel(); + + const port1 = _ref.port1, + port2 = _ref.port2; + + port1.onmessage = async function ({ data: { action } }) { + if (action === 'read') { + try { + const result = await reader.read(); + port1.postMessage(result, _util2.default.getTransferables(result)); + } catch (e) { + port1.postMessage({ error: e.message }); + } + } else if (action === 'cancel') { + await transformed.cancel(); + port1.postMessage(); + } + }; + obj[key] = port2; + collection.push(port2); + }); + } + return; + } + if (Object.prototype.toString.call(value) === '[object MessagePort]') { + throw new Error("Can't transfer the same stream twice."); + } + _util2.default.collectTransferables(value, collection, zero_copy); + }); + } + }, + + /** + * Convert MessagePorts back to ReadableStreams + * @param {Object} obj + * @returns {Object} + */ + restoreStreams: function restoreStreams(obj) { + if (Object.prototype.isPrototypeOf(obj) && !Uint8Array.prototype.isPrototypeOf(obj)) { + Object.entries(obj).forEach(([key, value]) => { + // recursively search all children + if (Object.prototype.toString.call(value) === '[object MessagePort]') { + obj[key] = new ReadableStream({ + pull(controller) { + return new Promise(resolve => { + value.onmessage = evt => { + var _evt$data = evt.data; + const done = _evt$data.done, + value = _evt$data.value, + error = _evt$data.error; + + if (error) { + controller.error(new Error(error)); + } else if (!done) { + controller.enqueue(value); + } else { + controller.close(); + } + resolve(); + }; + value.postMessage({ action: 'read' }); + }); + }, + cancel() { + return new Promise(resolve => { + value.onmessage = resolve; + value.postMessage({ action: 'cancel' }); + }); + } + }, { highWaterMark: 0 }); + return; + } + _util2.default.restoreStreams(value); + }); + } + return obj; + }, + + readNumber: function readNumber(bytes) { + let n = 0; + for (let i = 0; i < bytes.length; i++) { + n += 256 ** i * bytes[bytes.length - 1 - i]; + } + return n; + }, + + writeNumber: function writeNumber(n, bytes) { + const b = new Uint8Array(bytes); + for (let i = 0; i < bytes; i++) { + b[i] = n >> 8 * (bytes - i - 1) & 0xFF; + } + + return b; + }, + + readDate: function readDate(bytes) { + const n = _util2.default.readNumber(bytes); + const d = new Date(n * 1000); + return d; + }, + + writeDate: function writeDate(time) { + const numeric = Math.floor(time.getTime() / 1000); + + return _util2.default.writeNumber(numeric, 4); + }, + + normalizeDate: function normalizeDate(time = Date.now()) { + return time === null || time === Infinity ? time : new Date(Math.floor(+time / 1000) * 1000); + }, + + /** + * Create hex string from a binary + * @param {String} str String to convert + * @returns {String} String containing the hexadecimal values + */ + str_to_hex: function str_to_hex(str) { + if (str === null) { + return ""; + } + const r = []; + const e = str.length; + let c = 0; + let h; + while (c < e) { + h = str.charCodeAt(c++).toString(16); + while (h.length < 2) { + h = "0" + h; + } + r.push("" + h); + } + return r.join(''); + }, + + /** + * Create binary string from a hex encoded string + * @param {String} str Hex string to convert + * @returns {String} + */ + hex_to_str: function hex_to_str(hex) { + let str = ''; + for (let i = 0; i < hex.length; i += 2) { + str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); + } + return str; + }, + + /** + * Convert a Uint8Array to an MPI-formatted Uint8Array. + * Note: the output is **not** an MPI object. + * @see {@link module:type/mpi/MPI.fromUint8Array} + * @see {@link module:type/mpi/MPI.toUint8Array} + * @param {Uint8Array} bin An array of 8-bit integers to convert + * @returns {Uint8Array} MPI-formatted Uint8Array + */ + Uint8Array_to_MPI: function Uint8Array_to_MPI(bin) { + const size = (bin.length - 1) * 8 + _util2.default.nbits(bin[0]); + const prefix = Uint8Array.from([(size & 0xFF00) >> 8, size & 0xFF]); + return _util2.default.concatUint8Array([prefix, bin]); + }, + + /** + * Convert a Base-64 encoded string an array of 8-bit integer + * + * Note: accepts both Radix-64 and URL-safe strings + * @param {String} base64 Base-64 encoded string to convert + * @returns {Uint8Array} An array of 8-bit integers + */ + b64_to_Uint8Array: function b64_to_Uint8Array(base64) { + return _base2.default.decode(base64.replace(/-/g, '+').replace(/_/g, '/')); + }, + + /** + * Convert an array of 8-bit integer to a Base-64 encoded string + * @param {Uint8Array} bytes An array of 8-bit integers to convert + * @param {bool} url If true, output is URL-safe + * @returns {String} Base-64 encoded string + */ + Uint8Array_to_b64: function Uint8Array_to_b64(bytes, url) { + return _base2.default.encode(bytes, url).replace(/(\n)/g, ''); + }, + + /** + * Convert a hex string to an array of 8-bit integers + * @param {String} hex A hex string to convert + * @returns {Uint8Array} An array of 8-bit integers + */ + hex_to_Uint8Array: function hex_to_Uint8Array(hex) { + const result = new Uint8Array(hex.length >> 1); + for (let k = 0; k < hex.length >> 1; k++) { + result[k] = parseInt(hex.substr(k << 1, 2), 16); + } + return result; + }, + + /** + * Convert an array of 8-bit integers to a hex string + * @param {Uint8Array} bytes Array of 8-bit integers to convert + * @returns {String} Hexadecimal representation of the array + */ + Uint8Array_to_hex: function Uint8Array_to_hex(bytes) { + const r = []; + const e = bytes.length; + let c = 0; + let h; + while (c < e) { + h = bytes[c++].toString(16); + while (h.length < 2) { + h = "0" + h; + } + r.push("" + h); + } + return r.join(''); + }, + + /** + * Convert a string to an array of 8-bit integers + * @param {String} str String to convert + * @returns {Uint8Array} An array of 8-bit integers + */ + str_to_Uint8Array: function str_to_Uint8Array(str) { + return _webStreamTools2.default.transform(str, str => { + if (!_util2.default.isString(str)) { + throw new Error('str_to_Uint8Array: Data must be in the form of a string'); + } + + const result = new Uint8Array(str.length); + for (let i = 0; i < str.length; i++) { + result[i] = str.charCodeAt(i); + } + return result; + }); + }, + + /** + * Convert an array of 8-bit integers to a string + * @param {Uint8Array} bytes An array of 8-bit integers to convert + * @returns {String} String representation of the array + */ + Uint8Array_to_str: function Uint8Array_to_str(bytes) { + bytes = new Uint8Array(bytes); + const result = []; + const bs = 1 << 14; + const j = bytes.length; + + for (let i = 0; i < j; i += bs) { + result.push(String.fromCharCode.apply(String, bytes.subarray(i, i + bs < j ? i + bs : j))); + } + return result.join(''); + }, + + /** + * Convert a native javascript string to a Uint8Array of utf8 bytes + * @param {String|ReadableStream} str The string to convert + * @returns {Uint8Array|ReadableStream} A valid squence of utf8 bytes + */ + encode_utf8: function encode_utf8(str) { + const encoder = new TextEncoder('utf-8'); + // eslint-disable-next-line no-inner-declarations + function process(value, lastChunk = false) { + return encoder.encode(value, { stream: !lastChunk }); + } + return _webStreamTools2.default.transform(str, process, () => process('', true)); + }, + + /** + * Convert a Uint8Array of utf8 bytes to a native javascript string + * @param {Uint8Array|ReadableStream} utf8 A valid squence of utf8 bytes + * @returns {String|ReadableStream} A native javascript string + */ + decode_utf8: function decode_utf8(utf8) { + const decoder = new TextDecoder('utf-8'); + // eslint-disable-next-line no-inner-declarations + function process(value, lastChunk = false) { + return decoder.decode(value, { stream: !lastChunk }); + } + return _webStreamTools2.default.transform(utf8, process, () => process(new Uint8Array(), true)); + }, + + /** + * Concat a list of Uint8Arrays, Strings or Streams + * The caller must not mix Uint8Arrays with Strings, but may mix Streams with non-Streams. + * @param {Array} Array of Uint8Arrays/Strings/Streams to concatenate + * @returns {Uint8Array|String|ReadableStream} Concatenated array + */ + concat: _webStreamTools2.default.concat, + + /** + * Concat Uint8Arrays + * @param {Array} Array of Uint8Arrays to concatenate + * @returns {Uint8Array} Concatenated array + */ + concatUint8Array: _webStreamTools2.default.concatUint8Array, + + /** + * Check Uint8Array equality + * @param {Uint8Array} first array + * @param {Uint8Array} second array + * @returns {Boolean} equality + */ + equalsUint8Array: function equalsUint8Array(array1, array2) { + if (!_util2.default.isUint8Array(array1) || !_util2.default.isUint8Array(array2)) { + throw new Error('Data must be in the form of a Uint8Array'); + } + + if (array1.length !== array2.length) { + return false; + } + + for (let i = 0; i < array1.length; i++) { + if (array1[i] !== array2[i]) { + return false; + } + } + return true; + }, + + /** + * Calculates a 16bit sum of a Uint8Array by adding each character + * codes modulus 65535 + * @param {Uint8Array} Uint8Array to create a sum of + * @returns {Uint8Array} 2 bytes containing the sum of all charcodes % 65535 + */ + write_checksum: function write_checksum(text) { + let s = 0; + for (let i = 0; i < text.length; i++) { + s = s + text[i] & 0xFFFF; + } + return _util2.default.writeNumber(s, 2); + }, + + /** + * Helper function to print a debug message. Debug + * messages are only printed if + * @link module:config/config.debug is set to true. + * @param {String} str String of the debug message + */ + print_debug: function print_debug(str) { + if (_config2.default.debug) { + console.log(str); + } + }, + + /** + * Helper function to print a debug message. Debug + * messages are only printed if + * @link module:config/config.debug is set to true. + * Different than print_debug because will call Uint8Array_to_hex iff necessary. + * @param {String} str String of the debug message + */ + print_debug_hexarray_dump: function print_debug_hexarray_dump(str, arrToHex) { + if (_config2.default.debug) { + str += ': ' + _util2.default.Uint8Array_to_hex(arrToHex); + console.log(str); + } + }, + + /** + * Helper function to print a debug message. Debug + * messages are only printed if + * @link module:config/config.debug is set to true. + * Different than print_debug because will call str_to_hex iff necessary. + * @param {String} str String of the debug message + */ + print_debug_hexstr_dump: function print_debug_hexstr_dump(str, strToHex) { + if (_config2.default.debug) { + str += _util2.default.str_to_hex(strToHex); + console.log(str); + } + }, + + /** + * Helper function to print a debug error. Debug + * messages are only printed if + * @link module:config/config.debug is set to true. + * @param {String} str String of the debug message + */ + print_debug_error: function print_debug_error(error) { + if (_config2.default.debug) { + console.error(error); + } + }, + + /** + * Read a stream to the end and print it to the console when it's closed. + * @param {String} str String of the debug message + * @param {ReadableStream|Uint8array|String} input Stream to print + * @param {Function} concat Function to concatenate chunks of the stream (defaults to util.concat). + */ + print_entire_stream: function print_entire_stream(str, input, concat) { + _webStreamTools2.default.readToEnd(_webStreamTools2.default.clone(input), concat).then(result => { + console.log(str + ': ', result); + }); + }, + + getLeftNBits: function getLeftNBits(array, bitcount) { + const rest = bitcount % 8; + if (rest === 0) { + return array.subarray(0, bitcount / 8); + } + const bytes = (bitcount - rest) / 8 + 1; + const result = array.subarray(0, bytes); + return _util2.default.shiftRight(result, 8 - rest); // +String.fromCharCode(string.charCodeAt(bytes -1) << (8-rest) & 0xFF); + }, + + // returns bit length of the integer x + nbits: function nbits(x) { + let r = 1; + let t = x >>> 16; + if (t !== 0) { + x = t; + r += 16; + } + t = x >> 8; + if (t !== 0) { + x = t; + r += 8; + } + t = x >> 4; + if (t !== 0) { + x = t; + r += 4; + } + t = x >> 2; + if (t !== 0) { + x = t; + r += 2; + } + t = x >> 1; + if (t !== 0) { + x = t; + r += 1; + } + return r; + }, + + /** + * If S[1] == 0, then double(S) == (S[2..128] || 0); + * otherwise, double(S) == (S[2..128] || 0) xor + * (zeros(120) || 10000111). + * + * Both OCB and EAX (through CMAC) require this function to be constant-time. + * + * @param {Uint8Array} data + */ + double: function double(data) { + const double_var = new Uint8Array(data.length); + const last = data.length - 1; + for (let i = 0; i < last; i++) { + double_var[i] = data[i] << 1 ^ data[i + 1] >> 7; + } + double_var[last] = data[last] << 1 ^ (data[0] >> 7) * 0x87; + return double_var; + }, + + /** + * Shift a Uint8Array to the right by n bits + * @param {Uint8Array} array The array to shift + * @param {Integer} bits Amount of bits to shift (MUST be smaller + * than 8) + * @returns {String} Resulting array. + */ + shiftRight: function shiftRight(array, bits) { + if (bits) { + for (let i = array.length - 1; i >= 0; i--) { + array[i] >>= bits; + if (i > 0) { + array[i] |= array[i - 1] << 8 - bits; + } + } + } + return array; + }, + + /** + * Get native Web Cryptography api, only the current version of the spec. + * The default configuration is to use the api when available. But it can + * be deactivated with config.use_native + * @returns {Object} The SubtleCrypto api or 'undefined' + */ + getWebCrypto: function getWebCrypto() { + if (!_config2.default.use_native) { + return; + } + + return typeof window !== 'undefined' && window.crypto && window.crypto.subtle; + }, + + /** + * Get native Web Cryptography api for all browsers, including legacy + * implementations of the spec e.g IE11 and Safari 8/9. The default + * configuration is to use the api when available. But it can be deactivated + * with config.use_native + * @returns {Object} The SubtleCrypto api or 'undefined' + */ + getWebCryptoAll: function getWebCryptoAll() { + if (!_config2.default.use_native) { + return; + } + + if (typeof window !== 'undefined') { + if (window.crypto) { + return window.crypto.subtle || window.crypto.webkitSubtle; + } + if (window.msCrypto) { + return window.msCrypto.subtle; + } + } + }, + + /** + * Detect Node.js runtime. + */ + detectNode: function detectNode() { + return typeof window === 'undefined'; + }, + + /** + * Get native Node.js module + * @param {String} The module to require + * @returns {Object} The required module or 'undefined' + */ + nodeRequire: function nodeRequire(module) { + if (!_util2.default.detectNode()) { + return; + } + + // Requiring the module dynamically allows us to access the native node module. + // otherwise, it gets replaced with the browserified version + // eslint-disable-next-line import/no-dynamic-require + return require(module); + }, + + /** + * Get native Node.js crypto api. The default configuration is to use + * the api when available. But it can also be deactivated with config.use_native + * @returns {Object} The crypto module or 'undefined' + */ + getNodeCrypto: function getNodeCrypto() { + if (!_config2.default.use_native) { + return; + } + + return _util2.default.nodeRequire('crypto'); + }, + + getNodeZlib: function getNodeZlib() { + if (!_config2.default.use_native) { + return; + } + + return _util2.default.nodeRequire('zlib'); + }, + + /** + * Get native Node.js Buffer constructor. This should be used since + * Buffer is not available under browserify. + * @returns {Function} The Buffer constructor or 'undefined' + */ + getNodeBuffer: function getNodeBuffer() { + return (_util2.default.nodeRequire('buffer') || {}).Buffer; + }, + + getNodeStream: function getNodeStream() { + return (_util2.default.nodeRequire('stream') || {}).Readable; + }, + + getHardwareConcurrency: function getHardwareConcurrency() { + if (_util2.default.detectNode()) { + const os = _util2.default.nodeRequire('os'); + return os.cpus().length; + } + + return navigator.hardwareConcurrency || 1; + }, + + isEmailAddress: function isEmailAddress(data) { + if (!_util2.default.isString(data)) { + return false; + } + const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+([a-zA-Z]{2,}|xn--[a-zA-Z\-0-9]+)))$/; + return re.test(data); + }, + + /** + * Format user id for internal use. + */ + formatUserId: function formatUserId(id) { + // name, email address and comment can be empty but must be of the correct type + if (id.name && !_util2.default.isString(id.name) || id.email && !_util2.default.isEmailAddress(id.email) || id.comment && !_util2.default.isString(id.comment)) { + throw new Error('Invalid user id format'); + } + const components = []; + if (id.name) { + components.push(id.name); + } + if (id.comment) { + components.push(`(${id.comment})`); + } + if (id.email) { + components.push(`<${id.email}>`); + } + return components.join(' '); + }, + + /** + * Parse user id. + */ + parseUserId: function parseUserId(userid) { + if (userid.length > _config2.default.max_userid_length) { + throw new Error('User id string is too long'); + } + try { + var _rfc2822$parse = _addressRfc2.default.parse(userid), + _rfc2822$parse2 = _slicedToArray(_rfc2822$parse, 1), + _rfc2822$parse2$ = _rfc2822$parse2[0]; + + const name = _rfc2822$parse2$.phrase, + email = _rfc2822$parse2$.address, + comment = _rfc2822$parse2$.comment; + + return { name, email, comment: comment.replace(/^\(|\)$/g, '') }; + } catch (e) { + throw new Error('Invalid user id format'); + } + }, + + /** + * Normalize line endings to \r\n + */ + canonicalizeEOL: function canonicalizeEOL(text) { + return _webStreamTools2.default.transform(_util2.default.nativeEOL(text), value => value.replace(/\r/g, "\n").replace(/\n/g, "\r\n")); + }, + + /** + * Convert line endings from canonicalized \r\n to native \n + */ + nativeEOL: function nativeEOL(text) { + let lastChar = ''; + return _webStreamTools2.default.transform(text, value => { + value = lastChar + value; + if (value[value.length - 1] === '\r') { + lastChar = '\r'; + value = value.slice(0, -1); + } else { + lastChar = ''; + } + return value.replace(/\r\n/g, '\n'); + }, () => lastChar); + }, + + /** + * Remove trailing spaces and tabs from each line + */ + removeTrailingSpaces: function removeTrailingSpaces(text) { + return text.split('\n').map(line => { + let i = line.length - 1; + for (; i >= 0 && (line[i] === ' ' || line[i] === '\t'); i--); + return line.substr(0, i + 1); + }).join('\n'); + }, + + /** + * Encode input buffer using Z-Base32 encoding. + * See: https://tools.ietf.org/html/rfc6189#section-5.1.6 + * + * @param {Uint8Array} data The binary data to encode + * @returns {String} Binary data encoded using Z-Base32 + */ + encodeZBase32: function encodeZBase32(data) { + if (data.length === 0) { + return ""; + } + const ALPHABET = "ybndrfg8ejkmcpqxot1uwisza345h769"; + const SHIFT = 5; + const MASK = 31; + let buffer = data[0]; + let index = 1; + let bitsLeft = 8; + let result = ''; + while (bitsLeft > 0 || index < data.length) { + if (bitsLeft < SHIFT) { + if (index < data.length) { + buffer <<= 8; + buffer |= data[index++] & 0xff; + bitsLeft += 8; + } else { + const pad = SHIFT - bitsLeft; + buffer <<= pad; + bitsLeft += pad; + } + } + bitsLeft -= SHIFT; + result += ALPHABET[MASK & buffer >> bitsLeft]; + } + return result; + } +}; + +},{"./config":80,"./encoding/base64":113,"./util":153,"address-rfc2822":2,"web-stream-tools":76}],154:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); // OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2018 Wiktor Kwapisiewicz +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @fileoverview This class implements a client for the Web Key Directory (wkd) protocol + * in order to lookup keys on designated servers. + * See: https://datatracker.ietf.org/doc/draft-koch-openpgp-webkey-service/ + * @module wkd + */ + +var _util = require('./util'); + +var _util2 = _interopRequireDefault(_util); + +var _crypto = require('./crypto'); + +var _crypto2 = _interopRequireDefault(_crypto); + +var _key = require('./key'); + +var keyMod = _interopRequireWildcard(_key); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Initialize the WKD client + * @constructor + */ +function WKD() { + this._fetch = typeof window !== 'undefined' ? window.fetch : require('node-fetch'); +} + +/** + * Search for a public key using Web Key Directory protocol. + * @param {String} options.email User's email. + * @param {Boolean} options.rawBytes Returns Uint8Array instead of parsed key. + * @returns {Promise, + * err: (Array|null)}>} The public key. + * @async + */ +WKD.prototype.lookup = async function (options) { + const fetch = this._fetch; + + if (!options.email) { + throw new Error('You must provide an email parameter!'); + } + + if (!_util2.default.isEmailAddress(options.email)) { + throw new Error('Invalid e-mail address.'); + } + + var _$exec = /(.*)@(.*)/.exec(options.email), + _$exec2 = _slicedToArray(_$exec, 3); + + const localPart = _$exec2[1], + domain = _$exec2[2]; + + const localEncoded = _util2.default.encodeZBase32((await _crypto2.default.hash.sha1(_util2.default.str_to_Uint8Array(localPart.toLowerCase())))); + + const url = `https://${domain}/.well-known/openpgpkey/hu/${localEncoded}`; + + return fetch(url).then(function (response) { + if (response.status === 200) { + return response.arrayBuffer(); + } + }).then(function (publicKey) { + if (publicKey) { + const rawBytes = new Uint8Array(publicKey); + if (options.rawBytes) { + return rawBytes; + } + return keyMod.read(rawBytes); + } + }); +}; + +exports.default = WKD; + +},{"./crypto":95,"./key":117,"./util":153,"node-fetch":"node-fetch"}],155:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _util = require('../util.js'); + +var _util2 = _interopRequireDefault(_util); + +var _config = require('../config'); + +var _config2 = _interopRequireDefault(_config); + +var _crypto = require('../crypto'); + +var _crypto2 = _interopRequireDefault(_crypto); + +var _packet = require('../packet'); + +var _packet2 = _interopRequireDefault(_packet); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Initializes a new proxy and loads the web worker + * @param {String} path The path to the worker or 'openpgp.worker.js' by default + * @param {Number} n number of workers to initialize if path given + * @param {Object} config config The worker configuration + * @param {Array} worker alternative to path parameter: web worker initialized with 'openpgp.worker.js' + * @constructor + */ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @fileoverview Provides functions for maintaining browser workers + * @see module:openpgp.initWorker + * @see module:openpgp.getWorker + * @see module:openpgp.destroyWorker + * @see module:worker/worker + * @requires util + * @requires config + * @requires crypto + * @requires packet + * @module worker/async_proxy + */ + +function AsyncProxy({ path = 'openpgp.worker.js', n = 1, workers = [], config } = {}) { + /** + * Message handling + */ + const handleMessage = workerId => event => { + const msg = event.data; + switch (msg.event) { + case 'loaded': + this.workers[workerId].loadedResolve(true); + break; + case 'method-return': + if (msg.err) { + // fail + const err = new Error(msg.err); + // add worker stack + err.workerStack = msg.stack; + this.tasks[msg.id].reject(err); + } else { + // success + this.tasks[msg.id].resolve(msg.data); + } + delete this.tasks[msg.id]; + this.workers[workerId].requests--; + break; + case 'request-seed': + this.seedRandom(workerId, msg.amount); + break; + default: + throw new Error('Unknown Worker Event.'); + } + }; + + if (workers.length) { + this.workers = workers; + } else { + this.workers = []; + while (this.workers.length < n) { + this.workers.push(new Worker(path)); + } + } + + let workerId = 0; + this.workers.forEach(worker => { + worker.loadedPromise = new Promise(resolve => { + worker.loadedResolve = resolve; + }); + worker.requests = 0; + worker.onmessage = handleMessage(workerId++); + worker.onerror = e => { + worker.loadedResolve(false); + console.error('Unhandled error in openpgp worker: ' + e.message + ' (' + e.filename + ':' + e.lineno + ')'); + return false; + }; + + if (config) { + worker.postMessage({ event: 'configure', config }); + } + }); + + // Cannot rely on task order being maintained, use object keyed by request ID to track tasks + this.tasks = {}; + this.currentID = 0; +} + +/** + * Returns a promise that resolves when all workers have finished loading + * @returns {Promise} Resolves to true if all workers have loaded succesfully; false otherwise +*/ +AsyncProxy.prototype.loaded = async function () { + const loaded = await Promise.all(this.workers.map(worker => worker.loadedPromise)); + return loaded.every(Boolean); +}; + +/** + * Get new request ID + * @returns {integer} New unique request ID +*/ +AsyncProxy.prototype.getID = function () { + return this.currentID++; +}; + +/** + * Send message to worker with random data + * @param {Integer} size Number of bytes to send + * @async + */ +AsyncProxy.prototype.seedRandom = async function (workerId, size) { + const buf = await _crypto2.default.random.getRandomBytes(size); + this.workers[workerId].postMessage({ event: 'seed-random', buf }, _util2.default.getTransferables(buf, true)); +}; + +/** + * Terminates the workers + */ +AsyncProxy.prototype.terminate = function () { + this.workers.forEach(worker => { + worker.terminate(); + }); +}; + +/** + * Generic proxy function that handles all commands from the public api. + * @param {String} method the public api function to be delegated to the worker thread + * @param {Object} options the api function's options + * @returns {Promise} see the corresponding public api functions for their return types + * @async + */ +AsyncProxy.prototype.delegate = function (method, options) { + + const id = this.getID(); + const requests = this.workers.map(worker => worker.requests); + const minRequests = Math.min(...requests); + let workerId = 0; + for (; workerId < this.workers.length; workerId++) { + if (this.workers[workerId].requests === minRequests) { + break; + } + } + + return new Promise((_resolve, reject) => { + // clone packets (for web worker structured cloning algorithm) + this.workers[workerId].postMessage({ id: id, event: method, options: _packet2.default.clone.clonePackets(options) }, _util2.default.getTransferables(options, _config2.default.zero_copy)); + this.workers[workerId].requests++; + + // remember to handle parsing cloned packets from worker + this.tasks[id] = { resolve: data => _resolve(_packet2.default.clone.parseClonedPackets(_util2.default.restoreStreams(data), method)), reject }; + }); +}; + +exports.default = AsyncProxy; + +},{"../config":80,"../crypto":95,"../packet":126,"../util.js":153}]},{},[116])(116) +}); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..48e341a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3 @@ +{ + "lockfileVersion": 1 +} diff --git a/privacy-policy.md b/privacy-policy.md new file mode 100644 index 0000000..0c00441 --- /dev/null +++ b/privacy-policy.md @@ -0,0 +1,35 @@ +--- +layout: page +title: Privacy Policy +description: I encourage you to read the whole document. It's not particularly long or difficult to understand. +subtitle: What's being done with your data? +permalink: /privacy/ +cover: /assets/posts/privacy.png +--- +# Privacy Policy +This will hopefully be the briefest "legal" document you've ever read as well as the most readable. If you need additional information [let me know](/contact) and I'll add it. + +## IP Address +Some applications (Gitea, Mastodon, Mumble, XMPP) collect your IP when you register. At the moment, that information is kept indefinitely. However, I'm working on either completely disabling it or setting something up that will periodically delete stored IP addresses. When I do, this document will be updated accordingly. + +If you don't want me to have that information to begin with, just use [Tor Browser](https://www.torproject.org/). + +## Email Address +When you register for a service using an email address, that is obviously collected. You can control whether it's a real one or not. Even though I can see them for services like Gitea and Mastodon, I don't care and won't send you unsolicited mail. + +**Note:** whatever address you use for git is visible in commits. + +## Browser Fingerprint +Your web browser communicates uniquely identifying information to all websites it visits by allowing the site to know details about your operating system, browser information, plugins installed, fonts installed, screen resolution, and [much more](https://panopticlick.eff.org/). I don't care about that either and, if some services do collect that for their own use (I'm 99% sure none of them do), I'm not going to look at it ¯\\_(ツ)\_/¯ + +## Usage and storage of collected information +**For most services:** Whatever data is collected is stored on one server in Germany and won't be shared with any third parties whatsoever. + +**For Nextcloud:** Whatever data is collected is stored on one server in my living room and won't be shared with any third parties either. User's files are encrypted at rest so no one can hack into my server and steal them. I do have the encryption key so I *could* decrypt and view your files. I'm not going to bother with that though because I don't have any interest in looking at your personal stuff. That's your business and I won't invade your privacy. + + +# Exceptions +I do live in the US; one server is here and the other is in Germany. If, for whatever reason, I'm compelled by law enforcement to give up your email, IP address, or any other information, I will. *I don't want to*. As such, I do whatever I can to make sure *I don't have that information*. If I don't have it, I can't share it. + +# Recommendations +To mitigate invasions of privacy like this, use a throwaway email address for registration, such as one from [cock.li](https://cock.li/), or [ProtonMail](https://protonmail.com/), provide a [fake name](https://fakena.me/fake-name/), and use the service from behind [Tor](https://www.torproject.org/) or a VPN. Rather than a VPN, however, I *strongly* recommend using Tor across all devices. They have an [Android version](https://www.torproject.org/download/#android) now and there's another browser for iOS that they recommend called [Onion Browser](https://apps.apple.com/us/app/onion-browser/id519296448). I don't use iOS so I can't say whether or not it's any good, just that the Tor Project recommends it below the Android section. diff --git a/protocol-handler.js b/protocol-handler.js new file mode 100644 index 0000000..9377295 --- /dev/null +++ b/protocol-handler.js @@ -0,0 +1 @@ +window.addEventListener("load",function(){var e=document.getElementById("register");e&&e.addEventListener("click",function(){navigator.registerProtocolHandler("openpgp4fpr","https://metacode.biz/openpgp/resolve/%s","Search for key on the keyserver")});var r=document.getElementById("search");r&&r.addEventListener("submit",function(t){t.preventDefault(),t.disabled=!0;var o=t.target.querySelector("ul");o.textContent="Checking OpenPGP key using Web Key Directory...";var e=t.target.querySelector("input").value;fetch("./wkd-checker",{method:"POST",body:JSON.stringify({email:e})}).then(function(e){return e.json()}).then(function(e){var r=[];r.push({category:"info",value:"Key address",url:e.key.url}),200!==e.key.code?r.push({category:"error",value:"Key not found"}):(r.push({category:"ok",value:"Key file found"}),e.key.response.startsWith("9")?r.push({category:"ok",value:"Key format correct"}):r.push({category:"error",value:"Key format error: key should be in binary form, not ASCII-armored"}),e.key.cors?(r.push({category:"ok",value:"Key is accessible from all sites"}),e["fake-key"].cors?r.push({category:"ok",value:"Not found keys also generate CORS headers"}):r.push({category:"warn",value:"Not found keys do not have CORS headers"})):r.push({category:"warn",value:"Key is not accessible, add 'Access-Control-Allow-Origin: *' header"})),200!==e.policy.code?r.push({category:"error",value:"Policy not found. Policy file can be empty",url:e.policy.url}):r.push({category:"ok",value:"Policy file found"}),o.innerHTML="",r.map(function(e){var r=document.createElement("LI");if(r.textContent=e.value,r.className=e.category,e.url){var t=document.createElement("A");t.href=e.url,t.textContent=e.url,t.target="_blank",t.setAttribute("rel","nofollow noopener"),r.textContent+=": ",r.appendChild(t)}return r}).forEach(o.appendChild.bind(o)),console.log(r),t.disabled=!1})})}); diff --git a/scripts.js b/scripts.js new file mode 100644 index 0000000..741cb1d --- /dev/null +++ b/scripts.js @@ -0,0 +1,537 @@ +System.register("local", [], function (exports_1, context_1) { + "use strict"; + var __moduleName = context_1 && context_1.id; + function createElement(name, attributes, ...children) { + return { + name, + attributes: attributes || {}, + children: Array.prototype.concat(...(children || [])) + }; + } + exports_1("createElement", createElement); + return { + setters: [], + execute: function () { + } + }; +}); +System.register("renderer", [], function (exports_2, context_2) { + "use strict"; + var __moduleName = context_2 && context_2.id; + function render(element) { + if (element == null) + return ''; + if (typeof element !== "object") + element = String(element); + if (typeof element === "string") + return element.replace(/&/g, '&').replace(//g, '>'); + //if (element instanceof Raw) return element.html; + console.assert(!!element.attributes, 'Element attributes must be defined:\n' + JSON.stringify(element)); + const elementAttributes = element.attributes; + let attributes = Object.keys(elementAttributes).filter(key => { + const value = elementAttributes[key]; + return value != null; + }).map(key => { + const value = elementAttributes[key]; + if (value === true) { + return key; + } + return `${key}="${String(value).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"')}"`; + }).join(' '); + if (attributes.length > 0) { + attributes = ' ' + attributes; + } + const children = element.children.length > 0 ? `>${element.children.map(child => render(child)).join('')}` : '>'; + return `<${element.name}${attributes}${children}`; + } + exports_2("render", render); + return { + setters: [], + execute: function () { + } + }; +}); +/* + Copyright 2019 Wiktor Kwapisiewicz + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +System.register("openpgp-key", ["local", "renderer"], function (exports_3, context_3) { + "use strict"; + var local, renderer, proofs, dateFormat; + var __moduleName = context_3 && context_3.id; + function getLatestSignature(signatures, date = new Date()) { + let signature = signatures[0]; + for (let i = 1; i < signatures.length; i++) { + if (signatures[i].created >= signature.created && + (signatures[i].created <= date || date === null)) { + signature = signatures[i]; + } + } + return signature; + } + function getVerifier(proofUrl, fingerprint) { + for (const proof of proofs) { + const matches = proofUrl.match(proof.matcher); + if (!matches) + continue; + const bound = Object.entries(proof.variables).map(([key, value]) => [key, matches[value || 0]]).reduce((previous, current) => { previous[current[0]] = current[1]; return previous; }, { FINGERPRINT: fingerprint }); + const profile = proof.profile.replace(/\{([A-Z]+)\}/g, (_, name) => bound[name]); + const proofJson = proof.proof.replace(/\{([A-Z]+)\}/g, (_, name) => bound[name]); + const username = proof.username.replace(/\{([A-Z]+)\}/g, (_, name) => bound[name]); + return { + profile, + proofUrl, + proofJson, + username, + service: proof.service, + checks: (proof.checks || []).map((check) => ({ + relation: check.relation, + proof: check.proof, + claim: check.claim.replace(/\{([A-Z]+)\}/g, (_, name) => bound[name]) + })) + }; + } + return null; + } + async function verify(proofJson, checks) { + const response = await fetch(proofJson, { + headers: { + Accept: 'application/json' + }, + credentials: 'omit' + }); + if (!response.ok) { + throw new Error('Response failed: ' + response.status); + } + const json = await response.json(); + for (const check of checks) { + const proofValue = check.proof.reduce((previous, current) => { + if (current == null || previous == null) + return null; + if (Array.isArray(previous) && typeof current === 'string') { + return previous.map(value => value[current]); + } + return previous[current]; + }, json); + const claimValue = check.claim; + if (check.relation === 'eq') { + if (proofValue !== claimValue) { + throw new Error(`Proof value ${proofValue} !== claim value ${claimValue}`); + } + } + else if (check.relation === 'contains') { + if (!proofValue || proofValue.indexOf(claimValue) === -1) { + throw new Error(`Proof value ${proofValue} does not contain claim value ${claimValue}`); + } + } + else if (check.relation === 'oneOf') { + if (!proofValue || proofValue.indexOf(claimValue) === -1) { + throw new Error(`Proof value ${proofValue} does not contain claim value ${claimValue}`); + } + } + } + } + function serviceToClassName(service) { + if (service === 'github') { + return 'fab fa-github'; + } + else if (service === 'reddit') { + return 'fab fa-reddit'; + } + else if (service === 'hackernews') { + return 'fab fa-hacker-news'; + } + else if (service === 'mastodon') { + return 'fab fa-mastodon'; + } + else if (service === 'dns') { + return 'fas fa-globe'; + } + else { + return ''; + } + } + async function lookupKey(query) { + const result = document.getElementById('result'); + result.innerHTML = renderer.render(local.createElement("span", null, + "Looking up ", + query, + "...")); + let keys, keyUrl; + const keyLink = document.querySelector('[rel="pgpkey"]'); + if (!keyLink) { + const keyserver = document.querySelector('meta[name="keyserver"]').content; + keyUrl = `https://${keyserver}/pks/lookup?op=get&options=mr&search=${query}`; + const response = await fetch(keyUrl); + const key = await response.text(); + keys = (await openpgp.key.readArmored(key)).keys; + } + else { + keyUrl = keyLink.href; + const response = await fetch(keyUrl); + const key = await response.arrayBuffer(); + keys = (await openpgp.key.read(new Uint8Array(key))).keys; + } + if (keys.length > 0) { + loadKeys(keyUrl, keys).catch(e => { + result.innerHTML = renderer.render(local.createElement("span", null, + "Could not display this key: ", + String(e))); + }); + } + else { + result.innerHTML = renderer.render(local.createElement("span", null, + query, + ": not found")); + } + } + async function loadKeys(keyUrl, _keys) { + const key = _keys[0]; + window.key = key; + const primaryUser = await key.getPrimaryUser(); + for (var i = key.users.length - 1; i >= 0; i--) { + try { + if (await key.users[i].verify(key.primaryKey) === openpgp.enums.keyStatus.valid) { + continue; + } + } + catch (e) { + console.error('User verification error:', e); + } + //key.users.splice(i, 1); + } + for (const user of key.users) { + user.revoked = await user.isRevoked(); + } + const lastPrimarySig = primaryUser.selfCertification; + const keys = [{ + fingerprint: key.primaryKey.getFingerprint(), + status: await key.verifyPrimaryKey(), + keyFlags: lastPrimarySig.keyFlags, + created: key.primaryKey.created, + algorithmInfo: key.primaryKey.getAlgorithmInfo(), + expirationTime: lastPrimarySig.getExpirationTime() + }]; + //console.log(lastPrimarySig); + const proofs = (lastPrimarySig.notations || []) + .filter((notation) => notation[0] === 'proof@metacode.biz' && typeof notation[1] === 'string') + .map((notation) => notation[1]) + .map((proofUrl) => getVerifier(proofUrl, key.primaryKey.getFingerprint())) + .filter((verifier) => !!verifier); + /* + proofs.push(getVerifier('https://www.reddit.com/user/wiktor-k/comments/bo5oih/test/', key.primaryKey.getFingerprint())); + proofs.push(getVerifier('https://news.ycombinator.com/user?id=wiktor-k', key.primaryKey.getFingerprint())); + proofs.push(getVerifier('https://gist.github.com/wiktor-k/389d589dd19250e1f9a42bc3d5d40c16', key.primaryKey.getFingerprint())); + proofs.push(getVerifier('https://metacode.biz/@wiktor', key.primaryKey.getFingerprint())); + proofs.push(getVerifier('dns:metacode.biz?type=TXT', key.primaryKey.getFingerprint())); + */ + for (const subKey of key.subKeys) { + const lastSig = getLatestSignature(subKey.bindingSignatures); + let reasonForRevocation; + if (subKey.revocationSignatures.length > 0) { + reasonForRevocation = subKey.revocationSignatures[subKey.revocationSignatures.length - 1].reasonForRevocationString; + } + keys.push({ + fingerprint: subKey.keyPacket.getFingerprint(), + status: await subKey.verify(key.primaryKey), + reasonForRevocation, + keyFlags: lastSig.keyFlags, + created: lastSig.created, + algorithmInfo: subKey.keyPacket.getAlgorithmInfo(), + expirationTime: await subKey.getExpirationTime() + }); + } + //key.users.splice(primaryUser.index, 1); + const profileHash = await openpgp.crypto.hash.md5(openpgp.util.str_to_Uint8Array(primaryUser.user.userId.email)).then((u) => openpgp.util.str_to_hex(openpgp.util.Uint8Array_to_str(u))); + const now = new Date(); + // there is index property on primaryUser + document.title = primaryUser.user.userId.name + ' - OpenPGP key'; + const info = local.createElement("div", null, + local.createElement("div", { class: "wrapper" }, + local.createElement("div", { class: "bio" }, + local.createElement("img", { class: "avatar", src: "https://seccdn.libravatar.org/avatar/" + profileHash + "?s=148&d=" + encodeURIComponent("https://www.gravatar.com/avatar/" + profileHash + "?s=148&d=mm") }), + local.createElement("h2", null, primaryUser.user.userId.name)), + local.createElement("div", null, + local.createElement("ul", { class: "props" }, + local.createElement("li", { title: key.primaryKey.getFingerprint() }, + local.createElement("a", { href: keyUrl, target: "_blank", rel: "nofollow noopener" }, + "\uD83D\uDD11\u00A0", + local.createElement("code", null, key.primaryKey.getFingerprint()))), + key.users.filter((user) => !user.revoked && user.userId).map((user) => user.userId.email).filter((email) => !!email).map((email) => local.createElement("li", null, + local.createElement("a", { href: "mailto:" + email }, + "\uD83D\uDCE7 ", + email + //formatAttribute(user.userAttribute) + ))), + proofs.filter((proof) => !!proof).map((proof) => local.createElement("li", null, + local.createElement("a", { rel: "me noopener nofollow", target: "_blank", href: proof.profile }, + local.createElement("i", { class: serviceToClassName(proof.service) }), + proof.username), + local.createElement("a", { rel: "noopener nofollow", target: "_blank", href: proof.proofUrl, class: "proof", "data-proof-json": proof.proofJson, "data-checks": JSON.stringify(proof.checks) }, + local.createElement("i", { class: "fas fa-certificate" }), + "proof")))))), + local.createElement("details", null, + local.createElement("summary", null, "\uD83D\uDD12 Encrypt"), + local.createElement("textarea", { placeholder: "Message to encrypt...", id: "message" }), + local.createElement("input", { type: "button", value: "Encrypt", id: "encrypt" }), + ' ', + local.createElement("input", { type: "button", id: "send", "data-recipient": primaryUser.user.userId.email, value: "Send to " + primaryUser.user.userId.email })), + local.createElement("details", null, + local.createElement("summary", null, "\uD83D\uDD8B Verify"), + local.createElement("textarea", { placeholder: "Clearsigned message to verify...", id: "signed" }), + local.createElement("input", { type: "button", value: "Verify", id: "verify" })), + local.createElement("details", null, + local.createElement("summary", null, "\uD83D\uDD11 Key details"), + local.createElement("p", null, "Subkeys:"), + local.createElement("ul", null, keys.map((subKey) => local.createElement("li", null, + local.createElement("div", null, + getStatus(subKey.status, subKey.reasonForRevocation), + " ", + getIcon(subKey.keyFlags), + " ", + local.createElement("code", null, subKey.fingerprint.substring(24).match(/.{4}/g).join(" ")), + " ", + formatAlgorithm(subKey.algorithmInfo.algorithm), + " (", + subKey.algorithmInfo.bits, + ")"), + local.createElement("div", null, + "created: ", + formatDate(subKey.created), + ", expire", + now > subKey.expirationTime ? "d" : "s", + ": ", + formatDate(subKey.expirationTime))))))); + document.getElementById('result').innerHTML = renderer.render(info); + checkProofs(); + } + async function checkProofs() { + const proofs = document.querySelectorAll('[data-checks]'); + for (const proofLink of proofs) { + const checks = JSON.parse(proofLink.dataset.checks || ''); + const url = proofLink.dataset.proofJson || ''; + try { + await verify(url, checks); + proofLink.textContent = 'verified proof'; + proofLink.classList.add('verified'); + } + catch (e) { + console.error('Could not verify proof: ' + e); + } + } + } + async function verifyProof(e) { + const target = e.target; + if (target.id === 'encrypt') { + const text = document.getElementById('message'); + openpgp.encrypt({ + message: openpgp.message.fromText(text.value), + publicKeys: [window.key], + armor: true + }).then((cipherText) => { + text.value = cipherText.data; + }, (e) => alert(e)); + } + else if (target.id === 'send') { + location.href = "mailto:" + target.dataset.recipient + "?subject=Encrypted%20message&body=" + encodeURIComponent(document.getElementById('message').value); + } + else if (target.id === 'verify') { + const text = document.getElementById('signed'); + const message = await openpgp.cleartext.readArmored(text.value); + const verified = await openpgp.verify({ + message, + publicKeys: [window.key] + }); + console.log(verified); + alert('The signature is ' + (verified.signatures[0].valid ? '✅ correct.' : '❌ incorrect.')); + } + } + function formatAttribute(userAttribute) { + if (userAttribute.attributes[0][0] === String.fromCharCode(1)) { + return local.createElement("img", { src: "data:image/jpeg;base64," + btoa(userAttribute.attributes[0].substring(17)) }); + } + if (userAttribute.attributes[0][0] === 'e') { + const url = userAttribute.attributes[0].substring(userAttribute.attributes[0].indexOf('@') + 1); + return local.createElement("a", { href: url, rel: "noopener nofollow" }, url); + } + return 'unknown attribute'; + } + function formatAlgorithm(name) { + if (name === 'rsa_encrypt_sign') + return "RSA"; + return name; + } + function formatDate(date) { + if (date === Infinity) + return "never"; + if (typeof date === 'number') + return 'x'; + return dateFormat.format(date); + } + function getStatus(status, details) { + if (status === openpgp.enums.keyStatus.invalid) { + return local.createElement("span", { title: "Invalid key" }, "\u274C"); + } + if (status === openpgp.enums.keyStatus.expired) { + return local.createElement("span", { title: "Key expired" }, "\u23F0"); + } + if (status === openpgp.enums.keyStatus.revoked) { + return local.createElement("span", { title: "Key revoked: " + details }, "\u274C"); + } + if (status === openpgp.enums.keyStatus.valid) { + return local.createElement("span", { title: "Valid key" }, "\u2705"); + } + if (status === openpgp.enums.keyStatus.no_self_cert) { + return local.createElement("span", { title: "Key not certified" }, "\u274C"); + } + return "unknown:" + status; + } + function getIcon(keyFlags) { + if (!keyFlags || !keyFlags[0]) { + return ""; + } + let flags = []; + if ((keyFlags[0] & openpgp.enums.keyFlags.certify_keys) !== 0) { + flags.push(local.createElement("span", { title: "Certyfing key" }, "\uD83C\uDFF5\uFE0F")); + } + if ((keyFlags[0] & openpgp.enums.keyFlags.sign_data) !== 0) { + flags.push(local.createElement("span", { title: 'Signing key' }, "\uD83D\uDD8B")); + } + if (((keyFlags[0] & openpgp.enums.keyFlags.encrypt_communication) !== 0) || + ((keyFlags[0] & openpgp.enums.keyFlags.encrypt_storage) !== 0)) { + flags.push(local.createElement("span", { title: 'Encryption key' }, "\uD83D\uDD12")); + } + if ((keyFlags[0] & openpgp.enums.keyFlags.authentication) !== 0) { + flags.push(local.createElement("span", { title: 'Authentication key' }, "\uD83D\uDCB3")); + } + return flags; + } + return { + setters: [ + function (local_1) { + local = local_1; + }, + function (renderer_1) { + renderer = renderer_1; + } + ], + execute: function () { + openpgp.config.show_version = false; + openpgp.config.show_comment = false; + proofs = [ + { + matcher: /^https:\/\/gist\.github\.com\/([A-Za-z0-9_-]+)\/([0-9a-f]+)$/, + variables: { + USERNAME: 1, + PROOFID: 2 + }, + profile: 'https://github.com/{USERNAME}', + proof: 'https://api.github.com/gists/{PROOFID}', + username: '{USERNAME}', + service: 'github', + checks: [{ + relation: 'eq', + proof: ['owner', 'login'], + claim: '{USERNAME}' + }, { + relation: 'eq', + proof: ['owner', 'html_url'], + claim: 'https://github.com/{USERNAME}' + }, { + relation: 'contains', + proof: ['files', 'openpgp.md', 'content'], + claim: '[Verifying my OpenPGP key: openpgp4fpr:{FINGERPRINT}]' + }] + }, + { + matcher: /^https:\/\/news\.ycombinator\.com\/user\?id=([A-Za-z0-9-]+)$/, + variables: { + USERNAME: 1, + PROFILE: 0 + }, + profile: '{PROFILE}', + proof: 'https://hacker-news.firebaseio.com/v0/user/{USERNAME}.json', + username: '{USERNAME}', + service: 'hackernews', + checks: [{ + relation: 'contains', + proof: ['about'], + claim: '[Verifying my OpenPGP key: openpgp4fpr:{FINGERPRINT}]' + }] + }, + { + matcher: /^https:\/\/www\.reddit\.com\/user\/([^/]+)\/comments\/([^/]+)\/([^/]+\/)?$/, + variables: { + USERNAME: 1, + PROOF: 2 + }, + profile: 'https://www.reddit.com/user/{USERNAME}', + proof: 'https://www.reddit.com/user/{USERNAME}/comments/{PROOF}.json', + username: '{USERNAME}', + service: 'reddit', + checks: [{ + relation: 'contains', + proof: [0, 'data', 'children', 0, 'data', 'selftext'], + claim: 'Verifying my OpenPGP key: openpgp4fpr:{FINGERPRINT}' + }, { + relation: 'eq', + proof: [0, 'data', 'children', 0, 'data', 'author'], + claim: '{USERNAME}' + }] + }, + { + matcher: /^https:\/\/([^/]+)\/@([A-Za-z0-9_-]+)$/, + variables: { + INSTANCE: 1, + USERNAME: 2, + PROFILE: 0 + }, + profile: '{PROFILE}', + proof: '{PROFILE}', + username: '@{USERNAME}@{INSTANCE}', + service: 'mastodon', + checks: [{ + relation: 'oneOf', + proof: ['attachment', 'value'], + claim: '{FINGERPRINT}' + }] + }, + { + matcher: /^dns:([^?]+)\?type=TXT$/, + variables: { + DOMAIN: 1 + }, + profile: 'https://{DOMAIN}', + proof: 'https://dns.google.com/resolve?name={DOMAIN}&type=TXT', + username: '{DOMAIN}', + service: 'dns', + checks: [{ + relation: 'oneOf', + proof: ['Answer', 'data'], + claim: '\"openpgp4fpr:{FINGERPRINT}\"' + }] + } + ]; + window.onload = window.onhashchange = function () { + lookupKey(location.hash.substring(1)); + }; + ; + document.addEventListener('click', verifyProof); + dateFormat = new Intl.DateTimeFormat(undefined, { + year: 'numeric', month: 'numeric', day: 'numeric', + hour: 'numeric', minute: 'numeric', second: 'numeric', + }); + } + }; +}); diff --git a/verification.txt b/verification.txt new file mode 100644 index 0000000..5fcf77e --- /dev/null +++ b/verification.txt @@ -0,0 +1,4 @@ +This file claims ownership of the OpenPGP key with long id 0x51fd40936db0065b. + +Token for proof: +[Verifying my OpenPGP key: openpgp4fpr:75dd32ca1bfeea6ae40d823c51fd40936db0065b]