RFC 8308

Extension Negotiation in the Secure Shell (SSH) Protocol

πŸ“… published March 2018, from ietf

RFC 8308: Extension Negotiation in the Secure Shell (SSH) Protocol πŸŽ€

okay bestie, this one is a whole spec πŸ¦‹ It came out in March 2018 from D. Bider at Bitvise Limited, and it's a Standards Track document, so it's the real deal, an official IETF (Internet Engineering Task Force) standard πŸ’… It also has the ISSN 2070-1721 if you're into that. It updates RFCs 4251, 4252, 4253, and 4254, which means it adds to them, so you have to read them together 🧚

Abstract

okay so this memo updates RFCs 4251, 4252, 4253, and 4254 by defining a way for Secure Shell (SSH) clients and servers to swap info about which protocol extensions they support, and they do it confidentially after SSH key exchange πŸ”βœ¨

Status of This Memo

this is an Internet Standards Track document πŸ’œ

it's a product of the Internet Engineering Task Force (IETF), so it represents the consensus of the IETF community. it got public review and was approved for publication by the Internet Engineering Steering Group (IESG). if you want more on Internet Standards, check Section 2 of RFC 7841 🌸

for the current status, any errata, and how to give feedback, go to https://www.rfc-editor.org/info/rfc8308 πŸ“

Copyright (c) 2018 IETF Trust and the persons identified as the document authors. All rights reserved. 🌈

this document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they stood on the publication date. please read these carefully, because they describe your rights and restrictions for this document 🫢

if you pull Code Components out of this document, they have to include the Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions. they come without warranty, as the Simplified BSD License says πŸ’…


Table of Contents

   1. Overview and Rationale ..........................................3
      1.1. Requirements Terminology ...................................3
      1.2. Wire Encoding Terminology ..................................3
   2. Extension Negotiation Mechanism .................................3
      2.1. Signaling of Extension Negotiation in SSH_MSG_KEXINIT ......3
      2.2. Enabling Criteria ..........................................4
      2.3. SSH_MSG_EXT_INFO Message ...................................4
      2.4. Message Order ..............................................5
      2.5. Interpretation of Extension Names and Values ...............6
   3. Initially Defined Extensions ....................................6
      3.1. "server-sig-algs" ..........................................6
      3.2. "delay-compression" ........................................7
           3.2.1. Awkwardly Timed Key Re-Exchange .....................9
           3.2.2. Subsequent Re-Exchange ..............................9
           3.2.3. Compatibility Note: OpenSSH up to Version 7.5 .......9
      3.3. "no-flow-control" .........................................10
           3.3.1. Prior "No Flow Control" Practice ...................10
      3.4. "elevation" ...............................................11
   4. IANA Considerations ............................................12
      4.1. Additions to Existing Registries ..........................12
      4.2. New Registry: Extension Names .............................12
           4.2.1. Future Assignments to Extension Names Registry .....12
   5. Security Considerations ........................................12
   6. References .....................................................13
      6.1. Normative References ......................................13
      6.2. Informative References ....................................13
   Acknowledgments ...................................................14
   Author's Address ..................................................14

1. Overview and Rationale ✨

okay so Secure Shell (SSH) is a super common protocol for secure communication on the Internet πŸ” the original design of the SSH transport layer [RFC4253] doesn't have proper extension negotiation, which is kind of a big deal πŸ™ˆ meanwhile, lots of implementations work really hard to make sure known message types don't contain any unrecognized info.

so it's hard for implementations to signal their capabilities and negotiate extensions without risking a disconnection πŸ’€ this problem came up while SSH was being updated to support RSA signatures using SHA-256 and SHA-512 [RFC8332]. to avoid trial and error (and authentication penalties), a client has to be able to figure out which public key algorithms a server accepts. this extension mechanism is what makes that discovery possible πŸ¦‹

this memo updates RFCs 4251, 4252, 4253, and 4254 πŸŽ€

1.1. Requirements Terminology

the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. πŸ’œ

1.2. Wire Encoding Terminology

the wire encoding types in this document ("byte", "uint32", "string", "boolean", "name-list") have the meanings described in [RFC4251]. 🧚

2. Extension Negotiation Mechanism 🌸

