Git Product home page Git Product logo

ring's Introduction

THE SOFTWARE IS PROVIDED "AS IS" AND BRIAN SMITH AND THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL BRIAN SMITH OR THE AUTHORS BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

ring

ring is focused on the implementation, testing, and optimization of a core set of cryptographic operations exposed via an easy-to-use (and hard-to-misuse) API. ring exposes a Rust API and is written in a hybrid of Rust, C, and assembly language.

Particular attention is being paid to making it easy to build and integrate ring into applications and higher-level frameworks, and to ensuring that ring works optimally on small devices, and eventually microcontrollers, to support Internet of Things (IoT) applications.

Most of the C and assembly language code in ring comes from BoringSSL, and BoringSSL is derived from OpenSSL. ring merges changes from BoringSSL regularly. Also, several changes that were developed for ring have been contributed to and integrated into BoringSSL.

Documentation

See the documentation at https://docs.rs/ring/latest/ring/.

See BUILDING.md for instructions on how to build it. These instructions are especially important for cross-compiling and for building on Windows when not building from crates.io, as there are build prerequisites that need to be installed.

Benchmarks

ring's benchmarks are located in the bench folder of this repository. Because there is lots of platform-specific code in ring, and because ring chooses dynamically at runtime which optimized implementation of each crypto primitive to use, it is very difficult to publish a useful single set of benchmarks; instead, you are highly encouraged to run the benchmarks yourselves on your target hardware.

Contributing

The most important contributions are uses of ring. That is, we're very interested in seeing useful things built on top of ring, like implementations of TLS, SSH, the Noise Protocol, etc.

The ring project happily accepts pull requests. The portions of pull requests that modify existing files must be licensed under the same terms as the files being modified. New files in pull requests, including in particular all Rust code, must be licensed under the ISC-style license. Please state that you agree to license your contributions in the commit messages of commits in pull requests by putting this at the bottom of your commit message:


I agree to license my contributions to each file under the terms given
at the top of each file I changed.

Minimum Supported Rust Version (MSRV)

ring is tested on the latest Stable, Beta, and Nightly releases of Rust, as well as the oldest version known to work according to the tests run in CI. That oldest version known to work is documented as the MSRV in Cargo.toml.

Bug Reporting

Please see SECURITY.md for help on reporting security vulnerabilities.

Please report bugs that aren't security vulnerabilities either as pull requests or as issues in the issue tracker.

License

See LICENSE.

ring's People

Contributors

achernya avatar agl avatar andres-erbsen avatar andrewtj avatar briansmith avatar codebytere avatar davidben avatar djc avatar dmcardle avatar doughd avatar dvorak42 avatar ecstatic-morse avatar frewsxcv avatar ghedo avatar haavardmolland avatar illicitonion avatar joshlf avatar kreichgauer avatar llogiq avatar nharper avatar nopsledder avatar nornagon avatar pietro avatar piotrsikora avatar prbprbprb avatar ranweiler avatar samscott89 avatar shishkander avatar varomodt avatar vkrasnov 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ring's Issues

OPENSSL_cleanse should use memset_s

WE should juse use memset_s. If necessary, we can supply a default implementation of memset_s for platforms that don't provide it, if any. Alternatively, we can drop support for older versions of compilers that don't supply it.

void OPENSSL_cleanse(void *ptr, size_t len) {
#if defined(OPENSSL_WINDOWS)
    SecureZeroMemory(ptr, len);
#else
    memset(ptr, 0, len);

#if !defined(OPENSSL_NO_ASM)
  /* As best as we can tell, this is sufficient to break any optimisations that
     might try to eliminate "superfluous" memsets. If there's an easy way to
     detect memset_s, it would be better to use that. */
  __asm__ __volatile__("" : : "r"(ptr) : "memory");
#endif
#endif  /* !OPENSSL_NO_ASM */
}

Remove all casts that use the `as` operator

Rust doesn't have implicit widening, so casts that are lossy (not necessarily safe) look just like casts that are 100% safe. Instead of using the as operator for widening casts, we should use (and create, if necessary) functions that safely do widening conversions, and use those. Then the use of as would be limited to only those functions.

This depends on #4.

Add analogs to rustdoc search index

Most ring features' documentations has a "C analogs" section that documents what C API feature is analogous to the Rust API feature. Soon we'll have similar sections mapping the golang and JavaScript WebCrypto APIs to the ring API.

