Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ Future<void> main() async {
* RSASSA-PKCS1-v1_5 (sign/verify)
* RSA-PSS (sign/verify)
* ECDSA (sign/verify)
* EdDSA (Ed25519) (sign/verify)
* RSA-OAEP (encrypt/decrypt)
* AES-CTR, AES-CBC, AES-GCM (encrypt/decrypt)
* ECDH (deriveBits)
* X25519 (deriveBits)
* HKDF (deriveBits)
* PBKDF2 (deriveBits)
* BoringSSL, Chrome and Firefox implementations pass the same test cases.
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.5.7"
version: "0.6.0"
webdriver:
dependency: transitive
description:
Expand Down
24 changes: 24 additions & 0 deletions lib/src/boringssl/lookup/symbols.generated.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,12 @@ enum Sym {
EVP_CipherUpdate,
EVP_DigestFinal,
EVP_DigestInit,
EVP_DigestSign,
EVP_DigestSignFinal,
EVP_DigestSignInit,
EVP_DigestSignUpdate,
EVP_DigestUpdate,
EVP_DigestVerify,
EVP_DigestVerifyFinal,
EVP_DigestVerifyInit,
EVP_DigestVerifyUpdate,
Expand All @@ -101,20 +103,30 @@ enum Sym {
EVP_parse_public_key,
EVP_PKEY_CTX_free,
EVP_PKEY_CTX_new,
EVP_PKEY_CTX_new_id,
EVP_PKEY_CTX_set0_rsa_oaep_label,
EVP_PKEY_CTX_set_rsa_mgf1_md,
EVP_PKEY_CTX_set_rsa_oaep_md,
EVP_PKEY_CTX_set_rsa_padding,
EVP_PKEY_CTX_set_rsa_pss_saltlen,
EVP_PKEY_decrypt,
EVP_PKEY_decrypt_init,
EVP_PKEY_derive,
EVP_PKEY_derive_init,
EVP_PKEY_derive_set_peer,
EVP_PKEY_encrypt,
EVP_PKEY_encrypt_init,
EVP_PKEY_free,
EVP_PKEY_get_raw_private_key,
EVP_PKEY_get_raw_public_key,
EVP_PKEY_get1_EC_KEY,
EVP_PKEY_get1_RSA,
EVP_PKEY_id,
EVP_PKEY_keygen,
EVP_PKEY_keygen_init,
EVP_PKEY_new,
EVP_PKEY_new_raw_private_key,
EVP_PKEY_new_raw_public_key,
EVP_PKEY_set1_EC_KEY,
EVP_PKEY_set1_RSA,
EVP_sha1,
Expand Down Expand Up @@ -224,10 +236,12 @@ const _SymName = [
'EVP_CipherUpdate',
'EVP_DigestFinal',
'EVP_DigestInit',
'EVP_DigestSign',
'EVP_DigestSignFinal',
'EVP_DigestSignInit',
'EVP_DigestSignUpdate',
'EVP_DigestUpdate',
'EVP_DigestVerify',
'EVP_DigestVerifyFinal',
'EVP_DigestVerifyInit',
'EVP_DigestVerifyUpdate',
Expand All @@ -240,20 +254,30 @@ const _SymName = [
'EVP_parse_public_key',
'EVP_PKEY_CTX_free',
'EVP_PKEY_CTX_new',
'EVP_PKEY_CTX_new_id',
'EVP_PKEY_CTX_set0_rsa_oaep_label',
'EVP_PKEY_CTX_set_rsa_mgf1_md',
'EVP_PKEY_CTX_set_rsa_oaep_md',
'EVP_PKEY_CTX_set_rsa_padding',
'EVP_PKEY_CTX_set_rsa_pss_saltlen',
'EVP_PKEY_decrypt',
'EVP_PKEY_decrypt_init',
'EVP_PKEY_derive',
'EVP_PKEY_derive_init',
'EVP_PKEY_derive_set_peer',
'EVP_PKEY_encrypt',
'EVP_PKEY_encrypt_init',
'EVP_PKEY_free',
'EVP_PKEY_get_raw_private_key',
'EVP_PKEY_get_raw_public_key',
'EVP_PKEY_get1_EC_KEY',
'EVP_PKEY_get1_RSA',
'EVP_PKEY_id',
'EVP_PKEY_keygen',
'EVP_PKEY_keygen_init',
'EVP_PKEY_new',
'EVP_PKEY_new_raw_private_key',
'EVP_PKEY_new_raw_public_key',
'EVP_PKEY_set1_EC_KEY',
'EVP_PKEY_set1_RSA',
'EVP_sha1',
Expand Down
132 changes: 132 additions & 0 deletions lib/src/impl_ffi/impl_ffi.crv25519.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

part of 'impl_ffi.dart';

const _crv25519KeyLength = 32;

String _crv25519FromType(int pkeyType) {
switch (pkeyType) {
case EVP_PKEY_X25519:
return 'X25519';
case EVP_PKEY_ED25519:
return 'Ed25519';
}
throw operationError('internal error detecting curve');
}

_EvpPKey _newRawPrivateKey(int type, Uint8List keyData) {
final crv = _crv25519FromType(type);

_checkData(
keyData.length == _crv25519KeyLength,
message: '$crv private key should be $_crv25519KeyLength bytes',
);

return _Scope.sync((scope) {
final bytes = scope.dataAsPointer<ffi.Uint8>(keyData);
final key = ssl.EVP_PKEY_new_raw_private_key(
type,
ffi.nullptr,
bytes,
_crv25519KeyLength,
);
_checkOp(key.address != 0);
return _EvpPKey.wrap(key);
});
}

_EvpPKey _newRawPublicKey(int type, Uint8List keyData) {
final crv = _crv25519FromType(type);

_checkData(
keyData.length == _crv25519KeyLength,
message: '$crv public key should be $_crv25519KeyLength bytes',
);

return _Scope.sync((scope) {
final bytes = scope.dataAsPointer<ffi.Uint8>(keyData);
final key = ssl.EVP_PKEY_new_raw_public_key(
type,
ffi.nullptr,
bytes,
_crv25519KeyLength,
);
_checkOp(key.address != 0);
return _EvpPKey.wrap(key);
});
}

_EvpPKey _crv25519ImportJsonWebKey(
int pkeyType,
JsonWebKey jwk, {
required bool isPrivateKey,
required String use,
Set<String> alg = const {},
}) {
final crv = _crv25519FromType(pkeyType);

_checkData(
jwk.kty == 'OKP',
message: 'expected an $crv key, JWK property "kty" must be "OKP"',
);

_checkData(
jwk.x != null,
message: 'expected an $crv key, JWK property "x" is missing',
);

_checkData(
jwk.use == null || jwk.use == use,
message: 'JWK property "use" should be "enc", if present',
);

_checkData(
jwk.crv == crv,
message: 'expected an $crv key, JWK property "crv" must be "$crv"',
);

final algs = alg.map((e) => '"$e"').join(' or ');
_checkData(
jwk.alg == null || alg.isEmpty || alg.contains(jwk.alg),
message: 'expected an $crv key, JWK property "alg" must be $algs.',
);

final x = _jwkDecodeBase64UrlNoPadding(jwk.x!, 'x');
_checkData(
x.length == _crv25519KeyLength,
message: 'JWK property "x" should be $_crv25519KeyLength bytes',
);

if (isPrivateKey) {
_checkData(
jwk.d != null,
message: 'expected an $crv private key, JWK property "d" is missing',
);

final d = _jwkDecodeBase64UrlNoPadding(jwk.d!, 'd');
_checkData(
d.length == _crv25519KeyLength,
message: 'JWK property "d" should be $_crv25519KeyLength bytes',
);
return _newRawPrivateKey(pkeyType, d);
}

_checkData(
jwk.d == null,
message: 'expected an $crv public key, JWK property "d" is present',
);

return _newRawPublicKey(pkeyType, x);
}
15 changes: 15 additions & 0 deletions lib/src/impl_ffi/impl_ffi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ part 'impl_ffi.aescbc.dart';
part 'impl_ffi.aesctr.dart';
part 'impl_ffi.aesgcm.dart';
part 'impl_ffi.digest.dart';
part 'impl_ffi.crv25519.dart';
part 'impl_ffi.ecdh.dart';
part 'impl_ffi.ecdsa.dart';
part 'impl_ffi.ed25519.dart';
part 'impl_ffi.hkdf.dart';
part 'impl_ffi.hmac.dart';
part 'impl_ffi.pbkdf2.dart';
Expand All @@ -45,6 +47,7 @@ part 'impl_ffi.rsaoaep.dart';
part 'impl_ffi.rsapss.dart';
part 'impl_ffi.rsassapkcs1v15.dart';
part 'impl_ffi.utils.dart';
part 'impl_ffi.x25519.dart';
part 'impl_ffi.rsa_common.dart';
part 'impl_ffi.ec_common.dart';
part 'impl_ffi.aes_common.dart';
Expand Down Expand Up @@ -81,6 +84,12 @@ final class _WebCryptoImpl implements WebCryptoImpl {
@override
final ecdsaPublicKey = const _StaticEcdsaPublicKeyImpl();

@override
final ed25519PrivateKey = const _StaticEd25519PrivateKeyImpl();

@override
final ed25519PublicKey = const _StaticEd25519PublicKeyImpl();

@override
final rsaOaepPrivateKey = const _StaticRsaOaepPrivateKeyImpl();

Expand All @@ -102,6 +111,12 @@ final class _WebCryptoImpl implements WebCryptoImpl {
@override
final rsaSsaPkcs1v15PublicKey = const _StaticRsaSsaPkcs1V15PublicKeyImpl();

@override
final x25519PrivateKey = const _StaticX25519PrivateKeyImpl();

@override
final x25519PublicKey = const _StaticX25519PublicKeyImpl();

@override
final sha1 = const _Sha1();

Expand Down
Loading