Git Product home page Git Product logo

Comments (25)

giocamurati avatar giocamurati commented on June 14, 2024 1

Hello,

As I am on holiday, I do not have all the hardware with me.
I will try with the PCA as soon as I am back.

Best,
Giovanni

from screaming_channels.

giocamurati avatar giocamurati commented on June 14, 2024

Hi,

This error comes from the fact that somehow the there are not enough data.
Unfortunately, the current synchronizes gnuradio and encryptions in a slightly cumbersome way, so
it must be tuned properly.

The fact that there are too few data could be due to one or more of these three reasons:

  1. drop_start too big, so that in the end no data is left.
  2. num_traces_per_point is too small, so that the collection is too short.
  3. gnuradio.start() is too slow, so that the collection is too short.

As a general rule, you can try with drop_start=0 and then see how much drop_start should be.

However, I think with the B210 the problem is point 3. In this case you can try this:
at line 359 of reproduce.py, increase time.sleep(0.03), e.g. to time.sleep(0.11)
at line 370, add time.sleep(0.09)
This will make sure the collection is a large enough window around beginning and end of encryptions.

Best,
Giovanni

from screaming_channels.

AteetKumar avatar AteetKumar commented on June 14, 2024

Hi Giovanni,

I made the suggested changes in the config files but I am still getting some errors which are as following.

UHD Error:
    recv packet demuxer unexpected sid 0x7fff9c
/home/rex/.local/lib/python2.7/site-packages/numpy/lib/function_base.py:392: RuntimeWarning: Mean of empty slice.
  avg = a.mean(axis)
/home/rex/.local/lib/python2.7/site-packages/numpy/core/_methods.py:85: RuntimeWarning: invalid value encountered in double_scalars
  ret = ret.dtype.type(ret / rcount)
/usr/local/lib/python2.7/dist-packages/ScreamingChannels-1.0-py2.7.egg/screamingchannels/analyze.py:67: RuntimeWarning: invalid value encountered in greater
  trigger_fn = lambda x, y: x > y

Traceback (most recent call last):
  File "/home/rex/.local/bin/sc-experiment", line 11, in <module>
    load_entry_point('ScreamingChannels', 'console_scripts', 'sc-experiment')()
  File "/home/rex/.local/lib/python2.7/site-packages/click/core.py", line 722, in __call__
    return self.main(*args, **kwargs)
  File "/home/rex/.local/lib/python2.7/site-packages/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/home/rex/.local/lib/python2.7/site-packages/click/core.py", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/rex/.local/lib/python2.7/site-packages/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/rex/.local/lib/python2.7/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/ScreamingChannels-1.0-py2.7.egg/screamingchannels/reproduce.py", line 374, in collect
    trace = analyze.extract(OUTFILE, collection_config, average_out, plot)
  File "/usr/local/lib/python2.7/dist-packages/ScreamingChannels-1.0-py2.7.egg/screamingchannels/analyze.py", line 121, in extract
    trace_starts, trigger, trigger_avg = find_starts(config, data)
  File "/usr/local/lib/python2.7/dist-packages/ScreamingChannels-1.0-py2.7.egg/screamingchannels/analyze.py", line 78, in find_starts
    if trigger_signal[0]:
IndexError: index 0 is out of bounds for axis 0 with size 0

FYI I am using ubuntu 18.04 on 32 GB RAM, NUC. Am I facing problems because of different version of Ubuntu as you have used ubuntu 16?

Please help me with this.

Thanks and regards,
Ateet

from screaming_channels.

giocamurati avatar giocamurati commented on June 14, 2024

Hi Ateet,

Sorry for the late reply!

The version of Ubuntu should not be a problem.

The error you have now occurs just after trace extraction, during the computation of the average.
Simply, with the current configuration the extraction code could not retrieve any useful trace, hence the error mean of empty slice.

But I guess the real origin of the problem is:
UHD Error:
recv packet demuxer unexpected sid 0x7fff9c
We never occurred into this with the other radios.
It needs some investigation, at a quick search it seems many people have encountered it.

