parallaxsecond / parsec-tool Goto Github PK
View Code? Open in Web Editor NEWThe Parsec Command Line Interface
License: Apache License 2.0
The Parsec Command Line Interface
License: Apache License 2.0
When the parsec tool is compiled in yocto, build warnings are reported as shown below.
WARNING: parsec-tool-0.5.4-r0 do_package_qa: QA Issue: File /usr/bin/.debug/parsec-tool in package parsec-tool-dbg contains reference to TMPDIR [buildpaths]
This is because the parsec tool binary contains build paths.
$ strings parsec-tool |grep yocto
/data/shared/yocto/poky/build/tmp/work/cortexa57-poky-linux/parsec-tool/0.5.3-r0/cargo_home/bitbake/ring-0.16.20/pregenerated/armv8-mont-linux64.S
/data/shared/yocto/poky/build/tmp/work/cortexa57-poky-linux/parsec-tool/0.5.3-r0/cargo_home/bitbake/ring-0.16.20
/data/shared/yocto/poky/build/tmp/work/cortexa57-poky-linux/parsec-tool/0.5.3-r0/cargo_home/bitbake/ring-0.16.20/pregenerated/ecp_nistz256-armv8-linux64.S
/data/shared/yocto/poky/build/tmp/work/cortexa57-poky-linux/parsec-tool/0.5.3-r0/cargo_home/bitbake/ring-0.16.20/pregenerated/sha512-armv8-linux64.S
/data/shared/yocto/poky/build/tmp/work/cortexa57-poky-linux/parsec-tool/0.5.3-r0/cargo_home/bitbake/ring-0.16.20/pregenerated/sha256-armv8-linux64.S
/data/shared/yocto/poky/build/tmp/work/cortexa57-poky-linux/parsec-tool/0.5.3-r0/cargo_home/bitbake/ring-0.16.20
/data/shared/yocto/poky/build/tmp/work/cortexa57-poky-linux/parsec-tool/0.5.3-r0/cargo_home/bitbake/ring-0.16.20/pregenerated
The RUST_DEBUG_REMAP yocto variable which does the remap is included in the compilation of the above libraries but the issue still exists.
poky/meta/classes-recipe/rust-common.bbclass
RUST_DEBUG_REMAP = "--remap-path-prefix=${WORKDIR}=/usr/src/debug/${PN}/${EXTENDPE}${PV}-${PR}"
Currently, the QA check has been disabled by using INSANE_SKIP.
INSANE_SKIP:${PN}-dbg += "buildpaths"
Thanks to @anta5010 for reporting the issue.
Once #27 is done, it should not be too hard to use similar features for signing/verifying.
Similarly commands sign
and verify
could be added. Currently as we only support the SignHash
/VerifyHash
commands we could just expect a hash or perform the hash in software in the tool.
We can use similar techniques as in #27 for the input/output file.
parsec-tool
help states that --provider
option can be passed right after parsec-tool
call, before subcommand. If omitted, the default provider is used.
But for list-opcodes
, this does not work. --provider
cannot be omitted and must be after list-opcodes
subcommand.
The create-csr
function uses the rcgen crate. We need to investigate whether there is an alternative way to implement this functionality without using that crate, relying instead on something like openssl. This is to avoid having a growing number of crypto-function-related libraries being consumed.
This is an investigation - we need to look at what rcgen
is providing and see whether equivalent functions are readily obtainable from more standard security libraries such as openssl or in other crates that were previously being consumed before the rcgen
dependency was added. However, at time of writing, there is no integration between openssl and Parsec, so any use of openssl would just be to help create CSR data in the correct format. We would still need a way to call into Parsec to do the signing with the private key.
DoD would be to have an understanding of how feasible it would be to implement CSR in a different way, with some approximate plan of attack and estimation of the size of the work.
The ansi_term
crate is now reported as unmaintained (see here) - we need to migrate away from both ansi_term
and structopt
to address this. Apparently clap v3 already offers some support for the migration.
The CI should have Parsec running with the Mbed Crypto provider and test various commands with the parsec-tool
to check that it works properly, that the commands stay stable and that the output is correct.
The functionality of the Parsec tool (commands, options...) is available as part of the binary itself, generated by clap
, but no proper external documentation exists. This page documents the process of rendering such documentation as a manpage for a Rust CLI app. The linked discussion points to clap-mangen as the tool to use for this purpose.
Another important factor would be to provide some example scripts for how to achieve various goals, in a markdown page.
Once such documentation is available, the Parsec book (in particular this page) should be enhanced with information about the manpage and the examples.
Now that we pass the BasicClient
to the various subcommands, it might be possible to call it directly to perform the various operations instead of instantiating a new OperationClient
. Some commands might need the fine-grained control of the OperationClient
and for those we can still create it, or get it from the BasicClient
via a getter method.
It will make the various subcommands easier to write (higher-level).
Parsec-tool encodes countryName element of DN as UTF8STRING. At least Openssl and Wireshark expect it to be a PRINTABLESTRING instead.
For the landing page of the repo we could have an asciinema recording of installing the parsec-tool
and running some some basic commands.
The export-public-key
command writes to standard output as space-separated hex digits, which are quite difficult to process. We should make it possible to generate PEM, which is the same data in base64 with standard (BEGIN
and END
) headers and footers - the output can then be trivially copy/pasted into other tools that process keys.
Default behaviour is probably fine as it is, but it would be good to accept a --pem
switch on the command line and output as per this example:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAui/eDjvMavCdat/JDTt2/e51eRttNpdAV9mxaEqF8uf4oZYiM0oVPq4DG6pN2FSOgOR9urD57dGG3ddFmUif1cQc8OeHBCB58HwQzNvSAKaB1TU2YFTGjvX5cDw4pT2gCrbzdQGnVAuHog1Mez5iOt/Ken2dN3zQ3LJhOnml08OTr3+909mU9Bf9uTi6ctVznhaqSvHY7ilHzNZfCi/9skwEze+ExA3xwBMeqJ+kOghgozNcLvv+qEWpyfAEh0JtdtzvWvQPqw3bD9jvlnPQ6HoP5xggLEsNcyuFUHKNU03xNK9qsGItC4GMvtc1U6aHLPyZc51y/L3+/8e/HLcJAQIDAQAB
-----END RSA PUBLIC KEY-----
The data bytes are identical to the current output, just base64 encoded, and the headers and footers (for RSA public keys) should be as shown.
It should be possible to paste this output into an encryption tool such as this one, and successfully encrypt a plaintext message: https://8gwifi.org/rsafunctions.jsp
It would be fine to only support this for RSA public keys for now, although we can research how this would scale to other key types by peeking the key properties before getting the raw bytes.
The parsec-cli-tests script runs tests in a loop with a pipe command in it, which forces them to be run in a sub-shell.
The tests update EXIT_CODE correctly within the loop but that does not gets exported outside of the loop.
So any failures detected are not reported via the exit status of the script.
I provoked an error by changing "--key-name" to "--ky-name" in one of the checks.
The following tests should be added in the CI:
For signing:
For encryption:
Add commands in support of encryption and decryption of data with nominated keys.
Add encrypt
and decrypt
commands to the tool. Possible specifications below (subject to discussion).
Commands should accept a --key-name
parameter to follow the convention of existing commands that deal with keys.
Commands should write cypher/plaintext to standard output by default, but should also accept an --out-file
parameter allowing a file path to be specified instead.
Commands should read cypher/plaintext from standard input by default, but should also accept an --in-file
parameter allowing a file path to be specified instead.
Commands should accept an optional --base64
switch to indicate base64 processing of input/output. When specified, input should be base64-decoded prior to processing, and output should be base64-encoded after processing. By default, both input and output are raw binary.
Functionality can be limited to what the Parsec service supports (asymmetric encryption currently), but the CLI design should be such that it will support future use cases without breaking changes to scripts.
Data sizes can be limited (since Parsec does not yet support multi-part buffering). However, the CLI should be designed to scale to indefinitely-sized input/output in the future. The important thing is that we shouldn't make breaking changes when new functionality appears in the future.
Since we changed a lot of command names and added some, it would be nice to have an up-to-date asciinema demo ๐ฏ
This issue is not reproducible easily, but happens on a slow platforms:
Parsec config
$ sudo cat /etc/parsec/config.toml
[core_settings]
thread_pool_size = 1
log_level = "trace"
log_timestamp = true
[listener]
listener_type = "DomainSocket"
timeout = 7200000 # in milliseconds
[authenticator]
auth_type = "UnixPeerCredentials"
[[key_manager]]
name = "sqlite-manager"
manager_type = "SQLite"
[[provider]]
name = "trusted-service-provider"
provider_type = "TrustedService"
key_info_manager = "sqlite-manager"
Run command
time RUST_LOG=debug PARSEC_TOOL="/usr/bin/parsec-tool -t 7200" /usr/bin/parsec-cli-tests.sh
Log - parsec-error.log
Added a patch -to list the files in the test script
0001-list-keys.patch
The issue:
- Using Parsec to decrypt the result:
[DEBUG parsec_client::core::basic_client] Parsec BasicClient created with implicit provider "Trusted Service provider" and authentication data "UnixPeerCredentials"
[INFO parsec_tool::subcommands::decrypt] Decrypting data with RsaOaep { hash_alg: Sha256 }...
Thu Jan 4 19:17:29 UTC 2024 Parsec decryption test
- Current keys, before deleting -
[DEBUG parsec_client::core::basic_client] Parsec BasicClient created with implicit provider "Trusted Service provider" and authentication data "UnixPeerCredentials"
[INFO parsec_tool::subcommands::list_keys] Available keys:
* anta-key-rsa-crypt (Trusted Service provider, RsaKeyPair, 2048 bits, permitted algorithm: AsymmetricEncryption(RsaOaep { hash_alg: Sha256 }))
- Deleting the RSA key
[DEBUG parsec_client::core::basic_client] Parsec BasicClient created with implicit provider "Trusted Service provider" and authentication data "UnixPeerCredentials"
[INFO parsec_tool::subcommands::delete_key] Deleting a key...
[INFO parsec_tool::subcommands::delete_key] Key "anta-key-rsa-crypt" deleted.
- Current keys, after deleting -
[DEBUG parsec_client::core::basic_client] Parsec BasicClient created with implicit provider "Trusted Service provider" and authentication data "UnixPeerCredentials"
[INFO parsec_tool::subcommands::list_keys] No keys currently available.
- Creating an RSA key and exporting its public part
[DEBUG parsec_client::core::basic_client] Parsec BasicClient created with implicit provider "Trusted Service provider" and authentication data "UnixPeerCredentials"
[INFO parsec_tool::subcommands::create_rsa_key] Creating RSA signing key...
[ERROR parsec_tool] Subcommand failed: asking for an item that already exists (ParsecClientError(Service(PsaErrorAlreadyExists)))
[DEBUG parsec_client::core::basic_client] Parsec BasicClient created with implicit provider "Trusted Service provider" and authentication data "UnixPeerCredentials"
[INFO parsec_tool::subcommands::list_keys] No keys currently available.
Error: anta-key-sign is not listed
[DEBUG parsec_client::core::basic_client] Parsec BasicClient created with implicit provider "Trusted Service provider" and authentication data "UnixPeerCredentials"
[ERROR parsec_tool] Subcommand failed: asking for an item that doesn't exist (ParsecClientError(Service(PsaErrorDoesNotExist)))
- Current keys, before deleting -
[DEBUG parsec_client::core::basic_client] Parsec BasicClient created with implicit provider "Trusted Service provider" and authentication data "UnixPeerCredentials"
[INFO parsec_tool::subcommands::list_keys] No keys currently available.
- Deleting the RSA key
[DEBUG parsec_client::core::basic_client] Parsec BasicClient created with implicit provider "Trusted Service provider" and authentication data "UnixPeerCredentials"
[INFO parsec_tool::subcommands::delete_key] Deleting a key...
[ERROR parsec_tool] Subcommand failed: asking for an item that doesn't exist (ParsecClientError(Service(PsaErrorDoesNotExist)))
- Current keys, after deleting -
[DEBUG parsec_client::core::basic_client] Parsec BasicClient created with implicit provider "Trusted Service provider" and authentication data "UnixPeerCredentials"
[INFO parsec_tool::subcommands::list_keys] No keys currently available.
Question:
If a key_name
is not given by the user for the create_rsa/ecc_key
commands, the parsec-tool
could default to the key name parsec-tool-ecc/rsa-key
.
To go further, if a key name is not given when signing, an ECC key with the name above could be created just-in-time for the signing operation to proceed with it. Maybe the same would make sense for encrypting but maybe with a symetric key (which we don't currently support).
Maybe the same would make sense when exporting a public key, a RSA key for encryption could be created before.
A good enhancement for parsec-tool
would be the auto-completion for sub-commands.
Some of the tool command names are chosen to match the opcode names in the wire protocol, but it would be better to give them names that are more intuitive for scripting, and also to cater for the fact that some commands might end up being compositions of multiple wire protocol ops. Also, words like "generate" and "destroy" are not idiomatic for scripting of key management: "create" and "delete" would be better. There is no need to align to PSA terminology here. The overriding aim is to make a nice usable CLI tool.
psa-generate-key
should be renamed to either create-key
or create-rsa-keypair
depending on whether we want to specialise it to RSA in the future. A single create-key
command will eventually need all sorts of differently-constrained parameter sets for all the different key types, so having a separate command for different main key types might make sense.
psa-destroy-key
should be renamed to delete-key
(and we only need one of those regardless of the key type).
psa-generate-random
can just be generate-random
or make-random
.
psa-export-key
can just be export-key
.
psa-export-public-key
can just be export-publiic-key
.
It should be possible to import keys trough Parsec-tool with an import-key command as that functionality exists in Parsec. This should support at least Mbed-crypto. It shoud take the key or key pair we want to import as an input and at least EccKeyPair should be supported.
The test_rsa_key_bits()
function in parsec-cli-tests.sh greps openssl output for RSA Public-Key:
: https://github.com/parallaxsecond/parsec-tool/blob/main/tests/parsec-cli-tests.sh#L234
But, openssl 3.0.5 doesn't include RSA
in the output:
Public-Key: (1024 bit)
Modulus:
00:b8:0d:17:75:56:ce:52:57:cd:8a:55:28:d6:b8:
6c:a2:95:db:86:58:f7:cc:6e:fb:f5:e3:31:ea:e3:
8b:48:0c:15:24:ac:78:41:99:6e:12:ce:6a:05:18:
9d:fc:52:8a:51:82:97:83:e0:a6:39:ca:eb:06:07:
89:01:6a:0a:1b:83:d6:fd:4b:3d:48:91:de:9a:41:
f8:09:6c:68:0a:5c:fb:85:f2:16:51:8b:2a:0d:ea:
6d:8d:8b:c5:38:f6:b3:9d:22:49:55:c6:01:0c:3b:
b3:20:eb:d6:af:46:14:8a:0e:ea:a2:3d:27:1a:b8:
e9:f3:33:c6:cd:7e:e1:ec:e7
Exponent: 65537 (0x10001)
As a result we see parsec-cli-tests.sh fails, for example, in Yocto built images:
[INFO ] Creating RSA encryption key...
[INFO ] Key "anta-key-rsa-bits" created.
Error: create-rsa-key should have produced a 2048-bit RSA key.
[INFO ] Creating RSA encryption key...
[INFO ] Key "anta-key-rsa-bits" created.
Error: create-rsa-key should have produced a 1024-bit RSA key.
A fix would be very simple:
diff --git a/tests/parsec-cli-tests.sh b/tests/parsec-cli-tests.sh
index 2a56fb2..78e5434 100755
--- a/tests/parsec-cli-tests.sh
+++ b/tests/parsec-cli-tests.sh
@@ -231,7 +231,7 @@ test_rsa_key_bits() {
run_cmd $PARSEC_TOOL_CMD create-rsa-key --key-name $KEY $key_param
run_cmd $PARSEC_TOOL_CMD export-public-key --key-name $KEY >${MY_TMP}/checksize-${KEY}.pem
- if ! run_cmd $OPENSSL rsa -pubin -text -noout -in ${MY_TMP}/checksize-${KEY}.pem | grep -q "RSA Public-Key: (${key_size} bit)"; then
+ if ! run_cmd $OPENSSL rsa -pubin -text -noout -in ${MY_TMP}/checksize-${KEY}.pem | grep -q "Public-Key: (${key_size} bit)"; then
echo "Error: create-rsa-key should have produced a ${key_size}-bit RSA key."
EXIT_CODE=$(($EXIT_CODE+1))
fi
Currently a lot of defaults are handled with structopt default_value
option but it might not be the best choice everywhere.
For example the provider
field of ProviderOpts
has that defaulted to zero where it should maybe be an Option
and the None
case can then be checked statically.
I spotted the following bug because of that:
[hugdev01@machine parsec-tool]$ ./target/debug/parsec-tool list-opcodes
[INFO] Available opcodes for provider MbedCrypto:
* ListProviders
* ListAuthenticators
* ListOpcodes
* ListKeys
* Ping
This is the case for ProviderOpts
but let's review all of them.
Currently, the tool only works with Parsec 0.4.0. It needs to be updated to work with 0.5.0. The main thing to do would be to increase parsec-client version to 0.5.0.
Hi,
We are currently seeing intermittent test failure when executing parsec-cli-tests.sh
on the FVP platform. The intermittent failure test cases are always test_csr()
and test_signing()
.
Reproducer:
#!/bin/sh
set -e
max=200
for i in `seq 1 $max`
do
echo "--------------$i test-------------"
parsec-cli-tests.sh -d
echo "--------------$i test done-------------"
done
We've investigated a bit and below is what we found.
test_csr()
case:Full log with debug info when the case fails:
test_csr_fail_full.txt
Full log with debug info when the case passes:
test_csr_pass_full.txt
It looks like when the test case fails, the generated CSR below
-----BEGIN CERTIFICATE REQUEST-----
MIIBEzCBugIBADAxMRswGQYDVQQDDBJwYXJhbGxheHNlY29uZC5jb20xEjAQBgNV
BAUMCUVaNFUyQ0lYTDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHqW3v6RNxNC
rq4aqQDXf/gUAKHe+MKRzwcaJWoWpt7+vJmNbZmToVfrCClM8epDHwA1U1q7gGYN
UtgVMXIGoCSgJzAlBgkqhkiG9w0BCQ4xGDAWMBQGA1UdEQQNMAuCCWxvY2FsaG9z
dDAKBggqhkjOPQQDAgNIADBFAiAAMbC74dHo2sHy9cizKtkjssaCsV1E4asBP6+r
TUqTYQIhAOPZEmPDeUq45f6KrHqhgZQw2HThIwhPVFAzxxFtPk6M
-----END CERTIFICATE REQUEST-----
is shorter than the passed CSR below:
-----BEGIN CERTIFICATE REQUEST-----
MIIBFDCBugIBADAxMRswGQYDVQQDDBJwYXJhbGxheHNlY29uZC5jb20xEjAQBgNV
BAUMCUVaNFUyQ0lYTDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFKFxB9RJfvx
rQlb8rRIh1tPAK5yW6/ycXKJVpFFkkk43NNJpHUmUZKSagGBZPAKIItCDnnCMxLz
qBogAsYjfWugJzAlBgkqhkiG9w0BCQ4xGDAWMBQGA1UdEQQNMAuCCWxvY2FsaG9z
dDAKBggqhkjOPQQDAgNJADBGAiEA/GXlRImox5nNyLZY3g73SjWo5UoWrMuxEEXQ
YqgYeGYCIQCdDLos2E6HIWv/rk0lz0OwXjgS/86Lcd+MMC8Ff2C4tw==
-----END CERTIFICATE REQUEST-----
test_signing()
case:test_csr()
case.May I please ask some helps from the parsec experts? Thanks very much!
Feature request:
Some platforms are very slow computing RSA 2048 bits keys (RSA or ECC) or simply not capable of generating the default where the test fails, Is it possible to have the option to override in parsec-cli-tests.sh
to use a different key length bits to enable testing?
Depending on the platform, if we want to cross-verify the current configuration of the parsec-service, Is there a way to have a command option to read out the current config parameters set for the parsec-service?
If there are any passwords/pins in the config.toml, probably those can be encrypted as done by /dev/random?
When using create-rsa-key
, whether for signing or encryption, the key strength is hard-coded to 2048 bits within the tool. This is a sensible default, which will need to be maintained for backwards compatibility, but it should also be possible to specify explicitly on the command-line.
--strength
or --bits
.Based on this
Use the bootstrapping feature to not have to select auth type and provider.
Some commands:
should not need an authenticator. At the moment they do because they are executed through the BasicClient which does automatic authenticator selection.
Maybe the automatic authenticator selection should only be done in the Subcommand::run
method for the subcommands that need it.
Summary
The create-csr
command does not support the serialNumber
attribute as part of the Distinguished Name (DN). We should add this option on the command line so that CSRs can identify individual devices for zero-touch onboard scenarios.
Requirements
--serialNumber
to the create-csr
command. Following the existing pattern of DN components, the long parameter name is the same as the attribute in the X509 spec, and there is deliberately no short version.--serialNumber
is supplied on the command line, the string value should be added to the DN in the CSR.rcgen
crate does not have a DnType::SerialNumber
category, so it will be necessary to use DnType::CustomDnType
and supply an explicit Object ID (OID), which is [2, 5, 4, 5]
according to https://www.alvestrand.no/objectid/2.5.4.5.htmlPlaceholder issue to investigate how to define stability for the Parsec Tool and how to enforce it.
cc @ruchi393
Hi,
We're testing our implementation of PKCS11 library with parsec
(service) (0.7.2
tag) and parsec-tool
(0.3.1
tag) and seeing a couple of issues.
# parsec-tool create-ecc-key -k tutu
[ERROR parsec_service::providers::pkcs11::key_management] The PKCS11 provider currently only supports creating RSA key pairs.
[ERROR] Subcommand failed: the requested operation or a parameter is not supported by this implementation (ParsecClientError(Service(PsaErrorNotSupported)))
But using pkcs-tool
, we verified that we're able to create ECC keys. Any ideas what might be wrong with the above please?
# pkcs11-tool --token-label test-token --pin 123456 --keypairgen --key-type EC:secp384r1 --id 2 --label ecc-key1
Key pair generated:
Private Key Object; EC
label: ecc-key1
ID: 02
Usage: sign, derive
Access: sensitive, always sensitive, never extractable, local
Public Key Object; EC EC_POINT 384 bits
EC_POINT: 046104feed6d490bf1f3662f7f06a9c1fc2b1490e2661aa4a91df019781a9f2c55a052338101ffb33deb1a5ff793bbdecc6f43779297b49f33780792a557ee55dab78922b52b5f29efb30900f801c6f89c0b221439c5c0449f96e4cb7c56222778339f
EC_PARAMS: 06052b81040022
label: ecc-key1
ID: 02
Usage: verify, derive
Access: local
# parsec-tool create-rsa-key -k toto
# parsec-tool sign -k toto "Sign this!"
[ERROR] Key's algorithm is AsymmetricEncryption(RsaPkcs1v15Crypt) which can not be used for signing.
[ERROR] Subcommand failed: They key was not created with the correct algorithm for this operation (ParsecToolError(WrongKeyAlgorithm))
I'm not very well-versed in Rust code but looking at https://github.com/parallaxsecond/parsec-tool/blob/0.3.1/src/subcommands/sign.rs, is it right that only signing with ECC keys is supported atm?
Add a new command to the tool that can output a complete, self-signed X509 certificate based on a private signing key.
The following recent PR introduced the create-csr
command, which can create a Certificate Signing Request (CSR) from a private key: #68
The tool uses the rcgen
crate to make the CSR.
This crate is also capable of making complete self-signed certs as well as CSRs, and it might be handy to also add this facility to parsec-tool
.
Much of the implementation of the existing create-csr
command could possibly be copied or refactored in order to output a self-signed cert instead of a CSR. Some of the command-line inputs would also be the same, although there would also be the need for additional inputs because certs have more fields than CSRs have.
Automated tests would also be required in the CLI test script.
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.