Giter Club home page Giter Club logo

verifiablecredentials-crypto-sdk-typescript's Introduction

From October 31, 2021, some Microsoft Azure AD Verifiable Credential SDK functionality will stop working in Microsoft Authenticator. Applications and services that currently use the Microsoft Azure AD Verifiable Credential SDK should migrate to the Microsoft Request Service REST API.


Repo

https://github.com/microsoft/VerifiableCredentials-Crypto-SDK-Typescript

Goals

Goal - Provide crypto primitives used within DID

DID has defined a set of protocols supported by cryptographic primitives. This SDK will implement most of these cryptographic primitives. This includes algorithms such as:

  • ECDSA (secp256k1)
  • RSA signing (RSASSA-PKCS1-v1_5)
  • RSA encryption (RSA-OAEP)
  • AES GCM
  • Digests (SHA-256, SHA-384, SHA-512)
  • HMAC (HS256, HS384, HS512)
  • EdDSA (ed25519) - still experimental

Goal - Provide a standardized API

The crypto API is based on the W3C Web Crypto API. This is typically referred to as Web Crypto, Subtle or subtle.

The SDK uses several layers or crypto implementations:

Most the algorithms are implemented in Nodejs crypto. No need to rewrite them.

For Elliptic curve ed25519 the npm package elliptic is used.

The subtle crypto API is provided by the excellent work of @peculiar/webcrypto.

See diafygi/webcrypto-examples for an extensive list of examples on how to use subtle.

Goal - Support for payload protection

The crypto SDK provides support for protecting payloads. The protocol support is provided in a layer above subtle crypto and uses the pluggable crypto concept (see further).

Supported protocols:

Goal - Pluggable Crypto layers

The goal of pluggable crypto is to make applications agnostic to the used algorithms and hardware environments. The applications specify the used algorithms in configuration and as such a standardized API can be used for any crypto calls. So, changing algorithms or hardware environments will not impact the applications themselves.

Key Vault

We can configure an application to do all private key operations on another service (hardware security module) such as Key Vault. This implies that keys can be generated on Key Vault and all private key operations will happen on Key Vault. A such there is no need for the private key to ever leave the secure Key Vault environment.

A test environment might not need the same level of security and could generate and use keys on nodejs, while the same application in production will use Key Vault. This behavior can be achieve by just changing the application’s configuration.

Concepts

KeyStore

KeyStore is an abstraction of where cryptographic keys are stored. This SDK provides two key stores:

  • KeyStoreInMemory: Simple cache in memory for keys
  • KeyStoreKeyVault: Store the keys on Key Vault

KeyStores can be created by honoring the IKeyStore interface, as such application builder can write their own plugins with new key stores.

You can use the KeyStoreFactory.create('KeyStoreInMemory') to create the KeyStore, KeyStoreInMemory in this case.

CryptoFactory

The CryptoFactory defines which KeyStore to use and which plugins for which algorithms. You can make your own CryptoFactory and add your own plugins to it. Have a look to CrytoFactoryNode which is the default crypto factory. It maps the EdDSA algorithm to a special provider because EdDSA is not implemented in nodejs crypto.

export default class CryptoFactoryNode extends CryptoFactory {  
   /**  
    * Constructs a new CryptoFactoryNode  
    * @param keyStore used to store private keys  
    * @param crypto Default subtle crypto used for e.g. hashing.  
    */  
    constructor (keyStore: IKeyStore, crypto: any) {  
        super(keyStore, crypto);  
        const subtleCrypto: any = new SubtleCryptoElliptic(crypto);  
        this.addMessageSigner('EdDSA', {subtleCrypto, scope: CryptoFactoryScope.All});  
        this.addMessageSigner('EdDSA', {subtleCrypto, scope: CryptoFactoryScope.All});  
        this.addMessageSigner('ed25519', {subtleCrypto, scope: CryptoFactoryScope.All});  
    }  
}

Use the CryptoFactoryManager.create('CryptoFactoryNode', new Subtle()) factory method to create the default Subtle API.

Payload protection API

The SDK has a high-level API intended to protect payloads. Apps will typically use this high-level API.

