Commit 66ee479

Browse files
panvaaduh95
authored andcommitted
crypto: disable non-FIPS WebCrypto paths in FIPS mode
Hide TurboSHAKE and KangarooTwelve when FIPS is enabled. Reject cSHAKE and KMAC parameters that require implementations outside the OpenSSL provider, while keeping provider-backed paths available. Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #65172 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent fd7e904 commit 66ee479

20 files changed

Lines changed: 472 additions & 183 deletions

‎lib/internal/crypto/mac.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const {
2020
normalizeHashName,
2121
numBitsToBytes,
2222
truncateToBitLength,
23+
validateKmacKeyLength,
2324
}=require('internal/crypto/util');
2425

2526
const{
@@ -60,6 +61,9 @@ function normalizeKeyLength(handle, algorithm) {
6061
length=algorithm.length;
6162
}
6263

64+
if(algorithm.name==='KMAC128'||algorithm.name==='KMAC256')
65+
validateKmacKeyLength(length);
66+
6367
return{ handle, length };
6468
}
6569

‎lib/internal/crypto/util.js‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,12 @@ const {
4747
EVP_PKEY_ML_KEM_1024,
4848
kKeyVariantAES_OCB_128: hasAesOcbMode,
4949
Argon2Job,
50+
getFipsCrypto,
5051
KmacJob,
5152
}=internalBinding('crypto');
5253

54+
constisFips=getFipsCrypto()===1;
55+
5356
const{ getOptionValue }=require('internal/options');
5457

