sui_rpc_api/grpc/v2/
signature_verification_service.rs1use std::collections::HashMap;
5use sui_crypto::Verifier;
6use sui_sdk_types::Jwk;
7use sui_sdk_types::JwkId;
8use tap::Pipe;
9
10use crate::ErrorReason;
11use crate::Result;
12use crate::RpcError;
13use crate::RpcService;
14use sui_rpc::proto::google::rpc::bad_request::FieldViolation;
15use sui_rpc::proto::sui::rpc::v2::VerifySignatureRequest;
16use sui_rpc::proto::sui::rpc::v2::VerifySignatureResponse;
17use sui_rpc::proto::sui::rpc::v2::signature_verification_service_server::SignatureVerificationService;
18
19#[tonic::async_trait]
20impl SignatureVerificationService for RpcService {
21 async fn verify_signature(
22 &self,
23 request: tonic::Request<VerifySignatureRequest>,
24 ) -> Result<tonic::Response<VerifySignatureResponse>, tonic::Status> {
25 verify_signature(self, request.into_inner())
26 .map(tonic::Response::new)
27 .map_err(Into::into)
28 }
29}
30
31#[tracing::instrument(skip(service))]
32fn verify_signature(
33 service: &RpcService,
34 request: VerifySignatureRequest,
35) -> Result<VerifySignatureResponse> {
36 let signature = request
37 .signature
38 .as_ref()
39 .ok_or_else(|| FieldViolation::new("signature").with_reason(ErrorReason::FieldMissing))?
40 .pipe(sui_sdk_types::UserSignature::try_from)
41 .map_err(|e| {
42 FieldViolation::new("signature")
43 .with_description(format!("invalid signature: {e}"))
44 .with_reason(ErrorReason::FieldInvalid)
45 })?;
46
47 let signing_digest = {
48 let bcs = request
49 .message
50 .ok_or_else(|| FieldViolation::new("message").with_reason(ErrorReason::FieldMissing))?;
51
52 match bcs.name() {
53 "TransactionData" => bcs
54 .deserialize::<sui_sdk_types::Transaction>()?
55 .signing_digest(),
56 "PersonalMessage" => bcs
57 .deserialize::<&[u8]>()
58 .map(|slice| sui_sdk_types::PersonalMessage(slice.into()))?
59 .signing_digest(),
60 _ => {
61 if let Ok(personal_message) = bcs
62 .deserialize::<&[u8]>()
63 .map(|slice| sui_sdk_types::PersonalMessage(slice.into()))
64 {
65 personal_message.signing_digest()
66 } else if let Ok(transaction) = bcs.deserialize::<sui_sdk_types::Transaction>() {
67 transaction.signing_digest()
68 } else {
69 return Err(FieldViolation::new("message")
70 .with_description("invalid message")
71 .with_reason(ErrorReason::FieldInvalid)
72 .into());
73 }
74 }
75 }
76 };
77
78 if let Some(address) = request
79 .address
80 .map(|address| address.parse::<sui_sdk_types::Address>())
81 .transpose()
82 .map_err(|e| {
83 FieldViolation::new("address")
84 .with_description(format!("invalid address: {e}"))
85 .with_reason(ErrorReason::FieldInvalid)
86 })?
87 {
88 let derived_addresses = match &signature {
90 sui_sdk_types::UserSignature::Simple(simple_signature) => match simple_signature {
91 sui_sdk_types::SimpleSignature::Ed25519 { public_key, .. } => {
92 [Some(public_key.derive_address()), None]
93 }
94 sui_sdk_types::SimpleSignature::Secp256k1 { public_key, .. } => {
95 [Some(public_key.derive_address()), None]
96 }
97 sui_sdk_types::SimpleSignature::Secp256r1 { public_key, .. } => {
98 [Some(public_key.derive_address()), None]
99 }
100 _ => {
101 return Err(RpcError::new(
102 tonic::Code::Internal,
103 "unknown signature scheme",
104 ));
105 }
106 },
107 sui_sdk_types::UserSignature::Multisig(multisig) => {
108 [Some(multisig.committee().derive_address()), None]
109 }
110 sui_sdk_types::UserSignature::ZkLogin(z) => {
111 let id = z.inputs.public_identifier();
112 [
113 Some(id.derive_address_padded()),
114 Some(id.derive_address_unpadded()),
115 ]
116 }
117 sui_sdk_types::UserSignature::Passkey(p) => {
118 [Some(p.public_key().derive_address()), None]
119 }
120 _ => {
121 return Err(RpcError::new(
122 tonic::Code::Internal,
123 "unknown signature scheme",
124 ));
125 }
126 };
127
128 let first_derived_address = derived_addresses[0].unwrap();
129
130 if !derived_addresses
132 .into_iter()
133 .flatten()
134 .any(|derived_address| derived_address == address)
135 {
136 let mut message = VerifySignatureResponse::default();
137 message.is_valid = Some(false);
138 message.reason = Some(format!(
139 "provided address `{}` does not match derived address `{}`",
140 address, first_derived_address
141 ));
142 return Ok(message);
143 }
144 }
145
146 let jwks = {
148 let mut jwks = request
149 .jwks
150 .iter()
151 .enumerate()
152 .map(|(i, jwk)| {
153 let jwk = sui_sdk_types::ActiveJwk::try_from(jwk).map_err(|e| {
154 FieldViolation::new_at("jwks", i)
155 .with_description(e.to_string())
156 .with_reason(ErrorReason::FieldInvalid)
157 })?;
158 Ok((jwk.jwk_id, jwk.jwk))
159 })
160 .collect::<Result<HashMap<JwkId, Jwk>>>()?;
161
162 if jwks.is_empty()
163 && let Some(authenticator_state) = service.reader.get_authenticator_state()?
164 {
165 jwks.extend(
166 authenticator_state
167 .active_jwks
168 .into_iter()
169 .map(sui_sdk_types::ActiveJwk::from)
170 .map(|active_jwk| (active_jwk.jwk_id, active_jwk.jwk)),
171 );
172 }
173
174 jwks
175 };
176
177 let mut zklogin_verifier = match service.chain_id().chain() {
178 sui_protocol_config::Chain::Mainnet => sui_crypto::zklogin::ZkloginVerifier::new_mainnet(),
179 sui_protocol_config::Chain::Testnet | sui_protocol_config::Chain::Unknown => {
180 sui_crypto::zklogin::ZkloginVerifier::new_dev()
181 }
182 };
183 *zklogin_verifier.jwks_mut() = jwks;
184 let mut verifier = sui_crypto::UserSignatureVerifier::new();
185 verifier.with_zklogin_verifier(zklogin_verifier);
186
187 let mut message = VerifySignatureResponse::default();
188 match verifier.verify(&signing_digest, &signature) {
189 Ok(()) => message.is_valid = Some(true),
190 Err(error) => {
191 message.is_valid = Some(false);
192 message.reason = Some(error.to_string());
193 }
194 }
195
196 Ok(message)
197}