Generate SSS shares from mnemonic words
This commit is contained in:
@@ -22,6 +22,14 @@ import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { colors, typography, spacing, borderRadius, shadows } from '../theme/colors';
|
||||
import { SystemStatus, KillSwitchLog } from '../types';
|
||||
import VaultScreen from './VaultScreen';
|
||||
import {
|
||||
SSSShare,
|
||||
mnemonicToEntropy,
|
||||
splitSecret,
|
||||
formatShareCompact,
|
||||
serializeShare,
|
||||
verifyShares,
|
||||
} from '../utils/sss';
|
||||
|
||||
// Nautical-themed mnemonic word list (unique words only)
|
||||
const MNEMONIC_WORDS = [
|
||||
@@ -52,11 +60,39 @@ const generateMnemonic = (wordCount = 12) => {
|
||||
return words;
|
||||
};
|
||||
|
||||
const splitMnemonic = (words: string[]) => [
|
||||
words.slice(0, 4),
|
||||
words.slice(4, 8),
|
||||
words.slice(8, 12),
|
||||
];
|
||||
/**
|
||||
* Generate SSS shares from mnemonic words
|
||||
* Uses Shamir's Secret Sharing (3,2) threshold scheme
|
||||
*/
|
||||
const generateSSSShares = (words: string[]): SSSShare[] => {
|
||||
try {
|
||||
// Convert mnemonic to entropy (big integer)
|
||||
const entropy = mnemonicToEntropy(words, MNEMONIC_WORDS);
|
||||
|
||||
// Split entropy into 3 shares using SSS
|
||||
const shares = splitSecret(entropy);
|
||||
|
||||
// Verify shares can recover the original (optional, for debugging)
|
||||
if (__DEV__) {
|
||||
const isValid = verifyShares(shares, entropy);
|
||||
if (!isValid) {
|
||||
console.warn('SSS verification failed!');
|
||||
} else {
|
||||
console.log('SSS shares verified successfully');
|
||||
}
|
||||
}
|
||||
|
||||
return shares;
|
||||
} catch (error) {
|
||||
console.error('Failed to generate SSS shares:', error);
|
||||
// Fallback: return empty shares (should not happen in production)
|
||||
return [
|
||||
{ x: 1, y: BigInt(0), label: 'device' },
|
||||
{ x: 2, y: BigInt(0), label: 'cloud' },
|
||||
{ x: 3, y: BigInt(0), label: 'heir' },
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
// Icon names type for type safety
|
||||
type StatusIconName = 'checkmark-circle' | 'warning' | 'alert-circle';
|
||||
@@ -127,7 +163,7 @@ export default function SentinelScreen() {
|
||||
const [showVault, setShowVault] = useState(false);
|
||||
const [showMnemonic, setShowMnemonic] = useState(false);
|
||||
const [mnemonicWords, setMnemonicWords] = useState<string[]>([]);
|
||||
const [mnemonicParts, setMnemonicParts] = useState<string[][]>([]);
|
||||
const [sssShares, setSssShares] = useState<SSSShare[]>([]);
|
||||
const [showEmailForm, setShowEmailForm] = useState(false);
|
||||
const [emailAddress, setEmailAddress] = useState('');
|
||||
const [isCapturing, setIsCapturing] = useState(false);
|
||||
@@ -188,16 +224,20 @@ export default function SentinelScreen() {
|
||||
|
||||
const openVaultWithMnemonic = () => {
|
||||
const words = generateMnemonic();
|
||||
const parts = splitMnemonic(words);
|
||||
const shares = generateSSSShares(words);
|
||||
setMnemonicWords(words);
|
||||
setMnemonicParts(parts);
|
||||
setSssShares(shares);
|
||||
setShowMnemonic(true);
|
||||
setShowVault(false);
|
||||
setShowEmailForm(false);
|
||||
setEmailAddress('');
|
||||
AsyncStorage.setItem('sentinel_mnemonic_part_local', parts[0].join(' ')).catch(() => {
|
||||
// Best-effort local store; UI remains available
|
||||
});
|
||||
|
||||
// Store Share A (device share) locally
|
||||
if (shares[0]) {
|
||||
AsyncStorage.setItem('sentinel_share_device', serializeShare(shares[0])).catch(() => {
|
||||
// Best-effort local store; UI remains available
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleScreenshot = async () => {
|
||||
@@ -515,7 +555,7 @@ export default function SentinelScreen() {
|
||||
<Text style={styles.mnemonicTitle}>12-Word Mnemonic</Text>
|
||||
</View>
|
||||
<Text style={styles.mnemonicSubtitle}>
|
||||
Your mnemonic is split into 3 parts (4/4/4). Part 1 is stored locally.
|
||||
Your seed is protected by SSS (3,2) threshold encryption. Any 2 shares can restore your vault.
|
||||
</Text>
|
||||
<View style={styles.mnemonicBlock}>
|
||||
<Text style={styles.mnemonicBlockText}>
|
||||
@@ -524,19 +564,25 @@ export default function SentinelScreen() {
|
||||
</View>
|
||||
<View style={styles.partGrid}>
|
||||
<View style={[styles.partCard, styles.partCardStored]}>
|
||||
<Text style={styles.partLabel}>PART 1 • LOCAL</Text>
|
||||
<Text style={styles.partValue}>{mnemonicParts[0]?.join(' ')}</Text>
|
||||
<Text style={styles.partLabel}>SHARE A • DEVICE</Text>
|
||||
<Text style={styles.partValue}>
|
||||
{sssShares[0] ? formatShareCompact(sssShares[0]) : '---'}
|
||||
</Text>
|
||||
<Text style={styles.partHint}>Stored on this device</Text>
|
||||
</View>
|
||||
<View style={styles.partCard}>
|
||||
<Text style={styles.partLabel}>PART 2 • CLOUD NODE</Text>
|
||||
<Text style={styles.partValue}>{mnemonicParts[1]?.join(' ')}</Text>
|
||||
<Text style={styles.partHint}>To be synced</Text>
|
||||
<Text style={styles.partLabel}>SHARE B • CLOUD</Text>
|
||||
<Text style={styles.partValue}>
|
||||
{sssShares[1] ? formatShareCompact(sssShares[1]) : '---'}
|
||||
</Text>
|
||||
<Text style={styles.partHint}>To be synced to Sentinel</Text>
|
||||
</View>
|
||||
<View style={styles.partCard}>
|
||||
<Text style={styles.partLabel}>PART 3 • HEIR</Text>
|
||||
<Text style={styles.partValue}>{mnemonicParts[2]?.join(' ')}</Text>
|
||||
<Text style={styles.partHint}>To be assigned</Text>
|
||||
<Text style={styles.partLabel}>SHARE C • HEIR</Text>
|
||||
<Text style={styles.partValue}>
|
||||
{sssShares[2] ? formatShareCompact(sssShares[2]) : '---'}
|
||||
</Text>
|
||||
<Text style={styles.partHint}>For your heir (2-of-3 required)</Text>
|
||||
</View>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
|
||||
Reference in New Issue
Block a user