2.1. Signaling of Extension Negotiation in SSH_MSG_KEXINIT

applications implementing this mechanism MUST add one of the following indicator names to the kex_algorithms field in the SSH_MSG_KEXINIT message they send in the first key exchange:

  • When acting as server: "ext-info-s"
  • When acting as client: "ext-info-c"

the indicator name gets added without quotes, and it MAY be added at any position in the name-list, as long as it's properly separated from other names per name-list conventions. ✨

the names go into the kex_algorithms field because it's one of two name-list fields in SSH_MSG_KEXINIT that don't have a separate copy for each data direction πŸ“

the indicator names inserted by the client and the server are different, so they won't produce a match and won't affect the algorithm chosen in key exchange algorithm negotiation. πŸ’…

putting textual indicator names in there is meant to give implementers a clue for finding this mechanism πŸ¦‹

2.2. Enabling Criteria

if a client or server offers "ext-info-c" or "ext-info-s" respectively, it MUST be prepared to accept an SSH_MSG_EXT_INFO message from the peer. 🫢

a server only needs to send "ext-info-s" if it intends to process SSH_MSG_EXT_INFO from the client. a client only needs to send "ext-info-c" if it plans to process SSH_MSG_EXT_INFO from the server. 🌈

if a server receives an "ext-info-c", or a client receives an "ext-info-s", it MAY send an SSH_MSG_EXT_INFO message, but it is not required to do so. ⭐

neither party needs to wait for the other's SSH_MSG_KEXINIT before deciding whether to send the right indicator in its own SSH_MSG_KEXINIT. πŸ’–

implementations MUST NOT send an incorrect indicator name for their role. implementations MAY disconnect if the counterparty sends an incorrect indicator. if "ext-info-c" or "ext-info-s" ends up being negotiated as a key exchange method, the parties MUST disconnect. πŸ’€

2.3. SSH_MSG_EXT_INFO Message

a party that received the "ext-info-c" or "ext-info-s" indicator MAY send the following message:

     byte       SSH_MSG_EXT_INFO (value 7)
     uint32     nr-extensions
     repeat the following 2 fields "nr-extensions" times:
       string   extension-name
       string   extension-value (binary)

implementers should pay close attention to Section 2.5, especially the requirement to tolerate any sequence of bytes (including null bytes at any position) in an unknown extension's extension-value. πŸ™ˆ

2.4. Message Order

if a client sends SSH_MSG_EXT_INFO, it MUST send it as the next packet after the client's first SSH_MSG_NEWKEYS message to the server. 🧚

if a server sends SSH_MSG_EXT_INFO, it MAY send it at zero, one, or both of the following opportunities:

  • As the next packet following the server's first SSH_MSG_NEWKEYS.

okay so where clients need info from the server's SSH_MSG_EXT_INFO to authenticate, it's helpful if the server sends its SSH_MSG_EXT_INFO not only as the next packet after SSH_MSG_NEWKEYS, but without delay. ⏰

clients can't rely on this, because the server isn't required to send the message at this time, and if it does send it, the network might delay it. 😭 but if a timely SSH_MSG_EXT_INFO comes in, a client can pipeline an authentication request right after its SSH_MSG_SERVICE_REQUEST, even when it needs extension info. πŸš€

  • Immediately preceding the server's SSH_MSG_USERAUTH_SUCCESS, as defined in [RFC4252].

the server MAY send SSH_MSG_EXT_INFO at this second opportunity, whether or not it sent one at the first. a client that sent "ext-info-c" MUST accept a server's SSH_MSG_EXT_INFO at both opportunities, but it MUST NOT require one. πŸ‘‘

this lets a server reveal support for extra extensions it didn't want to show an unauthenticated client πŸ¦‹ if a server sends a second SSH_MSG_EXT_INFO, it replaces any initial one, and both the client and the server re-evaluate the extensions in effect. the server's second SSH_MSG_EXT_INFO is matched against the client's original. πŸ’…

the timing of this second opportunity was chosen for a reason. if the message went out earlier, the server couldn't hold back information until the client has authenticated. if it went out later, a client that needs info from the second SSH_MSG_EXT_INFO right after it authenticates would have no reliable way to know whether to expect the message. πŸ™ƒ

