mirror of
https://github.com/kccleoc/seedpgp-web.git
synced 2026-03-06 17:37:51 +08:00
- Replace BarcodeDetector with jsQR for raw binary byte access - BarcodeDetector forced UTF-8 decoding which corrupted binary data - jsQR's binaryData property preserves raw bytes without text conversion - Fix regex bug: use single backslash \x00 instead of \x00 for binary detection - Add debug logging for scan data inspection - QR generation already worked (Krux-compatible), only scanning was broken Resolves binary QR code scanning for 12/24-word CompactSeedQR format. Tested with Krux device - full bidirectional compatibility confirmed.
470 lines
23 KiB
Plaintext
470 lines
23 KiB
Plaintext
{\rtf1\ansi\ansicpg1252\cocoartf2867
|
|
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\froman\fcharset0 Times-Roman;\f1\froman\fcharset0 Times-Bold;\f2\fnil\fcharset0 HelveticaNeue;
|
|
\f3\fmodern\fcharset0 Courier;\f4\fmodern\fcharset0 Courier-Bold;\f5\fmodern\fcharset0 Courier-Oblique;
|
|
\f6\fnil\fcharset0 AppleColorEmoji;}
|
|
{\colortbl;\red255\green255\blue255;\red255\green255\blue255;\red0\green0\blue0;\red185\green188\blue186;
|
|
\red111\green144\blue176;\red162\green127\blue173;\red166\green178\blue85;\red132\green134\blue132;\red212\green128\blue77;
|
|
\red0\green0\blue0;\red191\green80\blue83;}
|
|
{\*\expandedcolortbl;;\cssrgb\c100000\c100000\c100000;\cssrgb\c0\c0\c0\c84706;\cssrgb\c77255\c78431\c77647;
|
|
\cssrgb\c50588\c63529\c74510;\cssrgb\c69804\c58039\c73333;\cssrgb\c70980\c74118\c40784;\cssrgb\c58824\c59608\c58824;\cssrgb\c87059\c57647\c37255;
|
|
\cssrgb\c0\c0\c0;\cssrgb\c80000\c40000\c40000;}
|
|
\paperw11900\paperh16840\margl1440\margr1440\vieww11520\viewh8400\viewkind0
|
|
\deftab720
|
|
\pard\pardeftab720\sa240\partightenfactor0
|
|
|
|
\f0\fs24 \cf2 \expnd0\expndtw0\kerning0
|
|
\outl0\strokewidth0 \strokec2 Here's a
|
|
\f1\b Bun TypeScript test
|
|
\f0\b0 for
|
|
\f1\b SeedSigner SeedQR
|
|
\f0\b0 (both Standard and Compact formats) using
|
|
\f1\b Test Vector 1
|
|
\f0\b0 from the official specification:\
|
|
\pard\pardeftab720\sa298\partightenfactor0
|
|
|
|
\f1\b\fs36 \cf2 Setup\
|
|
\pard\pardeftab720\qc\partightenfactor0
|
|
|
|
\f2\b0\fs22 \cf3 \strokec3 \
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f3\fs26 \cf2 \strokec2 bash\
|
|
\pard\pardeftab720\partightenfactor0
|
|
\cf4 \strokec4 mkdir seedsigner-test\
|
|
\pard\pardeftab720\partightenfactor0
|
|
\cf5 \strokec5 cd\cf4 \strokec4 seedsigner-test\
|
|
bun init -y\
|
|
bun add bip39\
|
|
\pard\pardeftab720\sa298\partightenfactor0
|
|
|
|
\f4\b\fs39 \cf2 \strokec2 seedsigner-test.ts
|
|
\f1\fs36 \
|
|
\pard\pardeftab720\qc\partightenfactor0
|
|
|
|
\f2\b0\fs22 \cf3 \strokec3 \
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f3\fs26 \cf2 \strokec2 typescript\
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f4\b \cf6 \strokec6 import
|
|
\f3\b0 \cf4 \strokec4 *
|
|
\f4\b \cf6 \strokec6 as
|
|
\f3\b0 \cf4 \strokec4 bip39
|
|
\f4\b \cf6 \strokec6 from
|
|
\f3\b0 \cf4 \strokec4 \cf7 \strokec7 "bip39"\cf4 \strokec4 ;\
|
|
\
|
|
|
|
\f4\b \cf6 \strokec6 const
|
|
\f3\b0 \cf4 \strokec4 BIP39_ENGLISH_WORDLIST = [\
|
|
\cf7 \strokec7 "abandon"\cf4 \strokec4 , \cf7 \strokec7 "ability"\cf4 \strokec4 , \cf7 \strokec7 "able"\cf4 \strokec4 , \cf7 \strokec7 "about"\cf4 \strokec4 , \cf7 \strokec7 "above"\cf4 \strokec4 , \cf7 \strokec7 "absent"\cf4 \strokec4 , \cf7 \strokec7 "absorb"\cf4 \strokec4 , \cf7 \strokec7 "abstract"\cf4 \strokec4 , \cf7 \strokec7 "absurd"\cf4 \strokec4 , \cf7 \strokec7 "abuse"\cf4 \strokec4 ,\
|
|
\cf7 \strokec7 "access"\cf4 \strokec4 , \cf7 \strokec7 "accident"\cf4 \strokec4 , \cf7 \strokec7 "account"\cf4 \strokec4 , \cf7 \strokec7 "accuse"\cf4 \strokec4 , \cf7 \strokec7 "achieve"\cf4 \strokec4 , \cf7 \strokec7 "acid"\cf4 \strokec4 , \cf7 \strokec7 "acoustic"\cf4 \strokec4 , \cf7 \strokec7 "acquire"\cf4 \strokec4 , \cf7 \strokec7 "across"\cf4 \strokec4 , \cf7 \strokec7 "act"\cf4 \strokec4 ,\
|
|
\cf7 \strokec7 "actor"\cf4 \strokec4 , \cf7 \strokec7 "actress"\cf4 \strokec4 , \cf7 \strokec7 "actual"\cf4 \strokec4 , \cf7 \strokec7 "adapt"\cf4 \strokec4 , \cf7 \strokec7 "add"\cf4 \strokec4 , \cf7 \strokec7 "addict"\cf4 \strokec4 , \cf7 \strokec7 "address"\cf4 \strokec4 , \cf7 \strokec7 "adjust"\cf4 \strokec4 , \cf7 \strokec7 "admit"\cf4 \strokec4 , \cf7 \strokec7 "adult"\cf4 \strokec4 ,\
|
|
\cf7 \strokec7 "advance"\cf4 \strokec4 , \cf7 \strokec7 "advice"\cf4 \strokec4 , \cf7 \strokec7 "aerobic"\cf4 \strokec4 , \cf7 \strokec7 "affair"\cf4 \strokec4 , \cf7 \strokec7 "afford"\cf4 \strokec4 , \cf7 \strokec7 "afraid"\cf4 \strokec4 , \cf7 \strokec7 "again"\cf4 \strokec4 , \cf7 \strokec7 "age"\cf4 \strokec4 , \cf7 \strokec7 "agent"\cf4 \strokec4 , \cf7 \strokec7 "agree"\cf4 \strokec4 ,\
|
|
\cf7 \strokec7 "ahead"\cf4 \strokec4 , \cf7 \strokec7 "aim"\cf4 \strokec4 , \cf7 \strokec7 "air"\cf4 \strokec4 , \cf7 \strokec7 "airport"\cf4 \strokec4 , \cf7 \strokec7 "aisle"\cf4 \strokec4 , \cf7 \strokec7 "alarm"\cf4 \strokec4 , \cf7 \strokec7 "album"\cf4 \strokec4 , \cf7 \strokec7 "alcohol"\cf4 \strokec4 , \cf7 \strokec7 "alert"\cf4 \strokec4 , \cf7 \strokec7 "alien"\cf4 \strokec4 ,\
|
|
\cf7 \strokec7 "all"\cf4 \strokec4 , \cf7 \strokec7 "alley"\cf4 \strokec4 , \cf7 \strokec7 "allow"\cf4 \strokec4 , \cf7 \strokec7 "almost"\cf4 \strokec4 , \cf7 \strokec7 "alone"\cf4 \strokec4 , \cf7 \strokec7 "alpha"\cf4 \strokec4 , \cf7 \strokec7 "already"\cf4 \strokec4 , \cf7 \strokec7 "also"\cf4 \strokec4 , \cf7 \strokec7 "alter"\cf4 \strokec4 , \cf7 \strokec7 "always"\cf4 \strokec4 ,\
|
|
\cf7 \strokec7 "amateur"\cf4 \strokec4 , \cf7 \strokec7 "amazing"\cf4 \strokec4 , \cf7 \strokec7 "among"\cf4 \strokec4 , \cf7 \strokec7 "amount"\cf4 \strokec4 , \cf7 \strokec7 "amused"\cf4 \strokec4 , \cf7 \strokec7 "analyst"\cf4 \strokec4 , \cf7 \strokec7 "anchor"\cf4 \strokec4 , \cf7 \strokec7 "ancient"\cf4 \strokec4 , \cf7 \strokec7 "anger"\cf4 \strokec4 , \cf7 \strokec7 "angle"\cf4 \strokec4 ,\
|
|
\cf7 \strokec7 "angry"\cf4 \strokec4 , \cf7 \strokec7 "animal"\cf4 \strokec4 , \cf7 \strokec7 "ankle"\cf4 \strokec4 , \cf7 \strokec7 "announce"\cf4 \strokec4 , \cf7 \strokec7 "annual"\cf4 \strokec4 , \cf7 \strokec7 "another"\cf4 \strokec4 , \cf7 \strokec7 "answer"\cf4 \strokec4 , \cf7 \strokec7 "antenna"\cf4 \strokec4 , \cf7 \strokec7 "antique"\cf4 \strokec4 , \cf7 \strokec7 "anxiety"\cf4 \strokec4 ,\
|
|
\cf7 \strokec7 "any"\cf4 \strokec4 , \cf7 \strokec7 "apart"\cf4 \strokec4 , \cf7 \strokec7 "apology"\cf4 \strokec4 , \cf7 \strokec7 "appear"\cf4 \strokec4 , \cf7 \strokec7 "apple"\cf4 \strokec4 , \cf7 \strokec7 "approve"\cf4 \strokec4 , \cf7 \strokec7 "april"\cf4 \strokec4 , \cf7 \strokec7 "arch"\cf4 \strokec4 , \cf7 \strokec7 "arctic"\cf4 \strokec4 , \cf7 \strokec7 "area"\cf4 \strokec4 ,\
|
|
\cf7 \strokec7 "arena"\cf4 \strokec4 , \cf7 \strokec7 "argue"\cf4 \strokec4 , \cf7 \strokec7 "arm"\cf4 \strokec4 , \cf7 \strokec7 "armed"\cf4 \strokec4 , \cf7 \strokec7 "armor"\cf4 \strokec4 , \cf7 \strokec7 "army"\cf4 \strokec4 , \cf7 \strokec7 "around"\cf4 \strokec4 , \cf7 \strokec7 "arrange"\cf4 \strokec4 , \cf7 \strokec7 "arrest"\cf4 \strokec4 , \cf7 \strokec7 "arrive"\cf4 \strokec4 ,\
|
|
|
|
\f5\i \cf8 \strokec8 // ... (truncated for brevity - full 2048 word list needed in production)
|
|
\f3\i0 \cf4 \strokec4 \
|
|
\cf7 \strokec7 "attack"\cf4 \strokec4 , \cf7 \strokec7 "pizza"\cf4 \strokec4 , \cf7 \strokec7 "motion"\cf4 \strokec4 , \cf7 \strokec7 "avocado"\cf4 \strokec4 , \cf7 \strokec7 "network"\cf4 \strokec4 , \cf7 \strokec7 "gather"\cf4 \strokec4 , \cf7 \strokec7 "crop"\cf4 \strokec4 , \cf7 \strokec7 "fresh"\cf4 \strokec4 , \cf7 \strokec7 "patrol"\cf4 \strokec4 , \cf7 \strokec7 "unusual"\cf4 \strokec4 ,\
|
|
|
|
\f5\i \cf8 \strokec8 // Full wordlist should be loaded from bip39 library
|
|
\f3\i0 \cf4 \strokec4 \
|
|
];\
|
|
\
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f5\i \cf8 \strokec8 // Full wordlist from bip39
|
|
\f3\i0 \cf4 \strokec4 \
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f4\b \cf6 \strokec6 const
|
|
\f3\b0 \cf4 \strokec4 FULL_WORDLIST = bip39.wordlists.english;\
|
|
\
|
|
|
|
\f4\b \cf6 \strokec6 interface
|
|
\f3\b0 \cf4 \strokec4 \cf5 \strokec5 SeedQRResult\cf4 \strokec4 \{\
|
|
mnemonic: \cf9 \strokec9 string\cf4 \strokec4 ;\
|
|
wordCount: \cf9 \strokec9 12\cf4 \strokec4 | \cf9 \strokec9 24\cf4 \strokec4 ;\
|
|
format: \cf7 \strokec7 "standard"\cf4 \strokec4 | \cf7 \strokec7 "compact"\cf4 \strokec4 ;\
|
|
\}\
|
|
\
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f5\i \cf8 \strokec8 // --- Standard SeedQR: Parse numeric digit stream ---
|
|
\f3\i0 \cf4 \strokec4 \
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f4\b \cf6 \strokec6 function
|
|
\f3\b0 \cf4 \strokec4 parseStandardSeedQR(digitStream: \cf9 \strokec9 string\cf4 \strokec4 ): SeedQRResult \{\
|
|
|
|
\f4\b \cf6 \strokec6 if
|
|
\f3\b0 \cf4 \strokec4 (digitStream.length % \cf9 \strokec9 4\cf4 \strokec4 !== \cf9 \strokec9 0\cf4 \strokec4 ) \{\
|
|
|
|
\f4\b \cf6 \strokec6 throw
|
|
\f3\b0 \cf4 \strokec4
|
|
\f4\b \cf6 \strokec6 new
|
|
\f3\b0 \cf4 \strokec4 \cf5 \strokec5 Error\cf4 \strokec4 (\cf7 \strokec7 "Invalid digit stream length. Must be multiple of 4."\cf4 \strokec4 );\
|
|
\}\
|
|
\
|
|
|
|
\f4\b \cf6 \strokec6 const
|
|
\f3\b0 \cf4 \strokec4 wordIndices: \cf9 \strokec9 number\cf4 \strokec4 [] = [];\
|
|
\
|
|
|
|
\f5\i \cf8 \strokec8 // Split into 4-digit indices
|
|
\f3\i0 \cf4 \strokec4 \
|
|
|
|
\f4\b \cf6 \strokec6 for
|
|
\f3\b0 \cf4 \strokec4 (
|
|
\f4\b \cf6 \strokec6 let
|
|
\f3\b0 \cf4 \strokec4 i = \cf9 \strokec9 0\cf4 \strokec4 ; i < digitStream.length; i += \cf9 \strokec9 4\cf4 \strokec4 ) \{\
|
|
|
|
\f4\b \cf6 \strokec6 const
|
|
\f3\b0 \cf4 \strokec4 indexStr = digitStream.slice(i, i + \cf9 \strokec9 4\cf4 \strokec4 );\
|
|
|
|
\f4\b \cf6 \strokec6 const
|
|
\f3\b0 \cf4 \strokec4 index = parseInt(indexStr, \cf9 \strokec9 10\cf4 \strokec4 );\
|
|
|
|
\f4\b \cf6 \strokec6 if
|
|
\f3\b0 \cf4 \strokec4 (isNaN(index) || index >= \cf9 \strokec9 2048\cf4 \strokec4 ) \{\
|
|
|
|
\f4\b \cf6 \strokec6 throw
|
|
\f3\b0 \cf4 \strokec4
|
|
\f4\b \cf6 \strokec6 new
|
|
\f3\b0 \cf4 \strokec4 \cf5 \strokec5 Error\cf4 \strokec4 (\cf7 \strokec7 `Invalid word index: \cf4 \strokec4 $\{indexStr\}\cf7 \strokec7 `\cf4 \strokec4 );\
|
|
\}\
|
|
wordIndices.push(index);\
|
|
\}\
|
|
\
|
|
|
|
\f4\b \cf6 \strokec6 const
|
|
\f3\b0 \cf4 \strokec4 wordCount = wordIndices.length;\
|
|
|
|
\f4\b \cf6 \strokec6 if
|
|
\f3\b0 \cf4 \strokec4 (wordCount !== \cf9 \strokec9 12\cf4 \strokec4 && wordCount !== \cf9 \strokec9 24\cf4 \strokec4 ) \{\
|
|
|
|
\f4\b \cf6 \strokec6 throw
|
|
\f3\b0 \cf4 \strokec4
|
|
\f4\b \cf6 \strokec6 new
|
|
\f3\b0 \cf4 \strokec4 \cf5 \strokec5 Error\cf4 \strokec4 (\cf7 \strokec7 `Invalid word count: \cf4 \strokec4 $\{wordCount\}\cf7 \strokec7 . Must be 12 or 24.`\cf4 \strokec4 );\
|
|
\}\
|
|
\
|
|
|
|
\f4\b \cf6 \strokec6 const
|
|
\f3\b0 \cf4 \strokec4 mnemonicWords = wordIndices.map(index => FULL_WORDLIST[index]);\
|
|
|
|
\f4\b \cf6 \strokec6 const
|
|
\f3\b0 \cf4 \strokec4 mnemonic = mnemonicWords.join(\cf7 \strokec7 " "\cf4 \strokec4 );\
|
|
\
|
|
|
|
\f4\b \cf6 \strokec6 return
|
|
\f3\b0 \cf4 \strokec4 \{\
|
|
mnemonic,\
|
|
wordCount,\
|
|
format: \cf7 \strokec7 "standard"\cf4 \strokec4 \
|
|
\};\
|
|
\}\
|
|
\
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f5\i \cf8 \strokec8 // --- Compact SeedQR: Parse binary entropy (128 bits for 12-word, 256 bits for 24-word) ---
|
|
\f3\i0 \cf4 \strokec4 \
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f4\b \cf6 \strokec6 function
|
|
\f3\b0 \cf4 \strokec4 parseCompactSeedQR(hexEntropy: \cf9 \strokec9 string\cf4 \strokec4 ): SeedQRResult \{\
|
|
|
|
\f4\b \cf6 \strokec6 const
|
|
\f3\b0 \cf4 \strokec4 entropy = Buffer.from(hexEntropy, \cf7 \strokec7 'hex'\cf4 \strokec4 );\
|
|
\
|
|
|
|
\f4\b \cf6 \strokec6 if
|
|
\f3\b0 \cf4 \strokec4 (entropy.length !== \cf9 \strokec9 16\cf4 \strokec4 && entropy.length !== \cf9 \strokec9 32\cf4 \strokec4 ) \{\
|
|
|
|
\f4\b \cf6 \strokec6 throw
|
|
\f3\b0 \cf4 \strokec4
|
|
\f4\b \cf6 \strokec6 new
|
|
\f3\b0 \cf4 \strokec4 \cf5 \strokec5 Error\cf4 \strokec4 (\cf7 \strokec7 `Invalid entropy length: \cf4 \strokec4 $\{entropy.length\}\cf7 \strokec7 . Must be 16 (12-word) or 32 (24-word) bytes.`\cf4 \strokec4 );\
|
|
\}\
|
|
\
|
|
|
|
\f4\b \cf6 \strokec6 const
|
|
\f3\b0 \cf4 \strokec4 wordCount = entropy.length === \cf9 \strokec9 16\cf4 \strokec4 ? \cf9 \strokec9 12\cf4 \strokec4 : \cf9 \strokec9 24\cf4 \strokec4 ;\
|
|
|
|
\f4\b \cf6 \strokec6 const
|
|
\f3\b0 \cf4 \strokec4 mnemonic = bip39.entropyToMnemonic(entropy);\
|
|
\
|
|
|
|
\f4\b \cf6 \strokec6 return
|
|
\f3\b0 \cf4 \strokec4 \{\
|
|
mnemonic,\
|
|
wordCount,\
|
|
format: \cf7 \strokec7 "compact"\cf4 \strokec4 \
|
|
\};\
|
|
\}\
|
|
\
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f5\i \cf8 \strokec8 // --- Test Vector 1 ---
|
|
\f3\i0 \cf4 \strokec4 \
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f4\b \cf6 \strokec6 async
|
|
\f3\b0 \cf4 \strokec4
|
|
\f4\b \cf6 \strokec6 function
|
|
\f3\b0 \cf4 \strokec4 runSeedSignerTests() \{\
|
|
\cf9 \strokec9 console\cf4 \strokec4 .log(\cf7 \strokec7 "
|
|
\f6 \uc0\u55358 \u56785 \u8205 \u55357 \u56620
|
|
\f3 Testing SeedSigner SeedQR Format\\n"\cf4 \strokec4 );\
|
|
\
|
|
|
|
\f5\i \cf8 \strokec8 // Test Vector 1: 24-word seed
|
|
\f3\i0 \cf4 \strokec4 \
|
|
|
|
\f4\b \cf6 \strokec6 const
|
|
\f3\b0 \cf4 \strokec4 testVector1 = \{\
|
|
mnemonic: \cf7 \strokec7 "attack pizza motion avocado network gather crop fresh patrol unusual wild holiday candy pony ranch winter theme error hybrid van cereal salon goddess expire"\cf4 \strokec4 ,\
|
|
standardDigitStream: \cf7 \strokec7 "011513251154012711900771041507421289190620080870026613431420201617920614089619290300152408010643"\cf4 \strokec4 ,\
|
|
compactHexEntropy: \cf7 \strokec7 "0e54b64107f94cc0ccfae6a13dcbec3662154fec67e0e00999c07892597d190a"\cf4 \strokec4 \
|
|
\};\
|
|
\
|
|
\cf9 \strokec9 console\cf4 \strokec4 .log(\cf7 \strokec7 "
|
|
\f6 \uc0\u55357 \u56522
|
|
\f3 Test Vector 1 (24-word)"\cf4 \strokec4 );\
|
|
\cf9 \strokec9 console\cf4 \strokec4 .log(\cf7 \strokec7 "Expected:"\cf4 \strokec4 , testVector1.mnemonic);\
|
|
\
|
|
|
|
\f4\b \cf6 \strokec6 try
|
|
\f3\b0 \cf4 \strokec4 \{\
|
|
|
|
\f5\i \cf8 \strokec8 // Test Standard SeedQR
|
|
\f3\i0 \cf4 \strokec4 \
|
|
|
|
\f4\b \cf6 \strokec6 const
|
|
\f3\b0 \cf4 \strokec4 standardResult = parseStandardSeedQR(testVector1.standardDigitStream);\
|
|
\cf9 \strokec9 console\cf4 \strokec4 .log(\cf7 \strokec7 "\\n
|
|
\f6 \uc0\u9989
|
|
\f3 Standard SeedQR PASSED"\cf4 \strokec4 );\
|
|
\cf9 \strokec9 console\cf4 \strokec4 .log(\cf7 \strokec7 ` Words: \cf4 \strokec4 $\{standardResult.wordCount\}\cf7 \strokec7 , Mnemonic: \cf4 \strokec4 $\{standardResult.mnemonic.slice(\cf9 \strokec9 0\cf4 \strokec4 , \cf9 \strokec9 50\cf4 \strokec4 )\}\cf7 \strokec7 ...`\cf4 \strokec4 );\
|
|
\
|
|
|
|
\f5\i \cf8 \strokec8 // Test Compact SeedQR
|
|
\f3\i0 \cf4 \strokec4 \
|
|
|
|
\f4\b \cf6 \strokec6 const
|
|
\f3\b0 \cf4 \strokec4 compactResult = parseCompactSeedQR(testVector1.compactHexEntropy);\
|
|
\cf9 \strokec9 console\cf4 \strokec4 .log(\cf7 \strokec7 "\\n
|
|
\f6 \uc0\u9989
|
|
\f3 Compact SeedQR PASSED"\cf4 \strokec4 );\
|
|
\cf9 \strokec9 console\cf4 \strokec4 .log(\cf7 \strokec7 ` Words: \cf4 \strokec4 $\{compactResult.wordCount\}\cf7 \strokec7 , Mnemonic: \cf4 \strokec4 $\{compactResult.mnemonic.slice(\cf9 \strokec9 0\cf4 \strokec4 , \cf9 \strokec9 50\cf4 \strokec4 )\}\cf7 \strokec7 ...`\cf4 \strokec4 );\
|
|
\
|
|
|
|
\f4\b \cf6 \strokec6 if
|
|
\f3\b0 \cf4 \strokec4 (standardResult.mnemonic === testVector1.mnemonic && compactResult.mnemonic === testVector1.mnemonic) \{\
|
|
\cf9 \strokec9 console\cf4 \strokec4 .log(\cf7 \strokec7 "\\n
|
|
\f6 \uc0\u55356 \u57225
|
|
\f3 ALL TESTS PASSED!"\cf4 \strokec4 );\
|
|
\}\
|
|
\
|
|
\}
|
|
\f4\b \cf6 \strokec6 catch
|
|
\f3\b0 \cf4 \strokec4 (error) \{\
|
|
\cf9 \strokec9 console\cf4 \strokec4 .error(\cf7 \strokec7 "
|
|
\f6 \uc0\u10060
|
|
\f3 Test failed:"\cf4 \strokec4 , error);\
|
|
\}\
|
|
\}\
|
|
\
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f5\i \cf8 \strokec8 // Run tests
|
|
\f3\i0 \cf4 \strokec4 \
|
|
runSeedSignerTests();\
|
|
\pard\pardeftab720\sa298\partightenfactor0
|
|
|
|
\f1\b\fs36 \cf2 \strokec2 Run Command\
|
|
\pard\pardeftab720\qc\partightenfactor0
|
|
|
|
\f2\b0\fs22 \cf3 \strokec3 \
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f3\fs26 \cf2 \strokec2 bash\
|
|
\pard\pardeftab720\partightenfactor0
|
|
\cf4 \strokec4 bun run seedsigner-test.ts\
|
|
\pard\pardeftab720\sa298\partightenfactor0
|
|
|
|
\f1\b\fs36 \cf2 \strokec2 Expected Output\
|
|
\pard\pardeftab720\qc\partightenfactor0
|
|
|
|
\f2\b0\fs22 \cf3 \strokec3 \
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f3\fs26 \cf2 \strokec2 text\
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f6 \cf4 \strokec4 \uc0\u55358 \u56785 \u8205 \u55357 \u56620
|
|
\f3 Testing SeedSigner SeedQR Format\
|
|
\
|
|
|
|
\f6 \uc0\u55357 \u56522
|
|
\f3 Test Vector 1 (24-word)\
|
|
Expected: attack pizza motion avocado network gather crop fresh patrol unusual wild holiday candy pony ranch winter theme error hybrid van cereal salon goddess expire\
|
|
\
|
|
|
|
\f6 \uc0\u9989
|
|
\f3 Standard SeedQR PASSED\
|
|
Words: 24, Mnemonic: attack pizza motion avocado network gather crop f...\
|
|
\
|
|
|
|
\f6 \uc0\u9989
|
|
\f3 Compact SeedQR PASSED\
|
|
Words: 24, Mnemonic: attack pizza motion avocado network gather crop f...\
|
|
\
|
|
|
|
\f6 \uc0\u55356 \u57225
|
|
\f3 ALL TESTS PASSED!\
|
|
\pard\pardeftab720\sa298\partightenfactor0
|
|
|
|
\f1\b\fs36 \cf2 \strokec2 Key Differences from Krux KEF\
|
|
|
|
\itap1\trowd \taflags0 \trgaph108\trleft-108 \trbrdrt\brdrnil \trbrdrl\brdrnil \trbrdrr\brdrnil
|
|
\clvertalc \clshdrawnil \clwWidth1160\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx2880
|
|
\clvertalc \clshdrawnil \clwWidth3425\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx5760
|
|
\clvertalc \clshdrawnil \clwWidth3052\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx8640
|
|
\pard\intbl\itap1\pardeftab720\qc\partightenfactor0
|
|
|
|
\fs24 \cf2 \strokec10 Feature\cf0 \cell
|
|
\pard\intbl\itap1\pardeftab720\qc\partightenfactor0
|
|
\cf2 SeedSigner SeedQR\cf0 \cell
|
|
\pard\intbl\itap1\pardeftab720\qc\partightenfactor0
|
|
\cf2 Krux KEF\cf0 \cell \row
|
|
|
|
\itap1\trowd \taflags0 \trgaph108\trleft-108 \trbrdrl\brdrnil \trbrdrr\brdrnil
|
|
\clvertalc \clshdrawnil \clwWidth1160\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx2880
|
|
\clvertalc \clshdrawnil \clwWidth3425\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx5760
|
|
\clvertalc \clshdrawnil \clwWidth3052\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx8640
|
|
\pard\intbl\itap1\pardeftab720\partightenfactor0
|
|
\cf2 Purpose
|
|
\f0\b0 \cf0 \cell
|
|
\pard\intbl\itap1\pardeftab720\partightenfactor0
|
|
\cf2 Encode BIP39 mnemonic indices\cf0 \cell
|
|
\pard\intbl\itap1\pardeftab720\partightenfactor0
|
|
\cf2 Encrypt arbitrary data\cf0 \cell \row
|
|
|
|
\itap1\trowd \taflags0 \trgaph108\trleft-108 \trbrdrl\brdrnil \trbrdrr\brdrnil
|
|
\clvertalc \clshdrawnil \clwWidth1160\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx2880
|
|
\clvertalc \clshdrawnil \clwWidth3425\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx5760
|
|
\clvertalc \clshdrawnil \clwWidth3052\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx8640
|
|
\pard\intbl\itap1\pardeftab720\partightenfactor0
|
|
|
|
\f1\b \cf2 Encryption
|
|
\f0\b0 \cf0 \cell
|
|
\pard\intbl\itap1\pardeftab720\partightenfactor0
|
|
|
|
\f1\b \cf2 None
|
|
\f0\b0 (plain text)\cf0 \cell
|
|
\pard\intbl\itap1\pardeftab720\partightenfactor0
|
|
\cf2 AES-GCM + PBKDF2\cf0 \cell \row
|
|
|
|
\itap1\trowd \taflags0 \trgaph108\trleft-108 \trbrdrl\brdrnil \trbrdrr\brdrnil
|
|
\clvertalc \clshdrawnil \clwWidth1160\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx2880
|
|
\clvertalc \clshdrawnil \clwWidth3425\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx5760
|
|
\clvertalc \clshdrawnil \clwWidth3052\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx8640
|
|
\pard\intbl\itap1\pardeftab720\partightenfactor0
|
|
|
|
\f1\b \cf2 Formats
|
|
\f0\b0 \cf0 \cell
|
|
\pard\intbl\itap1\pardeftab720\partightenfactor0
|
|
\cf2 Standard (digits), Compact (binary)\cf0 \cell
|
|
\pard\intbl\itap1\pardeftab720\partightenfactor0
|
|
\cf2 Versioned envelopes\cf0 \cell \row
|
|
|
|
\itap1\trowd \taflags0 \trgaph108\trleft-108 \trbrdrl\brdrnil \trbrdrr\brdrnil
|
|
\clvertalc \clshdrawnil \clwWidth1160\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx2880
|
|
\clvertalc \clshdrawnil \clwWidth3425\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx5760
|
|
\clvertalc \clshdrawnil \clwWidth3052\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx8640
|
|
\pard\intbl\itap1\pardeftab720\partightenfactor0
|
|
|
|
\f1\b \cf2 Input
|
|
\f0\b0 \cf0 \cell
|
|
\pard\intbl\itap1\pardeftab720\partightenfactor0
|
|
\cf2 QR contains raw indices/entropy\cf0 \cell
|
|
\pard\intbl\itap1\pardeftab720\partightenfactor0
|
|
\cf2 Base43-encoded encrypted data\cf0 \cell \row
|
|
|
|
\itap1\trowd \taflags0 \trgaph108\trleft-108 \trbrdrl\brdrnil \trbrdrt\brdrnil \trbrdrr\brdrnil
|
|
\clvertalc \clshdrawnil \clwWidth1160\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx2880
|
|
\clvertalc \clshdrawnil \clwWidth3425\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx5760
|
|
\clvertalc \clshdrawnil \clwWidth3052\clftsWidth3 \clmart10 \clmarl10 \clmarb10 \clmarr10 \clbrdrt\brdrnil \clbrdrl\brdrnil \clbrdrb\brdrnil \clbrdrr\brdrnil \clpadt20 \clpadl20 \clpadb20 \clpadr20 \gaph\cellx8640
|
|
\pard\intbl\itap1\pardeftab720\partightenfactor0
|
|
|
|
\f1\b \cf2 Output
|
|
\f0\b0 \cf0 \cell
|
|
\pard\intbl\itap1\pardeftab720\partightenfactor0
|
|
\cf2 BIP39 mnemonic\cf0 \cell
|
|
\pard\intbl\itap1\pardeftab720\partightenfactor0
|
|
\cf2 Raw decrypted bytes\cf0 \cell \lastrow\row
|
|
\pard\pardeftab720\qc\partightenfactor0
|
|
|
|
\f2\fs22 \cf3 \strokec3 \
|
|
\
|
|
\pard\pardeftab720\sa298\partightenfactor0
|
|
|
|
\f1\b\fs36 \cf2 \strokec2 Usage in Your App\
|
|
\pard\pardeftab720\qc\partightenfactor0
|
|
|
|
\f2\b0\fs22 \cf3 \strokec3 \
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f3\fs26 \cf2 \strokec2 typescript\
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f5\i \cf8 \strokec8 // Detect format automatically
|
|
\f3\i0 \cf4 \strokec4 \
|
|
\pard\pardeftab720\partightenfactor0
|
|
|
|
\f4\b \cf6 \strokec6 export
|
|
\f3\b0 \cf4 \strokec4
|
|
\f4\b \cf6 \strokec6 async
|
|
\f3\b0 \cf4 \strokec4
|
|
\f4\b \cf6 \strokec6 function
|
|
\f3\b0 \cf4 \strokec4 parseSeedQR(qrData: \cf9 \strokec9 string\cf4 \strokec4 ): \cf9 \strokec9 Promise\cf4 \strokec4 <\cf9 \strokec9 string\cf4 \strokec4 > \{\
|
|
|
|
\f5\i \cf8 \strokec8 // Check if it's numeric digits (Standard SeedQR)
|
|
\f3\i0 \cf4 \strokec4 \
|
|
|
|
\f4\b \cf6 \strokec6 if
|
|
\f3\b0 \cf4 \strokec4 (\cf11 \strokec11 /^\\d+$/\cf4 \strokec4 .test(qrData)) \{\
|
|
|
|
\f4\b \cf6 \strokec6 return
|
|
\f3\b0 \cf4 \strokec4 parseStandardSeedQR(qrData).mnemonic;\
|
|
\}\
|
|
\
|
|
|
|
\f5\i \cf8 \strokec8 // Check if it's hex (Compact SeedQR)
|
|
\f3\i0 \cf4 \strokec4 \
|
|
|
|
\f4\b \cf6 \strokec6 if
|
|
\f3\b0 \cf4 \strokec4 (\cf11 \strokec11 /^[0-9a-fA-F]+$/\cf4 \strokec4 .test(qrData)) \{\
|
|
|
|
\f4\b \cf6 \strokec6 return
|
|
\f3\b0 \cf4 \strokec4 parseCompactSeedQR(qrData).mnemonic;\
|
|
\}\
|
|
\
|
|
|
|
\f4\b \cf6 \strokec6 throw
|
|
\f3\b0 \cf4 \strokec4
|
|
\f4\b \cf6 \strokec6 new
|
|
\f3\b0 \cf4 \strokec4 \cf5 \strokec5 Error\cf4 \strokec4 (\cf7 \strokec7 "Unsupported SeedQR format"\cf4 \strokec4 );\
|
|
\}\
|
|
\pard\pardeftab720\sa240\partightenfactor0
|
|
|
|
\f0\fs24 \cf2 \strokec2 The
|
|
\f3\fs26 bip39
|
|
\f0\fs24 library handles the full English wordlist and checksum validation automatically!\
|
|
} |