Setup the payload protection object as JOSE and generate a signing key:

    const keyReference = new KeyReference('neo');
    let crypto = new CryptoBuilder()
        .useSigningKeyReference(keyReference)
        .build();

    let jose: IPayloadProtectionSigning = new JoseBuilder(crypto)
        .build();

    // Generate and save a signing key
    crypto = await crypto.generateKey(KeyUse.Signature);

Create the JWS signature and serialize the signature

    // Sign the payload
    jose = await jose.sign(payload);

    // Serialize the signature
    const signature = jose.serialize();

Deserialize the signature and validate it

    // Deserialize the received signature
    jose = jose.deserialize(signature);

    const validated = await jose.verify();

Running the same sample on key vault requires the following change in the setup:

    const keyReference = new KeyReference('neo', 'key');
    let crypto = new CryptoBuilder()
        .useKeyVault(credentials, Credentials.vaultUri)
        .useSigningKeyReference(keyReference)
        .build();
Mark that keyReference has the type 'key', meaning we will be using key vault keys.

Subtle

Subtle is the standardized API defined by W3C and is the basis of the primitives API.

const subtle = SubtleCryptoFactory.create('SubtleCryptoNode');  
const key = await subtle.generateKey(  
    {  
        name: "ECDSA",  
        namedCurve: "secp256k1"  
    },          
    true,   
    ["sign", "verify"]);

This is an example how to generate a secp256k1 key, commonly used in the DID community. Next we can export this key into a Json Web Key

const jwk = await subtle.exportKey(  
    "jwk",  
    key.privateKey);

We can also sign with this generated private key.

cont signature = await subtle.sign(  
    {  
        name: "ECDSA",  
        hash: {name: "SHA-256"}  
    },          
    key.privateKey,   
    Buffer.from('Payload to sign')); 

And finally verify the signature

const result = await subtle.verify(  
    {  
        name: "ECDSA",  
        hash: {name: "SHA-256"}  
    },  
    key.publicKey,   
    signature,   
    Buffer.from('Payload to sign')); 

Checkout the /samples folder for samples. Have a look at the subtle API examples on github diafygi/webcrypto-examples.

Use the SubtleCryptoFactory.create('SubtleCryptoNode') factory method to create the default Subtle API.

Pairwise keys

Pairwise keys are a special set of key generation algorithms which allows you to generate a deterministic key that can be used between two parties. This means that these keys can be generated on the fly when needed and they do not need to be stored.

Pairwise keys are supported for ECDSA and RSA signatures.

Getting started

Install

To add the sdk to your package.json:

npm i verifiablecredentials-crypto-sdk-typescript

Cloning

If you want to clone the SDK, you need to use rush. The crypto SDK is assembled by several smaller packages. Rush is transparent to applications using the SDK. The only install verifiablecredentials-crypto-sdk-typescript.

Install rush

npm install -g @microsoft/rush

Update all packages

rush update or rush update --full for a refresh

Build

rush build or rush rebuild to force a full build

Test

The SDK is an assembly of smaller NPM packages. You can go into the directory of each packages and do npm run test to test the package.

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.

When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

verifiablecredentials-crypto-sdk-typescript's People

Contributors

beejones avatar gpproano avatar gproanomsft avatar jcruiz avatar jorgeibarra13 avatar mahoekst avatar mauesrog avatar microsoft-github-operations[bot] avatar microsoftopensource avatar nithyaganeshng avatar symorton avatar

Stargazers

 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

verifiablecredentials-crypto-sdk-typescript's Issues

Please support Linked Data Proofs / JSON-LD

Per the VC Data Model there are 2 proof formats defined: https://www.w3.org/TR/vc-data-model/#proof-formats

JWTs provide no semantic features, and have no canonicalization support... But given they have been around for long enough to have a number of cryptographic algorithms prohibited https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms there is an abundance of JOSE tooling to build on.

When we implemented https://identity.foundation/sidetree/spec/#commitment-value-generation

We were forced to require the use of JCS, beyond specifying vanilla JOSE, because, per:

https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-17

