Coder Social home page Coder Social logo

atc1441 / esp32_nrf52_swd Goto Github PK

View Code? Open in Web Editor NEW
213.0 14.0 36.0 1.21 MB

This software brings you the possibility to Read and Write the internal Flash of the Nordic nRF52 series with an ESP32

License: GNU General Public License v3.0

C++ 71.28% C 6.43% HTML 22.29%

esp32_nrf52_swd's Introduction

ESP32 SWD Flasher for nRF52

This software makes it possible to Read and Write the internal Flash of the Nordic nRF52 series with an ESP32 using the SWD interface. A tool to exploit the APPROTECT vulnerability is included as well.

You can support my work via PayPal: https://paypal.me/hoverboard1 this keeps projects like this coming.

To flash an nRF52 connect the following:

  • nRF52 SWDCLK to ESP32 GPIO 21
  • nRF52 SWDIO to ESP32 GPIO 19
  • nRF52 GND to ESP32 GND to N-Channel MOSFET GND (Optional: O-scope GND Clips)
  • Then power the nRF52 as needed

To bypass the Readout protection (APPROTECT) of an nRF52 connect all of the above and the following:

  • nRF52 3.3V Power VDD to ESP32 GPIO 22 (Optional: O-scope Channel 2 Probe)
  • N-Channel MOSFET PWM+ to ESP32 GPIO 5 (as shown)
  • N-Channel MOSFET VOUT- to nRF52 DEC1 (as shown) (Optional: O-scope Channel 1 Probe)
  • Then power the nRF52 as needed

This repo is explained and demonstrated in these videos (click to watch):

YoutubeVideo

YoutubeVideo

Required Hardware

  • ESP32 Development Board
  • N-Channel MOSFET Board
  • nRF52 Series Board
  • Optional: Oscilloscope

Required Software

HowTo:

Note: Use version 1.0.6 of the ESP32 core. Also use the source files vs the release packages.

Arduino:

(It is also possible to use PlatformIO)

  • Copy or install the three downloaded libraries (AsyncTCP, ESPAsyncWebServer, WifiManager) into the Arduino > libraries directory
  • Arduino > libraries: Rename:
  • AsyncTCP-master to AsyncTCP
  • ESPAsyncWebServer-master to ESPAsyncWebServer
  • WiFiManager-master to WiFiManager
  • Add the ESP32 Core to Arduino (File > Additional Boards Manager URLS > https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json)
  • Install the ESP32 Core (Tools > Boards Manager > Search > esp32 > Select version > 1.0.6 > Install)
  • Open the "ESP32_SWD_WIFI.ino" file with Arduino and select the "ESP32 Dev Module" (Tools > Board: > ESP32 Arduino > ESP32 Dev Module)
  • Make sure to select "No OTA (1MB App / 3MB SPIFFS)" as the partition scheme for the ESP32 to have more space available. (Tools > Partition Scheme: > "No OTA (1MB App/3MB SPIFFS)")
  • Select the ESP32 COM port (Tools > Port: > COM#)
  • Click Verify
  • Click Upload
  • Long-press the BOOT button on ESP32 after clicking Upload, until it is showing "Uploading"
  • Once it displays "Leaving... Hard resetting..." it’s done flashing and ready to setup
  • The ESP32 will now create a Wifi Network with the WifiManager called: "AutoConnectAP" after connecting to the Wifi go to the ip: 192.168.4.1 in your Browser
  • Configure the Wifi for your home network
  • Once fully connected enter: "http://swd.local" in your internet browser and it should show a first page from the ESP32
  • Go to: "http://swd.local/edit" login with admin:admin
  • Click Choose File and browse for the "data/index.htm" file and click Upload
  • Go to: "http://swd.local" again, the ESP32 SWD Flasher page should now be displayed
  • If not already done, connect the nRF52 via SWD. Click the button "Init SWD" and wait for the response in the info page or look in the Arduino UART terminal if something doesn't work. The nRF chip should be detected and it will display a notification about whether or not the nRF52 is locked
  • To flash new firmware to an nRF52 you can erase the whole chip and then flash an uploaded file via the "Flash File" button, you need to enter the correct filename
  • To dump the flash content of an nRF52 enter a filename, an offset if wanted, and a size of bytes in decimal then click the "Dump to File" button and wait for it to finish
  • To Glitch the nRF52 use the Delay Input to find the right spot to glitch, it should be near the small voltage drop of the DEC1 line, best is to have an Oscilloscope connected to see what is happening, but you can also just blindly find the delay as the delay will automatically increase and the ESP32 will notify when it achieves a successful glitch after clicking "Enable Glitcher" you can change the delay time on the fly

ESP32 Glitcher schematic:

nRF52832 Glitch Tip, way better results with these 2 caps removed

Credits go to LimitedResults for finding the Power glitching Exploit: https://limitedresults.com/2020/06/nrf52-debug-resurrection-approtect-bypass-part-2/

esp32_nrf52_swd's People

Contributors

atc1441 avatar danielkucera avatar realcarvedart avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

esp32_nrf52_swd's Issues

Swd-clk seems to be powering the chip

When trying to run the glitcher on an nrf52832 the target doesn’t fully power cycle. The voltage on dec1 actually increases a bit when IO22 is low. This effect disappears when disconnecting the swd clock line which I think is somehow backpowering the chip.

Problems compiling the ESP32_SWD_wifi

Hi,
I followed all the instructions in the guide. For the WifimManager, I manually downloaded "https://github.com/tzapu/WiFiManager/tree/feature_asyncwebserver"
I got the following error when compiling:

"C:\Program Files (x86)\Arduino\libraries\WiFiManager\WiFiManager.cpp: In member function 'void WiFiManager::WiFiEvent(arduino_event_id_t, system_event_info_t)':
C:\Program Files (x86)\Arduino\libraries\WiFiManager\WiFiManager.cpp:3452:17: warning: comparison between 'enum arduino_event_id_t' and 'enum system_event_id_t' [-Wenum-compare]
     if(event == SYSTEM_EVENT_STA_DISCONNECTED){
                 ^
C:\Program Files (x86)\Arduino\libraries\WiFiManager\WiFiManager.cpp:3469:20: warning: comparison between 'enum arduino_event_id_t' and 'enum system_event_id_t' [-Wenum-compare]
   else if(event == SYSTEM_EVENT_SCAN_DONE){
                    ^
C:\Program Files (x86)\Arduino\libraries\WiFiManager\WiFiManager.cpp: In member function 'void WiFiManager::WiFi_autoReconnect()':
C:\Program Files (x86)\Arduino\libraries\WiFiManager\WiFiManager.cpp:3486:79: error: no matching function for call to 'WiFiClass::onEvent(std::_Bind_helper<false, void (WiFiManager::*)(arduino_event_id_t, system_event_info_t), WiFiManager*, const std::_Placeholder<1>&, const std::_Placeholder<2>&>::type)'
       wm_event_id = WiFi.onEvent(std::bind(&WiFiManager::WiFiEvent,this,_1,_2)); // @todo move, needed for async esp32 scannetworks
                                                                               ^
In file included from C:\Users\md\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.0-alpha1\libraries\WiFi\src/WiFiSTA.h:28:0,
                 from C:\Users\md\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.0-alpha1\libraries\WiFi\src/WiFi.h:32,
                 from C:\Program Files (x86)\Arduino\libraries\WiFiManager\WiFiManager.h:70,
                 from C:\Program Files (x86)\Arduino\libraries\WiFiManager\WiFiManager.cpp:13:
C:\Users\md\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.0-alpha1\libraries\WiFi\src/WiFiGeneric.h:145:21: note: candidate: wifi_event_id_t WiFiGenericClass::onEvent(WiFiEventCb, arduino_event_id_t)
     wifi_event_id_t onEvent(WiFiEventCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX);
                     ^
C:\Users\md\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.0-alpha1\libraries\WiFi\src/WiFiGeneric.h:145:21: note:   no known conversion for argument 1 from 'std::_Bind_helper<false, void (WiFiManager::*)(arduino_event_id_t, system_event_info_t), WiFiManager*, const std::_Placeholder<1>&, const std::_Placeholder<2>&>::type {aka std::_Bind<std::_Mem_fn<void (WiFiManager::*)(arduino_event_id_t, system_event_info_t)>(WiFiManager*, std::_Placeholder<1>, std::_Placeholder<2>)>}' to 'WiFiEventCb {aka void (*)(arduino_event_id_t)}'
C:\Users\md\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.0-alpha1\libraries\WiFi\src/WiFiGeneric.h:146:21: note: candidate: wifi_event_id_t WiFiGenericClass::onEvent(WiFiEventFuncCb, arduino_event_id_t)
     wifi_event_id_t onEvent(WiFiEventFuncCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX);
                     ^
C:\Users\md\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.0-alpha1\libraries\WiFi\src/WiFiGeneric.h:146:21: note:   no known conversion for argument 1 from 'std::_Bind_helper<false, void (WiFiManager::*)(arduino_event_id_t, system_event_info_t), WiFiManager*, const std::_Placeholder<1>&, const std::_Placeholder<2>&>::type {aka std::_Bind<std::_Mem_fn<void (WiFiManager::*)(arduino_event_id_t, system_event_info_t)>(WiFiManager*, std::_Placeholder<1>, std::_Placeholder<2>)>}' to 'WiFiEventFuncCb {aka std::function<void(arduino_event_id_t, arduino_event_info_t)>}'
C:\Users\md\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.0-alpha1\libraries\WiFi\src/WiFiGeneric.h:147:21: note: candidate: wifi_event_id_t WiFiGenericClass::onEvent(WiFiEventSysCb, arduino_event_id_t)
     wifi_event_id_t onEvent(WiFiEventSysCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX);
                     ^
C:\Users\md\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.0-alpha1\libraries\WiFi\src/WiFiGeneric.h:147:21: note:   no known conversion for argument 1 from 'std::_Bind_helper<false, void (WiFiManager::*)(arduino_event_id_t, system_event_info_t), WiFiManager*, const std::_Placeholder<1>&, const std::_Placeholder<2>&>::type {aka std::_Bind<std::_Mem_fn<void (WiFiManager::*)(arduino_event_id_t, system_event_info_t)>(WiFiManager*, std::_Placeholder<1>, std::_Placeholder<2>)>}' to 'WiFiEventSysCb {aka void (*)(arduino_event_t*)}'
At global scope:
cc1plus.exe: warning: unrecognized command line option '-Wno-frame-address'
exit status 1
Errore durante la compilazione per la scheda ESP32 Dev Module."

Problems with dumped firmware

The same nrf52840 chip, after the successful attack, dump out 0-100000 and Dump full flash, get two bin file, by comparison, the difference is very big, and both files are not 1024Kb.
0bf421a3a64107972aae0280abf5d06b
489dcd76160b0f5cc99f048cce1a46ab
image

in addition there is another problem, I test in the G913 keyboard, after the attack is completed and dumped the firmware, the original keyboard use problems, manifested in the use of the Ghub to switch to off-board mode and the keys fail.

Support for STM32 series

Please could you adapt this to STM32 series that use SWD?
I tried using this on an STM32 but it seems the protocol layout for the memory is different even though they use SWD and ARM-based

swd.local/edit upload bar not showing

Hello,

  • Issue is going to swd.local/edit for the index.htm upload

I installed everything like normal,
Connected to a WiFi AP
I have access to swd.local/ with the webpage saying go to /edit

  • once in /edit there is no way to upload everything is blank.

  • I tried completely reinstalling Arduino and removing everything, and I used a different devices.

  • different browsers and different devices.

Same issue keeps happening, thank you.

Problems compiling the ESP32_SWD_wifi

`In file included from C:\Users\L\Downloads\ESP32_nRF52_SWD-main\ESP32_nRF52_SWD-main\ESP32_SWD_WIFI\ESP32_SWD_WIFI.ino:1:

C:\Users\L\Documents\Arduino\libraries\WiFiManager-feature_asyncwebserver/strings_en.h:371:7: error: 'wifi_country_t' does not name a type; did you mean 'fsfilcnt_t'?

const wifi_country_t WM_COUNTRY_US{"US",1,11,CONFIG_ESP32_PHY_MAX_TX_POWER,WIFI_COUNTRY_POLICY_AUTO};

   ^~~~~~~~~~~~~~

   fsfilcnt_t

C:\Users\L\Documents\Arduino\libraries\WiFiManager-feature_asyncwebserver/strings_en.h:372:7: error: 'wifi_country_t' does not name a type; did you mean 'fsfilcnt_t'?

const wifi_country_t WM_COUNTRY_CN{"CN",1,13,CONFIG_ESP32_PHY_MAX_TX_POWER,WIFI_COUNTRY_POLICY_AUTO};

   ^~~~~~~~~~~~~~

   fsfilcnt_t

C:\Users\L\Documents\Arduino\libraries\WiFiManager-feature_asyncwebserver/strings_en.h:373:7: error: 'wifi_country_t' does not name a type; did you mean 'fsfilcnt_t'?

const wifi_country_t WM_COUNTRY_JP{"JP",1,14,CONFIG_ESP32_PHY_MAX_TX_POWER,WIFI_COUNTRY_POLICY_AUTO};

   ^~~~~~~~~~~~~~

   fsfilcnt_t

In file included from C:\Users\L\Documents\Arduino\libraries\WiFiManager-feature_asyncwebserver/WiFiManager.h:109,

             from C:\Users\L\Downloads\ESP32_nRF52_SWD-main\ESP32_nRF52_SWD-main\ESP32_SWD_WIFI\web.cpp:15:

C:\Users\L\Documents\Arduino\libraries\WiFiManager-feature_asyncwebserver/strings_en.h:371:46: error: 'CONFIG_ESP32_PHY_MAX_TX_POWER' was not declared in this scope

const wifi_country_t WM_COUNTRY_US{"US",1,11,CONFIG_ESP32_PHY_MAX_TX_POWER,WIFI_COUNTRY_POLICY_AUTO};

                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

C:\Users\L\Documents\Arduino\libraries\WiFiManager-feature_asyncwebserver/strings_en.h:371:46: note: suggested alternative: 'CONFIG_ESP_PHY_MAX_TX_POWER'

const wifi_country_t WM_COUNTRY_US{"US",1,11,CONFIG_ESP32_PHY_MAX_TX_POWER,WIFI_COUNTRY_POLICY_AUTO};

                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                                          CONFIG_ESP_PHY_MAX_TX_POWER

C:\Users\L\Documents\Arduino\libraries\WiFiManager-feature_asyncwebserver/strings_en.h:372:46: error: 'CONFIG_ESP32_PHY_MAX_TX_POWER' was not declared in this scope

const wifi_country_t WM_COUNTRY_CN{"CN",1,13,CONFIG_ESP32_PHY_MAX_TX_POWER,WIFI_COUNTRY_POLICY_AUTO};

                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

C:\Users\L\Documents\Arduino\libraries\WiFiManager-feature_asyncwebserver/strings_en.h:372:46: note: suggested alternative: 'CONFIG_ESP_PHY_MAX_TX_POWER'

const wifi_country_t WM_COUNTRY_CN{"CN",1,13,CONFIG_ESP32_PHY_MAX_TX_POWER,WIFI_COUNTRY_POLICY_AUTO};

                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                                          CONFIG_ESP_PHY_MAX_TX_POWER

C:\Users\L\Documents\Arduino\libraries\WiFiManager-feature_asyncwebserver/strings_en.h:373:46: error: 'CONFIG_ESP32_PHY_MAX_TX_POWER' was not declared in this scope

const wifi_country_t WM_COUNTRY_JP{"JP",1,14,CONFIG_ESP32_PHY_MAX_TX_POWER,WIFI_COUNTRY_POLICY_AUTO};

                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

C:\Users\L\Documents\Arduino\libraries\WiFiManager-feature_asyncwebserver/strings_en.h:373:46: note: suggested alternative: 'CONFIG_ESP_PHY_MAX_TX_POWER'

const wifi_country_t WM_COUNTRY_JP{"JP",1,14,CONFIG_ESP32_PHY_MAX_TX_POWER,WIFI_COUNTRY_POLICY_AUTO};

                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                                          CONFIG_ESP_PHY_MAX_TX_POWER

Multiple libraries were found for "WiFi.h"`

Unable to open the swd.local URL

What should I do after linking ESP32 to WIFI?
Should I connect my computer and ESP32 to the same network?

When I finished all this and wanted to link to SWD.local, EDGE reminded me like this——

Hmmm… can't reach this pageswd.local refused to connect.
Try:

Search the web for swd local
Checking the connection
Checking the proxy and the firewall
ERR_CONNECTION_REFUSED

Glitching through DEC4 instead of DEC1?

Firstly, thanks a ton for putting this together! I'm new to voltage glitching and I'm trying to glitch an nRF52832-CIAA but it's in a weird package (MBN52832) made by a company called Murata which doesn't expose the DEC1 pin. Do you think glitching would be possible through the main power supply (DEC4) instead of through DEC1 (CPU power) if I removed the capacitors?

Erase exit with error

uint8_t erase_flash (void), exit with a timeout, but the program still manages to program the chip.

The erase_flash return status is ignored, thus the error is 'not visible.

I changed the function to log the error on the serial terminal. I always get timeout 2, even if the timeout is 1 sec.

/** ---------------------------------------------------------------------------

  • @brief erase_flash

  • @return uint8_t
    */
    uint8_t erase_flash (void)
    {
    gprintf ("\r\n Erasing flash.. please wait ..");
    write_register(0x4001e504, 2);

    long timeout = millis();
    while (read_register(0x4001e400) != 1)
    {
    if (millis()-timeout > TIMEOUT)
    {
    gprintf ( "\r\n Erasing timeout 1 ..");
    return 1;
    }
    }

    write_register(0x4001e50c, 1);

    timeout = millis();
    while (read_register(0x4001e400) != 1)
    {
    if(millis()-timeout > TIMEOUT)
    {
    gprintf ( "\r\n Erasing timeout 2...");
    return 1;
    }
    }
    write_register(0x4001e504, 0);
    timeout = millis();
    while (read_register(0x4001e400) != 1)
    {
    if(millis()-timeout > TIMEOUT)
    {
    gprintf ("\r\n Erasing timeout 3...");
    return 1;
    }
    }
    gprintf ( "\r\n Erasing done ..");
    return 0;
    }

Problems Glitching nrf52832

Thank you for this nice little SWD programmer GUI and the glitcher for the ESP32 :D

I am currently trying to glitch an nrf52832. I narrowed down the glitching width in the source code to 6-10us, as this should do the trick according to other researchers. I try to find the right timing for glitching the nrf52832 for quite some time now. Somehow I am either glitching at the wrong timing or just doing something wrong. Has anyone a hint for me? I recorded some scope recordings:

overview
zoom1
zoom2

Where should the glitch be applied? I thought somewhere around +7ms in Zoom1/Zoom2 - is this correct?

My thermometer does not connect via BLE

My thermometer does not connect via BLE. The connection keeps breaking. I have tried different browsers and devices. Even with new batteries it does not work.
Are there any instructions on how to flash the device via cable/UART?

Flashing bootloader

I am trying to flash the bootloader on a brand new chip (Ebyte E73 nRF52840).
Checkin if the chip is locked gives me the following result: Ok: the nRF is unlocked

I downloaded a few bootloaders including the SEEED USB bootloader: Seeed_XIAO_nRF52840_Sense_bootloader-0.6.1_s140_7.3.0.hex

I can flash the chip and i see after a few moments the chip is written

....
14:55:22.959 -> 0000 Read Register: 0x4001e400 : 0x00000000
14:55:22.992 -> 0000 Read Register: 0x4001e400 : 0x00000000
14:55:22.992 -> 0000 Read Register: 0x4001e400 : 0x00000000
14:55:23.025 -> 0000 Read Register: 0x4001e400 : 0x00000000
14:55:23.025 -> 000 Write Register: 0x4001e504 : 0x00000001
14:55:23.025 -> 0000 Read Register: 0x4001e400 : 0x00000000
14:55:23.057 -> 0000 Read Register: 0x4001e400 : 0x00000000
14:55:23.057 -> 0000 Read Register: 0x4001e400 : 0x00000000
14:55:23.090 -> 0000 Read Register: 0x4001e400 : 0x00000000
14:55:23.090 -> 0000 Read Register: 0x4001e400 : 0x00000000
14:55:23.121 -> 0000 Read Register: 0x4001e400 : 0x00000000
14:55:23.121 -> 0000 Read Register: 0x4001e400 : 0x00000000
14:55:23.121 -> Done flashing file, it took 19015ms speed: 28.0205kbs

Is this the correct result?
I used the SEEED bootloader because i try to flash my own software via Arduino to the nRF52840. I connected a usb cable to the VBUS (5v), GND, D+ and D- (including resistors and capacitor)

Left part on this image:
https://camo.githubusercontent.com/f6eeaf4a553951599a5c2197d05be781ac764dd330e77d7f09aeae72b76785dd/68747470733a2f2f6465767a6f6e652e6e6f7264696373656d692e636f6d2f726573697a65642d696d6167652f5f5f73697a652f373032783437362f5f5f6b65792f737570706f72742d6174746163686d656e74732f62656566356431623737363434633434386461626666333136363866336134372d32643162336461623134623834646561613262313637646539626261363762612f5343482e504e47

When i connect the USB cable, nothing shows up, not in Arduino IDE and not in finder (i'm using mac).
Is my way of doing this correct or did i do something wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.