bl4ck5un / mbedtls-sgx Goto Github PK
View Code? Open in Web Editor NEWmbedtls-SGX: a SGX-friendly TLS stack (ported from mbedtls)
License: Apache License 2.0
mbedtls-SGX: a SGX-friendly TLS stack (ported from mbedtls)
License: Apache License 2.0
For multi threaded server sgx_emmt shows 0KB.
Raised the issue here, see details: intel/linux-sgx#237
But it seems the problem is in this project.
I found a implementation issue in ocall_mbedtls_net_accept
that will casue a vulnerability.
This is the definition in EDL.
int ocall_mbedtls_net_accept( [in] mbedtls_net_context *bind_ctx, [out] mbedtls_net_context *client_ctx, [out, size=buf_size] void *client_ip, size_t buf_size, [out] size_t *ip_len );
As we known, all the value OCALL returns is untrusted. mbedtls_net_accept_ocall
is a wrapper function of OCALL function ocall_mbedtls_net_accept
. mbedtls_net_accept_ocall
should have checked whether the value returned from ocall_mbedtls_net_accept
is valid, E.g., the ip_len
should always less than or equal to buf_size.
Missing the check means the callee takes the responsibility to check returned value. Unfortunately, in example/enclave/s_server.c
, the sample code doesn't check the value returned from mbedtls_net_accept_ocall
, which will cause a stack memory leak.
int ssl_server()
{
...
unsigned char client_ip[16] = { 0 };
...
if( ( ret = mbedtls_net_accept_ocall( &listen_fd, &client_fd,
client_ip, sizeof( client_ip ), &cliip_len ) ) != 0 )
...
#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY)
if( opt.transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
{
if( ( ret = mbedtls_ssl_set_client_transport_id( &ssl,
client_ip, cliip_len ) ) != 0 )
...
}
The code above invokes mbedtls_net_accept_ocall
to get client_ip
and cliip_len
at first, then it invokes mbedtls_ssl_set_client_transport_id
to store client_ip
with length of cliip_len
. The cliip_len
is returned from OCALL and there is no check on it. If the attacker returns cliip_len
that is larger than sizeof(client_ip)
, mbedtls_ssl_set_client_transport_id
will store larger size of contents than client_ip
should be. That's a stack memory leak.
To fix this issue, we can implement a wrapper function in enclave. It invokes mbedtls_net_accept_ocall
and check returned ip_len
.
It seems that the 'config.h' disabled the Intel AES-NI feature?
Is the ca_bundle.h is correct because when I do the first CA certificate verification itself fails? How could I fix this? Also which TLS version it supports?
roshan@bolt:~$ openssl s_client -connect localhost:4433 -tls1_2
CONNECTED(00000003)
depth=0 C = NL, O = PolarSSL, CN = localhost
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = NL, O = PolarSSL, CN = localhost
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
0 s:/C=NL/O=PolarSSL/CN=localhost
i:/C=NL/O=PolarSSL/CN=PolarSSL Test CA
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDNzCCAh+gAwIBAgIBAjANBgkqhkiG9w0BAQUFADA7MQswCQYDVQQGEwJOTDER
MA8GA1UEChMIUG9sYXJTU0wxGTAXBgNVBAMTEFBvbGFyU1NMIFRlc3QgQ0EwHhcN
MTEwMjEyMTQ0NDA2WhcNMjEwMjEyMTQ0NDA2WjA0MQswCQYDVQQGEwJOTDERMA8G
A1UEChMIUG9sYXJTU0wxEjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAMFNo93nzR3RBNdJcriZrA545Do8Ss86ExbQWuTN
owCIp+4ea5anUrSQ7y1yej4kmvy2NKwk9XfgJmSMnLAofaHa6ozmyRyWvP7BBFKz
NtSj+uGxdtiQwWG0ZlI2oiZTqqt0Xgd9GYLbKtgfoNkNHC1JZvdbJXNG6AuKT2kM
tQCQ4dqCEGZ9rlQri2V5kaHiYcPNQEkI7mgM8YuG0ka/0LiqEQMef1aoGh5EGA8P
hYvai0Re4hjGYi/HZo36Xdh98yeJKQHFkA4/J/EwyEoO79bex8cna8cFPXrEAjya
HT4P6DSYW8tzS1KW2BGiLICIaTla0w+w3lkvEcf36hIBMJcCAwEAAaNNMEswCQYD
VR0TBAIwADAdBgNVHQ4EFgQUpQXoZLjc32APUBJNYKhkr02LQ5MwHwYDVR0jBBgw
FoAUtFrkpbPe0lL2udWmlQ/rPrzH/f8wDQYJKoZIhvcNAQEFBQADggEBAJxnXClY
oHkbp70cqBrsGXLybA74czbO5RdLEgFs7rHVS9r+c293luS/KdliLScZqAzYVylw
UfRWvKMoWhHYKp3dEIS4xTXk6/5zXxhv9Rw8SGc8qn6vITHk1S1mPevtekgasY5Y
iWQuM3h4YVlRH3HHEMAD1TnAexfXHHDFQGe+Bd1iAbz1/sH9H8l4StwX6egvTK3M
wXRwkKkvjKaEDA9ATbZx0mI8LGsxSuCqe9r9dyjmttd47J1p1Rulz3CLzaRcVIuS
RRQfaD8neM9c1S/iJ/amTVqJxA1KOdOS5780WhPfSArA+g4qAmSjelc3p4wWpha8
zhuYwjVuX6JHG0c=
-----END CERTIFICATE-----
subject=/C=NL/O=PolarSSL/CN=localhost
issuer=/C=NL/O=PolarSSL/CN=PolarSSL Test CA
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-521, 521 bits
---
SSL handshake has read 1400 bytes and written 499 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
Session-ID: 1354B3F49E125CBCB3F1AD12F2C85974461E938DFF0BCBFDE026DEEACCA0381F
Session-ID-ctx:
Master-Key: D5A92FFB853FD3CDC71D25863F408C058B4393676B8C2BD8C3F33020E83C9E4BA96436E2D89815EA77DB77A6F33EF028
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1527897347
Timeout : 7200 (sec)
Verify return code: 21 (unable to verify the first certificate)
I've linked libmbedtls_sgx_u.a
and libmbedtls_sgx_u.t
to the untrusted part and enclave in my project, but including trusted/mbedtls_sgx.edl
in my .edl file leads to the following error during build:
Enclave/Enclave_t.o: In function `sgx_dummy':
/home/vagrant/shared/xchange/Enclave/Enclave_t.c:107: undefined reference to `dummy'
collect2: error: ld returned 1 exit status
Makefile:249: recipe for target 'enclave.so' failed
make: *** [enclave.so] Error 1
Commenting out the dummy declaration in mbedtls_SGX.edl
avoids this issue however.
Now I have mbed-tls HTTPS server(SGX), How SP (clients, Non SGX) could perform Intel SGX remote attestation of the server. The example here uses https://github.com/intel/linux-sgx TCP sockets for communication. Any insight into how It could be achieved ?
Source: mbedtls-SGX/example/enclave/s_client.c
The opt.psk
in ssl_client()
must check the string length before copy psk data to local psk buffer. The following code could vulnerable to stack overflow and overwrite sensitive buf
.
int ssl_client(client_opt_t opt, char* headers[],
int n_header, unsigned char* output,
int length){
unsigned char buf[16385];
unsigned char psk[32];
...
if(strlen(opt.psk)){
...
psk_len = strlen( opt.psk ) / 2;
for(j = 0;j < strlen( opt.psk );j += 2){
c = opt.psk[j];
...
psk[j/2] = c << 4;
...
c = opt.psk[j + 1];
...
psk[j/2] |= c;
}
}
}
When running make -j
after cmake .. -DCOMPILE_EXAMPLES=YES
, I get the following error:
c++: error: /home/vagrant/mbedtls-SGX/example/mbedtls_SGX-2.6.0/libs/libmbedtls_SGX_t.a: No such file or directory
example/enclave/CMakeFiles/enclave.dir/build.make:254: recipe for target 'enclave.so' failed
make[2]: *** [enclave.so] Error 1
CMakeFiles/Makefile2:369: recipe for target 'example/enclave/CMakeFiles/enclave.dir/all' failed
make[1]: *** [example/enclave/CMakeFiles/enclave.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
[ 76%] Built target mbedtls_SGX_t
Makefile:127: recipe for target 'all' failed
make: *** [all] Error 2
I am facing the error:
0x1007 which means SGX_ERROR_ECALL_NOT_ALLOWED
and the reason given here: https://software.intel.com/en-us/node/709252
ECALL is not allowed at this time. For examples:ECALL is not public.ECALL is blocked by the dynamic entry table. A nested ECALL is not allowed during global initialization.
I have checked my ecall is public. Could someone can explain the reason:
ECALL is blocked by the dynamic entry table.
A nested ECALL is not allowed during global initialization.
In my case: The server is running inside SGX(https://github.com/bl4ck5un/mbedtls-SGX/blob/master/example/enclave/ssl_conn_hdlr.cpp) After step 6 and in between step 7. I am calling an ocall, do something with the request payload and then call another different ecall which finally returns to the actual ecall(start of step 7 of ssl_conn_hdlr).
Am I doing nested ecall ? Can't I call an ocall after step 6 and from that ocall I call another ecall which sets up the response?
I am running multi_threaded server example.
If ssl_conn_handle
called after ssl_conn_teardown
by untrusted host
mbedtls-SGX/example/enclave/ecalls.cpp
Lines 44 to 50 in eab8e36
this
is dangling, and this->conf
at line 159 will cause UAFmbedtls-SGX/example/enclave/ssl_conn_hdlr.cpp
Lines 151 to 159 in eab8e36
If ssl_conn_teardown
called after ssl_conn_teardown
by untrusted host, second will call delete connectionHandler;
, srvcert
is freed member varibale
In TLSConnectionHandler::~TLSConnectionHandler
,
mbedtls-SGX/example/enclave/ssl_conn_hdlr.cpp
Lines 129 to 131 in eab8e36
In mbedtls_x509_crt_free
.
mbedtls-SGX/trusted/mbedtls-2.6.0/library/x509_crt.c
Lines 2346 to 2360 in eab8e36
In mbedtls_pk_free
, and finally ctx->pk_info
will access already free-ed ctx
, cause UAF.
mbedtls-SGX/trusted/mbedtls-2.6.0/library/pk.c
Lines 66 to 74 in eab8e36
Data races among the following shared variables and functions.
shared variable | function1 | function2 |
---|---|---|
add_count | ecp_add_mixed | ecp_add_mixed |
add_count | ecp_add_mixed | mbedtls_ecp_self_test |
add_count | mbedtls_ecp_self_test | mbedtls_ecp_self_test |
aes_init_done | mbedtls_aes_setkey_enc | mbedtls_aes_setkey_enc |
connectionHandler | ssl_conn_init | ssl_conn_handle |
connectionHandler | ssl_conn_init | ssl_conn_init |
connectionHandler | ssl_conn_init | ssl_conn_teardown |
dbl_count | ecp_double_jac | ecp_double_jac |
dbl_count | ecp_double_jac | mbedtls_ecp_self_test |
dbl_count | mbedtls_ecp_self_test | mbedtls_ecp_self_test |
debug_threshold | mbedtls_debug_set_threshold | mbedtls_debug_print_buf |
debug_threshold | mbedtls_debug_set_threshold | mbedtls_debug_print_crt |
debug_threshold | mbedtls_debug_set_threshold | mbedtls_debug_print_ecp |
debug_threshold | mbedtls_debug_set_threshold | mbedtls_debug_print_mpi |
debug_threshold | mbedtls_debug_set_threshold | mbedtls_debug_print_msg |
debug_threshold | mbedtls_debug_set_threshold | mbedtls_debug_print_ret |
debug_threshold | mbedtls_debug_set_threshold | mbedtls_debug_set_threshold |
init_done.2589 | mbedtls_ecp_grp_id_list | mbedtls_ecp_grp_id_list |
mul_count | ecp_add_mixed | ecp_add_mixed |
mul_count | ecp_add_mixed | ecp_check_pubkey_sw |
mul_count | ecp_add_mixed | ecp_double_add_mxz |
mul_count | ecp_add_mixed | ecp_normalize_mxz |
mul_count | ecp_add_mixed | ecp_randomize_jac |
mul_count | ecp_add_mixed | ecp_randomize_mxz |
mul_count | ecp_add_mixed | mbedtls_ecp_self_test |
mul_count | ecp_check_pubkey_sw | ecp_check_pubkey_sw |
mul_count | ecp_check_pubkey_sw | mbedtls_ecp_self_test |
mul_count | ecp_double_add_mxz | ecp_check_pubkey_sw |
mul_count | ecp_double_add_mxz | ecp_double_add_mxz |
mul_count | ecp_double_add_mxz | mbedtls_ecp_self_test |
mul_count | ecp_double_jac | ecp_add_mixed |
mul_count | ecp_double_jac | ecp_check_pubkey_sw |
mul_count | ecp_double_jac | ecp_double_add_mxz |
mul_count | ecp_double_jac | ecp_double_jac |
mul_count | ecp_double_jac | ecp_normalize_mxz |
mul_count | ecp_double_jac | ecp_randomize_jac |
mul_count | ecp_double_jac | ecp_randomize_mxz |
mul_count | ecp_double_jac | mbedtls_ecp_self_test |
mul_count | ecp_normalize_jac | ecp_add_mixed |
mul_count | ecp_normalize_jac | ecp_check_pubkey_sw |
mul_count | ecp_normalize_jac | ecp_double_add_mxz |
mul_count | ecp_normalize_jac | ecp_double_jac |
mul_count | ecp_normalize_jac | ecp_normalize_jac |
mul_count | ecp_normalize_jac | ecp_normalize_jac_many |
mul_count | ecp_normalize_jac | ecp_normalize_mxz |
mul_count | ecp_normalize_jac | ecp_randomize_jac |
mul_count | ecp_normalize_jac | ecp_randomize_mxz |
mul_count | ecp_normalize_jac | mbedtls_ecp_self_test |
mul_count | ecp_normalize_jac_many | ecp_add_mixed |
mul_count | ecp_normalize_jac_many | ecp_check_pubkey_sw |
mul_count | ecp_normalize_jac_many | ecp_double_add_mxz |
mul_count | ecp_normalize_jac_many | ecp_double_jac |
mul_count | ecp_normalize_jac_many | ecp_normalize_jac_many |
mul_count | ecp_normalize_jac_many | ecp_normalize_mxz |
mul_count | ecp_normalize_jac_many | ecp_randomize_jac |
mul_count | ecp_normalize_jac_many | ecp_randomize_mxz |
mul_count | ecp_normalize_jac_many | mbedtls_ecp_self_test |
mul_count | ecp_normalize_mxz | ecp_check_pubkey_sw |
mul_count | ecp_normalize_mxz | ecp_double_add_mxz |
mul_count | ecp_normalize_mxz | ecp_normalize_mxz |
mul_count | ecp_normalize_mxz | ecp_randomize_mxz |
mul_count | ecp_normalize_mxz | mbedtls_ecp_self_test |
mul_count | ecp_randomize_jac | ecp_check_pubkey_sw |
mul_count | ecp_randomize_jac | ecp_double_add_mxz |
mul_count | ecp_randomize_jac | ecp_normalize_mxz |
mul_count | ecp_randomize_jac | ecp_randomize_jac |
mul_count | ecp_randomize_jac | ecp_randomize_mxz |
mul_count | ecp_randomize_jac | mbedtls_ecp_self_test |
mul_count | ecp_randomize_mxz | ecp_check_pubkey_sw |
mul_count | ecp_randomize_mxz | ecp_double_add_mxz |
mul_count | ecp_randomize_mxz | ecp_randomize_mxz |
mul_count | ecp_randomize_mxz | mbedtls_ecp_self_test |
mul_count | mbedtls_ecp_self_test | mbedtls_ecp_self_test |
opt | ssl_server | ssl_server |
opt+0x10 | ssl_server | ssl_server |
opt+0x20 | ssl_server | ssl_server |
opt+0x28 | ssl_server | ssl_server |
opt+0x30 | ssl_server | ssl_server |
opt+0x38 | ssl_server | ssl_server |
opt+0x40 | ssl_server | ssl_server |
opt+0x48 | ssl_server | ssl_server |
opt+0x50 | ssl_server | ssl_server |
opt+0x58 | ssl_server | ssl_server |
opt+0x8 | ssl_server | ssl_server |
opt+0x84 | ssl_server | ssl_server |
opt+0x8c | ssl_server | ssl_server |
opt+0x90 | ssl_server | ssl_server |
opt+0x94 | ssl_server | ssl_server |
opt+0x98 | ssl_server | ssl_server |
opt+0x9c | ssl_server | ssl_server |
opt+0xa0 | ssl_server | ssl_server |
opt+0xa4 | ssl_server | ssl_server |
opt+0xac | ssl_server | ssl_server |
opt+0xb0 | ssl_server | ssl_server |
opt+0xb4 | ssl_server | ssl_server |
opt+0xd8 | ssl_server | ssl_server |
opt+0xdc | ssl_server | ssl_server |
opt+0xe4 | ssl_server | ssl_server |
opt+0xe8 | ssl_server | ssl_server |
opt+0xf4 | ssl_server | ssl_server |
received_sigterm | term_handler | ssl_server |
supported_init | mbedtls_cipher_list | mbedtls_cipher_list |
supported_init | mbedtls_ssl_list_ciphersuites | mbedtls_ssl_list_ciphersuites |
test_offset | ctr_drbg_self_test_entropy | ctr_drbg_self_test_entropy |
test_offset | ctr_drbg_self_test_entropy | mbedtls_ctr_drbg_self_test |
There is a massive information leak case in mbedtls_ssl_flush_output() using the ocall_mbedtls_net_send(). The vulnerable code is here:
while( ssl->out_left > 0 )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "message length: %d, out_left: %d",
mbedtls_ssl_hdr_len( ssl ) + ssl->out_msglen, ssl->out_left ) );
buf = ssl->out_hdr + mbedtls_ssl_hdr_len( ssl ) +
ssl->out_msglen - ssl->out_left;
ret = ssl->f_send( ssl->p_bio, buf, ssl->out_left );
MBEDTLS_SSL_DEBUG_RET( 2, "ssl->f_send", ret );
if( ret <= 0 )
return( ret );
ssl->out_left -= ret;
}
Here, ssl->out_left
is size_t (unsigned) and the ret
is ocall return (hence untrusted). The ssl->out_left -=ret;
line can cause ssl->out_left
to a large positive integer (hence infinite loop). Moreover, the buf
is pointed to memory using the untrusted ssl->out_left
can let it pointing to any memory location and using the next ocall, dump the entire enclave memory.
Note, ret
is only check for negative value filter. A value greater than ssl->out_left
can cause this issue.
Hi Mbedtls.
I got issues when I add libmbedtls_SGX_t.a and libmbedtls_SGX_u.a to my project
[100%] Linking CXX shared library libeu4_crypto_enclave.so ../../enclave/libs/mbedtls_sgx/libmbedtls_SGX_t.a(mbedtls_SGX_t.c.o):(.data.rel.ro.local+0x0): multiple definition of
g_ecall_table'g_ecall_table' changed from 648 in CMakeFiles/Enclave_t.dir/Enclave_t.c.o to 24 in ../../enclave/libs/mbedtls_sgx/libmbedtls_SGX_t.a(mbedtls_SGX_t.c.o) ../../enclave/libs/mbedtls_sgx/libmbedtls_SGX_t.a(mbedtls_SGX_t.c.o): In function
sgx_dummy':g_dyn_entry_table' CMakeFiles/Enclave_t.dir/Enclave_t.c.o:(.rodata+0x0): first defined here /usr/bin/ld: Warning: size of symbol
g_dyn_entry_table' changed from 488 in CMakeFiles/Enclave_t.dir/Enclave_t.c.o to 24 in ../../enclave/libs/mbedtls_sgx/libmbedtls_SGX_t.a(mbedtls_SGX_t.c.o)CMakefile
`set(MBEDTLS_SGX_LIBRARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs/mbedtls_sgx)
set(MBEDTLS_SGX_LIB_T ${MBEDTLS_SGX_LIBRARY_DIR}/libmbedtls_SGX_t.a)
target_link_libraries(${UE4_PROJECT_NAME}_enclave
"-Wl,--whole-archive"
${Trts_Library_Name}
"-Wl,--no-whole-archive"
"-Wl,--start-group"
${MBEDTLS_SGX_LIB_T}
"sgx_tstdc"
"sgx_tcxx"
"${Crypto_Library_Name}"
"${Service_Library_Name}"
"-Wl,--end-group"
"-Wl,-Bstatic -Wl,-Bsymbolic -Wl,--no-undefined -Wl,-pie,-eenclave_entry -Wl,--export-dynamic -Wl,--defsym,__ImageBase=0 -Wl,--gc-sections -Wl,--version-script=${version_script_file}"
)
`
Please help me to fix this issue.
Thanks!
Hi.
I'm using mbedtls-SGX for a project and this piece of code keeps failing with various errors:
[&](uint64_t index) {
client_opt_t opt;
unsigned char buf[512];
char port[10];
client_opt_init(&opt);
opt.debug_level = 1;
opt.server_name = thread_info->platform_address;
opt.request_page = "/incoming_trigger";
opt.server_port = "8000";
// disable certificate verification
opt.auth_mode = MBEDTLS_SSL_VERIFY_NONE;
// post request
opt.request_type = DFL_POST;
std::string payload = std::to_string(index);
return ssl_client(
opt, // Client options structure
nullptr, // Headers
0, // Number of headers
buf, // Output buffer
sizeof buf, // Output buffer size
payload.c_str() // Payload
);
}
As I said the errors I get vary, here is the last I got were:
[ LOG] ssl_client:488: Seeding the random number generator...
[ LOG] ssl_client:503: Loading the CA root certificate
[ LOG] ssl_client:540: connecting to TCP:XX.XX.XX.XX:8000...
[CRIT] ssl_client:548: mbedtls_net_connect returned -0xffff80d2
[CRIT] ssl_client:1162: Last error was: -0xFFFF80D2 - SSL - Memory allocation failed : OID - OID is not found
[ LOG] ssl_client:488: Seeding the random number generator...
[ LOG] ssl_client:503: Loading the CA root certificate
[ LOG] ssl_client:540: connecting to TCP:XX.XX.XX.XX:8000...
[CRIT] ssl_client:548: mbedtls_net_connect returned -0xffff806b
[CRIT] ssl_client:1162: Last error was: -0xFFFF806B - SSL - Hardware acceleration function returned with error : UNKNOWN ERROR CODE (0015)
[ LOG] ssl_client:488: Seeding the random number generator...
[ LOG] ssl_client:503: Loading the CA root certificate
[ LOG] ssl_client:540: connecting to TCP:147.210.128.152:8000...
[CRIT] ssl_client:548: mbedtls_net_connect returned -0xffff805b
[CRIT] ssl_client:1162: Last error was: -0xFFFF805B - SSL - Hardware acceleration function returned with error : UNKNOWN ERROR CODE (0025)
Can someone help?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.