2.5. Interpretation of Extension Names and Values

each extension is identified by its extension-name and defines the conditions under which it's considered to be in effect. applications MUST ignore unrecognized extension-names. 🌸

when it's specified, an extension MAY dictate that both parties must include it in their SSH_MSG_EXT_INFO for it to take effect, or that it's enough for only one party to include it. other rules MAY be specified too. the relative order in which extensions appear in an SSH_MSG_EXT_INFO message MUST be ignored. ✨

extension-value fields are interpreted as defined by their respective extension. this field MAY be empty if the extension permits it. applications that don't implement or recognize an extension MUST ignore its extension-value, no matter its size or content. applications MUST tolerate any sequence of bytes (including null bytes at any position) in an unknown extension's extension-value. 🫢

the cumulative size of an SSH_MSG_EXT_INFO message is limited only by the maximum packet length an implementation may apply under [RFC4253]. implementations MUST accept well-formed SSH_MSG_EXT_INFO messages up to the maximum packet length they accept. πŸ’–

3. Initially Defined Extensions 🧚

3.1. "server-sig-algs" ✨

Okay bestie, this one is the server telling you which public key algorithms it's willing to deal with πŸ”‘ It's giving "here's my guest list" energy πŸ’…

This extension is sent with the following extension name and value:

     string      "server-sig-algs"
     name-list   public-key-algorithms-accepted

The name-list type is a strict subset of the string type, so it's totally allowed as an extension-value 🫢 See [RFC4251] for more information.

The server is the one who sends this extension. It's a list of public key algorithms the server can process as part of a "publickey" authentication request πŸ—οΈ If a client sends this extension, the server MAY ignore it and MAY disconnect πŸ’€

Here's the vibe check: in this extension, a server MUST enumerate all public key algorithms it might accept during user authentication. Ngl though, some early server implementations don't list every algorithm they accept, and those are still out there πŸ™ˆ So a client MAY send a user authentication request using a public key algorithm that isn't included in server-sig-algs.

A client that wants to do public key authentication MAY wait for the server's SSH_MSG_EXT_INFO. That way it can send a "publickey" request with an appropriate algorithm, instead of guessing and hoping for the best 🎯

Servers that implement public key authentication SHOULD implement this extension ✨

If a server doesn't send this extension, a client MUST NOT make any assumptions about the server's public key algorithm support, and it MAY proceed with authentication requests using trial and error. Also, implementations are known to exist that apply authentication penalties if the client tries an unexpected public key algorithm 😭

Authentication penalties are how servers stop brute-force password guessing, username enumeration, and other behavior that the administrators or implementers find sus 🚨 Penalties may include automatic IP address throttling or blocking, and they may trigger email alerts or auditing. Very dramatic, very real πŸ’…


3.2. "delay-compression" 🌸

This extension MAY be sent by both parties, like a group project where everyone gets to contribute 🫢

     string         "delay-compression"
     string:
       name-list    compression_algorithms_client_to_server
       name-list    compression_algorithms_server_to_client

The extension-value is a string that encodes two name-lists, and each name-list is encoded as a string too. For example, if you want to say you prefer "foo,bar" in the client-to-server direction and "bar,baz" in the server-to-client direction, you encode the extension-value like this (length included):

     00000016 00000007 666f6f2c626172 00000007 6261722c62617a

Either party can send this exact same encoding, client or server. No favorites here πŸ¦‹

This extension lets the server and client renegotiate compression algorithm support without doing a full key re-exchange. The new algorithms take effect immediately upon successful authentication, which is so efficient, I'm obsessed πŸ”₯

This extension only takes effect if both parties send it. Name-lists MAY include any compression algorithm that could have been negotiated in SSH_MSG_KEXINIT, except algorithms that define their own delayed compression semantics. So "zlib,none" is a valid algorithm list in this context, but "zlib@openssh.com" is not πŸ™…β€β™€οΈ

If both parties send this extension but the name-lists don't contain a common algorithm in either direction, the parties MUST disconnect the same way they would if negotiation failed as part of SSH_MSG_KEXINIT. Rough, but that's the rule 😭

