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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# 0.6.2-wip
* Fixed JS interop to enable WebAssembly.
* Fixed native RSA public-key JWK imports to reject private key material.
* Fixed native HMAC key generation to use the hash block size by default.

# 0.6.1
* Added Dart native build hooks and native asset lookup for the bundled
Expand Down
17 changes: 17 additions & 0 deletions lib/src/impl_ffi/impl_ffi.digest.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ abstract class _HashImpl implements HashImpl {
/// https://www.iana.org/assignments/jose/jose.xhtml
String get hmacJwkAlg;

/// Recommended HMAC key length in bits.
///
/// Web Crypto defines this as the block size of the hash function.
int get hmacBlockSizeInBits;

/// Algorithm (`alg` for JWK) when this hash algorithm is used in RSA-OAEP.
///
/// For SHA-1, it returns 'RSA-OAEP'.
Expand Down Expand Up @@ -112,6 +117,9 @@ final class _Sha1 extends _HashImpl {
@override
String get hmacJwkAlg => 'HS1';

@override
int get hmacBlockSizeInBits => 512;

@override
String get rsaOaepJwkAlg => 'RSA-OAEP';

Expand All @@ -131,6 +139,9 @@ final class _Sha256 extends _HashImpl {
@override
String get hmacJwkAlg => 'HS256';

@override
int get hmacBlockSizeInBits => 512;

@override
String get rsaOaepJwkAlg => 'RSA-OAEP-256';

Expand All @@ -150,6 +161,9 @@ final class _Sha384 extends _HashImpl {
@override
String get hmacJwkAlg => 'HS384';

@override
int get hmacBlockSizeInBits => 1024;

@override
String get rsaOaepJwkAlg => 'RSA-OAEP-384';

Expand All @@ -169,6 +183,9 @@ final class _Sha512 extends _HashImpl {
@override
String get hmacJwkAlg => 'HS512';

@override
int get hmacBlockSizeInBits => 1024;

@override
String get rsaOaepJwkAlg => 'RSA-OAEP-512';

Expand Down
4 changes: 2 additions & 2 deletions lib/src/impl_ffi/impl_ffi.hmac.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Future<HmacSecretKeyImpl> hmacSecretKey_generateKey(
int? length,
}) async {
final h = _HashImpl.fromHash(hash);
length ??= ssl.EVP_MD_size(h._md) * 8;
length ??= h.hmacBlockSizeInBits;
final keyData = Uint8List((length / 8).ceil());
fillRandomBytes(keyData);

Expand Down Expand Up @@ -103,7 +103,7 @@ final class _StaticHmacSecretKeyImpl implements StaticHmacSecretKeyImpl {
}

@override
Future<HmacSecretKeyImpl> generateKey(HashImpl hash, {int? length = 32}) {
Future<HmacSecretKeyImpl> generateKey(HashImpl hash, {int? length}) {
return hmacSecretKey_generateKey(hash, length: length);
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/src/impl_js/impl_js.hmac.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ final class _StaticHmacSecretKeyImpl implements StaticHmacSecretKeyImpl {
}

@override
Future<HmacSecretKeyImpl> generateKey(HashImpl hash, {int? length = 32}) {
Future<HmacSecretKeyImpl> generateKey(HashImpl hash, {int? length}) {
return hmacSecretKey_generateKey(hash, length: length);
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/src/impl_stub/impl_stub.hmac.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ final class _StaticHmacSecretKeyImpl implements StaticHmacSecretKeyImpl {
}

@override
Future<HmacSecretKeyImpl> generateKey(HashImpl hash, {int? length = 32}) {
Future<HmacSecretKeyImpl> generateKey(HashImpl hash, {int? length}) {
throw UnimplementedError('Not implemented');
}
}
5 changes: 3 additions & 2 deletions lib/src/webcrypto/webcrypto.hmac.dart
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,9 @@ final class HmacSecretKey {
/// Generate random [HmacSecretKey].
///
/// The [length] specifies the length of the secret key in bits. If omitted
/// the random key will use the same number of bits as the underlying hash
/// algorithm given in [hash].
/// the random key will use the block size of the underlying hash algorithm
/// given in [hash] (512 bits for SHA-1 and SHA-256, and 1024 bits for
/// SHA-384 and SHA-512).
///
/// **Example**
/// ```dart
Expand Down
39 changes: 39 additions & 0 deletions test/hmac_default_key_length_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2026 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.

import 'package:test/test.dart';
import 'package:webcrypto/webcrypto.dart';

void main() {
group('HMAC default key length', () {
for (final testCase in [
(name: 'SHA-1', hash: Hash.sha1, expectedBits: 512),
(name: 'SHA-256', hash: Hash.sha256, expectedBits: 512),
(name: 'SHA-384', hash: Hash.sha384, expectedBits: 1024),
(name: 'SHA-512', hash: Hash.sha512, expectedBits: 1024),
]) {
test(testCase.name, () async {
final key = await HmacSecretKey.generateKey(testCase.hash);

expect(await key.exportRawKey(), hasLength(testCase.expectedBits ~/ 8));
});
}

test('explicit length takes precedence', () async {
final key = await HmacSecretKey.generateKey(Hash.sha512, length: 256);

expect(await key.exportRawKey(), hasLength(32));
});
});
}