Giter VIP home page Giter VIP logo

evidence-api's Introduction

Python Code Scan Document Scan Python License Check VMSDK Python Test

Evidence API

Evidence API helps the diverse applications to access and process the trust states which was represented by integrity measurement, event record, report/quote in the confidential computing environment. Find more details in the wiki.

APIs

Evidence APIs aims to collect confidential evidence (i.e., measurement, event log, quote) for zero-trust design, supporting multiple deployment environments (firmware/VM/cloud native cluster). The APIs are designed to be vendor agnostic and TCG compliant APIs. The APIs will keep evolving on demand.

API Description Parameters Response
get_default_algorithms Get the default Digest algorithms supported by trusted foundation. A TcgAlgorithmRegistry object telling the default algorithms
get_measurement_count Get the count of measurement register. An integer telling the count of measurement registers
get_cc_measurement Get measurement register according to given selected index and algorithms. imr_select ([int, int]): The first is index of measurement register, the second is the algorithms ID An integer telling the count of measurement registers
get_cc_report Get the quote for given nonce and data. nonce: a number used to protect private communications by preventing replay attacks
data: the data specified by user
extraArgs: the placeholder for extra arguments required in vTPM or other TEE cases
A CcReport (i.e. quote) object
get_cc_eventlog Get eventlog for given index and count. start: the index of the event log to start fetching
count: the number of event logs to fetch
A TcgEventLog object
replay_cc_eventlog Replay event logs fetched through get_cc_eventlog api. event_logs: a list of event logs fetched using get_cc_eventlog api A dict listing the replay result containing information including IMR index number, algorithm using and replayed measurement

SDKs

It provides different SDKs for producing the confidential primitives in different deployment environments. Choose correct SDK according to your environment. Installation guide can be found at the readme of each implementation.

SDK Deployment Scenarios Installation Guide
Firmware SDK Firmware Application
VM SDK Confidential Virtual Machine Guide
Container Integrity Measurement Agent (CIMA) Confidential Cluster/Container Guide

How to use the APIs

This section contains the brief samples of APIs. You can find more examples at API usage example.

An example of get_cc_measurement API

Below example code collects measurements from all integrity registers of the platform using API get_measurement_count, get_default_algorithms and get_cc_measurement using VMSDK in python.

from cctrusted import CCTrustedVmSdk

# Get total count of measurement registers, Intel® TDX is 4, vTPM is 24
count = CCTrustedVmSdk.inst().get_measurement_count()
for index in range(CCTrustedVmSdk.inst().get_measurement_count()):
    # Get default digest algorithms, Intel® TDX is SHA384, vTPM is SHA256
    alg = CCTrustedVmSdk.inst().get_default_algorithms()
    # Get digest object for given index and given algorithms
    digest_obj = CCTrustedVmSdk.inst().get_cc_measurement([index, alg.alg_id])

    hash_str = ""
    for hash_item in digest_obj.hash:
        hash_str += "".join([f"{hash_item:02x}", " "])

    LOG.info("Algorithms: %s", str(alg))
    LOG.info("HASH: %s", hash_str)

Run cc_imr_cli.py to execute the sample.

$ git clone https://github.com/cc-api/cc-trusted-vmsdk.git
$ cd cc-trusted-vmsdk
$ sudo su
# source setupenv.sh
# cd src/python
# python3 cc_imr_cli.py

Below is the example output for get_cc_measurement API on Intel® TDX via VM SDK:

