Delivery-date: Sat, 21 Dec 2024 14:04:25 -0800 Received: from mail-qv1-f56.google.com ([209.85.219.56]) by mail.fairlystable.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1tP7aB-0007rZ-Vn for bitcoindev@gnusha.org; Sat, 21 Dec 2024 14:04:25 -0800 Received: by mail-qv1-f56.google.com with SMTP id 6a1803df08f44-6d92f0737besf43747586d6.1 for ; Sat, 21 Dec 2024 14:04:23 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1734818657; cv=pass; d=google.com; s=arc-20240605; b=lJJvm3YpKndT82fK2F7szDlI7636xAg4ZknONCxktKUY1dQeR0bgTbcwSVTDL3QKw0 l1sORR+PvGJUqilCs2BmNmIi2yzskSsKJjY0VE4xEVQq2LvsTaqaqLPcmaSP4co45NRb Fb1He5ZL8OSdr36FOcXXMtXQS3vJPb/4KbOF7ZNH5EAsHEu94b5pDZojRM+1Q/z0HBDl RyQtXiO5S6ILpbpFdOyRToqhBZUT8f4naY6btwYxIzrhJy/W558GgR0nYtplbVbaFkqq 2ZHKJgctW6gMBal9iiQzcGClJ+8yzF/TTtuR6tkMty2eKNBPMev4DPOZQ14Qv/X+VgX/ 70VQ== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:reply-to:mime-version:feedback-id :references:in-reply-to:message-id:subject:cc:from:to:date :dkim-signature; bh=Ae2h7701zE9zPlmROD2cDHtWG+zMJwutzmws+rtEhmg=; fh=vV8Gql8Zhf+qx48TZtxAb1FZ3ftgQQ+kSTMOcQzzrOo=; b=Xc9iT8ofxgWjJdMjhuJAGbJO9Uk9iOIIO00dbXpczA6BX4e3xjcn1NO/FKxSxdcmVO 3+5DAKenTi2/tI2qA2sI3CBwXhiDmU7BCSKf/U9ZAD00lCSNT89tZG7aiNFD69TOupFS dVI/fvfL5xJqpwunaqAn4akpWwGoAmPPSsiXlhCBlTOya7ZoVzzgVhVNl6k8p0hq5+Em qJOmyjF9goeFkToQJtjgowR1fn+vPqUa7wg3CQy/k7YltJAPLXiELgC0/ScsKzeDR+M7 T5B44oaTXyezRKzSYo8NxTiD438zFFmlTSHKb9dN6tXhs1ZhtuN1DtGW7gd6pzJro1V9 yy2w==; darn=gnusha.org ARC-Authentication-Results: i=2; gmr-mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=K3nxNkrP; spf=pass (google.com: domain of conduition@proton.me designates 185.70.43.16 as permitted sender) smtp.mailfrom=conduition@proton.me; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20230601; t=1734818657; x=1735423457; darn=gnusha.org; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:reply-to :x-original-authentication-results:x-original-sender:mime-version :feedback-id:references:in-reply-to:message-id:subject:cc:from:to :date:from:to:cc:subject:date:message-id:reply-to; bh=Ae2h7701zE9zPlmROD2cDHtWG+zMJwutzmws+rtEhmg=; b=C3OkrkmNiyMZX7PpKq9A7diu0uZUuI36WvBXyn6ENKNe+Ozgv3Jtf7SQ2OqvDte5Z6 LmLx+qhx1byKV5RZ4gScXoeazvimG3x0Up/OVcakeL3fRIC9WijvlhDO3mVOIp4/0U8H 1mEVOX8Ez9OF7bFzUVX8DRULUaW1qE/Gh8SvSjIyQxyqcpgRWvVEleJLcbCNE01ujVB+ SFv77q1QHBufG6D8LGaJvkSUTiZqEU9MMmekQoljhAGF3LdS2xc1eNtUNzA2xiBS6kvB oOfGDsidaGgD0lgS0eMS93VOmKq0KUURO7QRX3jmgzJZrVVsPCJxLqdZDaXn6opHyzF+ +M5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1734818657; x=1735423457; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:reply-to :x-original-authentication-results:x-original-sender:mime-version :feedback-id:references:in-reply-to:message-id:subject:cc:from:to :date:x-beenthere:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=Ae2h7701zE9zPlmROD2cDHtWG+zMJwutzmws+rtEhmg=; b=kJSYvwcEoSueI5ZTbSzyGOncU4ohdHNVrTh2K/cbO5jHhIg135Ehuksld1L53tp43b SOmhh3emJzzIgP5xmPCJ5K/6hm4UOvNCPT41RFFndXWKuLkkUcM2oyx2QAE7AlS8tN/G 7CDBChi2XpA6D9r8RudbWCDfC6iCh+pnMl2Mu1qH5XmTAak7i3QLvusSAOzgJ6BNuVNR 7xl1F0Y0sufHe+u+7PMQ6yLJYF6OHgNJN5wOYaBp4cH1qsh9tro53p75G2+hsZ40Xgj6 wu8+0Msdb173W9VEXE0vP2UR1wRjTw8BDxQAAVVihLSzO7xeBy2N/GA5RtoADZi9j/au c2ZQ== X-Forwarded-Encrypted: i=2; AJvYcCXtKiIG1laUGob65P4/gQKd3KgFZtAG/ES9zoT7rnePxPuaHPIo4fJF08C4p5qD6aHSyjxpnvjsZo5U@gnusha.org X-Gm-Message-State: AOJu0YyU5zvJoyWtxftNhJzmNP02Ry6oUDSYUmmbuWa6Rn8t3n77mUfN X+NZjIofzRy7SyVHC9AKblTr3JlUjw5748XDU+RAbkgq2nVubbwG X-Google-Smtp-Source: AGHT+IFUNVB0fIvy9lRjkFBDVGg3GA1CWp5jOhno5YUCRvKldcSKmt64w3wih/DvlzZX8vBXw3ys5A== X-Received: by 2002:a05:6214:124c:b0:6d8:b2f2:bcb8 with SMTP id 6a1803df08f44-6dd233207b0mr115416836d6.8.1734818657164; Sat, 21 Dec 2024 14:04:17 -0800 (PST) X-BeenThere: bitcoindev@googlegroups.com Received: by 2002:ad4:480c:0:b0:6d9:832:c74c with SMTP id 6a1803df08f44-6dd15481938ls43416936d6.1.-pod-prod-03-us; Sat, 21 Dec 2024 14:04:14 -0800 (PST) X-Received: by 2002:a05:620a:2948:b0:7b6:cf48:ed5 with SMTP id af79cd13be357-7b9ba7168edmr1243277185a.7.1734818654506; Sat, 21 Dec 2024 14:04:14 -0800 (PST) Received: by 2002:a05:620a:1258:b0:7b6:d72a:7c26 with SMTP id af79cd13be357-7b9ab36d14ems85a; Sat, 21 Dec 2024 13:52:40 -0800 (PST) X-Received: by 2002:a05:6512:3ba4:b0:53e:3a43:9245 with SMTP id 2adb3069b0e04-542295404f9mr2504047e87.28.1734817958516; Sat, 21 Dec 2024 13:52:38 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1734817958; cv=none; d=google.com; s=arc-20240605; b=FHKB2+msgpLG3YLh4RvJc0od+bG9PCK/ci1xo32rnOP40LAZXHEmfRwObq52QTd/mb GhcPFt4BppGt0pyXVBuhph9mYzHLzaonMBx8mFBF+8us7SK79AEed5YeY979jIayupYm ETWgUDXQEg4JDSHNo9UMkqmUHHoba55EcTJFSH/T2CYgli8ErYmBXQe/kHwz14eEcYf8 yoAmSO5P5TL8W2rv3WQld9MAjt0KMmIu2K/DPzxMMdKIpHD/nB+On1Qvg4dr96ILveSh yHCZXog1F2tH1UuXiz97U5PW4ewtTX9Jk/votGaDX47HIy6GGpnfrkaLpf74u2+ecwRe XbUw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=mime-version:feedback-id:references:in-reply-to:message-id:subject :cc:from:to:date:dkim-signature; bh=/QiPUfMjOGBBtMETb9i6D8kz9c5nYtIL7jh7qJL9ihg=; fh=+67N2uHR2MfeB757DuDnNuhtYMQ1l3OX1mrsWyqvKgo=; b=ImS+4+VgJyB+V3hIWHcwLrI5nCieSlymUyktVhTWCXtwbyCKA/5lMlP+4aWVD4KVsA fAqlQ2o39emM0spi5vZDfuTzvaYdDRgC+NERSwV4QKxTKgMG17EtJPePgihMzEG18Xm9 XEZBFA3ZARB+wXZbHyNIjMcnygsgMr7rgb6gNPbSHuZwfu2lJI1LbgPUG4UUqrvVkT6W v/ljszzMyZUmuPxhcfgdR+JUD72y0qozWqKgPKuTFly7isr54Eo1S8rgkguKqTvCuy+X vXcoBl9cnsfds00MELZzxUlzhtY0plEWO1gpORDgGbPmRZkBx5pZronQjfjv8nmfvfUr XNGA==; dara=google.com ARC-Authentication-Results: i=1; gmr-mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=K3nxNkrP; spf=pass (google.com: domain of conduition@proton.me designates 185.70.43.16 as permitted sender) smtp.mailfrom=conduition@proton.me; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me Received: from mail-4316.protonmail.ch (mail-4316.protonmail.ch. [185.70.43.16]) by gmr-mx.google.com with ESMTPS id 2adb3069b0e04-542235fdc30si162367e87.5.2024.12.21.13.52.38 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 21 Dec 2024 13:52:38 -0800 (PST) Received-SPF: pass (google.com: domain of conduition@proton.me designates 185.70.43.16 as permitted sender) client-ip=185.70.43.16; Date: Sat, 21 Dec 2024 21:52:32 +0000 To: /dev /fd0 From: "'conduition' via Bitcoin Development Mailing List" Cc: Bitcoin Development Mailing List Subject: Re: [bitcoindev] Censorship and Privacy in Chaumian ecash implementations Message-ID: <-LMvaPkFoIOkgwJOch3qo7y_ueGgiOSJWqdu0gpv3wSHTunca6AB14V-ZiR4IoDcvIkPTdoQeiy_JigGwl0ei2VpBj2tFyK-GFeE2gXZzXE=@proton.me> In-Reply-To: <27b19012-20da-46a7-8a84-f90e0070aa77n@googlegroups.com> References: <27b19012-20da-46a7-8a84-f90e0070aa77n@googlegroups.com> Feedback-ID: 72003692:user:proton X-Pm-Message-ID: 2a8365c532abe7b76d69b0f057c7f6043ab27a22 MIME-Version: 1.0 Content-Type: multipart/signed; protocol="application/pgp-signature"; micalg=pgp-sha512; boundary="------66d57d1edc68865a41ef4023a808c81655de91a76bd4f380e8fdb7188fd3cb87"; charset=utf-8 X-Original-Sender: conduition@proton.me X-Original-Authentication-Results: gmr-mx.google.com; dkim=pass header.i=@proton.me header.s=protonmail header.b=K3nxNkrP; spf=pass (google.com: domain of conduition@proton.me designates 185.70.43.16 as permitted sender) smtp.mailfrom=conduition@proton.me; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=proton.me X-Original-From: conduition Reply-To: conduition Precedence: list Mailing-list: list bitcoindev@googlegroups.com; contact bitcoindev+owners@googlegroups.com List-ID: X-Google-Group-Id: 786775582512 List-Post: , List-Help: , List-Archive: , List-Unsubscribe: , X-Spam-Score: -1.0 (-) This is an OpenPGP/MIME signed message (RFC 4880 and 3156) --------66d57d1edc68865a41ef4023a808c81655de91a76bd4f380e8fdb7188fd3cb87 Content-Type: multipart/mixed;boundary=---------------------a155cf94aeade0a670509dcd45f46614 -----------------------a155cf94aeade0a670509dcd45f46614 Content-Type: multipart/alternative;boundary=---------------------66f9673fbf1d7758e5b1a52649812bc2 -----------------------66f9673fbf1d7758e5b1a52649812bc2 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="UTF-8" Hi fd0, For P2PK, the solution there seems deadly obvious: Just don't use raw unobf= uscated Nostr keys to receive ecash. Tweak it, or use a new random key. Thi= s is a non-issue. As for authentication systems, yes of course KYC-backed authentication woul= d allow censorship, but that is an optional spec which mints aren't compell= ed to implement or use, even once the spec is finished. AFAIK no mint imple= mentations have this system in code yet, so it's not even a feature at this= point: just a proposed NUT document, sitting in draft status. Your asserti= on that "none of the ecash implementation are censorship resistant" is a bl= atantly false statement bereft of fact or depth. Even if we fast forward several years when perhaps some ecash mint implemen= tations do=C2=A0implement the authentication spec as a fully-formed feature= ready to go...=C2=A0If a mint starts enforcing KYC, or any other badness l= ike censoring by IP address, users are free to switch to a different non-KY= C mint instance. At worst they might lose the money they already stored wit= h the compromised mint, which is always a risk to keep in mind with ecash. Your argument seems to be that introducing an opt-in feature spec like exte= rnal authentication is inherently bad because it encourages/enables=C2=A0KY= C in the first place, and that ecash devs should refuse to even standardize= any protocol which enables KYC.=C2=A0 Well I have hard news for you: Governments don't care about whether complia= nce is "easy" or "standardized". If uncle sam wants your mint to enforce KY= C, he won't balk just because the mint's code doesn't provide an easy way t= o do it. He'll give you a deadline to enforce KYC on your users, and if you= can't prove you're compliant by then, you can kiss your business goodbye. When faced with this choice, the ecash mint runner can either shut down the= ir mint, or add KYC. Without an authentication system in place already, the= mint runner would have to implement it all themselves to enforce KYC and s= tay in business. So ecash devs have proposed standardized auth systems whic= h would give mint runners the option of knuckling under to KYC. I agree it = would suck to be put to that choice, but giving them the freedom to choose = is important and valid, and in no way compromises the integrity of other no= n-KYC mints who perhaps operate freely in more enlightened jurisdictions.= =C2=A0 If it's not part of the NUT standards, and mint runners need it, then someo= ne will just fork off and add it themselves. Better to pre-empt that and ke= ep the standards from fragmenting - that's the point of standards.=C2=A0 -c On Saturday, December 21st, 2024 at 9:58 AM, /dev /fd0 wrote: > Hi Bitcoin Developers, >=20 > This post is about a myth and some misleading things shared about ecash. = It is possible to censor specific users and none of the ecash implementatio= n are censorship resistant. >=20 > # Censorship Methods in Cashu >=20 > There are 2 ways to censor individual users in cashu: >=20 > 1. P2PK > 2. Authentication >=20 > ## P2PK >=20 > Ecash tokens issued by cashu mints can be locked using public keys so tha= t it can only be redeemed by the user who owns the private key for it. This= links ecash with specific public keys and most implementation use nostr ke= ys for it. Most users are doxxed on nostr so they can be censored based on = their identity. Even if its linked to an anon they can be censored based on= their posts. >=20 > You can find relevant code snippets in [conditions.py][1] if using nutshe= ll for mint: >=20 > ```python > class LedgerSpendingConditions: > def _verify_p2pk_spending_conditions(self, proof, secret): > if SecretKind(secret.kind) !=3D SecretKind.P2PK: > return True > p2pk_secret =3D P2PKSecret.from_secret(secret) > pubkeys =3D [p2pk_secret.data] + p2pk_secret.tags.get_tag_all("pubkeys") > if p2pk_secret.locktime and p2pk_secret.locktime < time.time(): > refund_pubkeys =3D p2pk_secret.tags.get_tag_all("refund") > if not refund_pubkeys: > return True > return self._verify_secret_signatures( > proof, refund_pubkeys, proof.p2pksigs, 1 > ) > return self._verify_secret_signatures( > proof, pubkeys, proof.p2pksigs, p2pk_secret.n_sigs > ) >=20 > def _verify_htlc_spending_conditions(self, proof, secret): > if SecretKind(secret.kind) !=3D SecretKind.HTLC: > return True > htlc_secret =3D HTLCSecret.from_secret(secret) > if htlc_secret.locktime and htlc_secret.locktime < time.time(): > refund_pubkeys =3D htlc_secret.tags.get_tag_all("refund") > if refund_pubkeys: > return self._verify_secret_signatures( > proof, refund_pubkeys, proof.p2pksigs, 1 > ) > return True > assert proof.htlcpreimage, TransactionError("no HTLC preimage provided") > if not hashlib.sha256(bytes.fromhex(proof.htlcpreimage)).digest() =3D=3D = bytes.fromhex(htlc_secret.data): > raise TransactionError("HTLC preimage does not match.") > hashlock_pubkeys =3D htlc_secret.tags.get_tag_all("pubkeys") > if not hashlock_pubkeys: > return True > return self._verify_secret_signatures( > proof, hashlock_pubkeys, proof.htlcsigs or [], htlc_secret.n_sigs > ) >=20 > def _verify_secret_signatures(self, proof, pubkeys, signatures, n_sigs_re= quired=3D1): > assert len(set(pubkeys)) =3D=3D len(pubkeys), "pubkeys must be unique." > if not signatures: > raise TransactionError("no signatures in proof.") > if len(set(signatures)) !=3D len(signatures): > raise TransactionError("signatures must be unique.") > n_sigs_required =3D n_sigs_required or 1 > assert n_sigs_required > 0, "n_sigs must be positive." > assert len(signatures) >=3D n_sigs_required, f"not enough signatures prov= ided: {len(signatures)} < {n_sigs_required}." > n_valid_sigs_per_output =3D 0 > for input_sig in signatures: > for pubkey in pubkeys: > if verify_schnorr_signature( > message=3Dproof.secret.encode("utf-8"), > pubkey=3DPublicKey(bytes.fromhex(pubkey), raw=3DTrue), > signature=3Dbytes.fromhex(input_sig), > ): > n_valid_sigs_per_output +=3D 1 > assert n_valid_sigs_per_output, "no valid signature provided for input." > assert n_valid_sigs_per_output >=3D n_sigs_required, f"signature threshol= d not met. {n_valid_sigs_per_output} < {n_sigs_required}." > return True >=20 > def _verify_input_spending_conditions(self, proof): > try: > secret =3D Secret.deserialize(proof.secret) > except Exception: > return True > if SecretKind(secret.kind) =3D=3D SecretKind.P2PK: > return self._verify_p2pk_spending_conditions(proof, secret) > if SecretKind(secret.kind) =3D=3D SecretKind.HTLC: > return self._verify_htlc_spending_conditions(proof, secret) > return True >=20 > def _verify_output_p2pk_spending_conditions(self, proofs, outputs): > try: > secrets_generic =3D [Secret.deserialize(p.secret) for p in proofs] > p2pk_secrets =3D [P2PKSecret.from_secret(secret) for secret in secrets_ge= neric] > except Exception: > return True > if not all([SecretKind(secret.kind) =3D=3D SecretKind.P2PK for secret in = p2pk_secrets]): > return True > if not all([secret.sigflag =3D=3D SigFlags.SIG_ALL for secret in p2pk_sec= rets]): > return True > pubkeys_per_proof =3D [ > [p2pk_secret.data] + p2pk_secret.tags.get_tag_all("pubkeys") > for p2pk_secret in p2pk_secrets > ] > n_sigs_per_proof =3D [p2pk_secret.n_sigs for p2pk_secret in p2pk_secrets] > for p2pk_secret in p2pk_secrets: > if p2pk_secret.locktime and p2pk_secret.locktime < time.time(): > refund_pubkeys =3D p2pk_secret.tags.get_tag_all("refund") > if refund_pubkeys: > pubkeys_per_proof.append(refund_pubkeys) > n_sigs_per_proof.append(1) > if not pubkeys_per_proof: > return True > assert len({tuple(pubs_output) for pubs_output in pubkeys_per_proof}) =3D= =3D 1, "pubkeys in all proofs must match." > assert len(set(n_sigs_per_proof)) =3D=3D 1, "n_sigs in all proofs must ma= tch." > pubkeys =3D pubkeys_per_proof[0] > n_sigs =3D n_sigs_per_proof[0] or 1 > for output in outputs: > p2pksigs =3D output.p2pksigs > assert p2pksigs, "no signatures in output." > assert len(set(p2pksigs)) =3D=3D len(p2pksigs), "duplicate signatures in = output." > n_valid_sigs_per_output =3D 0 > for sig in p2pksigs: > for pubkey in pubkeys: > if verify_schnorr_signature( > message=3Dbytes.fromhex(output.B_), > pubkey=3DPublicKey(bytes.fromhex(pubkey), raw=3DTrue), > signature=3Dbytes.fromhex(sig), > ): > n_valid_sigs_per_output +=3D 1 > assert n_valid_sigs_per_output, "no valid signature provided for output." > assert n_valid_sigs_per_output >=3D n_sigs, f"signature threshold not met= . {n_valid_sigs_per_output} < {n_sigs}." > return True >=20 > def _verify_output_spending_conditions(self, proofs, outputs): > return self._verify_output_p2pk_spending_conditions(proofs, outputs) > ``` >=20 > ## Authentication >=20 > Mints can enforce authentication at some point and do KYC for mint, melt,= swap etc. Users who refuse to KYC will not be able to use or redeem their = ecash tokens. Some of the KYCed users can be censored based on their identi= ty. This would also affect privacy. >=20 > gandlaf21 agrees this is possible however it is still marketed as censors= hip resistant. >=20 > There was some discussion about it in a pull request and supertestnet als= o shared his thoughts: https://github.com/bitcoinlayers/bitcoinlayers/pull/= 164 >=20 > This whole debate started in May 2024 when cashu's twitter account [annou= nced][2] that they are considering adding an authentication in the protocol= as it is requested by regulated entities. >=20 > The authentication mechanism is shared in this [pull request][3] which li= nks each user with linkingkey and it will compromise privacy: >=20 > ``` > POST https://bob.com/v1/auth >=20 > Post Data: > { > action:"mint", > k1:"8278e1a48e61c261916791dabb6af760488e4f01932e11fe7054f59754e3de6e" > signature:c568f78e4b234a5f7d8c3b2a679e48d1234567890abcdef > linkingKey:7345786068584cd33000582ba87a9ddf77db5377c67910ab59d7e9a5f44 > } >=20 > Response: >=20 > HTTP/1.1 200 OK >=20 > { > "access_token": "9876543210fedcba", > "token_type": "Bearer", > "expires_in": 3600 > } > ``` >=20 > This pull request was closed last week and a new authentication mechanism= is proposed: https://github.com/cashubtc/nuts/pull/198 >=20 > It uses clear and blind auth but users can still be censored with KYC bas= ed on their identity. You can understand the details from this [comment][4]= . >=20 > ## Conclusion >=20 > The authentication mechanisms shared above are not the only way mints can= restrict users as there is nothing in the protocol that stops mints from u= sing a custom authentication. >=20 > Introducing KYC in protocol is against freedom and privacy. These custodi= al solutions might end up being another compliant ecash implementation like= [GNU Taler][5]. This would also make it easier for government agencies to = target other mints that do not comply. >=20 > [1]: https://github.com/cashubtc/nutshell/blob/main/cashu/mint/conditions= .py > [2]: https://x.com/CashuBTC/status/1791001643019809146 > [3]: https://github.com/cashubtc/nuts/pull/106 > [4]: https://github.com/cashubtc/nuts/pull/198#issuecomment-2508706328 > [5]: https://taler.net/en/index.html >=20 > /dev/fd0 > floppy disk guy >=20 > -- > You received this message because you are subscribed to the Google Groups= "Bitcoin Development Mailing List" group. > To unsubscribe from this group and stop receiving emails from it, send an= email to bitcoindev+unsubscribe@googlegroups.com. > To view this discussion visit https://groups.google.com/d/msgid/bitcoinde= v/27b19012-20da-46a7-8a84-f90e0070aa77n%40googlegroups.com. --=20 You received this message because you are subscribed to the Google Groups "= Bitcoin Development Mailing List" group. To unsubscribe from this group and stop receiving emails from it, send an e= mail to bitcoindev+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/bitcoindev/= -LMvaPkFoIOkgwJOch3qo7y_ueGgiOSJWqdu0gpv3wSHTunca6AB14V-ZiR4IoDcvIkPTdoQeiy= _JigGwl0ei2VpBj2tFyK-GFeE2gXZzXE%3D%40proton.me. -----------------------66f9673fbf1d7758e5b1a52649812bc2 Content-Type: multipart/related;boundary=---------------------80fdf0bb4bab8bd30c54666135c5ed69 -----------------------80fdf0bb4bab8bd30c54666135c5ed69 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi fd0,

