feat: add sncrypto client side packages

This commit is contained in:
Karol Sójko
2022-07-06 12:21:21 +02:00
parent 9d1f7043e5
commit 6ec66795d2
71 changed files with 9786 additions and 34 deletions

View File

@@ -0,0 +1,17 @@
import { Base64String } from '../Types/Base64String'
import { HexString } from '../Types/HexString'
/**
* @param iv initialization vector as a hex string
* @param tag authentication tag as a hex string
* @param ciphertext as a base64 string
* @param encoding that will be applied after decrypting
* @param aad additional authenticated data as a hex string
*/
export type Aes256GcmEncrypted<EncodingType> = {
iv: HexString
tag: HexString
ciphertext: Base64String
encoding: EncodingType
aad: HexString
}

View File

@@ -0,0 +1,15 @@
import { HexString } from '../Types/HexString'
import { Unencrypted } from '../Types/Unencrypted'
/**
* @param unencrypted -- UTF-8 string or a `string` with `encoding`
* @param iv initialization vector as a hex string
* @param key encryption key as a hex string
* @param aad additional authenticated data as a hex string
*/
export type Aes256GcmInput<EncodingType> = {
unencrypted: Unencrypted<EncodingType>
iv: HexString
key: HexString
aad?: HexString
}

View File

@@ -0,0 +1,20 @@
import { HexString } from '../Types/HexString'
import { Aes256GcmEncrypted } from './Aes256GcmEncrypted'
import { Aes256GcmInput } from './Aes256GcmInput'
export interface CryptoAes256GcmInterface<EncodingType> {
/**
* Encrypts a string using AES-GCM.
* @param input
* @returns An object which can be run through aes256GcmDecrypt to retrieve the input text.
*/
aes256GcmEncrypt(input: Aes256GcmInput<EncodingType>): Promise<Aes256GcmEncrypted<EncodingType>>
/**
* Decrypts a string using AES-GCM.
* @param encrypted
* @param key - encryption key as a hex string
* @returns A string encoded with encoding provided in the input
*/
aes256GcmDecrypt(encrypted: Aes256GcmEncrypted<EncodingType>, key: HexString): Promise<string>
}

View File

@@ -0,0 +1,3 @@
export * from './Aes256GcmEncrypted'
export * from './Aes256GcmInput'
export * from './CryptoAes256GcmInterface'

View File

@@ -0,0 +1,25 @@
import { Base64String } from '../Types/Base64String'
import { Utf8String } from '../Types/Utf8String'
export interface CryptoBase64Interface {
/**
* Converts a plain string into base64
* @param text - A plain string
* @returns A base64 encoded string
*/
base64Encode(text: Utf8String): Base64String
/**
* Converts a plain string into url-safe base64
* @param text - A plain string
* @returns A base64 encoded string
*/
base64URLEncode(text: Utf8String): Base64String
/**
* Converts a base64 string into a plain string
* @param base64String - A base64 encoded string
* @returns A plain string
*/
base64Decode(base64String: Base64String): Utf8String
}

View File

@@ -0,0 +1 @@
export * from './CryptoBase64Interface'

View File