The rustdoc search feature doesn't seem to index these cross-references. We should make some automated tool to make that indexing happen.

Building on Mac OSX

Currently, the build exits with errors on OS X.
In trying to get this to build, so far I have changes in the mk\ring.mk:
...
-$(RING_LIB): ARFLAGS = cDrs
+$(RING_LIB): ARFLAGS = crs
...
-PERLASM_x86_ARGS = elf -fPIC -DOPENSSL_IA32_SSE2
-PERLASM_x86_64_ARGS = elf
+PERLASM_x86_ARGS = macosx -fPIC -DOPENSSL_IA32_SSE2
+PERLASM_x86_64_ARGS = macosx
...

The first is because there is not a deterministic flag for ar on OSX, the second to build to the Mac platform.

Also, apparently --gc-sections is deprecated now on OSX, I saw it mentioned[1] that -dead_strip should replace it.
mk/top_of_makefile.mk
-LDFLAGS += -Wl,--gc-sections
+LDFLAGS += -Wl,-dead_strip

[1] http://stackoverflow.com/questions/24734409/make-error-in-mac-clang-ld-unknown-option-gc-sections

After making those changes, I was able to successfully build (x86 and x86_64) on OSX and get a 'PASS' on the various tests I tried in the bin/crypto folder.

-cem

Add `ring::hmac::verify_with_own_key`

Looking at badboy/nobsign#1, it is obvious that the HMAC API is a little off, in the case where HMAC is being used to sign something we created, pass it around, get it back, and then verify that the thing was something created by us. In particular, ring's current HMAC API is optimized for protocols like SSH and TLS where you we verify things signed by another party, and where we want to make sure we don't use use the same key for signing stuff we send that we use for verifying stuff that the other party sends.

I think we can improve the API here by simply adding:

pub fn verify_self_signature(key: &SigningKey, data: &[u8], sig: &[u8])

It would be just like ring::hmac::verify, except taking a SigningKey instead of a VerificationKey. The name would make it clear that this is to only be used for self-signatures.

I wonder if the AEAD interface needs something similar.

Simplify `BN_mod_inverse_ex`

It may be the case that only one of the two implementations is necessary. In theory the binary inverse algorithm should be faster for smallish N, but it is probably worth verifying at some point that this implementation of it actually is faster in some important situation (platform * algorithm). And/or it is worth considering whether the code needs to support even N, or whether it can restrict itself to odd N only so that the binary inversion can always be used. I have a faster implementation of binary inversion sitting around somewhere, so the latter may be a preferable solution. Either way, it would be good for code size to remove one of the implementations.

LINK : warning LNK4098: defaultlib 'MSVCRTD' conflicts with use of other libs; use /NODEFAULTLIB:library

The appveyor build logs report this. This doesn't seem to have any adverse effects, but we should fix it.

LINK : warning LNK4098: defaultlib 'MSVCRTD' conflicts with use of other libs; use /NODEFAULTLIB:library

Probably the best route is to make a configuration parameter that mk/Windows.props observes for overriding the default runtime library selection.

Note that Rust always links to MSVCRTD.

Ensure we have all of BoringSSL's and OpenSSL's signature test vectors

The BoringSSL EVP signature verification tests were removed because they depended on crypto/pem, which was removed, and because we didn't have anything like crypto/test/file_tests.cc. However, now we have file_tests.rs, and libwebpki's tests in signed_data.rs have a PEM parser in Rus tthat we can use for this, so it should be easy to resurrect the BoringSSL test cases.

As the BoringSSL tests only cover a few scenerios, we should probably add additional cases. It may be a good idea to copy/move some of the non-x.509-specific test cases from libwebpki's signed_data.rs to ring.

Add negative test vectors for ECDSA

After the removal of ecdsa_test.cc, we won't have any test vectors testing, e.g. verification using the wrong key. We should add such test vectors back in, using the Rust testing framework.

Fix assertions on `Result` values

From the Rust coding guidleines:

// Extract the contents of Ok variant; fail if Err
fn assert(self) -> V

// Extract the contents of Err variant; fail if Ok
fn assert_err(self) -> E

The code has been using assert(x.is_ok()) and assert(x.is_err()) instead.

Remove RSA blinding code

