sui_rpc_api/grpc/v2/
signature_verification_service.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use 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        //TODO add function in sui_sdk_types crate to do this
89        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 none of the possible derived addresses match we need to return that this is invalid
131        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    // If jwks from the request is empty we load the current set of active jwks that are onchain
147    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}