If this extension takes effect, the renegotiated compression algorithm gets activated for the very next SSH message after the trigger message. The trigger message depends on who's sending:

  • Sent by the server, the trigger message is SSH_MSG_USERAUTH_SUCCESS.
  • Sent by the client, the trigger message is SSH_MSG_NEWCOMPRESS.

If this extension takes effect, the client MUST send the following message within a reasonable number of outgoing SSH messages after receiving SSH_MSG_USERAUTH_SUCCESS. It doesn't have to be the first outgoing message, though 🧚

     byte       SSH_MSG_NEWCOMPRESS (value 8)

So why does SSH_MSG_NEWCOMPRESS even exist? It's to avoid a race condition πŸƒβ€β™€οΈ The server can't reliably know whether a message from the client was sent before or after it received SSH_MSG_USERAUTH_SUCCESS. For example, clients may send keep-alive messages during logon processing, and that's where the mess comes from πŸ€·β€β™€οΈ

As is the case for all extensions unless otherwise noted, the server MAY delay including this extension until its secondary SSH_MSG_EXT_INFO, which gets sent before SSH_MSG_USERAUTH_SUCCESS. This lets the server hold off on advertising compression until the client has authenticated πŸ”

If the parties renegotiate compression using this extension in a session where compression is already enabled, and the renegotiated algorithm is the same in one or both directions, then the internal compression state MUST be reset for each direction at the time the renegotiated algorithm takes effect. Clean slate, bestie 🧹

3.2.1. Awkwardly Timed Key Re-Exchange 😬

A party that has signaled, or intends to signal, support for this extension in an SSH session MUST NOT initiate key re-exchange in that session until one of the following happens:

  • This extension was negotiated, and the party that's about to start key re-exchange already sent its trigger message for compression.
  • The party has sent (if server) or received (if client) the message SSH_MSG_USERAUTH_SUCCESS, and this extension was not negotiated.

If a party breaks this rule, the other party MAY disconnect πŸšͺ

In general, parties SHOULD NOT start key re-exchange before successful user authentication, but they MAY tolerate it if they aren't using this extension 🀝

3.2.2. Subsequent Re-Exchange πŸ”

In subsequent key re-exchanges that unambiguously begin after the compression trigger messages, the compression algorithms negotiated in the re-exchange override the algorithms negotiated with this extension. The newest negotiation wins, iykyk πŸ‘‘

3.2.3. Compatibility Note: OpenSSH up to Version 7.5 🫠

This extension uses a binary extension-value encoding. OpenSSH clients up to and including version 7.5 advertise support to receive SSH_MSG_EXT_INFO, but they disconnect when they receive an extension-value containing null bytes. This is an error that was fixed in OpenSSH version 7.6 βœ…

Implementations that want to interoperate with OpenSSH 7.5 and earlier are advised to check the remote party's SSH version string, and to omit this extension if an affected version is detected. Affected versions don't implement this extension, so there's no harm in leaving it out 🌷 The extension SHOULD NOT be omitted if the detected OpenSSH version is 7.6 or higher. Leaving it out there would make it harder for the OpenSSH project to implement this extension in a higher version, and nobody wants that πŸ™…β€β™€οΈ


3.3. "no-flow-control" πŸ¦‹

This extension is sent with the following extension name and value:

     string      "no-flow-control"
     string      choice of: "p" for preferred | "s" for supported

A party SHOULD send "s" if it supports "no-flow-control" but doesn't prefer to enable it. A party SHOULD send "p" if it prefers to enable the extension when the other party supports it too. Parties MAY disconnect if they receive a different extension value πŸ’…

For this extension to take effect, the following must occur:

  • This extension MUST be sent by both parties.
  • At least one party MUST have sent the value "p" (preferred).

If this extension takes effect, the "initial window size" fields in SSH_MSG_CHANNEL_OPEN and SSH_MSG_CHANNEL_OPEN_CONFIRMATION, as defined in [RFC4254], become meaningless πŸ«₯ The values of these fields MUST be ignored, and a channel behaves as if all window sizes are infinite. Neither side is required to send any SSH_MSG_CHANNEL_WINDOW_ADJUST messages, and if one is received, it MUST be ignored. Basically, the flow control gets turned off for the vibe 🌊

