@turnkey/crypto
Advanced tools
+6
-0
| # @turnkey/crypto | ||
| ## 2.3.1 | ||
| ### Patch Changes | ||
| - 2bc0046: Migrated from WebCrypto (crypto.subtle.verify) to Noble for ECDSA signature verification | ||
| ## 2.3.0 | ||
@@ -4,0 +10,0 @@ |
+1
-1
@@ -97,3 +97,3 @@ /// <reference lib="dom" /> | ||
| /** | ||
| * Converts an ASN.1 DER-encoded ECDSA signature to the raw format that WebCrypto uses. | ||
| * Converts an ASN.1 DER-encoded ECDSA signature to the raw format used for verification. | ||
| * | ||
@@ -100,0 +100,0 @@ * @param {string} derSignature - The DER-encoded signature. |
+3
-9
@@ -7,2 +7,3 @@ 'use strict'; | ||
| var aes = require('@noble/ciphers/aes'); | ||
| var utils = require('@noble/hashes/utils'); | ||
| var encoding = require('@turnkey/encoding'); | ||
@@ -185,3 +186,3 @@ var math = require('./math.js'); | ||
| const generateP256KeyPair = () => { | ||
| const privateKey = randomBytes(32); | ||
| const privateKey = utils.randomBytes(32); | ||
| const publicKey = getPublicKey(privateKey, true); | ||
@@ -265,9 +266,2 @@ const publicKeyUncompressed = encoding.uint8ArrayToHexString(uncompressRawPublicKey(publicKey)); | ||
| /** | ||
| * Generate a random Uint8Array of a specific length. Note that this ultimately depends on the crypto implementation. | ||
| */ | ||
| const randomBytes = (length) => { | ||
| const array = new Uint8Array(length); | ||
| return crypto.getRandomValues(array); | ||
| }; | ||
| /** | ||
| * Build labeled Initial Key Material (IKM). | ||
@@ -365,3 +359,3 @@ * | ||
| /** | ||
| * Converts an ASN.1 DER-encoded ECDSA signature to the raw format that WebCrypto uses. | ||
| * Converts an ASN.1 DER-encoded ECDSA signature to the raw format used for verification. | ||
| * | ||
@@ -368,0 +362,0 @@ * @param {string} derSignature - The DER-encoded signature. |
+2
-8
@@ -5,2 +5,3 @@ import { p256 } from '@noble/curves/p256'; | ||
| import { gcm } from '@noble/ciphers/aes'; | ||
| import { randomBytes } from '@noble/hashes/utils'; | ||
| import { uint8ArrayFromHexString, uint8ArrayToHexString, normalizePadding } from '@turnkey/encoding'; | ||
@@ -243,9 +244,2 @@ import { modSqrt, testBit } from './math.mjs'; | ||
| /** | ||
| * Generate a random Uint8Array of a specific length. Note that this ultimately depends on the crypto implementation. | ||
| */ | ||
| const randomBytes = (length) => { | ||
| const array = new Uint8Array(length); | ||
| return crypto.getRandomValues(array); | ||
| }; | ||
| /** | ||
| * Build labeled Initial Key Material (IKM). | ||
@@ -343,3 +337,3 @@ * | ||
| /** | ||
| * Converts an ASN.1 DER-encoded ECDSA signature to the raw format that WebCrypto uses. | ||
| * Converts an ASN.1 DER-encoded ECDSA signature to the raw format used for verification. | ||
| * | ||
@@ -346,0 +340,0 @@ * @param {string} derSignature - The DER-encoded signature. |
+1
-1
@@ -37,3 +37,3 @@ 'use strict'; | ||
| } | ||
| let result = b; | ||
| let result = b % p; | ||
| const exponentBitString = exp.toString(2); | ||
@@ -40,0 +40,0 @@ for (let i = 1; i < exponentBitString.length; ++i) { |
+1
-1
@@ -35,3 +35,3 @@ /** | ||
| } | ||
| let result = b; | ||
| let result = b % p; | ||
| const exponentBitString = exp.toString(2); | ||
@@ -38,0 +38,0 @@ for (let i = 1; i < exponentBitString.length; ++i) { |
+28
-26
@@ -7,4 +7,6 @@ 'use strict'; | ||
| var constants = require('./constants.js'); | ||
| var crypto$1 = require('./crypto.js'); | ||
| var crypto = require('./crypto.js'); | ||
| var p256 = require('@noble/curves/p256'); | ||
| var ed25519 = require('@noble/curves/ed25519'); | ||
| var sha256 = require('@noble/hashes/sha256'); | ||
@@ -29,4 +31,4 @@ /// <reference lib="dom" /> | ||
| const ciphertextBuf = bundleBytes.slice(33); | ||
| const encappedKeyBuf = crypto$1.uncompressRawPublicKey(compressedEncappedKeyBuf); | ||
| const decryptedData = crypto$1.hpkeDecrypt({ | ||
| const encappedKeyBuf = crypto.uncompressRawPublicKey(compressedEncappedKeyBuf); | ||
| const decryptedData = crypto.hpkeDecrypt({ | ||
| ciphertextBuf, | ||
@@ -76,3 +78,3 @@ encappedKeyBuf, | ||
| const ciphertextBuf = encoding.uint8ArrayFromHexString(signedData.ciphertext); | ||
| const decryptedData = crypto$1.hpkeDecrypt({ | ||
| const decryptedData = crypto.hpkeDecrypt({ | ||
| ciphertextBuf, | ||
@@ -121,10 +123,11 @@ encappedKeyBuf, | ||
| const publicKeyBuffer = encoding.uint8ArrayFromHexString(publicKey); | ||
| const loadedPublicKey = await loadPublicKey(publicKeyBuffer); | ||
| const loadedPublicKey = loadPublicKey(publicKeyBuffer); | ||
| if (!loadedPublicKey) { | ||
| throw new Error("failed to load public key"); | ||
| } | ||
| // The ECDSA signature is ASN.1 DER encoded but WebCrypto uses raw format | ||
| const publicSignatureBuf = crypto$1.fromDerSignature(signature); | ||
| const signedDataBuf = Buffer.from(signedData); | ||
| return await crypto.subtle.verify({ name: "ECDSA", hash: { name: "SHA-256" } }, loadedPublicKey, publicSignatureBuf, signedDataBuf); | ||
| // Convert the ASN.1 DER-encoded signature for verification | ||
| const publicSignatureBuf = crypto.fromDerSignature(signature); | ||
| const signedDataBuf = new TextEncoder().encode(signedData); | ||
| const hashedData = sha256.sha256(signedDataBuf); | ||
| return p256.p256.verify(publicSignatureBuf, hashedData, loadedPublicKey.toHex()); | ||
| }; | ||
@@ -146,10 +149,11 @@ /** | ||
| const encryptionQuorumPublicBuf = new Uint8Array(encoding.uint8ArrayFromHexString(enclaveQuorumPublic)); | ||
| const quorumKey = await loadPublicKey(encryptionQuorumPublicBuf); | ||
| const quorumKey = loadPublicKey(encryptionQuorumPublicBuf); | ||
| if (!quorumKey) { | ||
| throw new Error("failed to load quorum key"); | ||
| } | ||
| // The ECDSA signature is ASN.1 DER encoded but WebCrypto uses raw format | ||
| const publicSignatureBuf = crypto$1.fromDerSignature(publicSignature); | ||
| // Convert the ASN.1 DER-encoded signature for verification | ||
| const publicSignatureBuf = crypto.fromDerSignature(publicSignature); | ||
| const signedDataBuf = encoding.uint8ArrayFromHexString(signedData); | ||
| return await crypto.subtle.verify({ name: "ECDSA", hash: { name: "SHA-256" } }, quorumKey, publicSignatureBuf, signedDataBuf); | ||
| const hashedData = sha256.sha256(signedDataBuf); | ||
| return p256.p256.verify(publicSignatureBuf, hashedData, quorumKey.toHex()); | ||
| }; | ||
@@ -159,10 +163,8 @@ /** | ||
| * | ||
| * @param {Uint8Array} publicKey - The raw public key bytes. | ||
| * @returns {Promise<CryptoKey>} - The imported ECDSA public key. | ||
| * @param {Uint8Array} publicKey - The raw P-256 public key bytes. | ||
| * @returns {ProjPointType<bigint>} - The parsed ECDSA public key. | ||
| * @throws {Error} - If the public key is invalid. | ||
| */ | ||
| const loadPublicKey = async (publicKey) => { | ||
| return await crypto.subtle.importKey("raw", publicKey, { | ||
| name: "ECDSA", | ||
| namedCurve: "P-256", | ||
| }, true, ["verify"]); | ||
| const loadPublicKey = (publicKey) => { | ||
| return p256.p256.ProjectivePoint.fromHex(encoding.uint8ArrayToHexString(publicKey)); | ||
| }; | ||
@@ -222,6 +224,6 @@ /** | ||
| } | ||
| // Load target public key generated from enclave and set in local storage | ||
| // Load target public key generated from enclave | ||
| const targetKeyBuf = encoding.uint8ArrayFromHexString(signedData.targetPublic); | ||
| const privateKeyBundle = crypto$1.hpkeEncrypt({ plainTextBuf, targetKeyBuf }); | ||
| return crypto$1.formatHpkeBuf(privateKeyBundle); | ||
| const privateKeyBundle = crypto.hpkeEncrypt({ plainTextBuf, targetKeyBuf }); | ||
| return crypto.formatHpkeBuf(privateKeyBundle); | ||
| }; | ||
@@ -254,6 +256,6 @@ /** | ||
| } | ||
| // Load target public key generated from enclave and set in local storage | ||
| // Load target public key generated from enclave | ||
| const targetKeyBuf = encoding.uint8ArrayFromHexString(signedData.targetPublic); | ||
| const privateKeyBundle = crypto$1.hpkeEncrypt({ plainTextBuf, targetKeyBuf }); | ||
| return crypto$1.formatHpkeBuf(privateKeyBundle); | ||
| const privateKeyBundle = crypto.hpkeEncrypt({ plainTextBuf, targetKeyBuf }); | ||
| return crypto.formatHpkeBuf(privateKeyBundle); | ||
| }; | ||
@@ -260,0 +262,0 @@ |
+18
-16
@@ -6,3 +6,5 @@ import bs58check from 'bs58check'; | ||
| import { uncompressRawPublicKey, hpkeDecrypt, fromDerSignature, hpkeEncrypt, formatHpkeBuf } from './crypto.mjs'; | ||
| import { p256 } from '@noble/curves/p256'; | ||
| import { ed25519 } from '@noble/curves/ed25519'; | ||
| import { sha256 } from '@noble/hashes/sha256'; | ||
@@ -117,10 +119,11 @@ /// <reference lib="dom" /> | ||
| const publicKeyBuffer = uint8ArrayFromHexString(publicKey); | ||
| const loadedPublicKey = await loadPublicKey(publicKeyBuffer); | ||
| const loadedPublicKey = loadPublicKey(publicKeyBuffer); | ||
| if (!loadedPublicKey) { | ||
| throw new Error("failed to load public key"); | ||
| } | ||
| // The ECDSA signature is ASN.1 DER encoded but WebCrypto uses raw format | ||
| // Convert the ASN.1 DER-encoded signature for verification | ||
| const publicSignatureBuf = fromDerSignature(signature); | ||
| const signedDataBuf = Buffer.from(signedData); | ||
| return await crypto.subtle.verify({ name: "ECDSA", hash: { name: "SHA-256" } }, loadedPublicKey, publicSignatureBuf, signedDataBuf); | ||
| const signedDataBuf = new TextEncoder().encode(signedData); | ||
| const hashedData = sha256(signedDataBuf); | ||
| return p256.verify(publicSignatureBuf, hashedData, loadedPublicKey.toHex()); | ||
| }; | ||
@@ -142,10 +145,11 @@ /** | ||
| const encryptionQuorumPublicBuf = new Uint8Array(uint8ArrayFromHexString(enclaveQuorumPublic)); | ||
| const quorumKey = await loadPublicKey(encryptionQuorumPublicBuf); | ||
| const quorumKey = loadPublicKey(encryptionQuorumPublicBuf); | ||
| if (!quorumKey) { | ||
| throw new Error("failed to load quorum key"); | ||
| } | ||
| // The ECDSA signature is ASN.1 DER encoded but WebCrypto uses raw format | ||
| // Convert the ASN.1 DER-encoded signature for verification | ||
| const publicSignatureBuf = fromDerSignature(publicSignature); | ||
| const signedDataBuf = uint8ArrayFromHexString(signedData); | ||
| return await crypto.subtle.verify({ name: "ECDSA", hash: { name: "SHA-256" } }, quorumKey, publicSignatureBuf, signedDataBuf); | ||
| const hashedData = sha256(signedDataBuf); | ||
| return p256.verify(publicSignatureBuf, hashedData, quorumKey.toHex()); | ||
| }; | ||
@@ -155,10 +159,8 @@ /** | ||
| * | ||
| * @param {Uint8Array} publicKey - The raw public key bytes. | ||
| * @returns {Promise<CryptoKey>} - The imported ECDSA public key. | ||
| * @param {Uint8Array} publicKey - The raw P-256 public key bytes. | ||
| * @returns {ProjPointType<bigint>} - The parsed ECDSA public key. | ||
| * @throws {Error} - If the public key is invalid. | ||
| */ | ||
| const loadPublicKey = async (publicKey) => { | ||
| return await crypto.subtle.importKey("raw", publicKey, { | ||
| name: "ECDSA", | ||
| namedCurve: "P-256", | ||
| }, true, ["verify"]); | ||
| const loadPublicKey = (publicKey) => { | ||
| return p256.ProjectivePoint.fromHex(uint8ArrayToHexString(publicKey)); | ||
| }; | ||
@@ -218,3 +220,3 @@ /** | ||
| } | ||
| // Load target public key generated from enclave and set in local storage | ||
| // Load target public key generated from enclave | ||
| const targetKeyBuf = uint8ArrayFromHexString(signedData.targetPublic); | ||
@@ -250,3 +252,3 @@ const privateKeyBundle = hpkeEncrypt({ plainTextBuf, targetKeyBuf }); | ||
| } | ||
| // Load target public key generated from enclave and set in local storage | ||
| // Load target public key generated from enclave | ||
| const targetKeyBuf = uint8ArrayFromHexString(signedData.targetPublic); | ||
@@ -253,0 +255,0 @@ const privateKeyBundle = hpkeEncrypt({ plainTextBuf, targetKeyBuf }); |
+3
-4
| { | ||
| "name": "@turnkey/crypto", | ||
| "version": "2.3.0", | ||
| "version": "2.3.1", | ||
| "main": "./dist/index.js", | ||
@@ -45,7 +45,6 @@ "module": "./dist/index.mjs", | ||
| "devDependencies": { | ||
| "crypto": "1.0.1", | ||
| "jest": "29.7.0", | ||
| "@turnkey/encoding": "0.4.0", | ||
| "@turnkey/http": "2.17.0", | ||
| "@turnkey/sdk-server": "1.7.0" | ||
| "@turnkey/http": "2.18.0", | ||
| "@turnkey/sdk-server": "2.0.1" | ||
| }, | ||
@@ -52,0 +51,0 @@ "scripts": { |
+1
-5
| # @turnkey/crypto | ||
| This package consolidates some common cryptographic utilities used across our applications, particularly primitives related to keys, encryption, and decryption in a pure JS implementation. For react-native you will need to polyfill our random byte generation by importing react-native-get-random-values: https://www.npmjs.com/package/react-native-get-random-values | ||
| This package consolidates some common cryptographic utilities used across our applications, particularly primitives related to keys, encryption, and decryption in a pure JS implementation. For react-native you will need to polyfill our random byte generation by importing [react-native-get-random-values](https://www.npmjs.com/package/react-native-get-random-values) | ||
| ``` | ||
| import 'react-native-get-random-values' | ||
| ``` | ||
| Example usage (Hpke E2E): | ||
@@ -10,0 +6,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
4
-20%156415
-0.54%1805
-0.44%36
-10%