GithubHelp home page GithubHelp logo

phpecc's Introduction

Pure PHP Elliptic Curve DSA and DH

Build Status

Scrutinizer Code Quality Code Coverage

Latest Stable Version Total Downloads Latest Unstable Version License

Information

This library is a rewrite/update of Matyas Danter's ECC library. All credit goes to him.

For more information on Elliptic Curve Cryptography please read this fine article.

The library supports the following curves:

  • secp112r1
  • secp256k1
  • nistp192
  • nistp224
  • nistp256 / secp256r1
  • nistp384 / secp384r1
  • nistp521

During ECDSA, a random value k is required. It is acceptable to use a true RNG to generate this value, but should the same k value ever be repeatedly used for a key, an attacker can recover that signing key. The HMAC random generator can derive a deterministic k value from the message hash and private key, voiding this concern.

The library uses a non-branching Montgomery ladder for scalar multiplication, as it's constant time and avoids secret dependant branches.

License

This package is released under the MIT license.

Requirements

  • PHP 7.0+ or PHP 8.0+
  • composer
  • ext-gmp

Support for older PHP versions:

  • v0.4.x: php ^5.6|<7.2
  • v0.5.x: php ^7.0
  • v1.0.x: php ^7.0|^8.0

Installation

You can install this library via Composer :

composer require mdanter/ecc:^1.0

Contribute

When sending in pull requests, please make sure to run the make command.

The default target runs all PHPUnit and PHPCS tests. All tests must validate for your contribution to be accepted.

It's also always a good idea to check the results of the Scrutinizer analysis for your pull requests.

Usage

Examples:

phpecc's People

Contributors

afk11 avatar aztech-dev avatar btcdrak avatar carusogabriel avatar dktapps avatar fgrosse avatar johanderuijter avatar kornrunner avatar makuser avatar mdance avatar mdanter avatar paragonie-scott avatar paragonie-security avatar rgex avatar rubensayshi avatar scintill avatar scrutinizer-auto-fixer avatar spomky avatar staabm avatar xsilen-tt 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

phpecc's Issues

Installation by composer fails at the moment

Tried to install this through composer instead of a clone and it failed, seems to be because it can't find the phpasn1 commit

@aztech-dev

  Problem 1
    - Installation request for mdanter/ecc dev-master -> satisfiable by mdanter/ecc[dev-master].
    - mdanter/ecc dev-master requires fgrosse/phpasn1 dev-master#1033ca99966ee75b957d6f923dc21f4dad9b334b -> no matching package found.

Potential causes:
 - A typo in the package name
 - The package is not available in a stable-enough version according to your minimum-stability setting
   see <https://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion> for more details.

Read <http://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.

Refine Point::mul()

It appears a call in Point::mul() is leaking side channel information, such larger scalars take longer to compute.

What alerted me to this was running the spec based curve tests, which use test vectors from an RFC document. The private keys span a wide range of the keyspace, and as larger keys are tested, the private to public test takes longer.

require_once "vendor/autoload.php";

use Mdanter\Ecc\EccFactory;

$math = EccFactory::getAdapter();
$G = EccFactory::getSecgCurves()->generator256k1();
for($i = 1; $i <= 16; $i += 16) {
  $k = $G->getPrivateKeyFrom($math->pow(2, $i));
  $a = microtime(true);
  $Q = $k->getPublicKey();
  $b = microtime(true);
  echo $b - $a . "\n";
}

It seems the first issue is https://github.com/mdanter/phpecc/blob/master/src/Primitives/Point.php#L267
Here, $n should be padded to the number of bits in the curve field, since the number of iterations in the loop depends on the length of the resulting string.

Chasing further issues like this, it seems cswapValue's $size variable should again be the size of the curve. This doesn't have much effect overall, and I suppose thi is because it's converted to decimal immediately after. Moving on.

Now that my patch has a constant number of iterations (corresponding to the bits of the private key), I looked at the two cswap() calls, and add-and-double calls separately. Simply checking the time per iteration proves interesting.

The total time spent during mul() for cswap() doesn't vary much (depending on the key, or the iteration). But the time for double-and-add does leak something.

I've tried collecting per-iteration times for a variety of keys, but the leak across different keys shows it's not as simple as 'it takes longer when the bit is set'. For example, the private key 1 is far quicker than a large private key:
https://thomaskerin.io/uploads/phpecc-research/one.txt
https://thomaskerin.io/uploads/phpecc-research/order_minus_something.txt

However, checking a private key where the bits alternate between 0 and 1, there is no immediate way to recover it's key: https://thomaskerin.io/uploads/phpecc-research/alternating.txt It seems once an early bit is set, the algorithm takes tends to take longer.