This extension is intended for (but not limited to) file transfer applications that only use one channel, where the flow control SSH provides is more of an impediment than a feature 🚚

Implementations MUST refuse to open more than one simultaneous channel when this extension is in effect. Nevertheless, server implementations SHOULD support clients opening more than one non-simultaneous channel. Rules are rules, but we still get to be nice πŸ₯°

3.3.1. Prior "No Flow Control" Practice πŸ“œ

Before this extension, some applications just didn't implement SSH flow control. They sent an initial channel window size of 2^32 - 1 instead. Applications SHOULD NOT do this, for these reasons:

  • It's plausible to transfer more than 2^32 bytes over a channel. A channel like that will hang if the other party implements SSH flow control according to [RFC4254] 😭
  • Some implementations can't handle large channel window sizes, and they can exhibit non-graceful behaviors, including disconnect πŸ’€

3.4. "elevation" πŸ›‘οΈ

Okay, so "elevation" and "elevated" refer to an operating system mechanism where an administrator's logon session is linked to two security contexts: one limited and one with administrative rights πŸ‘‘ To "elevate" a session means to activate the security context with full administrative rights. For more info about this mechanism on Windows, check out [WINADMIN] and [WINTOKEN] πŸͺŸ

This extension MAY be sent by the client as follows:

     string      "elevation"
     string      choice of: "y" | "n" | "d"

A client sends "y" to say it prefers the session to be elevated, "n" to say it doesn't want elevation, and "d" to tell the server to use its default behavior πŸŽ€ The server MAY disconnect if it receives a different extension value. If a client doesn't send the "elevation" extension, the server SHOULD act as if "d" was sent.

If a client has included this extension, then after authentication, a server that supports this extension SHOULD tell the client whether elevation was done. It does that by sending this global request:

     byte        SSH_MSG_GLOBAL_REQUEST
     string      "elevation"
     boolean     want reply = false
     boolean     elevation performed

Clients that implement this extension help reduce the attack surface for Windows servers that handle administrative logins, which is a whole main character move πŸ¦Έβ€β™€οΈ Where clients don't support this extension, servers must elevate sessions to allow full access by administrative users always. Where clients do support it, sessions can be created without elevation unless someone requests it 🌈


4. IANA Considerations πŸ“‹

4.1. Additions to Existing Registries πŸ—‚οΈ

IANA has added the following entries to the "Message Numbers" registry [IANA-M], under the "Secure Shell (SSH) Protocol Parameters" registry [RFC4250]:

     Value    Message ID             Reference
     -----------------------------------------
     7        SSH_MSG_EXT_INFO       RFC 8308
     8        SSH_MSG_NEWCOMPRESS    RFC 8308

IANA has also added the following entries to the "Key Exchange Method Names" registry [IANA-KE]:

     Method Name     Reference      Note
     ------------------------------------------
     ext-info-s      RFC 8308       Section 2
     ext-info-c      RFC 8308       Section 2

4.2. New Registry: Extension Names πŸ†•

Also under the "Secure Shell (SSH) Protocol Parameters" registry, IANA has created a brand new "Extension Names" registry, with the following initial content. Slay πŸ’–

     Extension Name       Reference       Note
     ------------------------------------------------
     server-sig-algs      RFC 8308        Section 3.1
     delay-compression    RFC 8308        Section 3.2
     no-flow-control      RFC 8308        Section 3.3
     elevation            RFC 8308        Section 3.4

4.2.1. Future Assignments to Extension Names Registry πŸ’…

okay bestie, looking ahead πŸ‘€ names in the "Extension Names" registry MUST follow the naming conventions from [RFC4250], Section 4.6.1. no skipping this one πŸ’–

if you want a brand-new non-local name (aka one that does not include the '@' character πŸ™ˆ), it MUST go through the IETF Review policy described in [RFC8126]. that's the vibe check for the registry, and it's giving careful gatekeeping ✨

5. Security Considerations πŸ”’

okay the security part is SERIOUS bestie πŸ˜­πŸ”’ security considerations are discussed throughout this whole document, not just in one sneaky corner, so keep your eyes peeled πŸ‘€

