private void CheckIfReaderRemoved() { if (LoadedBChips != null) { if (Monitor.TryEnter(readLock)) { try { string readerName = LoadedBChips.ReaderName; string[] readers = GetReaderNames(); foreach (string reader in readers) { if (readerName == reader) { return; } } // Card pulled with reader LoadedBChips = null; ChangePageUi(PageToShow.NoCard, null); StopMonitoring(); } finally { Monitor.Exit(readLock); } } } }
private void _monitor_CardInserted(object sender, CardStatusEventArgs e) { if (e.State == SCRState.Present) { try { BChipSmartCard insertedCard = new BChipSmartCard(e.ReaderName) { ATR = e.Atr, LastConnected = DateTime.Now, IsConnected = true }; if (insertedCard.Type() == CardType.BChip) { ScanAndLoadConnectedCards(insertedCard); } } catch (Exception ex) { Logger.Log(ex); } } }
private void _monitor_CardRemoved(object sender, CardStatusEventArgs e) { if (LoadedBChips != null) { if (LoadedBChips.ReaderName == e.ReaderName) { ChangePageUi(PageToShow.NoCard, null); // single card only. // bchip.IsConnected = false; LoadedBChips = null; } } }
public PageToShow AnalyzeCardData(BChipSmartCard bChipSmartCard) { if (!bChipSmartCard.IsConnected) { return(PageToShow.NoCard); } if (bChipSmartCard.Type() != CardType.BChip || bChipSmartCard.SmartCardData == null) { return(PageToShow.Unsupported); } // Analyze card try { BChipMemoryLayout_BCHIP cardMemory = (BChipMemoryLayout_BCHIP)bChipSmartCard.SmartCardData; if (cardMemory.IsFormatted) { WalletTypeComboBox.Dispatcher.BeginInvoke(new Action(() => { WalletTypeComboBox.SelectedItem = null; })); return(PageToShow.NotInitialized); } if (!cardMemory.IsChecksumValid()) { return(PageToShow.CrcError); } // All seems good return(PageToShow.Ready); } catch (Exception ex) { Logger.Log(ex); } return(PageToShow.Error); }
private async void FormatCardAsync() { try { DispatcherUpdater(FormatingViewGrid, Visibility.Visible); using (var context = _contextFactory.Establish(SCardScope.System)) { using (var isoReader = new IsoReader(context, LoadedBChips.ReaderName, SCardShareMode.Shared, SCardProtocol.Any)) { if (LoadedBChips.AdpuInterface.RequiresInit) { Response initResponse = isoReader.Transmit(LoadedBChips.AdpuInterface.InitCard()); if (initResponse.SW1 != 0x90) { Logger.Log("Error initializing smart card reader"); } } var unlockResponse = isoReader.Transmit(LoadedBChips.AdpuInterface.UnblockCard()); Logger.Log($"ADPU response from pin request: {unlockResponse.StatusWord:X}"); var writeResponse = isoReader.Transmit(LoadedBChips.AdpuInterface.FormatCard(CardType.BChip)); Logger.Log($"ADPU response from format request: {unlockResponse.StatusWord:X}"); } } LoadedBChips = null; } catch (Exception ex) { Logger.Log(ex); } finally { DispatcherUpdater(FormatingViewGrid, Visibility.Collapsed); ScanAndLoadConnectedCards(); } }
private void ProvisionCardButton_Click(object sender, RoutedEventArgs e) { CreateKeyErrorLabel.Content = ""; CreatingKeyLabel.Content = ""; if (passphrase.Password.Length == 0 || passphraseConfirmation.Password.Length == 0) { CreateKeyErrorLabel.Content = "A passphrase was not entered."; return; } if (passphrase.Password != passphraseConfirmation.Password) { CreateKeyErrorLabel.Content = "Passphrases entered did not match."; return; } if (PrivateKeyTextBox.Text.Length == 0) { CreateKeyErrorLabel.Content = "No private key data to store."; return; } if (CardPkType == PKType.BTC || CardPkType == PKType.BTC_TestNet) { // Check if we are importing a user's wif or mnemonic, update public key field // to allow the user to confirm if (CardGeneratedKey.GetBitcoinSecret(Network).ToWif() != PrivateKeyTextBox.Text) { try { NBitcoin.Key importedKey = null; int spacesDetected = PrivateKeyTextBox.Text.Count(a => a == ' '); // Check for mnemonic if (spacesDetected >= 11) { NBitcoin.Mnemonic mnemonic = new NBitcoin.Mnemonic(PrivateKeyTextBox.Text); // Force bitcoin and first address importedKey = mnemonic.DeriveExtKey().Derive(KeyPath.Parse("m/44'/0'/0'/0/0")).PrivateKey; } else { // Check for wif importedKey = NBitcoin.Key.Parse(PrivateKeyTextBox.Text); } // Replace CardGeneratedKey with imported key. Only valid for bitcoin addresses. CardGeneratedKey = importedKey; PublicKeyTextBox.Text = importedKey.PubKey.GetAddress(Network).ToString(); PrivateKeyTextBox.Text = importedKey.GetBitcoinSecret(Network).ToWif(); CreatingKeyLabel.Content = "Key data imported, ready to setup your bChip."; return; } catch (Exception ex) { Logger.Log(ex); CreateKeyErrorLabel.Content = "Failed to automatically parse private key data."; return; } } } byte[] privateKeyToEncrypt = null; byte[] publicKeyData = null; if (CardGeneratedKey != null) { // Users can optionally remove the public key portion, so we'll skip saving it. if (PublicKeyTextBox.Text.Length > 0) { publicKeyData = CardGeneratedKey.PubKey.ToBytes(); } byte[] privateKeyBytes = CardGeneratedKey.GetWif(Network.Main).ToBytes(); // Always seem to get an extra bit at the end... if (privateKeyBytes.Length == 33 && privateKeyBytes[32] == 0x1) { privateKeyToEncrypt = privateKeyBytes.Take(32).ToArray(); } else { privateKeyToEncrypt = privateKeyBytes; } } else { if (PublicKeyTextBox.Text.Length > 0) { publicKeyData = CryptographicBuffer.ConvertStringToBinary(PublicKeyTextBox.Text, BinaryStringEncoding.Utf8).ToArray(); } privateKeyToEncrypt = CryptographicBuffer.ConvertStringToBinary(PrivateKeyTextBox.Text, BinaryStringEncoding.Utf8).ToArray(); } if (privateKeyToEncrypt.Length > BChipMemoryLayout_BCHIP.PRIVATEKEY_MAX_DATA) { CreateKeyErrorLabel.Content = $"Private key was {PrivateKeyTextBox.Text.Length} bytes, {Math.Abs(PrivateKeyTextBox.Text.Length - BChipMemoryLayout_BCHIP.PRIVATEKEY_MAX_DATA)} bytes over the limit."; return; } if (publicKeyData != null) { if (publicKeyData.Length > BChipMemoryLayout_BCHIP.PUBKEY_MAX_DATA) { CreateKeyErrorLabel.Content = $"Public key was {PublicKeyTextBox.Text.Length} bytes, {Math.Abs(PublicKeyTextBox.Text.Length - BChipMemoryLayout_BCHIP.PUBKEY_MAX_DATA)} bytes over the limit."; return; } } string friendlyName = FriendlyNameTextBox.Text; ProvisionNewKeysViewGrid.Dispatcher.BeginInvoke(new Action(() => { ProvisionNewKeysViewGrid.IsEnabled = false; })); Task.Run(new Action(() => { try { UpdateTextLabel(CreatingKeyLabel, "Setting up bChip! Do not remove card."); // Encrypt data BChipMemoryLayout_BCHIP bchip = LoadedBChips.SmartCardData as BChipMemoryLayout_BCHIP; bchip.EncryptPrivateKeyData(CardPkType, passphrase.Password, privateKeyToEncrypt, publicKeyData); bchip.SetFriendlyName(friendlyName); using (var context = _contextFactory.Establish(SCardScope.System)) { using (var isoReader = new IsoReader(context, LoadedBChips.ReaderName, SCardShareMode.Shared, SCardProtocol.Any)) { if (LoadedBChips.AdpuInterface.RequiresInit) { Response initResponse = isoReader.Transmit(LoadedBChips.AdpuInterface.InitCard()); if (initResponse.SW1 != 0x90) { Logger.Log("Error initializing smart card reader"); } } var unlockResponse = isoReader.Transmit(LoadedBChips.AdpuInterface.UnblockCard()); Logger.Log($"ADPU response from pin request: {unlockResponse.StatusWord:X}"); if (unlockResponse.SW1 != 0x90) { UpdateTextLabel(CreatingKeyLabel, ""); UpdateTextLabel(CreateKeyErrorLabel, "Could not be unlock bchip for writing."); return; } var writeResponse = isoReader.Transmit(LoadedBChips.AdpuInterface.WriteCard(bchip)); Logger.Log($"ADPU response from write card data: {unlockResponse.StatusWord:X}"); if (writeResponse.StatusWord != 0x00009000) { UpdateTextLabel(CreatingKeyLabel, ""); UpdateTextLabel(CreateKeyErrorLabel, "Failure writing data to the bchip."); return; } } } UpdateTextLabel(CreatingKeyLabel, string.Empty); ClearTextBox(FriendlyNameTextBox); // Done? clear loaded card, reload card LoadedBChips = null; ChangePageUi(PageToShow.NoCard, null); ScanAndLoadConnectedCards(); } catch (Exception ex) { Logger.Log(ex); } finally { ProvisionNewKeysViewGrid.Dispatcher.BeginInvoke(new Action(() => { ProvisionNewKeysViewGrid.IsEnabled = true; })); } })); }
private void ScanAndLoadConnectedCards(BChipSmartCard insertedCard = null) { using (var context = _contextFactory.Establish(SCardScope.System)) { if (insertedCard == null) { List <string> readerNames = GetReaderNames().ToList(); foreach (string curReader in readerNames) { try { using (var reader = context.ConnectReader(curReader, SCardShareMode.Shared, SCardProtocol.Any)) { insertedCard = new BChipSmartCard(curReader) { ATR = reader.GetAttrib(SCardAttribute.AtrString), LastConnected = DateTime.Now, IsConnected = true }; } if (insertedCard.Type() != CardType.BChip) { Logger.Log($"Card type was not an expected BChip and skipped ({insertedCard.Type()})"); continue; } } // We try to check for an inserted card, eat errors. catch (Exception ex) { if ((uint)ex.HResult == 0x80131500) { // ignore card removed continue; } } } } if (insertedCard == null) { ChangePageUi(PageToShow.NoCard, null); } if (insertedCard != null) { using (var isoReader = new IsoReader(context, insertedCard.ReaderName, SCardShareMode.Shared, SCardProtocol.Any)) { try { if (insertedCard.AdpuInterface.RequiresInit) { Response initResponse = isoReader.Transmit(insertedCard.AdpuInterface.InitCard()); if (initResponse.SW1 != 0x90) { Logger.Log("Error initializing smart card reader"); } } Response response = isoReader.Transmit(insertedCard.AdpuInterface.ReadCard()); if (response.HasData) { byte[] data = response.GetData(); byte[] mlvi = data.Skip(0x20).Take(BChipMemoryLayout_BCHIP.MLVI_MAX_DATA).ToArray(); byte[] carddata = data.Skip(0x28).ToArray(); // At this stage, the card has not yet been validated/parsed. Another thread should handle cleanup/de-dupes insertedCard.SmartCardData = new BChipMemoryLayout_BCHIP(mlvi, carddata, PKStatus.NotValidated); LoadedBChips = insertedCard; ChangePageUi(AnalyzeCardData(insertedCard), insertedCard); } else { Logger.Log("Card added had no data to download"); // TODO: Add card data could not be loaded (not CRC error) } } catch (Exception ex) { Logger.Log(ex); }; } } } }
public void ChangePageUi(PageToShow pageToShow, BChipSmartCard bChipSmartCard) { Visibility noCardVisibility = Visibility.Collapsed; Visibility readyVisibility = Visibility.Collapsed; Visibility confirmFormatVisibility = Visibility.Collapsed; Visibility passphraseDialogVisibility = Visibility.Collapsed; Visibility notInitializedWizardVisibility = Visibility.Collapsed; Visibility crcErrorViewGridVisibility = Visibility.Collapsed; Visibility unsupportedDialogVisibility = Visibility.Collapsed; Visibility provisionNewKeysViewGridVisibility = Visibility.Collapsed; Visibility provisionMnemonicViewGridVisibility = Visibility.Collapsed; Visibility showPrivateKeyViewGridVisibility = Visibility.Collapsed; ClearPasswordBox(PassphraseEntryBox); ClearPasswordBox(passphrase); ClearPasswordBox(passphraseConfirmation); ClearPasswordBox(Mnemonicpassphrase); ClearPasswordBox(MnemonicpassphraseConfirmation); ClearTextBox(MnemonicEntryTextBox); // Clear last error if set UpdateTextLabel(ErrorMessageLabel, ""); switch (pageToShow) { case PageToShow.ProvisionMnemonic: provisionMnemonicViewGridVisibility = Visibility.Visible; break; case PageToShow.ProvisionCard: provisionNewKeysViewGridVisibility = Visibility.Visible; break; case PageToShow.ConfirmFormat: confirmFormatVisibility = Visibility.Visible; break; case PageToShow.Ready: readyVisibility = Visibility.Visible; break; case PageToShow.ShowPassphraseDialog: passphraseDialogVisibility = Visibility.Visible; break; case PageToShow.NoCard: noCardVisibility = Visibility.Visible; break; case PageToShow.Unsupported: case PageToShow.Error: unsupportedDialogVisibility = Visibility.Visible; break; case PageToShow.NotInitialized: notInitializedWizardVisibility = Visibility.Visible; break; case PageToShow.CrcError: crcErrorViewGridVisibility = Visibility.Visible; break; } DispatcherUpdater(ProvisionMnemonicPhraseViewGrid, provisionMnemonicViewGridVisibility); DispatcherUpdater(ShowPrivateKeyViewGrid, showPrivateKeyViewGridVisibility); DispatcherUpdater(ProvisionNewKeysViewGrid, provisionNewKeysViewGridVisibility); DispatcherUpdater(NonInitializedWizardViewGrid, notInitializedWizardVisibility); DispatcherUpdater(CrcErrorViewGrid, crcErrorViewGridVisibility); DispatcherUpdater(InitializedUnknownCardViewGrid, unsupportedDialogVisibility); DispatcherUpdater(ShowPassphraseViewGrid, passphraseDialogVisibility); DispatcherUpdater(InsertCardGrid, noCardVisibility); DispatcherUpdater(ConfirmFormatViewGrid, confirmFormatVisibility); DispatcherUpdater(ReadyCardViewGrid, readyVisibility); if (pageToShow == PageToShow.NoCard) { // Clear any keys left in UI pages UpdateTextLabel(PrivateKeyAddressLabel, ""); PrivateQrCodeImage.Dispatcher.BeginInvoke(new Action(() => { PrivateQrCodeImage.Source = null; })); } // Populate for card UI if (pageToShow == PageToShow.Ready) { QrCodeImage.Dispatcher.BeginInvoke(new Action(() => { QrCodeImage.Visibility = Visibility.Hidden; })); PubKeyCopyIcon.Dispatcher.BeginInvoke(new Action(() => { PubKeyCopyIcon.Visibility = Visibility.Hidden; })); BChipMemoryLayout_BCHIP bchip = (BChipMemoryLayout_BCHIP)bChipSmartCard.SmartCardData; UpdateTextLabel(BChipIdLabel, bchip.IdLabel); switch (bchip.PkType) { case PKType.BTC: UpdateTextLabel(PKTypeLabel, "Bitcoin (BTC)"); break; case PKType.BTC_TestNet: UpdateTextLabel(PKTypeLabel, "Bitcoin (TestNet)"); break; case PKType.CUSTOM: UpdateTextLabel(PKTypeLabel, "Custom"); break; case PKType.UNSET: UpdateTextLabel(PKTypeLabel, "(no key data)"); break; } string publicAddress = bchip.PublicAddress; if (publicAddress == "") { if (bchip.PkType == PKType.UNSET) { UpdateTextLabel(PublicKeyAddressLabel, "Card not setup - remove and re-insert card to reload"); DisplayPrivateKeyButton.Dispatcher.BeginInvoke(new Action(() => { DisplayPrivateKeyButton.IsEnabled = false; })); } else { UpdateTextLabel(PublicKeyAddressLabel, "No public key data on card"); } } else if (publicAddress == null) { UpdateTextLabel(PublicKeyAddressLabel, "Failed to parse public key data"); } else { QrCodeImage.Dispatcher.BeginInvoke(new Action(() => { QrCodeImage.Source = Imaging.CreateBitmapSourceFromHBitmap( new QrHandler(publicAddress, 5).GetQrCode().GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); QrCodeImage.Visibility = Visibility.Visible; })); PubKeyCopyIcon.Dispatcher.BeginInvoke(new Action(() => { PubKeyCopyIcon.Visibility = Visibility.Visible; })); UpdateTextLabel(PublicKeyAddressLabel, publicAddress); } if (bchip.PkType != PKType.UNSET) { DisplayPrivateKeyButton.Dispatcher.BeginInvoke(new Action(() => { DisplayPrivateKeyButton.IsEnabled = true; })); } } }
private void MnemonicProvisionCardButton_Click(object sender, RoutedEventArgs e) { MnemonicErrorLabel.Content = ""; MnemonicKeyLabel.Content = ""; if (Mnemonicpassphrase.Password.Length == 0 || MnemonicpassphraseConfirmation.Password.Length == 0) { MnemonicErrorLabel.Content = "A passphrase was not entered."; return; } if (Mnemonicpassphrase.Password != MnemonicpassphraseConfirmation.Password) { MnemonicErrorLabel.Content = "Passphrases entered did not match."; return; } if (MnemonicEntryTextBox.Text.Length == 0) { MnemonicErrorLabel.Content = "No private key data to store."; return; } if (CardPkType != PKType.Mnemonic) { MnemonicErrorLabel.Content = "Invalid application state. Please restart app."; return; } byte[] privateKeyToEncrypt = null; byte[] publicKeyData = CryptographicBuffer.ConvertStringToBinary("Mnemonic seed backed up.", BinaryStringEncoding.Utf8).ToArray(); try { privateKeyToEncrypt = BIP39Helpers.GenerateEntropyFromWords(MnemonicEntryTextBox.Text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries), NBitcoin.Wordlist.English); } // TODO: Implement better exception types to ease debugging catch (Exception ex) { Logger.Log(ex); MnemonicErrorLabel.Content = "Failed to automatically parse mnemonic phrase."; return; } if (privateKeyToEncrypt.Length > BChipMemoryLayout_BCHIP.PRIVATEKEY_MAX_DATA) { CreateKeyErrorLabel.Content = $"Private key was {PrivateKeyTextBox.Text.Length} bytes, {Math.Abs(PrivateKeyTextBox.Text.Length - BChipMemoryLayout_BCHIP.PRIVATEKEY_MAX_DATA)} bytes over the limit."; return; } string friendlyName = MnemonicFriendlyNameTextBox.Text; ProvisionMnemonicPhraseViewGrid.Dispatcher.BeginInvoke(new Action(() => { ProvisionMnemonicPhraseViewGrid.IsEnabled = false; })); Task.Run(new Action(() => { try { UpdateTextLabel(MnemonicKeyLabel, "Setting up bChip! Do not remove card."); // Encrypt data BChipMemoryLayout_BCHIP bchip = LoadedBChips.SmartCardData as BChipMemoryLayout_BCHIP; bchip.EncryptPrivateKeyData(CardPkType, Mnemonicpassphrase.Password, privateKeyToEncrypt, publicKeyData); bchip.SetFriendlyName(friendlyName); using (var context = _contextFactory.Establish(SCardScope.System)) { using (var isoReader = new IsoReader(context, LoadedBChips.ReaderName, SCardShareMode.Shared, SCardProtocol.Any)) { if (LoadedBChips.AdpuInterface.RequiresInit) { Response initResponse = isoReader.Transmit(LoadedBChips.AdpuInterface.InitCard()); if (initResponse.SW1 != 0x90) { Logger.Log("Error initializing smart card reader"); } } var unlockResponse = isoReader.Transmit(LoadedBChips.AdpuInterface.UnblockCard()); Logger.Log($"ADPU response from pin request: {unlockResponse.StatusWord:X}"); if (unlockResponse.SW1 != 0x90) { UpdateTextLabel(MnemonicKeyLabel, ""); UpdateTextLabel(MnemonicErrorLabel, "Could not be unlock bchip for writing."); return; } var writeResponse = isoReader.Transmit(LoadedBChips.AdpuInterface.WriteCard(bchip)); Logger.Log($"ADPU response from write card data: {unlockResponse.StatusWord:X}"); if (writeResponse.SW1 != 0x90) { UpdateTextLabel(MnemonicKeyLabel, ""); UpdateTextLabel(MnemonicErrorLabel, "Failure writing data to the bchip."); return; } } } UpdateTextLabel(MnemonicKeyLabel, string.Empty); ClearTextBox(MnemonicFriendlyNameTextBox); // Done? clear loaded card, reload card LoadedBChips = null; ScanAndLoadConnectedCards(); } catch (Exception ex) { Logger.Log(ex); } finally { ProvisionMnemonicPhraseViewGrid.Dispatcher.BeginInvoke(new Action(() => { ProvisionMnemonicPhraseViewGrid.IsEnabled = true; })); } })); }