Replace double-add method by double-add-always for point multiplication

I might be wrong, but from what I've gathered while doing #14 & #15 is that the lib uses standard double-and-add for point multiplication.

As exposed here, this algorithm has a known weakness against timing attacks, and should rather be replaced by either Montgomerry ladder algo or double-and-add always to perform the multiplication in constant time.

I'm keen to go with double-and-add-always as it does not require much changes in the algo, but it does have some performance considerations.

Should I go for it ?

MathAdapter::decHex(5) gives "5" instead of "05"

eg when using GMP::decHex(5) you will get "5" instead of "05" (both gmp and BCMath)

as a result everything needs to be wrapped in str_pad, is it an option to fix this in the phpecc library or is that a bad idea?

ASN.1

Hi,

You use FGrosse/PHPASN1 library as a dependency, but this dependency is not stable.
Is it possible to remove this dependency and use PHPSecLib instead ?

You can look at this library to know how they use it to read a file.

Split core lib from CLI

As a result of a small discussion on the issue #101, I would suggest to split the core lib from the CLI. The main reason for this split is that the library as itself doesn't need the CLI-part (symfony/console) and could cause a non-essential dependency conflict.

MessageFactory should not be required for EcDH

The MessageFactory introduced @afk11 in this commit is only needed for the encryption/decryption of information. If the EcDH class is only used for the key exchange and not the encryption part, a MessageFactory has always be passed in.
An other issue with this change is that the PublicKeyInterface for a PrivateKey exchange is option. But the recipientKey is always required.

In addtion, the current example in the README.md is outdated:

// ...
$bobDh = $bob->createExchange($alice);
$messageForBob = $bobDh->decrypt('... the encrypted message... too lazy to actually generate the encoded message');

IMHO the MessageFactory should be passed in as optional parameter into the EcDH constructor or passed as required parameter into encrypt/encryptFile and decrypt/decryptFile.

The test case EcDhMultiPartyTest.php is a good example of a MessageFactory initialisation, that is never been used.

MathAdaptor is missing functions (from older NumberTheory class)

I made great use of the square_root_mod_p function from the older NumberTheory class, which has been replaced with the MathsAdaptor interface and BcMath / GMP libraries with the respective functionality, although missing some functions.

I propose adding the following functions to the MathsAdaptor interface:
polynomialReduceMod($poly, $polymod, $p);
polynomialMultiplyMod($m1, $m2, $polymod, $p);
polynomialPowMod($base, $exponent, $polymod, $p);
squareRootModPrime($a, $p);
..and using the GMP/BcMath code from NumberTheory to bring back this functionality.

I'll start on this now and submit a PR soon once I get something working.

Create release pointing at original lib

I think we should label the original lib as v1.0, and consider the PR4/Composer migration as part of a v2.0, so that users depending on the original lib can still fetch it easliy without going through the git log...

readme wrong

the command $alice = $generator->createPrivateKey(); dont work

PHP version requirement bump ?

As you may be aware, PHP 5.3.3 is no longer officially supported.

I think it's decent to follow the supported versions, and drop support for EOL'ed versions.

Also, README.md currently states PHP 5.4 as min version, while composer enforces 5.3.3 . However, the library is currently not tested under 5.3.3 (ref .travis.yml)