@@ -0,0 +1,200 @@
import { Base64String } from '../Types/Base64String'
import { Base64URLSafeString } from '../Types/Base64URLSafeString'
import { HexString } from '../Types/HexString'
import { SodiumConstant } from '../Types/SodiumConstant'
import { StreamDecryptor } from '../Types/StreamDecryptor'
import { StreamEncryptor } from '../Types/StreamEncryptor'
import { Utf8String } from '../Types/Utf8String'
/**
* Interface that clients have to implement to use snjs
*/
export interface PureCryptoInterface {
initialize(): Promise<void>
/**
* Derives a key from a password and salt using PBKDF2 via WebCrypto.
* @param password - utf8 string
* @param salt - utf8 string
* @param iterations
* @param length - In bits
* @returns Hex string
*/
pbkdf2(password: Utf8String, salt: Utf8String, iterations: number, length: number): Promise<string | null>
/**
* Generates a random key in hex format
* @param bits - Length of key in bits
* @returns A string key in hex format
*/
generateRandomKey(bits: number): string
/**
* @legacy
* Encrypts a string using AES-CBC via WebCrypto.
* @param plaintext
* @param iv - In hex format
* @param key - In hex format
* @returns Ciphertext in Base64 format.
*/
aes256CbcEncrypt(plaintext: Utf8String, iv: HexString, key: HexString): Promise<Base64String>
/**
* @legacy
* Decrypts a string using AES-CBC via WebCrypto.
* @param ciphertext - Base64 format
* @param iv - In hex format
* @param key - In hex format
* @returns Plain utf8 string or null if decryption fails
*/
aes256CbcDecrypt(ciphertext: Base64String, iv: HexString, key: HexString): Promise<Utf8String | null>
/**
* Runs HMAC with SHA-256 on a message with key.
* @param message - Plain utf8 string
* @param key - In hex format
* @returns Hex string or null if computation fails
*/
hmac256(message: Utf8String, key: HexString): Promise<HexString | null>
/**
* @param text - Plain utf8 string
* @returns Hex string
*/
sha256(text: string): Promise<string>
/**
* Runs HMAC with SHA-1 on a message with key.
* @param message - Plain utf8 string
* @param key - In hex format
* @returns Hex string or null if computation fails
*/
hmac1(message: Utf8String, key: HexString): Promise<HexString | null>
/**
* Use only for legacy applications.
* @param text - Plain utf8 string
* @returns Hex string
*/
unsafeSha1(text: string): Promise<string>
/**
* Derives a key from a password and salt using
* argon2id (crypto_pwhash_ALG_DEFAULT).
* @param password - Plain text string
* @param salt - Salt in hex format
* @param iterations - The algorithm's opslimit (recommended min 2)
* @param bytes - The algorithm's memory limit (memlimit) (recommended min 67108864)
* @param length - The output key length
* @returns Derived key in hex format
*/
argon2(password: Utf8String, salt: HexString, iterations: number, bytes: number, length: number): HexString
/**
* Encrypt a message (and associated data) with XChaCha20-Poly1305.
* @param plaintext
* @param nonce - In hex format
* @param key - In hex format
* @param assocData
* @returns Base64 ciphertext string
*/
xchacha20Encrypt(plaintext: Utf8String, nonce: HexString, key: HexString, assocData: Utf8String): Base64String
/**
* Decrypt a message (and associated data) with XChaCha20-Poly1305
* @param ciphertext
* @param nonce - In hex format
* @param key - In hex format
* @param assocData
* @returns Plain utf8 string or null if decryption fails
*/
xchacha20Decrypt(
ciphertext: Base64String,
nonce: HexString,
key: HexString,
assocData: Utf8String | Uint8Array,
): Utf8String | null
xchacha20StreamInitEncryptor(key: HexString): StreamEncryptor
xchacha20StreamEncryptorPush(
encryptor: StreamEncryptor,
plainBuffer: Uint8Array,
assocData: Utf8String,
tag?: SodiumConstant,
): Uint8Array
xchacha20StreamInitDecryptor(header: Base64String, key: HexString): StreamDecryptor
xchacha20StreamDecryptorPush(
decryptor: StreamDecryptor,
encryptedBuffer: Uint8Array,
assocData: Utf8String,
): { message: Uint8Array; tag: SodiumConstant } | false
/**
* Converts a plain string into base64
* @param text - A plain string
* @returns A base64 encoded string
*/
base64Encode(text: Utf8String): Base64String
/**
* Converts a plain string into url safe base64
* @param text - A plain string
* @returns A base64 url safe encoded string
*/
base64URLEncode(text: Utf8String): Base64URLSafeString
/**
* Converts a base64 string into a plain string
* @param base64String - A base64 encoded string
* @returns A plain string
*/
base64Decode(base64String: Base64String): Utf8String
deinit(): void
/**
* Generates a UUID string syncronously.
*/
generateUUID(): string
/**
* Constant-time string comparison
* @param a
* @param b
*/
timingSafeEqual(a: string, b: string): boolean
/**
* Generates a random secret for TOTP authentication
*
* RFC4226 reccomends a length of at least 160 bits = 32 b32 chars
* https://datatracker.ietf.org/doc/html/rfc4226#section-4
*/
generateOtpSecret(): Promise<string>
/**
* Generates a HOTP code as per RFC4226 specification
* using HMAC-SHA1
* https://datatracker.ietf.org/doc/html/rfc4226
*
* @param secret OTP shared secret
* @param counter HOTP counter
* @returns HOTP auth code
*/
hotpToken(secret: string, counter: number, tokenLength: number): Promise<string>
/**
* Generates a TOTP code as per RFC6238 specification
* using HMAC-SHA1
* https://datatracker.ietf.org/doc/html/rfc6238
*
* @param secret OTP shared secret
* @param timestamp time specified in milliseconds since UNIX epoch
* @param step time step specified in seconds
* @returns TOTP auth code
*/
totpToken(secret: string, timestamp: number, tokenLength: number, step: number): Promise<string>
}