I asked Adam Langley why BoringSSL still has the RSA blinding code even though the BoringSSL RSA code is supposed to be constant-time. The response was "I don't think that I'm confident enough in the constant-timeness of the rest of the RSA code to remove blinding." Especially given experience with the ECC code, I agree with him for the time being. But, we should find a way to become confident (fixing the code if necessary) that the constant-time RSA implementation is constant time. Then we should remove the blinding code.

Improve C/C++ hardening build configuration

e.g. Control Flow Integrity, stack protection, etc.

It would be worth having the build system run one of the scanning tools to check that these flags are all set correctly.

Linkify cross-references in rustdoc

Currently rustdoc doesn't linkify cross-references that are done by mentioning an item with backtick quotes. Until rustdoc supports that feature, we should just do the linkification ourselves.

Add scrypt

https://www.tarsnap.com/scrypt.html.

In particular, we should provide an scrypt implementation that can implement the Android FDE cryptosystem. See https://source.android.com/devices/tech/security/encryption/#storing_the_encrypted_key, which describes the updates from http://nelenkov.blogspot.com/2014/10/revisiting-android-disk-encryption.html.

Note that Android's FDE seems to require outputting TWO blocks, but currently we only support outputting ONE block. So, we'll probably have to extend the PBKDF2 implementation to support larger outputs.

Remove libc dependency

Looking at the proposed new libc and considering our current usage, it becomes pretty obvious we don't need to depend on the libc crate and we're better off not doing so. Our needs are so simple we can get away with just defining a couple of types in ring::ffi. If we need a more advanced thing, then we can look at rust-c99.

Test cases where pbkdf2_hmac panics

Currently there are some test cases in pbkdf2_tests.txt that are commented out because they are invalid inputs for ring's PBKDF2 module. In particular, they request an output longer than the output length of the the digest function being used. We should turn those test cases into the equivalent of [should_panic]. AFAICT, this means we need to:

  1. Uncomment those test cases.
  2. Annotate those test cases with "FAILS: OUTPUT_LARGER_THAN_DIGEST_OUTPUT".
  3. Change the testing code so that for the cases marked "FAILS: OUTPUT_LARGER_THAN_DIGEST_OUTPUT", a task gets spawned that does the test case. We have to wait for the task to end and then verify that it was ended due to a panic!. In other words, we need to create the equivalent of a [should_panic] test.

Additionally, we should add more test cases, particularly a test case for iterations == 0, as BoringSSL has (had) a bug when interations == 0.

AFAICT, this is complicated by rust-lang/rust#25869, as I believe we cannot do [should_panic] tests or create a data-driven alternative to [should_panic] on Win32 since Rust on Win32 doesn't implement panic! correctly; `panic!`` just kills the entire process. We should probably just work around that by not running these tests on Win32.

Take inputs to parse as `untrusted::Input` values.

libwebpki has the Input/Reader framework in webpki::input that is used to safely parse input. Factor that framework out of libwebpki so that ring can use it. Then, all inputs that need to be parsed should bed passed as instances of Input or Reader, instead of &[u8] as they are now.

Document why *ring* does not use traits to model algorithm choice

For example, why doesn't ring have a DigestAlgorithm trait that is implemented by types named SHA256, SHA384, etc.? Why is everything dynamically dispatched instead of parameterized on types and statically dispatched? How does the current design avoids unnecessary heap usage and minimizes code size. That means we have to verify that there is actually a code size savings relative to the type parameterization and static dispatching approach that people seem to expect in Rust.

Add CCM AEAD

crypto/cmac is being removed because it depends on the non-AEAD cipher.h encryption interface, which is being removed. In order to implement the CCM AEAD, we need a new CTR + CBC-MAC (CCM) implementation. The new CCM implementation can assume that AES is the only cipher that it will be used with, and that it will only be exposed by the ring::aead interface, which should simplify the new implementation. [Edit: Say "CTR + CBC-MAC" instead of "CMAC".]

Make RSA API less of a footgun

See http://www.methics.fi/technology/signature-security/ and the stuff it cites.

At the very least, we should require callers to pass in the minimum key size they support.

In the upcoming generic signature and key exchange interfaces, that might mean that we need to define separate instances of Algorithm for various minimums. e.g. RSA_PKCS1_AT_LEAST_2048_BITS, RSA_PKCS1_AT_LEAST_1024_BITS, etc.

