Giter Club home page Giter Club logo

keygen-sh / air-gapped-activation-example Goto Github PK

View Code? Open in Web Editor NEW
30.0 3.0 4.0 358 KB

An example implementation of offline activation for air-gapped machines using QR codes, signed and encrypted license files, and a mobile device.

Home Page: https://keygen.sh

License: MIT License

HTML 3.26% TypeScript 86.75% SCSS 9.98%
keygen offline-licensing software-licensing licensing license-keys air-gapped-activation license-generator activation-portal license-activation on-premise

air-gapped-activation-example's Introduction

Air-gapped Activation Example

This is an example client/server implementation for air-gapped license activation. Essentially, the client displays a QR code which is read by the server, and the server will perform an activation request on behalf of the air-gapped client. The server will validate, activate, and finally check-out a license file. Lastly, once the license file is distributed to the client, the client can cryptographically verify and decrypt the license file.

image

Here's a detailed outline of the entire air-gapped licensing flow:

  1. The client will prompt for the end-user's license key.
  2. The client will fingerprint the air-gapped device.
  3. The client will generate a QR code containing the license key and fingerprint.
  4. The end-user, using the server app, will scan the QR code and extract the license key and fingerprint.
  5. The server will perform a license validation request for the license key, scoped to the fingerprint.
  6. The server will activate the fingerprint when required.
  7. The server will checkout a machine file, containing an encrypted license and machine object.
  8. The end-user will download the machine .lic file and transfer it to the client.
  9. The client will cryptographically verify the file using Ed25519.
  10. The client will decrypt the file using AES-256-GCM.

This example application is not 100% production-ready and only serves as an example implementation.

Running the example

First up, configure a few environment variables:

# Your Keygen account's Ed25519 verify key
export KEYGEN_VERIFY_KEY="e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788"

# Your Keygen account ID
export KEYGEN_ACCOUNT_ID="1fddcec8-8dd3-4d8d-9b16-215cac0f9b52"

You can either run each line above within your terminal session before starting the app, or you can add the above contents to your ~/.bashrc file and then run source ~/.bashrc after saving the file.

Next, install dependencies with yarn:

yarn

Then start the air-gapped client and the activation server:

yarn start

Services

Service Port
Client 1234 Represents the air-gapped client. None of the client code requires an internet connection.
Server 5678 Represents an activation portal. The server will communicate with Keygen on behalf of the client.

Testing the activation server

To access the activation server from a mobile device, create an ngrok tunnel for the server:

ngrok http 5678

Then visit the resulting ngrok HTTPS-enabled tunnel URL on your mobile device.

Testing the air-gapped client

Visit http://localhost:1234 and follow the on-screen instructions. Use your mobile device to scan the QR code. You may perform the client operations while disconnected from the internet. Upon successful activation, you will see a new machine resource created in your Keygen account, and both the activated machine and mobile device will let you know that the activation was a success.

Looking at your account's request logs, you should see a license validation request, an activation request, and a check-out request.

Questions?

Reach out at [email protected] if you have any questions or concerns!

air-gapped-activation-example's People

Contributors

aman00323 avatar ezekg 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

Watchers

 avatar  avatar  avatar

air-gapped-activation-example's Issues

Access to license or policy metadata

We are looking into an air-gapped solution for licenses that are bound to specific version ranges of a product. The "Choosing a Licensing Model" guide has a solution for individual features that works in the air-gapped case, but I don't quite understand.

The preferred way would be to add metadata fields to a license, minversion and maxversion. Unfortunately it seems the license metadata is not included in the key and thus cannot be read in the software. How can be link licenses to specific version ranges? In addition we also want to be able to extend the version range of air-gapped licenses over time. Is that possible or would they then require a new license?

Another oddity in the dashboard is that RSA_2048_PKCS1_PSS_SIGN requires you to manually specify the key. Why can it not be automatically generated?

Issue with installing dependencies

Yarn complained about node version initially so I changed it in package.json. But still, the dependency deasync isn't being fetched:

