[Masque] Encrypting between client and proxy

Martin Thomson <mt@lowentropy.net> Tue, 15 November 2022 23:44 UTC

Return-Path: <mt@lowentropy.net>
X-Original-To: masque@ietfa.amsl.com
Delivered-To: masque@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id BBB5FC14F74D for <masque@ietfa.amsl.com>; Tue, 15 Nov 2022 15:44:21 -0800 (PST)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -7.097
X-Spam-Level:
X-Spam-Status: No, score=-7.097 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_HI=-5, RCVD_IN_ZEN_BLOCKED_OPENDNS=0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001, URIBL_DBL_BLOCKED_OPENDNS=0.001, URIBL_ZEN_BLOCKED_OPENDNS=0.001] autolearn=ham autolearn_force=no
Authentication-Results: ietfa.amsl.com (amavisd-new); dkim=pass (2048-bit key) header.d=lowentropy.net header.b=MJVUmp5W; dkim=pass (2048-bit key) header.d=messagingengine.com header.b=s544s++l
Received: from mail.ietf.org ([50.223.129.194]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id wkCAqVFg0slC for <masque@ietfa.amsl.com>; Tue, 15 Nov 2022 15:44:17 -0800 (PST)
Received: from wout5-smtp.messagingengine.com (wout5-smtp.messagingengine.com [64.147.123.21]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id 269FBC1526EB for <masque@ietf.org>; Tue, 15 Nov 2022 15:44:16 -0800 (PST)
Received: from compute3.internal (compute3.nyi.internal [10.202.2.43]) by mailout.west.internal (Postfix) with ESMTP id 6BF9F3200A58 for <masque@ietf.org>; Tue, 15 Nov 2022 18:44:16 -0500 (EST)
Received: from imap41 ([10.202.2.91]) by compute3.internal (MEProxy); Tue, 15 Nov 2022 18:44:16 -0500
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lowentropy.net; h=cc:content-type:date:date:from:from:in-reply-to:message-id :mime-version:reply-to:sender:subject:subject:to:to; s=fm2; t= 1668555856; x=1668642256; bh=mhN5UIJsghG4NqUtOxi2ym5ZuIWKFJA+YWT ScRe3IdM=; b=MJVUmp5WbliS9F2fL9qtICs3+aA8NxGfFtUu+LF70XDkkWpuwUC l9SqvMnIM9OoYnI6QOp2T67lobgjyHErTzFL5SWQOocWpqNBAKp0HXSa1GVPh3vS 2+1GpVAxRjgasl+OafMtwHqfnrmHh3zYNACyzRzmj2hahtUtGlQjl9+ZXl/8Arph TDcl01MvK38QxsHnKUwAsdfpPdnOYJQJospvOjTlKaH7pqP1PQltzhDIGR6N1fJ7 gpPxCB1WbnAgc4N5lYTEoe+wpy3p1HHqUslHykyYUzSOfjnFStOZ+ehkJXSfxvFe ufwKIRc8dFf3KIz1dicuRXHwafmbCGJGX9w==
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:date:date:feedback-id :feedback-id:from:from:in-reply-to:message-id:mime-version :reply-to:sender:subject:subject:to:to:x-me-proxy:x-me-proxy :x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t=1668555856; x= 1668642256; bh=mhN5UIJsghG4NqUtOxi2ym5ZuIWKFJA+YWTScRe3IdM=; b=s 544s++lY2z/yCER1qbNM0ef9BFuMmpGETrcs/p9Nyh0y75vRXcld/fW46VY1JQ6a AVF9sA+LLJQs1z55Qu3l0Lo4cuumLI74Vp019H8wIqUen5xK/yAlS5qNmKEdFb4D g+KDkdHf9IKl0w6JuokIfIe9yElUMDv/kmNKXoeZJ5XHdEgg9p/e37UAw2XF/J1s SIZTZjZmsex7FuHNDtrc4z6VjKFBHCNZT8maZTBJkGvA5BKk4fOmnqm94JoL+Ths P4Zo+2tOIoZtdsSvXekuSSX5rGPmQ+xJqmMGvXhHYb1iqiqRU0komHGbU+SXXwxb uHHOQ6FrCD130FTyzwHkA==
X-ME-Sender: <xms:TyR0Y42ojsfI8JgoxP4Q_0V4Zt_oOXj92qfn2F75FyDoAvdx6UFdgw> <xme:TyR0YzGOHhOKMVA9RxPxU8CaGygOhqgJmU-3pBZIxwHEM-LsFsiNZrZAvWAmw_3pi 0SPQd7NpQQA0U_ElKY>
X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvgedrgeehgddufecutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecuogfuuhhsphgvtghtffhomhgrihhnucdlgeelmdenuc fjughrpefofgggkfffhffvufgtsehttdertderredtnecuhfhrohhmpedfofgrrhhtihhn ucfvhhhomhhsohhnfdcuoehmtheslhhofigvnhhtrhhophihrdhnvghtqeenucggtffrrg htthgvrhhnpeffudejkeethedtffffteeifeejhfdutdejveduteffhfekieelveeuueeg geekveenucffohhmrghinhepghhithhhuhgsrdhiohenucevlhhushhtvghrufhiiigvpe dtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehmtheslhhofigvnhhtrhhophihrdhnvght
X-ME-Proxy: <xmx:TyR0Yw7pTiKTZihMoQ8C8pLI8sWAIv5mMZkPdZQ5_HRHYvimo6j-8w> <xmx:TyR0Yx07x8KYByUUFt3H30m1mNp3A4ftc7oCGvugXtLTBKfSBZ2VAw> <xmx:TyR0Y7H97861OIB9J7we5919auNx-fxTOBhgLhLhnA0oYu_5ScO5-A> <xmx:UCR0Y_S3xM81chMQnorQ0xxHx2DJBuxSnBkKBjKFcS3NvNfflEVk1w>
Feedback-ID: ic129442d:Fastmail
Received: by mailuser.nyi.internal (Postfix, from userid 501) id C90F1234007E; Tue, 15 Nov 2022 18:44:15 -0500 (EST)
X-Mailer: MessagingEngine.com Webmail Interface
User-Agent: Cyrus-JMAP/3.7.0-alpha0-1115-g8b801eadce-fm-20221102.001-g8b801ead
Mime-Version: 1.0
Message-Id: <8e6fb1e1-6454-4706-a61a-53be58acaa61@betaapp.fastmail.com>
Date: Wed, 16 Nov 2022 10:43:56 +1100
From: Martin Thomson <mt@lowentropy.net>
To: masque@ietf.org
Content-Type: text/plain
Archived-At: <https://mailarchive.ietf.org/arch/msg/masque/fOzULPV-rppVEiYU71aWMIGbhcY>
Subject: [Masque] Encrypting between client and proxy
X-BeenThere: masque@ietf.org
X-Mailman-Version: 2.1.39
Precedence: list
List-Id: Multiplexed Application Substrate over QUIC Encryption <masque.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/masque>, <mailto:masque-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/masque/>
List-Post: <mailto:masque@ietf.org>
List-Help: <mailto:masque-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/masque>, <mailto:masque-request@ietf.org?subject=subscribe>
X-List-Received-Date: Tue, 15 Nov 2022 23:44:21 -0000

In draft-pauly-masque-quic-proxy, though connection IDs are replaced as packets hit the proxy, the content of those packets otherwise is unchanged.  This makes it trivial to correlate packets as they flow past a proxy[1].

In discussing this with a few people, it is clear that there are places where this is OK, but those cases are limited[2].  Generally, a MASQUE proxy provides traffic mixing for anonymization, ensuring that its active clients collectively form a loose anonymity set.  This is not achieved if data is only forwarded.

It seems like encryption is trivial.  In addition to replacing connection IDs on between the client and proxy, the remainder of the packet could be encrypted.  This does not need to include integrity protection as the underlying packet already has adequate end-to-end protection.  All we would need is obfuscation.

Key agreement is trivial.  Clients and proxies could send each other keys.  One key per connection ID might work, or - better still - the keys can be derived from a shared secret using a KDF that uses the connection ID as input.  The trick is in finding an encryption scheme that is sufficient strong and not subject to trivial attacks.  For example, a simple application of AES-CTR with a fixed nonce is not sufficient as it would allow an adversary to find the XOR of plaintexts by XORing ciphertexts.

A scheme that *might* work here is one that we can borrow from QUIC's packet protection:

Each packet is formed from three components: the semi-fixed first byte, a connection ID, and a protected payload.  The protected payload is high entropy, being comprised of ciphertext and is anywhere from 20 to 65506 bytes.  This payload can be split into two, with a 12 byte sample being used as a nonce for AES-CTR.  The remainder of the payload, plus part of the first byte, could then be protecting using AES-CTR.  The remaining 8+ bytes could again be sampled again as a nonce to protect the first sample.


first = packet.take(0)
cid = packet.take(cid_length)
sample = packet.take(12)
remainder = packet

// Keys might be stored for the connection ID or calculated something like this:
k1 = KDF(key=secret(cid), input=("k1" || cid))
k2 = KDF(key=secret(cid), input=("k2" || cid))

e_remainder = AES-CTR-ENC(key=k1, nonce=sample, pt=remainder)
e_first = AES-CTR-ENC(key=k1, nonce=(sample - 1 or something like that), pt=first)
e_sample = e_first ^ (AES-CTR-ENC(key=k2, nonce=e_remainder[0:12], pt=[0]) & 0x1f)
e_packet = e_first || replacement(cid) || e_sample || e_remainder


This is not an ideal design, but it seems like a reasonable start.  

An obvious problem is that it breaks down a little bit on very small packets.  A 12 byte sample is probably fine, but sampling the remain 8 bytes from a 20 byte packet means sending far less than 2^32 packets to avoid a noticeable chance of a collision.  You could sample the first byte as well, but that only provides a few more bits.  More on this in a follow-up.

Cheers,
Martin


[1] A few people have noted that timing tends to be a dead giveaway here, as does packet size.  I have some ideas about how that might be managed, but let's start with the basics.

[2] For example, some have claimed that the two-hop proxying mode (see https://intarchboard.github.io/draft-obliviousness/draft-kpw-iab-privacy-partitioning.html#figure-5) only requires one lot of encryption.  I'm not sure that this is true unless you extend trust in one proxy beyond not conspiring with the other proxy to also include it *not* monitoring traffic from the more remote peer (the client for proxy B or the server for proxy A).