Verify.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. // Copyright 2017-2020 @polkadot/app-toolbox authors & contributors
  2. // This software may be modified and distributed under the terms
  3. // of the Apache-2.0 license. See the LICENSE file for details.
  4. import { KeypairType } from '@polkadot/util-crypto/types';
  5. import React, { useCallback, useEffect, useState } from 'react';
  6. import styled from 'styled-components';
  7. import { Badge, Dropdown, Input, InputAddress, Static } from '@polkadot/react-components';
  8. import keyring from '@polkadot/ui-keyring';
  9. import uiSettings from '@polkadot/ui-settings';
  10. import { isHex } from '@polkadot/util';
  11. import { naclVerify, schnorrkelVerify } from '@polkadot/util-crypto';
  12. import { useTranslation } from './translate';
  13. type CryptoTypes = KeypairType | 'unknown';
  14. interface Props {
  15. className?: string;
  16. }
  17. function Verify ({ className = '' }: Props): React.ReactElement {
  18. const { t } = useTranslation();
  19. const [{ cryptoType, isValid }, setValidity] = useState<{ cryptoType: CryptoTypes; isValid: boolean }>({ cryptoType: 'unknown', isValid: false });
  20. const [{ data, isHexData }, setData] = useState<{ data: string; isHexData: boolean }>({ data: '', isHexData: false });
  21. const [{ isValidPk, publicKey }, setPublicKey] = useState<{ isValidPk: boolean; publicKey: Uint8Array | null }>({ isValidPk: false, publicKey: null });
  22. const [{ isValidSignature, signature }, setSignature] = useState<{ isValidSignature: boolean; signature: string }>({ isValidSignature: false, signature: '' });
  23. const [cryptoOptions] = useState([{ text: t<string>('Crypto not detected'), value: 'unknown' }].concat(uiSettings.availableCryptos as any[]));
  24. useEffect((): void => {
  25. let cryptoType: CryptoTypes = 'unknown';
  26. let isValid = isValidPk && isValidSignature;
  27. // We cannot just use the keyring verify since it may be an address. So here we first check
  28. // for ed25519, if not valid, we try against sr25519 - if neither are valid, well, we have
  29. // not been able to validate the signature
  30. if (isValid && publicKey) {
  31. let isValidSr = false;
  32. let isValidEd = false;
  33. try {
  34. isValidEd = naclVerify(data, signature, publicKey);
  35. } catch (error) {
  36. // do nothing, already set to false
  37. }
  38. if (isValidEd) {
  39. cryptoType = 'ed25519';
  40. } else {
  41. try {
  42. isValidSr = schnorrkelVerify(data, signature, publicKey);
  43. } catch (error) {
  44. // do nothing, already set to false
  45. }
  46. if (isValidSr) {
  47. cryptoType = 'sr25519';
  48. } else {
  49. isValid = false;
  50. }
  51. }
  52. }
  53. setValidity({ cryptoType, isValid });
  54. }, [data, isValidPk, isValidSignature, publicKey, signature]);
  55. const _onChangeAddress = useCallback(
  56. (accountId: string | null): void => {
  57. let publicKey: Uint8Array | null = null;
  58. try {
  59. publicKey = keyring.decodeAddress(accountId || '');
  60. } catch (err) {
  61. console.error(err);
  62. }
  63. setPublicKey({ isValidPk: !!publicKey && publicKey.length === 32, publicKey });
  64. },
  65. []
  66. );
  67. const _onChangeData = useCallback(
  68. (data: string) => setData({ data, isHexData: isHex(data) }),
  69. []
  70. );
  71. const _onChangeSignature = useCallback(
  72. (signature: string) => setSignature({ isValidSignature: isHex(signature) && signature.length === 130, signature }),
  73. []
  74. );
  75. return (
  76. <div className={`toolbox--Verify ${className}`}>
  77. <div className='ui--row'>
  78. <InputAddress
  79. className='full'
  80. help={t<string>('The account that signed the input')}
  81. isError={!isValidPk}
  82. isInput
  83. label={t<string>('verify using address')}
  84. onChange={_onChangeAddress}
  85. />
  86. </div>
  87. <div className='ui--row'>
  88. <Input
  89. autoFocus
  90. className='full'
  91. help={t<string>('The data that was signed. This is used in combination with the signature for the verification. It can either be hex or a string.')}
  92. label={t<string>('using the following data')}
  93. onChange={_onChangeData}
  94. value={data}
  95. />
  96. </div>
  97. <div className='ui--row'>
  98. <div className='ui--AlignedIconContainer'>
  99. <Badge
  100. className='alignedBadge'
  101. color={isValid ? 'green' : (isValidSignature ? 'red' : 'gray')}
  102. icon={isValid ? 'check' : (isValidSignature ? 'exclamation' : 'question')}
  103. />
  104. </div>
  105. <Input
  106. className='full'
  107. help={t<string>('The signature as by the account being checked, supplied as a hex-formatted string.')}
  108. isError={!isValidSignature}
  109. label={t<string>('the supplied signature')}
  110. onChange={_onChangeSignature}
  111. value={signature}
  112. />
  113. </div>
  114. <div className='ui--row'>
  115. <Dropdown
  116. defaultValue={cryptoType}
  117. help={t<string>('Cryptography used to create this signature. It is auto-detected on valid signatures.')}
  118. isDisabled
  119. label={t<string>('signature crypto type')}
  120. options={cryptoOptions}
  121. />
  122. <Static
  123. className='medium'
  124. help={t<string>('Detection on the input string to determine if it is hex or non-hex.')}
  125. label={t<string>('hex input data')}
  126. value={
  127. isHexData
  128. ? t<string>('Yes')
  129. : t<string>('No')
  130. }
  131. />
  132. </div>
  133. </div>
  134. );
  135. }
  136. export default React.memo(styled(Verify)`
  137. .ui--AlignedIconContainer {
  138. position: absolute;
  139. z-index: 1;
  140. }
  141. .alignedBadge {
  142. left: 1.25rem;
  143. position: relative;
  144. top: 1.25rem;
  145. }
  146. `);