5558
const{
@@ -415,6 +418,8 @@ const conditionalAlgorithms = {
415418
'Ed448': !process.features.openssl_is_boringssl,
416419
'KMAC128': !!KmacJob,
417420
'KMAC256': !!KmacJob,
421+
'KT128': !isFips,
422+
'KT256': !isFips,
418423
'ML-DSA-44': !!EVP_PKEY_ML_DSA_44,
419424
'ML-DSA-65': !!EVP_PKEY_ML_DSA_65,
420425
'ML-DSA-87': !!EVP_PKEY_ML_DSA_87,
@@ -427,6 +432,8 @@ const conditionalAlgorithms = {
427432
ArrayPrototypeIncludes(getHashes(),'sha3-384'),
428433
'SHA3-512': !process.features.openssl_is_boringssl||
429434
ArrayPrototypeIncludes(getHashes(),'sha3-512'),
435+
'TurboSHAKE128': !isFips,
436+
'TurboSHAKE256': !isFips,
430437
'X448': !process.features.openssl_is_boringssl,
431438
};
432439

@@ -571,6 +578,11 @@ function validateMaxBufferLength(data, name, max = kMaxBufferLength) {
571578
}
572579
}
573580

581+
functionvalidateKmacKeyLength(length){
582+
if((length<32||length%8)&&isFips)
583+
throwlazyDOMException('Invalid key length','NotSupportedError');
584+
}
585+
574586
/**
575587
* Converts a bit length to the number of bytes needed to contain it.
576588
* Non-byte lengths are rounded up to the next byte.
@@ -1088,6 +1100,7 @@ module.exports = {
10881100

10891101
kNamedCurveAliases,
10901102
kSupportedAlgorithms,
1103+
isFips,
10911104
normalizeAlgorithm,
10921105
normalizeHashName,
10931106
hasAnyNotIn,
@@ -1097,6 +1110,7 @@ module.exports = {
10971110
jobPromiseThen,
10981111
cleanupWebCryptoResult,
10991112
prepareWebCryptoResult,
1113+
validateKmacKeyLength,
11001114
validateMaxBufferLength,
11011115
numBitsToBytes,
11021116
truncateToBitLength,

‎lib/internal/crypto/webidl.js‎

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const {
99
StringPrototypeSplit,
1010
StringPrototypeStartsWith,
1111
StringPrototypeToLowerCase,
12-
TypedArrayPrototypeGetLength,
1312
}=primordials;
1413

1514
const{
@@ -28,8 +27,10 @@ const {
2827
validateMaxBufferLength,
2928
getBufferSourceByteLength,
3029
getBufferSourceBytes,
30+
isFips,
3131
kNamedCurveAliases,
3232
numBitsToBytes,
33+
validateKmacKeyLength,
3334
}=require('internal/crypto/util');
3435
const{
3536
converters: webidl,
@@ -276,30 +277,39 @@ function validateCShakeOutputLength(V) {
276277
}
277278
}
278279

279-
functionbufferSourceEqualsAscii(V,string){
280-
if(getBufferSourceByteLength(V)!==string.length)returnfalse;
281-
282-
constbytes=getBufferSourceBytes(V);
283-
constlength=TypedArrayPrototypeGetLength(bytes);
284-
for(leti=0;i<length;i++){
285-
if(bytes[i]!==StringPrototypeCharCodeAt(string,i))returnfalse;
286-
}
287-
returntrue;
288-
}
280+
constkCShakeFunctionNames=['KMAC','TupleHash','ParallelHash'];
289281

290282
functionvalidateCShakeFunctionName(V){
291-
if(getBufferSourceByteLength(V)===0||
292-
bufferSourceEqualsAscii(V,'KMAC')||
293-
bufferSourceEqualsAscii(V,'TupleHash')||
294-
bufferSourceEqualsAscii(V,'ParallelHash')){
295-
return;
283+
constlength=getBufferSourceByteLength(V);
284+
if(length===0)return;
285+
286+
if(!isFips){
287+
constbytes=getBufferSourceBytes(V);
288+
for(leti=0;i<kCShakeFunctionNames.length;i++){
289+
constfunctionName=kCShakeFunctionNames[i];
290+
if(length!==functionName.length)continue;
291+
292+
letj=0;
293+
for(;j<length;j++){
294+
if(bytes[j]!==StringPrototypeCharCodeAt(functionName,j))break;
295+
}
296+
if(j===length)return;
297+
}
296298
}
297299

298300
throwlazyDOMException(
299301
'Unsupported CShakeParams functionName',
300302
'NotSupportedError');
301303
}
302304

305+
functionvalidateCShakeCustomization(V){
306+
if(isFips&&getBufferSourceByteLength(V)!==0)
307+
throwlazyDOMException(
308+
'Unsupported CShakeParams customization',
309+
'NotSupportedError');
310+
validateMaxBufferLength(V,'CShakeParams.customization',512);
311+
}
312+
303313
converters.RsaPssParams=createDictionaryConverter(
304314
'RsaPssParams',[
305315
dictAlgorithm,
@@ -457,7 +467,7 @@ converters.CShakeParams = createDictionaryConverter(
457467
{
458468
key: 'customization',
459469
converter: converters.BufferSource,
460-
validator: (V,opts)=>validateMaxBufferLength(V,'CShakeParams.customization',512),
470+
validator: validateCShakeCustomization,
461471
},
462472
],
463473
]);
@@ -743,6 +753,7 @@ for (let i = 0; i < kKmacDictionaries.length; i++) {
743753
key: 'length',
744754
converter: (V,opts)=>
745755
converters['unsigned long'](V,enforceRangeOptions(opts)),
756+
validator: validateKmacKeyLength,
746757
},
747758
],
748759
]);
@@ -756,6 +767,12 @@ converters.KmacParams = createDictionaryConverter(
756767
key: 'outputLength',
757768
converter: (V,opts)=>
758769
converters['unsigned long'](V,enforceRangeOptions(opts)),
770+
validator: (V)=>{
771+
if((V===0||V%8)&&isFips)
772+
throwlazyDOMException(
773+
'Invalid KmacParams outputLength',
774+
'NotSupportedError');
775+
},
759776
required: true,
760777
},
761778
{

‎src/crypto/crypto_hash.cc‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,11 @@ Maybe<void> CShakeTraits::AdditionalConfig(
812812
CShakeConfig* params) {
813813
Environment* env = Environment::GetCurrent(args);
814814

815+
if (IsFipsEnabled()) {
816+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
817+
return Nothing<void>();
818+
}
819+
815820
CHECK(args[offset]->IsString()); // Algorithm name
816821
Utf8Value algorithm_name(env->isolate(), args[offset]);
817822
std::string_view algorithm_str = algorithm_name.ToStringView();

‎src/crypto/crypto_kmac.cc‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ bool DeriveBitsWithCShake(const KmacConfig& params,
151151
constvoid* key_data,
152152
size_t key_size,
153153
ByteSource* out) {
154+
if (IsFipsEnabled()) returnfalse;
155+
154156
constsize_t key_length_bytes = NumBitsToBytes(params.key_length);
155157
if (key_size < key_length_bytes) returnfalse;
156158

‎src/crypto/crypto_turboshake.cc‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,11 @@ Maybe<void> TurboShakeTraits::AdditionalConfig(
428428
TurboShakeConfig* params) {
429429
Environment* env = Environment::GetCurrent(args);
430430

431+
if (IsFipsEnabled()) {
432+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
433+
return Nothing<void>();
434+
}
435+
431436
// args[offset + 0] = algorithm name (string)
432437
CHECK(args[offset]->IsString());
433438
Utf8Value algorithm_name(env->isolate(), args[offset]);
@@ -535,6 +540,11 @@ Maybe<void> KangarooTwelveTraits::AdditionalConfig(
535540
KangarooTwelveConfig* params) {
536541
Environment* env = Environment::GetCurrent(args);
537542

543+
if (IsFipsEnabled()) {
544+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
545+
return Nothing<void>();
546+
}
547+
538548
// args[offset + 0] = algorithm name (string)
539549
CHECK(args[offset]->IsString());
540550
Utf8Value algorithm_name(env->isolate(), args[offset]);

‎src/crypto/crypto_util.cc‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ bool InitCryptoOnce(Isolate* isolate) {
146146
// be part of a larger mutex for global OpenSSL state.
147147
static Mutex fips_mutex;
148148

149+
boolIsFipsEnabled() {
150+
Mutex::ScopedLock fips_lock(fips_mutex);
151+
returnncrypto::isFipsEnabled();
152+
}
153+
149154
voidInitCryptoOnce() {
150155
Mutex::ScopedLock lock(per_process::cli_options_mutex);
151156
Mutex::ScopedLock fips_lock(fips_mutex);
@@ -223,8 +228,7 @@ void InitCryptoOnce() {
223228

224229
voidGetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
225230
Mutex::ScopedLock lock(per_process::cli_options_mutex);
226-
Mutex::ScopedLock fips_lock(fips_mutex);
227-
args.GetReturnValue().Set(ncrypto::isFipsEnabled() ? 1 : 0);
231+
args.GetReturnValue().Set(IsFipsEnabled() ? 1 : 0);
228232
}
229233

230234
voidSetFipsCrypto(const FunctionCallbackInfo<Value>& args) {

‎src/crypto/crypto_util.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ constexpr T NumBitsToBytes(T bits) {
6666
// what went wrong, or std::nullopt when there was nothing to do or the
6767
// options were applied successfully.
6868
std::optional<std::string> ProcessFipsOptions();
69+
boolIsFipsEnabled();
6970

7071
boolInitCryptoOnce(v8::Isolate* isolate);
7172
voidInitCryptoOnce();

‎test/parallel/test-crypto-key-objects-to-crypto-key.js‎

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
}=require('crypto');
1515
const{ hasFIPS }=require('../common/crypto');
1616
const{ kSupportedAlgorithms }=require('internal/crypto/util');
17+
constfips=hasFIPS();
1718
constrejectsXCurves=hasFIPS(3,5);
1819

1920
consthashes=Object.keys(kSupportedAlgorithms.digest).filter((name)=>{
@@ -135,14 +136,24 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
135136
constkey=createSecretKey(randomBytes(32));
136137
constusages=['sign','verify'];
137138

138-
if(allowZeroKey){
139+
if(allowZeroKey&&!fips){
139140
constzeroKey=createSecretKey(Buffer.alloc(0))
140141
.toCryptoKey(algorithm,true,usages);
141142
assert.strictEqual(zeroKey.algorithm.length,0);
142143

143144
constexplicitZeroKey=createSecretKey(Buffer.alloc(0))
144145
.toCryptoKey({ ...algorithm,length: 0},true,usages);
145146
assert.strictEqual(explicitZeroKey.algorithm.length,0);
147+
}elseif(allowZeroKey){
148+
for(constzeroAlgorithmof[algorithm,{ ...algorithm,length: 0}]){
149+
assert.throws(()=>{
150+
createSecretKey(Buffer.alloc(0))
151+
.toCryptoKey(zeroAlgorithm,true,usages);
152+
},{
153+
name: 'NotSupportedError',
154+
message: 'Invalid key length',
155+
});
156+
}
146157
}else{
147158
assert.throws(()=>{
148159
createSecretKey(Buffer.alloc(0)).toCryptoKey(algorithm,true,usages);
@@ -157,12 +168,15 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
157168
message: 'Usages cannot be empty when importing a secret key.'
158169
});
159170

160-
assert.throws(()=>{
161-
key.toCryptoKey({ ...algorithm,length: 0},true,usages);
162-
},{
163-
name: 'DataError',
164-
message: invalidLengthMessage,
165-
});
171+
assert.throws(
172+
()=>key.toCryptoKey({ ...algorithm,length: 0},true,usages),
173+
allowZeroKey&&fips ? {
174+
name: 'NotSupportedError',
175+
message: 'Invalid key length',
176+
} : {
177+
name: 'DataError',
178+
message: invalidLengthMessage,
179+
});
166180
}
167181

168182
functionhmacVectors(){

‎test/parallel/test-webcrypto-derivekey.js‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ const fips4 = hasFIPS(4);
283283
})().then(common.mustCall());
284284
}
285285

286-
if(hasOpenSSL(3)){
286+
if(hasOpenSSL(3)&&!hasFIPS()){
287287
(async()=>{
288288
constderivedKeyAlgorithm={name: 'KMAC128',length: 0};
289289
constusages=['sign'];
@@ -325,11 +325,7 @@ if (hasOpenSSL(3)) {
325325
name: 'KMAC128',
326326
outputLength: 256,
327327
},derived,newUint8Array());
328-
if(fips4){
329-
awaitassert.rejects(signature,{name: 'OperationError'});
330-
}else{
331-
assert.strictEqual((awaitsignature).byteLength,32);
332-
}
328+
assert.strictEqual((awaitsignature).byteLength,32);
333329
}
334330
})().then(common.mustCall());
335331
}

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Commit 66ee479

Browse files
panvaaduh95
authored andcommitted
crypto: disable non-FIPS WebCrypto paths in FIPS mode
Hide TurboSHAKE and KangarooTwelve when FIPS is enabled. Reject cSHAKE and KMAC parameters that require implementations outside the OpenSSL provider, while keeping provider-backed paths available. Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #65172 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent fd7e904 commit 66ee479

20 files changed

Lines changed: 472 additions & 183 deletions

‎lib/internal/crypto/mac.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const {
2020
normalizeHashName,
2121
numBitsToBytes,
2222
truncateToBitLength,
23+
validateKmacKeyLength,
2324
}=require('internal/crypto/util');
2425

2526
const{
@@ -60,6 +61,9 @@ function normalizeKeyLength(handle, algorithm) {
6061
length=algorithm.length;
6162
}
6263

64+
if(algorithm.name==='KMAC128'||algorithm.name==='KMAC256')
65+
validateKmacKeyLength(length);
66+
6367
return{ handle, length };
6468
}
6569

‎lib/internal/crypto/util.js‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,12 @@ const {
4747
EVP_PKEY_ML_KEM_1024,
4848
kKeyVariantAES_OCB_128: hasAesOcbMode,
4949
Argon2Job,
50+
getFipsCrypto,
5051
KmacJob,
5152
}=internalBinding('crypto');
5253

54+
constisFips=getFipsCrypto()===1;
55+
5356
const{ getOptionValue }=require('internal/options');
5457

5558
const{
@@ -415,6 +418,8 @@ const conditionalAlgorithms = {
415418
'Ed448': !process.features.openssl_is_boringssl,
416419
'KMAC128': !!KmacJob,
417420
'KMAC256': !!KmacJob,
421+
'KT128': !isFips,
422+
'KT256': !isFips,
418423
'ML-DSA-44': !!EVP_PKEY_ML_DSA_44,
419424
'ML-DSA-65': !!EVP_PKEY_ML_DSA_65,
420425
'ML-DSA-87': !!EVP_PKEY_ML_DSA_87,
@@ -427,6 +432,8 @@ const conditionalAlgorithms = {
427432
ArrayPrototypeIncludes(getHashes(),'sha3-384'),
428433
'SHA3-512': !process.features.openssl_is_boringssl||
429434
ArrayPrototypeIncludes(getHashes(),'sha3-512'),
435+
'TurboSHAKE128': !isFips,
436+
'TurboSHAKE256': !isFips,
430437
'X448': !process.features.openssl_is_boringssl,
431438
};
432439

@@ -571,6 +578,11 @@ function validateMaxBufferLength(data, name, max = kMaxBufferLength) {
571578
}
572579
}
573580

581+
functionvalidateKmacKeyLength(length){
582+
if((length<32||length%8)&&isFips)
583+
throwlazyDOMException('Invalid key length','NotSupportedError');
584+
}
585+
574586
/**
575587
* Converts a bit length to the number of bytes needed to contain it.
576588
* Non-byte lengths are rounded up to the next byte.
@@ -1088,6 +1100,7 @@ module.exports = {
10881100

10891101
kNamedCurveAliases,
10901102
kSupportedAlgorithms,
1103+
isFips,
10911104
normalizeAlgorithm,
10921105
normalizeHashName,
10931106
hasAnyNotIn,
@@ -1097,6 +1110,7 @@ module.exports = {
10971110
jobPromiseThen,
10981111
cleanupWebCryptoResult,
10991112
prepareWebCryptoResult,
1113+
validateKmacKeyLength,
11001114
validateMaxBufferLength,
11011115
numBitsToBytes,
11021116
truncateToBitLength,

‎lib/internal/crypto/webidl.js‎

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const {
99
StringPrototypeSplit,
1010
StringPrototypeStartsWith,
1111
StringPrototypeToLowerCase,
12-
TypedArrayPrototypeGetLength,
1312
}=primordials;
1413

1514
const{
@@ -28,8 +27,10 @@ const {
2827
validateMaxBufferLength,
2928
getBufferSourceByteLength,
3029
getBufferSourceBytes,
30+
isFips,
3131
kNamedCurveAliases,
3232
numBitsToBytes,
33+
validateKmacKeyLength,
3334
}=require('internal/crypto/util');
3435
const{
3536
converters: webidl,
@@ -276,30 +277,39 @@ function validateCShakeOutputLength(V) {
276277
}
277278
}
278279

279-
functionbufferSourceEqualsAscii(V,string){
280-
if(getBufferSourceByteLength(V)!==string.length)returnfalse;
281-
282-
constbytes=getBufferSourceBytes(V);
283-
constlength=TypedArrayPrototypeGetLength(bytes);
284-
for(leti=0;i<length;i++){
285-
if(bytes[i]!==StringPrototypeCharCodeAt(string,i))returnfalse;
286-
}
287-
returntrue;
288-
}
280+
constkCShakeFunctionNames=['KMAC','TupleHash','ParallelHash'];
289281

290282
functionvalidateCShakeFunctionName(V){
291-
if(getBufferSourceByteLength(V)===0||
292-
bufferSourceEqualsAscii(V,'KMAC')||
293-
bufferSourceEqualsAscii(V,'TupleHash')||
294-
bufferSourceEqualsAscii(V,'ParallelHash')){
295-
return;
283+
constlength=getBufferSourceByteLength(V);
284+
if(length===0)return;
285+
286+
if(!isFips){
287+
constbytes=getBufferSourceBytes(V);
288+
for(leti=0;i<kCShakeFunctionNames.length;i++){
289+
constfunctionName=kCShakeFunctionNames[i];
290+
if(length!==functionName.length)continue;
291+
292+
letj=0;
293+
for(;j<length;j++){
294+
if(bytes[j]!==StringPrototypeCharCodeAt(functionName,j))break;
295+
}
296+
if(j===length)return;
297+
}
296298
}
297299

298300
throwlazyDOMException(
299301
'Unsupported CShakeParams functionName',
300302
'NotSupportedError');
301303
}
302304

305+
functionvalidateCShakeCustomization(V){
306+
if(isFips&&getBufferSourceByteLength(V)!==0)
307+
throwlazyDOMException(
308+
'Unsupported CShakeParams customization',
309+
'NotSupportedError');
310+
validateMaxBufferLength(V,'CShakeParams.customization',512);
311+
}
312+
303313
converters.RsaPssParams=createDictionaryConverter(
304314
'RsaPssParams',[
305315
dictAlgorithm,
@@ -457,7 +467,7 @@ converters.CShakeParams = createDictionaryConverter(
457467
{
458468
key: 'customization',
459469
converter: converters.BufferSource,
460-
validator: (V,opts)=>validateMaxBufferLength(V,'CShakeParams.customization',512),
470+
validator: validateCShakeCustomization,
461471
},
462472
],
463473
]);
@@ -743,6 +753,7 @@ for (let i = 0; i < kKmacDictionaries.length; i++) {
743753
key: 'length',
744754
converter: (V,opts)=>
745755
converters['unsigned long'](V,enforceRangeOptions(opts)),
756+
validator: validateKmacKeyLength,
746757
},
747758
],
748759
]);
@@ -756,6 +767,12 @@ converters.KmacParams = createDictionaryConverter(
756767
key: 'outputLength',
757768
converter: (V,opts)=>
758769
converters['unsigned long'](V,enforceRangeOptions(opts)),
770+
validator: (V)=>{
771+
if((V===0||V%8)&&isFips)
772+
throwlazyDOMException(
773+
'Invalid KmacParams outputLength',
774+
'NotSupportedError');
775+
},
759776
required: true,
760777
},
761778
{

‎src/crypto/crypto_hash.cc‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,11 @@ Maybe<void> CShakeTraits::AdditionalConfig(
812812
CShakeConfig* params) {
813813
Environment* env = Environment::GetCurrent(args);
814814

815+
if (IsFipsEnabled()) {
816+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
817+
return Nothing<void>();
818+
}
819+
815820
CHECK(args[offset]->IsString()); // Algorithm name
816821
Utf8Value algorithm_name(env->isolate(), args[offset]);
817822
std::string_view algorithm_str = algorithm_name.ToStringView();

‎src/crypto/crypto_kmac.cc‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ bool DeriveBitsWithCShake(const KmacConfig& params,
151151
constvoid* key_data,
152152
size_t key_size,
153153
ByteSource* out) {
154+
if (IsFipsEnabled()) returnfalse;
155+
154156
constsize_t key_length_bytes = NumBitsToBytes(params.key_length);
155157
if (key_size < key_length_bytes) returnfalse;
156158

‎src/crypto/crypto_turboshake.cc‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,11 @@ Maybe<void> TurboShakeTraits::AdditionalConfig(
428428
TurboShakeConfig* params) {
429429
Environment* env = Environment::GetCurrent(args);
430430

431+
if (IsFipsEnabled()) {
432+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
433+
return Nothing<void>();
434+
}
435+
431436
// args[offset + 0] = algorithm name (string)
432437
CHECK(args[offset]->IsString());
433438
Utf8Value algorithm_name(env->isolate(), args[offset]);
@@ -535,6 +540,11 @@ Maybe<void> KangarooTwelveTraits::AdditionalConfig(
535540
KangarooTwelveConfig* params) {
536541
Environment* env = Environment::GetCurrent(args);
537542

543+
if (IsFipsEnabled()) {
544+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
545+
return Nothing<void>();
546+
}
547+
538548
// args[offset + 0] = algorithm name (string)
539549
CHECK(args[offset]->IsString());
540550
Utf8Value algorithm_name(env->isolate(), args[offset]);

‎src/crypto/crypto_util.cc‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ bool InitCryptoOnce(Isolate* isolate) {
146146
// be part of a larger mutex for global OpenSSL state.
147147
static Mutex fips_mutex;
148148

149+
boolIsFipsEnabled() {
150+
Mutex::ScopedLock fips_lock(fips_mutex);
151+
returnncrypto::isFipsEnabled();
152+
}
153+
149154
voidInitCryptoOnce() {
150155
Mutex::ScopedLock lock(per_process::cli_options_mutex);
151156
Mutex::ScopedLock fips_lock(fips_mutex);
@@ -223,8 +228,7 @@ void InitCryptoOnce() {
223228

224229
voidGetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
225230
Mutex::ScopedLock lock(per_process::cli_options_mutex);
226-
Mutex::ScopedLock fips_lock(fips_mutex);
227-
args.GetReturnValue().Set(ncrypto::isFipsEnabled() ? 1 : 0);
231+
args.GetReturnValue().Set(IsFipsEnabled() ? 1 : 0);
228232
}
229233

230234
voidSetFipsCrypto(const FunctionCallbackInfo<Value>& args) {

‎src/crypto/crypto_util.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ constexpr T NumBitsToBytes(T bits) {
6666
// what went wrong, or std::nullopt when there was nothing to do or the
6767
// options were applied successfully.
6868
std::optional<std::string> ProcessFipsOptions();
69+
boolIsFipsEnabled();
6970

7071
boolInitCryptoOnce(v8::Isolate* isolate);
7172
voidInitCryptoOnce();

‎test/parallel/test-crypto-key-objects-to-crypto-key.js‎

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
}=require('crypto');
1515
const{ hasFIPS }=require('../common/crypto');
1616
const{ kSupportedAlgorithms }=require('internal/crypto/util');
17+
constfips=hasFIPS();
1718
constrejectsXCurves=hasFIPS(3,5);
1819

1920
consthashes=Object.keys(kSupportedAlgorithms.digest).filter((name)=>{
@@ -135,14 +136,24 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
135136
constkey=createSecretKey(randomBytes(32));
136137
constusages=['sign','verify'];
137138

138-
if(allowZeroKey){
139+
if(allowZeroKey&&!fips){
139140
constzeroKey=createSecretKey(Buffer.alloc(0))
140141
.toCryptoKey(algorithm,true,usages);
141142
assert.strictEqual(zeroKey.algorithm.length,0);
142143

143144
constexplicitZeroKey=createSecretKey(Buffer.alloc(0))
144145
.toCryptoKey({ ...algorithm,length: 0},true,usages);
145146
assert.strictEqual(explicitZeroKey.algorithm.length,0);
147+
}elseif(allowZeroKey){
148+
for(constzeroAlgorithmof[algorithm,{ ...algorithm,length: 0}]){
149+
assert.throws(()=>{
150+
createSecretKey(Buffer.alloc(0))
151+
.toCryptoKey(zeroAlgorithm,true,usages);
152+
},{
153+
name: 'NotSupportedError',
154+
message: 'Invalid key length',
155+
});
156+
}
146157
}else{
147158
assert.throws(()=>{
148159
createSecretKey(Buffer.alloc(0)).toCryptoKey(algorithm,true,usages);
@@ -157,12 +168,15 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
157168
message: 'Usages cannot be empty when importing a secret key.'
158169
});
159170

160-
assert.throws(()=>{
161-
key.toCryptoKey({ ...algorithm,length: 0},true,usages);
162-
},{
163-
name: 'DataError',
164-
message: invalidLengthMessage,
165-
});
171+
assert.throws(
172+
()=>key.toCryptoKey({ ...algorithm,length: 0},true,usages),
173+
allowZeroKey&&fips ? {
174+
name: 'NotSupportedError',
175+
message: 'Invalid key length',
176+
} : {
177+
name: 'DataError',
178+
message: invalidLengthMessage,
179+
});
166180
}
167181

168182
functionhmacVectors(){

‎test/parallel/test-webcrypto-derivekey.js‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ const fips4 = hasFIPS(4);
283283
})().then(common.mustCall());
284284
}
285285

286-
if(hasOpenSSL(3)){
286+
if(hasOpenSSL(3)&&!hasFIPS()){
287287
(async()=>{
288288
constderivedKeyAlgorithm={name: 'KMAC128',length: 0};
289289
constusages=['sign'];
@@ -325,11 +325,7 @@ if (hasOpenSSL(3)) {
325325
name: 'KMAC128',
326326
outputLength: 256,
327327
},derived,newUint8Array());
328-
if(fips4){
329-
awaitassert.rejects(signature,{name: 'OperationError'});
330-
}else{
331-
assert.strictEqual((awaitsignature).byteLength,32);
332-
}
328+
assert.strictEqual((awaitsignature).byteLength,32);
333329
}
334330
})().then(common.mustCall());
335331
}

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 66ee479

Browse files
panvaaduh95
authored andcommitted
crypto: disable non-FIPS WebCrypto paths in FIPS mode
Hide TurboSHAKE and KangarooTwelve when FIPS is enabled. Reject cSHAKE and KMAC parameters that require implementations outside the OpenSSL provider, while keeping provider-backed paths available. Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #65172 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent fd7e904 commit 66ee479

20 files changed

Lines changed: 472 additions & 183 deletions

‎lib/internal/crypto/mac.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const {
2020
normalizeHashName,
2121
numBitsToBytes,
2222
truncateToBitLength,
23+
validateKmacKeyLength,
2324
}=require('internal/crypto/util');
2425

2526
const{
@@ -60,6 +61,9 @@ function normalizeKeyLength(handle, algorithm) {
6061
length=algorithm.length;
6162
}
6263

64+
if(algorithm.name==='KMAC128'||algorithm.name==='KMAC256')
65+
validateKmacKeyLength(length);
66+
6367
return{ handle, length };
6468
}
6569

‎lib/internal/crypto/util.js‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,12 @@ const {
4747
EVP_PKEY_ML_KEM_1024,
4848
kKeyVariantAES_OCB_128: hasAesOcbMode,
4949
Argon2Job,
50+
getFipsCrypto,
5051
KmacJob,
5152
}=internalBinding('crypto');
5253

54+
constisFips=getFipsCrypto()===1;
55+
5356
const{ getOptionValue }=require('internal/options');
5457

5558
const{
@@ -415,6 +418,8 @@ const conditionalAlgorithms = {
415418
'Ed448': !process.features.openssl_is_boringssl,
416419
'KMAC128': !!KmacJob,
417420
'KMAC256': !!KmacJob,
421+
'KT128': !isFips,
422+
'KT256': !isFips,
418423
'ML-DSA-44': !!EVP_PKEY_ML_DSA_44,
419424
'ML-DSA-65': !!EVP_PKEY_ML_DSA_65,
420425
'ML-DSA-87': !!EVP_PKEY_ML_DSA_87,
@@ -427,6 +432,8 @@ const conditionalAlgorithms = {
427432
ArrayPrototypeIncludes(getHashes(),'sha3-384'),
428433
'SHA3-512': !process.features.openssl_is_boringssl||
429434
ArrayPrototypeIncludes(getHashes(),'sha3-512'),
435+
'TurboSHAKE128': !isFips,
436+
'TurboSHAKE256': !isFips,
430437
'X448': !process.features.openssl_is_boringssl,
431438
};
432439

@@ -571,6 +578,11 @@ function validateMaxBufferLength(data, name, max = kMaxBufferLength) {
571578
}
572579
}
573580

581+
functionvalidateKmacKeyLength(length){
582+
if((length<32||length%8)&&isFips)
583+
throwlazyDOMException('Invalid key length','NotSupportedError');
584+
}
585+
574586
/**
575587
* Converts a bit length to the number of bytes needed to contain it.
576588
* Non-byte lengths are rounded up to the next byte.
@@ -1088,6 +1100,7 @@ module.exports = {
10881100

10891101
kNamedCurveAliases,
10901102
kSupportedAlgorithms,
1103+
isFips,
10911104
normalizeAlgorithm,
10921105
normalizeHashName,
10931106
hasAnyNotIn,
@@ -1097,6 +1110,7 @@ module.exports = {
10971110
jobPromiseThen,
10981111
cleanupWebCryptoResult,
10991112
prepareWebCryptoResult,
1113+
validateKmacKeyLength,
11001114
validateMaxBufferLength,
11011115
numBitsToBytes,
11021116
truncateToBitLength,

‎lib/internal/crypto/webidl.js‎

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const {
99
StringPrototypeSplit,
1010
StringPrototypeStartsWith,
1111
StringPrototypeToLowerCase,
12-
TypedArrayPrototypeGetLength,
1312
}=primordials;
1413

1514
const{
@@ -28,8 +27,10 @@ const {
2827
validateMaxBufferLength,
2928
getBufferSourceByteLength,
3029
getBufferSourceBytes,
30+
isFips,
3131
kNamedCurveAliases,
3232
numBitsToBytes,
33+
validateKmacKeyLength,
3334
}=require('internal/crypto/util');
3435
const{
3536
converters: webidl,
@@ -276,30 +277,39 @@ function validateCShakeOutputLength(V) {
276277
}
277278
}
278279

279-
functionbufferSourceEqualsAscii(V,string){
280-
if(getBufferSourceByteLength(V)!==string.length)returnfalse;
281-
282-
constbytes=getBufferSourceBytes(V);
283-
constlength=TypedArrayPrototypeGetLength(bytes);
284-
for(leti=0;i<length;i++){
285-
if(bytes[i]!==StringPrototypeCharCodeAt(string,i))returnfalse;
286-
}
287-
returntrue;
288-
}
280+
constkCShakeFunctionNames=['KMAC','TupleHash','ParallelHash'];
289281

290282
functionvalidateCShakeFunctionName(V){
291-
if(getBufferSourceByteLength(V)===0||
292-
bufferSourceEqualsAscii(V,'KMAC')||
293-
bufferSourceEqualsAscii(V,'TupleHash')||
294-
bufferSourceEqualsAscii(V,'ParallelHash')){
295-
return;
283+
constlength=getBufferSourceByteLength(V);
284+
if(length===0)return;
285+
286+
if(!isFips){
287+
constbytes=getBufferSourceBytes(V);
288+
for(leti=0;i<kCShakeFunctionNames.length;i++){
289+
constfunctionName=kCShakeFunctionNames[i];
290+
if(length!==functionName.length)continue;
291+
292+
letj=0;
293+
for(;j<length;j++){
294+
if(bytes[j]!==StringPrototypeCharCodeAt(functionName,j))break;
295+
}
296+
if(j===length)return;
297+
}
296298
}
297299

298300
throwlazyDOMException(
299301
'Unsupported CShakeParams functionName',
300302
'NotSupportedError');
301303
}
302304

305+
functionvalidateCShakeCustomization(V){
306+
if(isFips&&getBufferSourceByteLength(V)!==0)
307+
throwlazyDOMException(
308+
'Unsupported CShakeParams customization',
309+
'NotSupportedError');
310+
validateMaxBufferLength(V,'CShakeParams.customization',512);
311+
}
312+
303313
converters.RsaPssParams=createDictionaryConverter(
304314
'RsaPssParams',[
305315
dictAlgorithm,
@@ -457,7 +467,7 @@ converters.CShakeParams = createDictionaryConverter(
457467
{
458468
key: 'customization',
459469
converter: converters.BufferSource,
460-
validator: (V,opts)=>validateMaxBufferLength(V,'CShakeParams.customization',512),
470+
validator: validateCShakeCustomization,
461471
},
462472
],
463473
]);
@@ -743,6 +753,7 @@ for (let i = 0; i < kKmacDictionaries.length; i++) {
743753
key: 'length',
744754
converter: (V,opts)=>
745755
converters['unsigned long'](V,enforceRangeOptions(opts)),
756+
validator: validateKmacKeyLength,
746757
},
747758
],
748759
]);
@@ -756,6 +767,12 @@ converters.KmacParams = createDictionaryConverter(
756767
key: 'outputLength',
757768
converter: (V,opts)=>
758769
converters['unsigned long'](V,enforceRangeOptions(opts)),
770+
validator: (V)=>{
771+
if((V===0||V%8)&&isFips)
772+
throwlazyDOMException(
773+
'Invalid KmacParams outputLength',
774+
'NotSupportedError');
775+
},
759776
required: true,
760777
},
761778
{

‎src/crypto/crypto_hash.cc‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,11 @@ Maybe<void> CShakeTraits::AdditionalConfig(
812812
CShakeConfig* params) {
813813
Environment* env = Environment::GetCurrent(args);
814814

815+
if (IsFipsEnabled()) {
816+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
817+
return Nothing<void>();
818+
}
819+
815820
CHECK(args[offset]->IsString()); // Algorithm name
816821
Utf8Value algorithm_name(env->isolate(), args[offset]);
817822
std::string_view algorithm_str = algorithm_name.ToStringView();

‎src/crypto/crypto_kmac.cc‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ bool DeriveBitsWithCShake(const KmacConfig& params,
151151
constvoid* key_data,
152152
size_t key_size,
153153
ByteSource* out) {
154+
if (IsFipsEnabled()) returnfalse;
155+
154156
constsize_t key_length_bytes = NumBitsToBytes(params.key_length);
155157
if (key_size < key_length_bytes) returnfalse;
156158

‎src/crypto/crypto_turboshake.cc‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,11 @@ Maybe<void> TurboShakeTraits::AdditionalConfig(
428428
TurboShakeConfig* params) {
429429
Environment* env = Environment::GetCurrent(args);
430430

431+
if (IsFipsEnabled()) {
432+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
433+
return Nothing<void>();
434+
}
435+
431436
// args[offset + 0] = algorithm name (string)
432437
CHECK(args[offset]->IsString());
433438
Utf8Value algorithm_name(env->isolate(), args[offset]);
@@ -535,6 +540,11 @@ Maybe<void> KangarooTwelveTraits::AdditionalConfig(
535540
KangarooTwelveConfig* params) {
536541
Environment* env = Environment::GetCurrent(args);
537542

543+
if (IsFipsEnabled()) {
544+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
545+
return Nothing<void>();
546+
}
547+
538548
// args[offset + 0] = algorithm name (string)
539549
CHECK(args[offset]->IsString());
540550
Utf8Value algorithm_name(env->isolate(), args[offset]);

‎src/crypto/crypto_util.cc‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ bool InitCryptoOnce(Isolate* isolate) {
146146
// be part of a larger mutex for global OpenSSL state.
147147
static Mutex fips_mutex;
148148

149+
boolIsFipsEnabled() {
150+
Mutex::ScopedLock fips_lock(fips_mutex);
151+
returnncrypto::isFipsEnabled();
152+
}
153+
149154
voidInitCryptoOnce() {
150155
Mutex::ScopedLock lock(per_process::cli_options_mutex);
151156
Mutex::ScopedLock fips_lock(fips_mutex);
@@ -223,8 +228,7 @@ void InitCryptoOnce() {
223228

224229
voidGetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
225230
Mutex::ScopedLock lock(per_process::cli_options_mutex);
226-
Mutex::ScopedLock fips_lock(fips_mutex);
227-
args.GetReturnValue().Set(ncrypto::isFipsEnabled() ? 1 : 0);
231+
args.GetReturnValue().Set(IsFipsEnabled() ? 1 : 0);
228232
}
229233

230234
voidSetFipsCrypto(const FunctionCallbackInfo<Value>& args) {

‎src/crypto/crypto_util.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ constexpr T NumBitsToBytes(T bits) {
6666
// what went wrong, or std::nullopt when there was nothing to do or the
6767
// options were applied successfully.
6868
std::optional<std::string> ProcessFipsOptions();
69+
boolIsFipsEnabled();
6970

7071
boolInitCryptoOnce(v8::Isolate* isolate);
7172
voidInitCryptoOnce();

‎test/parallel/test-crypto-key-objects-to-crypto-key.js‎

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
}=require('crypto');
1515
const{ hasFIPS }=require('../common/crypto');
1616
const{ kSupportedAlgorithms }=require('internal/crypto/util');
17+
constfips=hasFIPS();
1718
constrejectsXCurves=hasFIPS(3,5);
1819

1920
consthashes=Object.keys(kSupportedAlgorithms.digest).filter((name)=>{
@@ -135,14 +136,24 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
135136
constkey=createSecretKey(randomBytes(32));
136137
constusages=['sign','verify'];
137138

138-
if(allowZeroKey){
139+
if(allowZeroKey&&!fips){
139140
constzeroKey=createSecretKey(Buffer.alloc(0))
140141
.toCryptoKey(algorithm,true,usages);
141142
assert.strictEqual(zeroKey.algorithm.length,0);
142143

143144
constexplicitZeroKey=createSecretKey(Buffer.alloc(0))
144145
.toCryptoKey({ ...algorithm,length: 0},true,usages);
145146
assert.strictEqual(explicitZeroKey.algorithm.length,0);
147+
}elseif(allowZeroKey){
148+
for(constzeroAlgorithmof[algorithm,{ ...algorithm,length: 0}]){
149+
assert.throws(()=>{
150+
createSecretKey(Buffer.alloc(0))
151+
.toCryptoKey(zeroAlgorithm,true,usages);
152+
},{
153+
name: 'NotSupportedError',
154+
message: 'Invalid key length',
155+
});
156+
}
146157
}else{
147158
assert.throws(()=>{
148159
createSecretKey(Buffer.alloc(0)).toCryptoKey(algorithm,true,usages);
@@ -157,12 +168,15 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
157168
message: 'Usages cannot be empty when importing a secret key.'
158169
});
159170

160-
assert.throws(()=>{
161-
key.toCryptoKey({ ...algorithm,length: 0},true,usages);
162-
},{
163-
name: 'DataError',
164-
message: invalidLengthMessage,
165-
});
171+
assert.throws(
172+
()=>key.toCryptoKey({ ...algorithm,length: 0},true,usages),
173+
allowZeroKey&&fips ? {
174+
name: 'NotSupportedError',
175+
message: 'Invalid key length',
176+
} : {
177+
name: 'DataError',
178+
message: invalidLengthMessage,
179+
});
166180
}
167181

168182
functionhmacVectors(){

‎test/parallel/test-webcrypto-derivekey.js‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ const fips4 = hasFIPS(4);
283283
})().then(common.mustCall());
284284
}
285285

286-
if(hasOpenSSL(3)){
286+
if(hasOpenSSL(3)&&!hasFIPS()){
287287
(async()=>{
288288
constderivedKeyAlgorithm={name: 'KMAC128',length: 0};
289289
constusages=['sign'];
@@ -325,11 +325,7 @@ if (hasOpenSSL(3)) {
325325
name: 'KMAC128',
326326
outputLength: 256,
327327
},derived,newUint8Array());
328-
if(fips4){
329-
awaitassert.rejects(signature,{name: 'OperationError'});
330-
}else{
331-
assert.strictEqual((awaitsignature).byteLength,32);
332-
}
328+
assert.strictEqual((awaitsignature).byteLength,32);
333329
}
334330
})().then(common.mustCall());
335331
}

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 66ee479

Browse files
panvaaduh95
authored andcommitted
crypto: disable non-FIPS WebCrypto paths in FIPS mode
Hide TurboSHAKE and KangarooTwelve when FIPS is enabled. Reject cSHAKE and KMAC parameters that require implementations outside the OpenSSL provider, while keeping provider-backed paths available. Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #65172 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent fd7e904 commit 66ee479

20 files changed

Lines changed: 472 additions & 183 deletions

‎lib/internal/crypto/mac.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const {
2020
normalizeHashName,
2121
numBitsToBytes,
2222
truncateToBitLength,
23+
validateKmacKeyLength,
2324
}=require('internal/crypto/util');
2425

2526
const{
@@ -60,6 +61,9 @@ function normalizeKeyLength(handle, algorithm) {
6061
length=algorithm.length;
6162
}
6263

64+
if(algorithm.name==='KMAC128'||algorithm.name==='KMAC256')
65+
validateKmacKeyLength(length);
66+
6367
return{ handle, length };
6468
}
6569

‎lib/internal/crypto/util.js‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,12 @@ const {
4747
EVP_PKEY_ML_KEM_1024,
4848
kKeyVariantAES_OCB_128: hasAesOcbMode,
4949
Argon2Job,
50+
getFipsCrypto,
5051
KmacJob,
5152
}=internalBinding('crypto');
5253

54+
constisFips=getFipsCrypto()===1;
55+
5356
const{ getOptionValue }=require('internal/options');
5457

5558
const{
@@ -415,6 +418,8 @@ const conditionalAlgorithms = {
415418
'Ed448': !process.features.openssl_is_boringssl,
416419
'KMAC128': !!KmacJob,
417420
'KMAC256': !!KmacJob,
421+
'KT128': !isFips,
422+
'KT256': !isFips,
418423
'ML-DSA-44': !!EVP_PKEY_ML_DSA_44,
419424
'ML-DSA-65': !!EVP_PKEY_ML_DSA_65,
420425
'ML-DSA-87': !!EVP_PKEY_ML_DSA_87,
@@ -427,6 +432,8 @@ const conditionalAlgorithms = {
427432
ArrayPrototypeIncludes(getHashes(),'sha3-384'),
428433
'SHA3-512': !process.features.openssl_is_boringssl||
429434
ArrayPrototypeIncludes(getHashes(),'sha3-512'),
435+
'TurboSHAKE128': !isFips,
436+
'TurboSHAKE256': !isFips,
430437
'X448': !process.features.openssl_is_boringssl,
431438
};
432439

@@ -571,6 +578,11 @@ function validateMaxBufferLength(data, name, max = kMaxBufferLength) {
571578
}
572579
}
573580

581+
functionvalidateKmacKeyLength(length){
582+
if((length<32||length%8)&&isFips)
583+
throwlazyDOMException('Invalid key length','NotSupportedError');
584+
}
585+
574586
/**
575587
* Converts a bit length to the number of bytes needed to contain it.
576588
* Non-byte lengths are rounded up to the next byte.
@@ -1088,6 +1100,7 @@ module.exports = {
10881100

10891101
kNamedCurveAliases,
10901102
kSupportedAlgorithms,
1103+
isFips,
10911104
normalizeAlgorithm,
10921105
normalizeHashName,
10931106
hasAnyNotIn,
@@ -1097,6 +1110,7 @@ module.exports = {
10971110
jobPromiseThen,
10981111
cleanupWebCryptoResult,
10991112
prepareWebCryptoResult,
1113+
validateKmacKeyLength,
11001114
validateMaxBufferLength,
11011115
numBitsToBytes,
11021116
truncateToBitLength,

‎lib/internal/crypto/webidl.js‎

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const {
99
StringPrototypeSplit,
1010
StringPrototypeStartsWith,
1111
StringPrototypeToLowerCase,
12-
TypedArrayPrototypeGetLength,
1312
}=primordials;
1413

1514
const{
@@ -28,8 +27,10 @@ const {
2827
validateMaxBufferLength,
2928
getBufferSourceByteLength,
3029
getBufferSourceBytes,
30+
isFips,
3131
kNamedCurveAliases,
3232
numBitsToBytes,
33+
validateKmacKeyLength,
3334
}=require('internal/crypto/util');
3435
const{
3536
converters: webidl,
@@ -276,30 +277,39 @@ function validateCShakeOutputLength(V) {
276277
}
277278
}
278279

279-
functionbufferSourceEqualsAscii(V,string){
280-
if(getBufferSourceByteLength(V)!==string.length)returnfalse;
281-
282-
constbytes=getBufferSourceBytes(V);
283-
constlength=TypedArrayPrototypeGetLength(bytes);
284-
for(leti=0;i<length;i++){
285-
if(bytes[i]!==StringPrototypeCharCodeAt(string,i))returnfalse;
286-
}
287-
returntrue;
288-
}
280+
constkCShakeFunctionNames=['KMAC','TupleHash','ParallelHash'];
289281

290282
functionvalidateCShakeFunctionName(V){
291-
if(getBufferSourceByteLength(V)===0||
292-
bufferSourceEqualsAscii(V,'KMAC')||
293-
bufferSourceEqualsAscii(V,'TupleHash')||
294-
bufferSourceEqualsAscii(V,'ParallelHash')){
295-
return;
283+
constlength=getBufferSourceByteLength(V);
284+
if(length===0)return;
285+
286+
if(!isFips){
287+
constbytes=getBufferSourceBytes(V);
288+
for(leti=0;i<kCShakeFunctionNames.length;i++){
289+
constfunctionName=kCShakeFunctionNames[i];
290+
if(length!==functionName.length)continue;
291+
292+
letj=0;
293+
for(;j<length;j++){
294+
if(bytes[j]!==StringPrototypeCharCodeAt(functionName,j))break;
295+
}
296+
if(j===length)return;
297+
}
296298
}
297299

298300
throwlazyDOMException(
299301
'Unsupported CShakeParams functionName',
300302
'NotSupportedError');
301303
}
302304

305+
functionvalidateCShakeCustomization(V){
306+
if(isFips&&getBufferSourceByteLength(V)!==0)
307+
throwlazyDOMException(
308+
'Unsupported CShakeParams customization',
309+
'NotSupportedError');
310+
validateMaxBufferLength(V,'CShakeParams.customization',512);
311+
}
312+
303313
converters.RsaPssParams=createDictionaryConverter(
304314
'RsaPssParams',[
305315
dictAlgorithm,
@@ -457,7 +467,7 @@ converters.CShakeParams = createDictionaryConverter(
457467
{
458468
key: 'customization',
459469
converter: converters.BufferSource,
460-
validator: (V,opts)=>validateMaxBufferLength(V,'CShakeParams.customization',512),
470+
validator: validateCShakeCustomization,
461471
},
462472
],
463473
]);
@@ -743,6 +753,7 @@ for (let i = 0; i < kKmacDictionaries.length; i++) {
743753
key: 'length',
744754
converter: (V,opts)=>
745755
converters['unsigned long'](V,enforceRangeOptions(opts)),
756+
validator: validateKmacKeyLength,
746757
},
747758
],
748759
]);
@@ -756,6 +767,12 @@ converters.KmacParams = createDictionaryConverter(
756767
key: 'outputLength',
757768
converter: (V,opts)=>
758769
converters['unsigned long'](V,enforceRangeOptions(opts)),
770+
validator: (V)=>{
771+
if((V===0||V%8)&&isFips)
772+
throwlazyDOMException(
773+
'Invalid KmacParams outputLength',
774+
'NotSupportedError');
775+
},
759776
required: true,
760777
},
761778
{

‎src/crypto/crypto_hash.cc‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,11 @@ Maybe<void> CShakeTraits::AdditionalConfig(
812812
CShakeConfig* params) {
813813
Environment* env = Environment::GetCurrent(args);
814814

815+
if (IsFipsEnabled()) {
816+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
817+
return Nothing<void>();
818+
}
819+
815820
CHECK(args[offset]->IsString()); // Algorithm name
816821
Utf8Value algorithm_name(env->isolate(), args[offset]);
817822
std::string_view algorithm_str = algorithm_name.ToStringView();

‎src/crypto/crypto_kmac.cc‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ bool DeriveBitsWithCShake(const KmacConfig& params,
151151
constvoid* key_data,
152152
size_t key_size,
153153
ByteSource* out) {
154+
if (IsFipsEnabled()) returnfalse;
155+
154156
constsize_t key_length_bytes = NumBitsToBytes(params.key_length);
155157
if (key_size < key_length_bytes) returnfalse;
156158

‎src/crypto/crypto_turboshake.cc‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,11 @@ Maybe<void> TurboShakeTraits::AdditionalConfig(
428428
TurboShakeConfig* params) {
429429
Environment* env = Environment::GetCurrent(args);
430430

431+
if (IsFipsEnabled()) {
432+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
433+
return Nothing<void>();
434+
}
435+
431436
// args[offset + 0] = algorithm name (string)
432437
CHECK(args[offset]->IsString());
433438
Utf8Value algorithm_name(env->isolate(), args[offset]);
@@ -535,6 +540,11 @@ Maybe<void> KangarooTwelveTraits::AdditionalConfig(
535540
KangarooTwelveConfig* params) {
536541
Environment* env = Environment::GetCurrent(args);
537542

543+
if (IsFipsEnabled()) {
544+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
545+
return Nothing<void>();
546+
}
547+
538548
// args[offset + 0] = algorithm name (string)
539549
CHECK(args[offset]->IsString());
540550
Utf8Value algorithm_name(env->isolate(), args[offset]);

‎src/crypto/crypto_util.cc‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ bool InitCryptoOnce(Isolate* isolate) {
146146
// be part of a larger mutex for global OpenSSL state.
147147
static Mutex fips_mutex;
148148

149+
boolIsFipsEnabled() {
150+
Mutex::ScopedLock fips_lock(fips_mutex);
151+
returnncrypto::isFipsEnabled();
152+
}
153+
149154
voidInitCryptoOnce() {
150155
Mutex::ScopedLock lock(per_process::cli_options_mutex);
151156
Mutex::ScopedLock fips_lock(fips_mutex);
@@ -223,8 +228,7 @@ void InitCryptoOnce() {
223228

224229
voidGetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
225230
Mutex::ScopedLock lock(per_process::cli_options_mutex);
226-
Mutex::ScopedLock fips_lock(fips_mutex);
227-
args.GetReturnValue().Set(ncrypto::isFipsEnabled() ? 1 : 0);
231+
args.GetReturnValue().Set(IsFipsEnabled() ? 1 : 0);
228232
}
229233

230234
voidSetFipsCrypto(const FunctionCallbackInfo<Value>& args) {

‎src/crypto/crypto_util.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ constexpr T NumBitsToBytes(T bits) {
6666
// what went wrong, or std::nullopt when there was nothing to do or the
6767
// options were applied successfully.
6868
std::optional<std::string> ProcessFipsOptions();
69+
boolIsFipsEnabled();
6970

7071
boolInitCryptoOnce(v8::Isolate* isolate);
7172
voidInitCryptoOnce();

‎test/parallel/test-crypto-key-objects-to-crypto-key.js‎

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
}=require('crypto');
1515
const{ hasFIPS }=require('../common/crypto');
1616
const{ kSupportedAlgorithms }=require('internal/crypto/util');
17+
constfips=hasFIPS();
1718
constrejectsXCurves=hasFIPS(3,5);
1819

1920
consthashes=Object.keys(kSupportedAlgorithms.digest).filter((name)=>{
@@ -135,14 +136,24 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
135136
constkey=createSecretKey(randomBytes(32));
136137
constusages=['sign','verify'];
137138

138-
if(allowZeroKey){
139+
if(allowZeroKey&&!fips){
139140
constzeroKey=createSecretKey(Buffer.alloc(0))
140141
.toCryptoKey(algorithm,true,usages);
141142
assert.strictEqual(zeroKey.algorithm.length,0);
142143

143144
constexplicitZeroKey=createSecretKey(Buffer.alloc(0))
144145
.toCryptoKey({ ...algorithm,length: 0},true,usages);
145146
assert.strictEqual(explicitZeroKey.algorithm.length,0);
147+
}elseif(allowZeroKey){
148+
for(constzeroAlgorithmof[algorithm,{ ...algorithm,length: 0}]){
149+
assert.throws(()=>{
150+
createSecretKey(Buffer.alloc(0))
151+
.toCryptoKey(zeroAlgorithm,true,usages);
152+
},{
153+
name: 'NotSupportedError',
154+
message: 'Invalid key length',
155+
});
156+
}
146157
}else{
147158
assert.throws(()=>{
148159
createSecretKey(Buffer.alloc(0)).toCryptoKey(algorithm,true,usages);
@@ -157,12 +168,15 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
157168
message: 'Usages cannot be empty when importing a secret key.'
158169
});
159170

160-
assert.throws(()=>{
161-
key.toCryptoKey({ ...algorithm,length: 0},true,usages);
162-
},{
163-
name: 'DataError',
164-
message: invalidLengthMessage,
165-
});
171+
assert.throws(
172+
()=>key.toCryptoKey({ ...algorithm,length: 0},true,usages),
173+
allowZeroKey&&fips ? {
174+
name: 'NotSupportedError',
175+
message: 'Invalid key length',
176+
} : {
177+
name: 'DataError',
178+
message: invalidLengthMessage,
179+
});
166180
}
167181

168182
functionhmacVectors(){

‎test/parallel/test-webcrypto-derivekey.js‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ const fips4 = hasFIPS(4);
283283
})().then(common.mustCall());
284284
}
285285

286-
if(hasOpenSSL(3)){
286+
if(hasOpenSSL(3)&&!hasFIPS()){
287287
(async()=>{
288288
constderivedKeyAlgorithm={name: 'KMAC128',length: 0};
289289
constusages=['sign'];
@@ -325,11 +325,7 @@ if (hasOpenSSL(3)) {
325325
name: 'KMAC128',
326326
outputLength: 256,
327327
},derived,newUint8Array());
328-
if(fips4){
329-
awaitassert.rejects(signature,{name: 'OperationError'});
330-
}else{
331-
assert.strictEqual((awaitsignature).byteLength,32);
332-
}
328+
assert.strictEqual((awaitsignature).byteLength,32);
333329
}
334330
})().then(common.mustCall());
335331
}

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Commit 66ee479

Browse files
panvaaduh95
authored andcommitted
crypto: disable non-FIPS WebCrypto paths in FIPS mode
Hide TurboSHAKE and KangarooTwelve when FIPS is enabled. Reject cSHAKE and KMAC parameters that require implementations outside the OpenSSL provider, while keeping provider-backed paths available. Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #65172 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent fd7e904 commit 66ee479

20 files changed

Lines changed: 472 additions & 183 deletions

‎lib/internal/crypto/mac.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const {
2020
normalizeHashName,
2121
numBitsToBytes,
2222
truncateToBitLength,
23+
validateKmacKeyLength,
2324
}=require('internal/crypto/util');
2425

2526
const{
@@ -60,6 +61,9 @@ function normalizeKeyLength(handle, algorithm) {
6061
length=algorithm.length;
6162
}
6263

64+
if(algorithm.name==='KMAC128'||algorithm.name==='KMAC256')
65+
validateKmacKeyLength(length);
66+
6367
return{ handle, length };
6468
}
6569

‎lib/internal/crypto/util.js‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,12 @@ const {
4747
EVP_PKEY_ML_KEM_1024,
4848
kKeyVariantAES_OCB_128: hasAesOcbMode,
4949
Argon2Job,
50+
getFipsCrypto,
5051
KmacJob,
5152
}=internalBinding('crypto');
5253

54+
constisFips=getFipsCrypto()===1;
55+
5356
const{ getOptionValue }=require('internal/options');
5457

5558
const{
@@ -415,6 +418,8 @@ const conditionalAlgorithms = {
415418
'Ed448': !process.features.openssl_is_boringssl,
416419
'KMAC128': !!KmacJob,
417420
'KMAC256': !!KmacJob,
421+
'KT128': !isFips,
422+
'KT256': !isFips,
418423
'ML-DSA-44': !!EVP_PKEY_ML_DSA_44,
419424
'ML-DSA-65': !!EVP_PKEY_ML_DSA_65,
420425
'ML-DSA-87': !!EVP_PKEY_ML_DSA_87,
@@ -427,6 +432,8 @@ const conditionalAlgorithms = {
427432
ArrayPrototypeIncludes(getHashes(),'sha3-384'),
428433
'SHA3-512': !process.features.openssl_is_boringssl||
429434
ArrayPrototypeIncludes(getHashes(),'sha3-512'),
435+
'TurboSHAKE128': !isFips,
436+
'TurboSHAKE256': !isFips,
430437
'X448': !process.features.openssl_is_boringssl,
431438
};
432439

@@ -571,6 +578,11 @@ function validateMaxBufferLength(data, name, max = kMaxBufferLength) {
571578
}
572579
}
573580

581+
functionvalidateKmacKeyLength(length){
582+
if((length<32||length%8)&&isFips)
583+
throwlazyDOMException('Invalid key length','NotSupportedError');
584+
}
585+
574586
/**
575587
* Converts a bit length to the number of bytes needed to contain it.
576588
* Non-byte lengths are rounded up to the next byte.
@@ -1088,6 +1100,7 @@ module.exports = {
10881100

10891101
kNamedCurveAliases,
10901102
kSupportedAlgorithms,
1103+
isFips,
10911104
normalizeAlgorithm,
10921105
normalizeHashName,
10931106
hasAnyNotIn,
@@ -1097,6 +1110,7 @@ module.exports = {
10971110
jobPromiseThen,
10981111
cleanupWebCryptoResult,
10991112
prepareWebCryptoResult,
1113+
validateKmacKeyLength,
11001114
validateMaxBufferLength,
11011115
numBitsToBytes,
11021116
truncateToBitLength,

‎lib/internal/crypto/webidl.js‎

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const {
99
StringPrototypeSplit,
1010
StringPrototypeStartsWith,
1111
StringPrototypeToLowerCase,
12-
TypedArrayPrototypeGetLength,
1312
}=primordials;
1413

1514
const{
@@ -28,8 +27,10 @@ const {
2827
validateMaxBufferLength,
2928
getBufferSourceByteLength,
3029
getBufferSourceBytes,
30+
isFips,
3131
kNamedCurveAliases,
3232
numBitsToBytes,
33+
validateKmacKeyLength,
3334
}=require('internal/crypto/util');
3435
const{
3536
converters: webidl,
@@ -276,30 +277,39 @@ function validateCShakeOutputLength(V) {
276277
}
277278
}
278279

279-
functionbufferSourceEqualsAscii(V,string){
280-
if(getBufferSourceByteLength(V)!==string.length)returnfalse;
281-
282-
constbytes=getBufferSourceBytes(V);
283-
constlength=TypedArrayPrototypeGetLength(bytes);
284-
for(leti=0;i<length;i++){
285-
if(bytes[i]!==StringPrototypeCharCodeAt(string,i))returnfalse;
286-
}
287-
returntrue;
288-
}
280+
constkCShakeFunctionNames=['KMAC','TupleHash','ParallelHash'];
289281

290282
functionvalidateCShakeFunctionName(V){
291-
if(getBufferSourceByteLength(V)===0||
292-
bufferSourceEqualsAscii(V,'KMAC')||
293-
bufferSourceEqualsAscii(V,'TupleHash')||
294-
bufferSourceEqualsAscii(V,'ParallelHash')){
295-
return;
283+
constlength=getBufferSourceByteLength(V);
284+
if(length===0)return;
285+
286+
if(!isFips){
287+
constbytes=getBufferSourceBytes(V);
288+
for(leti=0;i<kCShakeFunctionNames.length;i++){
289+
constfunctionName=kCShakeFunctionNames[i];
290+
if(length!==functionName.length)continue;
291+
292+
letj=0;
293+
for(;j<length;j++){
294+
if(bytes[j]!==StringPrototypeCharCodeAt(functionName,j))break;
295+
}
296+
if(j===length)return;
297+
}
296298
}
297299

298300
throwlazyDOMException(
299301
'Unsupported CShakeParams functionName',
300302
'NotSupportedError');
301303
}
302304

305+
functionvalidateCShakeCustomization(V){
306+
if(isFips&&getBufferSourceByteLength(V)!==0)
307+
throwlazyDOMException(
308+
'Unsupported CShakeParams customization',
309+
'NotSupportedError');
310+
validateMaxBufferLength(V,'CShakeParams.customization',512);
311+
}
312+
303313
converters.RsaPssParams=createDictionaryConverter(
304314
'RsaPssParams',[
305315
dictAlgorithm,
@@ -457,7 +467,7 @@ converters.CShakeParams = createDictionaryConverter(
457467
{
458468
key: 'customization',
459469
converter: converters.BufferSource,
460-
validator: (V,opts)=>validateMaxBufferLength(V,'CShakeParams.customization',512),
470+
validator: validateCShakeCustomization,
461471
},
462472
],
463473
]);
@@ -743,6 +753,7 @@ for (let i = 0; i < kKmacDictionaries.length; i++) {
743753
key: 'length',
744754
converter: (V,opts)=>
745755
converters['unsigned long'](V,enforceRangeOptions(opts)),
756+
validator: validateKmacKeyLength,
746757
},
747758
],
748759
]);
@@ -756,6 +767,12 @@ converters.KmacParams = createDictionaryConverter(
756767
key: 'outputLength',
757768
converter: (V,opts)=>
758769
converters['unsigned long'](V,enforceRangeOptions(opts)),
770+
validator: (V)=>{
771+
if((V===0||V%8)&&isFips)
772+
throwlazyDOMException(
773+
'Invalid KmacParams outputLength',
774+
'NotSupportedError');
775+
},
759776
required: true,
760777
},
761778
{

‎src/crypto/crypto_hash.cc‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,11 @@ Maybe<void> CShakeTraits::AdditionalConfig(
812812
CShakeConfig* params) {
813813
Environment* env = Environment::GetCurrent(args);
814814

815+
if (IsFipsEnabled()) {
816+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
817+
return Nothing<void>();
818+
}
819+
815820
CHECK(args[offset]->IsString()); // Algorithm name
816821
Utf8Value algorithm_name(env->isolate(), args[offset]);
817822
std::string_view algorithm_str = algorithm_name.ToStringView();

‎src/crypto/crypto_kmac.cc‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ bool DeriveBitsWithCShake(const KmacConfig& params,
151151
constvoid* key_data,
152152
size_t key_size,
153153
ByteSource* out) {
154+
if (IsFipsEnabled()) returnfalse;
155+
154156
constsize_t key_length_bytes = NumBitsToBytes(params.key_length);
155157
if (key_size < key_length_bytes) returnfalse;
156158

‎src/crypto/crypto_turboshake.cc‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,11 @@ Maybe<void> TurboShakeTraits::AdditionalConfig(
428428
TurboShakeConfig* params) {
429429
Environment* env = Environment::GetCurrent(args);
430430

431+
if (IsFipsEnabled()) {
432+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
433+
return Nothing<void>();
434+
}
435+
431436
// args[offset + 0] = algorithm name (string)
432437
CHECK(args[offset]->IsString());
433438
Utf8Value algorithm_name(env->isolate(), args[offset]);
@@ -535,6 +540,11 @@ Maybe<void> KangarooTwelveTraits::AdditionalConfig(
535540
KangarooTwelveConfig* params) {
536541
Environment* env = Environment::GetCurrent(args);
537542

543+
if (IsFipsEnabled()) {
544+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
545+
return Nothing<void>();
546+
}
547+
538548
// args[offset + 0] = algorithm name (string)
539549
CHECK(args[offset]->IsString());
540550
Utf8Value algorithm_name(env->isolate(), args[offset]);

‎src/crypto/crypto_util.cc‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ bool InitCryptoOnce(Isolate* isolate) {
146146
// be part of a larger mutex for global OpenSSL state.
147147
static Mutex fips_mutex;
148148

149+
boolIsFipsEnabled() {
150+
Mutex::ScopedLock fips_lock(fips_mutex);
151+
returnncrypto::isFipsEnabled();
152+
}
153+
149154
voidInitCryptoOnce() {
150155
Mutex::ScopedLock lock(per_process::cli_options_mutex);
151156
Mutex::ScopedLock fips_lock(fips_mutex);
@@ -223,8 +228,7 @@ void InitCryptoOnce() {
223228

224229
voidGetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
225230
Mutex::ScopedLock lock(per_process::cli_options_mutex);
226-
Mutex::ScopedLock fips_lock(fips_mutex);
227-
args.GetReturnValue().Set(ncrypto::isFipsEnabled() ? 1 : 0);
231+
args.GetReturnValue().Set(IsFipsEnabled() ? 1 : 0);
228232
}
229233

230234
voidSetFipsCrypto(const FunctionCallbackInfo<Value>& args) {

‎src/crypto/crypto_util.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ constexpr T NumBitsToBytes(T bits) {
6666
// what went wrong, or std::nullopt when there was nothing to do or the
6767
// options were applied successfully.
6868
std::optional<std::string> ProcessFipsOptions();
69+
boolIsFipsEnabled();
6970

7071
boolInitCryptoOnce(v8::Isolate* isolate);
7172
voidInitCryptoOnce();

‎test/parallel/test-crypto-key-objects-to-crypto-key.js‎

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
}=require('crypto');
1515
const{ hasFIPS }=require('../common/crypto');
1616
const{ kSupportedAlgorithms }=require('internal/crypto/util');
17+
constfips=hasFIPS();
1718
constrejectsXCurves=hasFIPS(3,5);
1819

1920
consthashes=Object.keys(kSupportedAlgorithms.digest).filter((name)=>{
@@ -135,14 +136,24 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
135136
constkey=createSecretKey(randomBytes(32));
136137
constusages=['sign','verify'];
137138

138-
if(allowZeroKey){
139+
if(allowZeroKey&&!fips){
139140
constzeroKey=createSecretKey(Buffer.alloc(0))
140141
.toCryptoKey(algorithm,true,usages);
141142
assert.strictEqual(zeroKey.algorithm.length,0);
142143

143144
constexplicitZeroKey=createSecretKey(Buffer.alloc(0))
144145
.toCryptoKey({ ...algorithm,length: 0},true,usages);
145146
assert.strictEqual(explicitZeroKey.algorithm.length,0);
147+
}elseif(allowZeroKey){
148+
for(constzeroAlgorithmof[algorithm,{ ...algorithm,length: 0}]){
149+
assert.throws(()=>{
150+
createSecretKey(Buffer.alloc(0))
151+
.toCryptoKey(zeroAlgorithm,true,usages);
152+
},{
153+
name: 'NotSupportedError',
154+
message: 'Invalid key length',
155+
});
156+
}
146157
}else{
147158
assert.throws(()=>{
148159
createSecretKey(Buffer.alloc(0)).toCryptoKey(algorithm,true,usages);
@@ -157,12 +168,15 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
157168
message: 'Usages cannot be empty when importing a secret key.'
158169
});
159170

160-
assert.throws(()=>{
161-
key.toCryptoKey({ ...algorithm,length: 0},true,usages);
162-
},{
163-
name: 'DataError',
164-
message: invalidLengthMessage,
165-
});
171+
assert.throws(
172+
()=>key.toCryptoKey({ ...algorithm,length: 0},true,usages),
173+
allowZeroKey&&fips ? {
174+
name: 'NotSupportedError',
175+
message: 'Invalid key length',
176+
} : {
177+
name: 'DataError',
178+
message: invalidLengthMessage,
179+
});
166180
}
167181

168182
functionhmacVectors(){

‎test/parallel/test-webcrypto-derivekey.js‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ const fips4 = hasFIPS(4);
283283
})().then(common.mustCall());
284284
}
285285

286-
if(hasOpenSSL(3)){
286+
if(hasOpenSSL(3)&&!hasFIPS()){
287287
(async()=>{
288288
constderivedKeyAlgorithm={name: 'KMAC128',length: 0};
289289
constusages=['sign'];
@@ -325,11 +325,7 @@ if (hasOpenSSL(3)) {
325325
name: 'KMAC128',
326326
outputLength: 256,
327327
},derived,newUint8Array());
328-
if(fips4){
329-
awaitassert.rejects(signature,{name: 'OperationError'});
330-
}else{
331-
assert.strictEqual((awaitsignature).byteLength,32);
332-
}
328+
assert.strictEqual((awaitsignature).byteLength,32);
333329
}
334330
})().then(common.mustCall());
335331
}

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 66ee479

Browse files
panvaaduh95
authored andcommitted
crypto: disable non-FIPS WebCrypto paths in FIPS mode
Hide TurboSHAKE and KangarooTwelve when FIPS is enabled. Reject cSHAKE and KMAC parameters that require implementations outside the OpenSSL provider, while keeping provider-backed paths available. Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #65172 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent fd7e904 commit 66ee479

20 files changed

Lines changed: 472 additions & 183 deletions

‎lib/internal/crypto/mac.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const {
2020
normalizeHashName,
2121
numBitsToBytes,
2222
truncateToBitLength,
23+
validateKmacKeyLength,
2324
}=require('internal/crypto/util');
2425

2526
const{
@@ -60,6 +61,9 @@ function normalizeKeyLength(handle, algorithm) {
6061
length=algorithm.length;
6162
}
6263

64+
if(algorithm.name==='KMAC128'||algorithm.name==='KMAC256')
65+
validateKmacKeyLength(length);
66+
6367
return{ handle, length };
6468
}
6569

‎lib/internal/crypto/util.js‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,12 @@ const {
4747
EVP_PKEY_ML_KEM_1024,
4848
kKeyVariantAES_OCB_128: hasAesOcbMode,
4949
Argon2Job,
50+
getFipsCrypto,
5051
KmacJob,
5152
}=internalBinding('crypto');
5253

54+
constisFips=getFipsCrypto()===1;
55+
5356
const{ getOptionValue }=require('internal/options');
5457

5558
const{
@@ -415,6 +418,8 @@ const conditionalAlgorithms = {
415418
'Ed448': !process.features.openssl_is_boringssl,
416419
'KMAC128': !!KmacJob,
417420
'KMAC256': !!KmacJob,
421+
'KT128': !isFips,
422+
'KT256': !isFips,
418423
'ML-DSA-44': !!EVP_PKEY_ML_DSA_44,
419424
'ML-DSA-65': !!EVP_PKEY_ML_DSA_65,
420425
'ML-DSA-87': !!EVP_PKEY_ML_DSA_87,
@@ -427,6 +432,8 @@ const conditionalAlgorithms = {
427432
ArrayPrototypeIncludes(getHashes(),'sha3-384'),
428433
'SHA3-512': !process.features.openssl_is_boringssl||
429434
ArrayPrototypeIncludes(getHashes(),'sha3-512'),
435+
'TurboSHAKE128': !isFips,
436+
'TurboSHAKE256': !isFips,
430437
'X448': !process.features.openssl_is_boringssl,
431438
};
432439

@@ -571,6 +578,11 @@ function validateMaxBufferLength(data, name, max = kMaxBufferLength) {
571578
}
572579
}
573580

581+
functionvalidateKmacKeyLength(length){
582+
if((length<32||length%8)&&isFips)
583+
throwlazyDOMException('Invalid key length','NotSupportedError');
584+
}
585+
574586
/**
575587
* Converts a bit length to the number of bytes needed to contain it.
576588
* Non-byte lengths are rounded up to the next byte.
@@ -1088,6 +1100,7 @@ module.exports = {
10881100

10891101
kNamedCurveAliases,
10901102
kSupportedAlgorithms,
1103+
isFips,
10911104
normalizeAlgorithm,
10921105
normalizeHashName,
10931106
hasAnyNotIn,
@@ -1097,6 +1110,7 @@ module.exports = {
10971110
jobPromiseThen,
10981111
cleanupWebCryptoResult,
10991112
prepareWebCryptoResult,
1113+
validateKmacKeyLength,
11001114
validateMaxBufferLength,
11011115
numBitsToBytes,
11021116
truncateToBitLength,

‎lib/internal/crypto/webidl.js‎

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const {
99
StringPrototypeSplit,
1010
StringPrototypeStartsWith,
1111
StringPrototypeToLowerCase,
12-
TypedArrayPrototypeGetLength,
1312
}=primordials;
1413

1514
const{
@@ -28,8 +27,10 @@ const {
2827
validateMaxBufferLength,
2928
getBufferSourceByteLength,
3029
getBufferSourceBytes,
30+
isFips,
3131
kNamedCurveAliases,
3232
numBitsToBytes,
33+
validateKmacKeyLength,
3334
}=require('internal/crypto/util');
3435
const{
3536
converters: webidl,
@@ -276,30 +277,39 @@ function validateCShakeOutputLength(V) {
276277
}
277278
}
278279

279-
functionbufferSourceEqualsAscii(V,string){
280-
if(getBufferSourceByteLength(V)!==string.length)returnfalse;
281-
282-
constbytes=getBufferSourceBytes(V);
283-
constlength=TypedArrayPrototypeGetLength(bytes);
284-
for(leti=0;i<length;i++){
285-
if(bytes[i]!==StringPrototypeCharCodeAt(string,i))returnfalse;
286-
}
287-
returntrue;
288-
}
280+
constkCShakeFunctionNames=['KMAC','TupleHash','ParallelHash'];
289281

290282
functionvalidateCShakeFunctionName(V){
291-
if(getBufferSourceByteLength(V)===0||
292-
bufferSourceEqualsAscii(V,'KMAC')||
293-
bufferSourceEqualsAscii(V,'TupleHash')||
294-
bufferSourceEqualsAscii(V,'ParallelHash')){
295-
return;
283+
constlength=getBufferSourceByteLength(V);
284+
if(length===0)return;
285+
286+
if(!isFips){
287+
constbytes=getBufferSourceBytes(V);
288+
for(leti=0;i<kCShakeFunctionNames.length;i++){
289+
constfunctionName=kCShakeFunctionNames[i];
290+
if(length!==functionName.length)continue;
291+
292+
letj=0;
293+
for(;j<length;j++){
294+
if(bytes[j]!==StringPrototypeCharCodeAt(functionName,j))break;
295+
}
296+
if(j===length)return;
297+
}
296298
}
297299

298300
throwlazyDOMException(
299301
'Unsupported CShakeParams functionName',
300302
'NotSupportedError');
301303
}
302304

305+
functionvalidateCShakeCustomization(V){
306+
if(isFips&&getBufferSourceByteLength(V)!==0)
307+
throwlazyDOMException(
308+
'Unsupported CShakeParams customization',
309+
'NotSupportedError');
310+
validateMaxBufferLength(V,'CShakeParams.customization',512);
311+
}
312+
303313
converters.RsaPssParams=createDictionaryConverter(
304314
'RsaPssParams',[
305315
dictAlgorithm,
@@ -457,7 +467,7 @@ converters.CShakeParams = createDictionaryConverter(
457467
{
458468
key: 'customization',
459469
converter: converters.BufferSource,
460-
validator: (V,opts)=>validateMaxBufferLength(V,'CShakeParams.customization',512),
470+
validator: validateCShakeCustomization,
461471
},
462472
],
463473
]);
@@ -743,6 +753,7 @@ for (let i = 0; i < kKmacDictionaries.length; i++) {
743753
key: 'length',
744754
converter: (V,opts)=>
745755
converters['unsigned long'](V,enforceRangeOptions(opts)),
756+
validator: validateKmacKeyLength,
746757
},
747758
],
748759
]);
@@ -756,6 +767,12 @@ converters.KmacParams = createDictionaryConverter(
756767
key: 'outputLength',
757768
converter: (V,opts)=>
758769
converters['unsigned long'](V,enforceRangeOptions(opts)),
770+
validator: (V)=>{
771+
if((V===0||V%8)&&isFips)
772+
throwlazyDOMException(
773+
'Invalid KmacParams outputLength',
774+
'NotSupportedError');
775+
},
759776
required: true,
760777
},
761778
{

‎src/crypto/crypto_hash.cc‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,11 @@ Maybe<void> CShakeTraits::AdditionalConfig(
812812
CShakeConfig* params) {
813813
Environment* env = Environment::GetCurrent(args);
814814

815+
if (IsFipsEnabled()) {
816+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
817+
return Nothing<void>();
818+
}
819+
815820
CHECK(args[offset]->IsString()); // Algorithm name
816821
Utf8Value algorithm_name(env->isolate(), args[offset]);
817822
std::string_view algorithm_str = algorithm_name.ToStringView();

‎src/crypto/crypto_kmac.cc‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ bool DeriveBitsWithCShake(const KmacConfig& params,
151151
constvoid* key_data,
152152
size_t key_size,
153153
ByteSource* out) {
154+
if (IsFipsEnabled()) returnfalse;
155+
154156
constsize_t key_length_bytes = NumBitsToBytes(params.key_length);
155157
if (key_size < key_length_bytes) returnfalse;
156158

‎src/crypto/crypto_turboshake.cc‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,11 @@ Maybe<void> TurboShakeTraits::AdditionalConfig(
428428
TurboShakeConfig* params) {
429429
Environment* env = Environment::GetCurrent(args);
430430

431+
if (IsFipsEnabled()) {
432+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
433+
return Nothing<void>();
434+
}
435+
431436
// args[offset + 0] = algorithm name (string)
432437
CHECK(args[offset]->IsString());
433438
Utf8Value algorithm_name(env->isolate(), args[offset]);
@@ -535,6 +540,11 @@ Maybe<void> KangarooTwelveTraits::AdditionalConfig(
535540
KangarooTwelveConfig* params) {
536541
Environment* env = Environment::GetCurrent(args);
537542

543+
if (IsFipsEnabled()) {
544+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
545+
return Nothing<void>();
546+
}
547+
538548
// args[offset + 0] = algorithm name (string)
539549
CHECK(args[offset]->IsString());
540550
Utf8Value algorithm_name(env->isolate(), args[offset]);

‎src/crypto/crypto_util.cc‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ bool InitCryptoOnce(Isolate* isolate) {
146146
// be part of a larger mutex for global OpenSSL state.
147147
static Mutex fips_mutex;
148148

149+
boolIsFipsEnabled() {
150+
Mutex::ScopedLock fips_lock(fips_mutex);
151+
returnncrypto::isFipsEnabled();
152+
}
153+
149154
voidInitCryptoOnce() {
150155
Mutex::ScopedLock lock(per_process::cli_options_mutex);
151156
Mutex::ScopedLock fips_lock(fips_mutex);
@@ -223,8 +228,7 @@ void InitCryptoOnce() {
223228

224229
voidGetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
225230
Mutex::ScopedLock lock(per_process::cli_options_mutex);
226-
Mutex::ScopedLock fips_lock(fips_mutex);
227-
args.GetReturnValue().Set(ncrypto::isFipsEnabled() ? 1 : 0);
231+
args.GetReturnValue().Set(IsFipsEnabled() ? 1 : 0);
228232
}
229233

230234
voidSetFipsCrypto(const FunctionCallbackInfo<Value>& args) {

‎src/crypto/crypto_util.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ constexpr T NumBitsToBytes(T bits) {
6666
// what went wrong, or std::nullopt when there was nothing to do or the
6767
// options were applied successfully.
6868
std::optional<std::string> ProcessFipsOptions();
69+
boolIsFipsEnabled();
6970

7071
boolInitCryptoOnce(v8::Isolate* isolate);
7172
voidInitCryptoOnce();

‎test/parallel/test-crypto-key-objects-to-crypto-key.js‎

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
}=require('crypto');
1515
const{ hasFIPS }=require('../common/crypto');
1616
const{ kSupportedAlgorithms }=require('internal/crypto/util');
17+
constfips=hasFIPS();
1718
constrejectsXCurves=hasFIPS(3,5);
1819

1920
consthashes=Object.keys(kSupportedAlgorithms.digest).filter((name)=>{
@@ -135,14 +136,24 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
135136
constkey=createSecretKey(randomBytes(32));
136137
constusages=['sign','verify'];
137138

138-
if(allowZeroKey){
139+
if(allowZeroKey&&!fips){
139140
constzeroKey=createSecretKey(Buffer.alloc(0))
140141
.toCryptoKey(algorithm,true,usages);
141142
assert.strictEqual(zeroKey.algorithm.length,0);
142143

143144
constexplicitZeroKey=createSecretKey(Buffer.alloc(0))
144145
.toCryptoKey({ ...algorithm,length: 0},true,usages);
145146
assert.strictEqual(explicitZeroKey.algorithm.length,0);
147+
}elseif(allowZeroKey){
148+
for(constzeroAlgorithmof[algorithm,{ ...algorithm,length: 0}]){
149+
assert.throws(()=>{
150+
createSecretKey(Buffer.alloc(0))
151+
.toCryptoKey(zeroAlgorithm,true,usages);
152+
},{
153+
name: 'NotSupportedError',
154+
message: 'Invalid key length',
155+
});
156+
}
146157
}else{
147158
assert.throws(()=>{
148159
createSecretKey(Buffer.alloc(0)).toCryptoKey(algorithm,true,usages);
@@ -157,12 +168,15 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
157168
message: 'Usages cannot be empty when importing a secret key.'
158169
});
159170

160-
assert.throws(()=>{
161-
key.toCryptoKey({ ...algorithm,length: 0},true,usages);
162-
},{
163-
name: 'DataError',
164-
message: invalidLengthMessage,
165-
});
171+
assert.throws(
172+
()=>key.toCryptoKey({ ...algorithm,length: 0},true,usages),
173+
allowZeroKey&&fips ? {
174+
name: 'NotSupportedError',
175+
message: 'Invalid key length',
176+
} : {
177+
name: 'DataError',
178+
message: invalidLengthMessage,
179+
});
166180
}
167181

168182
functionhmacVectors(){

‎test/parallel/test-webcrypto-derivekey.js‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ const fips4 = hasFIPS(4);
283283
})().then(common.mustCall());
284284
}
285285

286-
if(hasOpenSSL(3)){
286+
if(hasOpenSSL(3)&&!hasFIPS()){
287287
(async()=>{
288288
constderivedKeyAlgorithm={name: 'KMAC128',length: 0};
289289
constusages=['sign'];
@@ -325,11 +325,7 @@ if (hasOpenSSL(3)) {
325325
name: 'KMAC128',
326326
outputLength: 256,
327327
},derived,newUint8Array());
328-
if(fips4){
329-
awaitassert.rejects(signature,{name: 'OperationError'});
330-
}else{
331-
assert.strictEqual((awaitsignature).byteLength,32);
332-
}
328+
assert.strictEqual((awaitsignature).byteLength,32);
333329
}
334330
})().then(common.mustCall());
335331
}

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 66ee479

Browse files
panvaaduh95
authored andcommitted
crypto: disable non-FIPS WebCrypto paths in FIPS mode
Hide TurboSHAKE and KangarooTwelve when FIPS is enabled. Reject cSHAKE and KMAC parameters that require implementations outside the OpenSSL provider, while keeping provider-backed paths available. Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #65172 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent fd7e904 commit 66ee479

20 files changed

Lines changed: 472 additions & 183 deletions

‎lib/internal/crypto/mac.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const {
2020
normalizeHashName,
2121
numBitsToBytes,
2222
truncateToBitLength,
23+
validateKmacKeyLength,
2324
}=require('internal/crypto/util');
2425

2526
const{
@@ -60,6 +61,9 @@ function normalizeKeyLength(handle, algorithm) {
6061
length=algorithm.length;
6162
}
6263

64+
if(algorithm.name==='KMAC128'||algorithm.name==='KMAC256')
65+
validateKmacKeyLength(length);
66+
6367
return{ handle, length };
6468
}
6569

‎lib/internal/crypto/util.js‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,12 @@ const {
4747
EVP_PKEY_ML_KEM_1024,
4848
kKeyVariantAES_OCB_128: hasAesOcbMode,
4949
Argon2Job,
50+
getFipsCrypto,
5051
KmacJob,
5152
}=internalBinding('crypto');
5253

54+
constisFips=getFipsCrypto()===1;
55+
5356
const{ getOptionValue }=require('internal/options');
5457

5558
const{
@@ -415,6 +418,8 @@ const conditionalAlgorithms = {
415418
'Ed448': !process.features.openssl_is_boringssl,
416419
'KMAC128': !!KmacJob,
417420
'KMAC256': !!KmacJob,
421+
'KT128': !isFips,
422+
'KT256': !isFips,
418423
'ML-DSA-44': !!EVP_PKEY_ML_DSA_44,
419424
'ML-DSA-65': !!EVP_PKEY_ML_DSA_65,
420425
'ML-DSA-87': !!EVP_PKEY_ML_DSA_87,
@@ -427,6 +432,8 @@ const conditionalAlgorithms = {
427432
ArrayPrototypeIncludes(getHashes(),'sha3-384'),
428433
'SHA3-512': !process.features.openssl_is_boringssl||
429434
ArrayPrototypeIncludes(getHashes(),'sha3-512'),
435+
'TurboSHAKE128': !isFips,
436+
'TurboSHAKE256': !isFips,
430437
'X448': !process.features.openssl_is_boringssl,
431438
};
432439

@@ -571,6 +578,11 @@ function validateMaxBufferLength(data, name, max = kMaxBufferLength) {
571578
}
572579
}
573580

581+
functionvalidateKmacKeyLength(length){
582+
if((length<32||length%8)&&isFips)
583+
throwlazyDOMException('Invalid key length','NotSupportedError');
584+
}
585+
574586
/**
575587
* Converts a bit length to the number of bytes needed to contain it.
576588
* Non-byte lengths are rounded up to the next byte.
@@ -1088,6 +1100,7 @@ module.exports = {
10881100

10891101
kNamedCurveAliases,
10901102
kSupportedAlgorithms,
1103+
isFips,
10911104
normalizeAlgorithm,
10921105
normalizeHashName,
10931106
hasAnyNotIn,
@@ -1097,6 +1110,7 @@ module.exports = {
10971110
jobPromiseThen,
10981111
cleanupWebCryptoResult,
10991112
prepareWebCryptoResult,
1113+
validateKmacKeyLength,
11001114
validateMaxBufferLength,
11011115
numBitsToBytes,
11021116
truncateToBitLength,

‎lib/internal/crypto/webidl.js‎

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const {
99
StringPrototypeSplit,
1010
StringPrototypeStartsWith,
1111
StringPrototypeToLowerCase,
12-
TypedArrayPrototypeGetLength,
1312
}=primordials;
1413

1514
const{
@@ -28,8 +27,10 @@ const {
2827
validateMaxBufferLength,
2928
getBufferSourceByteLength,
3029
getBufferSourceBytes,
30+
isFips,
3131
kNamedCurveAliases,
3232
numBitsToBytes,
33+
validateKmacKeyLength,
3334
}=require('internal/crypto/util');
3435
const{
3536
converters: webidl,
@@ -276,30 +277,39 @@ function validateCShakeOutputLength(V) {
276277
}
277278
}
278279

279-
functionbufferSourceEqualsAscii(V,string){
280-
if(getBufferSourceByteLength(V)!==string.length)returnfalse;
281-
282-
constbytes=getBufferSourceBytes(V);
283-
constlength=TypedArrayPrototypeGetLength(bytes);
284-
for(leti=0;i<length;i++){
285-
if(bytes[i]!==StringPrototypeCharCodeAt(string,i))returnfalse;
286-
}
287-
returntrue;
288-
}
280+
constkCShakeFunctionNames=['KMAC','TupleHash','ParallelHash'];
289281

290282
functionvalidateCShakeFunctionName(V){
291-
if(getBufferSourceByteLength(V)===0||
292-
bufferSourceEqualsAscii(V,'KMAC')||
293-
bufferSourceEqualsAscii(V,'TupleHash')||
294-
bufferSourceEqualsAscii(V,'ParallelHash')){
295-
return;
283+
constlength=getBufferSourceByteLength(V);
284+
if(length===0)return;
285+
286+
if(!isFips){
287+
constbytes=getBufferSourceBytes(V);
288+
for(leti=0;i<kCShakeFunctionNames.length;i++){
289+
constfunctionName=kCShakeFunctionNames[i];
290+
if(length!==functionName.length)continue;
291+
292+
letj=0;
293+
for(;j<length;j++){
294+
if(bytes[j]!==StringPrototypeCharCodeAt(functionName,j))break;
295+
}
296+
if(j===length)return;
297+
}
296298
}
297299

298300
throwlazyDOMException(
299301
'Unsupported CShakeParams functionName',
300302
'NotSupportedError');
301303
}
302304

305+
functionvalidateCShakeCustomization(V){
306+
if(isFips&&getBufferSourceByteLength(V)!==0)
307+
throwlazyDOMException(
308+
'Unsupported CShakeParams customization',
309+
'NotSupportedError');
310+
validateMaxBufferLength(V,'CShakeParams.customization',512);
311+
}
312+
303313
converters.RsaPssParams=createDictionaryConverter(
304314
'RsaPssParams',[
305315
dictAlgorithm,
@@ -457,7 +467,7 @@ converters.CShakeParams = createDictionaryConverter(
457467
{
458468
key: 'customization',
459469
converter: converters.BufferSource,
460-
validator: (V,opts)=>validateMaxBufferLength(V,'CShakeParams.customization',512),
470+
validator: validateCShakeCustomization,
461471
},
462472
],
463473
]);
@@ -743,6 +753,7 @@ for (let i = 0; i < kKmacDictionaries.length; i++) {
743753
key: 'length',
744754
converter: (V,opts)=>
745755
converters['unsigned long'](V,enforceRangeOptions(opts)),
756+
validator: validateKmacKeyLength,
746757
},
747758
],
748759
]);
@@ -756,6 +767,12 @@ converters.KmacParams = createDictionaryConverter(
756767
key: 'outputLength',
757768
converter: (V,opts)=>
758769
converters['unsigned long'](V,enforceRangeOptions(opts)),
770+
validator: (V)=>{
771+
if((V===0||V%8)&&isFips)
772+
throwlazyDOMException(
773+
'Invalid KmacParams outputLength',
774+
'NotSupportedError');
775+
},
759776
required: true,
760777
},
761778
{

‎src/crypto/crypto_hash.cc‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,11 @@ Maybe<void> CShakeTraits::AdditionalConfig(
812812
CShakeConfig* params) {
813813
Environment* env = Environment::GetCurrent(args);
814814

815+
if (IsFipsEnabled()) {
816+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
817+
return Nothing<void>();
818+
}
819+
815820
CHECK(args[offset]->IsString()); // Algorithm name
816821
Utf8Value algorithm_name(env->isolate(), args[offset]);
817822
std::string_view algorithm_str = algorithm_name.ToStringView();

‎src/crypto/crypto_kmac.cc‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ bool DeriveBitsWithCShake(const KmacConfig& params,
151151
constvoid* key_data,
152152
size_t key_size,
153153
ByteSource* out) {
154+
if (IsFipsEnabled()) returnfalse;
155+
154156
constsize_t key_length_bytes = NumBitsToBytes(params.key_length);
155157
if (key_size < key_length_bytes) returnfalse;
156158

‎src/crypto/crypto_turboshake.cc‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,11 @@ Maybe<void> TurboShakeTraits::AdditionalConfig(
428428
TurboShakeConfig* params) {
429429
Environment* env = Environment::GetCurrent(args);
430430

431+
if (IsFipsEnabled()) {
432+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
433+
return Nothing<void>();
434+
}
435+
431436
// args[offset + 0] = algorithm name (string)
432437
CHECK(args[offset]->IsString());
433438
Utf8Value algorithm_name(env->isolate(), args[offset]);
@@ -535,6 +540,11 @@ Maybe<void> KangarooTwelveTraits::AdditionalConfig(
535540
KangarooTwelveConfig* params) {
536541
Environment* env = Environment::GetCurrent(args);
537542

543+
if (IsFipsEnabled()) {
544+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
545+
return Nothing<void>();
546+
}
547+
538548
// args[offset + 0] = algorithm name (string)
539549
CHECK(args[offset]->IsString());
540550
Utf8Value algorithm_name(env->isolate(), args[offset]);

‎src/crypto/crypto_util.cc‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ bool InitCryptoOnce(Isolate* isolate) {
146146
// be part of a larger mutex for global OpenSSL state.
147147
static Mutex fips_mutex;
148148

149+
boolIsFipsEnabled() {
150+
Mutex::ScopedLock fips_lock(fips_mutex);
151+
returnncrypto::isFipsEnabled();
152+
}
153+
149154
voidInitCryptoOnce() {
150155
Mutex::ScopedLock lock(per_process::cli_options_mutex);
151156
Mutex::ScopedLock fips_lock(fips_mutex);
@@ -223,8 +228,7 @@ void InitCryptoOnce() {
223228

224229
voidGetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
225230
Mutex::ScopedLock lock(per_process::cli_options_mutex);
226-
Mutex::ScopedLock fips_lock(fips_mutex);
227-
args.GetReturnValue().Set(ncrypto::isFipsEnabled() ? 1 : 0);
231+
args.GetReturnValue().Set(IsFipsEnabled() ? 1 : 0);
228232
}
229233

230234
voidSetFipsCrypto(const FunctionCallbackInfo<Value>& args) {

‎src/crypto/crypto_util.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ constexpr T NumBitsToBytes(T bits) {
6666
// what went wrong, or std::nullopt when there was nothing to do or the
6767
// options were applied successfully.
6868
std::optional<std::string> ProcessFipsOptions();
69+
boolIsFipsEnabled();
6970

7071
boolInitCryptoOnce(v8::Isolate* isolate);
7172
voidInitCryptoOnce();

‎test/parallel/test-crypto-key-objects-to-crypto-key.js‎

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
}=require('crypto');
1515
const{ hasFIPS }=require('../common/crypto');
1616
const{ kSupportedAlgorithms }=require('internal/crypto/util');
17+
constfips=hasFIPS();
1718
constrejectsXCurves=hasFIPS(3,5);
1819

1920
consthashes=Object.keys(kSupportedAlgorithms.digest).filter((name)=>{
@@ -135,14 +136,24 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
135136
constkey=createSecretKey(randomBytes(32));
136137
constusages=['sign','verify'];
137138

138-
if(allowZeroKey){
139+
if(allowZeroKey&&!fips){
139140
constzeroKey=createSecretKey(Buffer.alloc(0))
140141
.toCryptoKey(algorithm,true,usages);
141142
assert.strictEqual(zeroKey.algorithm.length,0);
142143

143144
constexplicitZeroKey=createSecretKey(Buffer.alloc(0))
144145
.toCryptoKey({ ...algorithm,length: 0},true,usages);
145146
assert.strictEqual(explicitZeroKey.algorithm.length,0);
147+
}elseif(allowZeroKey){
148+
for(constzeroAlgorithmof[algorithm,{ ...algorithm,length: 0}]){
149+
assert.throws(()=>{
150+
createSecretKey(Buffer.alloc(0))
151+
.toCryptoKey(zeroAlgorithm,true,usages);
152+
},{
153+
name: 'NotSupportedError',
154+
message: 'Invalid key length',
155+
});
156+
}
146157
}else{
147158
assert.throws(()=>{
148159
createSecretKey(Buffer.alloc(0)).toCryptoKey(algorithm,true,usages);
@@ -157,12 +168,15 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
157168
message: 'Usages cannot be empty when importing a secret key.'
158169
});
159170

160-
assert.throws(()=>{
161-
key.toCryptoKey({ ...algorithm,length: 0},true,usages);
162-
},{
163-
name: 'DataError',
164-
message: invalidLengthMessage,
165-
});
171+
assert.throws(
172+
()=>key.toCryptoKey({ ...algorithm,length: 0},true,usages),
173+
allowZeroKey&&fips ? {
174+
name: 'NotSupportedError',
175+
message: 'Invalid key length',
176+
} : {
177+
name: 'DataError',
178+
message: invalidLengthMessage,
179+
});
166180
}
167181

168182
functionhmacVectors(){

‎test/parallel/test-webcrypto-derivekey.js‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ const fips4 = hasFIPS(4);
283283
})().then(common.mustCall());
284284
}
285285

286-
if(hasOpenSSL(3)){
286+
if(hasOpenSSL(3)&&!hasFIPS()){
287287
(async()=>{
288288
constderivedKeyAlgorithm={name: 'KMAC128',length: 0};
289289
constusages=['sign'];
@@ -325,11 +325,7 @@ if (hasOpenSSL(3)) {
325325
name: 'KMAC128',
326326
outputLength: 256,
327327
},derived,newUint8Array());
328-
if(fips4){
329-
awaitassert.rejects(signature,{name: 'OperationError'});
330-
}else{
331-
assert.strictEqual((awaitsignature).byteLength,32);
332-
}
328+
assert.strictEqual((awaitsignature).byteLength,32);
333329
}
334330
})().then(common.mustCall());
335331
}

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Commit 66ee479

Browse files
panvaaduh95
authored andcommitted
crypto: disable non-FIPS WebCrypto paths in FIPS mode
Hide TurboSHAKE and KangarooTwelve when FIPS is enabled. Reject cSHAKE and KMAC parameters that require implementations outside the OpenSSL provider, while keeping provider-backed paths available. Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #65172 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent fd7e904 commit 66ee479

20 files changed

Lines changed: 472 additions & 183 deletions

‎lib/internal/crypto/mac.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const {
2020
normalizeHashName,
2121
numBitsToBytes,
2222
truncateToBitLength,
23+
validateKmacKeyLength,
2324
}=require('internal/crypto/util');
2425

2526
const{
@@ -60,6 +61,9 @@ function normalizeKeyLength(handle, algorithm) {
6061
length=algorithm.length;
6162
}
6263

64+
if(algorithm.name==='KMAC128'||algorithm.name==='KMAC256')
65+
validateKmacKeyLength(length);
66+
6367
return{ handle, length };
6468
}
6569

‎lib/internal/crypto/util.js‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,12 @@ const {
4747
EVP_PKEY_ML_KEM_1024,
4848
kKeyVariantAES_OCB_128: hasAesOcbMode,
4949
Argon2Job,
50+
getFipsCrypto,
5051
KmacJob,
5152
}=internalBinding('crypto');
5253

54+
constisFips=getFipsCrypto()===1;
55+
5356
const{ getOptionValue }=require('internal/options');
5457

5558
const{
@@ -415,6 +418,8 @@ const conditionalAlgorithms = {
415418
'Ed448': !process.features.openssl_is_boringssl,
416419
'KMAC128': !!KmacJob,
417420
'KMAC256': !!KmacJob,
421+
'KT128': !isFips,
422+
'KT256': !isFips,
418423
'ML-DSA-44': !!EVP_PKEY_ML_DSA_44,
419424
'ML-DSA-65': !!EVP_PKEY_ML_DSA_65,
420425
'ML-DSA-87': !!EVP_PKEY_ML_DSA_87,
@@ -427,6 +432,8 @@ const conditionalAlgorithms = {
427432
ArrayPrototypeIncludes(getHashes(),'sha3-384'),
428433
'SHA3-512': !process.features.openssl_is_boringssl||
429434
ArrayPrototypeIncludes(getHashes(),'sha3-512'),
435+
'TurboSHAKE128': !isFips,
436+
'TurboSHAKE256': !isFips,
430437
'X448': !process.features.openssl_is_boringssl,
431438
};
432439

@@ -571,6 +578,11 @@ function validateMaxBufferLength(data, name, max = kMaxBufferLength) {
571578
}
572579
}
573580

581+
functionvalidateKmacKeyLength(length){
582+
if((length<32||length%8)&&isFips)
583+
throwlazyDOMException('Invalid key length','NotSupportedError');
584+
}
585+
574586
/**
575587
* Converts a bit length to the number of bytes needed to contain it.
576588
* Non-byte lengths are rounded up to the next byte.
@@ -1088,6 +1100,7 @@ module.exports = {
10881100

10891101
kNamedCurveAliases,
10901102
kSupportedAlgorithms,
1103+
isFips,
10911104
normalizeAlgorithm,
10921105
normalizeHashName,
10931106
hasAnyNotIn,
@@ -1097,6 +1110,7 @@ module.exports = {
10971110
jobPromiseThen,
10981111
cleanupWebCryptoResult,
10991112
prepareWebCryptoResult,
1113+
validateKmacKeyLength,
11001114
validateMaxBufferLength,
11011115
numBitsToBytes,
11021116
truncateToBitLength,

‎lib/internal/crypto/webidl.js‎

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const {
99
StringPrototypeSplit,
1010
StringPrototypeStartsWith,
1111
StringPrototypeToLowerCase,
12-
TypedArrayPrototypeGetLength,
1312
}=primordials;
1413

1514
const{
@@ -28,8 +27,10 @@ const {
2827
validateMaxBufferLength,
2928
getBufferSourceByteLength,
3029
getBufferSourceBytes,
30+
isFips,
3131
kNamedCurveAliases,
3232
numBitsToBytes,
33+
validateKmacKeyLength,
3334
}=require('internal/crypto/util');
3435
const{
3536
converters: webidl,
@@ -276,30 +277,39 @@ function validateCShakeOutputLength(V) {
276277
}
277278
}
278279

279-
functionbufferSourceEqualsAscii(V,string){
280-
if(getBufferSourceByteLength(V)!==string.length)returnfalse;
281-
282-
constbytes=getBufferSourceBytes(V);
283-
constlength=TypedArrayPrototypeGetLength(bytes);
284-
for(leti=0;i<length;i++){
285-
if(bytes[i]!==StringPrototypeCharCodeAt(string,i))returnfalse;
286-
}
287-
returntrue;
288-
}
280+
constkCShakeFunctionNames=['KMAC','TupleHash','ParallelHash'];
289281

290282
functionvalidateCShakeFunctionName(V){
291-
if(getBufferSourceByteLength(V)===0||
292-
bufferSourceEqualsAscii(V,'KMAC')||
293-
bufferSourceEqualsAscii(V,'TupleHash')||
294-
bufferSourceEqualsAscii(V,'ParallelHash')){
295-
return;
283+
constlength=getBufferSourceByteLength(V);
284+
if(length===0)return;
285+
286+
if(!isFips){
287+
constbytes=getBufferSourceBytes(V);
288+
for(leti=0;i<kCShakeFunctionNames.length;i++){
289+
constfunctionName=kCShakeFunctionNames[i];
290+
if(length!==functionName.length)continue;
291+
292+
letj=0;
293+
for(;j<length;j++){
294+
if(bytes[j]!==StringPrototypeCharCodeAt(functionName,j))break;
295+
}
296+
if(j===length)return;
297+
}
296298
}
297299

298300
throwlazyDOMException(
299301
'Unsupported CShakeParams functionName',
300302
'NotSupportedError');
301303
}
302304

305+
functionvalidateCShakeCustomization(V){
306+
if(isFips&&getBufferSourceByteLength(V)!==0)
307+
throwlazyDOMException(
308+
'Unsupported CShakeParams customization',
309+
'NotSupportedError');
310+
validateMaxBufferLength(V,'CShakeParams.customization',512);
311+
}
312+
303313
converters.RsaPssParams=createDictionaryConverter(
304314
'RsaPssParams',[
305315
dictAlgorithm,
@@ -457,7 +467,7 @@ converters.CShakeParams = createDictionaryConverter(
457467
{
458468
key: 'customization',
459469
converter: converters.BufferSource,
460-
validator: (V,opts)=>validateMaxBufferLength(V,'CShakeParams.customization',512),
470+
validator: validateCShakeCustomization,
461471
},
462472
],
463473
]);
@@ -743,6 +753,7 @@ for (let i = 0; i < kKmacDictionaries.length; i++) {
743753
key: 'length',
744754
converter: (V,opts)=>
745755
converters['unsigned long'](V,enforceRangeOptions(opts)),
756+
validator: validateKmacKeyLength,
746757
},
747758
],
748759
]);
@@ -756,6 +767,12 @@ converters.KmacParams = createDictionaryConverter(
756767
key: 'outputLength',
757768
converter: (V,opts)=>
758769
converters['unsigned long'](V,enforceRangeOptions(opts)),
770+
validator: (V)=>{
771+
if((V===0||V%8)&&isFips)
772+
throwlazyDOMException(
773+
'Invalid KmacParams outputLength',
774+
'NotSupportedError');
775+
},
759776
required: true,
760777
},
761778
{

‎src/crypto/crypto_hash.cc‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,11 @@ Maybe<void> CShakeTraits::AdditionalConfig(
812812
CShakeConfig* params) {
813813
Environment* env = Environment::GetCurrent(args);
814814

815+
if (IsFipsEnabled()) {
816+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
817+
return Nothing<void>();
818+
}
819+
815820
CHECK(args[offset]->IsString()); // Algorithm name
816821
Utf8Value algorithm_name(env->isolate(), args[offset]);
817822
std::string_view algorithm_str = algorithm_name.ToStringView();

‎src/crypto/crypto_kmac.cc‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ bool DeriveBitsWithCShake(const KmacConfig& params,
151151
constvoid* key_data,
152152
size_t key_size,
153153
ByteSource* out) {
154+
if (IsFipsEnabled()) returnfalse;
155+
154156
constsize_t key_length_bytes = NumBitsToBytes(params.key_length);
155157
if (key_size < key_length_bytes) returnfalse;
156158

‎src/crypto/crypto_turboshake.cc‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,11 @@ Maybe<void> TurboShakeTraits::AdditionalConfig(
428428
TurboShakeConfig* params) {
429429
Environment* env = Environment::GetCurrent(args);
430430

431+
if (IsFipsEnabled()) {
432+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
433+
return Nothing<void>();
434+
}
435+
431436
// args[offset + 0] = algorithm name (string)
432437
CHECK(args[offset]->IsString());
433438
Utf8Value algorithm_name(env->isolate(), args[offset]);
@@ -535,6 +540,11 @@ Maybe<void> KangarooTwelveTraits::AdditionalConfig(
535540
KangarooTwelveConfig* params) {
536541
Environment* env = Environment::GetCurrent(args);
537542

543+
if (IsFipsEnabled()) {
544+
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
545+
return Nothing<void>();
546+
}
547+
538548
// args[offset + 0] = algorithm name (string)
539549
CHECK(args[offset]->IsString());
540550
Utf8Value algorithm_name(env->isolate(), args[offset]);

‎src/crypto/crypto_util.cc‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ bool InitCryptoOnce(Isolate* isolate) {
146146
// be part of a larger mutex for global OpenSSL state.
147147
static Mutex fips_mutex;
148148

149+
boolIsFipsEnabled() {
150+
Mutex::ScopedLock fips_lock(fips_mutex);
151+
returnncrypto::isFipsEnabled();
152+
}
153+
149154
voidInitCryptoOnce() {
150155
Mutex::ScopedLock lock(per_process::cli_options_mutex);
151156
Mutex::ScopedLock fips_lock(fips_mutex);
@@ -223,8 +228,7 @@ void InitCryptoOnce() {
223228

224229
voidGetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
225230
Mutex::ScopedLock lock(per_process::cli_options_mutex);
226-
Mutex::ScopedLock fips_lock(fips_mutex);
227-
args.GetReturnValue().Set(ncrypto::isFipsEnabled() ? 1 : 0);
231+
args.GetReturnValue().Set(IsFipsEnabled() ? 1 : 0);
228232
}
229233

230234
voidSetFipsCrypto(const FunctionCallbackInfo<Value>& args) {

‎src/crypto/crypto_util.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ constexpr T NumBitsToBytes(T bits) {
6666
// what went wrong, or std::nullopt when there was nothing to do or the
6767
// options were applied successfully.
6868
std::optional<std::string> ProcessFipsOptions();
69+
boolIsFipsEnabled();
6970

7071
boolInitCryptoOnce(v8::Isolate* isolate);
7172
voidInitCryptoOnce();

‎test/parallel/test-crypto-key-objects-to-crypto-key.js‎

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
}=require('crypto');
1515
const{ hasFIPS }=require('../common/crypto');
1616
const{ kSupportedAlgorithms }=require('internal/crypto/util');
17+
constfips=hasFIPS();
1718
constrejectsXCurves=hasFIPS(3,5);
1819

1920
consthashes=Object.keys(kSupportedAlgorithms.digest).filter((name)=>{
@@ -135,14 +136,24 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
135136
constkey=createSecretKey(randomBytes(32));
136137
constusages=['sign','verify'];
137138

138-
if(allowZeroKey){
139+
if(allowZeroKey&&!fips){
139140
constzeroKey=createSecretKey(Buffer.alloc(0))
140141
.toCryptoKey(algorithm,true,usages);
141142
assert.strictEqual(zeroKey.algorithm.length,0);
142143

143144
constexplicitZeroKey=createSecretKey(Buffer.alloc(0))
144145
.toCryptoKey({ ...algorithm,length: 0},true,usages);
145146
assert.strictEqual(explicitZeroKey.algorithm.length,0);
147+
}elseif(allowZeroKey){
148+
for(constzeroAlgorithmof[algorithm,{ ...algorithm,length: 0}]){
149+
assert.throws(()=>{
150+
createSecretKey(Buffer.alloc(0))
151+
.toCryptoKey(zeroAlgorithm,true,usages);
152+
},{
153+
name: 'NotSupportedError',
154+
message: 'Invalid key length',
155+
});
156+
}
146157
}else{
147158
assert.throws(()=>{
148159
createSecretKey(Buffer.alloc(0)).toCryptoKey(algorithm,true,usages);
@@ -157,12 +168,15 @@ function macInvalid(algorithm, invalidLengthMessage, allowZeroKey = false) {
157168
message: 'Usages cannot be empty when importing a secret key.'
158169
});
159170

160-
assert.throws(()=>{
161-
key.toCryptoKey({ ...algorithm,length: 0},true,usages);
162-
},{
163-
name: 'DataError',
164-
message: invalidLengthMessage,
165-
});
171+
assert.throws(
172+
()=>key.toCryptoKey({ ...algorithm,length: 0},true,usages),
173+
allowZeroKey&&fips ? {
174+
name: 'NotSupportedError',
175+
message: 'Invalid key length',
176+
} : {
177+
name: 'DataError',
178+
message: invalidLengthMessage,
179+
});
166180
}
167181

168182
functionhmacVectors(){

‎test/parallel/test-webcrypto-derivekey.js‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ const fips4 = hasFIPS(4);
283283
})().then(common.mustCall());
284284
}
285285

286-
if(hasOpenSSL(3)){
286+
if(hasOpenSSL(3)&&!hasFIPS()){
287287
(async()=>{
288288
constderivedKeyAlgorithm={name: 'KMAC128',length: 0};
289289
constusages=['sign'];
@@ -325,11 +325,7 @@ if (hasOpenSSL(3)) {
325325
name: 'KMAC128',
326326
outputLength: 256,
327327
},derived,newUint8Array());
328-
if(fips4){
329-
awaitassert.rejects(signature,{name: 'OperationError'});
330-
}else{
331-
assert.strictEqual((awaitsignature).byteLength,32);
332-
}
328+
assert.strictEqual((awaitsignature).byteLength,32);
333329
}
334330
})().then(common.mustCall());
335331
}

0 commit comments

Comments
 (0)