this document updates the SSH protocol as defined in [RFC4251] and related documents, so it's literally the SSH protocol getting a glow-up πŸ’œ and the security considerations of [RFC4251] apply. no exceptions, no excuses πŸ’€

6. References πŸ“š

6.1. Normative References πŸ’œ

here are the normative references, all of them, no cap 🫢

[RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
           Requirement Levels", BCP 14, RFC 2119,
           DOI 10.17487/RFC2119, March 1997,
           <https://www.rfc-editor.org/info/rfc2119>.

[RFC4250]  Lehtinen, S. and C. Lonvick, Ed., "The Secure Shell (SSH)
           Protocol Assigned Numbers", RFC 4250,
           DOI 10.17487/RFC4250, January 2006,
           <https://www.rfc-editor.org/info/rfc4250>.

[RFC4251]  Ylonen, T. and C. Lonvick, Ed., "The Secure Shell (SSH)
           Protocol Architecture", RFC 4251, DOI 10.17487/RFC4251,
           January 2006, <https://www.rfc-editor.org/info/rfc4251>.

[RFC4252]  Ylonen, T. and C. Lonvick, Ed., "The Secure Shell (SSH)
           Authentication Protocol", RFC 4252, DOI 10.17487/RFC4252,
           January 2006, <https://www.rfc-editor.org/info/rfc4252>.

[RFC4253]  Ylonen, T. and C. Lonvick, Ed., "The Secure Shell (SSH)
           Transport Layer Protocol", RFC 4253, DOI 10.17487/RFC4253,
           January 2006, <https://www.rfc-editor.org/info/rfc4253>.

[RFC4254]  Ylonen, T. and C. Lonvick, Ed., "The Secure Shell (SSH)
           Connection Protocol", RFC 4254, DOI 10.17487/RFC4254,
           January 2006, <https://www.rfc-editor.org/info/rfc4254>.

[RFC8126]  Cotton, M., Leiba, B., and T. Narten, "Guidelines for
           Writing an IANA Considerations Section in RFCs", BCP 26,
           RFC 8126, DOI 10.17487/RFC8126, June 2017,
           <https://www.rfc-editor.org/info/rfc8126>.

[RFC8174]  Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
           2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
           May 2017, <https://www.rfc-editor.org/info/rfc8174>.

6.2. Informative References 🧚

and now the informative ones, which are still very much worth your time ✨

[IANA-KE]  IANA, "Key Exchange Method Names",
           <https://www.iana.org/assignments/ssh-parameters/>.

[IANA-M]   IANA, "Message Numbers",
           <https://www.iana.org/assignments/ssh-parameters/>.

[RFC8332]  Bider, D., "Use of RSA Keys with SHA-256 and SHA-512 in
           the Secure Shell (SSH) Protocol", RFC 8332,
           DOI 10.17487/RFC8332, March 2018,
           <https://www.rfc-editor.org/info/rfc8332>.

[WINADMIN] Microsoft, "How to launch a process as a Full
           Administrator when UAC is enabled?", March 2013,
           <https://blogs.msdn.microsoft.com/winsdk/2013/03/22/
           how-to-launch-a-process-as-a-full-administrator-when-
           uac-is-enabled/>.

[WINTOKEN] Microsoft, "TOKEN_ELEVATION_TYPE enumeration",
           <https://msdn.microsoft.com/en-us/library/windows/desktop/
           bb530718.aspx>.

Acknowledgments 🫢

huge thank-yous to Markus Friedl and Damien Miller for the comments and the initial implementation πŸ’– and a big group hug for Peter Gutmann, Roumen Petrov, Mark D. Baushke, Daniel Migault, Eric Rescorla, Matthew A. Miller, Mirja Kuehlewind, Adam Roach, Spencer Dawkins, Alexey Melnikov, and Ben Campbell for the reviews and feedback 🌸 you all are literally the reason this is so good 😭✨

Author's Address 🌸

the person behind it all, with contact details right here πŸ¦‹

Denis Bider
Bitvise Limited
4105 Lombardy Court
Colleyville, TX  76034
United States of America

Email: ietf-ssh3@denisbider.com
URI:   https://www.bitvise.com/
πŸ“¦ where this page comes from