In the meanwhile, a quick and dirty fix could be to skip the trace and go on.

I edited the message, the following is still valid in general:

  1. Maybe at 5cm the default amplification is too high and the ADC saturates.
    You can increase the distance or change radio_block.set_gain(40) at line 481.
    You can check this by choosing a small number of traces per point and trace number = 1, and plotting the result. You can also check that with your settings you actually see encryptions in the capture.

  2. The default output power of the nRF is 0dBm. If you want you can increase it to 4dBm, extraction should be easier. (Firmware menu 'p' and then '0').

If you want, you can send me your exact setup/settings and I will try in the very same conditions.
You can also send me the /tmp/time dumped by the program with the raw data from Gnuradio. I can have a look at it to see if I can spot any problem.

Best,
Giovanni

from screaming_channels.

giocamurati avatar giocamurati commented on June 14, 2024

Hi Ateet,

I have just tried with my B210 and I could collect some traces as follows.

  1. Unmodified code from this git.
  2. B210 with standard WiFi antenna connected to TX/RX
  3. BLE Nano v2 at 20cm distance
  4. Output power 4dBm (option 'p' '0' in the firmware)
  5. Config below

However, I also encounter the UHD errors.
I need to look into it to see if it is a general problem with UHD for the B210 or related to the way we use it.

This dirty fix at line 157 of analyze should let you continue to experiment if you do not have another radio.

   if traces == []:
       return np.zeros(len(template))

Best,
Giovanni

{
        "firmware": {
        "mode": "tinyaes",
        "fixed_key": true,
        "modulate": true
    },
    "collection": {
        "target_freq": 2.528e9,
        "sampling_rate": 5e6,
        "num_points": 1000,
        "num_traces_per_point": 500,
        "bandpass_lower": 1.85e6,
        "bandpass_upper": 1.95e6,
        "lowpass_freq": 5e3,
        "drop_start": 50e-3,
        "trigger_rising": true,
        "trigger_offset": 100e-6,
        "signal_length": 300e-6,
        "template_name": "templates/tiny_anechoic_10m_080618.npy",
        "min_correlation": 0.00
    }
}

from screaming_channels.

AteetKumar avatar AteetKumar commented on June 14, 2024

Hi,

Thanks for the solutions.

Still getting the same error. It looks like it is more of a hit and trial on the parameters in config file and the USRP settings.

Can we not just turn off the analysis during the collection of the traces and just collect the traces first and analyze them offline on tools like Riscure Inspector?
Is it possible? If yes how?

FYI I am not using any anechoic chamber right now. First I have to show this concept works only then I can ask for the anechoic chamber because it is costly.

Thanks and regards,
Ateet

from screaming_channels.

AteetKumar avatar AteetKumar commented on June 14, 2024

My goal is to collect the desired number of traces and then extract the complete AES key.
I am running the nRF is continuous wave mode and then trying to collect the traces.

Can you tell me how many number of traces are required to attack tiny_AES and get the complete key?

Are you able to collect more than 1000 traces everytime? I am not . Sometimes I collect only 200 traces sometimes 500, but it stops much before I can reach even 1000.

Can you tell me what version of UHD, frimwware and GNU radio are you using? Because I think this
UHD Error:
recv packet demuxer unexpected sid 0x7fff9c

is related to the UHD version and the firmware of USRP device.

Thanks and regards,
Ateet Kumar

from screaming_channels.

giocamurati avatar giocamurati commented on June 14, 2024

Hi Ateet,

Indeed the problem is the UHD error. I noticed only later.
The extraction fails because the data are empty.
I need to look into it to see if it is a general problem with UHD for the B210 or related to the way we use it.

I think a quick and dirty solution is to skip collection when there is a driver error but continue:
This dirty fix at line 157 of analyze should let you continue to experiment if you do not have another radio.

   if traces == []:
       return np.zeros(len(template))

With this I could collect 1000 traces and only 15 were zeroed.

Regarding the "template" parameter:
The extracted traces have to be aligned to a reference that is always the same. This is the role of the template. If you do not specify it the code takes the first trace as reference.
It is important to specify it to have a common reference for alignment.

