Ensure keycard account can send transaction after upgrading from v1 to v2 #20552 (#20845)

This commit is contained in:
flexsurfer 2024-07-26 17:45:45 +02:00 committed by GitHub
parent a6d3fc3742
commit 73777e052e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 162 additions and 51 deletions

View File

@ -38,6 +38,12 @@
"wallet" "wallet"
{:fx [[:dispatch [:wallet/signal-received event-js]]]} {:fx [[:dispatch [:wallet/signal-received event-js]]]}
"wallet.sign.transactions"
{:fx [[:dispatch
[:standard-auth/authorize-with-keycard
{:on-complete #(rf/dispatch [:keycard/sign-hash %
(first (transforms/js->clj event-js))])}]]]}
"wallet.suggested.routes" "wallet.suggested.routes"
{:fx [[:dispatch [:wallet/handle-suggested-routes (transforms/js->clj event-js)]]]} {:fx [[:dispatch [:wallet/handle-suggested-routes (transforms/js->clj event-js)]]]}

View File

@ -3,21 +3,35 @@
[schema.core :as schema] [schema.core :as schema]
[status-im.common.standard-authentication.enter-password.view :as enter-password] [status-im.common.standard-authentication.enter-password.view :as enter-password]
[status-im.common.standard-authentication.events-schema :as events-schema] [status-im.common.standard-authentication.events-schema :as events-schema]
[status-im.contexts.keycard.pin.view :as keycard.pin]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[utils.address]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[utils.security.core :as security])) [utils.security.core :as security]))
(rf/reg-fx :effects.keycard/call-on-auth-success
(fn [on-auth-success]
(when on-auth-success (on-auth-success ""))))
(defn authorize (defn authorize
[{:keys [db]} [args]] [{:keys [db]} [{:keys [on-auth-success keycard-supported?] :as args}]]
(let [key-uid (get-in db [:profile/profile :key-uid]) (let [key-uid (get-in db [:profile/profile :key-uid])
keycard? (get-in db [:profile/profile :keycard-pairing])] keycard? (get-in db [:profile/profile :keycard-pairing])]
{:fx [[:effects.biometric/check-if-available {:fx
{:key-uid key-uid [(if keycard?
:on-success #(rf/dispatch [:standard-auth/authorize-with-biometric args]) (if keycard-supported?
:on-fail (if keycard? [:effects.keycard/call-on-auth-success on-auth-success]
#(rf/dispatch [:standard-auth/authorize-with-keycard args]) [:effects.utils/show-popup
#(rf/dispatch [:standard-auth/authorize-with-password args]))}]]})) {:title "This feature is not supported yet "
:content
"Keycard support is limited to logging in
and signing the sending transaction.
Use Status Desktop to access all functions."}])
[:effects.biometric/check-if-available
{:key-uid key-uid
:on-success #(rf/dispatch [:standard-auth/authorize-with-biometric args])
:on-fail #(rf/dispatch [:standard-auth/authorize-with-password args])}])]}))
(schema/=> authorize events-schema/?authorize) (schema/=> authorize events-schema/?authorize)
(rf/reg-event-fx :standard-auth/authorize authorize) (rf/reg-event-fx :standard-auth/authorize authorize)
@ -94,6 +108,16 @@
:button-icon-left auth-button-icon-left :button-icon-left auth-button-icon-left
:button-label auth-button-label}]))) :button-label auth-button-label}])))
(defn authorize-with-keycard
[_ [{:keys [on-complete]}]]
{:fx [[:dispatch
[:show-bottom-sheet
{:hide-on-background-press? false
:on-close #(rf/dispatch [:standard-auth/reset-login-password])
:content (fn []
[keycard.pin/auth {:on-complete on-complete}])}]]]})
(rf/reg-event-fx :standard-auth/authorize-with-keycard authorize-with-keycard)
(defn authorize-with-password (defn authorize-with-password
[_ [{:keys [on-close theme blur?] :as args}]] [_ [{:keys [on-close theme blur?] :as args}]]
{:fx [[:dispatch [:standard-auth/reset-login-password]] {:fx [[:dispatch [:standard-auth/reset-login-password]]
@ -110,7 +134,9 @@
(rf/reg-event-fx (rf/reg-event-fx
:standard-auth/reset-login-password :standard-auth/reset-login-password
(fn [{:keys [db]}] (fn [{:keys [db]}]
{:db (update db :profile/login dissoc :password :error)})) {:db (-> db
(update :profile/login dissoc :password :error)
(update :keycard dissoc :pin))}))
(rf/reg-fx (rf/reg-fx
:standard-auth/on-close :standard-auth/on-close

View File

@ -8,7 +8,7 @@
(defn view (defn view
[{:keys [track-text customization-color auth-button-label on-auth-success on-auth-fail [{:keys [track-text customization-color auth-button-label on-auth-success on-auth-fail
auth-button-icon-left size blur? container-style disabled? dependencies] auth-button-icon-left size blur? container-style disabled? dependencies keycard-supported?]
:or {container-style {:flex 1}}}] :or {container-style {:flex 1}}}]
(let [theme (quo.theme/use-theme) (let [theme (quo.theme/use-theme)
auth-method (rf/sub [:auth-method]) auth-method (rf/sub [:auth-method])
@ -21,6 +21,7 @@
:auth-button-icon-left auth-button-icon-left :auth-button-icon-left auth-button-icon-left
:theme theme :theme theme
:blur? blur? :blur? blur?
:keycard-supported? keycard-supported?
:biometric-auth? biometric-auth? :biometric-auth? biometric-auth?
:on-auth-success on-auth-success :on-auth-success on-auth-success
:on-auth-fail on-auth-fail :on-auth-fail on-auth-fail

View File

@ -109,3 +109,22 @@
[pairings] [pairings]
(keycard/set-pairings pairings)) (keycard/set-pairings pairings))
(rf/reg-fx :effects.keycard/set-pairing-to-keycard set-pairing-to-keycard) (rf/reg-fx :effects.keycard/set-pairing-to-keycard set-pairing-to-keycard)
(defn sign
[{:keys [on-success on-failure] :as args}]
(log/debug "[keycard] sign")
(keycard/sign
(assoc
args
:on-success
(fn [response]
(log/debug "[keycard response succ] sign")
(when on-success
(on-success (transforms/js->clj response))))
:on-failure
(fn [response]
(log/warn "[keycard response fail] sign"
(error-object->map response))
(when on-failure
(on-failure (error-object->map response)))))))
(rf/reg-fx :effects.keycard/sign sign)

View File

@ -3,6 +3,8 @@
status-im.contexts.keycard.login.events status-im.contexts.keycard.login.events
status-im.contexts.keycard.pin.events status-im.contexts.keycard.pin.events
status-im.contexts.keycard.sheet.events status-im.contexts.keycard.sheet.events
status-im.contexts.keycard.sign.events
[status-im.contexts.keycard.utils :as keycard.utils]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
(rf/reg-event-fx :keycard/on-check-nfc-enabled-success (rf/reg-event-fx :keycard/on-check-nfc-enabled-success
@ -55,3 +57,19 @@
(fn [{:keys [db]} [{:keys [on-cancel-event-vector]}]] (fn [{:keys [db]} [{:keys [on-cancel-event-vector]}]]
(log/debug "[keycard] nfc started success") (log/debug "[keycard] nfc started success")
{:db (assoc-in db [:keycard :on-nfc-cancelled-event-vector] on-cancel-event-vector)})) {:db (assoc-in db [:keycard :on-nfc-cancelled-event-vector] on-cancel-event-vector)}))
(rf/reg-event-fx :keycard/on-action-with-pin-error
(fn [{:keys [db]} [error]]
(log/debug "[keycard] get keys error: " error)
(let [tag-was-lost? (keycard.utils/tag-lost? (:error error))
pin-retries-count (keycard.utils/pin-retries (:error error))]
(if tag-was-lost?
{:db (assoc-in db [:keycard :pin :status] nil)}
(if (nil? pin-retries-count)
{:effects.utils/show-popup {:title "wrong-keycard"}}
{:db (-> db
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count)
(update-in [:keycard :pin] assoc :status :error))
:fx [[:dispatch [:keycard/hide-connection-sheet]]
(when (zero? pin-retries-count)
[:effects.utils/show-popup {:title "frozen-keycard"}])]})))))

View File

@ -3,22 +3,6 @@
[taoensso.timbre :as log] [taoensso.timbre :as log]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(rf/reg-event-fx :keycard.login/on-get-keys-error
(fn [{:keys [db]} [error]]
(log/debug "[keycard] get keys error: " error)
(let [tag-was-lost? (keycard.utils/tag-lost? (:error error))
pin-retries-count (keycard.utils/pin-retries (:error error))]
(if tag-was-lost?
{:db (assoc-in db [:keycard :pin :status] nil)}
(if (nil? pin-retries-count)
{:effects.utils/show-popup {:title "wrong-keycard"}}
{:db (-> db
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count)
(update-in [:keycard :pin] assoc :status :error))
:fx [[:dispatch [:keycard/hide-connection-sheet]]
(when (zero? pin-retries-count)
[:effects.utils/show-popup {:title "frozen-keycard"}])]})))))
(rf/reg-event-fx :keycard.login/on-get-keys-success (rf/reg-event-fx :keycard.login/on-get-keys-success
(fn [{:keys [db]} [data]] (fn [{:keys [db]} [data]]
(let [{:keys [key-uid encryption-public-key (let [{:keys [key-uid encryption-public-key
@ -56,28 +40,25 @@
:key-uid key-uid}]]})))) :key-uid key-uid}]]}))))
(rf/reg-event-fx :keycard.login/on-get-application-info-success (rf/reg-event-fx :keycard.login/on-get-application-info-success
(fn [{:keys [db]} [application-info]] (fn [{:keys [db]} [application-info {:keys [key-uid on-read-fx]}]]
(let [profile (get-in db [:profile/profiles-overview (get-in db [:profile/login :key-uid])]) (let [error (keycard.utils/validate-application-info key-uid application-info)]
pin (get-in db [:keycard :pin :text])
error (keycard.utils/validate-application-info profile application-info)]
(if error (if error
{:effects.utils/show-popup {:title (str error)}} {:effects.utils/show-popup {:title (str error)}}
{:db (-> db {:db (-> db
(assoc-in [:keycard :application-info] application-info) (assoc-in [:keycard :application-info] application-info)
(assoc-in [:keycard :pin :status] :verifying)) (assoc-in [:keycard :pin :status] :verifying))
:effects.keycard/get-keys {:pin pin :fx on-read-fx}))))
:on-success #(rf/dispatch [:keycard.login/on-get-keys-success %])
:on-failure #(rf/dispatch [:keycard.login/on-get-keys-error %])}}))))
(rf/reg-event-fx :keycard.login/cancel-reading-card (rf/reg-event-fx :keycard.login/cancel-reading-card
(fn [{:keys [db]}] (fn [{:keys [db]}]
{:db (assoc-in db [:keycard :on-card-connected-event-vector] nil)})) {:db (assoc-in db [:keycard :on-card-connected-event-vector] nil)}))
(rf/reg-event-fx :keycard/read-card-and-login (rf/reg-event-fx :keycard/read-card
(fn [{:keys [db]}] (fn [{:keys [db]} [args]]
(let [connected? (get-in db [:keycard :card-connected?]) (let [connected? (get-in db [:keycard :card-connected?])
event-vector [:keycard/get-application-info event-vector [:keycard/get-application-info
{:on-success #(rf/dispatch [:keycard.login/on-get-application-info-success %])}]] {:on-success #(rf/dispatch [:keycard.login/on-get-application-info-success %
args])}]]
(log/debug "[keycard] proceed-to-login") (log/debug "[keycard] proceed-to-login")
{:db (assoc-in db [:keycard :on-card-connected-event-vector] event-vector) {:db (assoc-in db [:keycard :on-card-connected-event-vector] event-vector)
:fx [[:dispatch :fx [[:dispatch

View File

@ -9,13 +9,17 @@
(assoc-in [:keycard :pin :text] (.slice pin 0 -1)) (assoc-in [:keycard :pin :text] (.slice pin 0 -1))
(assoc-in [:keycard :pin :status] nil))})))) (assoc-in [:keycard :pin :status] nil))}))))
(rf/reg-fx :effects.keycard.pin/dispatch-on-complete
(fn [[on-complete new-pin]]
(on-complete new-pin)))
(rf/reg-event-fx :keycard.pin/number-pressed (rf/reg-event-fx :keycard.pin/number-pressed
(fn [{:keys [db]} [number max-numbers on-complete-event]] (fn [{:keys [db]} [number max-numbers on-complete]]
(let [pin (get-in db [:keycard :pin :text]) (let [pin (get-in db [:keycard :pin :text])
new-pin (str pin number)] new-pin (str pin number)]
(when (<= (count new-pin) max-numbers) (when (<= (count new-pin) max-numbers)
{:db (-> db {:db (-> db
(assoc-in [:keycard :pin :text] new-pin) (assoc-in [:keycard :pin :text] new-pin)
(assoc-in [:keycard :pin :status] nil)) (assoc-in [:keycard :pin :status] nil))
:fx [(when (= (dec max-numbers) (count pin)) :fx [(when (and on-complete (= (dec max-numbers) (count pin)))
[:dispatch [on-complete-event]])]})))) [:effects.keycard.pin/dispatch-on-complete [on-complete new-pin]])]}))))

View File

@ -6,7 +6,7 @@
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn auth (defn auth
[callback-event-key] [{:keys [on-complete]}]
(let [{:keys [text status]} (rf/sub [:keycard/pin]) (let [{:keys [text status]} (rf/sub [:keycard/pin])
pin-retry-counter (rf/sub [:keycard/pin-retry-counter]) pin-retry-counter (rf/sub [:keycard/pin-retry-counter])
error? (= status :error)] error? (= status :error)]
@ -23,4 +23,4 @@
{:delete-key? true {:delete-key? true
:on-delete #(rf/dispatch [:keycard.pin/delete-pressed]) :on-delete #(rf/dispatch [:keycard.pin/delete-pressed])
:on-press #(rf/dispatch [:keycard.pin/number-pressed % constants/pincode-length :on-press #(rf/dispatch [:keycard.pin/number-pressed % constants/pincode-length
callback-event-key])}]])) on-complete])}]]))

View File

@ -0,0 +1,29 @@
(ns status-im.contexts.keycard.sign.events
(:require [utils.address]
[utils.re-frame :as rf]))
(defn get-signature-map
[tx-hash signature]
{tx-hash {:r (subs signature 0 64)
:s (subs signature 64 128)
:v (str (js/parseInt (subs signature 128 130) 16))}})
(rf/reg-event-fx :keycard/sign-hash
(fn [{:keys [db]} [pin-text tx-hash]]
(let [current-address (get-in db [:wallet :current-viewing-account-address])
path (get-in db [:wallet :accounts current-address :path])
key-uid (get-in db [:profile/profile :key-uid])]
{:fx [[:dispatch
[:keycard/read-card
{:key-uid key-uid
:on-read-fx [[:effects.keycard/sign
{:pin pin-text
:path path
:hash (utils.address/naked-address tx-hash)
:on-success
#(do
(rf/dispatch [:keycard/hide-connection-sheet])
(rf/dispatch
[:wallet/proceed-with-transactions-signatures
(get-signature-map tx-hash %)]))
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error %])}]]}]]]})))

View File

@ -16,8 +16,8 @@
(re-matches #".*NFCError:100.*" error))) (re-matches #".*NFCError:100.*" error)))
(defn validate-application-info (defn validate-application-info
[profile {:keys [key-uid paired? pin-retry-counter puk-retry-counter] :as application-info}] [profile-key-uid {:keys [key-uid paired? pin-retry-counter puk-retry-counter] :as application-info}]
(let [profile-mismatch? (or (nil? profile) (not= (:key-uid profile) key-uid))] (let [profile-mismatch? (or (nil? profile-key-uid) (not= profile-key-uid key-uid))]
(log/debug "[keycard] login-with-keycard" (log/debug "[keycard] login-with-keycard"
"empty application info" (empty? application-info) "empty application info" (empty? application-info)
"no key-uid" (empty? key-uid) "no key-uid" (empty? key-uid)

View File

@ -239,7 +239,16 @@
:profile-picture profile-picture :profile-picture profile-picture
:card-style style/login-profile-card}] :card-style style/login-profile-card}]
(if keycard-pairing (if keycard-pairing
[keycard.pin/auth :keycard/read-card-and-login] [keycard.pin/auth
{:on-complete
(fn [pin-text]
(rf/dispatch
[:keycard/read-card
{:key-uid key-uid
:on-read-fx [[:effects.keycard/get-keys
{:pin pin-text
:on-success #(rf/dispatch [:keycard.login/on-get-keys-success %])
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error %])}]]}]))}]
[password-input])] [password-input])]
(when-not keycard-pairing (when-not keycard-pairing
[quo/button [quo/button

View File

@ -698,8 +698,9 @@
{:json-rpc/call [{:method "wallet_createMultiTransaction" {:json-rpc/call [{:method "wallet_createMultiTransaction"
:params request-params :params request-params
:on-success (fn [result] :on-success (fn [result]
(rf/dispatch [:wallet/add-authorized-transaction result]) (when result
(rf/dispatch [:hide-bottom-sheet])) (rf/dispatch [:wallet/add-authorized-transaction result])
(rf/dispatch [:hide-bottom-sheet])))
:on-error (fn [error] :on-error (fn [error]
(log/error "failed to send transaction" (log/error "failed to send transaction"
{:event :wallet/send-transaction {:event :wallet/send-transaction
@ -710,6 +711,23 @@
:type :negative :type :negative
:text (:message error)}]))}]}))) :text (:message error)}]))}]})))
(rf/reg-event-fx :wallet/proceed-with-transactions-signatures
(fn [_ [signatures]]
{:json-rpc/call [{:method "wallet_proceedWithTransactionsSignatures"
:params [signatures]
:on-success (fn [result]
(when result
(rf/dispatch [:wallet/add-authorized-transaction result])
(rf/dispatch [:hide-bottom-sheet])))
:on-error (fn [error]
(log/error "failed to proceed-with-transactions-signatures"
{:event :wallet/proceed-with-transactions-signatures
:error error})
(rf/dispatch [:toasts/upsert
{:id :send-transaction-error
:type :negative
:text (:message error)}]))}]}))
(rf/reg-event-fx (rf/reg-event-fx
:wallet/select-from-account :wallet/select-from-account
(fn [{db :db} [{:keys [address stack-id network-details start-flow?]}]] (fn [{db :db} [{:keys [address stack-id network-details start-flow?]}]]

View File

@ -257,7 +257,8 @@
:transaction-type transaction-type}] :transaction-type transaction-type}]
(when (and (not loading-suggested-routes?) route (seq route)) (when (and (not loading-suggested-routes?) route (seq route))
[standard-auth/slide-button [standard-auth/slide-button
{:size :size-48 {:keycard-supported? true
:size :size-48
:track-text (if (= transaction-type :tx/bridge) :track-text (if (= transaction-type :tx/bridge)
(i18n/label :t/slide-to-bridge) (i18n/label :t/slide-to-bridge)
(i18n/label :t/slide-to-send)) (i18n/label :t/slide-to-send))
@ -265,8 +266,7 @@
:customization-color account-color :customization-color account-color
:on-auth-success #(rf/dispatch :on-auth-success #(rf/dispatch
[:wallet/send-transaction [:wallet/send-transaction
(security/safe-unmask-data (security/safe-unmask-data %)])
%)])
:auth-button-label (i18n/label :t/confirm)}])] :auth-button-label (i18n/label :t/confirm)}])]
:gradient-cover? true :gradient-cover? true
:customization-color (:color account)} :customization-color (:color account)}