Giter VIP home page Giter VIP logo

aes's Introduction

AES algorithm implementation by ABAP

ABAP Utilities for AES encryption, decryption under MIT License.

Actual Implementation is done by the more generic Rijndael way, and AES is treated as a special case.

Supporting:

  • Encryption mode: ECB, CBC, PCBC, CFB, OFB, CTR.
  • Padding standard: None, PKCS #5, PKCS #7. (Please use PKCS #7, No Padding and PKCS #5 do not work well with AES, unless you know clearly what they are doing.)

Please just copy the source code file into ABAP editor (Source Code-Based mode), and activate it. Or install via abapGit.

Classes:

  • ZIF_AES_MODE: Interface for different encryption mode.
  • ZCL_AES_MODE_CBC: CBC mode.
  • ZCL_AES_MODE_CFB: CFB mode.
  • ZCL_AES_MODE_CTR: CTR mode.
  • ZCL_AES_MODE_ECB: ECB mode.
  • ZCL_AES_MODE_OFB: OFB mode.
  • ZCL_AES_MODE_PCBC: PCBC mode.
  • ZCL_BYTE_PADDING_UTILITY: Abstract class for Byte padding utilities, including factory method to get concrete class instances.
  • ZCL_PADDING_UTILITY_NONE: No padding.
  • ZCL_PADDING_UTILITY_PKCS_5: Padding using PKCS #5.
  • ZCL_PADDING_UTILITY_PKCS_7: Padding using PKCS #7.
  • ZCL_RIJNDAEL_UTILITY: implementation of Rijndael, encrypt and decrypt using xstring.
  • ZCL_AES_UTILITY: AES wrapper on ZCL_RIJNDAEL_UTILITY, just need to provide key and data.
  • ZCL_AES_UTILITY_TEST:

aes's People

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

Watchers

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

aes's Issues

Working Example

Hi, are there any quick ABAP usage examples with this library anywhere? Am keen to give it a try, but wondering where to start... Thanks :)

Encryption Issue

Hi,
I find this code really useful but am struggling to understand where I am going wrong.
I am trying to encrypt with the following details

PKCS5 PAdding.
Cipherkey - 5545427A64334A6B49314E555255564D

I have written code as follows

`CALL METHOD zcl_aes_utility=>encrypt_xstring
    EXPORTING
      i_key              = lv_cipherkey
      i_data             = lv_plaintext
*      i_initialization_vector = i_iv
      i_padding_standard = zcl_aes_utility=>mc_padding_standard_pkcs_5
      i_encryption_mode  = zcl_aes_utility=>mc_encryption_mode_ecb
    IMPORTING
      e_data             = lv_ciphertext.`

  CALL FUNCTION 'SCMS_BASE64_ENCODE_STR'
    EXPORTING
      input  = lv_ciphertext
    IMPORTING
      output = lv_encrypted.

My plain text is '7B227472616E73616374696F6E4944223A22333039323230313031353739313739343330393730222C22737461747573223A2253756363657373222C227061796D656E744279223A225341505F46554E4331222C22636F6E74726163744E6F223A2247454D432D353131363837373330393232373530222C2267656D496E766F6963654E6F223A2247454D2D3634303139222C22696E766F6963654E6F223A223736746768222C2262696C6C4E6F223A223531313638373733303932323735302D334231222C2262696C6C416D6F756E7450616964223A22373330302E3030222C227472616E73616374696F6E44617465223A22323032302D30312D3136222C226465647563746564416D6F756E74223A22302E303020222C22646564756374696F6E54797065223A22222C2262616E6B4E616D65223A224943494349222C226368657175654E756D626572223A22222C2262616E6B5472616E73616374696F6E4E6F223A22313233343332222C2264656D616E6444726166744E6F223A22222C2273616E6374696F6E4E6F223A22222C2273616E6374696F6E44617465223A22227D'