cctrusted.cvm DEBUG    Successful open device node /dev/tdx_guest
cctrusted.cvm DEBUG    Successful read TDREPORT from /dev/tdx_guest.
cctrusted.cvm DEBUG    Successful parse TDREPORT.
cctrusted.cvm INFO     ======================================
cctrusted.cvm INFO     CVM type = TDX
cctrusted.cvm INFO     CVM version = 1.5
cctrusted.cvm INFO     ======================================
__main__ INFO     Algorithms: TPM_ALG_SHA384
__main__ INFO     HASH: c1 57 27 ca c1 f5 7d 0e 91 10 6d a1 80 b3 ea ba 72 11 66 61 e1 7b a0 55 37 73 84 3a 9b 07 2e cf a3 8c c8 03 df b5 5e 0f 87 ec 23 67 80 ad b3 a6
cctrusted.cvm INFO     ======================================
cctrusted.cvm INFO     CVM type = TDX
cctrusted.cvm INFO     CVM version = 1.5
cctrusted.cvm INFO     ======================================
__main__ INFO     Algorithms: TPM_ALG_SHA384
__main__ INFO     HASH: ee 35 46 2b 47 53 58 1b 4c 5a 53 8d c1 92 51 89 ba 9d 21 f5 19 7b 6b 15 ce 10 a6 00 fb d3 12 e0 e3 5c 2b 87 01 fc b2 17 51 82 43 3c 9b 12 b9 dc
cctrusted.cvm INFO     ======================================
cctrusted.cvm INFO     CVM type = TDX
cctrusted.cvm INFO     CVM version = 1.5
cctrusted.cvm INFO     ======================================
__main__ INFO     Algorithms: TPM_ALG_SHA384
__main__ INFO     HASH: 9a c0 ba 4e db 45 03 08 9a a4 a9 2a fe 97 cb 15 94 18 2f 44 aa e0 e5 8d 6f 90 a2 22 9c f9 a4 22 86 5d 87 35 d6 0b 87 3d 6b ec 36 41 d8 96 68 00
cctrusted.cvm INFO     ======================================
cctrusted.cvm INFO     CVM type = TDX
cctrusted.cvm INFO     CVM version = 1.5
cctrusted.cvm INFO     ======================================
__main__ INFO     Algorithms: TPM_ALG_SHA384
__main__ INFO     HASH: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

An example of get_cc_report API

Below example code collect the CcReport (i.e. quote) on the platform using get_cc_report API using VMSDK implemented by python.

from cctrusted import CCTrustedVmSdk

# Specify the `nonce`, `data` and `extraArgs` as None in the example
quote = CCTrustedVmSdk.inst().get_cc_report(None, None, None)
if quote is not None:
    # Dump CcReport (i.e. quote) object as raw data
    quote.dump(is_raw=True)

Run cc_quote_cli.py to execute the sample.

$ git clone https://github.com/cc-api/cc-trusted-vmsdk.git
$ cd cc-trusted-vmsdk
$ sudo su
# source setupenv.sh
# cd src/python
# python3 cc_quote_cli.py

Below is the example output for get_cc_report API on Intel® TDX via VM SDK:

root@tdx-guest:/home/tdx/cc-trusted-vmsdk/src/python# python3 ./cc_quote_cli.py
cctrusted.cvm DEBUG    Successful open device node /dev/tdx_guest
cctrusted.cvm DEBUG    Successful read TDREPORT from /dev/tdx_guest.
cctrusted.cvm DEBUG    Successful parse TDREPORT.
cctrusted.cvm INFO     Using report data directly to generate quote
cctrusted.cvm DEBUG    Successful open device node /dev/tdx_guest
cctrusted.cvm DEBUG    Successful get Quote from /dev/tdx_guest.
evidence_api.tdx.quote INFO     ======================================
evidence_api.tdx.quote INFO     TD Quote
evidence_api.tdx.quote INFO     ======================================
evidence_api.tdx.quote INFO     TD Quote Header:
evidence_api.binaryblob INFO     00000000  04 00 02 00 81 00 00 00 00 00 00 00 93 9A 72 33  ..............r3
evidence_api.binaryblob INFO     00000010  F7 9C 4C A9 94 0A 0D B3 95 7F 06 07 C6 0E 85 25  ..L............%
evidence_api.binaryblob INFO     00000020  C8 09 3C 0E A0 64 EF F1 29 6B 85 83 00 00 00 00  ..<..d..)k......
evidence_api.tdx.quote INFO     TD Quote Body:
evidence_api.binaryblob INFO     00000000  04 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
evidence_api.binaryblob INFO     00000010  97 90 D8 9A 10 21 0E C6 96 8A 77 3C EE 2C A0 5B  .....!....w<.,.[
evidence_api.binaryblob INFO     00000020  5A A9 73 09 F3 67 27 A9 68 52 7B E4 60 6F C1 9E  Z.s..g'.hR{.`o..
...
evidence_api.binaryblob INFO     00000230  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
evidence_api.binaryblob INFO     00000240  00 00 00 00 00 00 00 00                          ........
evidence_api.tdx.quote INFO     TD Quote Signature:
evidence_api.binaryblob INFO     00000000  16 1F E4 F6 8C 05 D4 8F E2 EB EB C8 32 1A CE 6C  ............2..l
evidence_api.binaryblob INFO     00000010  90 2A B5 EA 74 F5 4C 4D A2 6A 30 AC 5C A5 13 84  .*..t.LM.j0.\...
evidence_api.binaryblob INFO     00000020  3D CB A2 31 20 43 8C 38 63 3D EE D1 7F B4 9F B5  =..1 C.8c=......
...
evidence_api.binaryblob INFO     000010D0  44 20 43 45 52 54 49 46 49 43 41 54 45 2D 2D 2D  D CERTIFICATE---
evidence_api.binaryblob INFO     000010E0  2D 2D 0A 00                                      --..

An example of get_cc_eventlog API

Below example code collects all boot time event logs on the platform using API get_cc_eventlog implemented in VMSDK in python. Sample Event logs collected within container using CCNP API can be found here.

from cctrusted import CCTrustedVmSdk

# Specify the index of event log to start fetching(optional argument, default as 0)
start = 0
# Specify the number of event logs to be fetched.(optional argument, default as total number of event logs available)
count = 5

event_logs = CCTrustedVmSdk.inst().get_cc_eventlog(start, count)
    if event_logs is not None:
        LOG.info("Total %d of event logs fetched.", len(event_logs))
        # Dump event as formatted
        for event in event_logs:
            event_logs.dump()

Run cc_event_log_cli.py to execute the sample.

$ git clone https://github.com/cc-api/cc-trusted-vmsdk.git
$ cd cc-trusted-vmsdk
$ sudo su
# source setupenv.sh
# cd src/python
# python3 cc_event_log_cli.py [-s <start_index_of_event_log>] [-c <count_of_event_logs>]

Below is the description of the output of get_cc_eventlog API on Intel® TDX via VM SDK. Full event logs can be found in API usage example.

vmsdk event log output description

6. Contributors

kenplusplus
Lu Ken
Ruoyu-y
Ying Ruoyu
intelzhongjie
Shi Zhongjie
hairongchen
Hairongchen
wenhuizhang
Wenhui Zhang
ruomengh
Ruomeng Hao
dongx1x
Xiaocheng Dong
jyao1
Jiewen Yao
leyao-daily
Le Yao

evidence-api's People

Contributors

dongx1x avatar github-actions[bot] avatar hairongchen avatar haokunx-intel avatar intelzhongjie avatar jyao1 avatar kenplusplus avatar lindao0o avatar ruomengh avatar ruoyu-y avatar wenhuizhang 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

Watchers

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

evidence-api's Issues

Enhance CEL support in runtime event log fetching

What would you like to be added?

Support CEL in all three languages in vmsdk implementation.
Currently, we only support the CEL standard in our python implementation and limited to the TLV encoding. For next steps, we need to provide full support for all encodings (TLV, CBOR and native JSON).

Why is this needed?

Canonical Event Log(CEL) located at https://trustedcomputinggroup.org/wp-content/uploads/TCG_IWG_CEL_v1_r0p30_13feb2021.pdf is the recent standard from TCG that can encapsulate native Event Log Records from various sources.
This specification also provides a simple Type-Length-Value (TLV) encoding layer and a Concise Binary Object
Representation (CBOR) encoding layer. This specification covers TLV and CBOR encapsulation of some content
layers, including CEL Management, PCCLIENT [2] and IMA [5] content. While a content layer implementation may choose to create an exact binary mapping to this information model as its native Event Log Record, other
implementations may choose to bind this information model to other formats, such as TLV or CBOR.
This specification is also an important standard for runtime measurement, as it covers the scope of IMA which is one of the fundamental technologies that leverage by the CC API.

Code quality issues in the Rust SDK

Hi, team. After examining the eventlog parsing code of the Rust version of this library, I think there are several areas that might need attention.

Unchecked input data slicing may lead to panics

As shown below:

        // Parse EFI Spec Id Event structure
        let spec_id_signature = data[index..index + 16].try_into().unwrap();
        index += 16;
        let spec_id_platform_cls = get_u32(data[index..index + 4].to_vec());
        index += 4;
        let spec_id_version_minor = get_u8(data[index..index + 1].to_vec());
        index += 1;
        ...

All of the parsing logic directly slice the input data without a length check. If a user inputs malformed or incomplete data, the parser would panic. "Panicking" is not a valid error handling choice in a Rust library; it should return Err if there is any problem with the input data.

I suggest using a data decoding library rather than hand-rolling the parsing code from scratch. By using a library, you might get an input reader for free to get rid of manually managing the input data cursor. For example, the above code might become:

        // Parse EFI Spec Id Event structure
        let spec_id_signature = <[u8; 16]>::decode(input_reader)?;
        let spec_id_platform_cls = u32::decode(input_reader)?;
        let spec_id_version_minor = u8::decode(input_reader)?;
        ...

I'll send a PR to demonstrate the details of how to use a library to handle it.

.unwrap() might cause panic

.unwrap() should never be used in Rust except in unit tests or examples. The same applies to .expect("the reason") unless it is known that it would never fail. For example:

let hash: [u8; 32] = data.get(..32).context("insufficient data")?.try_into().expect("convert should never fail");

Unsafe enum transmutation could result in Undefined Behavior

There are some uses of unsafe transmute in the code. When using unsafe in Rust, you should be very careful, because unsafe Rust is harder than C/C++ to use correctly.

Take an example from this repo:

impl TdxQuote {
    pub fn parse_tdx_quote(quote: Vec<u8>) -> Result<TdxQuote, anyhow::Error> {
        let tdx_quote_header: TdxQuoteHeader = unsafe {
            transmute::<[u8; 48], TdxQuoteHeader>(
                quote[0..48]
                    .try_into()
                    .expect("slice with incorrect length"),
            )
        };
        ...
   }
}

// where the definition of TdxQuoteHeader is:
#[repr(C)]
#[derive(Clone)]
pub struct TdxQuoteHeader {
    pub version: u16,
    pub ak_type: AttestationKeyType,
    ...
}

#[repr(u16)]
#[derive(Clone, PartialEq, Debug)]
pub enum AttestationKeyType {
    ECDSA_P256 = 2,
    ECDSA_P384 = 3,
}

As you can see, there is at least one enum field ak_type in the transmuted struct. If the input data of the field ak_type is a value not in [2, 3], (for example, if some future platform got a third AttestationKeyType == 4), the code behavior becomes undefined.
The Rust compiler always optimizes the code based on the assumption that the ak_type never gets an invalid value.

See here for the demonstration code.

Additional areas for enhancement

  • Prefer using &[u8] rather than Vec<u8> as function parameters.

For example, the following definition will cause unnecessary call-site heap allocation.

pub fn get_u16(data: Vec<u8>) -> u16 { ... }
// Call-site, the to_vec() will allocate a new heap memory.
fn foo() { let algo_id = get_u16(data[index..index + 2].to_vec()); }

Better:

pub fn get_u16(data: &[u8]) -> u16 { ... }
// Call-site:
fn foo() { let algo_id = get_u16(&data[index..index + 2]); } // Without extra heap allocation.
  • More agnostic error handling
       match self.parse() {
            Ok(_) => (),
            Err(e) => {
                return Err(anyhow!("[select] error in parse function {:?}", e));
            }
        }

A shorter equivalent could be:

       _ = self.parse().context("[select] error in parse function")?;

And also return Err(anyhow!("...")); could be shortened to anyhow::bail!("...");.

  • Prefer match on enum vars over if else
    Using match, the compiler forces you to update your logic when you add variants to the enum.
  • Some other minor style issues may be found using cargo clippy.

The guest is enabled with TDX, however the cc-api shows "not in any TEE"

I have a guest enabled with Trusted Domain Extensions (TDX), but when I query the cc-api, it shows "not in any TEE". This indicates that the system is not recognizing the TDX environment properly.

Steps to Reproduce

  • Set up a guest with TDX enabled.
  • Query the cc-api to check the TEE status.
  • Observe that the cc-api response is "not in any TEE".

Expected Behavior

  • The cc-api should recognize the TDX environment and report the guest as running in TDX.

Actual Behavior

  • The cc-api shows "not in any TEE" despite the guest being enabled with TDX.
root@localhost:~/runc_agent# dmesg | grep tdx
[    0.000000] tdx: Guest detected
[    0.000000] Linux version 5.15.120.bsk.0-tdxg.0-amd64 (STE-Kernel@ByteDance) (gcc (Debian 8.3.0-6) 8.3.0, GNU ld (GNU Binutils for Debian) 2.31.1) #tdxg.0 SMP Debian 5.15.120.bsk.0-tdxg.0 Tue May 7 06:25:17 UTC 
[    0.000000] Command line: init=/bin/systemd rootwait rw root=/dev/vda1 rootfstype=ext4 console=ttyS0 console=tty0 systemd.show_status=true systemd.log_level=debug net.ifnames=0 tdx_disable_filter tdx_host=on numa_balancing=disable ima=on ima_policy=tcb ima_hash=sha384 initrd=initrd
[    0.381383] Kernel command line: init=/bin/systemd rootwait rw root=/dev/vda1 rootfstype=ext4 console=ttyS0 console=tty0 systemd.show_status=true systemd.log_level=debug net.ifnames=0 tdx_disable_filter tdx_host=on numa_balancing=disable ima=on ima_policy=tcb ima_hash=sha384 initrd=initrd
[    0.381455] Unknown kernel command line parameters "tdx_disable_filter tdx_host=on ima=on ima_policy=tcb ima_hash=sha384", will be passed to user space.
[    5.933650]     tdx_disable_filter
[    5.933652]     tdx_host=on
root@localhost:~/runc_agent# grep -o tdx_guest /proc/cpuinfo 
tdx_guest
tdx_guest

root@localhost:~/runc_agent/test/target/release# ./cc-sample-eventlog 
[2024-06-13T00:20:27Z ERROR cc_sample_eventlog] error getting TDX report: [get_cc_eventlog] error create cvm: [build_cvm] Error: not in any TEE!


root@localhost:~/runc_agent/test/target/release# ./cc-sample-measurement 
[2024-06-13T00:20:36Z INFO  cc_sample_measurement] call cc trusted API [get_default_algorithm] to get CVM supported algorithm!
[2024-06-13T00:20:36Z ERROR cc_sample_measurement] error get algorithm: [get_default_algorithm] error get algorithm: [build_cvm] Error: not in any TEE!
root@localhost:~/runc_agent/test/target/release# ./cc-sample-quote 
[2024-06-13T00:20:41Z INFO  cc_sample_quote] call cc trusted API [get_cc_report] to retrieve cc report!
[2024-06-13T00:20:41Z ERROR cc_sample_quote] error getting TDX report: [get_cc_report] error create cvm: [build_cvm] Error: not in any TEE!

Potential enhancement on TPM get_cc_report

What would you like to be added?

More algorithm support for hashing the user defined 'nonce' and 'userData' into qualifying data.

Why is this needed?

In TPM, one uses qualifying data to add user information into quote or to qualify the quote. To merge the two params 'userData' and 'nonce' in our get_cc_report() API into one value to fit into qualifying data, we need some hash algorithm to help. In TPM case, the length of qualifying data is closely related to the hash algorithm it supports. So for different TPMs, the supported length of qualifying data differs.
In our current implementation, we will choose the hash algorithm from user input (pcr_selection). And the current hash algorithm supported only covers SHA1, SHA256, SHA384 and SHA512. More algorithm maybe needed in different circumstances.

Support vTPM in Rust and Go

What would you like to be added?

vTPM has been supported initially in Python for getting eventlog and getting measurement. We still need to support it in Rust and Go. Rust implementation will take high priority given container measurement depends on it.

  • Support vTPM in Rust
  • Support v TPM in Go

Why is this needed?

vTPm is broadly supported in famous CSP, such as Google Cloud and Azure. vTPM is also supported in confidential VM (CVM) provided by CSP. It's an option of building trust chain via vTPM. With supporting vTPM in CC Trusted API, it's feasible to collect evidence for the trust chain based on vTPM.

Output of VM Image Rewriter has issues on both tdx 1.0 stack and tdx 1.5 stack

System log on TDX 1.5 stack

root@n73-164-13:/data01/wenhui/cc-trusted-vmsdk/src/cvm-image-rewriter# uname -a
Linux n73-164-13 6.2.16-v5.0.mvp40-generic #tdx SMP PREEMPT_DYNAMIC Tue Oct 10 03:25:22 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux 
./qemu-test.sh -i output.qcow2 -k /boot/vmlinuz-6.2.16-v5.0.mvp40-generic
+ echo =========================================
=========================================
+ launch_vm
+ echo 'Remapping CTRL-C to CTRL-]'
Remapping CTRL-C to CTRL-]
+ stty intr '^]'
+ echo 'Launch VM:'
Launch VM:
+ echo /usr/bin/qemu-system-x86_64 -accel kvm -name process=tdxvm,debug-threads=on -m 4G -vga none -monitor pty -no-hpet -nodefaults -drive 
/usr/bin/qemu-system-x86_64 -accel kvm -name process=tdxvm,debug-threads=on -m 4G -vga none -monitor pty -no-hpet -nodefaults -drive file=/d
+ eval /usr/bin/qemu-system-x86_64 -accel kvm -name process=tdxvm,debug-threads=on -m 4G -vga none -monitor pty -no-hpet -nodefaults -drive 
++ /usr/bin/qemu-system-x86_64 -accel kvm -name process=tdxvm,debug-threads=on -m 4G -vga none -monitor pty -no-hpet -nodefaults -drive file
char device redirected to /dev/pts/1 (label compat_monitor0)
qemu-system-x86_64: warning: Unknown start 0x0 size 0x1000 shared_to_private 1
EAX=00000000 EBX=00000000 ECX=00000000 EDX=000806f8
ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000000
EIP=0000fff0 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0000 00000000 0000ffff 00009300
CS =f000 ffff0000 0000ffff 00009b00
SS =0000 00000000 0000ffff 00009300
DS =0000 00000000 0000ffff 00009300
FS =0000 00000000 0000ffff 00009300
GS =0000 00000000 0000ffff 00009300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT=     00000000 0000ffff
IDT=     00000000 0000ffff
CR0=60000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
EFER=0000000000000000
Code=00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <00> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0
qemu-system-x86_64: terminating on signal 15 from pid 3746 (-bash)
Terminated

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.