Giovanni

from screaming_channels.

AteetKumar avatar AteetKumar commented on June 14, 2024

Hi Giovanni,

Here is the configuration I am using. I created the template using same configuration.

{
    "firmware": {
        "mode": "tinyaes",
        "fixed_key": true,
        "modulate": false
    },
    "collection": {
        "target_freq": 2.528e9,
        "sampling_rate": 5e6,
        "num_points": 1000,
        "num_traces_per_point": 500,
        "bandpass_lower": 1.85e6,
        "bandpass_upper": 1.95e6,
        "lowpass_freq": 5e3,
        "drop_start": 40e-3,
        "trigger_rising": true,
        "trigger_offset": 100e-6,
        "signal_length": 300e-6,
        "template_name": "templates/temp_17cm_cw.npy",
        "min_correlation": 0.00
    }
}

I am using a regular wifi antenna and not using any amplifier or filters. The distance is 17cm. My nRF board is the Segger Jlink board using Nordic Semiconductor's nRF52832 PCA10040 chip.

I could only collect 450 traces and then the program stopped, showing the UHD error like earlier. Even after using the following

if traces == [ ]:
       return np.zeros(len(template))

I am getting the same error or the program just hangs after some number of traces.

Here is a screenshot of graphs.
screenshot

Also I couldn't see anything in /tmp/time file it is empty.
Please see what can be done in this case.

Thanks and Regards,
Ateet

from screaming_channels.

giocamurati avatar giocamurati commented on June 14, 2024

Hi Ateet,

All in your setup looks good and from the screenshot I can see the configuration works as well.

Can you post the error you get from our program after the UHD error?

We can try to figure out which extra action is required.

Giovanni

from screaming_channels.

AteetKumar avatar AteetKumar commented on June 14, 2024

Hi,
Thanks for replying.

I basically get two different errors now:

  1. The trace collection hangs and the program doesn't exits and doesn't show any error. The output on terminal is like this:
  [------------------------------------]    2%  0d 02:35:45Extracted 
Number =  500
avg[Max(std)] = 4.81E-03
Max(u) = Max(std) = 1.68E-03
Max(u_rel) = 3.49E+01 %
  [------------------------------------]    2%  0d 02:35:44Extracted 
Number =  500
avg[Max(std)] = 4.88E-03
Max(u) = Max(std) = 1.71E-03
Max(u_rel) = 3.52E+01 %
  [------------------------------------]    2%  0d 02:35:43Extracted 
Number =  500
avg[Max(std)] = 3.94E-03
Max(u) = Max(std) = 1.68E-03
Max(u_rel) = 4.27E+01 %
  [------------------------------------]    2%  0d 02:35:42Extracted 
Number =  500
avg[Max(std)] = 4.00E-03
Max(u) = Max(std) = 1.71E-03
Max(u_rel) = 4.28E+01 %
  [------------------------------------]    2%  0d 02:35:41^Z
[4]+  Stopped                 sc-experiment --radio=USRP_mini --device=/dev/ttyACM0 collect config/myconfig.json mytraces/tracese2
  1. Another error is the UHD Error as follows:
UHD Error:
    recv packet demuxer unexpected sid 0xff64ffdf

UHD Error:
    recv packet demuxer unexpected sid 0xa30029

UHD Error:
    recv packet demuxer unexpected sid 0xff84ffaf

UHD Error:
    recv packet demuxer unexpected sid 0x87004c

UHD Error:
    recv packet demuxer unexpected sid 0xff90ff86

UHD Error:
    recv packet demuxer unexpected sid 0x850083

UHD Error:
    recv packet demuxer unexpected sid 0xffa7ff77

UHD Error:
    recv packet demuxer unexpected sid 0x5e0095

UHD Error:
    recv packet demuxer unexpected sid 0xffadff71

UHD Error:
    recv packet demuxer unexpected sid 0x640096

UHD Error:
    recv packet demuxer unexpected sid 0xffa3ff81

UHD Error:
    recv packet demuxer unexpected sid 0x300086

UHD Error:
    recv packet demuxer unexpected sid 0xffdcff79

