Skip to content

Commit

Permalink
KyberKotlin v1.0.0
Browse files Browse the repository at this point in the history
* API improvements and removed deprecated methods and classes.
* Finalized with the release of NIST FIPS 203 final document.
* Uses the 1.0.0 version of KeccakKotlin.
* Uses the 0.3.2 version of the KotlinCrypto/SecureRandom library.
* Kotlin 2.0.20
  • Loading branch information
ronhombre committed Aug 31, 2024
1 parent 7260eaa commit 2e2011b
Show file tree
Hide file tree
Showing 19 changed files with 369 additions and 1,172 deletions.
269 changes: 23 additions & 246 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# KyberKotlin _implements ML-KEM (CRYSTALS-Kyber)_
# KyberKotlin (1.0.0)
## _Implements ML-KEM (CRYSTALS-Kyber)_
_**Digital security for all, everywhere, no matter who they are, or what they believe in.**_

[![CodeQL master](https://github.com/ronhombre/KyberKotlin/actions/workflows/codeql.yml/badge.svg)](https://github.com/ronhombre/KyberKotlin/actions/workflows/codeql.yml)
Expand All @@ -10,283 +11,59 @@ _**Digital security for all, everywhere, no matter who they are, or what they be
![C#](https://img.shields.io/badge/c%23-%23239120.svg?style=for-the-badge&logo=csharp&logoColor=white)
![JS](https://img.shields.io/badge/JavaScript-F7DF1E?style=for-the-badge&logo=javascript&logoColor=black)

> **ML-KEM** is a key-encapsulation mechanism based on _CRYSTALS-KYBER_. The security of ML-KEM is based on the presumed
> hardness of the so-called Module Learning with Errors (MLWE) problem, which is a generalization of the Learning With
> Errors (LWE) problem introduced by Regev in 2005. The hardness of the MLWE problem is itself based on the presumed
> hardness of certain computational problems in module lattices. This motivates the name of the scheme ML-KEM.
[Kyber](https://pq-crystals.org/kyber/index.shtml) is an IND-CCA2-secure key encapsulation mechanism (KEM), whose
security is based on the hardness of solving the learning-with-errors (LWE) problem over module lattices. Kyber is one
of the finalists in the NIST post-quantum cryptography project. The submission lists three different parameter sets
aiming at different security levels. Specifically, Kyber-512 aims at security roughly equivalent to AES-128, Kyber-768
aims at security roughly equivalent to AES-192, and Kyber-1024 aims at security roughly equivalent to AES-256.
*This is quoted from
[Section 3.2](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf) of the NIST FIPS 203 document.*

## Introduction

This is a 100% Kotlin Multiplatform implementation of ML-KEM.
It depends on [KeccakKotlin](https://github.com/ronhombre/KeccakKotlin) and [secure-random](https://github.com/KotlinCrypto/secure-random) Kotlin libraries in order to implement SHA3, SHAKE, and
Secure Random within the library.

> [!WARNING]
> KyberKotlin is in **ALPHA**. At this stage, we are confident it can withstand production. However, it has not been battle-tested yet. Mock tests are on the way.
### Intent

At the 1.0.0 release, developers from various platforms should be able to use this dependency if they want to support ML-KEM.

## Benchmarks (Tested on a Ryzen 7 5800X; Windows 11)

| Variant | Generation | Encapsulation | Decapsulation |
|---------|---------------------------|---------------------------|---------------------------|
| 512 | 0.0762125 (1093% Faster) | 0.0745875 (1198% Faster) | 0.09534375 (1042% Faster) |
| 768 | 0.1194375 (1135% Faster) | 0.12181875 (1235% Faster) | 0.1507125 (1120% Faster) |
| 1024 | 0.18365625 (1157% Faster) | 0.183575 (1265% Faster) | 0.22190 (1172% Faster) |
| ML-KEM | (in ms) | (in ms) | (in ms) |

JVM: Coretto 1.8, Count: 10000, Iterations: 5 (Average), Relative to 'standard' branch as of April 15, 2024.

Code is in [JVMBenchmark.kt](https://github.com/ronhombre/KyberKotlin/blob/master/src/jvmTest/kotlin/asia/hombre/kyber/tests/JVMBenchmark.kt)

This benchmark is for performance tracking through the development.

> [!NOTE]
> This master branch is faster than the standard branch due to optimizations. Additionally, I implemented my own Keccak,
> which gave me great control over its optimizations.
> With the release of the final version of NIST FIPS 203 for ML-KEM, I'm proud to present that my KyberKotlin library is
> ready for production use. In the past months, there have been no reports about any problems.
## Capabilities
* Key Generation (512, 768, 1024)
* Encapsulation (512, 768, 1024)
* Decapsulation (512, 768, 1024)
* Convert to or from HEX, BASE64, and BYTES.
* Convert to or from bytes.

## Supported & Tested Platforms
* JVM (Java, Kotlin)
* Javascript (NPM) _Slower by 2-3x_
* Native (C#)
* Javascript (NPM)

## Documentation
* [kyber.hombre.asia](https://kyber.hombre.asia)

> [!WARNING]
> Upgrading to 0.7.0+ introduces breaking changes. Please refer to the documentation or to the code samples below.
> [!WARNING]
> In preparation for the 1.0.0 release, 0.9.0 is a deprecation version. There were not much changes except for the
> deprecated methods and classes. These are marked with the Deprecated annotation to notify the current users of the
> library. `Use the 1.x.x versions if you want the latest version!`
> Upgrading to 1.x.x from the 0.x.x versions requires a quick read up with the documentation. This is because there have
> been massive improvements and changes in the way the API works.
## JVM Installation

### Maven with Gradle Kotlin DSL

```Kotlin
dependencies {
implementation("asia.hombre:kyber:0.9.0")
}
```

### JAR with Gradle Kotlin DSL

Get a jar from [releases](https://github.com/ronhombre/KyberKotlin/releases) and copy it into `libs/` of your JVM project.
## Installation

```Kotlin
//Gradle Kotlin DSL (build.gradle.kts)
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
}
```

[More](https://central.sonatype.com/artifact/asia.hombre/kyber/overview) installation methods

## JS NPM Installation
```
npm install kyberkotlin@0.9.0
```

## Native C# Installation

### DLL Import

Get it from [releases](https://github.com/ronhombre/KyberKotlin/releases) and copy it into your C# project.

## Usage

### With JVM

Kotlin
```Kotlin
import asia.hombre.kyber.KyberAgreement
import asia.hombre.kyber.KyberKeyGenerator
import asia.hombre.kyber.KyberParameter

//Generate keys
//This would be done in their own systems
val keyPairAlice = KyberKeyGenerator.generate(KyberParameter.ML_KEM_512)
val keyPairBob = KyberKeyGenerator.generate(KyberParameter.ML_KEM_512)

//Bob sends his encapsulation key to Alice
//Alice uses it to encapsulate a Secret Key
val cipherTextAlice = KyberAgreement.encapsulate(keyPairBob.encapsulationKey)

val agreementBob = KyberAgreement(keyPairBob.decapsulationKey)

//Alice sends the Cipher Text to Bob
//Bob decapsulates the Cipher Text
val secretKeyBob = agreementBob.decapsulate(cipherTextAlice.cipherText)

//Alice generated the Secret Key
println("Gen: " + cipherTextAlice.secretKey.joinToString(", "))
//Bob decapsulated the same Secret Key
println("Rec: " + secretKeyBob.joinToString(", "))

//Bob's Secret Key
//[91, 119, -51, 71, -106, 30, -66, 47, 53, -7, -119, -38, -78, 61, -27, 44, -15, -47, -115, -92, -26, -120, 124, -17, -121, 83, 0, -57, -71, 118, 2, -31]
//Alice's Secret Key
//[91, 119, -51, 71, -106, 30, -66, 47, 53, -7, -119, -38, -78, 61, -27, 44, -15, -47, -115, -92, -26, -120, 124, -17, -121, 83, 0, -57, -71, 118, 2, -31]

//secretKeyBob == cipherTextAlice.secretKey
```

Java

```Java
import asia.hombre.kyber.KyberKeyGenerator;
import asia.hombre.kyber.KyberKEMKeyPair;
import asia.hombre.kyber.KyberParameter;
import asia.hombre.kyber.KyberAgreement;
import asia.hombre.kyber.KyberEncapsulationResult;

//Generate keys
//This would be done in their own systems
KyberKEMKeyPair keyPairBob = KyberKeyGenerator.generate(KyberParameter.ML_KEM_512);

//Bob sends his encapsulation key to Alice
//Alice uses it to encapsulate a Secret Key
KyberEncapsulationResult encapsResult = KyberAgreement.encapsulate(keyPairBob.getEncapsulationKey());

KyberAgreement agreementBob = new KyberAgreement(keyPairBob.getDecapsulationKey());

//Alice sends the Cipher Text to Bob
//Bob decapsulates the Cipher Text
byte[] decapsSecretKey = agreementBob.decapsulate(encapsResult.getCipherText());

//Alice generated the Secret Key
System.out.println(Arrays.toString(encapsResult.getSecretKey()));
//Bob decapsulated the same Secret Key
System.out.println(Arrays.toString(decapsSecretKey));

//Bob's Secret Key
//[91, 119, -51, 71, -106, 30, -66, 47, 53, -7, -119, -38, -78, 61, -27, 44, -15, -47, -115, -92, -26, -120, 124, -17, -121, 83, 0, -57, -71, 118, 2, -31]
//Alice's Secret Key
//[91, 119, -51, 71, -106, 30, -66, 47, 53, -7, -119, -38, -78, 61, -27, 44, -15, -47, -115, -92, -26, -120, 124, -17, -121, 83, 0, -57, -71, 118, 2, -31]


//encapsResult.getSecretKey() == decapsSecretKey
```

In this example, **Bob** and **Alice** creates an **Agreement** that an eavesdropper would not be able to comprehend even
if it is intercepted. After generating the _Shared Secret Key_, they may communicate using a Symmetric Encryption
algorithm _i.e. AES_.

### With Javascript

```javascript
const { KyberParameter, KyberKeyGenerator, KyberAgreement } = require("kyberkotlin").asia.hombre.kyber;

let aliceKeypair = KyberKeyGenerator.Companion.generate(KyberParameter.ML_KEM_512);

let aliceAgreement = new KyberAgreement(aliceKeypair.decapsulationKey);

//Bob receives Alice's Encapsulation Key and generates a Secret Key out of it.
let results = KyberAgreement.Companion.encapsulate(aliceKeypair.encapsulationKey);

let ciphertext = results.cipherText; //Send back to Alice.
let bobSecretKey = results.secretKey;

//Alice receives Bob's Cipher Text and decapsulates the Secret Key in it.
let aliceSecretKey = aliceAgreement.decapsulate(ciphertext);

//This checks the equality of the Secret Key. This should not fail unless you are very very very unlucky.
console.assert(contentEquals(aliceSecretKey, bobSecretKey), "Secret Keys does not match!");

//Simple check
function contentEquals(a, b) {
for(let i = 0; i < a.length; i++)
if(a[i] !== b[i])
return false;
return true;
implementation("asia.hombre:kyber:1.0.0")
}

```

### With Native C#
Checkout the Wiki for more installation options.

> [!CAUTION]
> Native is in BETA. No one is confident it will work reliably.
## API Usage

```csharp
using System.Runtime.InteropServices;

//Use absolute path if relative does not work.
const string dllName = "KyberKotlin.dll";

//Declare methods from the DLL.
[DllImport(dllName, EntryPoint = "generateKeyPair")]
static extern IntPtr generateKeyPair(int parameterId);

[DllImport(dllName, EntryPoint = "getEncapsulationKeySize")]
static extern int getEncapsulationKeySize(int parameterId);

[DllImport(dllName, EntryPoint = "getDecapsulationKeySize")]
static extern int getDecapsulationKeySize(int parameterId);

[DllImport(dllName, EntryPoint = "getCipherTextSize")]
static extern int getCipherTextSize(int parameterId);

[DllImport(dllName, EntryPoint = "getSecretKeySize")]
static extern int getSecretKeySize();

[DllImport(dllName, EntryPoint = "encapsulate")]
static extern IntPtr encapsulate(byte[] encapsulationKey, int parameterId);

[DllImport(dllName, EntryPoint = "decapsulate")]
static extern IntPtr decapsulate(byte[] decapsulationKey, byte[] cipherText, int parameterId);

//Get sizes
int encapsulationKeySize = getEncapsulationKeySize(0); //0 = 512, 1 = 768, 2 = 1024
int decapsulationKeySize = getDecapsulationKeySize(0);
int cipherTextSize = getCipherTextSize(0);

//Generate Key Pair
IntPtr keysPtr = generateKeyPair(0);

byte[] encapsulationKey = new byte[encapsulationKeySize];
byte[] decapsulationKey = new byte[decapsulationKeySize];

Marshal.Copy(keysPtr, encapsulationKey, 0, encapsulationKey.Length);
Marshal.Copy(keysPtr + encapsulationKey.Length, decapsulationKey, 0, decapsulationKey.Length);

//Send encapsulation key to Bob
//Bob encapsulates using it
IntPtr cipherPtr = encapsulate(encapsulationKey, 0);

//Bob receives the Secret Key
byte[] secretKey = new byte[getSecretKeySize()];
byte[] cipherText = new byte[cipherTextSize];

Marshal.Copy(cipherPtr, secretKey, 0, secretKey.Length);
Marshal.Copy(cipherPtr + secretKey.Length, cipherText, 0, cipherText.Length);

//Bob sends the Cipher Text to Alice
//Alice uses it to get a copy of the Secret Key
IntPtr secretKeyPtr = decapsulate(decapsulationKey, cipherText, 0);

//Alice's Copy of the Secret Key
byte[] secretKey2 = new byte[getSecretKeySize()];

Marshal.Copy(secretKeyPtr, secretKey2, 0, secretKey2.Length);

Console.WriteLine("End!");
```
Checkout the Wiki or the Documentation for more information.

### References

* [NIST FIPS 203 ipd](https://csrc.nist.gov/pubs/fips/203/ipd)
* [NIST FIPS 203](https://csrc.nist.gov/pubs/fips/203/final)
* [CRYSTALS-Kyber Algorithm Specs v3.02](https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf)
* [kyberJCE](https://github.com/fisherstevenk/kyberJCE)
* [CCTestVectors](https://github.com/C2SP/CCTV/)
Expand Down Expand Up @@ -326,5 +103,5 @@ See the License for the specific language governing permissions and
limitations under the License.
```

Although CRYSTALS-Kyber is Public Domain, this implementation is created through Hard Work by its Contributors.
Thus, the APACHE LICENSE v2.0 has been chosen.
Although ML-KEM is declared Public Domain, this implementation is created through the efforts of its contributors. As
such, some form of recognition for their work are required for all users of this library.
7 changes: 1 addition & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ plugins {
}

group = "asia.hombre"
version = "0.9.0"
version = "1.0.0"
description = "ML-KEM (NIST FIPS 203) optimized implementation on 100% Kotlin."

val projectName = "kyber"
Expand Down Expand Up @@ -71,11 +71,6 @@ kotlin {
}
binaries.executable()
}
mingwX64("windows") {
binaries {
sharedLib { }
}
} //TODO: Build process for 'native mingw windows' release
sourceSets {
val commonMain by getting {
dependencies {
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ kotlin.code.style=official
signing.gnupg.keyName=FEF4601F
kmm=2.0.20
dokka=1.9.20
keccak=0.2.0
random=0.3.1
keccak=1.0.0
random=0.3.2
Loading

0 comments on commit 2e2011b

Please sign in to comment.