I'm all for bumping composer.json to 5.4, but I think consensus should be reached before doing so:

  • Option A: keep supporting 5.3.3, add 5.3 to travis file, and work through the potential issues that may have been introduced in the recent commits but were not tested against 5.3
  • Option B: drop 5.3.3 and move on with the future (no, I'm not the very least biased), bump composer.json to 5.4 and done.

Semantic Versioning

Hi all,

It seems you are ready to tag a new major release.
The tag will be v0.3.0 as suggested in issue #59.
The next major release appears to be v0.4.0(#83)

For all my projects, I follow the semantic versioning descibed on this page, even for small projects.
The version numbers follow a logic that is easy to understand and to implement with (IMHO) great benefits.

I will be great if this project is tagged following this semantic versioning.

What do you think?

Avoid strlen() and substr()

https://secure.php.net/manual/en/mbstring.overload.php

In short, only use mbstring.func_overload if you are 100% certain that nothing on your site relies on manipulating binary data in PHP.

If you're doing crypto, then you're manipulating binary data. Fortunately, there's a workaround.

I added a utility in #114 so if you merge that, then you can begin using BinaryString::length() instead of strlen() and BinaryString::substring() instead of substr().

PHPASN1 is now >PHP5.6 only

And this wouldn't affect us besides the fact I need it's Integer class that can handle big-int's. I use the latest release in my recent series of PR's #92 #93 #94 #95 - in DerSignatureSerializer (not for anything exotic like cert's).

What do people think about abandoning older versions? They're nearing end of support, so perhaps this is fine. I forgot that tests would fail on my builds, since the required version isn't supported on PHP 5.4 / 5.5

Incorrect method signature

I think this method is incorrect: public function __construct(CurveFpInterface $curve, $x, $y, $order = null, MathAdapter $adapter)

It should be public function __construct(CurveFpInterface $curve, $x, $y, $order = null, MathAdapter $adapter = null).

The constructor should also set the argument $adapter if null.

Abstracted EcMath class?

I've been thinking about writing a wrapper for the Math classes which would support taking either a Point, or an integer, and allowing the library to apply the relevant arithmetic operations based on whatever it's acting on.

ECDSA supports deterministic algorithms to calculate public keys from a master public key. This master public key is the result of k * G, where k is your master private key for a sequence of addresses.

The master public key is used to deterministically derive a unique offset through hashing, meaning you can add this offset to a master private key, or convert to a point and add it to a master public key, yielding child public keys.

The thing is, regardless if you're operating on a point (you only had the master public key) or an integer (you had a private key) the operations are the exact same: Simply add two integers or points together

You'll see that the private case requires mod() after adding two integers together. This happens everywhere in the library.

Private:

  $privKey = Math::hexDec(openssl_random_pseudo_bytes(32))
  $offset = Math:hexDec(hash(..))

  $childKey = 
Math::mod(
  Math::add(
    $offset, 
    $privKey
  )
  $curveOrder
);

Or a public case:

  $generator = new GeneratorPoint(..);

  $offset = Math::hexDec(hash(..));
  $offset = $generator->mul($offset);

  $pubkey = new Point(..);
  $newKey = $pubkey->add($offset);

This is a minor example, though could help reduce the level of code in other places in the library (Wherever math::add(), and math::mul() occur are often followed by math::mod(), so this class would handle that internally if required)

The homomorphic properties of points on an elliptic curve, and integers mod q, mean these operations should be abstractable:

  $offsetSrc = ... A point, or integer.
  $keySrc = .. a point, or integer

  $offset = new EcMath($offsetSrc);
  $key = new EcMath($startKey)
  $child = $key->add($offset);

I can whip something up for this, just wondering what you all think. No sense duplicating code just because you happen to be dealing with a point (publickey) or integer (privatekey) at the time.

docblocks

I'd do a PR to fix it but before I spend time on it I'm curious if there might be a reason (personal prefence or smt) for it ...

why is the following (in src/Point.php):

    /*
     * (non-PHPdoc) @see \Mdanter\Ecc\PointInterface::mul()
     */
    public function mul($n)

not like;

    /**
     * @see \Mdanter\Ecc\PointInterface::mul()
     */
    public function mul($multiplier)
  • using /** so IDEs will use it for the function
  • removing the (non-PHPdoc) so the @see is actually parsed
  • matching the argument name to the PointInterface

if there's no particular reason why you guys prefer it this way then I'll go and clean it up, because I personally like it a lot better when my IDE (phpstorm in my case) properly parses the docblocks and as a result gives better code intel

database issue

hi
when i create a new table getting error.
#1089 - incorrect prefix key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique prefix keys

it is happening when i select field A_I. if don't use A_I field. it is working. I am looking for permanent solutions. Please help me.

i am using win8.1 and xampp server.

Thank you in advance for your time.

thanks
Alok

Missing test vectors for curves

Only NIST P-192 curve is currently tested.

  • Refactor testsuite to load test vectors from data files -- YAML based loader
  • Add missing test vectors
    • NIST P-192
      • Keypairs
      • Diffie Hellman Key exchange
      • DSA / RFC6979
    • NIST P-224
      • Keypairs
      • Diffie Hellman Key exchange
      • DSA / RFC6979
    • NIST P-256
      • Keypairs
      • Diffie Hellman Key exchange
      • DSA / RFC6979
    • NIST P-384
      • Keypairs
      • Diffie Hellman Key exchange
      • DSA / RFC6979
    • NIST P-521
      • Keypairs
      • Diffie Hellman Key exchange
      • DSA
    • SECP-256K1
      • Keypairs
      • Diffie Hellman Key exchange
      • DSA / RFC6979
    • SECG-256R1 (technically identical as NIST-P256, but loaded via different code path, so standalone tests)
      • Keypairs
      • Diffie Hellman Key exchange
      • DSA
    • SECG-384R1
      • Keypairs
      • Diffie Hellman Key exchange
      • DSA
    • SECP-112R1
      • Keypairs
      • Diffie Hellman Key exchange
      • DSA

Edit: updated by @afk11 - all complete

Branching requirements

I've created a 0.3 maintenance branch. Any BC breaks or big features must go into master, while bug fixes need to go in 0.3. When creating a pull request, please target the lowest branch applicable. For example, a bugfix should go into 0.3, while a big new feature, or BC break should go into master. I will "merge up" from the lowest branch to master.

Feature for phpecc executable

openssl takes an argument to dump the parameters of a curve to a file. It could be useful to have something similar here.. Will take a look at this myself

Usage questions

Hi :)

First of all, thanks for your work on this library.

I would like to implement this particular use case (Web Push API) for my library WebPush.

I'm new to cryptography. I'm not sure if you've written this library for people with a minimum of experience with cryptography or not, but I tried to do it anyway.

So far, I have written this:

$localCurveGenerator = EccFactory::getNistCurves()->generator256();
$localPrivateKey = $localCurveGenerator->createPrivateKey();
$localPublicKey = $localPrivateKey->getPublicKey();

$messages = new MessageFactory(EccFactory::getAdapter());
$message = $messages->plaintext($payload, 'sha256');

$pubKeySerializer = new PemPublicKeySerializer(new DerPublicKeySerializer());
$remotePublicKey = $pubKeySerializer->parse($userPublicKey);

$dh = $localPrivateKey->createExchange($messages, $remotePublicKey);
$salt = hash('sha256', $dh->calculateSharedKey(), true);

$cipherText = $dh->encrypt($message);

return array(
    'localPublicKey' => base64_encode($localPublicKey->getContent()), // this doesn't exist
    'salt' => base64_encode($salt),
    'cipherText' => base64_encode($cipherText->getContent()),
);

I have several questions:

  1. I get $salt the way above because I naively thought it was the same as the $key in your EcDH->encrypt(). Am I right or not at all?
  2. How do you get content of a PublicKey ? Am I right to assume I can do it by calling getPoint()->getX() on it? Or does this return something else?
  3. In the code above $userPublicKey is in fact a base 64 encoded string of the user public key. One example is: BKD9anmcirmuG9J4wlPAmezLpqvrBd3mD2Q680XfJNkCzjIx2AwTcUcSrBSzJI9eCkk61ZGbjAglCoPQO9kh0g4=. The parser throws a "Invalid data.". Do you know why ?

Missing tests for deterministic signatures

When I saw #47 I remembered that there are test vectors in RFC6979 for some of the curves we have here. It didn't include secp256k1, but I found some that two implementations were in agreement on.

I'll look for these, want to mark this for 0.3.0?

Byte size is incorrect in PEM files for some NIST P-521 curves

OpenSSL fails to parse some generated keys (encoded uncompressed public key is too short -- see https://www.openssl.org/docs/crypto/BN_num_bytes.html)

$ bin/phpecc genkey --curve=nist-p521 --out=pem > keypair2.pem
Using curve "nist-p521"
$ openssl ec -in keypair2.pem -pubout > mykey.pub
read EC key
unable to load Key
140566204237472:error:10067066:elliptic curve routines:ec_GFp_simple_oct2point:invalid encoding:ecp_oct.c:372:
140566204237472:error:10092010:elliptic curve routines:d2i_ECPrivateKey:EC lib:ec_asn1.c:1206:
140566204237472:error:100DE08E:elliptic curve routines:OLD_EC_PRIV_DECODE:decode error:ec_ameth.c:565:
140566204237472:error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag:tasn_dec.c:1319:
140566204237472:error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error:tasn_dec.c:381:Type=PKCS8_PRIV_KEY_INFO
140566204237472:error:0907B00D:PEM routines:PEM_READ_BIO_PRIVATEKEY:ASN1 lib:pem_pkey.c:132:

Tagging the 0.4 release

I'm thinking it's almost time to tag 0.4, given there have been numerous breaks and improvements that have gone into master. I've created a tagged fork to serve as a release candidate on packagist. afk11/phpecc: ~v0.2, which can be used to test applications in advance of the release. It will be updated until 0.4 is officially tagged.

For the date, I propose tagging in a week (25th June), which should allow any last minute improvements/breaks. I encourage everyone to test, review and leave feedback.

Please have a look at the release notes file: https://github.com/phpecc/phpecc/blob/master/doc/0.4-release-notes.md which captures the aims & significant changes.

Backports

I'll try backport the following to 0.3, but not much more.

  • Backports
    • HmacRandomNumberGenerator (#121) - backported PR #158
    • Constant time ECDSA verification (#129) - backported PR #159

Adopt a mailing list or IRC channel?

I'd like to propose we use an IRC channel or mailing list for lighter discussions about the direction of the project.

This follows some recent work I have done around X509 using php-ecc (CSR/Certificate capabilities) - I'd quite like to create a new repository for it under phpecc, similar to what we're doing with the console application.

A mailing list or IRC channel might be best for project wide decisions, and general discussion.

I am unable to install laravel 5

Hi there

when i install laravel 5. i am getting error "installation failed, deleting ./composer.json"

i don't know what happening there.

So please help me.

i look forward hear from you!
Kinds Regard
alok

gmp_cmp() is not constant-time

Console improvements

-- note: this is more a reminder for me to do it than to raise a real issue, but my free time is limited so can't tackle it right now --

Todo once #41 is merged

  • Parse full PEM data with headers and new lines
  • Sign message
  • Verify signature
  • Add options to read files instead of inline data
  • Add options to configure curve used for generated keypairs (currently only NIST P256/SECP 256R1)
  • Add list supported curves command
  • Add support for DER encoded files

Installation through composer fails

Trying to follow the current README with Composer version 1.0-dev (a309e1d89ded6919935a842faeaed8e888fbfe37) results in:

$ composer require mdanter/ecc:0.1
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - The requested package mdanter/ecc could not be found in any version, there may be a typo in the package name.

Potential causes:
 - A typo in the package name
 - The package is not available in a stable-enough version according to your minimum-stability setting
   see <https://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion> for more details.

Read <http://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.

Installation failed, deleting ./composer.json.

KeyStorage class of some kind?

It's a bit unclear what's happening here, though thinking a KeyStorage class could help with persisting classes, and giving some simplified interface to loading/persisting keys of different types?

use \Mdanter\Ecc\File\PemLoader;
use \Mdanter\Ecc\Serializer\PrivateKey\DerPrivateKeySerializer;
use \Mdanter\Ecc\Serializer\PrivateKey\PemPrivateKeySerializer;
use \Mdanter\Ecc\Serializer\PublicKey\DerPublicKeySerializer;
use \Mdanter\Ecc\Serializer\PublicKey\PemPublicKeySerializer;

$loader = new PemLoader();
$privKeySerializer = new PemPrivateKeySerializer(new DerPrivateKeySerializer());
$pubKeySerializer = new PemPublicKeySerializer(new DerPublicKeySerializer());

MathAdapter name

MathAdapter is an interface, but unlike the other interfaces, there is no Interface suffix.

Is it normal or this interface should be renamed into MathAdapterInterface?

Refactor for autoloading and Composer

Would you mind if I refactor the code to autoloading standards (PSR-4) so it becomes compatible with most modern PHP libraries? It can then get added to Composer/Packagist and be available for wide deployment. manually including/requiring files is pretty outdated.

This would greatly help distribution within the PHP community which has mostly migrated to dependency management through Composer.

refs:

http://www.php-fig.org/psr/psr-4/
https://getcomposer.org/
https://packagist.org/

Direction for 0.4.0

  • EcParamsSerializer (to ecparams file which contains the OID).
  • EcParamsExplicitSerializer (to ecparams file which contains the explicitly named parameters).
  • Console: Sign/Verify messages.
  • Console: Generate an ECParams file.

Reading over the parameters specification, https://tools.ietf.org/html/draft-ietf-pkix-ecc-pkalgs-01#section-3, it seems we can specify a hash function to use in the explicit parameters file - otherwise the default should be SHA1. Will

Also in that document is a full specification of all the necessary types when generating a parameters file. I'm writing small classes to convert our types into theirs, instead of serializing everything - makes sense in the long run I think.

Update README.md

-- note: this is more a reminder for me to do it than to raise a real issue, but my free time is limited so can't tackle it right now --

  • Update key exchange example
  • Document console commands
  • Warn about usage of insecure RNG's

Next Major Release 0.3.0

I would like to start thinking about a major version release of this library given we have had some BC breaks in the API. Let's have a discussion about what is necessary.

Help

I'm writing this because i don't found a way to communicate with you, guys.
I want to know, how to verify a signed transaction in bitcoin.
In my case, i have the scriptsig (r and s) and the pubkey
running the script (https://en.bitcoin.it/wiki/Script) how i can verify that?

Decouple RNG from MathAdapter

Currently, the RNG used to generate private keys is directly coupled to the MathAdapter implementation:

It would be better to separate both concerns (math ops vs. rng). It would allow for example to use openssl_random_pseudo_bytes() or an external lib (I'm thinking ircmaxell/randomlib) to provide better randoms, independantly of the used math library.

Thoughts before I start coding on this issue ?

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.