|
1 | | -import { expect } from 'chai'; |
| 1 | +import { assert, expect } from 'chai'; |
2 | 2 | import { Buffer } from '@craftzdog/react-native-buffer'; |
3 | 3 | import { |
4 | 4 | fromByteArray, |
@@ -108,83 +108,6 @@ describe('subtle - importKey / exportKey', () => { |
108 | 108 | ]), |
109 | 109 | 'Invalid argument type for "key". Need ArrayBuffer, TypedArray, KeyObject, CryptoKey, string', |
110 | 110 | ); |
111 | | - await assertThrowsAsync( |
112 | | - async () => |
113 | | - await subtle.importKey( |
114 | | - 'raw', |
115 | | - keyData, |
116 | | - { |
117 | | - name: 'HMAC', |
118 | | - }, |
119 | | - false, |
120 | | - ['sign', 'verify'], |
121 | | - ), |
122 | | - '"subtle.importKey()" is not implemented for HMAC', |
123 | | - // TODO: will be ERR_MISSING_OPTION or similar |
124 | | - ); |
125 | | - await assertThrowsAsync( |
126 | | - async () => |
127 | | - await subtle.importKey( |
128 | | - 'raw', |
129 | | - keyData, |
130 | | - { |
131 | | - name: 'HMAC', |
132 | | - hash: 'SHA-256', |
133 | | - }, |
134 | | - false, |
135 | | - ['deriveBits'], |
136 | | - ), |
137 | | - '"subtle.importKey()" is not implemented for HMAC', |
138 | | - // TODO: will be 'Unsupported key usage for an HMAC key' |
139 | | - ); |
140 | | - await assertThrowsAsync( |
141 | | - async () => |
142 | | - await subtle.importKey( |
143 | | - 'raw', |
144 | | - keyData, |
145 | | - { |
146 | | - name: 'HMAC', |
147 | | - hash: 'SHA-256', |
148 | | - length: 0, |
149 | | - }, |
150 | | - false, |
151 | | - ['sign', 'verify'], |
152 | | - ), |
153 | | - '"subtle.importKey()" is not implemented for HMAC', |
154 | | - // TODO: will be 'Zero-length key is not supported' |
155 | | - ); |
156 | | - await assertThrowsAsync( |
157 | | - async () => |
158 | | - await subtle.importKey( |
159 | | - 'raw', |
160 | | - keyData, |
161 | | - { |
162 | | - name: 'HMAC', |
163 | | - hash: 'SHA-256', |
164 | | - length: 1, |
165 | | - }, |
166 | | - false, |
167 | | - ['sign', 'verify'], |
168 | | - ), |
169 | | - '"subtle.importKey()" is not implemented for HMAC', |
170 | | - // TODO: will be 'Invalid key length' |
171 | | - ); |
172 | | - await assertThrowsAsync( |
173 | | - async () => |
174 | | - await subtle.importKey( |
175 | | - 'jwk', |
176 | | - // @ts-expect-error bad key data |
177 | | - null, |
178 | | - { |
179 | | - name: 'HMAC', |
180 | | - hash: 'SHA-256', |
181 | | - }, |
182 | | - false, |
183 | | - ['sign', 'verify'], |
184 | | - ), |
185 | | - '"subtle.importKey()" is not implemented for HMAC', |
186 | | - // TODO: will be 'Invalid keyData' |
187 | | - ); |
188 | 111 | }); |
189 | 112 |
|
190 | 113 | it('Good Input - Uint8Array', async () => { |
@@ -862,44 +785,223 @@ describe('subtle - importKey / exportKey', () => { |
862 | 785 | } |
863 | 786 | } |
864 | 787 |
|
865 | | - // // Import/Export HMAC Secret Key |
866 | | - // // TODO: enable this after implementing HMAC import/export |
867 | | - // // from Node.js https://github.com/nodejs/node/blob/main/test/parallel/test-webcrypto-export-import.js#L73-L113 |
868 | | - // const keyData = globalThis.crypto.getRandomValues(new Uint8Array(32)); |
869 | | - // const key = await subtle.importKey( |
870 | | - // 'raw', |
871 | | - // keyData, { |
872 | | - // name: 'HMAC', |
873 | | - // hash: 'SHA-256' |
874 | | - // }, true, ['sign', 'verify']); |
| 788 | + // Import/Export HMAC Secret Key |
| 789 | + it('HMAC should import raw HMAC key', async () => { |
| 790 | + const keyData = crypto.getRandomValues(new Uint8Array(32)); |
| 791 | + const key = await subtle.importKey( |
| 792 | + 'raw', |
| 793 | + keyData, |
| 794 | + { |
| 795 | + name: 'HMAC', |
| 796 | + hash: 'SHA-256', |
| 797 | + }, |
| 798 | + true, |
| 799 | + ['sign', 'verify'], |
| 800 | + ); |
| 801 | + |
| 802 | + assert.strictEqual(key.algorithm, key.algorithm); |
| 803 | + assert.strictEqual(key.usages, key.usages); |
| 804 | + |
| 805 | + const raw = await subtle.exportKey('raw', key); |
| 806 | + |
| 807 | + assert.instanceOf(raw, ArrayBuffer); |
| 808 | + |
| 809 | + assert.deepStrictEqual( |
| 810 | + Buffer.from(keyData).toString('hex'), |
| 811 | + Buffer.from(raw).toString('hex'), |
| 812 | + ); |
| 813 | + |
| 814 | + const jwk = (await subtle.exportKey('jwk', key)) as JWK; |
| 815 | + |
| 816 | + assert.property(jwk, 'key_ops'); |
| 817 | + assert.property(jwk, 'kty'); |
| 818 | + |
| 819 | + assert.deepStrictEqual(jwk.key_ops, ['sign', 'verify']); |
| 820 | + assert(jwk.ext); |
| 821 | + |
| 822 | + assert.strictEqual(jwk.kty, 'oct'); |
| 823 | + |
| 824 | + assert.isDefined(jwk.k); |
| 825 | + |
| 826 | + assert.deepStrictEqual( |
| 827 | + Buffer.from(jwk.k, 'base64').toString('hex'), |
| 828 | + Buffer.from(raw).toString('hex'), |
| 829 | + ); |
| 830 | + |
| 831 | + await assertThrowsAsync( |
| 832 | + async () => |
| 833 | + await subtle.importKey( |
| 834 | + 'raw', |
| 835 | + keyData, |
| 836 | + { |
| 837 | + name: 'HMAC', |
| 838 | + hash: 'SHA-256', |
| 839 | + }, |
| 840 | + true, |
| 841 | + [ |
| 842 | + /* empty usages */ |
| 843 | + ], |
| 844 | + ), |
| 845 | + 'Usages cannot be empty when importing a secret key.', |
| 846 | + ); |
| 847 | + }); |
| 848 | + |
| 849 | + it('HMAC should import JWK HMAC key', async () => { |
| 850 | + const jwk: JWK = { |
| 851 | + kty: 'oct', |
| 852 | + k: 'Y0zt37HgOx-BY7SQjYVmrqhPkO44Ii2Jcb9yydUDPfE', |
| 853 | + alg: 'HS256', |
| 854 | + ext: true, |
| 855 | + key_ops: ['sign', 'verify'], |
| 856 | + }; |
| 857 | + |
| 858 | + const key = await subtle.importKey( |
| 859 | + 'jwk', |
| 860 | + jwk, |
| 861 | + { |
| 862 | + name: 'HMAC', |
| 863 | + hash: 'SHA-256', |
| 864 | + }, |
| 865 | + true, |
| 866 | + ['sign', 'verify'], |
| 867 | + ); |
| 868 | + |
| 869 | + expect(key.type).to.equal('secret'); |
| 870 | + expect(key.extractable).to.equal(true); |
| 871 | + expect(key.algorithm.name).to.equal('HMAC'); |
| 872 | + expect(key.usages).to.have.members(['sign', 'verify']); |
| 873 | + }); |
| 874 | + |
| 875 | + it('HMAC should reject invalid key usages', async () => { |
| 876 | + const keyData = crypto.getRandomValues(new Uint8Array(32)); |
| 877 | + |
| 878 | + await assertThrowsAsync( |
| 879 | + async () => |
| 880 | + await subtle.importKey( |
| 881 | + 'raw', |
| 882 | + keyData, |
| 883 | + { |
| 884 | + name: 'HMAC', |
| 885 | + hash: 'SHA-256', |
| 886 | + }, |
| 887 | + true, |
| 888 | + ['encrypt'], // invalid usage for HMAC |
| 889 | + ), |
| 890 | + 'Unsupported key usage for an HMAC key', |
| 891 | + ); |
| 892 | + }); |
| 893 | + |
| 894 | + it('HMAC should reject invalid JWK format', async () => { |
| 895 | + const invalidJwk: JWK = { |
| 896 | + kty: 'RSA', // wrong key type |
| 897 | + k: 'Y0zt37HgOx-BY7SQjYVmrqhPkO44Ii2Jcb9yydUDPfE', |
| 898 | + }; |
| 899 | + |
| 900 | + await assertThrowsAsync( |
| 901 | + async () => |
| 902 | + await subtle.importKey( |
| 903 | + 'jwk', |
| 904 | + invalidJwk, |
| 905 | + { |
| 906 | + name: 'HMAC', |
| 907 | + hash: 'SHA-256', |
| 908 | + }, |
| 909 | + true, |
| 910 | + ['sign', 'verify'], |
| 911 | + ), |
| 912 | + 'Invalid JWK format for HMAC key', |
| 913 | + ); |
| 914 | + }); |
| 915 | + |
| 916 | + it('HMAC should reject invalid key length', async () => { |
| 917 | + const jwk: JWK = { |
| 918 | + kty: 'oct', |
| 919 | + k: 'Y0zt37HgOx-BY7SQjYVmrqhPkO44Ii2Jcb9yydUDPfE', |
| 920 | + alg: 'HS256', |
| 921 | + ext: true, |
| 922 | + }; |
| 923 | + |
| 924 | + await assertThrowsAsync( |
| 925 | + async () => |
| 926 | + await subtle.importKey( |
| 927 | + 'jwk', |
| 928 | + jwk, |
| 929 | + { |
| 930 | + name: 'HMAC', |
| 931 | + hash: 'SHA-256', |
| 932 | + length: 128, // Doesn't match the actual key length |
| 933 | + }, |
| 934 | + true, |
| 935 | + ['sign', 'verify'], |
| 936 | + ), |
| 937 | + 'Invalid key length', |
| 938 | + ); |
| 939 | + }); |
| 940 | + |
| 941 | + it('HMAC should reject invalid zero key length', async () => { |
| 942 | + const jwk: JWK = { |
| 943 | + kty: 'oct', |
| 944 | + k: 'Y0zt37HgOx-BY7SQjYVmrqhPkO44Ii2Jcb9yydUDPfE', |
| 945 | + alg: 'HS256', |
| 946 | + ext: true, |
| 947 | + }; |
875 | 948 |
|
876 | | - // const raw = await subtle.exportKey('raw', key); |
877 | | - |
878 | | - // expect( |
879 | | - // Buffer.from(keyData).toString('hex')).to.equal( |
880 | | - // Buffer.from(raw).toString('hex')); |
881 | | - |
882 | | - // const jwk = await subtle.exportKey('jwk', key); |
883 | | - // expect(jwk.key_ops).to.have.all.members(['sign', 'verify']); |
884 | | - // assert(jwk.ext); |
885 | | - // expect(jwk.kty, 'oct'); |
886 | | - |
887 | | - // expect( |
888 | | - // TODO: gonna be ab2str(base64toArrayBuffer(jwk.k)) like above ^^^^ |
889 | | - // Buffer.from(jwk.k, 'base64').toString('hex')).to.equal( |
890 | | - // Buffer.from(raw).toString('hex')); |
891 | | - |
892 | | - // await assert.rejects( |
893 | | - // subtle.importKey( |
894 | | - // 'raw', |
895 | | - // keyData, |
896 | | - // { |
897 | | - // name: 'HMAC', |
898 | | - // hash: 'SHA-256' |
899 | | - // }, |
900 | | - // true, |
901 | | - // [// empty usages ]), |
902 | | - // { name: 'SyntaxError', message: 'Usages cannot be empty when importing a secret key.' }); |
| 949 | + await assertThrowsAsync( |
| 950 | + async () => |
| 951 | + await subtle.importKey( |
| 952 | + 'jwk', |
| 953 | + jwk, |
| 954 | + { |
| 955 | + name: 'HMAC', |
| 956 | + hash: 'SHA-256', |
| 957 | + length: 0, // Doesn't match the actual key length |
| 958 | + }, |
| 959 | + true, |
| 960 | + ['sign', 'verify'], |
| 961 | + ), |
| 962 | + 'Zero-length key is not supported', |
| 963 | + ); |
| 964 | + }); |
| 965 | + |
| 966 | + it('HMAC should reject invalid keyData', async () => { |
| 967 | + await assertThrowsAsync( |
| 968 | + async () => |
| 969 | + await subtle.importKey( |
| 970 | + 'jwk', |
| 971 | + /** |
| 972 | + * Force JWT ts validation, it just ensure that if someone use an invalide type then |
| 973 | + * we throw an error even if they don't use typescript |
| 974 | + */ |
| 975 | + null as unknown as JWK, |
| 976 | + { |
| 977 | + name: 'HMAC', |
| 978 | + hash: 'SHA-256', |
| 979 | + }, |
| 980 | + true, |
| 981 | + ['sign', 'verify'], |
| 982 | + ), |
| 983 | + 'Invalid keyData', |
| 984 | + ); |
| 985 | + }); |
| 986 | + |
| 987 | + it('HMAC should reject unsupported import format', async () => { |
| 988 | + const keyData = crypto.getRandomValues(new Uint8Array(32)); |
| 989 | + |
| 990 | + await assertThrowsAsync( |
| 991 | + async () => |
| 992 | + await subtle.importKey( |
| 993 | + 'spki', // unsupported format for HMAC |
| 994 | + keyData, |
| 995 | + { |
| 996 | + name: 'HMAC', |
| 997 | + hash: 'SHA-256', |
| 998 | + }, |
| 999 | + true, |
| 1000 | + ['sign', 'verify'], |
| 1001 | + ), |
| 1002 | + 'Unable to import HMAC key with format spki', |
| 1003 | + ); |
| 1004 | + }); |
903 | 1005 |
|
904 | 1006 | // Import/Export RSA Key Pairs |
905 | 1007 | // from Node.js https://github.com/nodejs/node/blob/main/test/parallel/test-webcrypto-export-import.js#L157-L215 |
|
0 commit comments