However, when i get the encoded string, it shows as below
IUEgq2A30lHwd/tf97TBvuiYKKPj5XjmGwLru5ueFBqdAkZm7M7Di0poHr1FObd2MqEcR/GJvPrcF8hw3rpFQq0SV9tdXP1wctG4s5uf0tppraJHXSI1Vluq/FHhdTHVvxvseg4bQ1671DYIW5tFJ0HCRpm79T8d9XAycgqmb6E4US7v5NP5+420NPIwpQGWDqUV9e8/kdzAhUtTjXZmY8HRS7T5WUgL2pkc6zWbFGKoKrJ9M/TP0IF4puI0oBrFUzmi4ipP74gSo4K+1NvwUhK9pgWXXLggvOyCj62Xu7Gz8r177mR2rMLUQVgI5RmM+yO3lAFUeFyPw8plWo74aTdkdejtBx7VeLlrvi1pXD+m6X7/uasr7AgfRaIlY9sHxqvId11v5zURY7pEVWEF6oODp1bh8o3SKpHhX74KUONJkmEVeVXvuB+QHggJnWNFVzPCg9AOOH8F9BhgY+L5b5gyUnsxrEuioSpamY7WXarQYey8qK+039R+JmZUUW1OwWecPJBCp5gtyIDuiM5FK1IlL7vQaKyv/IHSrCLM+c/iS2Gm2XCuwD1r4Hr/U4Bo

And when i use online tools like https://www.devglan.com/online-tools/aes-encryption-decryption, i get
IUEgq2A30lHwd/tf97TBvuiYKKPj5XjmGwLru5ueFBqdAkZm7M7Di0poHr1FObd2MqEcR/GJvPrcF8hw3rpFQq0SV9tdXP1wctG4s5uf0tppraJHXSI1Vluq/FHhdTHVvxvseg4bQ1671DYIW5tFJ0HCRpm79T8d9XAycgqmb6E4US7v5NP5+420NPIwpQGWDqUV9e8/kdzAhUtTjXZmY8HRS7T5WUgL2pkc6zWbFGKoKrJ9M/TP0IF4puI0oBrFUzmi4ipP74gSo4K+1NvwUhK9pgWXXLggvOyCj62Xu7Gz8r177mR2rMLUQVgI5RmM+yO3lAFUeFyPw8plWo74aTdkdejtBx7VeLlrvi1pXD+m6X7/uasr7AgfRaIlY9sHxqvId11v5zURY7pEVWEF6oODp1bh8o3SKpHhX74KUONJkmEVeVXvuB+QHggJnWNFVzPCg9AOOH8F9BhgY+L5b5gyUnsxrEuioSpamY7WXarQYey8qK+039R+JmZUUW1OwWecPJBCp5gtyIDuiM5FK1IlL7vQaKyv/IHSrCLM+c/6S/NxlgIM1Q1HZM4XlL0W

If you see, the encoded result is same except for the last 21 characters. I do not know how to the the last 21 characters to match the result from devglan site.

Perhaps something wrong with padding which I am unable to identify. I've been thinking about this in my sleep lol.

Before calling the encrypt, i do the following

`  CALL FUNCTION 'SCMS_STRING_TO_XSTRING'
    EXPORTING
      text     = lv_key
      encoding = '4110'
    IMPORTING
      buffer   = lv_cipherkey
    EXCEPTIONS
      failed   = 1
      OTHERS   = 2.
  IF sy-subrc <> 0.
*    gv_er_msg = 'Error while Binary conversion of key'(010).
*    RAISE EXCEPTION TYPE cx_alert_cat_exists.
  ENDIF.`

` CALL FUNCTION 'SCMS_STRING_TO_XSTRING'
    EXPORTING
      text   = lv_paydata
      encoding = '4110'
    IMPORTING
      buffer = lv_plaintext
    EXCEPTIONS
      failed = 1
      OTHERS = 2.
  IF sy-subrc <> 0.
*    gv_er_msg = 'Error while Binary conversion of key'(010).
*    RAISE EXCEPTION TYPE cx_alert_cat_exists.
  ENDIF.`