Traceback (most recent call last):
  File "/home/rex/.local/bin/sc-experiment", line 11, in <module>
    load_entry_point('ScreamingChannels', 'console_scripts', 'sc-experiment')()
  File "/home/rex/.local/lib/python2.7/site-packages/click/core.py", line 722, in __call__
    return self.main(*args, **kwargs)
  File "/home/rex/.local/lib/python2.7/site-packages/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/home/rex/.local/lib/python2.7/site-packages/click/core.py", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/rex/.local/lib/python2.7/site-packages/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/rex/.local/lib/python2.7/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/home/rex/screaming_channels/experiments/src/screamingchannels/reproduce.py", line 372, in collect
    trace = analyze.extract(OUTFILE, collection_config, average_out, plot)
  File "/home/rex/screaming_channels/experiments/src/screamingchannels/analyze.py", line 104, in extract
    assert len(data) != 0, "ERROR, empty data after drop_start"
AssertionError: ERROR, empty data after drop_start

Right now i am only getting these two errors for past few days. I think my template is good enough.

Thanks and regards,
Ateet Kumar

from screaming_channels.

giocamurati avatar giocamurati commented on June 14, 2024

Hi,

Yes, from the screenshot I can see extraction works well and it is well configured.

Now it seems that the errors come from the data returned by the driver. I never encountered the first error, but the second is due to the fact that gnuradio returned an empty buffer.

One solution to reduce the chances that the buffer is empty:
at line 359 of reproduce.py, increase time.sleep(0.03), e.g. to time.sleep(0.11)
at line 370, add time.sleep(0.09)

Then, to avoid crashing in case of error a quick fix would be:
Substitute the assertion with:
if len(data) == 0:
return np.zeros(len(template))

Of course, you will have to move the code that reads the template above this line.

Sorry for these problems, we never had them with the N210 and the HackRF. But it’s good we try to fully support the B210 too.

Best,
Giovanni

from screaming_channels.

AteetKumar avatar AteetKumar commented on June 14, 2024

Hi,

Yes I am using time.sleep( ) at the places you told.

