一文读懂 zklogin

zklogin 是 sui foundation 最新力作,它能够让用户直接通过 google,facebook,apple id (支持 openid connect 即可)登录来创建并控制 sui 钱包,且隐藏了具体的登录信息(用户名,邮箱等)。登录后,可像正常区块链钱包一样发起交易。

本文力图摒弃各种专业术语和底层细节,保留关键的信息要素,缕清 zklogin 怎么实现上述功能。非水文,请坐稳小板凳。

使用场景

我们先描述一个典型的使用场景。后文会依据该场景介绍相关操作所设计的底层技术。

假设 sueet 是一款纯本地的 web 钱包应用,可以通过 http://localhost:3000 访问。

  • 登录页面是一个 Sign in with Google 的大按钮。

post image
  • 用户点击 Sign in with Google ,浏览器跳转到 google oauth 登录页面。

post image
  • 用户用 google 的账户密码登录后,页面跳转回 sueet 钱包的页面 http://localhost:3000

  • 此时,sueet 页面在 loading 10秒后,显示登录成功,展示用户的钱包地址,钱包余额等等,如同登录 metamask 后的界面。

  • 用户可以在这个界面,发起转账等各种交易。

上述使用场景是不是相当丝滑。我们来逐步分析流程中的这几个操作。

登录准备

用户进入登录页面后, sueet 调用 zklogin 生成以下信息:

  • 临时的密钥对:(eph_sk, eph_pk)。

  • 登录后的最长有效时间: max_epoch

  • 一个随机数据:jwt_randomness

然后,将 eph_pk, max_epoch, jwt_randomness 放在一起哈希,生成 nonce = H(eph_pk, max_epoch, jwt_randomness)

Sign in with Google 本质上一个调用 google oauth 接口的链接 ,sueet 将生成的 nonce 信息放进链接中,传递给 google。比如:

https://accounts.google.com/o/oauth2/v2/auth?client_id=$CLIENT_ID&response_type=id_token&redirect_uri=$REDIRECT_URL&scope=openid&nonce=$NONCE

用户点击 Sign in with Google 后, 会跳转到 Google 的登录鉴权页面。这一步用户直接和 Google 交互,与 sueet 无关。

获取用户信息

当用户成功授权后,google 会跳转到 sueet 页面 http://localhost:3000 并将用户的 jwt_token 数据放在跳转链接中,从而让 sueet 可以拿到该数据。jwt_token 包含了以下信息,payload 中包含了 iss, aud, sub,以及被原封不动返回来的 nonce

{
  header: {
    alg: "RS256",
    kid:  "c3afe7a9bda46bae6ef97e46c95cda48912e5979", // Identifies the JWK that should be used to verify the JWT.
    typ: "JWT"
  },
  payload: {
    iss: "https://accounts.google.com", // OpenID 提供商,这里是 谷歌 
    aud: "25769832374-famecqrhe2gkebt5fvqms2263046lj96.apps.googleusercontent.com", // 谷歌给 sueet 分配的应用标识
    sub: "110463452167303000000", // 用户在谷歌里的唯一标识
    nonce: "hTPpgF7XAKbW37rEUS6pEVZqmoI" // 前述构造的nonce,google 原封不动返回
  },
  signature: "3Thp81rDFrKXr3WrY1MyMnNK8kKoZBX9lg-JwFznR-M" // header+payload 的签名数据
}

生成链上地址

sueet 拿到 jwt_token 后,本地生成一个随机数 user_salt (或者由用户输入决定)。

这个时候,sueet 有了足够的信息,来为用户生成 sui 的链上地址:

address = H(iss, H(user_salt,aud, sub))

对于一个用户来说,(iss , aud, sub) 是固定的,因此,地址基本上是由 user_salt 决定。user_salt 不同,生成的地址也不一样。

生成地址证明