For= P2PK, the solution there seems deadly obvious: Just don't use raw unobfusc= ated Nostr keys to receive ecash. Tweak it, or use a new random key. This i= s a non-issue.

As for authentication systems, yes of course KYC-backed = authentication would allow censorship, but that is an optional spec which m= ints aren't compelled to implement or use, even once the spec is finished. = AFAIK no mint implementations have this system in code yet, so it's not eve= n a feature at this point: just a proposed NUT document, sitting in draft s= tatus. Your assertion that "none of the ecash implementation are cens= orship resistant" is a blatantly false statement bereft of fact or depth.
=
E= ven if we fast forward several years when perhaps some ecash mint im= plementations do implement the authentication spec as a fully-formed f= eature ready to go... If a mint starts enforcing KYC, or any other bad= ness like censoring by IP address, users are free to switch to a different = non-KYC mint instance. At worst they might lose the money they already stor= ed with the compromised mint, which is always a risk to keep in mind with e= cash.
=
Your argument seems to be that introducing an opt-in fe= ature spec like external authentication is inherently bad because it encour= ages/enables KYC in the first place, and that ecash devs should refuse= to even standardize any protocol which enables KYC. 

Well I have hard news= for you: Governments don't care about whether compliance is "easy" or "sta= ndardized". If uncle sam wants your mint to enforce KYC, he won't balk just= because the mint's code doesn't provide an easy way to do it. He'll give y= ou a deadline to enforce KYC on your users, and if you can't prove you're c= ompliant by then, you can kiss your business goodbye.

