@twin.org/crypto 0.0.1 → 0.0.2-next.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +330 -1
- package/dist/esm/index.mjs +329 -2
- package/dist/types/ciphers/rsa.d.ts +75 -0
- package/dist/types/helpers/pemHelper.d.ts +19 -0
- package/dist/types/index.d.ts +2 -0
- package/docs/changelog.md +87 -0
- package/docs/reference/classes/PemHelper.md +69 -0
- package/docs/reference/classes/RSA.md +209 -0
- package/docs/reference/index.md +2 -0
- package/locales/en.json +4 -0
- package/package.json +4 -3
package/dist/cjs/index.cjs
CHANGED
|
@@ -8,6 +8,7 @@ var blake2b = require('@noble/hashes/blake2b');
|
|
|
8
8
|
var bip32 = require('@scure/bip32');
|
|
9
9
|
var slip10_js = require('micro-key-producer/slip10.js');
|
|
10
10
|
var chacha = require('@noble/ciphers/chacha');
|
|
11
|
+
var node_crypto = require('node:crypto');
|
|
11
12
|
var blake3 = require('@noble/hashes/blake3');
|
|
12
13
|
var hmac = require('@noble/hashes/hmac');
|
|
13
14
|
var sha1 = require('@noble/hashes/sha1');
|
|
@@ -717,8 +718,292 @@ class ChaCha20Poly1305 {
|
|
|
717
718
|
// Copyright 2024 IOTA Stiftung.
|
|
718
719
|
// SPDX-License-Identifier: Apache-2.0.
|
|
719
720
|
/**
|
|
720
|
-
*
|
|
721
|
+
* Implementation of the RSA cipher.
|
|
721
722
|
*/
|
|
723
|
+
class RSA {
|
|
724
|
+
/**
|
|
725
|
+
* Runtime name for the class.
|
|
726
|
+
* @internal
|
|
727
|
+
*/
|
|
728
|
+
static _CLASS_NAME = "RSA";
|
|
729
|
+
/**
|
|
730
|
+
* The public key for encryption.
|
|
731
|
+
* @internal
|
|
732
|
+
*/
|
|
733
|
+
_publicKeyBytes;
|
|
734
|
+
/**
|
|
735
|
+
* The private key for decryption.
|
|
736
|
+
* @internal
|
|
737
|
+
*/
|
|
738
|
+
_privateKeyBytes;
|
|
739
|
+
/**
|
|
740
|
+
* The public key for encryption.
|
|
741
|
+
* @internal
|
|
742
|
+
*/
|
|
743
|
+
_publicKey;
|
|
744
|
+
/**
|
|
745
|
+
* The private key for decryption.
|
|
746
|
+
* @internal
|
|
747
|
+
*/
|
|
748
|
+
_privateKey;
|
|
749
|
+
/**
|
|
750
|
+
* The block size for encryption.
|
|
751
|
+
* @internal
|
|
752
|
+
*/
|
|
753
|
+
_blockSize;
|
|
754
|
+
/**
|
|
755
|
+
* The key size for decryption.
|
|
756
|
+
* @internal
|
|
757
|
+
*/
|
|
758
|
+
_keySize;
|
|
759
|
+
/**
|
|
760
|
+
* Create a new instance of RSA.
|
|
761
|
+
* @param publicKey The public key for encryption (DER format as Uint8Array).
|
|
762
|
+
* @param privateKey The private key for decryption (DER format as Uint8Array).
|
|
763
|
+
*/
|
|
764
|
+
constructor(publicKey, privateKey) {
|
|
765
|
+
core.Guards.uint8Array(RSA._CLASS_NAME, "publicKey", publicKey);
|
|
766
|
+
this._publicKeyBytes = publicKey;
|
|
767
|
+
this._privateKeyBytes = privateKey;
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Generate a new RSA key pair in PKCS8 format.
|
|
771
|
+
* @param modulusLength The key size in bits (default: 2048).
|
|
772
|
+
* @returns The public and private keys as Uint8Array.
|
|
773
|
+
*/
|
|
774
|
+
static async generateKeyPair(modulusLength = 2048) {
|
|
775
|
+
const { publicKey, privateKey } = node_crypto.generateKeyPairSync("rsa", {
|
|
776
|
+
modulusLength,
|
|
777
|
+
publicKeyEncoding: {
|
|
778
|
+
type: "spki",
|
|
779
|
+
format: "der"
|
|
780
|
+
},
|
|
781
|
+
privateKeyEncoding: {
|
|
782
|
+
type: "pkcs8",
|
|
783
|
+
format: "der"
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
return {
|
|
787
|
+
publicKey: new Uint8Array(publicKey),
|
|
788
|
+
privateKey: new Uint8Array(privateKey)
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Convert a PKCS1 key to a PKCS8 key.
|
|
793
|
+
* @param pkcs1Key The PKCS1 key as Uint8Array.
|
|
794
|
+
* @returns The PKCS8 key as Uint8Array.
|
|
795
|
+
*/
|
|
796
|
+
static async convertPkcs1ToPkcs8(pkcs1Key) {
|
|
797
|
+
core.Guards.uint8Array(RSA._CLASS_NAME, "pkcs1Key", pkcs1Key);
|
|
798
|
+
const privateKey = node_crypto.createPrivateKey({
|
|
799
|
+
key: Buffer.from(pkcs1Key),
|
|
800
|
+
format: "der",
|
|
801
|
+
type: "pkcs1"
|
|
802
|
+
});
|
|
803
|
+
return new Uint8Array(privateKey.export({
|
|
804
|
+
format: "der",
|
|
805
|
+
type: "pkcs8"
|
|
806
|
+
}));
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Break the private key down in to its components.
|
|
810
|
+
* @param pkcs8Key The PKCS8 key as Uint8Array.
|
|
811
|
+
* @returns The key components.
|
|
812
|
+
*/
|
|
813
|
+
static async getPrivateKeyComponents(pkcs8Key) {
|
|
814
|
+
core.Guards.uint8Array(RSA._CLASS_NAME, "pkcs8Key", pkcs8Key);
|
|
815
|
+
const privateKey = node_crypto.createPrivateKey({
|
|
816
|
+
key: Buffer.from(pkcs8Key),
|
|
817
|
+
format: "der",
|
|
818
|
+
type: "pkcs8"
|
|
819
|
+
});
|
|
820
|
+
const jwk = privateKey.export({ format: "jwk" });
|
|
821
|
+
return {
|
|
822
|
+
n: this.base64UrlToBigInt(jwk.n),
|
|
823
|
+
e: this.base64UrlToBigInt(jwk.e),
|
|
824
|
+
d: this.base64UrlToBigInt(jwk.d),
|
|
825
|
+
p: this.base64UrlToBigInt(jwk.p),
|
|
826
|
+
q: this.base64UrlToBigInt(jwk.q),
|
|
827
|
+
dp: this.base64UrlToBigInt(jwk.dp),
|
|
828
|
+
dq: this.base64UrlToBigInt(jwk.dq),
|
|
829
|
+
qi: this.base64UrlToBigInt(jwk.qi)
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Break the public key down in to its components.
|
|
834
|
+
* @param spkiKey The SPKI key as Uint8Array.
|
|
835
|
+
* @returns The key components.
|
|
836
|
+
*/
|
|
837
|
+
static async getPublicKeyComponents(spkiKey) {
|
|
838
|
+
core.Guards.uint8Array(RSA._CLASS_NAME, "spkiKey", spkiKey);
|
|
839
|
+
const publicKey = node_crypto.createPublicKey({
|
|
840
|
+
key: Buffer.from(spkiKey),
|
|
841
|
+
format: "der",
|
|
842
|
+
type: "spki"
|
|
843
|
+
});
|
|
844
|
+
const jwk = publicKey.export({ format: "jwk" });
|
|
845
|
+
return {
|
|
846
|
+
n: this.base64UrlToBigInt(jwk.n),
|
|
847
|
+
e: this.base64UrlToBigInt(jwk.e)
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Convert base64 encoded data to a big int.
|
|
852
|
+
* @param bytes The bytes to convert.
|
|
853
|
+
* @returns The bigint representation of the bytes.
|
|
854
|
+
* @internal
|
|
855
|
+
*/
|
|
856
|
+
static base64UrlToBigInt(base64Url) {
|
|
857
|
+
if (core.Is.empty(base64Url)) {
|
|
858
|
+
return BigInt(0);
|
|
859
|
+
}
|
|
860
|
+
const bytes = core.Converter.base64UrlToBytes(base64Url);
|
|
861
|
+
const hexString = Array.from(bytes)
|
|
862
|
+
.map(byte => byte.toString(16).padStart(2, "0"))
|
|
863
|
+
.join("");
|
|
864
|
+
return BigInt(`0x${hexString}`);
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Encrypt the data using the public key.
|
|
868
|
+
* @param data The data to encrypt.
|
|
869
|
+
* @returns The data encrypted.
|
|
870
|
+
*/
|
|
871
|
+
async publicEncrypt(data) {
|
|
872
|
+
core.Guards.uint8Array(RSA._CLASS_NAME, "data", data);
|
|
873
|
+
if (data.length === 0) {
|
|
874
|
+
return new Uint8Array(0);
|
|
875
|
+
}
|
|
876
|
+
const keyDetails = await this.calculateKeyDetails();
|
|
877
|
+
const blocks = [];
|
|
878
|
+
// Split data into blocks of block size
|
|
879
|
+
for (let i = 0; i < data.length; i += keyDetails.blockSize) {
|
|
880
|
+
const block = data.slice(i, i + keyDetails.blockSize);
|
|
881
|
+
const encryptedBlock = node_crypto.publicEncrypt({
|
|
882
|
+
key: keyDetails.publicKey,
|
|
883
|
+
padding: node_crypto.constants.RSA_PKCS1_OAEP_PADDING
|
|
884
|
+
}, block);
|
|
885
|
+
blocks.push(encryptedBlock);
|
|
886
|
+
}
|
|
887
|
+
return core.Uint8ArrayHelper.concat(blocks);
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Encrypt the data using the private key.
|
|
891
|
+
* @param data The data to encrypt.
|
|
892
|
+
* @returns The data encrypted.
|
|
893
|
+
*/
|
|
894
|
+
async privateEncrypt(data) {
|
|
895
|
+
core.Guards.uint8Array(RSA._CLASS_NAME, "data", data);
|
|
896
|
+
const keyDetails = await this.calculateKeyDetails();
|
|
897
|
+
if (core.Is.empty(keyDetails.privateKey)) {
|
|
898
|
+
throw new core.GeneralError(RSA._CLASS_NAME, "noPrivateKey");
|
|
899
|
+
}
|
|
900
|
+
if (data.length === 0) {
|
|
901
|
+
return new Uint8Array(0);
|
|
902
|
+
}
|
|
903
|
+
const blocks = [];
|
|
904
|
+
// Split data into blocks of block size
|
|
905
|
+
for (let i = 0; i < data.length; i += keyDetails.blockSize) {
|
|
906
|
+
const block = data.slice(i, i + keyDetails.blockSize);
|
|
907
|
+
const encryptedBlock = node_crypto.privateEncrypt({
|
|
908
|
+
key: keyDetails.privateKey,
|
|
909
|
+
padding: node_crypto.constants.RSA_PKCS1_PADDING
|
|
910
|
+
}, block);
|
|
911
|
+
blocks.push(encryptedBlock);
|
|
912
|
+
}
|
|
913
|
+
return core.Uint8ArrayHelper.concat(blocks);
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Decrypt the data using the private key.
|
|
917
|
+
* @param data The data to decrypt.
|
|
918
|
+
* @returns The data decrypted.
|
|
919
|
+
* @throws GeneralError If no private key is provided.
|
|
920
|
+
*/
|
|
921
|
+
async privateDecrypt(data) {
|
|
922
|
+
core.Guards.uint8Array(RSA._CLASS_NAME, "data", data);
|
|
923
|
+
const keyDetails = await this.calculateKeyDetails();
|
|
924
|
+
if (core.Is.empty(keyDetails.privateKey)) {
|
|
925
|
+
throw new core.GeneralError(RSA._CLASS_NAME, "noPrivateKey");
|
|
926
|
+
}
|
|
927
|
+
if (data.length === 0) {
|
|
928
|
+
return new Uint8Array(0);
|
|
929
|
+
}
|
|
930
|
+
const blocks = [];
|
|
931
|
+
// Split encrypted data into blocks of key size
|
|
932
|
+
for (let i = 0; i < data.length; i += keyDetails.keySize) {
|
|
933
|
+
const block = data.slice(i, i + keyDetails.keySize);
|
|
934
|
+
const decryptedBlock = node_crypto.privateDecrypt({
|
|
935
|
+
key: keyDetails.privateKey,
|
|
936
|
+
padding: node_crypto.constants.RSA_PKCS1_OAEP_PADDING
|
|
937
|
+
}, block);
|
|
938
|
+
blocks.push(decryptedBlock);
|
|
939
|
+
}
|
|
940
|
+
return core.Uint8ArrayHelper.concat(blocks);
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Decrypt the data using the public key.
|
|
944
|
+
* @param data The data to decrypt.
|
|
945
|
+
* @returns The data decrypted.
|
|
946
|
+
*/
|
|
947
|
+
async publicDecrypt(data) {
|
|
948
|
+
core.Guards.uint8Array(RSA._CLASS_NAME, "data", data);
|
|
949
|
+
const keyDetails = await this.calculateKeyDetails();
|
|
950
|
+
if (data.length === 0) {
|
|
951
|
+
return new Uint8Array(0);
|
|
952
|
+
}
|
|
953
|
+
const blocks = [];
|
|
954
|
+
// Split encrypted data into blocks of key size
|
|
955
|
+
for (let i = 0; i < data.length; i += keyDetails.keySize) {
|
|
956
|
+
const block = data.slice(i, i + keyDetails.keySize);
|
|
957
|
+
const decryptedBlock = node_crypto.publicDecrypt({
|
|
958
|
+
key: keyDetails.publicKey,
|
|
959
|
+
padding: node_crypto.constants.RSA_PKCS1_PADDING
|
|
960
|
+
}, block);
|
|
961
|
+
blocks.push(decryptedBlock);
|
|
962
|
+
}
|
|
963
|
+
return core.Uint8ArrayHelper.concat(blocks);
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Calculate key details.
|
|
967
|
+
* @internal
|
|
968
|
+
*/
|
|
969
|
+
async calculateKeyDetails() {
|
|
970
|
+
if (core.Is.empty(this._publicKey) || core.Is.empty(this._keySize) || core.Is.empty(this._blockSize)) {
|
|
971
|
+
this._publicKey = node_crypto.createPublicKey({
|
|
972
|
+
key: Buffer.from(this._publicKeyBytes),
|
|
973
|
+
format: "der",
|
|
974
|
+
type: "spki"
|
|
975
|
+
});
|
|
976
|
+
if (!core.Is.empty(this._privateKeyBytes)) {
|
|
977
|
+
this._privateKey = node_crypto.createPrivateKey({
|
|
978
|
+
key: Buffer.from(this._privateKeyBytes),
|
|
979
|
+
format: "der",
|
|
980
|
+
type: "pkcs8"
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
// Get modulus length in bits from key details
|
|
984
|
+
const modulusLengthBits = this._publicKey.asymmetricKeyDetails?.modulusLength;
|
|
985
|
+
if (core.Is.empty(modulusLengthBits)) {
|
|
986
|
+
throw new core.GeneralError(RSA._CLASS_NAME, "invalidKeySize");
|
|
987
|
+
}
|
|
988
|
+
// Convert bits to bytes
|
|
989
|
+
this._keySize = Math.ceil(modulusLengthBits / 8);
|
|
990
|
+
// Calculate block size for OAEP with SHA-256
|
|
991
|
+
// Formula: keySize - 2 * hashLength - 2
|
|
992
|
+
const hashLength = 32; // SHA-256 = 32 bytes
|
|
993
|
+
// eslint-disable-next-line no-mixed-operators
|
|
994
|
+
this._blockSize = this._keySize - 2 * hashLength - 2;
|
|
995
|
+
}
|
|
996
|
+
return {
|
|
997
|
+
publicKey: this._publicKey,
|
|
998
|
+
privateKey: this._privateKey,
|
|
999
|
+
keySize: this._keySize,
|
|
1000
|
+
blockSize: this._blockSize
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// Copyright 2024 IOTA Stiftung.
|
|
1006
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
722
1007
|
/**
|
|
723
1008
|
* Implementation of X25519.
|
|
724
1009
|
*/
|
|
@@ -1526,6 +1811,48 @@ class Sha512 {
|
|
|
1526
1811
|
}
|
|
1527
1812
|
}
|
|
1528
1813
|
|
|
1814
|
+
// Copyright 2024 IOTA Stiftung.
|
|
1815
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
1816
|
+
/**
|
|
1817
|
+
* Helper class for working with PEM (Privacy-Enhanced Mail) formatted data.
|
|
1818
|
+
*/
|
|
1819
|
+
class PemHelper {
|
|
1820
|
+
/**
|
|
1821
|
+
* Runtime name for the class.
|
|
1822
|
+
* @internal
|
|
1823
|
+
*/
|
|
1824
|
+
static _CLASS_NAME = "PemHelper";
|
|
1825
|
+
/**
|
|
1826
|
+
* Strip the PEM content of its headers, footers, and newlines.
|
|
1827
|
+
* @param pemContent The PEM content to strip.
|
|
1828
|
+
* @returns The stripped PEM content in bas64 format.
|
|
1829
|
+
*/
|
|
1830
|
+
static stripPemMarkers(pemContent) {
|
|
1831
|
+
core.Guards.string(PemHelper._CLASS_NAME, "pemContent", pemContent);
|
|
1832
|
+
return pemContent
|
|
1833
|
+
.replace(/-----BEGIN.*-----/, "")
|
|
1834
|
+
.replace(/-----END.*-----/, "")
|
|
1835
|
+
.replace(/\n/g, "")
|
|
1836
|
+
.trim();
|
|
1837
|
+
}
|
|
1838
|
+
/**
|
|
1839
|
+
* Format the PEM content to have a specific line length.
|
|
1840
|
+
* @param marker The marker for the PEM content, e.g. RSA PRIVATE KEY
|
|
1841
|
+
* @param base64Content The base64 content to format.
|
|
1842
|
+
* @param lineLength The length of each line in the PEM content, default is 64 characters.
|
|
1843
|
+
* @returns The formatted PEM content.
|
|
1844
|
+
*/
|
|
1845
|
+
static formatPem(marker, base64Content, lineLength = 64) {
|
|
1846
|
+
core.Guards.stringValue(PemHelper._CLASS_NAME, "marker", marker);
|
|
1847
|
+
core.Guards.stringBase64(PemHelper._CLASS_NAME, "base64Content", base64Content);
|
|
1848
|
+
const lines = [];
|
|
1849
|
+
for (let i = 0; i < base64Content.length; i += lineLength) {
|
|
1850
|
+
lines.push(base64Content.slice(i, i + lineLength));
|
|
1851
|
+
}
|
|
1852
|
+
return [`-----BEGIN ${marker}-----`, ...lines, `-----END ${marker}-----`].join("\n");
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1529
1856
|
// Copyright 2024 IOTA Stiftung.
|
|
1530
1857
|
// SPDX-License-Identifier: Apache-2.0.
|
|
1531
1858
|
/**
|
|
@@ -1812,6 +2139,8 @@ exports.KeyType = KeyType;
|
|
|
1812
2139
|
exports.PasswordGenerator = PasswordGenerator;
|
|
1813
2140
|
exports.PasswordValidator = PasswordValidator;
|
|
1814
2141
|
exports.Pbkdf2 = Pbkdf2;
|
|
2142
|
+
exports.PemHelper = PemHelper;
|
|
2143
|
+
exports.RSA = RSA;
|
|
1815
2144
|
exports.Secp256k1 = Secp256k1;
|
|
1816
2145
|
exports.Sha1 = Sha1;
|
|
1817
2146
|
exports.Sha256 = Sha256;
|
package/dist/esm/index.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import { blake2b } from '@noble/hashes/blake2b';
|
|
|
6
6
|
import { HDKey as HDKey$1 } from '@scure/bip32';
|
|
7
7
|
import { HDKey } from 'micro-key-producer/slip10.js';
|
|
8
8
|
import { chacha20poly1305 } from '@noble/ciphers/chacha';
|
|
9
|
+
import { generateKeyPairSync, createPrivateKey, createPublicKey, publicEncrypt, constants, privateEncrypt, privateDecrypt, publicDecrypt } from 'node:crypto';
|
|
9
10
|
import { blake3 } from '@noble/hashes/blake3';
|
|
10
11
|
import { hmac } from '@noble/hashes/hmac';
|
|
11
12
|
import { sha1 } from '@noble/hashes/sha1';
|
|
@@ -695,8 +696,292 @@ class ChaCha20Poly1305 {
|
|
|
695
696
|
// Copyright 2024 IOTA Stiftung.
|
|
696
697
|
// SPDX-License-Identifier: Apache-2.0.
|
|
697
698
|
/**
|
|
698
|
-
*
|
|
699
|
+
* Implementation of the RSA cipher.
|
|
699
700
|
*/
|
|
701
|
+
class RSA {
|
|
702
|
+
/**
|
|
703
|
+
* Runtime name for the class.
|
|
704
|
+
* @internal
|
|
705
|
+
*/
|
|
706
|
+
static _CLASS_NAME = "RSA";
|
|
707
|
+
/**
|
|
708
|
+
* The public key for encryption.
|
|
709
|
+
* @internal
|
|
710
|
+
*/
|
|
711
|
+
_publicKeyBytes;
|
|
712
|
+
/**
|
|
713
|
+
* The private key for decryption.
|
|
714
|
+
* @internal
|
|
715
|
+
*/
|
|
716
|
+
_privateKeyBytes;
|
|
717
|
+
/**
|
|
718
|
+
* The public key for encryption.
|
|
719
|
+
* @internal
|
|
720
|
+
*/
|
|
721
|
+
_publicKey;
|
|
722
|
+
/**
|
|
723
|
+
* The private key for decryption.
|
|
724
|
+
* @internal
|
|
725
|
+
*/
|
|
726
|
+
_privateKey;
|
|
727
|
+
/**
|
|
728
|
+
* The block size for encryption.
|
|
729
|
+
* @internal
|
|
730
|
+
*/
|
|
731
|
+
_blockSize;
|
|
732
|
+
/**
|
|
733
|
+
* The key size for decryption.
|
|
734
|
+
* @internal
|
|
735
|
+
*/
|
|
736
|
+
_keySize;
|
|
737
|
+
/**
|
|
738
|
+
* Create a new instance of RSA.
|
|
739
|
+
* @param publicKey The public key for encryption (DER format as Uint8Array).
|
|
740
|
+
* @param privateKey The private key for decryption (DER format as Uint8Array).
|
|
741
|
+
*/
|
|
742
|
+
constructor(publicKey, privateKey) {
|
|
743
|
+
Guards.uint8Array(RSA._CLASS_NAME, "publicKey", publicKey);
|
|
744
|
+
this._publicKeyBytes = publicKey;
|
|
745
|
+
this._privateKeyBytes = privateKey;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Generate a new RSA key pair in PKCS8 format.
|
|
749
|
+
* @param modulusLength The key size in bits (default: 2048).
|
|
750
|
+
* @returns The public and private keys as Uint8Array.
|
|
751
|
+
*/
|
|
752
|
+
static async generateKeyPair(modulusLength = 2048) {
|
|
753
|
+
const { publicKey, privateKey } = generateKeyPairSync("rsa", {
|
|
754
|
+
modulusLength,
|
|
755
|
+
publicKeyEncoding: {
|
|
756
|
+
type: "spki",
|
|
757
|
+
format: "der"
|
|
758
|
+
},
|
|
759
|
+
privateKeyEncoding: {
|
|
760
|
+
type: "pkcs8",
|
|
761
|
+
format: "der"
|
|
762
|
+
}
|
|
763
|
+
});
|
|
764
|
+
return {
|
|
765
|
+
publicKey: new Uint8Array(publicKey),
|
|
766
|
+
privateKey: new Uint8Array(privateKey)
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Convert a PKCS1 key to a PKCS8 key.
|
|
771
|
+
* @param pkcs1Key The PKCS1 key as Uint8Array.
|
|
772
|
+
* @returns The PKCS8 key as Uint8Array.
|
|
773
|
+
*/
|
|
774
|
+
static async convertPkcs1ToPkcs8(pkcs1Key) {
|
|
775
|
+
Guards.uint8Array(RSA._CLASS_NAME, "pkcs1Key", pkcs1Key);
|
|
776
|
+
const privateKey = createPrivateKey({
|
|
777
|
+
key: Buffer.from(pkcs1Key),
|
|
778
|
+
format: "der",
|
|
779
|
+
type: "pkcs1"
|
|
780
|
+
});
|
|
781
|
+
return new Uint8Array(privateKey.export({
|
|
782
|
+
format: "der",
|
|
783
|
+
type: "pkcs8"
|
|
784
|
+
}));
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Break the private key down in to its components.
|
|
788
|
+
* @param pkcs8Key The PKCS8 key as Uint8Array.
|
|
789
|
+
* @returns The key components.
|
|
790
|
+
*/
|
|
791
|
+
static async getPrivateKeyComponents(pkcs8Key) {
|
|
792
|
+
Guards.uint8Array(RSA._CLASS_NAME, "pkcs8Key", pkcs8Key);
|
|
793
|
+
const privateKey = createPrivateKey({
|
|
794
|
+
key: Buffer.from(pkcs8Key),
|
|
795
|
+
format: "der",
|
|
796
|
+
type: "pkcs8"
|
|
797
|
+
});
|
|
798
|
+
const jwk = privateKey.export({ format: "jwk" });
|
|
799
|
+
return {
|
|
800
|
+
n: this.base64UrlToBigInt(jwk.n),
|
|
801
|
+
e: this.base64UrlToBigInt(jwk.e),
|
|
802
|
+
d: this.base64UrlToBigInt(jwk.d),
|
|
803
|
+
p: this.base64UrlToBigInt(jwk.p),
|
|
804
|
+
q: this.base64UrlToBigInt(jwk.q),
|
|
805
|
+
dp: this.base64UrlToBigInt(jwk.dp),
|
|
806
|
+
dq: this.base64UrlToBigInt(jwk.dq),
|
|
807
|
+
qi: this.base64UrlToBigInt(jwk.qi)
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Break the public key down in to its components.
|
|
812
|
+
* @param spkiKey The SPKI key as Uint8Array.
|
|
813
|
+
* @returns The key components.
|
|
814
|
+
*/
|
|
815
|
+
static async getPublicKeyComponents(spkiKey) {
|
|
816
|
+
Guards.uint8Array(RSA._CLASS_NAME, "spkiKey", spkiKey);
|
|
817
|
+
const publicKey = createPublicKey({
|
|
818
|
+
key: Buffer.from(spkiKey),
|
|
819
|
+
format: "der",
|
|
820
|
+
type: "spki"
|
|
821
|
+
});
|
|
822
|
+
const jwk = publicKey.export({ format: "jwk" });
|
|
823
|
+
return {
|
|
824
|
+
n: this.base64UrlToBigInt(jwk.n),
|
|
825
|
+
e: this.base64UrlToBigInt(jwk.e)
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Convert base64 encoded data to a big int.
|
|
830
|
+
* @param bytes The bytes to convert.
|
|
831
|
+
* @returns The bigint representation of the bytes.
|
|
832
|
+
* @internal
|
|
833
|
+
*/
|
|
834
|
+
static base64UrlToBigInt(base64Url) {
|
|
835
|
+
if (Is.empty(base64Url)) {
|
|
836
|
+
return BigInt(0);
|
|
837
|
+
}
|
|
838
|
+
const bytes = Converter.base64UrlToBytes(base64Url);
|
|
839
|
+
const hexString = Array.from(bytes)
|
|
840
|
+
.map(byte => byte.toString(16).padStart(2, "0"))
|
|
841
|
+
.join("");
|
|
842
|
+
return BigInt(`0x${hexString}`);
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* Encrypt the data using the public key.
|
|
846
|
+
* @param data The data to encrypt.
|
|
847
|
+
* @returns The data encrypted.
|
|
848
|
+
*/
|
|
849
|
+
async publicEncrypt(data) {
|
|
850
|
+
Guards.uint8Array(RSA._CLASS_NAME, "data", data);
|
|
851
|
+
if (data.length === 0) {
|
|
852
|
+
return new Uint8Array(0);
|
|
853
|
+
}
|
|
854
|
+
const keyDetails = await this.calculateKeyDetails();
|
|
855
|
+
const blocks = [];
|
|
856
|
+
// Split data into blocks of block size
|
|
857
|
+
for (let i = 0; i < data.length; i += keyDetails.blockSize) {
|
|
858
|
+
const block = data.slice(i, i + keyDetails.blockSize);
|
|
859
|
+
const encryptedBlock = publicEncrypt({
|
|
860
|
+
key: keyDetails.publicKey,
|
|
861
|
+
padding: constants.RSA_PKCS1_OAEP_PADDING
|
|
862
|
+
}, block);
|
|
863
|
+
blocks.push(encryptedBlock);
|
|
864
|
+
}
|
|
865
|
+
return Uint8ArrayHelper.concat(blocks);
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* Encrypt the data using the private key.
|
|
869
|
+
* @param data The data to encrypt.
|
|
870
|
+
* @returns The data encrypted.
|
|
871
|
+
*/
|
|
872
|
+
async privateEncrypt(data) {
|
|
873
|
+
Guards.uint8Array(RSA._CLASS_NAME, "data", data);
|
|
874
|
+
const keyDetails = await this.calculateKeyDetails();
|
|
875
|
+
if (Is.empty(keyDetails.privateKey)) {
|
|
876
|
+
throw new GeneralError(RSA._CLASS_NAME, "noPrivateKey");
|
|
877
|
+
}
|
|
878
|
+
if (data.length === 0) {
|
|
879
|
+
return new Uint8Array(0);
|
|
880
|
+
}
|
|
881
|
+
const blocks = [];
|
|
882
|
+
// Split data into blocks of block size
|
|
883
|
+
for (let i = 0; i < data.length; i += keyDetails.blockSize) {
|
|
884
|
+
const block = data.slice(i, i + keyDetails.blockSize);
|
|
885
|
+
const encryptedBlock = privateEncrypt({
|
|
886
|
+
key: keyDetails.privateKey,
|
|
887
|
+
padding: constants.RSA_PKCS1_PADDING
|
|
888
|
+
}, block);
|
|
889
|
+
blocks.push(encryptedBlock);
|
|
890
|
+
}
|
|
891
|
+
return Uint8ArrayHelper.concat(blocks);
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Decrypt the data using the private key.
|
|
895
|
+
* @param data The data to decrypt.
|
|
896
|
+
* @returns The data decrypted.
|
|
897
|
+
* @throws GeneralError If no private key is provided.
|
|
898
|
+
*/
|
|
899
|
+
async privateDecrypt(data) {
|
|
900
|
+
Guards.uint8Array(RSA._CLASS_NAME, "data", data);
|
|
901
|
+
const keyDetails = await this.calculateKeyDetails();
|
|
902
|
+
if (Is.empty(keyDetails.privateKey)) {
|
|
903
|
+
throw new GeneralError(RSA._CLASS_NAME, "noPrivateKey");
|
|
904
|
+
}
|
|
905
|
+
if (data.length === 0) {
|
|
906
|
+
return new Uint8Array(0);
|
|
907
|
+
}
|
|
908
|
+
const blocks = [];
|
|
909
|
+
// Split encrypted data into blocks of key size
|
|
910
|
+
for (let i = 0; i < data.length; i += keyDetails.keySize) {
|
|
911
|
+
const block = data.slice(i, i + keyDetails.keySize);
|
|
912
|
+
const decryptedBlock = privateDecrypt({
|
|
913
|
+
key: keyDetails.privateKey,
|
|
914
|
+
padding: constants.RSA_PKCS1_OAEP_PADDING
|
|
915
|
+
}, block);
|
|
916
|
+
blocks.push(decryptedBlock);
|
|
917
|
+
}
|
|
918
|
+
return Uint8ArrayHelper.concat(blocks);
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Decrypt the data using the public key.
|
|
922
|
+
* @param data The data to decrypt.
|
|
923
|
+
* @returns The data decrypted.
|
|
924
|
+
*/
|
|
925
|
+
async publicDecrypt(data) {
|
|
926
|
+
Guards.uint8Array(RSA._CLASS_NAME, "data", data);
|
|
927
|
+
const keyDetails = await this.calculateKeyDetails();
|
|
928
|
+
if (data.length === 0) {
|
|
929
|
+
return new Uint8Array(0);
|
|
930
|
+
}
|
|
931
|
+
const blocks = [];
|
|
932
|
+
// Split encrypted data into blocks of key size
|
|
933
|
+
for (let i = 0; i < data.length; i += keyDetails.keySize) {
|
|
934
|
+
const block = data.slice(i, i + keyDetails.keySize);
|
|
935
|
+
const decryptedBlock = publicDecrypt({
|
|
936
|
+
key: keyDetails.publicKey,
|
|
937
|
+
padding: constants.RSA_PKCS1_PADDING
|
|
938
|
+
}, block);
|
|
939
|
+
blocks.push(decryptedBlock);
|
|
940
|
+
}
|
|
941
|
+
return Uint8ArrayHelper.concat(blocks);
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Calculate key details.
|
|
945
|
+
* @internal
|
|
946
|
+
*/
|
|
947
|
+
async calculateKeyDetails() {
|
|
948
|
+
if (Is.empty(this._publicKey) || Is.empty(this._keySize) || Is.empty(this._blockSize)) {
|
|
949
|
+
this._publicKey = createPublicKey({
|
|
950
|
+
key: Buffer.from(this._publicKeyBytes),
|
|
951
|
+
format: "der",
|
|
952
|
+
type: "spki"
|
|
953
|
+
});
|
|
954
|
+
if (!Is.empty(this._privateKeyBytes)) {
|
|
955
|
+
this._privateKey = createPrivateKey({
|
|
956
|
+
key: Buffer.from(this._privateKeyBytes),
|
|
957
|
+
format: "der",
|
|
958
|
+
type: "pkcs8"
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
// Get modulus length in bits from key details
|
|
962
|
+
const modulusLengthBits = this._publicKey.asymmetricKeyDetails?.modulusLength;
|
|
963
|
+
if (Is.empty(modulusLengthBits)) {
|
|
964
|
+
throw new GeneralError(RSA._CLASS_NAME, "invalidKeySize");
|
|
965
|
+
}
|
|
966
|
+
// Convert bits to bytes
|
|
967
|
+
this._keySize = Math.ceil(modulusLengthBits / 8);
|
|
968
|
+
// Calculate block size for OAEP with SHA-256
|
|
969
|
+
// Formula: keySize - 2 * hashLength - 2
|
|
970
|
+
const hashLength = 32; // SHA-256 = 32 bytes
|
|
971
|
+
// eslint-disable-next-line no-mixed-operators
|
|
972
|
+
this._blockSize = this._keySize - 2 * hashLength - 2;
|
|
973
|
+
}
|
|
974
|
+
return {
|
|
975
|
+
publicKey: this._publicKey,
|
|
976
|
+
privateKey: this._privateKey,
|
|
977
|
+
keySize: this._keySize,
|
|
978
|
+
blockSize: this._blockSize
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// Copyright 2024 IOTA Stiftung.
|
|
984
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
700
985
|
/**
|
|
701
986
|
* Implementation of X25519.
|
|
702
987
|
*/
|
|
@@ -1504,6 +1789,48 @@ class Sha512 {
|
|
|
1504
1789
|
}
|
|
1505
1790
|
}
|
|
1506
1791
|
|
|
1792
|
+
// Copyright 2024 IOTA Stiftung.
|
|
1793
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
1794
|
+
/**
|
|
1795
|
+
* Helper class for working with PEM (Privacy-Enhanced Mail) formatted data.
|
|
1796
|
+
*/
|
|
1797
|
+
class PemHelper {
|
|
1798
|
+
/**
|
|
1799
|
+
* Runtime name for the class.
|
|
1800
|
+
* @internal
|
|
1801
|
+
*/
|
|
1802
|
+
static _CLASS_NAME = "PemHelper";
|
|
1803
|
+
/**
|
|
1804
|
+
* Strip the PEM content of its headers, footers, and newlines.
|
|
1805
|
+
* @param pemContent The PEM content to strip.
|
|
1806
|
+
* @returns The stripped PEM content in bas64 format.
|
|
1807
|
+
*/
|
|
1808
|
+
static stripPemMarkers(pemContent) {
|
|
1809
|
+
Guards.string(PemHelper._CLASS_NAME, "pemContent", pemContent);
|
|
1810
|
+
return pemContent
|
|
1811
|
+
.replace(/-----BEGIN.*-----/, "")
|
|
1812
|
+
.replace(/-----END.*-----/, "")
|
|
1813
|
+
.replace(/\n/g, "")
|
|
1814
|
+
.trim();
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Format the PEM content to have a specific line length.
|
|
1818
|
+
* @param marker The marker for the PEM content, e.g. RSA PRIVATE KEY
|
|
1819
|
+
* @param base64Content The base64 content to format.
|
|
1820
|
+
* @param lineLength The length of each line in the PEM content, default is 64 characters.
|
|
1821
|
+
* @returns The formatted PEM content.
|
|
1822
|
+
*/
|
|
1823
|
+
static formatPem(marker, base64Content, lineLength = 64) {
|
|
1824
|
+
Guards.stringValue(PemHelper._CLASS_NAME, "marker", marker);
|
|
1825
|
+
Guards.stringBase64(PemHelper._CLASS_NAME, "base64Content", base64Content);
|
|
1826
|
+
const lines = [];
|
|
1827
|
+
for (let i = 0; i < base64Content.length; i += lineLength) {
|
|
1828
|
+
lines.push(base64Content.slice(i, i + lineLength));
|
|
1829
|
+
}
|
|
1830
|
+
return [`-----BEGIN ${marker}-----`, ...lines, `-----END ${marker}-----`].join("\n");
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1507
1834
|
// Copyright 2024 IOTA Stiftung.
|
|
1508
1835
|
// SPDX-License-Identifier: Apache-2.0.
|
|
1509
1836
|
/**
|
|
@@ -1774,4 +2101,4 @@ class PasswordValidator {
|
|
|
1774
2101
|
}
|
|
1775
2102
|
}
|
|
1776
2103
|
|
|
1777
|
-
export { Bech32, Bip32Path, Bip39, Bip44, Blake2b, Blake3, ChaCha20Poly1305, Ed25519, HmacSha1, HmacSha256, HmacSha512, Hotp, KeyType, PasswordGenerator, PasswordValidator, Pbkdf2, Secp256k1, Sha1, Sha256, Sha3, Sha512, Slip0010, Totp, X25519, Zip215 };
|
|
2104
|
+
export { Bech32, Bip32Path, Bip39, Bip44, Blake2b, Blake3, ChaCha20Poly1305, Ed25519, HmacSha1, HmacSha256, HmacSha512, Hotp, KeyType, PasswordGenerator, PasswordValidator, Pbkdf2, PemHelper, RSA, Secp256k1, Sha1, Sha256, Sha3, Sha512, Slip0010, Totp, X25519, Zip215 };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Implementation of the RSA cipher.
|
|
3
|
+
*/
|
|
4
|
+
export declare class RSA {
|
|
5
|
+
/**
|
|
6
|
+
* Create a new instance of RSA.
|
|
7
|
+
* @param publicKey The public key for encryption (DER format as Uint8Array).
|
|
8
|
+
* @param privateKey The private key for decryption (DER format as Uint8Array).
|
|
9
|
+
*/
|
|
10
|
+
constructor(publicKey: Uint8Array, privateKey?: Uint8Array);
|
|
11
|
+
/**
|
|
12
|
+
* Generate a new RSA key pair in PKCS8 format.
|
|
13
|
+
* @param modulusLength The key size in bits (default: 2048).
|
|
14
|
+
* @returns The public and private keys as Uint8Array.
|
|
15
|
+
*/
|
|
16
|
+
static generateKeyPair(modulusLength?: number): Promise<{
|
|
17
|
+
publicKey: Uint8Array;
|
|
18
|
+
privateKey: Uint8Array;
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* Convert a PKCS1 key to a PKCS8 key.
|
|
22
|
+
* @param pkcs1Key The PKCS1 key as Uint8Array.
|
|
23
|
+
* @returns The PKCS8 key as Uint8Array.
|
|
24
|
+
*/
|
|
25
|
+
static convertPkcs1ToPkcs8(pkcs1Key: Uint8Array): Promise<Uint8Array>;
|
|
26
|
+
/**
|
|
27
|
+
* Break the private key down in to its components.
|
|
28
|
+
* @param pkcs8Key The PKCS8 key as Uint8Array.
|
|
29
|
+
* @returns The key components.
|
|
30
|
+
*/
|
|
31
|
+
static getPrivateKeyComponents(pkcs8Key: Uint8Array): Promise<{
|
|
32
|
+
n: bigint;
|
|
33
|
+
e: bigint;
|
|
34
|
+
d: bigint;
|
|
35
|
+
p: bigint;
|
|
36
|
+
q: bigint;
|
|
37
|
+
dp: bigint;
|
|
38
|
+
dq: bigint;
|
|
39
|
+
qi: bigint;
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
* Break the public key down in to its components.
|
|
43
|
+
* @param spkiKey The SPKI key as Uint8Array.
|
|
44
|
+
* @returns The key components.
|
|
45
|
+
*/
|
|
46
|
+
static getPublicKeyComponents(spkiKey: Uint8Array): Promise<{
|
|
47
|
+
n: bigint;
|
|
48
|
+
e: bigint;
|
|
49
|
+
}>;
|
|
50
|
+
/**
|
|
51
|
+
* Encrypt the data using the public key.
|
|
52
|
+
* @param data The data to encrypt.
|
|
53
|
+
* @returns The data encrypted.
|
|
54
|
+
*/
|
|
55
|
+
publicEncrypt(data: Uint8Array): Promise<Uint8Array>;
|
|
56
|
+
/**
|
|
57
|
+
* Encrypt the data using the private key.
|
|
58
|
+
* @param data The data to encrypt.
|
|
59
|
+
* @returns The data encrypted.
|
|
60
|
+
*/
|
|
61
|
+
privateEncrypt(data: Uint8Array): Promise<Uint8Array>;
|
|
62
|
+
/**
|
|
63
|
+
* Decrypt the data using the private key.
|
|
64
|
+
* @param data The data to decrypt.
|
|
65
|
+
* @returns The data decrypted.
|
|
66
|
+
* @throws GeneralError If no private key is provided.
|
|
67
|
+
*/
|
|
68
|
+
privateDecrypt(data: Uint8Array): Promise<Uint8Array>;
|
|
69
|
+
/**
|
|
70
|
+
* Decrypt the data using the public key.
|
|
71
|
+
* @param data The data to decrypt.
|
|
72
|
+
* @returns The data decrypted.
|
|
73
|
+
*/
|
|
74
|
+
publicDecrypt(data: Uint8Array): Promise<Uint8Array>;
|
|
75
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper class for working with PEM (Privacy-Enhanced Mail) formatted data.
|
|
3
|
+
*/
|
|
4
|
+
export declare class PemHelper {
|
|
5
|
+
/**
|
|
6
|
+
* Strip the PEM content of its headers, footers, and newlines.
|
|
7
|
+
* @param pemContent The PEM content to strip.
|
|
8
|
+
* @returns The stripped PEM content in bas64 format.
|
|
9
|
+
*/
|
|
10
|
+
static stripPemMarkers(pemContent: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Format the PEM content to have a specific line length.
|
|
13
|
+
* @param marker The marker for the PEM content, e.g. RSA PRIVATE KEY
|
|
14
|
+
* @param base64Content The base64 content to format.
|
|
15
|
+
* @param lineLength The length of each line in the PEM content, default is 64 characters.
|
|
16
|
+
* @returns The formatted PEM content.
|
|
17
|
+
*/
|
|
18
|
+
static formatPem(marker: string, base64Content: string, lineLength?: number): string;
|
|
19
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from "./address/bech32";
|
|
2
2
|
export * from "./address/bip44";
|
|
3
3
|
export * from "./ciphers/chaCha20Poly1305";
|
|
4
|
+
export * from "./ciphers/rsa";
|
|
4
5
|
export * from "./curves/ed25519";
|
|
5
6
|
export * from "./curves/secp256k1";
|
|
6
7
|
export * from "./curves/x25519";
|
|
@@ -15,6 +16,7 @@ export * from "./hashes/sha1";
|
|
|
15
16
|
export * from "./hashes/sha256";
|
|
16
17
|
export * from "./hashes/sha3";
|
|
17
18
|
export * from "./hashes/sha512";
|
|
19
|
+
export * from "./helpers/pemHelper";
|
|
18
20
|
export * from "./keys/bip32Path";
|
|
19
21
|
export * from "./keys/bip39";
|
|
20
22
|
export * from "./keys/slip0010";
|
package/docs/changelog.md
CHANGED
|
@@ -1,5 +1,92 @@
|
|
|
1
1
|
# @twin.org/crypto - Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.2-next.4](https://github.com/twinfoundation/framework/compare/crypto-v0.0.2-next.3...crypto-v0.0.2-next.4) (2025-08-15)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* additional RSA methods and async ([1fceee2](https://github.com/twinfoundation/framework/commit/1fceee2d1248a24a7620846025fcf906495c07f4))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Dependencies
|
|
12
|
+
|
|
13
|
+
* The following workspace dependencies were updated
|
|
14
|
+
* dependencies
|
|
15
|
+
* @twin.org/core bumped from 0.0.2-next.3 to 0.0.2-next.4
|
|
16
|
+
* @twin.org/nameof bumped from 0.0.2-next.3 to 0.0.2-next.4
|
|
17
|
+
* devDependencies
|
|
18
|
+
* @twin.org/nameof-transformer bumped from 0.0.2-next.3 to 0.0.2-next.4
|
|
19
|
+
* @twin.org/nameof-vitest-plugin bumped from 0.0.2-next.3 to 0.0.2-next.4
|
|
20
|
+
|
|
21
|
+
## [0.0.2-next.3](https://github.com/twinfoundation/framework/compare/crypto-v0.0.2-next.2...crypto-v0.0.2-next.3) (2025-08-06)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
|
|
26
|
+
* add guards arrayEndsWith and arrayStartsWith ([95d875e](https://github.com/twinfoundation/framework/commit/95d875ec8ccb4713c145fdde941d4cfedcec2ed3))
|
|
27
|
+
* add rsa cipher support ([7af6cc6](https://github.com/twinfoundation/framework/commit/7af6cc67512d3363bd4a2f2e87bd7733c2800147))
|
|
28
|
+
* add RSA support for public key components ([7126259](https://github.com/twinfoundation/framework/commit/7126259103b758c291e52a8a03818eb822d1aad1))
|
|
29
|
+
* change method accessibility ([c1b77fc](https://github.com/twinfoundation/framework/commit/c1b77fcfb61c092a01c97aebb2fe2dc2bbaa221b))
|
|
30
|
+
* relocate core packages from tools ([bcab8f3](https://github.com/twinfoundation/framework/commit/bcab8f3160442ea4fcaf442947462504f3d6a17d))
|
|
31
|
+
* update dependencies ([f3bd015](https://github.com/twinfoundation/framework/commit/f3bd015efd169196b7e0335f5cab876ba6ca1d75))
|
|
32
|
+
* use new shared store mechanism ([#131](https://github.com/twinfoundation/framework/issues/131)) ([934385b](https://github.com/twinfoundation/framework/commit/934385b2fbaf9f5c00a505ebf9d093bd5a425f55))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
### Dependencies
|
|
36
|
+
|
|
37
|
+
* The following workspace dependencies were updated
|
|
38
|
+
* dependencies
|
|
39
|
+
* @twin.org/core bumped from 0.0.2-next.2 to 0.0.2-next.3
|
|
40
|
+
* @twin.org/nameof bumped from 0.0.2-next.2 to 0.0.2-next.3
|
|
41
|
+
* devDependencies
|
|
42
|
+
* @twin.org/nameof-transformer bumped from 0.0.2-next.2 to 0.0.2-next.3
|
|
43
|
+
* @twin.org/nameof-vitest-plugin bumped from 0.0.2-next.2 to 0.0.2-next.3
|
|
44
|
+
|
|
45
|
+
## [0.0.2-next.2](https://github.com/twinfoundation/framework/compare/crypto-v0.0.2-next.1...crypto-v0.0.2-next.2) (2025-08-06)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
### Features
|
|
49
|
+
|
|
50
|
+
* add guards arrayEndsWith and arrayStartsWith ([95d875e](https://github.com/twinfoundation/framework/commit/95d875ec8ccb4713c145fdde941d4cfedcec2ed3))
|
|
51
|
+
* add rsa cipher support ([7af6cc6](https://github.com/twinfoundation/framework/commit/7af6cc67512d3363bd4a2f2e87bd7733c2800147))
|
|
52
|
+
* change method accessibility ([c1b77fc](https://github.com/twinfoundation/framework/commit/c1b77fcfb61c092a01c97aebb2fe2dc2bbaa221b))
|
|
53
|
+
* relocate core packages from tools ([bcab8f3](https://github.com/twinfoundation/framework/commit/bcab8f3160442ea4fcaf442947462504f3d6a17d))
|
|
54
|
+
* update dependencies ([f3bd015](https://github.com/twinfoundation/framework/commit/f3bd015efd169196b7e0335f5cab876ba6ca1d75))
|
|
55
|
+
* use new shared store mechanism ([#131](https://github.com/twinfoundation/framework/issues/131)) ([934385b](https://github.com/twinfoundation/framework/commit/934385b2fbaf9f5c00a505ebf9d093bd5a425f55))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
### Dependencies
|
|
59
|
+
|
|
60
|
+
* The following workspace dependencies were updated
|
|
61
|
+
* dependencies
|
|
62
|
+
* @twin.org/core bumped from 0.0.2-next.1 to 0.0.2-next.2
|
|
63
|
+
* @twin.org/nameof bumped from 0.0.2-next.1 to 0.0.2-next.2
|
|
64
|
+
* devDependencies
|
|
65
|
+
* @twin.org/nameof-transformer bumped from 0.0.2-next.1 to 0.0.2-next.2
|
|
66
|
+
* @twin.org/nameof-vitest-plugin bumped from 0.0.2-next.1 to 0.0.2-next.2
|
|
67
|
+
|
|
68
|
+
## [0.0.2-next.1](https://github.com/twinfoundation/framework/compare/crypto-v0.0.2-next.0...crypto-v0.0.2-next.1) (2025-08-06)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
### Features
|
|
72
|
+
|
|
73
|
+
* add guards arrayEndsWith and arrayStartsWith ([95d875e](https://github.com/twinfoundation/framework/commit/95d875ec8ccb4713c145fdde941d4cfedcec2ed3))
|
|
74
|
+
* add rsa cipher support ([7af6cc6](https://github.com/twinfoundation/framework/commit/7af6cc67512d3363bd4a2f2e87bd7733c2800147))
|
|
75
|
+
* relocate core packages from tools ([bcab8f3](https://github.com/twinfoundation/framework/commit/bcab8f3160442ea4fcaf442947462504f3d6a17d))
|
|
76
|
+
* update dependencies ([f3bd015](https://github.com/twinfoundation/framework/commit/f3bd015efd169196b7e0335f5cab876ba6ca1d75))
|
|
77
|
+
* use new shared store mechanism ([#131](https://github.com/twinfoundation/framework/issues/131)) ([934385b](https://github.com/twinfoundation/framework/commit/934385b2fbaf9f5c00a505ebf9d093bd5a425f55))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
### Dependencies
|
|
81
|
+
|
|
82
|
+
* The following workspace dependencies were updated
|
|
83
|
+
* dependencies
|
|
84
|
+
* @twin.org/core bumped from 0.0.2-next.0 to 0.0.2-next.1
|
|
85
|
+
* @twin.org/nameof bumped from 0.0.2-next.0 to 0.0.2-next.1
|
|
86
|
+
* devDependencies
|
|
87
|
+
* @twin.org/nameof-transformer bumped from 0.0.2-next.0 to 0.0.2-next.1
|
|
88
|
+
* @twin.org/nameof-vitest-plugin bumped from 0.0.2-next.0 to 0.0.2-next.1
|
|
89
|
+
|
|
3
90
|
## 0.0.1 (2025-07-03)
|
|
4
91
|
|
|
5
92
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Class: PemHelper
|
|
2
|
+
|
|
3
|
+
Helper class for working with PEM (Privacy-Enhanced Mail) formatted data.
|
|
4
|
+
|
|
5
|
+
## Constructors
|
|
6
|
+
|
|
7
|
+
### Constructor
|
|
8
|
+
|
|
9
|
+
> **new PemHelper**(): `PemHelper`
|
|
10
|
+
|
|
11
|
+
#### Returns
|
|
12
|
+
|
|
13
|
+
`PemHelper`
|
|
14
|
+
|
|
15
|
+
## Methods
|
|
16
|
+
|
|
17
|
+
### stripPemMarkers()
|
|
18
|
+
|
|
19
|
+
> `static` **stripPemMarkers**(`pemContent`): `string`
|
|
20
|
+
|
|
21
|
+
Strip the PEM content of its headers, footers, and newlines.
|
|
22
|
+
|
|
23
|
+
#### Parameters
|
|
24
|
+
|
|
25
|
+
##### pemContent
|
|
26
|
+
|
|
27
|
+
`string`
|
|
28
|
+
|
|
29
|
+
The PEM content to strip.
|
|
30
|
+
|
|
31
|
+
#### Returns
|
|
32
|
+
|
|
33
|
+
`string`
|
|
34
|
+
|
|
35
|
+
The stripped PEM content in bas64 format.
|
|
36
|
+
|
|
37
|
+
***
|
|
38
|
+
|
|
39
|
+
### formatPem()
|
|
40
|
+
|
|
41
|
+
> `static` **formatPem**(`marker`, `base64Content`, `lineLength`): `string`
|
|
42
|
+
|
|
43
|
+
Format the PEM content to have a specific line length.
|
|
44
|
+
|
|
45
|
+
#### Parameters
|
|
46
|
+
|
|
47
|
+
##### marker
|
|
48
|
+
|
|
49
|
+
`string`
|
|
50
|
+
|
|
51
|
+
The marker for the PEM content, e.g. RSA PRIVATE KEY
|
|
52
|
+
|
|
53
|
+
##### base64Content
|
|
54
|
+
|
|
55
|
+
`string`
|
|
56
|
+
|
|
57
|
+
The base64 content to format.
|
|
58
|
+
|
|
59
|
+
##### lineLength
|
|
60
|
+
|
|
61
|
+
`number` = `64`
|
|
62
|
+
|
|
63
|
+
The length of each line in the PEM content, default is 64 characters.
|
|
64
|
+
|
|
65
|
+
#### Returns
|
|
66
|
+
|
|
67
|
+
`string`
|
|
68
|
+
|
|
69
|
+
The formatted PEM content.
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# Class: RSA
|
|
2
|
+
|
|
3
|
+
Implementation of the RSA cipher.
|
|
4
|
+
|
|
5
|
+
## Constructors
|
|
6
|
+
|
|
7
|
+
### Constructor
|
|
8
|
+
|
|
9
|
+
> **new RSA**(`publicKey`, `privateKey?`): `RSA`
|
|
10
|
+
|
|
11
|
+
Create a new instance of RSA.
|
|
12
|
+
|
|
13
|
+
#### Parameters
|
|
14
|
+
|
|
15
|
+
##### publicKey
|
|
16
|
+
|
|
17
|
+
`Uint8Array`
|
|
18
|
+
|
|
19
|
+
The public key for encryption (DER format as Uint8Array).
|
|
20
|
+
|
|
21
|
+
##### privateKey?
|
|
22
|
+
|
|
23
|
+
`Uint8Array`\<`ArrayBufferLike`\>
|
|
24
|
+
|
|
25
|
+
The private key for decryption (DER format as Uint8Array).
|
|
26
|
+
|
|
27
|
+
#### Returns
|
|
28
|
+
|
|
29
|
+
`RSA`
|
|
30
|
+
|
|
31
|
+
## Methods
|
|
32
|
+
|
|
33
|
+
### generateKeyPair()
|
|
34
|
+
|
|
35
|
+
> `static` **generateKeyPair**(`modulusLength`): `Promise`\<\{ `publicKey`: `Uint8Array`; `privateKey`: `Uint8Array`; \}\>
|
|
36
|
+
|
|
37
|
+
Generate a new RSA key pair in PKCS8 format.
|
|
38
|
+
|
|
39
|
+
#### Parameters
|
|
40
|
+
|
|
41
|
+
##### modulusLength
|
|
42
|
+
|
|
43
|
+
`number` = `2048`
|
|
44
|
+
|
|
45
|
+
The key size in bits (default: 2048).
|
|
46
|
+
|
|
47
|
+
#### Returns
|
|
48
|
+
|
|
49
|
+
`Promise`\<\{ `publicKey`: `Uint8Array`; `privateKey`: `Uint8Array`; \}\>
|
|
50
|
+
|
|
51
|
+
The public and private keys as Uint8Array.
|
|
52
|
+
|
|
53
|
+
***
|
|
54
|
+
|
|
55
|
+
### convertPkcs1ToPkcs8()
|
|
56
|
+
|
|
57
|
+
> `static` **convertPkcs1ToPkcs8**(`pkcs1Key`): `Promise`\<`Uint8Array`\<`ArrayBufferLike`\>\>
|
|
58
|
+
|
|
59
|
+
Convert a PKCS1 key to a PKCS8 key.
|
|
60
|
+
|
|
61
|
+
#### Parameters
|
|
62
|
+
|
|
63
|
+
##### pkcs1Key
|
|
64
|
+
|
|
65
|
+
`Uint8Array`
|
|
66
|
+
|
|
67
|
+
The PKCS1 key as Uint8Array.
|
|
68
|
+
|
|
69
|
+
#### Returns
|
|
70
|
+
|
|
71
|
+
`Promise`\<`Uint8Array`\<`ArrayBufferLike`\>\>
|
|
72
|
+
|
|
73
|
+
The PKCS8 key as Uint8Array.
|
|
74
|
+
|
|
75
|
+
***
|
|
76
|
+
|
|
77
|
+
### getPrivateKeyComponents()
|
|
78
|
+
|
|
79
|
+
> `static` **getPrivateKeyComponents**(`pkcs8Key`): `Promise`\<\{ `n`: `bigint`; `e`: `bigint`; `d`: `bigint`; `p`: `bigint`; `q`: `bigint`; `dp`: `bigint`; `dq`: `bigint`; `qi`: `bigint`; \}\>
|
|
80
|
+
|
|
81
|
+
Break the private key down in to its components.
|
|
82
|
+
|
|
83
|
+
#### Parameters
|
|
84
|
+
|
|
85
|
+
##### pkcs8Key
|
|
86
|
+
|
|
87
|
+
`Uint8Array`
|
|
88
|
+
|
|
89
|
+
The PKCS8 key as Uint8Array.
|
|
90
|
+
|
|
91
|
+
#### Returns
|
|
92
|
+
|
|
93
|
+
`Promise`\<\{ `n`: `bigint`; `e`: `bigint`; `d`: `bigint`; `p`: `bigint`; `q`: `bigint`; `dp`: `bigint`; `dq`: `bigint`; `qi`: `bigint`; \}\>
|
|
94
|
+
|
|
95
|
+
The key components.
|
|
96
|
+
|
|
97
|
+
***
|
|
98
|
+
|
|
99
|
+
### getPublicKeyComponents()
|
|
100
|
+
|
|
101
|
+
> `static` **getPublicKeyComponents**(`spkiKey`): `Promise`\<\{ `n`: `bigint`; `e`: `bigint`; \}\>
|
|
102
|
+
|
|
103
|
+
Break the public key down in to its components.
|
|
104
|
+
|
|
105
|
+
#### Parameters
|
|
106
|
+
|
|
107
|
+
##### spkiKey
|
|
108
|
+
|
|
109
|
+
`Uint8Array`
|
|
110
|
+
|
|
111
|
+
The SPKI key as Uint8Array.
|
|
112
|
+
|
|
113
|
+
#### Returns
|
|
114
|
+
|
|
115
|
+
`Promise`\<\{ `n`: `bigint`; `e`: `bigint`; \}\>
|
|
116
|
+
|
|
117
|
+
The key components.
|
|
118
|
+
|
|
119
|
+
***
|
|
120
|
+
|
|
121
|
+
### publicEncrypt()
|
|
122
|
+
|
|
123
|
+
> **publicEncrypt**(`data`): `Promise`\<`Uint8Array`\<`ArrayBufferLike`\>\>
|
|
124
|
+
|
|
125
|
+
Encrypt the data using the public key.
|
|
126
|
+
|
|
127
|
+
#### Parameters
|
|
128
|
+
|
|
129
|
+
##### data
|
|
130
|
+
|
|
131
|
+
`Uint8Array`
|
|
132
|
+
|
|
133
|
+
The data to encrypt.
|
|
134
|
+
|
|
135
|
+
#### Returns
|
|
136
|
+
|
|
137
|
+
`Promise`\<`Uint8Array`\<`ArrayBufferLike`\>\>
|
|
138
|
+
|
|
139
|
+
The data encrypted.
|
|
140
|
+
|
|
141
|
+
***
|
|
142
|
+
|
|
143
|
+
### privateEncrypt()
|
|
144
|
+
|
|
145
|
+
> **privateEncrypt**(`data`): `Promise`\<`Uint8Array`\<`ArrayBufferLike`\>\>
|
|
146
|
+
|
|
147
|
+
Encrypt the data using the private key.
|
|
148
|
+
|
|
149
|
+
#### Parameters
|
|
150
|
+
|
|
151
|
+
##### data
|
|
152
|
+
|
|
153
|
+
`Uint8Array`
|
|
154
|
+
|
|
155
|
+
The data to encrypt.
|
|
156
|
+
|
|
157
|
+
#### Returns
|
|
158
|
+
|
|
159
|
+
`Promise`\<`Uint8Array`\<`ArrayBufferLike`\>\>
|
|
160
|
+
|
|
161
|
+
The data encrypted.
|
|
162
|
+
|
|
163
|
+
***
|
|
164
|
+
|
|
165
|
+
### privateDecrypt()
|
|
166
|
+
|
|
167
|
+
> **privateDecrypt**(`data`): `Promise`\<`Uint8Array`\<`ArrayBufferLike`\>\>
|
|
168
|
+
|
|
169
|
+
Decrypt the data using the private key.
|
|
170
|
+
|
|
171
|
+
#### Parameters
|
|
172
|
+
|
|
173
|
+
##### data
|
|
174
|
+
|
|
175
|
+
`Uint8Array`
|
|
176
|
+
|
|
177
|
+
The data to decrypt.
|
|
178
|
+
|
|
179
|
+
#### Returns
|
|
180
|
+
|
|
181
|
+
`Promise`\<`Uint8Array`\<`ArrayBufferLike`\>\>
|
|
182
|
+
|
|
183
|
+
The data decrypted.
|
|
184
|
+
|
|
185
|
+
#### Throws
|
|
186
|
+
|
|
187
|
+
GeneralError If no private key is provided.
|
|
188
|
+
|
|
189
|
+
***
|
|
190
|
+
|
|
191
|
+
### publicDecrypt()
|
|
192
|
+
|
|
193
|
+
> **publicDecrypt**(`data`): `Promise`\<`Uint8Array`\<`ArrayBufferLike`\>\>
|
|
194
|
+
|
|
195
|
+
Decrypt the data using the public key.
|
|
196
|
+
|
|
197
|
+
#### Parameters
|
|
198
|
+
|
|
199
|
+
##### data
|
|
200
|
+
|
|
201
|
+
`Uint8Array`
|
|
202
|
+
|
|
203
|
+
The data to decrypt.
|
|
204
|
+
|
|
205
|
+
#### Returns
|
|
206
|
+
|
|
207
|
+
`Promise`\<`Uint8Array`\<`ArrayBufferLike`\>\>
|
|
208
|
+
|
|
209
|
+
The data decrypted.
|
package/docs/reference/index.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
- [Bech32](classes/Bech32.md)
|
|
6
6
|
- [Bip44](classes/Bip44.md)
|
|
7
7
|
- [ChaCha20Poly1305](classes/ChaCha20Poly1305.md)
|
|
8
|
+
- [RSA](classes/RSA.md)
|
|
8
9
|
- [Ed25519](classes/Ed25519.md)
|
|
9
10
|
- [Secp256k1](classes/Secp256k1.md)
|
|
10
11
|
- [X25519](classes/X25519.md)
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
- [Sha256](classes/Sha256.md)
|
|
20
21
|
- [Sha3](classes/Sha3.md)
|
|
21
22
|
- [Sha512](classes/Sha512.md)
|
|
23
|
+
- [PemHelper](classes/PemHelper.md)
|
|
22
24
|
- [Bip32Path](classes/Bip32Path.md)
|
|
23
25
|
- [Bip39](classes/Bip39.md)
|
|
24
26
|
- [Slip0010](classes/Slip0010.md)
|
package/locales/en.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twin.org/crypto",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2-next.4",
|
|
4
4
|
"description": "Contains helper methods and classes which implement cryptographic functions",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
"@scure/base": "1.2.6",
|
|
21
21
|
"@scure/bip32": "1.7.0",
|
|
22
22
|
"@scure/bip39": "1.6.0",
|
|
23
|
-
"@twin.org/core": "
|
|
24
|
-
"@twin.org/nameof": "
|
|
23
|
+
"@twin.org/core": "0.0.2-next.4",
|
|
24
|
+
"@twin.org/nameof": "0.0.2-next.4",
|
|
25
|
+
"crypto-browserify": "3.12.1",
|
|
25
26
|
"micro-key-producer": "0.7.6"
|
|
26
27
|
},
|
|
27
28
|
"main": "./dist/cjs/index.cjs",
|