The first error is probably because of the reasons discussed here:
[https://www.ruby-forum.com/t/usrp-b210-uhd-error-recv-packet-demuxer-unexpected-sid-0xff87ffc3/234661]

And for the second error I will try what you recommended. I am actually thinking to use Hackrf for the whole project now.

Let's see what can be done for B210.

Thanks a lot,
Regards
Ateet

from screaming_channels.

AteetKumar avatar AteetKumar commented on June 14, 2024

Hi,

So now I am working with the hackRF now. Everything is working well but the program stops and I am getting trails of OOOOOOOOOOOOOOO.... on the terminal.

What is this associated with and what could be the solution?

Thanks and regards
Ateet

from screaming_channels.

giocamurati avatar giocamurati commented on June 14, 2024

Hi,

Good, now the only difference between our setups (we use a ble nano) is the target chip but it should not be a big problem.

So, the OOOO appear when there is overflow. This usually happens when the program stops collection and runs the analysis code. This lasts one two seconds at most with num_traces_per_point 500.
So it should not be a problem at all.

Let’s focus on why the code stops then. It never happened to me. Do you have some clue of what could be the reason? Any extra info to try to understand.
I would guess two options. The analysis code takes too much to process the data because the buffer ends up being to big. It would probably happen, for example, with num_traces_per_point = 100000 or with plot on. Or for some reason we have to figure out for this case.
The second option is some error in the communication with the target, but it seems unlikely to me.

Giovanni

from screaming_channels.

giocamurati avatar giocamurati commented on June 14, 2024

Hi Ateet,

I have taken your configuration file and used it to generate the temp_17cm_cw.npy template.
Then, I have run the collection with the unmodified code from the repository, and your configuration.
python2.7 src/screamingchannels/reproduce.py --radio HackRF collect config.json /tmp/test
I could successfully collect 1000x500 traces, never experiencing any OOOO overflow.

If I increase num_traces_per_point, e.g., to 1000 or even 10000, the buffer becomes bigger, and the analysis code becomes slower, therefore I start observing OOOOO overflows during analysis, but collection still goes on without any problem, it is just slower.
Even if I leave the delays we added for the B210, I do not experience any block.
The limit depends on the laptop, but I find it unlikely for 500 to be already too big for yours.
Still, maybe it's worth trying to reduce to 100 and see if it helps.

The only case when I really see the code stuck is when I leave the --plot option on with num_points bigger than 1.

Does the HackRF works well with other tools on your system?

Best,
Giovanni

from screaming_channels.

AteetKumar avatar AteetKumar commented on June 14, 2024

Hi,

So in my case the OOO trail starts appearing from the very beginning of collection, but the recording keeps going. But at certain point the trace collection stops and I get trail of OOO... only.

I checked that my nRF52832 chip doesn't respond to the inputs in minicom quite often. Then I have to either reset it or remove and reconnect it to the computer. I think this happens during the collection also that is why the program gets stuck and doesn't waits infinitely.

So I think the communication with the chip can be a major problem here.

For the first time I was able to collect 10000 traces today, with commenting line 213 to 222 in the reproduce.py. But then I tried again and the program didn't work.

I would request you to try replicate the experiment on Nordic's nRF chip also. Meanwhile, I think some changes are needed in the code to modify the way nordic's chip communicates with the SDR and program.
Also, I am not using the Ykush USB switcher, and my board is directly connected to the computer.

And I am now using a NUC with 32GB RAM, so it shouldn't be a problem of RAM now.

Thanks and regards,
Ateet

from screaming_channels.

giocamurati avatar giocamurati commented on June 14, 2024

Hi Ateet,

I could finally get a pca10040 and run some tests.

I confirm the same issue you experienced with the UART.
By investigating a little further, I found that sometimes some lines are not transmitted/received correctly by the UART, causing the protocol to hang. I still have to see why this happens on the pca10040 and not on the ble nano.

In the meanwhile, you can use this quick and dirty fix to collect as many traces as you want.
I have tried it and I could collect 9000 traces without any issue.
Basically, I put a timeout on the serial read used by the device to acknowledge reception to the laptop. If the timeout ends without having received any data, I just ignore and continue.
The checks in the following transmissions show that there is no error propagation.

Best,
Giovanni

PS run the command with option -l=DEBUG to have more info about the communication between laptop and target.

diff --git a/experiments/src/screamingchannels/reproduce.py b/experiments/src/screamingchannels/reproduce.py
index b7aceb5..c7ca1b5 100644
--- a/experiments/src/screamingchannels/reproduce.py
+++ b/experiments/src/screamingchannels/reproduce.py
@@ -202,7 +202,7 @@ def _send_parameter(ser, command, param):
     The function assumes that we've already entered tiny_aes mode.
     """
     command_line = '%s%s\r\n' % (command, _encode_for_device(param))
-    l.debug('Sending command:  %s' % command_line)
+    l.debug('Sending command:  %s\n' % command_line)
     if not COMMUNICATE_SLOW:
         ser.write(command_line)
     else:
@@ -210,7 +210,13 @@ def _send_parameter(ser, command, param):
             ser.write(p+' ')
             time.sleep(.05)
 
-    check = ''.join(chr(int(word)) for word in ser.readline().split(' '))
+    l.debug('Waiting check\n')
+    x = ser.readline()
+    print "received: ",x
+    if len(x) == 0:
+        print "nothing received on timeout, ignoring error"
+        return 
+    check = ''.join(chr(int(word)) for word in x.split(' '))
     # -- create check like this instead for ESP32:
     #response = ser.readline()
     #response = [ a for a in response.split(' ') if a.isdigit() ]
@@ -220,6 +226,7 @@ def _send_parameter(ser, command, param):
                                  _encode_for_device(check))
         ser.write(b'q')
         sys.exit(1)
+    l.debug('Check done\n')
 
 
 def _send_key(ser, key):
@@ -463,7 +470,7 @@ def create_waterfall(output_file):
 
 def _open_serial_port():
     l.debug("Opening serial port")
-    return serial.Serial(DEVICE, BAUD)
+    return serial.Serial(DEVICE, BAUD,timeout=5)
 
 
 class GNUradio(gr.top_block):

from screaming_channels.

AteetKumar avatar AteetKumar commented on June 14, 2024

Hi,

Thanks for the reply.

Sorry, for me even this is not working. I am having the same UHD Error with the USRP, and also with the HackRF, the program stops randomly after some 450+ traces giving one of the same errors I mentioned earlier.

Are you able to perfectly mount the attack on the traces you collected for Nordic nRF52832 chip?

I would suggest you to try mounting the attack on this chip because for me I couldn't get even a single Byte of the key from 180000 Traces I collected earlier.

Thanks and regards,
Ateet

from screaming_channels.

AteetKumar avatar AteetKumar commented on June 14, 2024

Hi Giovanni,

Did you find anything on Nordic's nRF board? I am still struggling to find out the solution of my problem.

Thanks and regards,
Ateet

from screaming_channels.

giocamurati avatar giocamurati commented on June 14, 2024

Hello Ateet,

I applied the patch I sent in my previous message, using a HackRF, a PCA10040 board, connected
via a cable.

With this, I was able to collect 130000 template traces and 5700 attack traces.
I quickly run a simple template attack and recovered 11 bytes of the key.
The leak is very clear and it would be easy add more traces / tune the attack to get the full key.
I can send you the traces if you want.

Could you please confirm me that the patch applied to the unmodiefied git does still not work for you?

I am sorry you experience these problems.
Do you have access to a BLE Nano v2? This is the nRF52832 board on which we have never had any problem.
(e.g., https://www.electrokit.com/en/product/redbearlab-ble-nano-kit-v2-nrf52832-2/)

Let me know
Giovanni

from screaming_channels.

AteetKumar avatar AteetKumar commented on June 14, 2024

Hi,

Sorry for late reply.

No, I am still having the same problem with the USRP and HACKRF even after the changes you recommended.

Also, I can get some results using "TRA ATTACK" over 10000 traces but only get 1 or 2 bytes of the key.

I am still looking into the possible solutions. Can you please send me the working reproduce.py with all the changes you made?

Thanks and regards,
Ateet Kumar

from screaming_channels.

giocamurati avatar giocamurati commented on June 14, 2024

Hi,

Sure, I have pushed the "quick and dirty" fixes that I have proposed on a separate branch, you can get the code from there:

git fetch
git checkout issue2_quick_and_dirty_fix

In summary:

  1. For the B210, I just ignore the error (which leads to empty data buffer), return a zero trace, and continue collection without crashing. You still see the error appearing, but collection can go on.
  2. For the PCA10040, I just put a timeout on the UART. If the device is stuck waiting for an ack for more that 5s, I skip that trace and go on.

I have reproduced your setup, that is a pca10040 board at 17cm, with your configuration file.
With the fixes, I can collect traces (e.g., many thousands) without any problem (e.g., crash, stuck) both with the USRP B210 and with the HackRF.

Please, try this code on your setup and let me know if it works on your side too.
For the B210, as long as collection does not crash, you can ignore the error message from UHD, it is handled in the code now. For the HackRF, the collection should not get stuck anymore now thanks to the 5s timeout.
Do not hesitate to ask me if you encounter further problems.

Regarding the attack, we implemented very simple attacks (to show the strength of the channel itself). Depending on your setup (antenna, amplifier, radio, environment, etc.) you may need a large number of traces.
If you want to play with other tools (Daredevil, Jlsca), you can convert the traces as explained in the README here

Best,
Giovanni

from screaming_channels.

giocamurati avatar giocamurati commented on June 14, 2024

I have updated code and instructions, including the results of a new paper.
The instructions are here https://eurecom-s3.github.io/screaming_channels/.
I have included some of the fixes we discussed, and the B210 and the PCA10040 are now supported.
So I will close this issue.

Best,
Giovanni

from screaming_channels.

AteetKumar avatar AteetKumar commented on June 14, 2024

from screaming_channels.

Related Issues (5)

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.