View File

@@ -0,0 +1,22 @@
/**
* Constant-time string comparison
* @param a
* @param b
*/
export function timingSafeEqual(a: string, b: string): boolean {
const strA = String(a)
let strB = String(b)
const lenA = strA.length
let result = 0
if (lenA !== strB.length) {
strB = strA
result = 1
}
for (let i = 0; i < lenA; i++) {
result |= strA.charCodeAt(i) ^ strB.charCodeAt(i)
}
return result === 0
}

View File

@@ -0,0 +1,2 @@
export * from './PureCryptoInterface'
export * from './Utils'

View File

@@ -0,0 +1,6 @@
import { HexString } from '../Types/HexString'
import { Utf8String } from '../Types/Utf8String'
export interface CryptoSha256Interface {
sha256(text: Utf8String): HexString
}

View File

@@ -0,0 +1 @@
export * from './CryptoSha256Interface'

View File

@@ -0,0 +1 @@
export type Base64String = string

View File

@@ -0,0 +1 @@
export type Base64URLSafeString = string

View File

@@ -0,0 +1 @@
export type HexString = string

View File

@@ -0,0 +1,11 @@
export enum SodiumConstant {
CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_STATEBYTES = 52,
CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES = 17,
CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES = 24,
CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES = 32,
CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH = 0,
CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PULL = 1,
CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY = 2,
CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL = 3,
CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX = 0x3fffffff80,
}

View File

@@ -0,0 +1 @@
export type SodiumStateAddress = unknown

View File

@@ -0,0 +1,5 @@
import { SodiumStateAddress } from './SodiumStateAddress'
export type StreamDecryptor = {
state: SodiumStateAddress
}

View File

@@ -0,0 +1,6 @@
import { SodiumConstant } from './SodiumConstant'
export type StreamDecryptorResult = {
message: Uint8Array
tag: SodiumConstant
}

View File

@@ -0,0 +1,7 @@
import { Base64String } from './Base64String'
import { SodiumStateAddress } from './SodiumStateAddress'
export type StreamEncryptor = {
state: SodiumStateAddress
header: Base64String
}

View File

@@ -0,0 +1,6 @@
import { Utf8String } from './Utf8String'
/**
* Either a plaintext (UTF-8 string) or a `string` with an `encoding`.
*/
export type Unencrypted<EncodingType> = Utf8String | { string: string; encoding: EncodingType }

View File

@@ -0,0 +1 @@
export type Utf8String = string

View File

@@ -0,0 +1,10 @@
export * from './Base64String'
export * from './Base64URLSafeString'
export * from './HexString'
export * from './SodiumConstant'
export * from './SodiumStateAddress'
export * from './StreamDecryptor'
export * from './StreamDecryptorResult'
export * from './StreamEncryptor'
export * from './Unencrypted'
export * from './Utf8String'

View File

@@ -0,0 +1,5 @@
export * from './AES-GCM'
export * from './Base64'
export * from './Common'
export * from './SHA'
export * from './Types'