@@ -6,8 +6,9 @@ import EmbeddedPolicy from './models/Policy/EmbeddedPolicy.js';
6
6
import Payload from './models/Payload.js' ;
7
7
import getHkdfSalt from './helpers/getHkdfSalt.js' ;
8
8
import { getBitLength as authTagLengthForCipher } from './models/Ciphers.js' ;
9
- import { lengthOfBinding } from './helpers/calculateByCipher.js' ;
10
9
import { TypedArray } from '../tdf/index.js' ;
10
+ import { GMAC_BINDING_LEN } from './constants.js' ;
11
+ import { AlgorithmName , KeyFormat , KeyUsageType } from './../nanotdf-crypto/enums.js' ;
11
12
12
13
import {
13
14
encrypt as cryptoEncrypt ,
@@ -16,6 +17,7 @@ import {
16
17
exportCryptoKey ,
17
18
} from '../nanotdf-crypto/index.js' ;
18
19
import { KasPublicKeyInfo } from '../access.js' ;
20
+ import { computeECDSASig , extractRSValuesFromSignature } from '../nanotdf-crypto/ecdsaSignature.js' ;
19
21
20
22
/**
21
23
* Encrypt the plain data into nanotdf buffer
@@ -25,13 +27,15 @@ import { KasPublicKeyInfo } from '../access.js';
25
27
* @param ephemeralKeyPair SDK ephemeral key pair to generate symmetric key
26
28
* @param iv
27
29
* @param data The data to be encrypted
30
+ * @param ecdsaBinding Flag to enable ECDSA binding
28
31
*/
29
32
export default async function encrypt (
30
33
policy : string ,
31
34
kasInfo : KasPublicKeyInfo ,
32
35
ephemeralKeyPair : CryptoKeyPair ,
33
36
iv : Uint8Array ,
34
- data : string | TypedArray | ArrayBuffer
37
+ data : string | TypedArray | ArrayBuffer ,
38
+ ecdsaBinding : boolean = DefaultParams . ecdsaBinding
35
39
) : Promise < ArrayBuffer > {
36
40
// Generate a symmetric key.
37
41
if ( ! ephemeralKeyPair . privateKey ) {
@@ -60,33 +64,34 @@ export default async function encrypt(
60
64
authTagLengthInBytes * 8
61
65
) ;
62
66
63
- // Enable - once ecdsaBinding is true
64
- // if (!DefaultParams.ecdsaBinding) {
65
- // throw new Error("ECDSA binding should enable by default.");
66
- // }
67
-
68
- // // Calculate the policy binding.
69
- // const policyBinding = await calculateSignature(this.ephemeralKeyPair.privateKey, new Uint8Array(encryptedPolicy));
70
- // console.log("Length of the policyBinding " + policyBinding.byteLength);
71
-
72
- // // Create embedded policy
73
- // const embeddedPolicy = new EmbeddedPolicy(DefaultParams.policyType,
74
- // new Uint8Array(policyBinding),
75
- // new Uint8Array(encryptedPolicy)
76
- // );
67
+ let policyBinding : Uint8Array ;
77
68
78
69
// Calculate the policy binding.
79
- const lengthOfPolicyBinding = lengthOfBinding (
80
- DefaultParams . ecdsaBinding ,
81
- DefaultParams . ephemeralCurveName
82
- ) ;
83
-
84
- const policyBinding = await digest ( 'SHA-256' , new Uint8Array ( encryptedPolicy ) ) ;
70
+ if ( ecdsaBinding ) {
71
+ const curveName = await getCurveNameFromPrivateKey ( ephemeralKeyPair . privateKey ) ;
72
+ const ecdsaPrivateKey = await convertECDHToECDSA ( ephemeralKeyPair . privateKey , curveName ) ;
73
+ const ecdsaSignature = await computeECDSASig ( ecdsaPrivateKey , new Uint8Array ( encryptedPolicy ) ) ;
74
+ const { r, s } = extractRSValuesFromSignature ( new Uint8Array ( ecdsaSignature ) ) ;
75
+
76
+ const rLength = r . length ;
77
+ const sLength = s . length ;
78
+
79
+ policyBinding = new Uint8Array ( 1 + rLength + 1 + sLength ) ;
80
+
81
+ // Set the lengths and values of r and s in policyBinding
82
+ policyBinding [ 0 ] = rLength ;
83
+ policyBinding . set ( r , 1 ) ;
84
+ policyBinding [ 1 + rLength ] = sLength ;
85
+ policyBinding . set ( s , 1 + rLength + 1 ) ;
86
+ } else {
87
+ const signature = await digest ( 'SHA-256' , new Uint8Array ( encryptedPolicy ) ) ;
88
+ policyBinding = new Uint8Array ( signature . slice ( - GMAC_BINDING_LEN ) ) ;
89
+ }
85
90
86
91
// Create embedded policy
87
92
const embeddedPolicy = new EmbeddedPolicy (
88
93
DefaultParams . policyType ,
89
- new Uint8Array ( policyBinding . slice ( - lengthOfPolicyBinding ) ) ,
94
+ policyBinding ,
90
95
new Uint8Array ( encryptedPolicy )
91
96
) ;
92
97
@@ -99,7 +104,7 @@ export default async function encrypt(
99
104
const header = new Header (
100
105
DefaultParams . magicNumberVersion ,
101
106
kasResourceLocator ,
102
- DefaultParams . ecdsaBinding ,
107
+ ecdsaBinding ,
103
108
DefaultParams . signatureCurveName ,
104
109
DefaultParams . signature ,
105
110
DefaultParams . signatureCurveName ,
@@ -134,3 +139,58 @@ export default async function encrypt(
134
139
const nanoTDF = new NanoTDF ( header , payload ) ;
135
140
return nanoTDF . toBuffer ( ) ;
136
141
}
142
+
143
+ /**
144
+ * Retrieves the curve name from a given ECDH private key.
145
+ *
146
+ * This function exports the provided ECDH private key in JWK format and extracts
147
+ * the curve name from the 'crv' property of the JWK.
148
+ *
149
+ * @param {CryptoKey } privateKey - The ECDH private key from which to retrieve the curve name.
150
+ * @returns {Promise<string> } - A promise that resolves to the curve name.
151
+ *
152
+ * @throws {Error } - Throws an error if the curve name is undefined.
153
+ *
154
+ */
155
+ async function getCurveNameFromPrivateKey ( privateKey : CryptoKey ) : Promise < string > {
156
+ // Export the private key
157
+ const keyData = await crypto . subtle . exportKey ( 'jwk' , privateKey ) ;
158
+
159
+ // The curve name is stored in the 'crv' property of the JWK
160
+ if ( ! keyData . crv ) {
161
+ throw new Error ( 'Curve name is undefined' ) ;
162
+ }
163
+
164
+ return keyData . crv ;
165
+ }
166
+
167
+ /**
168
+ * Converts an ECDH private key to an ECDSA private key.
169
+ *
170
+ * This function exports the given ECDH private key in PKCS#8 format and then
171
+ * imports it as an ECDSA private key using the specified curve name.
172
+ *
173
+ * @param {CryptoKey } key - The ECDH private key to be converted.
174
+ * @param {string } curveName - The name of the elliptic curve to be used for the ECDSA key.
175
+ * @returns {Promise<CryptoKey> } - A promise that resolves to the converted ECDSA private key.
176
+ *
177
+ * @throws {Error } - Throws an error if the key export or import fails.
178
+ */
179
+ async function convertECDHToECDSA ( key : CryptoKey , curveName : string ) : Promise < CryptoKey > {
180
+ // Export the ECDH private key
181
+ const ecdhPrivateKey = await crypto . subtle . exportKey ( 'pkcs8' , key ) ;
182
+
183
+ // Import the ECDH private key as an ECDSA private key
184
+ const ecdsaPrivateKey = await crypto . subtle . importKey (
185
+ KeyFormat . Pkcs8 ,
186
+ ecdhPrivateKey ,
187
+ {
188
+ name : AlgorithmName . ECDSA ,
189
+ namedCurve : curveName ,
190
+ } ,
191
+ true ,
192
+ [ KeyUsageType . Sign ]
193
+ ) ;
194
+
195
+ return ecdsaPrivateKey ;
196
+ }
0 commit comments