When faced with this choice,= the ecash mint runner can either shut down their mint, or add KYC. Without= an authentication system in place already, the mint runner would have to i= mplement it all themselves to enforce KYC and stay in business. So ecash de= vs have proposed standardized auth systems which would give mint runners th= e option of knuckling under to KYC. I agree it would suck to be put = to that choice, but giving them the freedom to choose is important and vali= d, and in no way compromises the integrity of other non-KYC mints who perha= ps operate freely in more enlightened jurisdictions. 

If it's not part of t= he NUT standards, and mint runners need it, then someone will just fork off= and add it themselves. Better to pre-empt that and keep the standards from= fragmenting - that's the point of standards. 

-c
On Saturday, December 21st, 2024 at 9:58 AM, /dev /fd0 <alicexbt= ong@gmail.com> wrote:
Hi Bitcoin Developers,

This post is about a myth and som= e misleading things shared about ecash. It is possible to censor specific u= sers and none of the ecash implementation are censorship resistant.

# Censorship Methods in Cashu

There are 2 ways to ce= nsor individual users in cashu:

1. P2PK
2. Authentication

= ## P2PK

Ecash tokens issued by cashu mints can be locked using publi= c keys so that it can only be redeemed by the user who owns the private key= for it. This links ecash with specific public keys and most implementation= use nostr keys for it. Most users are doxxed on nostr so they can be censo= red based on their identity. Even if its linked to an anon they can be cens= ored based on their posts.