接着,sueet 将数据 user_salt, jwt_token, eph_pk, max_epoch, jwt_randomness , sub 作为隐私参数传递给外部服务 ZK proving service,让其生成 address proof。(如果不信任外部服务,也可以本地生成 proof,可能耗时较长)。证明系统会证明:

  1. jwt_token 的合法性,证明它确实是由 google 签署的。

  2. nonce 的生成符合预期的规则。即: jwt_token.payload.nonce = H(eph_pk, max_epoch, jwt_randomness)

  3. 传入 的 sub 确实等于 jwt_token 中 的sub。即: jwt_token.payload.sub = sub

  4. 地址的生成符合预期的规则。即:address = H(jwt_token.payload.iss, H(user_salt, jwt_token.payload.aud, jwt_token.payload.sub))

说人话就是:proof 可以证明,生成的 address 确实关联一个 google 用户,而且这个 google 账户关联了一个有效期是 max_epoch 的密钥对,这个密钥对的公钥是 eph_pk

从而,如果一个从 address 发起的交易是用 eph_pk 对应的私钥签署的, 就可以证明这个交易确实是这个 address 对应的 google 用户发起的。因为只有这个 google 用户掌握了 eph_pk 对应的私钥。

除了生成 proof 之外,prover 还会生成公开信息,以便验证 prove 时使用,比如eph_pk, iss, address_seed = H(user_salt, aud, sub)。另外比较重要的一点是,proof 生成后,sueet 可以将其缓存在本地,有效期内不用重复生成。

用户交易

拥有了地址证明,用户可以用 (eph_sk, eph_pk) 签署交易,并附带上缓存的地址证明,提交给 sui 区块链节点。

节点校验

节点收到此类交易,会校验:

  • 交易发起地址的生成规则,符合 :sender_address ==H(iss, address_seed)

  • 交易签名的有效性。 eph_pk.verify(tx_signature, tx_data)

  • 交易签名用的公钥确实和 zkproof 里使用的 eph_pk 一致。

  • 最后就是,地址证明的验证。这一步需要用到 OpenID 提供商公开的 JWK,比如 google 的是 https://www.googleapis.com/oauth2/v3/certs 。侧面来讲,sui 的节点需要对支持的 openid 服务商的 JWK 达成共识。(比如说,将这些 JWKs 当作一种 resource,发起一个写入交易,走一遍全局共识即可)

这样就校验了:交易是由这个 address 关联的 google 账户关联的 (eph_sk, eph_pk) 签署发布的!

安全性

理论情况下,整套流程是非常安全的。应用是本地应用,user_saltproof 生成都可以在本地实现。用户要么和应用打交道,要么和 google 服务打交道。

目前尚不清楚地址证明的生成效率如何。如果电脑端或手机端,能够在30秒或者更短的时间内生成证明,可能大多数用户会选择本地生成(等待时间不长),安全系数比较高。否则需要依赖三方的证明服务,将敏感信息传递出去,安全性降低。

实际应用场景下,还要解决 user_salt 如何保存 的问题。

  • 要么 由用户自行承担,登陆时手动输入,缺点是,用户还是需要记一个密码(虽然可能比较简单),而且用户输入的 salt 可能比较单一,反而降低了安全性。

  • 要么 应用本地随机生成并缓存。缺点是,不能多端登录,salt 不一样会导致地址也不一样。

  • 要么 交由三方服务。缺点是,需要将部分信息传递给三方服务,且三方服务知晓了 salt 信息,有泄漏风险。

使用 salt 也有不少优点。比如,用户在换设备后,依旧可以用同一个账户登录,输入相同的 salt(相当于密码),即可恢复账户地址,无需私钥。同时,只要输入不同 salt,就可以生成不同的地址,达到切换账户的效果,从而实现多账户功能。

结语

zklogin 实现优雅,在保证隐私的情况下,实现免密钥登录,确实拥有 onboarding billions of users 的实力。

短期内,zklogin 无法被其他区块链复制。因为这套方案依赖区块链支持这种独特的交易,并且能够验证地址证明,目前只有 sui 的区块链节点实现了,这是它的独特优势。

但目前 AA 钱包也做的如火如荼,长期来看,类似 zklogin 的方案一定会出现在不同的区块链生态里。拭目以待吧!

如果这篇文章对你有帮助,欢迎在推特转发评论加关注!

https://twitter.com/nanne007/status/1705867888375263631

参考链接

以及官方出品的流程图