[4/5] โ   deasync
error {Local_Path_TO_REPO}/air-gapped-activation-example/node_modules/deasync: Command failed.
Exit code: 1
Command: node ./build.js
Arguments:
Directory: {Local_Path_TO_REPO}/air-gapped-activation-example/node_modules/deasync
Output:
gyp info it worked if it ends with ok
gyp info using [email protected]
gyp info using [email protected] | darwin | x64
gyp info find Python using Python version 3.9.5 found at "/usr/local/bin/python3"
(node:72119) [DEP0150] DeprecationWarning: Setting process.config is deprecated. In the future the property will be read-only.
(Use node --trace-deprecation ... to show where the warning was created)
gyp info spawn /usr/local/bin/python3
gyp info spawn args [
gyp info spawn args '/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/gyp_main.py',
gyp info spawn args 'binding.gyp',
gyp info spawn args '-f',
gyp info spawn args 'make',
gyp info spawn args '-I',
gyp info spawn args '{Local_Path_TO_REPO}/air-gapped-activation-example/node_modules/deasync/build/config.gypi',
gyp info spawn args '-I',
gyp info spawn args '/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/addon.gypi',
gyp info spawn args '-I',
gyp info spawn args '/Users/arj/Library/Caches/node-gyp/16.3.0/include/node/common.gypi',
gyp info spawn args '-Dlibrary=shared_library',
gyp info spawn args '-Dvisibility=default',
gyp info spawn args '-Dnode_root_dir=/Users/arj/Library/Caches/node-gyp/16.3.0',
gyp info spawn args '-Dnode_gyp_dir=/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp',
gyp info spawn args '-Dnode_lib_file=/Users/arj/Library/Caches/node-gyp/16.3.0/<(target_arch)/node.lib',
gyp info spawn args '-Dmodule_root_dir={Local_Path_TO_REPO}/air-gapped-activation-example/node_modules/deasync',
gyp info spawn args '-Dnode_engine=v8',
gyp info spawn args '--depth=.',
gyp info spawn args '--no-parallel',
gyp info spawn args '--generator-output',
gyp info spawn args 'build',
gyp info spawn args '-Goutput_dir=.'
gyp info spawn args ]
No receipt for 'com.apple.pkg.CLTools_Executables' found at '/'.

No receipt for 'com.apple.pkg.DeveloperToolsCLILeo' found at '/'.

No receipt for 'com.apple.pkg.DeveloperToolsCLI' found at '/'.

Traceback (most recent call last):
File "/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 1500, in XcodeVersion
version_list = GetStdoutQuiet(["xcodebuild", "-version"]).splitlines()
File "/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 1563, in GetStdoutQuiet
raise GypError("Error %d running %s" % (job.returncode, cmdlist[0]))
gyp.common.GypError: Error 1 running xcodebuild

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/gyp_main.py", line 51, in
sys.exit(gyp.script_main())
File "/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/init.py", line 670, in script_main
return main(sys.argv[1:])
File "/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/init.py", line 662, in main
return gyp_main(args)
File "/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/init.py", line 647, in gyp_main
generator.GenerateOutput(flat_list, targets, data, params)
File "/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py", line 2451, in GenerateOutput
writer.Write(
File "/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py", line 831, in Write
self.WriteSources(
File "/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py", line 1302, in WriteSources
cflags = self.xcode_settings.GetCflags(
File "/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 661, in GetCflags
archs = self.GetActiveArchs(self.configname)
File "/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 515, in GetActiveArchs
xcode_archs_default = GetXcodeArchsDefault()
File "/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 122, in GetXcodeArchsDefault
xcode_version, _ = XcodeVersion()
File "/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 1511, in XcodeVersion
version = CLTVersion() # macOS Catalina returns 11.0.0.0.1.1567737322
File "/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 1549, in CLTVersion
return re.search(regex, output).groupdict()["version"]
AttributeError: 'NoneType' object has no attribute 'groupdict'
gyp ERR! configure error
gyp ERR! stack Error: gyp failed with exit code: 1
gyp ERR! stack at ChildProcess.onCpExit (/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:351:16)
gyp ERR! stack at ChildProcess.emit (node:events:394:28)
gyp ERR! stack at Process.ChildProcess._handle.onexit (node:internal/child_process:290:12)
gyp ERR! System Darwin 20.5.0
gyp ERR! command "/usr/local/Cellar/node/16.3.0/bin/node" "/usr/local/Cellar/node/16.3.0/libexec/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd {Local_Path_TO_REPO}/air-gapped-activation-example/node_modules/deasync
gyp ERR! node -v v16.3.0

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.