You can find relevant code snippets in [c= onditions.py][1] if using nutshell for mint:

```python
class Ledg= erSpendingConditions:
def _verify_p2pk_spending_conditions(self, pro= of, secret):
if SecretKind(secret.kind) !=3D SecretKind.P2PK: return True
p2pk_secret =3D P2PKSecret.from_secret(= secret)
pubkeys =3D [p2pk_secret.data] + p2pk_secret.tags.get_ta= g_all("pubkeys")
if p2pk_secret.locktime and p2pk_secret.locktim= e < time.time():
refund_pubkeys =3D p2pk_secret.tags.get_= tag_all("refund")
if not refund_pubkeys:
= return True
return self._verify_secret_signatures(
= proof, refund_pubkeys, proof.p2pksigs, 1
)
= return self._verify_secret_signatures(
proof, pubkeys, p= roof.p2pksigs, p2pk_secret.n_sigs
)

def _verify_htlc_= spending_conditions(self, proof, secret):
if SecretKind(secret.k= ind) !=3D SecretKind.HTLC:
return True
htlc_secre= t =3D HTLCSecret.from_secret(secret)
if htlc_secret.locktime and= htlc_secret.locktime < time.time():
refund_pubkeys =3D h= tlc_secret.tags.get_tag_all("refund")
if refund_pubkeys:
= return self._verify_secret_signatures(
= proof, refund_pubkeys, proof.p2pksigs, 1
)
= return True
assert proof.htlcpreimage, TransactionError("no= HTLC preimage provided")
if not hashlib.sha256(bytes.fromhex(pr= oof.htlcpreimage)).digest() =3D=3D bytes.fromhex(htlc_secret.data):
= raise TransactionError("HTLC preimage does not match.")
= hashlock_pubkeys =3D htlc_secret.tags.get_tag_all("pubkeys")
if = not hashlock_pubkeys:
return True
return self._ve= rify_secret_signatures(
proof, hashlock_pubkeys, proof.htlcs= igs or [], htlc_secret.n_sigs
)

def _verify_secret_si= gnatures(self, proof, pubkeys, signatures, n_sigs_required=3D1):
= assert len(set(pubkeys)) =3D=3D len(pubkeys), "pubkeys must be unique." if not signatures:
raise TransactionError("no signa= tures in proof.")
if len(set(signatures)) !=3D len(signatures):<= br> raise TransactionError("signatures must be unique.")
= n_sigs_required =3D n_sigs_required or 1
assert n_sigs_requi= red > 0, "n_sigs must be positive."
assert len(signatures) &g= t;=3D n_sigs_required, f"not enough signatures provided: {len(signatures)} = < {n_sigs_required}."
n_valid_sigs_per_output =3D 0
= for input_sig in signatures:
for pubkey in pubkeys:
= if verify_schnorr_signature(
message=3D= proof.secret.encode("utf-8"),
pubkey=3DPublicKey(byt= es.fromhex(pubkey), raw=3DTrue),
signature=3Dbytes.f= romhex(input_sig),
):
n_valid_sig= s_per_output +=3D 1
assert n_valid_sigs_per_output, "no valid si= gnature provided for input."
assert n_valid_sigs_per_output >= =3D n_sigs_required, f"signature threshold not met. {n_valid_sigs_per_outpu= t} < {n_sigs_required}."
return True

def _verify_i= nput_spending_conditions(self, proof):
try:
secre= t =3D Secret.deserialize(proof.secret)
except Exception:
= return True
if SecretKind(secret.kind) =3D=3D SecretKind= .P2PK:
return self._verify_p2pk_spending_conditions(proof, s= ecret)
if SecretKind(secret.kind) =3D=3D SecretKind.HTLC:
= return self._verify_htlc_spending_conditions(proof, secret)
= return True

def _verify_output_p2pk_spending_conditions(sel= f, proofs, outputs):
try:
secrets_generic =3D [Se= cret.deserialize(p.secret) for p in proofs]
p2pk_secrets =3D= [P2PKSecret.from_secret(secret) for secret in secrets_generic]
= except Exception:
return True
if not all([SecretK= ind(secret.kind) =3D=3D SecretKind.P2PK for secret in p2pk_secrets]):
= return True
if not all([secret.sigflag =3D=3D SigFlags= .SIG_ALL for secret in p2pk_secrets]):
return True
= pubkeys_per_proof =3D [
[p2pk_secret.data] + p2pk_secret.t= ags.get_tag_all("pubkeys")
for p2pk_secret in p2pk_secrets ]
n_sigs_per_proof =3D [p2pk_secret.n_sigs for p2pk_se= cret in p2pk_secrets]
for p2pk_secret in p2pk_secrets:
= if p2pk_secret.locktime and p2pk_secret.locktime < time.time(): refund_pubkeys =3D p2pk_secret.tags.get_tag_all("refund")<= br> if refund_pubkeys:
pubkeys_per_pr= oof.append(refund_pubkeys)
n_sigs_per_proof.append(1= )
if not pubkeys_per_proof:
return True
= assert len({tuple(pubs_output) for pubs_output in pubkeys_per_proof}) =3D= =3D 1, "pubkeys in all proofs must match."
assert len(set(n_sigs= _per_proof)) =3D=3D 1, "n_sigs in all proofs must match."
pubkey= s =3D pubkeys_per_proof[0]
n_sigs =3D n_sigs_per_proof[0] or 1 for output in outputs:
p2pksigs =3D output.p2pksig= s
assert p2pksigs, "no signatures in output."
= assert len(set(p2pksigs)) =3D=3D len(p2pksigs), "duplicate signatures in o= utput."
n_valid_sigs_per_output =3D 0
for sig= in p2pksigs:
for pubkey in pubkeys:
= if verify_schnorr_signature(
message=3Dbytes= .fromhex(output.B_),
pubkey=3DPublicKey(bytes.fr= omhex(pubkey), raw=3DTrue),
signature=3Dbytes.fr= omhex(sig),
):
n_valid_si= gs_per_output +=3D 1
assert n_valid_sigs_per_output, "no val= id signature provided for output."
assert n_valid_sigs_per_o= utput >=3D n_sigs, f"signature threshold not met. {n_valid_sigs_per_outp= ut} < {n_sigs}."
return True

def _verify_output_sp= ending_conditions(self, proofs, outputs):
return self._verify_ou= tput_p2pk_spending_conditions(proofs, outputs)
```