Can you please guide me as to what I might be missing?

Thank you.
S

Last bytes of AES encryption result is not matching with online tools

my string:
{"transactionID":"175738861574757193070","status":"SUCCESS","paymentBy":"INTERNET BANKING","contractNo":"GEMC-11687717573886","gemInvoiceNo":"GEM-61550","invoiceNo":"3131","billNo":"511687717573886-1B1","billAmountPaid":"391108.00","transactionDate":"2020-03-02","deductedAmount":"0","deductionType":"LD/TDS","bankName":"SBI","chequeNumber":"","bankTransactionNo":"SBIUATTEST1234","demandDraftNo":"","sanctionNo":"","sanctionDate":""}

my key: qcNv4GRqj40WdqhY

iam using ECB128/PKCS5PADDING

zcl_aes_utility=>encrypt_xstring(
exporting
i_key = i_key_xstring
i_data = i_xstring
i_padding_standard = ZCL_PADDING_UTILITY_PKCS_5=>MC_PADDING_STANDARD_PKCS_5
i_encryption_mode = zcl_aes_utility=>mc_encryption_mode_ecb "mode
importing
e_data = lv_message ).

iam getting:
HmS6lVtVWBJRkPozvNUFU4uF7/ihCQT64oE3TaMF2pqGW4MK1Kr+bq2IaxVOVGCwe72sotxVC+H88qeAQXnUpCdVlUFUsWg7d73vqnw7IbUhSJnBQ+7mfhNz2FdSuxGfyAIzlxMJBxSP0Lz1gF5m98Xd1VdUpivc1GvxacrLqRWmm+TIr63nm2WzSiR3WE8P2Oy+gK/VIrInDaliVEnPbsu0Tfpd1+0RX85AiAcRsh4xpUEpM1f2e0lVCfDcrd3ooy5CEjHGMLUjImIix3rMCwyv4dBV1YDGPWiUd4X+Nedd+lh1+ROk5gQtlw2juCipxhXFFgIA0sfHuHJRACm53oJWTm45S8rbBFs6ygDaDK0lMe/DfAT1lvhwIVG69X5S6t8EVpL1RTmmFINX4b+zsbDnlV1dfBfUXsnX+wjDWgEo1Tk1TTKoo6tRHunAQs8ZmK31lx4oxl5SD5QQ2uwKg7zu38beXTUAYTXDim5qyRHZ536VnN7iXP30FGpHaUIMHvxIb7G5DnBURGIafOYotaKQ8W8wUo2fYOd3GMFyHja1iaLVavr/BttVTOrvqDKlFw7tLK//nTTqBqGNA9nTNg==

