Skip to content

Commit 0d65501

Browse files
authored
Refactoring (#49)
* Migrate ECDSA to use _Scope * Document reasoning for first checking Hash * Migrate RsaPss to _Scope * Migrate RsaSsa to _Scope * Remove unused _withEVP_MD_CTX * Refactor signing and verification into methods * More _Scope migrations * Move RsaOaep to _Scope * Ecdsa migration to _Scope * Migrate more to _Scope * Utility methods for creating stuff with _Scope
1 parent 2ac2d62 commit 0d65501

14 files changed

+369
-518
lines changed

lib/src/impl_ffi/impl_ffi.aescbc.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Stream<Uint8List> _aesCbcEncryptOrDecrypt(
4747
throw ArgumentError.value(iv, 'iv', 'must be $ivSize bytes');
4848
}
4949

50-
final ctx = scope.create(ssl.EVP_CIPHER_CTX_new, ssl.EVP_CIPHER_CTX_free);
50+
final ctx = scope.createEVP_CIPHER_CTX();
5151
_checkOpIsOne(ssl.EVP_CipherInit_ex(
5252
ctx,
5353
cipher,

lib/src/impl_ffi/impl_ffi.aesctr.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ Stream<Uint8List> _aesCtrEncryptOrDecrypt(
9292
// reuse the same counter value which is not allowed.
9393
var bytes_after_wraparound = ctr * BigInt.from(blockSize);
9494

95-
final ctx = scope.create(ssl.EVP_CIPHER_CTX_new, ssl.EVP_CIPHER_CTX_free);
95+
final ctx = scope.createEVP_CIPHER_CTX();
9696
_checkOpIsOne(ssl.EVP_CipherInit_ex(
9797
ctx,
9898
cipher,

lib/src/impl_ffi/impl_ffi.ec_common.dart

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -90,28 +90,31 @@ _EvpPKey _importPkcs8EcPrivateKey(
9090
List<int> keyData,
9191
EllipticCurve curve,
9292
) {
93-
final k = _withDataAsCBS(keyData, ssl.EVP_parse_private_key);
94-
_checkData(k.address != 0, fallback: 'unable to parse key');
95-
final key = _EvpPKey.wrap(k);
96-
97-
_validateEllipticCurveKey(key, curve);
98-
return key;
93+
return _Scope.sync((scope) {
94+
final k = ssl.EVP_parse_private_key(scope.createCBS(keyData));
95+
_checkData(k.address != 0, fallback: 'unable to parse key');
96+
final key = _EvpPKey.wrap(k);
97+
_validateEllipticCurveKey(key, curve);
98+
return key;
99+
});
99100
}
100101

101102
_EvpPKey _importSpkiEcPublicKey(
102103
List<int> keyData,
103104
EllipticCurve curve,
104105
) {
105-
// TODO: When calling EVP_parse_public_key it might wise to check that CBS_len(cbs) == 0 is true afterwards
106-
// otherwise it might be that all of the contents of the key was not consumed and we should throw
107-
// a FormatException. Notice that this the case for private/public keys, and RSA keys.
108-
final k = _withDataAsCBS(keyData, ssl.EVP_parse_public_key);
109-
_checkData(k.address != 0, fallback: 'unable to parse key');
110-
final key = _EvpPKey.wrap(k);
106+
return _Scope.sync((scope) {
107+
// TODO: When calling EVP_parse_public_key it might wise to check that CBS_len(cbs) == 0 is true afterwards
108+
// otherwise it might be that all of the contents of the key was not consumed and we should throw
109+
// a FormatException. Notice that this the case for private/public keys, and RSA keys.
110+
final k = ssl.EVP_parse_public_key(scope.createCBS(keyData));
111+
_checkData(k.address != 0, fallback: 'unable to parse key');
112+
final key = _EvpPKey.wrap(k);
111113

112-
_validateEllipticCurveKey(key, curve);
114+
_validateEllipticCurveKey(key, curve);
113115

114-
return key;
116+
return key;
117+
});
115118
}
116119

117120
_EvpPKey _importJwkEcPrivateOrPublicKey(
@@ -256,17 +259,18 @@ Uint8List _exportRawEcPublicKey(_EvpPKey key) {
256259
_checkOp(ec.address != 0, fallback: 'internal key type invariant error');
257260
scope.defer(() => ssl.EC_KEY_free(ec));
258261

259-
return _withOutCBB((cbb) {
260-
return _checkOpIsOne(
261-
ssl.EC_POINT_point2cbb(
262-
cbb,
263-
ssl.EC_KEY_get0_group(ec),
264-
ssl.EC_KEY_get0_public_key(ec),
265-
point_conversion_form_t.POINT_CONVERSION_UNCOMPRESSED,
266-
ffi.nullptr,
267-
),
268-
fallback: 'formatting failed');
269-
});
262+
final cbb = scope.createCBB();
263+
_checkOpIsOne(
264+
ssl.EC_POINT_point2cbb(
265+
cbb,
266+
ssl.EC_KEY_get0_group(ec),
267+
ssl.EC_KEY_get0_public_key(ec),
268+
point_conversion_form_t.POINT_CONVERSION_UNCOMPRESSED,
269+
ffi.nullptr,
270+
),
271+
fallback: 'formatting failed',
272+
);
273+
return cbb.copy();
270274
});
271275
}
272276

@@ -286,8 +290,8 @@ Map<String, dynamic> _exportJwkEcPrivateOrPublicKey(
286290
// Determine byte size used for encoding params
287291
final paramSize = _numBitsToBytes(ssl.EC_GROUP_get_degree(group));
288292

289-
final x = scope.create(ssl.BN_new, ssl.BN_free);
290-
final y = scope.create(ssl.BN_new, ssl.BN_free);
293+
final x = scope.createBN();
294+
final y = scope.createBN();
291295

292296
_checkOpIsOne(ssl.EC_POINT_get_affine_coordinates_GFp(
293297
group,

lib/src/impl_ffi/impl_ffi.ecdh.dart

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -124,20 +124,20 @@ class _EcdhPrivateKey implements EcdhPrivateKey {
124124
}
125125

126126
final lengthInBytes = (length / 8).ceil();
127-
final derived = _withOutPointer(lengthInBytes, (ffi.Pointer<ffi.Void> p) {
128-
final outLen = ssl.ECDH_compute_key(
129-
p,
130-
lengthInBytes,
131-
ssl.EC_KEY_get0_public_key(pubEcKey),
132-
privEcKey,
133-
ffi.nullptr,
134-
);
135-
_checkOp(outLen != -1, fallback: 'ECDH key derivation failed');
136-
_checkOp(
137-
outLen == lengthInBytes,
138-
message: 'internal error in ECDH key derivation',
139-
);
140-
});
127+
final out = scope<ffi.Uint8>(lengthInBytes);
128+
final outLen = ssl.ECDH_compute_key(
129+
out.cast(),
130+
lengthInBytes,
131+
ssl.EC_KEY_get0_public_key(pubEcKey),
132+
privEcKey,
133+
ffi.nullptr,
134+
);
135+
_checkOp(outLen != -1, fallback: 'ECDH key derivation failed');
136+
_checkOp(
137+
outLen == lengthInBytes,
138+
message: 'internal error in ECDH key derivation',
139+
);
140+
final derived = out.copy(lengthInBytes);
141141

142142
// Only return the first [length] bits from derived.
143143
final zeroBits = lengthInBytes * 8 - length;
@@ -159,11 +159,7 @@ class _EcdhPrivateKey implements EcdhPrivateKey {
159159
_exportJwkEcPrivateOrPublicKey(_key, isPrivateKey: true, jwkUse: null);
160160

161161
@override
162-
Future<Uint8List> exportPkcs8Key() async {
163-
return _withOutCBB((cbb) {
164-
_checkOp(ssl.EVP_marshal_private_key.invoke(cbb, _key) == 1);
165-
});
166-
}
162+
Future<Uint8List> exportPkcs8Key() async => _exportPkcs8Key(_key);
167163
}
168164

169165
class _EcdhPublicKey implements EcdhPublicKey {
@@ -183,9 +179,5 @@ class _EcdhPublicKey implements EcdhPublicKey {
183179
Future<Uint8List> exportRawKey() async => _exportRawEcPublicKey(_key);
184180

185181
@override
186-
Future<Uint8List> exportSpkiKey() async {
187-
return _withOutCBB((cbb) {
188-
_checkOp(ssl.EVP_marshal_public_key.invoke(cbb, _key) == 1);
189-
});
190-
}
182+
Future<Uint8List> exportSpkiKey() async => _exportSpkiKey(_key);
191183
}

lib/src/impl_ffi/impl_ffi.ecdsa.dart

Lines changed: 44 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ Uint8List _convertEcdsaDerSignatureToWebCryptoSignature(
9494
) {
9595
return _Scope.sync((scope) {
9696
// TODO: Check if cbs is empty after parsing, consider using ECDSA_SIG_from_bytes instead (like chrome does)
97-
final ecdsa = _withDataAsCBS(signature, ssl.ECDSA_SIG_parse);
97+
final ecdsa = ssl.ECDSA_SIG_parse(scope.createCBS(signature));
9898
_checkOp(ecdsa.address != 0,
9999
message: 'internal error formatting signature');
100100
scope.defer(() => ssl.ECDSA_SIG_free(ecdsa));
@@ -108,25 +108,22 @@ Uint8List _convertEcdsaDerSignatureToWebCryptoSignature(
108108
ec,
109109
)));
110110

111-
return _withAllocation(_sslAlloc<ffi.Pointer<BIGNUM>>(2),
112-
(ffi.Pointer<ffi.Pointer<BIGNUM>> RS) {
113-
// Access R and S from the ecdsa signature
114-
final R = RS.elementAt(0);
115-
final S = RS.elementAt(1);
116-
ssl.ECDSA_SIG_get0(ecdsa, R, S);
117-
118-
// Dump R and S to return value.
119-
return _withOutPointer(N * 2, (ffi.Pointer<ffi.Uint8> p) {
120-
_checkOpIsOne(
121-
ssl.BN_bn2bin_padded(p.elementAt(0), N, R.value),
122-
fallback: 'internal error formatting R in signature',
123-
);
124-
_checkOpIsOne(
125-
ssl.BN_bn2bin_padded(p.elementAt(N), N, S.value),
126-
fallback: 'internal error formatting S in signature',
127-
);
128-
});
129-
});
111+
// Access R and S from the ecdsa signature
112+
final R = scope<ffi.Pointer<BIGNUM>>();
113+
final S = scope<ffi.Pointer<BIGNUM>>();
114+
ssl.ECDSA_SIG_get0(ecdsa, R, S);
115+
116+
// Dump R and S to return value.
117+
final out = scope<ffi.Uint8>(N * 2);
118+
_checkOpIsOne(
119+
ssl.BN_bn2bin_padded(out.elementAt(0), N, R.value),
120+
fallback: 'internal error formatting R in signature',
121+
);
122+
_checkOpIsOne(
123+
ssl.BN_bn2bin_padded(out.elementAt(N), N, S.value),
124+
fallback: 'internal error formatting S in signature',
125+
);
126+
return out.copy(N * 2);
130127
});
131128
}
132129

@@ -157,33 +154,29 @@ Uint8List? _convertEcdsaWebCryptoSignatureToDerSignature(
157154
return null;
158155
}
159156

160-
final ecdsa = ssl.ECDSA_SIG_new();
161-
_checkOp(ecdsa.address != 0,
162-
message: 'internal error formatting signature');
163-
scope.defer(() => ssl.ECDSA_SIG_free(ecdsa));
164-
165-
return _withAllocation(_sslAlloc<ffi.Pointer<BIGNUM>>(2),
166-
(ffi.Pointer<ffi.Pointer<BIGNUM>> RS) {
167-
// Access R and S from the ecdsa signature
168-
final R = RS.elementAt(0);
169-
final S = RS.elementAt(1);
170-
ssl.ECDSA_SIG_get0(ecdsa, R, S);
171-
172-
_withDataAsPointer(signature, (ffi.Pointer<ffi.Uint8> p) {
173-
_checkOp(
174-
ssl.BN_bin2bn(p.elementAt(0), N, R.value).address != 0,
175-
fallback: 'allocation failure',
176-
);
177-
_checkOp(
178-
ssl.BN_bin2bn(p.elementAt(N), N, S.value).address != 0,
179-
fallback: 'allocation failure',
180-
);
181-
});
182-
return _withOutCBB((cbb) => _checkOpIsOne(
183-
ssl.ECDSA_SIG_marshal(cbb, ecdsa),
184-
fallback: 'internal error reformatting signature',
185-
));
186-
});
157+
final ecdsa = scope.create(ssl.ECDSA_SIG_new, ssl.ECDSA_SIG_free);
158+
159+
// Access R and S from the ecdsa signature
160+
final R = scope<ffi.Pointer<BIGNUM>>();
161+
final S = scope<ffi.Pointer<BIGNUM>>();
162+
ssl.ECDSA_SIG_get0(ecdsa, R, S);
163+
164+
final psig = scope.dataAsPointer<ffi.Uint8>(signature);
165+
_checkOp(
166+
ssl.BN_bin2bn(psig.elementAt(0), N, R.value).address != 0,
167+
fallback: 'allocation failure',
168+
);
169+
_checkOp(
170+
ssl.BN_bin2bn(psig.elementAt(N), N, S.value).address != 0,
171+
fallback: 'allocation failure',
172+
);
173+
174+
final cbb = scope.createCBB();
175+
_checkOpIsOne(
176+
ssl.ECDSA_SIG_marshal(cbb, ecdsa),
177+
fallback: 'internal error reformatting signature',
178+
);
179+
return cbb.copy();
187180
});
188181
}
189182

@@ -199,26 +192,7 @@ class _EcdsaPrivateKey implements EcdsaPrivateKey {
199192
@override
200193
Future<Uint8List> signStream(Stream<List<int>> data, Hash hash) async {
201194
final md = _Hash.fromHash(hash)._md;
202-
203-
final sig = await _withEVP_MD_CTX((ctx) async {
204-
_checkOpIsOne(ssl.EVP_DigestSignInit.invoke(
205-
ctx,
206-
ffi.nullptr,
207-
md,
208-
ffi.nullptr,
209-
_key,
210-
));
211-
212-
await _streamToUpdate(data, ctx, ssl.EVP_DigestSignUpdate);
213-
return _withAllocation(_sslAlloc<ffi.Size>(),
214-
(ffi.Pointer<ffi.Size> len) {
215-
len.value = 0;
216-
_checkOpIsOne(ssl.EVP_DigestSignFinal(ctx, ffi.nullptr, len));
217-
return _withOutPointer(len.value, (ffi.Pointer<ffi.Uint8> p) {
218-
_checkOpIsOne(ssl.EVP_DigestSignFinal(ctx, p, len));
219-
}).sublist(0, len.value);
220-
});
221-
});
195+
final sig = await _signStream(_key, md, data);
222196
return _convertEcdsaDerSignatureToWebCryptoSignature(_key, sig);
223197
}
224198

@@ -227,11 +201,7 @@ class _EcdsaPrivateKey implements EcdsaPrivateKey {
227201
_exportJwkEcPrivateOrPublicKey(_key, isPrivateKey: true, jwkUse: 'sig');
228202

229203
@override
230-
Future<Uint8List> exportPkcs8Key() async {
231-
return _withOutCBB((cbb) {
232-
_checkOp(ssl.EVP_marshal_private_key.invoke(cbb, _key) == 1);
233-
});
234-
}
204+
Future<Uint8List> exportPkcs8Key() async => _exportPkcs8Key(_key);
235205
}
236206

237207
class _EcdsaPublicKey implements EcdsaPublicKey {
@@ -258,29 +228,7 @@ class _EcdsaPublicKey implements EcdsaPublicKey {
258228
return false;
259229
}
260230

261-
return await _withEVP_MD_CTX((ctx) async {
262-
return await _withPEVP_PKEY_CTX((pctx) async {
263-
_checkOpIsOne(ssl.EVP_DigestVerifyInit.invoke(
264-
ctx,
265-
pctx,
266-
md,
267-
ffi.nullptr,
268-
_key,
269-
));
270-
await _streamToUpdate(data, ctx, ssl.EVP_DigestVerifyUpdate);
271-
return _withDataAsPointer(sig, (ffi.Pointer<ffi.Uint8> p) {
272-
final result = ssl.EVP_DigestVerifyFinal(ctx, p, sig.length);
273-
if (result != 1) {
274-
// TODO: We should always clear errors, when returning from any
275-
// function that uses BoringSSL.
276-
// Note: In this case we could probably assert that error is just
277-
// signature related.
278-
ssl.ERR_clear_error();
279-
}
280-
return result == 1;
281-
});
282-
});
283-
});
231+
return await _verifyStream(_key, md, sig, data);
284232
}
285233

286234
@override
@@ -291,9 +239,5 @@ class _EcdsaPublicKey implements EcdsaPublicKey {
291239
Future<Uint8List> exportRawKey() async => _exportRawEcPublicKey(_key);
292240

293241
@override
294-
Future<Uint8List> exportSpkiKey() async {
295-
return _withOutCBB((cbb) {
296-
_checkOp(ssl.EVP_marshal_public_key.invoke(cbb, _key) == 1);
297-
});
298-
}
242+
Future<Uint8List> exportSpkiKey() async => _exportSpkiKey(_key);
299243
}

0 commit comments

Comments
 (0)