Comparing version 0.0.6 to 0.1.0
@@ -17,3 +17,3 @@ { | ||
}, | ||
"version": "0.0.6", | ||
"version": "0.1.0", | ||
"description": "A public-private key library for post-quantum cryptography (early stage, use with caution)", | ||
@@ -20,0 +20,0 @@ "bugs": { |
@@ -23,3 +23,3 @@ # EPOLITE Privacy Guard | ||
## Using this library | ||
This library is specifically built for the [Bun Runtime](https://bun.sh). Please install that and replace Node.JS with this runtime, as it is much faster. | ||
This library is specifically built for the [Bun Runtime](https://bun.sh). Please install that and replace NodeJS with this runtime, as it is much faster. | ||
@@ -66,3 +66,3 @@ Afterwards, run `bun add epolite` to install this package, and then use the documentation below. | ||
//returns a base64 encoded string (signatures aren't too big). | ||
//returns a base64 encoded string (signatures aren't too big, but they do include the original message). | ||
const signedMessage: string = await sign("I do not like pineapple pizza", privateKey); | ||
@@ -77,7 +77,9 @@ | ||
const realMessage: string = "I do not like pineapple pizza"; | ||
const fakeMessage: string = "I LOVE pineapple on pizza"; | ||
//fill these in with the signed message, starting with: | ||
// ----------BEGIN EPOLITE SIGNED MESSAGE---------- | ||
const realSignature: string; | ||
const fakeSignature: string; | ||
const verified: boolean = await verify(realMessage, signedMessage, publicKey); //true | ||
const notVerified: boolean = await verify(fakeMessage, signedMessage, publicKey); //false | ||
await verify(realSignature, publicKey); //true | ||
await verify(fakeSignature, publicKey); //false | ||
``` | ||
@@ -84,0 +86,0 @@ |
@@ -38,3 +38,2 @@ export type KeyPair = { | ||
* | ||
* @param data The data that was signed. | ||
* @param signature The signature to verify. | ||
@@ -45,2 +44,2 @@ * @param publicKey The sender's public key. | ||
*/ | ||
export declare function verify(data: string, signature: string, publicKey: string): Promise<boolean>; | ||
export declare function verify(signature: string, publicKey: string): Promise<boolean>; |
138
src/index.ts
@@ -1,4 +0,6 @@ | ||
import { ml_kem512 } from '@noble/post-quantum/ml-kem'; | ||
import { Buffer } from 'buffer'; //for web | ||
import { ml_kem512 } from "@noble/post-quantum/ml-kem"; | ||
import { Buffer } from "buffer"; //for web | ||
const VERSION = 2; //incremental versions, each one is not compatible with earlier ones. | ||
interface SIGN { | ||
@@ -21,10 +23,17 @@ publicKeyBytes: Promise<number>; | ||
if(typeof document !== "undefined") | ||
signBuilder = (await import('@dashlane/pqc-sign-falcon-512-browser') as any).default; | ||
signBuilder = (await import("@dashlane/pqc-sign-falcon-512-browser") as any).default; | ||
else | ||
signBuilder = (await import('@dashlane/pqc-sign-falcon-512-node') as any).default; | ||
signBuilder = (await import("@dashlane/pqc-sign-falcon-512-node") as any).default; | ||
const EPOLITE_PUBLIC_KEY_LABEL = '----------BEGIN EPOLITE PUBLIC KEY----------'; | ||
const EPOLITE_PRIVATE_KEY_LABEL = '----------BEGIN EPOLITE PRIVATE KEY----------'; | ||
const KEY_END_LABEL = '----------END EPOLITE KEY----------'; | ||
const EPOLITE_PUBLIC_KEY_LABEL = "----------BEGIN EPOLITE PUBLIC KEY----------"; | ||
const EPOLITE_PRIVATE_KEY_LABEL = "----------BEGIN EPOLITE PRIVATE KEY----------"; | ||
const KEY_END_LABEL = "----------END EPOLITE KEY----------"; | ||
const SIGN_START_LABEL = "----------BEGIN EPOLITE SIGNED MESSAGE----------"; | ||
const SIGN_END_LABEL = "----------END EPOLITE SIGNED MESSAGE----------"; | ||
const ENCRYPTED_START_LABEL = "----------BEGIN EPOLITE ENCRYPTED MESSAGE----------"; | ||
const ENCRYPTED_END_LABEL = "----------END EPOLITE ENCRYPTED MESSAGE----------"; | ||
export type KeyPair = { | ||
@@ -52,2 +61,3 @@ publicKey: string, | ||
const publicKeyObj = { | ||
version: VERSION, | ||
kyberPublicKey: Array.from(kyberKeyPair.publicKey), | ||
@@ -58,2 +68,3 @@ falconPublicKey: Array.from(falconKeyPair.publicKey), | ||
const privateKeyObj = { | ||
version: VERSION, | ||
kyberPrivateKey: Array.from(kyberKeyPair.secretKey), | ||
@@ -66,7 +77,7 @@ falconPrivateKey: Array.from(falconKeyPair.privateKey), | ||
JSON.stringify(publicKeyObj) | ||
).toString('base64')}\n${KEY_END_LABEL}`; | ||
).toString("base64")}\n${KEY_END_LABEL}`; | ||
const privateKeyString = `${EPOLITE_PRIVATE_KEY_LABEL}\n${Buffer.from( | ||
JSON.stringify(privateKeyObj) | ||
).toString('base64')}\n${KEY_END_LABEL}`; | ||
).toString("base64")}\n${KEY_END_LABEL}`; | ||
@@ -80,6 +91,6 @@ return { | ||
/** | ||
* Encrypts data using the recipient's public key. | ||
* Encrypts data using the recipient"s public key. | ||
* | ||
* @param data The data to encrypt. | ||
* @param otherPublicKey The recipient's public key. | ||
* @param otherPublicKey The recipient"s public key. | ||
* | ||
@@ -91,6 +102,6 @@ * @returns The encrypted data. | ||
const publicKeyEncoded = otherPublicKey | ||
.replace(EPOLITE_PUBLIC_KEY_LABEL, '') | ||
.replace(KEY_END_LABEL, '') | ||
.replace(EPOLITE_PUBLIC_KEY_LABEL, "") | ||
.replace(KEY_END_LABEL, "") | ||
.trim(); | ||
const publicKeyObj = JSON.parse(Buffer.from(publicKeyEncoded, 'base64').toString('utf-8')); | ||
const publicKeyObj = JSON.parse(Buffer.from(publicKeyEncoded, "base64").toString("utf-8")); | ||
const kyberPublicKey = new Uint8Array(publicKeyObj.kyberPublicKey); | ||
@@ -107,13 +118,13 @@ | ||
const aesKey = await crypto.subtle.importKey( | ||
'raw', | ||
"raw", | ||
sharedSecret.slice(0, 32), | ||
'AES-GCM', | ||
"AES-GCM", | ||
false, | ||
['encrypt'] | ||
["encrypt"] | ||
); | ||
// Encrypt the data using AES-GCM | ||
const encryptedData = await crypto.subtle.encrypt( | ||
{ | ||
name: 'AES-GCM', | ||
name: "AES-GCM", | ||
iv, | ||
@@ -124,3 +135,3 @@ }, | ||
); | ||
// Return cipherText, encryptedData, IV | ||
@@ -131,5 +142,6 @@ const payload = { | ||
iv: Array.from(iv), | ||
version: VERSION, | ||
}; | ||
return Buffer.from(JSON.stringify(payload)).toString('base64'); | ||
return ENCRYPTED_START_LABEL + "\n" + Buffer.from(JSON.stringify(payload)).toString("base64") + "\n" + ENCRYPTED_END_LABEL; | ||
} | ||
@@ -143,32 +155,35 @@ | ||
export async function decrypt(encryptedPayload: string, privateKey: string): Promise<string> { | ||
// Decode the payload | ||
const payload = JSON.parse(Buffer.from(encryptedPayload, 'base64').toString('utf-8')); | ||
encryptedPayload = encryptedPayload.replace(ENCRYPTED_START_LABEL, "").replace(ENCRYPTED_END_LABEL, "").trim(); | ||
//decode payload | ||
const payload = JSON.parse(Buffer.from(encryptedPayload, "base64").toString("utf-8")); | ||
const cipherText = new Uint8Array(payload.cipherText); | ||
const encryptedData = new Uint8Array(payload.encryptedData); | ||
const iv = new Uint8Array(payload.iv); | ||
// Extract and decode the private key | ||
const privateKeyEncoded = privateKey | ||
.replace(EPOLITE_PRIVATE_KEY_LABEL, '') | ||
.replace(KEY_END_LABEL, '') | ||
.replace(EPOLITE_PRIVATE_KEY_LABEL, "") | ||
.replace(KEY_END_LABEL, "") | ||
.trim(); | ||
const privateKeyObj = JSON.parse(Buffer.from(privateKeyEncoded, 'base64').toString('utf-8')); | ||
const privateKeyObj = JSON.parse(Buffer.from(privateKeyEncoded, "base64").toString("utf-8")); | ||
const kyberPrivateKey = new Uint8Array(privateKeyObj.kyberPrivateKey); | ||
// Decapsulate shared secret using Kyber | ||
//decapsulate shared secret using Kyber | ||
const sharedSecret = ml_kem512.decapsulate(cipherText, kyberPrivateKey); | ||
// Import the shared secret as a CryptoKey | ||
//import the shared secret as a CryptoKey | ||
const aesKey = await crypto.subtle.importKey( | ||
'raw', | ||
"raw", | ||
sharedSecret.slice(0, 32), | ||
'AES-GCM', | ||
"AES-GCM", | ||
false, | ||
['decrypt'] | ||
["decrypt"] | ||
); | ||
// Decrypt the data using AES-GCM | ||
//decrypt the data using AES-GCM | ||
const decryptedData = await crypto.subtle.decrypt( | ||
{ | ||
name: 'AES-GCM', | ||
name: "AES-GCM", | ||
iv, | ||
@@ -184,6 +199,6 @@ }, | ||
/** | ||
* Signs data using the sender's private key. | ||
* Signs data using the sender"s private key. | ||
* | ||
* @param data The data to sign. | ||
* @param privateKey The sender's private key. | ||
* @param privateKey The sender"s private key. | ||
* | ||
@@ -195,7 +210,7 @@ * @returns The signature. | ||
const privateKeyEncoded = privateKey | ||
.replace(EPOLITE_PRIVATE_KEY_LABEL, '') | ||
.replace(KEY_END_LABEL, '') | ||
.replace(EPOLITE_PRIVATE_KEY_LABEL, "") | ||
.replace(KEY_END_LABEL, "") | ||
.trim(); | ||
const privateKeyObj = JSON.parse(Buffer.from(privateKeyEncoded, 'base64').toString('utf-8')); | ||
const privateKeyObj = JSON.parse(Buffer.from(privateKeyEncoded, "base64").toString("utf-8")); | ||
const falconPrivateKey = new Uint8Array(privateKeyObj.falconPrivateKey); | ||
@@ -205,26 +220,33 @@ | ||
const sign = await signBuilder(); | ||
const message = new TextEncoder().encode(data); | ||
const { signature } = await sign.sign(message, falconPrivateKey); | ||
return Buffer.from(signature).toString('base64'); | ||
const ro = JSON.stringify({ | ||
sig: Buffer.from(signature).toString("base64"), | ||
raw: data, | ||
version: VERSION, | ||
}); | ||
return SIGN_START_LABEL + "\n" + Buffer.from(ro).toString("base64") + "\n" + SIGN_END_LABEL; | ||
} | ||
/** | ||
* Verifies the signature using the sender's public key. | ||
* Verifies the signature using the sender"s public key. | ||
* | ||
* @param data The data that was signed. | ||
* @param signature The signature to verify. | ||
* @param publicKey The sender's public key. | ||
* @param publicKey The sender"s public key. | ||
* | ||
* @returns Whether the signature is valid. | ||
*/ | ||
export async function verify(data: string, signature: string, publicKey: string): Promise<boolean> { | ||
export async function verify(signature: string, publicKey: string): Promise<boolean> { | ||
//extract and decode the public key | ||
const publicKeyEncoded = publicKey | ||
.replace(EPOLITE_PUBLIC_KEY_LABEL, '') | ||
.replace(KEY_END_LABEL, '') | ||
.replace(EPOLITE_PUBLIC_KEY_LABEL, "") | ||
.replace(KEY_END_LABEL, "") | ||
.trim(); | ||
const publicKeyObj = JSON.parse(Buffer.from(publicKeyEncoded, 'base64').toString('utf-8')); | ||
signature = signature.replace(SIGN_START_LABEL, "").replace(SIGN_END_LABEL, "").trim(); | ||
const publicKeyObj = JSON.parse(Buffer.from(publicKeyEncoded, "base64").toString("utf-8")); | ||
const falconPublicKey = new Uint8Array(publicKeyObj.falconPublicKey); | ||
@@ -234,10 +256,10 @@ | ||
const sign = await signBuilder(); | ||
const message = new TextEncoder().encode(data); | ||
const signatureArray = new Uint8Array(Buffer.from(signature, 'base64')); | ||
const message = new TextEncoder().encode(JSON.parse(Buffer.from(signature, "base64").toString("utf-8")).raw); | ||
const signatureArray = new Uint8Array(Buffer.from(JSON.parse(Buffer.from(signature, "base64").toString("utf-8")).sig, "base64")); | ||
const isValid = await sign.verify(signatureArray, message, falconPublicKey); | ||
return isValid; | ||
} | ||
@@ -20,3 +20,3 @@ import { createKeyPair, decrypt, encrypt, sign, verify } from "."; | ||
//Step 4: Bob verifies the signature | ||
const isValid = await verify(decryptedText, decryptedSignature, aliceKeys.publicKey); | ||
const isValid = await verify(decryptedSignature, aliceKeys.publicKey); | ||
@@ -23,0 +23,0 @@ console.log('Decrypted Message:', decryptedText); // Should output the original message |
21607
294
90