whereas online tool at [https://www.devglan.com/online-tools/aes-encryption-decryption] is showing:
HmS6lVtVWBJRkPozvNUFU4uF7/ihCQT64oE3TaMF2pqGW4MK1Kr+bq2IaxVOVGCwe72sotxVC+H88qeAQXnUpCdVlUFUsWg7d73vqnw7IbUhSJnBQ+7mfhNz2FdSuxGfyAIzlxMJBxSP0Lz1gF5m98Xd1VdUpivc1GvxacrLqRWmm+TIr63nm2WzSiR3WE8P2Oy+gK/VIrInDaliVEnPbsu0Tfpd1+0RX85AiAcRsh4xpUEpM1f2e0lVCfDcrd3ooy5CEjHGMLUjImIix3rMCwyv4dBV1YDGPWiUd4X+Nedd+lh1+ROk5gQtlw2juCipxhXFFgIA0sfHuHJRACm53oJWTm45S8rbBFs6ygDaDK0lMe/DfAT1lvhwIVG69X5S6t8EVpL1RTmmFINX4b+zsbDnlV1dfBfUXsnX+wjDWgEo1Tk1TTKoo6tRHunAQs8ZmK31lx4oxl5SD5QQ2uwKg7zu38beXTUAYTXDim5qyRHZ536VnN7iXP30FGpHaUIMHvxIb7G5DnBURGIafOYotaKQ8W8wUo2fYOd3GMFyHja1iaLVavr/BttVTOrvqDKlKfxb5PoFsX2FsS1vGmTUVQ==

we need to send an encrypted text to a destination, where message will be decrypted, processed and response will be sent back.
encrypted message through zcl_aes_utility was showing an error at receiver side.
encrypted message through online tool has worked properly as expected.

difference between above 2 encrypted messages is last 24 characters.
Please let me know if iam missing anything during encryption

Base class ZCL_RIJNDAEL_UTILITY might have problems encrypting data after first block

Seems the ZCL_RIJNDAEL_UTILITY not always generate the same data for all the blocks

For example:
Key:
000102030405060708090A0B0C0D0E0F

PlainText:
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000

CipherText:
C6A13B37878F5B826F4F8162A1C8D879
103F26CFB99ADF9E48C8ACD130A0A4FD
103F26CFB99ADF9E48C8ACD130A0A4FD
103F26CFB99ADF9E48C8ACD130A0A4FD

Seems the initilization round of later blocks are missing or something.

However, the ZCL_AES_UTILITY class is using only the first block, so it is still good to use.

Renaming classes

Hi @Sumu-Ning,
I would like to rename the classes so everything is prefixed with "ZCL_AES_", my suggestion:

rename ZCL_BYTE_PADDING_UTILITY to ZCL_AES_PADDING
rename ZCL_PADDING_UTILITY_NONE to ZCL_AES_PADDING_NONE
rename ZCL_PADDING_UTILITY_PKCS_5 to ZCL_AES_PADDING_PKCS_5
rename ZCL_PADDING_UTILITY_PKCS_7 to ZCL_AES_PADDING_PKCS_7

rename ZCL_RIJNDAEL_UTILITY to ZCL_AES_RIJNDAEL

Some "garbage" as suffix left after decrypting via AES-CBC

Hi, thanks for adding the CBC support to your library, now I can successfully decrypt AES-encrypted strings (in base64) coming from OpenSSL. There seems to be a bug though somewhere. I do the following steps:

First, I get the key and init. vector for the password from OpenSSL:

openssl aes-256-cbc -nosalt -P -pass pass:unserGeheimesPasswort

Then I encrypt a string using that same password:

echo geheime_Kundendaten | openssl enc -aes-256-cbc -a -nosalt -pass pass:unserGeheimesPasswort

In ABAP I decode that encrypted base64-encoded string:

CALL FUNCTION 'SCMS_BASE64_DECODE_STR'
  EXPORTING
    input    = p_data  "base64-encoded, encrypted AES string: geheime_Kundendaten
    unescape = 'X'
  IMPORTING
    output   = lv_x_data.

Then I decrypt that string via your class:

CALL METHOD lr_aes_util->decrypt_xstring(
  EXPORTING
    i_data                  = lv_x_data
    i_key                   = lv_x_key
    i_initialization_vector = lv_x_iv
    i_encryption_mode       = 'CBC'
  IMPORTING
    e_data                  = lv_x_result ).

That method call returns the correctly decrypted string, which I have encrypted via OpenSSL before. However, there are some additional bytes as suffix. In the ABAP debugger the value of lv_x_result is

67656865696D655F4B756E64656E646174656E200D0A0A0A0A0A0A0A0A0A0A0A

which, after converting the xstring to binary via SCMS_XSTRING_TO_BINARY and from binary to string via SCMS_BINARY_TO_STRING results in

geheime_Kundendaten ############

So the first part is correct, but these trailing ############ should not be there. When I remove all the 0D0A bytes (Carriage Return / Line Feed) from the xstring, then the result is correct.

Now, is this a bug in your code, or do I need an additional step to decrypt AES-CBC correctly?

abapGit repository format

Hi,
I am working on implementing OpenPGP in ABAP

https://github.com/larshp/abapPGP
https://tools.ietf.org/html/rfc4880

And will eventually need AES encryption, and I have found your nice AES library 👍

In order to make it easier to install, have you considered using abapGit, and will you accept a pull request which changes the format to abapGit format?

You can take a look at https://github.com/larshp/abapPGP for how abap code in abapGit format looks like in a git repository.

Exception CX_ME_ILLEGAL_ARGUMENT

suggest creating custom exception class instead of reusing CX_ME_ILLEGAL_ARGUMENT

The exception is from package "Mobile: Core Components", application component BC-MOB. By having a custom exception class we can remove this dependency

How to use with ABAP strings?

I am really interested in your library, but I am having a hard time using it with normal ABAP string variables. Converting from and to xstring basically works, but I cannot decrypt a string that have been encrypted via OpenSSL.

Do you have any examples on how to use your library with OpenSSL-encrypted strings?

Decrypt file from SAP app.server

I have a requirement where I must decrypt a file that in SAP that has been encrypted with aes-256-cbc protocol. The file is encrypted with a fixed KEY and IV. I've already implemented all classes from this Git repo, test class also works fine so basically I'm ready to go. Unfortunately I haven't come to the point where I can start using the classes to really decrypt the data yet.

I'm able to decrypt the file via openSSL, so I'm sure it's possible to successfully decrypt the file. However, in ABAP I'm quite unsuccessful until now, probably because I'm completely new to encryption/decryption and still quite new to ABAP too. When I try to read the data from the file (file is placed on the app.server from where I should read it), I open a dataset in text mode with non-unicode encoding. I read the data into a variable lv_data which has type string (see code snippet below).

TRY.
      OPEN DATASET lv_filepath FOR INPUT IN TEXT MODE ENCODING NON-UNICODE.

      DO.
        READ DATASET lv_filepath INTO lv_str.
        IF sy-subrc NE 0.
          EXIT.
        ENDIF.
        lv_data = |{ lv_data }{ lv_str }|.
      ENDDO.

    CATCH cx_root.
  ENDTRY.

When I open the encrypted file with notepad it looks like this:
image

When the data is read into parameter lv_data (type string) in SAP it looks like this. Could it be that I'm messing something up with the data types?
image

Any help is much appreciated, thanks a lot in advance!

Use built-in ABAP types instead of DDIC

Suggest using built-in types instead of referring to DDIC data elements, this will make sure the code works across more systems, following are candiates to be changed

DTEL            BOOLE_D
DTEL            CHAR10
DTEL            HEXTYP
DTEL            INT1
DTEL            INT4

use abap_bool, type c length 10, type x length 1 etc., if needed the types can be defined in a interface

Help Is appreciated

Hi,

I'm new to ABAP, appreciate if you can help to pen down what needs to be done to have AES 128 CBC encryption? There are many class file, do I have to create all in SAP? Do you have sample program to do encryption?

Thanks in advance

Bug in decryption algorithm?

Hi, I have a string encrypted via OpenSSL command line:

echo "0075266945" | openssl enc -aes-256-cbc -a -nosalt -pass pass:TUA_DKK_CCIP_PW
H6xgbzDoj091wZjAj+mvKA==

This encrypted string "H6xgbzDoj091wZjAj+mvKA==" is stored in an ABAP string variable. According to a previous ticket here on GitHub I do the following: First call function SCMS_BASE64_DECODE_STR like so:

"Convert payload from Base64 to Hex
CALL FUNCTION 'SCMS_BASE64_DECODE_STR'
  EXPORTING
    input    = i_encrypted_data
    unescape = 'X'
  IMPORTING
    output   = lv_x_data.

Then I decrypt the string via your tool:

"Then decrypt the payload using the encryption key and init. vector, all in xstring format
CALL METHOD lr_aes_util->decrypt_xstring(
  EXPORTING
    i_data                  = lv_x_data
    i_key                   = lv_x_key   "CAE47A6067F0F2DCC728AF877530529038035761C89E0636DB043A6FE6815F04
    i_initialization_vector = lv_x_iv    "4BBF93F6535EC0AD41CBDEB346719675
    i_encryption_mode       = i_encryption_mode
  IMPORTING
    e_data                  = lv_x_result ).

Then I remove the trailing CR/LF bytes:

  DO.
    lv_len = xstrlen( lv_x_result ) - 1.
    IF lv_len = -1.
      EXIT.
    ENDIF.
    IF lv_x_result+lv_len(1) <= lc_byte_ws.
      "remove trailing non-printable character
      lv_x_result = lv_x_result(lv_len).
    ELSE.
      EXIT.
    ENDIF.
  ENDDO.

And the last steps:

"Finally convert the decrypted binary xstring back to a string again
CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
  EXPORTING
    buffer        = lv_x_result
  IMPORTING
    output_length = lv_len
  TABLES
    binary_tab    = lt_data.

CALL FUNCTION 'SCMS_BINARY_TO_STRING'
  EXPORTING
    input_length = lv_len
  IMPORTING
    text_buffer  = lv_result
  TABLES
    binary_tab   = lt_data.

MOVE lv_result TO r_decrypted_data.

However, now in r_decrypted_data the wrong data is stored: MF¬ö�í«a6Š©w
Is this a bug in your library, or am I doing something wrong here?

Incorrect padding x00 , it should be PKCS#7

The padding used x00 up to the block boundary is incorrect. AES CBC is supposed to use PKCS#7 padding. There is actually a chance of losing data when removing x00 based on this padding approach apara from the fact that it is the wrong padding.

The pkcs 7 padding in simple terms is:
bytes needed to fill last block / padding to use
01
0202
030303
04040404
etc to
0E............0E for padd 15 bytes
and
importantly
of the plain text actually plain hex is a better description is a multiple of the block size
and although no padding would appear to be necessary, a complete extra block is required with
1010101010......10 16 times for 16 byte block size

This is important if 2 systems are to share messages successfully. The Padding needs to be consistent and correct.

Public methods in ZCL_AES_UTILITY

Following are the public methods in class ZCL_AES_UTILITY,

IS_VALID_KEY_XSTRING
IS_VALID_IV_XSTRING
ENCRYPT_XSTRING
DECRYPT_XSTRING
ENCRYPT_RAW16_TABLE
DECRYPT_RAW16_TABLE
CONVERT_XSTRING_TO_RAW16_TABLE
CONVERT_RAW16_TABLE_TO_XSTRING
VALIDATE_ENCRYPTION_MODE
VALIDATE_PADDING_STANDARD
VALIDATE_RAW16_TABLE_SIZE
ADD_PADDING_RAW16_TABLE
REMOVE_PADDING_RAW16_TABLE

I guess only ENCRYPT_XSTRING and DECRYPT_XSTRING is used by applications, so I suggest changing the rest to private.

Introduce factory class

Hi,
Another idea, what do you think about introducing a factory class eg class ZCL_AES_FACTORY with 2 methods:

CREATE_MODE returns reference to ZIF_AES_MODE

  • would implement the functinoallity from ZCL_AES_UTILITY=>GET_AES_MODE

CREATE_PADDING returns reference to new interface ZIF_AES_PADDING

  • would implement the same as methods GET_BYTE_PADDING_UTILITY + VALIDATE_PADDING_STANDARD from ZCL_BYTE_PADDING_UTILITY, making it easy to change ZCL_BYTE_PADDING_UTILITY to an interface instead
  • and the singleton functionallity from ZCL_AES_UTILITY=>GET_PADDING_UTILITY

This would also change ENCRYPT_XSTRING so it takes a reference to ZIF_AES_MODE and ZIF_AES_PADDING instead of the constants.

Advantages:

  • The responsibility for creating objects are moved to a new class
  • ZCL_BYTE_PADDING_UTILITY is changed to an interface instead of a superclass, which will make it similar to ZIF_AES_MODE
  • ENCRYPT_XSTRING will take references to objects, which will make it easier to test new padding logic and modes

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.