Remove C code that supports AES-GCM nonces that aren't 96 bits

It takes a fair amount of code to support these non-optimal nonce lengths. Very few applications will need to support these non-standard nonce lengths and it isn't worth the effort to test the code to verify that it works correctly for them. The ring::aead interface is much simplified if the nonce length is fixed per ring::aead::Algorithm instance.

It looks like it will be easier to do this if the non-AEAD AES-GCM interface in crypto/cipher/e_aes.c is removed first.

See Chromium issue https://code.google.com/p/chromium/issues/detail?id=535758.

cargo build on Windows doesn't work out of the box

C:\msys64\home\Peter\ring> cargo build
    Updating registry `https://github.com/rust-lang/crates.io-index`
   Compiling ring v0.1.0 (file:///C:/msys64/home/Peter/ring)
   Compiling rustc-serialize v0.3.16
   Compiling libc v0.1.10
Build failed, waiting for other jobs to finish...
failed to run custom build command for `ring v0.1.0 (file:///C:/msys64/home/Peter/ring)`
Process didn't exit successfully: `C:\msys64\home\Peter\ring\target\debug\build\ring-761370bdb05fc17f\build-script-build` (exit code: 101)
--- stdout
APPDATA: C:\Users\Peter\AppData\Roaming
PROMPT: $P$G
HOMEPATH: \Users\Peter
OPT_LEVEL: 0
COMMONPROGRAMW6432: C:\Program Files\Common Files
VS120COMNTOOLS: C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\
COMPUTERNAME: WIN81-SSD
PROCESSOR_IDENTIFIER: AMD64 Family 21 Model 19 Stepping 1, AuthenticAMD
PROGRAMW6432: C:\Program Files
: C:=C:\msys64\home\Peter
OUT_DIR: C:\msys64\home\Peter\ring\target\debug\build\ring-761370bdb05fc17f\out
PROCESSOR_LEVEL: 21
LOCALAPPDATA: C:\Users\Peter\AppData\Local
TARGET: x86_64-pc-windows-msvc
PROCESSOR_REVISION: 1301
USERDOMAIN_ROAMINGPROFILE: WIN81-SSD
CARGO_PKG_VERSION_MINOR: 1
COMMONPROGRAMFILES(X86): C:\Program Files (x86)\Common Files
HOMEDRIVE: C:
PSMODULEPATH: C:\Users\Peter\Documents\WindowsPowerShell\Modules;C:\Program Files\WindowsPowerShell\Modules;C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\
PATHEXT: .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.CPL
PROGRAMDATA: C:\ProgramData
VS110COMNTOOLS: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Tools\
CARGO_PKG_VERSION_PATCH: 0
WINDIR: C:\WINDOWS
FPS_BROWSER_USER_PROFILE_STRING: Default
PROGRAMFILES(X86): C:\Program Files (x86)
NUM_JOBS: 4
SESSIONNAME: Console
VBOX_MSI_INSTALL_PATH: C:\Program Files\Oracle\Virtualbox\
HOST: x86_64-pc-windows-msvc
PUBLIC: C:\Users\Public
PATH: C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\Microsoft SQL Server\110\Tools\Binn\;C:\Program Files (x86)\Microsoft SDKs\TypeScript\1.0\;C:\Program Files (x86)\Windows Kits\8.1\Windows Performance Toolkit\;C:\Program Files (x86)\ATI Technologies\ATI.ACE\Core-Static;C:\Program Files (x86)\AMD\ATI.ACE\Core-Static;C:\Program Files (x86)\Windows Kits\10\Windows Performance Toolkit\;C:\Program Files (x86)\nodejs\;C:\WINDOWS\system32\config\systemprofile\.dnx\bin;C:\Program Files\Microsoft DNX\Dnvm\;C:\Program Files (x86)\Skype\Phone\;;C:\Users\Peter\AppData\Local\Code\bin;C:\Users\Peter\AppData\Roaming\npm;C:\rust64-msvc\bin;C:\Program Files (x86)\CMake\bin;C:\Program Files (x86)\Git\bin;C:\msys64\home\Peter\ring\target\debug\deps
SYSTEMDRIVE: C:
USERPROFILE: C:\Users\Peter
CARGO_PKG_VERSION_PRE:
VS140COMNTOOLS: C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools\
FPS_BROWSER_APP_PROFILE_STRING: Internet Explorer
NUMBER_OF_PROCESSORS: 4
TEMP: C:\Users\Peter\AppData\Local\Temp
SSH_AGENT_PID: 748
HOME: C:\Users\Peter
CARGO_PKG_VERSION_MAJOR: 0
DXSDK_DIR: C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\
VSSDK140INSTALL: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VSSDK\
USERNAME: Peter
SSH_AUTH_SOCK: /tmp/ssh-l2ycI5SLhjmF/agent.3796
CARGO_PKG_VERSION: 0.1.0
COMMONPROGRAMFILES: C:\Program Files\Common Files
OS: Windows_NT
PROCESSOR_ARCHITECTURE: AMD64
DEBUG: true
USERDOMAIN: WIN81-SSD
PROFILE: debug
CARGO_MANIFEST_DIR: C:\msys64\home\Peter\ring
SYSTEMROOT: C:\WINDOWS
COMSPEC: C:\WINDOWS\system32\cmd.exe
LOGONSERVER: \\MicrosoftAccount
ALLUSERSPROFILE: C:\ProgramData
FP_NO_HOST_CHECK: NO
PROGRAMFILES: C:\Program Files
TMP: C:\Users\Peter\AppData\Local\Temp

--- stderr
thread '<main>' panicked at 'failed to execute msbuild: The system cannot find the file specified. (os error 2)', build.rs:87

I have VS 2015 installed, so it would be nice if ring could find where msbuild is.

Crashing bug using reference to digest::SHA1

When using your proposed code directly (badboy/nobsign/issues/2) I hit a bug crashing (and coredumping) the compiler.

This little piece of Rust reproduces it:

extern crate ring;

static _ALGORITHM: &'static ring::digest::Algorithm = &ring::digest::SHA1;

pub fn bug() {
    vec![0u8; ring::digest::SHA1.digest_len];
}

Compiling that (add this repo as a dependency to a crate, then cargo build) leads to:

rustc: /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/llvm/lib/IR/Constants.cpp:2044: static llvm::Constant* llvm::ConstantExpr::getGetElementPtr(llvm::Type*, llvm::Constant*, llvm::ArrayRef<llvm::Value*>, bool, llvm::Type*): Assertion `DestTy && "GEP indices invalid!"' failed.

and a coredump.

@Kimundi was so nice to explain a bit:

library does wrong unsafe things => invariants are violated => rust compiler emits IR with UB to llvm => llvm in debug mode checks somke of the invariants and triggers an assertion error => assertion do abort the process => abort is implemented as a invalid instruction => core dump

It seems that this usage of references to digest::SHA1 (which itself is a static) leads to this undefined behavior.

Of course it might be nice if rustc can prevent this.
But nevertheless ring should try to provide safe abstractions.

Implement `std::marker::Send` and `std::marker::Sync` for relevant types

This means using std::ptr::Unique for things like
ECDHEphemeralKeyPair. We should probably have some helper in ring::ffi that wraps a pointer with std::ptr::Unque, implements Send, and also implements Drop by calling a one-argument C function to delete the object. That's particularly true because std::ptr::Unique is unstable, so we can't actually use it.

See also https://internals.rust-lang.org/t/pre-rfc-raw-pointer-cleanup/2544.

Remove casts to size_t in Rust code

See rust-lang/rust/#28096.

There are some casts like digest.len() as libc::size_t that are potentially truncating and thus unsafe. At the same time, the comments in the code questioning the safety of such casts need to be removed.

This will have to wait for a higher-versioned released of the libc crate with rust-lang/rust#28096 merged into it.

Refactor |ring::digest| to improve perf and reduce size.

  1. The "init" functions just initialize the context with constant per-algorithm values. That can be done with a per-algorithm (static) initial context value array and a single init function that uses memcpy to copy that initial value into the context, avoiding the need for per-algorithm init functions. Then we can delete the C code for the init functions.
  2. The streaming interface requires the context to buffer data until an entire block of data is available. Right now, every digest implementation implements its own copy of this logic in C. Instead, w should implement one copy of tthe buffering logic in Rust and remove the C _Update and _Finalfunctions, leaving only the C _Transform functions for Rust to call into.

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.