## Authenticat= ion

Mints can enforce authentication at some point and do KYC for mi= nt, melt, swap etc. Users who refuse to KYC will not be able to use or rede= em their ecash tokens. Some of the KYCed users can be censored based on the= ir identity. This would also affect privacy.

gandlaf21 agrees this i= s possible however it is still marketed as censorship resistant.

Th= ere was some discussion about it in a pull request and supertestnet also sh= ared his thoughts: https://github.com/bitcoinlayers/bitcoinlayers/pull/164<= br>
This whole debate started in May 2024 when cashu's twitter account [= announced][2] that they are considering adding an authentication in the pro= tocol as it is requested by regulated entities.

The authentication m= echanism is shared in this [pull request][3] which links each user with lin= kingkey and it will compromise privacy:

```
POST https://bob.com/= v1/auth

Post Data:
{
action:"mint",
k1:"8278e1a48e61c= 261916791dabb6af760488e4f01932e11fe7054f59754e3de6e"
signature:c568f78= e4b234a5f7d8c3b2a679e48d1234567890abcdef
linkingKey:7345786068584cd330= 00582ba87a9ddf77db5377c67910ab59d7e9a5f44
}

Response:

HTTP= /1.1 200 OK

{
"access_token": "9876543210fedcba",
"token_t= ype": "Bearer",
"expires_in": 3600
}
```