Cryptographic operations like hashing and signing need the data to be expressed in an invariant format so that the operations are reliably repeatable. One way to address this is to create a canonical representation of the data. Canonicalization also permits data to be exchanged in its original form on the "wire" while cryptographic operations performed on the canonicalized counterpart of the data in the producer and consumer end points, generate consistent results.

In addition to JOSE not having a canonical representation for data, JSON also does not reliably handle big numbers: https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-17#appendix-D

Big Number are incredibly common when handling crypto currency, or mathematical operations...

Also per the VC Data Model Basic Concepts:

https://www.w3.org/TR/vc-data-model/#basic-concepts

@context is required, and is used to define the credential as linked data (JSON-LD).

The JWT variant's embed these credentials as vc and vp.

https://w3c.github.io/vc-data-model/#json-web-token-extensions

But does this implementation check to make sure they are valid JSON-LD? Or do you treat vc and vp as arbitrary JSON attributes? If you don't validate them, I don't see the value in VC-JWT at all, since you can get the same result by just creating a vanilla JWS...

Finally, LD Proofs are 100% compatible with JOSE, and add support for semantic disambiguity, unlocking common vocabularies such as schema.org and google knowledge graph.... And JSON-LD is already being used by Azure IoT: https://docs.microsoft.com/en-us/azure/iot-pnp/overview-iot-plug-and-play

IoT Plug and Play and the DTDL are open to the community, and Microsoft welcomes collaboration with customers, partners, and the industry. Both are based on open W3C standards such as JSON-LD and RDF, which enable easier adoption across services and tooling

Bing also supports Schema.org and JSON-LD... https://blogs.bing.com/webmaster/august-2018/Introducing-JSON-LD-Support-in-Bing-Webmaster-Tools

Here is an example of creating an LD Proof for all the popular JOSE key and signature formats, it leverages the best of JOSE (as popular legacy cryptographic suite), along with support canonicalization and compatibility with RDF / schema.org and JSON-LD.

https://github.com/w3c-ccg/lds-jws2020

If you do not plan to support LD Proofs along with JWTs, might I suggest renaming this repo (and the others) to avoid misleading developers who might assume Microsoft is supporting the same linked data technology currently used by Azure IoT and Bing.

Mandatory and optional fields of a VC-JWT

let jwt = new JoseBuilder(crypto)
      .useJwtProtocol({ jti: credential.id, nbf: 456, exp: 123 })
      .build();

Per https://w3c.github.io/vc-data-model/#identifiers

If the id property is present:

- The id property MUST express an identifier that others are expected to use when expressing statements about a specific thing identified by that identifier.
- The id property MUST NOT have more than one value.
- The value of the id property MUST be a URI.

Per https://w3c.github.io/vc-data-model/#jwt-encoding

jti MUST represent the id property of the verifiable credential or verifiable presentation.

Therefore jti should not be required, and if present should be a URI.. it is currently always present and defaulted to a guid.

exp is also always present, even though the vc data model says that it is optional.

These differences prevent this library from producing vc-jwt that are standards compliant where the issuer desires to follow the standard and:

  • The issuer does not want to set a credential.id or jti
  • The issuer does not want to set an expirationDate or exp.

These decisions are legal for the issuer to make, but impossible to achieve with the current implementation, due to:

// Add standardized properties
      const current = Math.trunc(Date.now() / 1000);
      if (!(<any>payload).nbf) {
        (<any>payload).nbf = this.builder.jwtProtocol!.nbf || current;
      }
      if (!(<any>payload).exp) {
        (<any>payload).exp = this.builder.jwtProtocol!.exp || current + (60 * 60); // this is not a required attribute
      }

      if (!(<any>payload).jti) {
        (<any>payload).jti = this.builder.jwtProtocol!.jti || uuidv4(); // this is not a URI
      }

      // Override properties
      for (let key in this.builder.jwtProtocol) {
        if (key in ['nbf', 'exp', 'jti']) {
          continue;
        }
        (<any>payload)[key] = (<any>payload)[key] || this.builder.jwtProtocol[key];
      }

Use in a browser?

Are any of the packages in this SDK suitable for use in a browser, or is has this been designed assuming node?

I was hoping to do things like create a LongFormDid in a web app using a different IKeyStore, perhaps a WebAuthnKeyStore based on the Web Authentication API?

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.