|
45 | 45 | import static com.oracle.graal.python.nodes.BuiltinNames.J_SHA3;
|
46 | 46 | import static com.oracle.graal.python.nodes.BuiltinNames.T_HASHLIB;
|
47 | 47 | import static com.oracle.graal.python.nodes.BuiltinNames.T_SHA3;
|
| 48 | +import static com.oracle.graal.python.nodes.ErrorMessages.ITERATION_VALUE_IS_TOO_GREAT; |
| 49 | +import static com.oracle.graal.python.nodes.ErrorMessages.ITERATION_VALUE_MUST_BE_GREATER_THAN_ZERO; |
| 50 | +import static com.oracle.graal.python.nodes.ErrorMessages.KEY_LENGTH_MUST_BE_GREATER_THAN_ZERO; |
| 51 | +import static com.oracle.graal.python.nodes.ErrorMessages.UNSUPPORTED_HASH_TYPE; |
48 | 52 | import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
|
49 | 53 | import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached;
|
50 | 54 | import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
|
|
60 | 64 | import javax.crypto.Mac;
|
61 | 65 | import javax.crypto.spec.SecretKeySpec;
|
62 | 66 |
|
| 67 | +import org.bouncycastle.crypto.CipherParameters; |
| 68 | +import org.bouncycastle.crypto.Digest; |
| 69 | +import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; |
| 70 | +import org.bouncycastle.crypto.params.KeyParameter; |
| 71 | +import org.bouncycastle.jcajce.provider.util.DigestFactory; |
| 72 | + |
63 | 73 | import com.oracle.graal.python.PythonLanguage;
|
64 | 74 | import com.oracle.graal.python.annotations.ArgumentClinic;
|
65 | 75 | import com.oracle.graal.python.builtins.Builtin;
|
|
68 | 78 | import com.oracle.graal.python.builtins.PythonBuiltinClassType;
|
69 | 79 | import com.oracle.graal.python.builtins.PythonBuiltins;
|
70 | 80 | import com.oracle.graal.python.builtins.modules.hashlib.HashlibModuleBuiltinsClinicProviders.NewNodeClinicProviderGen;
|
| 81 | +import com.oracle.graal.python.builtins.modules.hashlib.HashlibModuleBuiltinsClinicProviders.Pbkdf2HmacNodeClinicProviderGen; |
71 | 82 | import com.oracle.graal.python.builtins.objects.PNone;
|
72 | 83 | import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary;
|
73 | 84 | import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAcquireLibrary;
|
74 | 85 | import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage;
|
75 | 86 | import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes;
|
76 | 87 | import com.oracle.graal.python.builtins.objects.module.PythonModule;
|
77 | 88 | import com.oracle.graal.python.builtins.objects.ssl.CertUtils;
|
| 89 | +import com.oracle.graal.python.lib.PyLongAsLongNode; |
78 | 90 | import com.oracle.graal.python.nodes.ErrorMessages;
|
| 91 | +import com.oracle.graal.python.nodes.PGuards; |
79 | 92 | import com.oracle.graal.python.nodes.PRaiseNode;
|
80 | 93 | import com.oracle.graal.python.nodes.attributes.ReadAttributeFromPythonObjectNode;
|
81 | 94 | import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
|
|
89 | 102 | import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
|
90 | 103 | import com.oracle.graal.python.runtime.IndirectCallData;
|
91 | 104 | import com.oracle.graal.python.runtime.object.PFactory;
|
| 105 | +import com.oracle.truffle.api.CompilerDirectives; |
92 | 106 | import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
93 | 107 | import com.oracle.truffle.api.dsl.Bind;
|
94 | 108 | import com.oracle.truffle.api.dsl.Cached;
|
|
103 | 117 | import com.oracle.truffle.api.frame.VirtualFrame;
|
104 | 118 | import com.oracle.truffle.api.library.CachedLibrary;
|
105 | 119 | import com.oracle.truffle.api.nodes.Node;
|
| 120 | +import com.oracle.truffle.api.profiles.InlinedConditionProfile; |
106 | 121 | import com.oracle.truffle.api.strings.TruffleString;
|
107 | 122 | import com.oracle.truffle.api.strings.TruffleString.CodeRange;
|
108 | 123 |
|
@@ -451,4 +466,77 @@ static int getFips() {
|
451 | 466 | return 0;
|
452 | 467 | }
|
453 | 468 | }
|
| 469 | + |
| 470 | + @Builtin(name = "pbkdf2_hmac", minNumOfPositionalArgs = 4, parameterNames = {"hash_name", "password", "salt", "iterations", "dklen"}) |
| 471 | + @GenerateNodeFactory |
| 472 | + @ArgumentClinic(name = "hash_name", conversion = ArgumentClinic.ClinicConversion.TString) |
| 473 | + @ArgumentClinic(name = "password", conversion = ArgumentClinic.ClinicConversion.ReadableBuffer) |
| 474 | + @ArgumentClinic(name = "salt", conversion = ArgumentClinic.ClinicConversion.ReadableBuffer) |
| 475 | + @ArgumentClinic(name = "iterations", conversion = ArgumentClinic.ClinicConversion.Long) |
| 476 | + abstract static class Pbkdf2HmacNode extends PythonClinicBuiltinNode { |
| 477 | + @Override |
| 478 | + protected ArgumentClinicProvider getArgumentClinic() { |
| 479 | + return Pbkdf2HmacNodeClinicProviderGen.INSTANCE; |
| 480 | + } |
| 481 | + |
| 482 | + @Specialization(limit = "3") |
| 483 | + static Object pbkdf2(VirtualFrame frame, TruffleString hashName, Object password, Object salt, long iterations, Object dklenObj, |
| 484 | + @Bind("this") Node inliningTarget, |
| 485 | + @Bind PythonLanguage language, |
| 486 | + @CachedLibrary("password") PythonBufferAccessLibrary passwordLib, |
| 487 | + @CachedLibrary("salt") PythonBufferAccessLibrary saltLib, |
| 488 | + @Cached PyLongAsLongNode asLongNode, |
| 489 | + @Cached InlinedConditionProfile noDklenProfile, |
| 490 | + @Cached TruffleString.ToJavaStringNode toJavaStringNode, |
| 491 | + @Cached PRaiseNode raiseNode) { |
| 492 | + try { |
| 493 | + Digest digest = getDigest(toJavaStringNode.execute(hashName)); |
| 494 | + if (digest == null) { |
| 495 | + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.UnsupportedDigestmodError, UNSUPPORTED_HASH_TYPE, hashName); |
| 496 | + } |
| 497 | + if (iterations < 1) { |
| 498 | + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.ValueError, ITERATION_VALUE_MUST_BE_GREATER_THAN_ZERO); |
| 499 | + } |
| 500 | + if (iterations > Integer.MAX_VALUE) { |
| 501 | + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.OverflowError, ITERATION_VALUE_IS_TOO_GREAT); |
| 502 | + } |
| 503 | + long dklen; |
| 504 | + if (noDklenProfile.profile(inliningTarget, PGuards.isPNone(dklenObj))) { |
| 505 | + dklen = digest.getDigestSize(); |
| 506 | + } else { |
| 507 | + dklen = asLongNode.execute(frame, inliningTarget, dklenObj); |
| 508 | + } |
| 509 | + dklen *= Byte.SIZE; |
| 510 | + if (dklen < 1) { |
| 511 | + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.ValueError, KEY_LENGTH_MUST_BE_GREATER_THAN_ZERO); |
| 512 | + } |
| 513 | + if (dklen > Integer.MAX_VALUE) { |
| 514 | + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.OverflowError, ITERATION_VALUE_IS_TOO_GREAT); |
| 515 | + } |
| 516 | + byte[] passwordBytes = passwordLib.getInternalOrCopiedExactByteArray(password); |
| 517 | + byte[] saltBytes = saltLib.getInternalOrCopiedExactByteArray(salt); |
| 518 | + return PFactory.createBytes(language, generate(digest, passwordBytes, saltBytes, (int) iterations, (int) dklen)); |
| 519 | + } finally { |
| 520 | + passwordLib.release(password); |
| 521 | + saltLib.release(salt); |
| 522 | + } |
| 523 | + } |
| 524 | + |
| 525 | + @TruffleBoundary |
| 526 | + private static Digest getDigest(String name) { |
| 527 | + name = name.toLowerCase(); |
| 528 | + return DigestFactory.getDigest(NAME_MAPPINGS.getOrDefault(name, name)); |
| 529 | + } |
| 530 | + |
| 531 | + @TruffleBoundary |
| 532 | + private static byte[] generate(Digest digest, byte[] password, byte[] salt, int iterations, int dklen) { |
| 533 | + PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator(digest); |
| 534 | + generator.init(password, salt, iterations); |
| 535 | + CipherParameters cipherParameters = generator.generateDerivedParameters(dklen); |
| 536 | + if (!(cipherParameters instanceof KeyParameter keyParameter)) { |
| 537 | + throw CompilerDirectives.shouldNotReachHere("unexpected cipher parameters"); |
| 538 | + } |
| 539 | + return keyParameter.getKey(); |
| 540 | + } |
| 541 | + } |
454 | 542 | }
|
0 commit comments