This pull reques= t was closed last week and a new authentication mechanism is proposed: http= s://github.com/cashubtc/nuts/pull/198

It uses clear and blind auth b= ut users can still be censored with KYC based on their identity. You can un= derstand the details from this [comment][4].

## Conclusion

Th= e authentication mechanisms shared above are not the only way mints can res= trict users as there is nothing in the protocol that stops mints from using= a custom authentication.

Introducing KYC in protocol is against fre= edom and privacy. These custodial solutions might end up being another comp= liant ecash implementation like [GNU Taler][5]. This would also make it eas= ier for government agencies to target other mints that do not comply.
[1]: https://github.com/cashubtc/nutshell/blob/main/cashu/mint/conditions= .py
[2]: https://x.com/CashuBTC/status/1791001643019809146
[3]: https= ://github.com/cashubtc/nuts/pull/106
[4]: https://github.com/cashubtc/nu= ts/pull/198#issuecomment-2508706328
[5]: https://taler.net/en/index.html=

/dev/fd0
floppy disk guy

--
You received this message because you are subscribed to the Google Groups "= Bitcoin Development Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an e= mail to bitcoindev+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/= d/msgid/bitcoindev/27b19012-20da-46a7-8a84-f90e0070aa77n%40googlegroups.com= .

--
You received this message because you are subscribed to the Google Groups &= quot;Bitcoin Development Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an e= mail to bitcoind= ev+unsubscribe@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/bitcoindev/-LMvaP= kFoIOkgwJOch3qo7y_ueGgiOSJWqdu0gpv3wSHTunca6AB14V-ZiR4IoDcvIkPTdoQeiy_JigGw= l0ei2VpBj2tFyK-GFeE2gXZzXE%3D%40proton.me.
-----------------------80fdf0bb4bab8bd30c54666135c5ed69-- -----------------------66f9673fbf1d7758e5b1a52649812bc2-- -----------------------a155cf94aeade0a670509dcd45f46614 Content-Type: application/pgp-keys; filename="publickey - conduition@proton.me - 0x474891AD.asc"; name="publickey - conduition@proton.me - 0x474891AD.asc" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="publickey - conduition@proton.me - 0x474891AD.asc"; name="publickey - conduition@proton.me - 0x474891AD.asc" LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tCgp4ak1FWkRub0tSWUpLd1lCQkFI YVJ3OEJBUWRBcnBZYWFjZDgwcXdocmNaQW9VbW9NSHNWS21iZWlPZUEKcFhXbk1ybFdPZkxOSzJO dmJtUjFhWFJwYjI1QWNISnZkRzl1TG0xbElEeGpiMjVrZFdsMGFXOXVRSEJ5CmIzUnZiaTV0WlQ3 Q2pBUVFGZ29BUGdXQ1pEbm9LUVFMQ1FjSUNaQjRLV3p0aFBhenhRTVZDQW9FRmdBQwpBUUlaQVFL YkF3SWVBUlloQkVkSWthMENNdHJMZGcxM2EzZ3BiTzJFOXJQRkFBQTZhQUVBM1RmNHdqSVoKYnox K0diS0h4K09WQytNUXlVdi84RStoWUpjTE5QZnA0NEFBLzNiak5OTXN4WHdJTGZEM0xManNVVWFo CitBV2JyblVjVUFqQ2R1d3hUT01LempnRVpEbm9LUklLS3dZQkJBR1hWUUVGQVFFSFFDSXYxZW5J MU5MbAo3Zm55RzlVWk1wQ3ZsdG5vc0JrTmhQUVZxT3BXL3RKSkF3RUlCOEo0QkJnV0NBQXFCWUpr T2VncENaQjQKS1d6dGhQYXp4UUtiREJZaEJFZElrYTBDTXRyTGRnMTNhM2dwYk8yRTlyUEZBQUFR TFFEL2NCR2kwUDdwCkZTTkl2N1B6OVpkeUNVQjhzTy90dWZkV3NjQkNZK2ZMYTV3QkFNK0hTL3Jp S014RGt0TkhLakRGc2EvUgpEVDFxUGNBYXZCaXc2dDZ4Ti9jRgo9Y3d5eAotLS0tLUVORCBQR1Ag UFVCTElDIEtFWSBCTE9DSy0tLS0tCg== -----------------------a155cf94aeade0a670509dcd45f46614-- --------66d57d1edc68865a41ef4023a808c81655de91a76bd4f380e8fdb7188fd3cb87 Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="signature.asc" -----BEGIN PGP SIGNATURE----- Version: ProtonMail wnUEARYKACcFgmdnOJEJkHgpbO2E9rPFFiEER0iRrQIy2st2DXdreCls7YT2 s8UAAIqWAP9HU3pxAD0+m0fbfUcv3t9etWUV3OxSFQLYugXtz8auTgD9H0bg imMKuZJO4QQ1cfcqPV3q3viJ8Hntzr7biB8hWQQ= =cFW/ -----END PGP SIGNATURE----- --------66d57d1edc68865a41ef4023a808c81655de91a76bd4f380e8fdb7188fd3cb87--