From 550c49d9c101a7d22a39d6f96c9c6dc37672d734 Mon Sep 17 00:00:00 2001 From: Vivek Agrawal Date: Sun, 16 Jun 2019 01:34:10 +0530 Subject: [PATCH 1/8] feat: RSA cryptosystem signature project --- rsa-cryptosystem-signature/index.html | 51 +++++++++++++ rsa-cryptosystem-signature/index.js | 105 ++++++++++++++++++++++++++ rsa-cryptosystem-signature/style.css | 18 +++++ 3 files changed, 174 insertions(+) create mode 100644 rsa-cryptosystem-signature/index.html create mode 100644 rsa-cryptosystem-signature/index.js create mode 100644 rsa-cryptosystem-signature/style.css diff --git a/rsa-cryptosystem-signature/index.html b/rsa-cryptosystem-signature/index.html new file mode 100644 index 00000000..27f138d8 --- /dev/null +++ b/rsa-cryptosystem-signature/index.html @@ -0,0 +1,51 @@ + + + + + + freeCodeCamp Cryptography Lesson - Digital Signature System + + + +
+
+ Alice's workspace + +
+ +

Signature is none

+ + +
+
+ +
+
+ Bob's workspace + +
+ +

+ Signature of received document is + none +

+

+ +
+
+ + + + diff --git a/rsa-cryptosystem-signature/index.js b/rsa-cryptosystem-signature/index.js new file mode 100644 index 00000000..5b0803ca --- /dev/null +++ b/rsa-cryptosystem-signature/index.js @@ -0,0 +1,105 @@ +"use strict"; + +const signBtn = document.querySelector("#sign-btn"); +const transmitBtn = document.querySelector("#transmit-btn"); +const verifyBtn = document.querySelector("#verify-btn"); +const messageBox = document.querySelector("#message-to-be-sent"); +const receivedMessageBox = document.querySelector("#received-message"); +const generatedSignature = document.querySelector("#generated-signature"); +const receivedSignature = document.querySelector("#received-signature"); +const verificationStatus = document.querySelector("#verification-status"); + +const firstPrime = 3; +const secondPrime = 7; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue % N === 0 ? 1 : hashValue % N; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let i = 2; i < phiOfN; ++i) { + if (isCoPrime(i, N) && isCoPrime(i, phiOfN)) { + return i; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + publicKey = 1; + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); + publicKey = 0; +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +signBtn.addEventListener("click", function() { + let hashValue = hashTheMessage(messageBox.value); + let privateKey = generatePrivateKey(); + generatePublicKey(privateKey); + + generatedSignature.innerText = generateSignature( + hashValue, + privateKey + ).toString(); + + transmitBtn.disabled = false; +}); + +messageBox.addEventListener("input", function() { + generatedSignature.innerText = "none"; + transmitBtn.disabled = true; +}); + +transmitBtn.addEventListener("click", function() { + receivedMessageBox.value = messageBox.value; + receivedSignature.innerText = generatedSignature.textContent; + verificationStatus.innerText = ""; + verifyBtn.disabled = false; +}); + +verifyBtn.addEventListener("click", function() { + let hashValue = hashTheMessage(receivedMessageBox.value); + let decryptedSignature = decryptSignature( + parseInt(receivedSignature.textContent) + ); + + if (hashValue === decryptedSignature) { + verificationStatus.innerText = + "Success! Signature is verified and Data is intact."; + } else { + verificationStatus.innerText = + "Failure! Looks like data has been modified."; + } +}); diff --git a/rsa-cryptosystem-signature/style.css b/rsa-cryptosystem-signature/style.css new file mode 100644 index 00000000..933bcfc6 --- /dev/null +++ b/rsa-cryptosystem-signature/style.css @@ -0,0 +1,18 @@ +body { + font-family: sans-serif; + color: #444444; + font-size: 18px; +} + +form { + display: inline-block; + margin: 24px; +} + +fieldset { + padding: 32px; +} + +legend { + font-size: 24px; +} From 2ec6089b6ec8ac7fec1a932df25923956914a179 Mon Sep 17 00:00:00 2001 From: Vivek Agrawal Date: Mon, 17 Jun 2019 16:10:00 +0530 Subject: [PATCH 2/8] fix: Large primes and message text - Fixes issue with large primes - Default message revised to fix minor typos - Revised success and failure messages --- rsa-cryptosystem-signature/index.html | 19 +++++++++++++------ rsa-cryptosystem-signature/index.js | 12 +++++++----- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/rsa-cryptosystem-signature/index.html b/rsa-cryptosystem-signature/index.html index 27f138d8..831ae3b4 100644 --- a/rsa-cryptosystem-signature/index.html +++ b/rsa-cryptosystem-signature/index.html @@ -12,12 +12,19 @@ Alice's workspace
-

Signature is none

- - - - -
-
- Bob's workspace - -
- -

- Signature of received document is - none -

-

- -
-
- diff --git a/rsa-cryptosystem-signature/index.js b/rsa-cryptosystem-signature/index.js index 0b06a6ee..920c1f0c 100644 --- a/rsa-cryptosystem-signature/index.js +++ b/rsa-cryptosystem-signature/index.js @@ -1,12 +1,3 @@ -const signBtn = document.querySelector("#sign-btn"); -const transmitBtn = document.querySelector("#transmit-btn"); -const verifyBtn = document.querySelector("#verify-btn"); -const messageBox = document.querySelector("#message-to-be-sent"); -const receivedMessageBox = document.querySelector("#received-message"); -const generatedSignature = document.querySelector("#generated-signature"); -const receivedSignature = document.querySelector("#received-signature"); -const verificationStatus = document.querySelector("#verification-status"); - const firstPrime = 2; const secondPrime = 5; const N = firstPrime * secondPrime; @@ -18,7 +9,7 @@ function hashTheMessage(message) { for (let i = 0, msgLength = message.length; i < msgLength; ++i) { hashValue += message.charCodeAt(i); } - return hashValue % N === 0 ? 1 : hashValue % N; + return hashValue % N; } function isCoPrime(smallerNum, largerNum) { @@ -57,47 +48,25 @@ function generateSignature(hashValue, privateKey) { } function decryptSignature(digitalSignature) { - return Number( - BigInt(BigInt(digitalSignature) ** BigInt(publicKey)) % BigInt(N) - ); + return Math.pow(digitalSignature, publicKey) % N; } -signBtn.addEventListener("click", function() { - let hashValue = hashTheMessage(messageBox.value); - let privateKey = generatePrivateKey(); +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); generatePublicKey(privateKey); + const hashValue = hashTheMessage(message); + const generatedSignature = generateSignature(hashValue, privateKey); + sendAndVerify(generatedSignature, message); +} - generatedSignature.innerText = generateSignature( - hashValue, - privateKey - ).toString(); - - transmitBtn.disabled = false; -}); - -messageBox.addEventListener("input", function() { - generatedSignature.innerText = "none"; - transmitBtn.disabled = true; -}); - -transmitBtn.addEventListener("click", function() { - receivedMessageBox.value = messageBox.value; - receivedSignature.innerText = generatedSignature.textContent; - verificationStatus.innerText = ""; - verifyBtn.disabled = false; -}); - -verifyBtn.addEventListener("click", function() { - let hashValue = hashTheMessage(receivedMessageBox.value); - let decryptedSignature = decryptSignature( - parseInt(receivedSignature.textContent) - ); - +function sendAndVerify(digitalSignature, message) { + const hashValue = hashTheMessage(message); + const decryptedSignature = decryptSignature(digitalSignature); if (hashValue === decryptedSignature) { - verificationStatus.innerText = - "Success! Signature is verified and data is intact."; + console.log("Success! Data is intact and signature is verified."); } else { - verificationStatus.innerText = - "Failure! Something is wrong with data or signature."; + console.log("Failure! There's something wrong with data or signature."); } -}); +} + +sendMsgToBob("Hey Bob, I'm Alice here. Bob, Buy 300 shares of TSLA!"); diff --git a/rsa-cryptosystem-signature/index01.js b/rsa-cryptosystem-signature/index01.js index b202beac..ed583935 100644 --- a/rsa-cryptosystem-signature/index01.js +++ b/rsa-cryptosystem-signature/index01.js @@ -5,5 +5,5 @@ Digital signature is a way to verify the authenticity and integrity of documents First, we hash our message, which is a method to transform a document into a fixed-size value. -Create a hash function `hashTheMessage()` with `message` as a parameter. +Create a function `hashTheMessage()` with `message` as a parameter. Functions which hash the data are called hash functions. */ diff --git a/rsa-cryptosystem-signature/index02.js b/rsa-cryptosystem-signature/index02.js index afc92f5e..86303ede 100644 --- a/rsa-cryptosystem-signature/index02.js +++ b/rsa-cryptosystem-signature/index02.js @@ -1,7 +1,6 @@ -function hashTheMessage(message) {} - -/* +/* The fixed-size value is also known as a hash value. - Create a variable `hashValue` and set it to 0. */ + +function hashTheMessage(message) {} diff --git a/rsa-cryptosystem-signature/index03.js b/rsa-cryptosystem-signature/index03.js index be196c1d..9e2958ba 100644 --- a/rsa-cryptosystem-signature/index03.js +++ b/rsa-cryptosystem-signature/index03.js @@ -1,9 +1,9 @@ -function hashTheMessage(message) { - let hashValue = 0; -} - /* Hash functions ideally use all of the data for hashing. Create a `for` loop to iterate over all the characters of `message`. */ + +function hashTheMessage(message) { + let hashValue = 0; +} diff --git a/rsa-cryptosystem-signature/index04.js b/rsa-cryptosystem-signature/index04.js index 42021352..087e19f7 100644 --- a/rsa-cryptosystem-signature/index04.js +++ b/rsa-cryptosystem-signature/index04.js @@ -1,13 +1,11 @@ +/* +The hash value should not let users know the original data. So, we will represent our message as an integer. + +Add the ASCII value of characters to `hashValue`. +Use `charCodeAt(index)` to find the ASCII value of each character in `message`. +*/ + function hashTheMessage(message) { let hashValue = 0; for (let i = 0, msgLength = message.length; i < msgLength; i++) {} } - -/* -The hash value should not let users know the original data. - -So, we will represent our message as an integer. - -Add the ASCII value of characters to `hashValue`. -Use `charCodeAt(index)` to find the ASCII value of each character in `message`. -*/ diff --git a/rsa-cryptosystem-signature/index05.js b/rsa-cryptosystem-signature/index05.js index c3d26eec..d202afc0 100644 --- a/rsa-cryptosystem-signature/index05.js +++ b/rsa-cryptosystem-signature/index05.js @@ -1,12 +1,11 @@ +/* +Hash functions should always return the same hash value when same message is passed. +Return `hashValue`. +*/ + function hashTheMessage(message) { let hashValue = 0; for (let i = 0, msgLength = message.length; i < msgLength; i++) { hashValue += message.charCodeAt(i); } } - -/* -Hash functions should always return the same hash value when same message is passed. - -Return `hashValue`. -*/ diff --git a/rsa-cryptosystem-signature/index08.js b/rsa-cryptosystem-signature/index08.js index 5076dfb5..2c47d3e2 100644 --- a/rsa-cryptosystem-signature/index08.js +++ b/rsa-cryptosystem-signature/index08.js @@ -1,3 +1,9 @@ +/* +If `publicKey` is used for encryption then only the `privateKey` can decrypt it. Keep in mind that the vice-versa is also true. Therefore, they are referred to as key pairs. + +Create a variable `publicKey` and set it to 0. +*/ + function hashTheMessage(message) { let hashValue = 0; for (let i = 0, msgLength = message.length; i < msgLength; ++i) { @@ -9,9 +15,3 @@ function hashTheMessage(message) { function generatePrivateKey() {} function generatePublicKey() {} - -/* -If `publicKey` is used for encryption then only the `privateKey` can decrypt and vice-versa. Therefore, they are referred to as key pairs. - -Create a variable `publicKey` and set it to 0. -*/ diff --git a/rsa-cryptosystem-signature/index11.js b/rsa-cryptosystem-signature/index11.js index ef1d6e9b..98c1c529 100644 --- a/rsa-cryptosystem-signature/index11.js +++ b/rsa-cryptosystem-signature/index11.js @@ -1,5 +1,13 @@ let publicKey = 0; +/* +We'll use the RSA asymmetric cryptographic algorithm to generate key pairs, to encrypt and decrypt data. + +RSA is based on the fact that finding prime factors of a large number is difficult. + +Create a constant `firstPrime` and set it to 2. +*/ + function hashTheMessage(message) { let hashValue = 0; for (let i = 0, msgLength = message.length; i < msgLength; ++i) { @@ -15,11 +23,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -We'll use the RSA asymmetric cryptographic algorithm to generate key pairs, to encrypt and decrypt data. - -RSA is based on the fact that finding prime factors of a large number is difficult. - -Create a constant `firstPrime` and set it to 2. -*/ diff --git a/rsa-cryptosystem-signature/index12.js b/rsa-cryptosystem-signature/index12.js index 607d42e3..6d64a376 100644 --- a/rsa-cryptosystem-signature/index12.js +++ b/rsa-cryptosystem-signature/index12.js @@ -1,6 +1,13 @@ const firstPrime = 2; let publicKey = 0; +/* +Remember, prime numbers are only divisible by 1 and by the number itself. +E.g. 2, 3, 5, 7, 11, etc. + +Create a constant `secondPrime` and set it to 5. +*/ + function hashTheMessage(message) { let hashValue = 0; for (let i = 0, msgLength = message.length; i < msgLength; ++i) { @@ -16,10 +23,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -Remember, prime numbers are only divisible by 1 and by the number itself. -E.g. 2, 3, 5, 7, 11, etc. - -Create a constant `secondPrime` and set it to 5. -*/ diff --git a/rsa-cryptosystem-signature/index13.js b/rsa-cryptosystem-signature/index13.js index 7561b7a2..f76cb42f 100644 --- a/rsa-cryptosystem-signature/index13.js +++ b/rsa-cryptosystem-signature/index13.js @@ -2,6 +2,12 @@ const firstPrime = 2; const secondPrime = 5; let publicKey = 0; +/* +Create a constant `N` and set it as `firstPrime * secondPrime`. `N` will be a part of both private and public key. Here, `N = 10`. So, it's easy to find the prime factors 2 and 5. + +But in real world usage `N` is around 600 digits long which makes prime factorization almost impossible. Our implementation is insecure. It is just to teach you about the fundamentals of asymmetric cryptography. +*/ + function hashTheMessage(message) { let hashValue = 0; for (let i = 0, msgLength = message.length; i < msgLength; ++i) { @@ -17,8 +23,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -Create a constant `N` and set it as `firstPrime * secondPrime`. -`N` will be a part of both private and public key. -*/ diff --git a/rsa-cryptosystem-signature/index14.js b/rsa-cryptosystem-signature/index14.js index 89a26070..33a145d5 100644 --- a/rsa-cryptosystem-signature/index14.js +++ b/rsa-cryptosystem-signature/index14.js @@ -3,6 +3,14 @@ const secondPrime = 5; const N = firstPrime * secondPrime; let publicKey = 0; +/* +Private key must be between 1 and `Φ(N)` i.e. 1 < privateKey < `Φ(N)`. + +`Φ(N)` pronounced as phi of N is called Euler's totient function. It outputs number of integers upto `N` that are coprime with `N`. + +Create a constant `phiOfN` and set it to 0. +*/ + function hashTheMessage(message) { let hashValue = 0; for (let i = 0, msgLength = message.length; i < msgLength; ++i) { @@ -18,11 +26,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -Private key must be between 1 and `Φ(N)` i.e. 1 < privateKey < `Φ(N)`. - -`Φ(N)` pronounced as phi of N is called Euler's totient function. It outputs number of integers upto `N` that are coprime with `N`. - -Create a constant `phiOfN` and set it to 0. -*/ diff --git a/rsa-cryptosystem-signature/index15.js b/rsa-cryptosystem-signature/index15.js index 8b9d71c3..59d6773a 100644 --- a/rsa-cryptosystem-signature/index15.js +++ b/rsa-cryptosystem-signature/index15.js @@ -12,14 +12,6 @@ function hashTheMessage(message) { return hashValue; } -function generatePrivateKey() {} - -function generatePublicKey() {} - -function generateSignature() {} - -function decryptSignature() {} - /* Two integers are coprime if the only positive integer that divides both of them simultaneously is 1. @@ -28,3 +20,11 @@ E.g. 10 and 7 are coprime. Create an empty function `isCoPrime` with two integer parameters. */ + +function generatePrivateKey() {} + +function generatePublicKey() {} + +function generateSignature() {} + +function decryptSignature() {} diff --git a/rsa-cryptosystem-signature/index16.js b/rsa-cryptosystem-signature/index16.js index 3b457901..ec1699f4 100644 --- a/rsa-cryptosystem-signature/index16.js +++ b/rsa-cryptosystem-signature/index16.js @@ -12,6 +12,14 @@ function hashTheMessage(message) { return hashValue; } +/* +To check if two integers are coprime, we start with 2 and check till the smaller of two integers. + +For e.g. to check if 12 and 9 are coprime, we start from 2 and check till 9 because any integer greater than 9 won't divide 9 evenly. + +Create a `for` loop to iterate from 2 to `smallerNum` (including `smallerNum`). +*/ + function isCoPrime(smallerNum, largerNum) {} function generatePrivateKey() {} @@ -21,11 +29,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -To check if two integers are coprime, we start with 2 and check till the smaller of two integers. - -For e.g. to check if 12 and 9 are coprime, we start from 2 and check till 9 because any integer greater than 9 won't divide 9 evenly. - -Create a `for` loop to iterate from 2 to `smallerNum` (including `smallerNum`). -*/ diff --git a/rsa-cryptosystem-signature/index17.js b/rsa-cryptosystem-signature/index17.js index a2b907c0..8d3bef61 100644 --- a/rsa-cryptosystem-signature/index17.js +++ b/rsa-cryptosystem-signature/index17.js @@ -12,6 +12,13 @@ function hashTheMessage(message) { return hashValue; } +/* +If any integer till the `smallerNum` divide both the numbers then they are `not coprime`. + +Use a conditional to check if `i` divides both `smallerNum` and `largerNum` evenly. +Hint: `smallerNum % i === 0` returns true if `smallerNum` is divisble by `i` else false. +*/ + function isCoPrime(smallerNum, largerNum) { for (let i = 2; i <= smallerNum; ++i) {} } @@ -23,10 +30,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -If any integer till the `smallerNum` divide both the numbers then they are not coprime. - -Use a conditional to check if `i` divides both `smallerNum` and `largerNum` evenly. -Hint: `smallerNum % i === 0` returns true if `smallerNum` is divisble by `i` else false. -*/ diff --git a/rsa-cryptosystem-signature/index18.js b/rsa-cryptosystem-signature/index18.js index 085fed4c..46441441 100644 --- a/rsa-cryptosystem-signature/index18.js +++ b/rsa-cryptosystem-signature/index18.js @@ -12,6 +12,12 @@ function hashTheMessage(message) { return hashValue; } +/* +Continuing with our example of 12 and 9. We observe, 3 divide both the numbers evenly. Thus, they are not coprime. + +Return `false` if `i` divides both `smallerNum` and `largerNum` evenly. +*/ + function isCoPrime(smallerNum, largerNum) { for (let i = 2; i <= smallerNum; ++i) { if (smallerNum % i === 0 && largerNum % i === 0) { @@ -26,9 +32,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -Continuing with our example of 12 and 9. We observe, 3 divides both the numbers. Thus, they are not coprime. - -Return `false` if `i` divides both `smallerNum` and `largerNum` evenly. -*/ diff --git a/rsa-cryptosystem-signature/index19.js b/rsa-cryptosystem-signature/index19.js index 0fbb1fa6..bbe5fc16 100644 --- a/rsa-cryptosystem-signature/index19.js +++ b/rsa-cryptosystem-signature/index19.js @@ -18,6 +18,11 @@ function isCoPrime(smallerNum, largerNum) { return false; } } + /* + If we were to check coprimality of 12 and 7, then they both are only divisible by 1. Hence, they are coprime. + + Return `true` if both the numbers are not divisible by any integer from 2 till `smallerNum`. + */ } function generatePrivateKey() {} @@ -27,9 +32,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -If we were to check coprimality of 12 and 7, then they both are only divisible by 1. Hence, they are coprime. - -Return `true` if both the numbers are not divisible by any integer from 2 till `smallerNum`. -*/ diff --git a/rsa-cryptosystem-signature/index20.js b/rsa-cryptosystem-signature/index20.js index 5aec32bb..8463a1c7 100644 --- a/rsa-cryptosystem-signature/index20.js +++ b/rsa-cryptosystem-signature/index20.js @@ -21,6 +21,12 @@ function isCoPrime(smallerNum, largerNum) { return true; } +/* +We know, private key must follow the condition: 1 < privateKey < `Φ(N)`. + +Create a `for` loop in `generatePrivateKey()` function to iterate from 2 to `phiOfN - 1`. +*/ + function generatePrivateKey() {} function generatePublicKey() {} @@ -28,9 +34,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -We know, private key must be: 1 < privateKey < `Φ(N)`. - -Create a `for` loop in `generatePrivateKey()` function to iterate from 2 to `phiOfN - 1`. -*/ diff --git a/rsa-cryptosystem-signature/index21.js b/rsa-cryptosystem-signature/index21.js index c53a4495..43518c2e 100644 --- a/rsa-cryptosystem-signature/index21.js +++ b/rsa-cryptosystem-signature/index21.js @@ -1,6 +1,17 @@ const firstPrime = 2; const secondPrime = 5; const N = firstPrime * secondPrime; + +/* +`Φ(N) or phi of N` outputs number of integers upto `N` that are coprime with `N`. + +Let's find `Φ(10)`: (1, 10) (3, 10) (7, 10) (9, 10) are coprimes with `N` upto `N`. Here `N = 10` +So, coprimes of 10 upto 10 are 1, 3, 7 and 9. Number of coprimes of 10 upto 10 = 4. +Hence, `Φ(10)` = 4. + +Set `phiOfN` to 4. +*/ + const phiOfN = 0; let publicKey = 0; @@ -30,14 +41,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -`Φ(N) or phi of N` outputs number of integers upto `N` that are coprime with `N`. - -Let's find `Φ(10)`: Here `N = 10`. -(1, 10) (3, 10) (7, 10) (9, 10) are coprimes with `N` upto `N`. -So, coprimes of 10 upto 10 are 1, 3, 7 and 9. -Hence, `Φ(10)` = 4. - -Set `phiOfN` to 4. -*/ diff --git a/rsa-cryptosystem-signature/index22.js b/rsa-cryptosystem-signature/index22.js index 78eb2426..c1c735ab 100644 --- a/rsa-cryptosystem-signature/index22.js +++ b/rsa-cryptosystem-signature/index22.js @@ -1,6 +1,16 @@ const firstPrime = 2; const secondPrime = 5; const N = firstPrime * secondPrime; + +/* +Let's find `Φ(7)`: Coprimes with 7 upto 7 will be: 1, 2, 3, 4, 5 and 6. Hence, `Φ(7)` = 6. + +In general, `Φ(prime) = prime - 1`. +You should try finding phi of a random prime to check whether the above formula is true or not. + +Set `phiOfN` to `firstPrime - 1`. +*/ + const phiOfN = 4; let publicKey = 0; @@ -30,12 +40,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -Let's find `Φ(7)`: Coprimes with 7 upto 7 will be: 1, 2, 3, 4, 5 and 6. Hence, `Φ(7)` = 6. - -In general, `Φ(prime) = prime - 1`. -To verify, you should find Φ(11) or phi of any prime. - -Set `phiOfN` to `firstPrime - 1`. -*/ diff --git a/rsa-cryptosystem-signature/index23.js b/rsa-cryptosystem-signature/index23.js index 75ff2482..15690eda 100644 --- a/rsa-cryptosystem-signature/index23.js +++ b/rsa-cryptosystem-signature/index23.js @@ -1,6 +1,18 @@ const firstPrime = 2; const secondPrime = 5; const N = firstPrime * secondPrime; + +/* +`Φ` function is multiplicative which means Φ(A * B) = Φ(A) * Φ(B). + +We can say, Φ(firstPrime * secondPrime) = Φ(firstPrime) * Φ(secondPrime). +We know, `N = firstPrime * secondPrime`. So, Φ(N) = Φ(firstPrime) * Φ(secondPrime). +We know, Φ(firstPrime) = firstPrime - 1 and Φ(secondPrime) = secondPrime - 1. +Substituting above values we get, Φ(N) = (firstPrime - 1) * (secondPrime - 1) 🎉. + +Set `phiOfN` to `(firstPrime - 1) * (secondPrime - 1)`. +*/ + const phiOfN = firstPrime - 1; let publicKey = 0; @@ -30,14 +42,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -`Φ` function is multiplicative which means Φ(A * B) = Φ(A) * Φ(B). - -We can say, Φ(firstPrime * secondPrime) = Φ(firstPrime) * Φ(secondPrime). -We know, `N = firstPrime * secondPrime`. So, Φ(N) = Φ(firstPrime) * Φ(secondPrime). -We know, Φ(firstPrime) = firstPrime - 1 and Φ(secondPrime) = secondPrime - 1. -Substituting above values we get, Φ(N) = (firstPrime - 1) * (secondPrime - 1) 🎉. - -Set `phiOfN` to `(firstPrime - 1) * (secondPrime - 1)`. -*/ diff --git a/rsa-cryptosystem-signature/index24.js b/rsa-cryptosystem-signature/index24.js index 605bd49c..b6b674bd 100644 --- a/rsa-cryptosystem-signature/index24.js +++ b/rsa-cryptosystem-signature/index24.js @@ -21,6 +21,14 @@ function isCoPrime(smallerNum, largerNum) { return true; } +/* +Private key has two constraints: +- It must be greater than 1 and less than Φ(N). +- It must be coprime with N and Φ(N). + +Implement a conditional to check coprimality of `privateKey` with `N` and `phiOfN` using `isCoPrime` function. +*/ + function generatePrivateKey() { for (let privateKey = 2; privateKey < phiOfN; ++privateKey) {} } @@ -30,11 +38,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -Private key has two constraints: -- It must be greater than 1 and less than Φ(N). -- It must be coprime with N and Φ(N). - -Implement a conditional to check coprimality of `privateKey` with `N` and `phiOfN` using `isCoPrime` function. -*/ diff --git a/rsa-cryptosystem-signature/index25.js b/rsa-cryptosystem-signature/index25.js index 6d5f3ffa..f034ea6e 100644 --- a/rsa-cryptosystem-signature/index25.js +++ b/rsa-cryptosystem-signature/index25.js @@ -21,6 +21,10 @@ function isCoPrime(smallerNum, largerNum) { return true; } +/* +If `privateKey` is coprime with `N` and `phiOfN` then return `privateKey`. +*/ + function generatePrivateKey() { for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { @@ -33,7 +37,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -If `privateKey` is coprime with `N` and `phiOfN` then return `privateKey`. -*/ diff --git a/rsa-cryptosystem-signature/index26.js b/rsa-cryptosystem-signature/index26.js index 87d3e992..236c7969 100644 --- a/rsa-cryptosystem-signature/index26.js +++ b/rsa-cryptosystem-signature/index26.js @@ -21,6 +21,10 @@ function isCoPrime(smallerNum, largerNum) { return true; } +/* +If the possible values of privateKey is not found to be coprime with `N` and `phiOfN` then log an error message. +*/ + function generatePrivateKey() { for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { @@ -34,7 +38,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -If the possible values of privateKey is not found to be coprime with `N` and `phiOfN` then log an error message. -*/ diff --git a/rsa-cryptosystem-signature/index27.js b/rsa-cryptosystem-signature/index27.js index dfe295e5..10db3429 100644 --- a/rsa-cryptosystem-signature/index27.js +++ b/rsa-cryptosystem-signature/index27.js @@ -21,6 +21,10 @@ function isCoPrime(smallerNum, largerNum) { return true; } +/* +Also, return 0 to indicate failure. +*/ + function generatePrivateKey() { for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { @@ -36,7 +40,3 @@ function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -Also, return 0 to indicate failure. -*/ diff --git a/rsa-cryptosystem-signature/index28.js b/rsa-cryptosystem-signature/index28.js index 5465f1ec..460dd254 100644 --- a/rsa-cryptosystem-signature/index28.js +++ b/rsa-cryptosystem-signature/index28.js @@ -32,14 +32,14 @@ function generatePrivateKey() { return 0; } +/* +Public key is mathematically related to private key. + +Provide a parameter in `generatePublicKey()` function for private key. +*/ + function generatePublicKey() {} function generateSignature() {} function decryptSignature() {} - -/* -Public key is mathematically related to private key. - -Accept `privateKey` as a parameter in `generatePublicKey()` function. -*/ diff --git a/rsa-cryptosystem-signature/index29.js b/rsa-cryptosystem-signature/index29.js index d845f3b8..9a6949e0 100644 --- a/rsa-cryptosystem-signature/index29.js +++ b/rsa-cryptosystem-signature/index29.js @@ -32,15 +32,15 @@ function generatePrivateKey() { return 0; } -function generatePublicKey(privateKey) {} - -function generateSignature() {} - -function decryptSignature() {} - /* If we have successfully generated the private key then there must exist a corresponding public key. Create an infinite while loop in `generatePublicKey` function that iterates when `privateKey` is valid i.e. when `privateKey > 0`. */ + +function generatePublicKey(privateKey) {} + +function generateSignature() {} + +function decryptSignature() {} diff --git a/rsa-cryptosystem-signature/index30.js b/rsa-cryptosystem-signature/index30.js index a476c158..673f5293 100644 --- a/rsa-cryptosystem-signature/index30.js +++ b/rsa-cryptosystem-signature/index30.js @@ -32,6 +32,12 @@ function generatePrivateKey() { return 0; } +/* +Public key must fulfil the condition: `(publicKey * privateKey) mod phiOfN = 1` + +Use a conditional to check for the above mentioned condition inside the loop. +*/ + function generatePublicKey(privateKey) { while (privateKey) {} } @@ -39,9 +45,3 @@ function generatePublicKey(privateKey) { function generateSignature() {} function decryptSignature() {} - -/* -Public key must fulfil the condition: `(publicKey * privateKey) mod phiOfN = 1` - -Use a conditional to check for the above mentioned condition inside the loop. -*/ diff --git a/rsa-cryptosystem-signature/index31.js b/rsa-cryptosystem-signature/index31.js index d69ea6b1..b0f8d020 100644 --- a/rsa-cryptosystem-signature/index31.js +++ b/rsa-cryptosystem-signature/index31.js @@ -32,6 +32,14 @@ function generatePrivateKey() { return 0; } +/* +Public key has two constraints: +1. It must fulfil the condition: `(publicKey * privateKey) mod phiOfN = 1`. +2. It should not be same as private key. + +Use `&&` operator in the conditional to check for the second constraint. +*/ + function generatePublicKey(privateKey) { while (privateKey) { if ((publicKey * privateKey) % phiOfN === 1) { @@ -42,11 +50,3 @@ function generatePublicKey(privateKey) { function generateSignature() {} function decryptSignature() {} - -/* -Public key has two constraints: -- It must fulfil the condition: `(publicKey * privateKey) mod phiOfN = 1`. -- It should not be same as private key. - -Use `&&` operator to check for the second constraint. -*/ diff --git a/rsa-cryptosystem-signature/index32.js b/rsa-cryptosystem-signature/index32.js index 090faa66..6aa2f33c 100644 --- a/rsa-cryptosystem-signature/index32.js +++ b/rsa-cryptosystem-signature/index32.js @@ -32,6 +32,12 @@ function generatePrivateKey() { return 0; } +/* +We need to increment `publicKey` on every iteration to test the conditions. + +Increment `publicKey` on every iteration of loop. +*/ + function generatePublicKey(privateKey) { while (privateKey) { if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { @@ -42,9 +48,3 @@ function generatePublicKey(privateKey) { function generateSignature() {} function decryptSignature() {} - -/* -We need to increment `publicKey` on every iteration to check the conditions. - -Increment `publicKey` on every iteration of loop. -*/ diff --git a/rsa-cryptosystem-signature/index33.js b/rsa-cryptosystem-signature/index33.js index 5ee451dd..87664a51 100644 --- a/rsa-cryptosystem-signature/index33.js +++ b/rsa-cryptosystem-signature/index33.js @@ -32,6 +32,12 @@ function generatePrivateKey() { return 0; } +/* +If both the conditions are true then we can stop the execution of loop as we have found a mathematically related public key. + +If both the conditions are true then terminate the function using `return;`. +*/ + function generatePublicKey(privateKey) { while (privateKey) { if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { @@ -43,9 +49,3 @@ function generatePublicKey(privateKey) { function generateSignature() {} function decryptSignature() {} - -/* -If both the conditions are true then we can stop the execution of loop as we have found the mathematically related public key. - -Stop executing the loop if both conditions are true. -*/ diff --git a/rsa-cryptosystem-signature/index34.js b/rsa-cryptosystem-signature/index34.js index c9be9455..e9344c95 100644 --- a/rsa-cryptosystem-signature/index34.js +++ b/rsa-cryptosystem-signature/index34.js @@ -32,6 +32,12 @@ function generatePrivateKey() { return 0; } +/* +If private key isn't successfully generated i.e. when `privateKey = 0` then there can't be a valid public key. + +Log an error message when `privateKey = 0`. +*/ + function generatePublicKey(privateKey) { while (privateKey) { if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { @@ -44,9 +50,3 @@ function generatePublicKey(privateKey) { function generateSignature() {} function decryptSignature() {} - -/* -If private key isn't successfully generated i.e. when `privateKey = 0` then there can't be a valid public key. - -Log an error message when `privateKey = 0`. -*/ diff --git a/rsa-cryptosystem-signature/index35.js b/rsa-cryptosystem-signature/index35.js index 1c6c84ed..64f6a310 100644 --- a/rsa-cryptosystem-signature/index35.js +++ b/rsa-cryptosystem-signature/index35.js @@ -43,9 +43,12 @@ function generatePublicKey(privateKey) { console.log("Public key can't be generated."); } +/* +Alice encrypts the hash value of data with his private key which we call signature. Thus, to generate signature we need to access hash value of data and Alice's private key. + +Provide parameters for hash value and private key in `generateSignature()` function. +*/ + function generateSignature() {} function decryptSignature() {} - -/* - */ diff --git a/rsa-cryptosystem-signature/index36.js b/rsa-cryptosystem-signature/index36.js new file mode 100644 index 00000000..d343065b --- /dev/null +++ b/rsa-cryptosystem-signature/index36.js @@ -0,0 +1,56 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +/* +To encrypt data using RSA we use a mathematical equation: `encryptedData = (Data ^ privateKey) % N`. + +We need to encrypt `hashValue`. So, our `Data` is `hashValue`. + +Return `(hashValue ^ privateKey) % N` using `Math.pow`. +*/ + +function generateSignature(hashValue, privateKey) {} + +function decryptSignature() {} diff --git a/rsa-cryptosystem-signature/index37.js b/rsa-cryptosystem-signature/index37.js new file mode 100644 index 00000000..a60985b8 --- /dev/null +++ b/rsa-cryptosystem-signature/index37.js @@ -0,0 +1,58 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +/* +Alice attaches the signature to data and sends it to Bob. Bob decrypts the received signature with Alice's public key. + +`publicKey` being a global variable, we have access to it. So, our function only need access to signature to decrypt it. + +Provide a parameter for signature in `decryptSignature()` function. +*/ + +function decryptSignature() {} diff --git a/rsa-cryptosystem-signature/index38.js b/rsa-cryptosystem-signature/index38.js new file mode 100644 index 00000000..adba8b10 --- /dev/null +++ b/rsa-cryptosystem-signature/index38.js @@ -0,0 +1,59 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +/* +To decrypt data using RSA we use a mathematical equation similar to that of encryption: +`Data = (encryptedData ^ publicKey) % N`. + +Here, the encryptedData is `digitalSignature`. + +Return `(digitalSignature ^ publicKey) % N` using `Math.pow`. +*/ + +function decryptSignature(digitalSignature) {} diff --git a/rsa-cryptosystem-signature/index39.js b/rsa-cryptosystem-signature/index39.js new file mode 100644 index 00000000..90f73abb --- /dev/null +++ b/rsa-cryptosystem-signature/index39.js @@ -0,0 +1,58 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +/* +Now, Alice is ready to send his secret message to Bob! + +Create an empty function `sendMsgToBob` with `message` as a parameter. +*/ diff --git a/rsa-cryptosystem-signature/index40.js b/rsa-cryptosystem-signature/index40.js new file mode 100644 index 00000000..796a86e6 --- /dev/null +++ b/rsa-cryptosystem-signature/index40.js @@ -0,0 +1,60 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +/* +Before Alice send his secret message. He needs to generate the key pairs then he hashes the data and encrypts the hash value to generate signature. + +Help Alice generate his private key by using `generatePrivateKey()` function and store the returned value in a constant. +*/ + +function sendMsgToBob(message) {} diff --git a/rsa-cryptosystem-signature/index41.js b/rsa-cryptosystem-signature/index41.js new file mode 100644 index 00000000..88617720 --- /dev/null +++ b/rsa-cryptosystem-signature/index41.js @@ -0,0 +1,60 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +/* +Now, generate public key using `generatePublicKey` function and pass `privateKey` as an argument. +*/ + +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); +} diff --git a/rsa-cryptosystem-signature/index42.js b/rsa-cryptosystem-signature/index42.js new file mode 100644 index 00000000..5a8bc23e --- /dev/null +++ b/rsa-cryptosystem-signature/index42.js @@ -0,0 +1,61 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +/* +Alice keeps private key as a secret and shares the public key with the world. Now, hash the message using the hash function you created and store the returned value in a constant. +*/ + +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); + generatePublicKey(privateKey); +} diff --git a/rsa-cryptosystem-signature/index43.js b/rsa-cryptosystem-signature/index43.js new file mode 100644 index 00000000..25bde977 --- /dev/null +++ b/rsa-cryptosystem-signature/index43.js @@ -0,0 +1,65 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +/* +Hash value should be less than `N` for encryption and decryption to properly work. + +Make sure the function `hashTheMessage` always return hash value less than `N`. +Hint: You can use `%` operator to achieve this. +*/ + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); + generatePublicKey(privateKey); + const hashValue = hashTheMessage(message); +} diff --git a/rsa-cryptosystem-signature/index44.js b/rsa-cryptosystem-signature/index44.js new file mode 100644 index 00000000..20225960 --- /dev/null +++ b/rsa-cryptosystem-signature/index44.js @@ -0,0 +1,64 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue % N; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +/* +We have generated the key pairs and we have the right hash value. Let's generate the signature. + +Generate signature using `generateSignature` function and store the returned value in a constant. +*/ + +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); + generatePublicKey(privateKey); + const hashValue = hashTheMessage(message); +} diff --git a/rsa-cryptosystem-signature/index45.js b/rsa-cryptosystem-signature/index45.js new file mode 100644 index 00000000..f4aff933 --- /dev/null +++ b/rsa-cryptosystem-signature/index45.js @@ -0,0 +1,65 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue % N; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); + generatePublicKey(privateKey); + const hashValue = hashTheMessage(message); + const generatedSignature = generateSignature(hashValue, privateKey); +} + +/* +Alice encrypted the hash value of his message which generated the signature. Now, we can send the message along with signature to Bob. + +Create an empty function `sendAndVerify` and provide parameters for signature and message. +*/ diff --git a/rsa-cryptosystem-signature/index46.js b/rsa-cryptosystem-signature/index46.js new file mode 100644 index 00000000..f4b82248 --- /dev/null +++ b/rsa-cryptosystem-signature/index46.js @@ -0,0 +1,67 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue % N; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); + generatePublicKey(privateKey); + const hashValue = hashTheMessage(message); + const generatedSignature = generateSignature(hashValue, privateKey); +} + +/* +Bob receives the data and signature. To verify the signature he first hashes the data through the same hash function which Alice used. + +Hash the received message using `hashTheMessage` function and store the returned value in a constant. +*/ + +function sendAndVerify(digitalSignature, message) {} diff --git a/rsa-cryptosystem-signature/index47.js b/rsa-cryptosystem-signature/index47.js new file mode 100644 index 00000000..8fc7600b --- /dev/null +++ b/rsa-cryptosystem-signature/index47.js @@ -0,0 +1,71 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue % N; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); + generatePublicKey(privateKey); + const hashValue = hashTheMessage(message); + const generatedSignature = generateSignature(hashValue, privateKey); +} + +/* +If you remember, Alice hashed the message then he encrypted the hash value which we called signature. + +Now, when we decrypt the signature or decrypt the encrypted hash value, we should get the original hash value of data. Makes sense? + +Decrypt the received signature using `decryptSignature` function and store the returned value in a constant. +*/ + +function sendAndVerify(digitalSignature, message) { + const hashValue = hashTheMessage(message); +} diff --git a/rsa-cryptosystem-signature/index48.js b/rsa-cryptosystem-signature/index48.js new file mode 100644 index 00000000..c9ecf240 --- /dev/null +++ b/rsa-cryptosystem-signature/index48.js @@ -0,0 +1,70 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue % N; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); + generatePublicKey(privateKey); + const hashValue = hashTheMessage(message); + const generatedSignature = generateSignature(hashValue, privateKey); +} + +/* +The decrypted signature is the hash value of the original data. If the hash value of recevied data and decrypted signature are equal then the signature is verified and data is intact. + +Check for the equality of `hashValue` and `decryptedSignature`. If they are equal then log a success message. +*/ + +function sendAndVerify(digitalSignature, message) { + const hashValue = hashTheMessage(message); + const decryptedSignature = decryptSignature(digitalSignature); +} diff --git a/rsa-cryptosystem-signature/index49.js b/rsa-cryptosystem-signature/index49.js new file mode 100644 index 00000000..5f58bba2 --- /dev/null +++ b/rsa-cryptosystem-signature/index49.js @@ -0,0 +1,71 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue % N; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); + generatePublicKey(privateKey); + const hashValue = hashTheMessage(message); + const generatedSignature = generateSignature(hashValue, privateKey); +} + +/* +Log a failure message when the `hashValue` and `decryptedSignature` don't match since someone must have modified the data or maybe the signature was wrong. +*/ + +function sendAndVerify(digitalSignature, message) { + const hashValue = hashTheMessage(message); + const decryptedSignature = decryptSignature(digitalSignature); + if (hashValue === decryptedSignature) { + console.log("Success! Data is intact and signature is verified."); + } +} diff --git a/rsa-cryptosystem-signature/index50.js b/rsa-cryptosystem-signature/index50.js new file mode 100644 index 00000000..bbadc025 --- /dev/null +++ b/rsa-cryptosystem-signature/index50.js @@ -0,0 +1,73 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue % N; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +/* +Call `sendAndVerify` function in `sendMsgToBob` and provide the `generatedSignature` and `message` as an argument. +*/ + +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); + generatePublicKey(privateKey); + const hashValue = hashTheMessage(message); + const generatedSignature = generateSignature(hashValue, privateKey); +} + +function sendAndVerify(digitalSignature, message) { + const hashValue = hashTheMessage(message); + const decryptedSignature = decryptSignature(digitalSignature); + if (hashValue === decryptedSignature) { + console.log("Success! Data is intact and signature is verified."); + } else { + console.log("Failure! There's something wrong with data or signature."); + } +} diff --git a/rsa-cryptosystem-signature/index51.js b/rsa-cryptosystem-signature/index51.js new file mode 100644 index 00000000..b96aa84d --- /dev/null +++ b/rsa-cryptosystem-signature/index51.js @@ -0,0 +1,74 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue % N; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); + generatePublicKey(privateKey); + const hashValue = hashTheMessage(message); + const generatedSignature = generateSignature(hashValue, privateKey); + sendAndVerify(generatedSignature, message); +} + +function sendAndVerify(digitalSignature, message) { + const hashValue = hashTheMessage(message); + const decryptedSignature = decryptSignature(digitalSignature); + if (hashValue === decryptedSignature) { + console.log("Success! Data is intact and signature is verified."); + } else { + console.log("Failure! There's something wrong with data or signature."); + } +} + +/* +Call `sendMsgToBob` function and pass a super secret message as an argument. Observe the output of `console.log`. +*/ diff --git a/rsa-cryptosystem-signature/index52.js b/rsa-cryptosystem-signature/index52.js new file mode 100644 index 00000000..244057c6 --- /dev/null +++ b/rsa-cryptosystem-signature/index52.js @@ -0,0 +1,76 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue % N; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); + generatePublicKey(privateKey); + const hashValue = hashTheMessage(message); + const generatedSignature = generateSignature(hashValue, privateKey); + sendAndVerify(generatedSignature, message); +} + +function sendAndVerify(digitalSignature, message) { + const hashValue = hashTheMessage(message); + const decryptedSignature = decryptSignature(digitalSignature); + if (hashValue === decryptedSignature) { + console.log("Success! Data is intact and signature is verified."); + } else { + console.log("Failure! There's something wrong with data or signature."); + } +} + +sendMsgToBob("Hey Bob, I'm Alice here. Bob, Buy 300 shares of TSLA!"); + +/* +Some more tests to go... +*/ diff --git a/rsa-cryptosystem-signature/style.css b/rsa-cryptosystem-signature/style.css deleted file mode 100644 index 933bcfc6..00000000 --- a/rsa-cryptosystem-signature/style.css +++ /dev/null @@ -1,18 +0,0 @@ -body { - font-family: sans-serif; - color: #444444; - font-size: 18px; -} - -form { - display: inline-block; - margin: 24px; -} - -fieldset { - padding: 32px; -} - -legend { - font-size: 24px; -} From ed7175ad6ecdd91ed4ece12fff29fb74037eb052 Mon Sep 17 00:00:00 2001 From: Vivek Agrawal Date: Thu, 18 Jul 2019 21:17:17 +0530 Subject: [PATCH 8/8] fix: Improve and add more descriptions - Added a diagram to explain the signing process - Revised all the test descriptions - Added some descriptions to illustrate hash collision To Do: Explain the internals of encryption and decryption equations --- .../RSA signing process diagram.png | Bin 0 -> 62190 bytes rsa-cryptosystem-signature/index01.js | 5 +- rsa-cryptosystem-signature/index02.js | 15 +-- rsa-cryptosystem-signature/index03.js | 13 +-- rsa-cryptosystem-signature/index04.js | 17 ++-- rsa-cryptosystem-signature/index05.js | 9 +- rsa-cryptosystem-signature/index06.js | 6 +- rsa-cryptosystem-signature/index07.js | 2 +- rsa-cryptosystem-signature/index08.js | 6 +- rsa-cryptosystem-signature/index09.js | 6 +- rsa-cryptosystem-signature/index10.js | 11 ++- rsa-cryptosystem-signature/index11.js | 2 +- rsa-cryptosystem-signature/index12.js | 4 +- rsa-cryptosystem-signature/index13.js | 8 +- rsa-cryptosystem-signature/index14.js | 4 +- rsa-cryptosystem-signature/index15.js | 6 +- rsa-cryptosystem-signature/index16.js | 16 ++-- rsa-cryptosystem-signature/index17.js | 14 ++- rsa-cryptosystem-signature/index18.js | 12 +-- rsa-cryptosystem-signature/index19.js | 4 +- rsa-cryptosystem-signature/index20.js | 12 +-- rsa-cryptosystem-signature/index21.js | 9 +- rsa-cryptosystem-signature/index22.js | 6 +- rsa-cryptosystem-signature/index23.js | 11 ++- rsa-cryptosystem-signature/index24.js | 18 ++-- rsa-cryptosystem-signature/index25.js | 7 +- rsa-cryptosystem-signature/index26.js | 7 +- rsa-cryptosystem-signature/index27.js | 7 +- rsa-cryptosystem-signature/index28.js | 4 +- rsa-cryptosystem-signature/index29.js | 13 ++- rsa-cryptosystem-signature/index30.js | 14 +-- rsa-cryptosystem-signature/index31.js | 15 ++- rsa-cryptosystem-signature/index32.js | 9 +- rsa-cryptosystem-signature/index33.js | 9 +- rsa-cryptosystem-signature/index34.js | 11 +-- rsa-cryptosystem-signature/index35.js | 5 +- rsa-cryptosystem-signature/index36.js | 15 ++- rsa-cryptosystem-signature/index37.js | 9 +- rsa-cryptosystem-signature/index38.js | 16 ++-- rsa-cryptosystem-signature/index39.js | 4 +- rsa-cryptosystem-signature/index40.js | 12 +-- rsa-cryptosystem-signature/index41.js | 7 +- rsa-cryptosystem-signature/index42.js | 9 +- rsa-cryptosystem-signature/index43.js | 10 +- rsa-cryptosystem-signature/index44.js | 12 +-- rsa-cryptosystem-signature/index45.js | 5 +- rsa-cryptosystem-signature/index46.js | 13 +-- rsa-cryptosystem-signature/index47.js | 14 +-- rsa-cryptosystem-signature/index48.js | 12 +-- rsa-cryptosystem-signature/index49.js | 11 ++- rsa-cryptosystem-signature/index50.js | 7 +- rsa-cryptosystem-signature/index51.js | 4 +- rsa-cryptosystem-signature/index52.js | 11 ++- rsa-cryptosystem-signature/index53.js | 79 ++++++++++++++++ rsa-cryptosystem-signature/index54.js | 82 +++++++++++++++++ rsa-cryptosystem-signature/index55.js | 86 ++++++++++++++++++ 56 files changed, 495 insertions(+), 240 deletions(-) create mode 100644 rsa-cryptosystem-signature/RSA signing process diagram.png create mode 100644 rsa-cryptosystem-signature/index53.js create mode 100644 rsa-cryptosystem-signature/index54.js create mode 100644 rsa-cryptosystem-signature/index55.js diff --git a/rsa-cryptosystem-signature/RSA signing process diagram.png b/rsa-cryptosystem-signature/RSA signing process diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..e3b2fb6258d1bd7e61529a1c1f0f2dcc37d8baf0 GIT binary patch literal 62190 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 0`. -*/ - -function generatePublicKey(privateKey) {} + Create a `while` loop that iterates when `privateKey` is valid i.e. when `privateKey > 0`. + */ +} function generateSignature() {} diff --git a/rsa-cryptosystem-signature/index30.js b/rsa-cryptosystem-signature/index30.js index 673f5293..b13234d6 100644 --- a/rsa-cryptosystem-signature/index30.js +++ b/rsa-cryptosystem-signature/index30.js @@ -32,14 +32,14 @@ function generatePrivateKey() { return 0; } -/* -Public key must fulfil the condition: `(publicKey * privateKey) mod phiOfN = 1` - -Use a conditional to check for the above mentioned condition inside the loop. -*/ - function generatePublicKey(privateKey) { - while (privateKey) {} + while (privateKey) { + /* + The public key must fulfill the condition `(publicKey * privateKey) mod phiOfN = 1`. + + Use an `if` statement to check for the condition mentioned above. + */ + } } function generateSignature() {} diff --git a/rsa-cryptosystem-signature/index31.js b/rsa-cryptosystem-signature/index31.js index b0f8d020..ac3f76b1 100644 --- a/rsa-cryptosystem-signature/index31.js +++ b/rsa-cryptosystem-signature/index31.js @@ -32,16 +32,15 @@ function generatePrivateKey() { return 0; } -/* -Public key has two constraints: -1. It must fulfil the condition: `(publicKey * privateKey) mod phiOfN = 1`. -2. It should not be same as private key. - -Use `&&` operator in the conditional to check for the second constraint. -*/ - function generatePublicKey(privateKey) { while (privateKey) { + /* + Public keys have two constraints. + First, `(publicKey * privateKey) mod phiOfN = 1`, but you already took care of this. + Second, it should not be the same as the private key. + + Use the `&&` operator in the `if` statement to check for the second constraint. + */ if ((publicKey * privateKey) % phiOfN === 1) { } } diff --git a/rsa-cryptosystem-signature/index32.js b/rsa-cryptosystem-signature/index32.js index 6aa2f33c..767c4e5e 100644 --- a/rsa-cryptosystem-signature/index32.js +++ b/rsa-cryptosystem-signature/index32.js @@ -32,16 +32,13 @@ function generatePrivateKey() { return 0; } -/* -We need to increment `publicKey` on every iteration to test the conditions. - -Increment `publicKey` on every iteration of loop. -*/ - function generatePublicKey(privateKey) { while (privateKey) { if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { } + /* + Increment `publicKey` so we can test it on every iteration of the loop. + */ } } diff --git a/rsa-cryptosystem-signature/index33.js b/rsa-cryptosystem-signature/index33.js index 87664a51..0694e48d 100644 --- a/rsa-cryptosystem-signature/index33.js +++ b/rsa-cryptosystem-signature/index33.js @@ -32,15 +32,12 @@ function generatePrivateKey() { return 0; } -/* -If both the conditions are true then we can stop the execution of loop as we have found a mathematically related public key. - -If both the conditions are true then terminate the function using `return;`. -*/ - function generatePublicKey(privateKey) { while (privateKey) { if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + /* + If both the conditions of the `if` statement are true then terminate the function using `return;` as we have found a mathematically related public key. + */ } ++publicKey; } diff --git a/rsa-cryptosystem-signature/index34.js b/rsa-cryptosystem-signature/index34.js index e9344c95..94f55fd2 100644 --- a/rsa-cryptosystem-signature/index34.js +++ b/rsa-cryptosystem-signature/index34.js @@ -32,12 +32,6 @@ function generatePrivateKey() { return 0; } -/* -If private key isn't successfully generated i.e. when `privateKey = 0` then there can't be a valid public key. - -Log an error message when `privateKey = 0`. -*/ - function generatePublicKey(privateKey) { while (privateKey) { if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { @@ -45,6 +39,11 @@ function generatePublicKey(privateKey) { } ++publicKey; } + /* + If `privateKey = 0`, then a private key couldn't successfully be generated so there can't be a valid public key. + + Log an error message to the console when `privateKey = 0`. + */ } function generateSignature() {} diff --git a/rsa-cryptosystem-signature/index35.js b/rsa-cryptosystem-signature/index35.js index 64f6a310..503a06d1 100644 --- a/rsa-cryptosystem-signature/index35.js +++ b/rsa-cryptosystem-signature/index35.js @@ -44,9 +44,10 @@ function generatePublicKey(privateKey) { } /* -Alice encrypts the hash value of data with his private key which we call signature. Thus, to generate signature we need to access hash value of data and Alice's private key. +Alice then encrypts the hash value of the data with her private key. +The encrypted value is known as a signature. -Provide parameters for hash value and private key in `generateSignature()` function. +To generate a signature, pass `hashValue` and `privateKey` as parameters to the `generateSignature()` function. */ function generateSignature() {} diff --git a/rsa-cryptosystem-signature/index36.js b/rsa-cryptosystem-signature/index36.js index d343065b..4b3f43d3 100644 --- a/rsa-cryptosystem-signature/index36.js +++ b/rsa-cryptosystem-signature/index36.js @@ -43,14 +43,13 @@ function generatePublicKey(privateKey) { console.log("Public key can't be generated."); } -/* -To encrypt data using RSA we use a mathematical equation: `encryptedData = (Data ^ privateKey) % N`. +function generateSignature(hashValue, privateKey) { + /* + To encrypt data using RSA, we use the equation: `encryptedData = (Data ^ privateKey) % N`. + We need to encrypt `hashValue`, so our `Data` is `hashValue`. -We need to encrypt `hashValue`. So, our `Data` is `hashValue`. - -Return `(hashValue ^ privateKey) % N` using `Math.pow`. -*/ - -function generateSignature(hashValue, privateKey) {} + Return `(hashValue ^ privateKey) % N` using `Math.pow`. + */ +} function decryptSignature() {} diff --git a/rsa-cryptosystem-signature/index37.js b/rsa-cryptosystem-signature/index37.js index a60985b8..71bde16b 100644 --- a/rsa-cryptosystem-signature/index37.js +++ b/rsa-cryptosystem-signature/index37.js @@ -48,11 +48,12 @@ function generateSignature(hashValue, privateKey) { } /* -Alice attaches the signature to data and sends it to Bob. Bob decrypts the received signature with Alice's public key. +Alice will attach her signature to the message and send it to Bob. +Then Bob needs to decrypt the signature with Alice's public key. +Since `publicKey` is a global variable, we have access to it everywhere. +So our decryption function only needs access to the signature to decrypt it. -`publicKey` being a global variable, we have access to it. So, our function only need access to signature to decrypt it. - -Provide a parameter for signature in `decryptSignature()` function. +Pass `digitalSignature` as a parameter to the `decryptSignature()` function. */ function decryptSignature() {} diff --git a/rsa-cryptosystem-signature/index38.js b/rsa-cryptosystem-signature/index38.js index adba8b10..63daf23f 100644 --- a/rsa-cryptosystem-signature/index38.js +++ b/rsa-cryptosystem-signature/index38.js @@ -47,13 +47,13 @@ function generateSignature(hashValue, privateKey) { return Math.pow(hashValue, privateKey) % N; } -/* -To decrypt data using RSA we use a mathematical equation similar to that of encryption: -`Data = (encryptedData ^ publicKey) % N`. +function decryptSignature(digitalSignature) { + /* + To decrypt data using RSA we use a equation similar to that of encryption: + `Data = (encryptedData ^ publicKey) % N`. -Here, the encryptedData is `digitalSignature`. + Here, the `encryptedData` is `digitalSignature`. -Return `(digitalSignature ^ publicKey) % N` using `Math.pow`. -*/ - -function decryptSignature(digitalSignature) {} + Return `(digitalSignature ^ publicKey) % N` using `Math.pow`. + */ +} diff --git a/rsa-cryptosystem-signature/index39.js b/rsa-cryptosystem-signature/index39.js index 90f73abb..a7475f35 100644 --- a/rsa-cryptosystem-signature/index39.js +++ b/rsa-cryptosystem-signature/index39.js @@ -52,7 +52,7 @@ function decryptSignature(digitalSignature) { } /* -Now, Alice is ready to send his secret message to Bob! +Now Alice is ready to send her secret message to Bob! -Create an empty function `sendMsgToBob` with `message` as a parameter. +Create an empty function called `sendMsgToBob` with message as a parameter. */ diff --git a/rsa-cryptosystem-signature/index40.js b/rsa-cryptosystem-signature/index40.js index 796a86e6..55392fa6 100644 --- a/rsa-cryptosystem-signature/index40.js +++ b/rsa-cryptosystem-signature/index40.js @@ -51,10 +51,10 @@ function decryptSignature(digitalSignature) { return Math.pow(digitalSignature, publicKey) % N; } -/* -Before Alice send his secret message. He needs to generate the key pairs then he hashes the data and encrypts the hash value to generate signature. +function sendMsgToBob(message) { + /* + Before Alice send her secret message, she needs to generate the key pair. -Help Alice generate his private key by using `generatePrivateKey()` function and store the returned value in a constant. -*/ - -function sendMsgToBob(message) {} + Create a constant called `privateKey` and set it equal to `generatePrivateKey()`. + */ +} diff --git a/rsa-cryptosystem-signature/index41.js b/rsa-cryptosystem-signature/index41.js index 88617720..d205665e 100644 --- a/rsa-cryptosystem-signature/index41.js +++ b/rsa-cryptosystem-signature/index41.js @@ -51,10 +51,9 @@ function decryptSignature(digitalSignature) { return Math.pow(digitalSignature, publicKey) % N; } -/* -Now, generate public key using `generatePublicKey` function and pass `privateKey` as an argument. -*/ - function sendMsgToBob(message) { const privateKey = generatePrivateKey(); + /* + Generate a public key by calling the `generatePublicKey()` function and pass it `privateKey` as an argument. + */ } diff --git a/rsa-cryptosystem-signature/index42.js b/rsa-cryptosystem-signature/index42.js index 5a8bc23e..86f49e8c 100644 --- a/rsa-cryptosystem-signature/index42.js +++ b/rsa-cryptosystem-signature/index42.js @@ -51,11 +51,12 @@ function decryptSignature(digitalSignature) { return Math.pow(digitalSignature, publicKey) % N; } -/* -Alice keeps private key as a secret and shares the public key with the world. Now, hash the message using the hash function you created and store the returned value in a constant. -*/ - function sendMsgToBob(message) { const privateKey = generatePrivateKey(); generatePublicKey(privateKey); + /* + Call the `hashTheMessage()` function and pass it `message` as an argument. + + Store the returned value as a constant named `hashValue`. + */ } diff --git a/rsa-cryptosystem-signature/index43.js b/rsa-cryptosystem-signature/index43.js index 25bde977..8ae23a7d 100644 --- a/rsa-cryptosystem-signature/index43.js +++ b/rsa-cryptosystem-signature/index43.js @@ -4,18 +4,14 @@ const N = firstPrime * secondPrime; const phiOfN = (firstPrime - 1) * (secondPrime - 1); let publicKey = 0; -/* -Hash value should be less than `N` for encryption and decryption to properly work. - -Make sure the function `hashTheMessage` always return hash value less than `N`. -Hint: You can use `%` operator to achieve this. -*/ - function hashTheMessage(message) { let hashValue = 0; for (let i = 0, msgLength = message.length; i < msgLength; ++i) { hashValue += message.charCodeAt(i); } + /* + Modify the return statement so that our hash function do not return values greater than or equal to `N` because RSA encryption and decryption work properly only when the hash value is less than `N`. + */ return hashValue; } diff --git a/rsa-cryptosystem-signature/index44.js b/rsa-cryptosystem-signature/index44.js index 20225960..b4626583 100644 --- a/rsa-cryptosystem-signature/index44.js +++ b/rsa-cryptosystem-signature/index44.js @@ -51,14 +51,14 @@ function decryptSignature(digitalSignature) { return Math.pow(digitalSignature, publicKey) % N; } -/* -We have generated the key pairs and we have the right hash value. Let's generate the signature. - -Generate signature using `generateSignature` function and store the returned value in a constant. -*/ - function sendMsgToBob(message) { const privateKey = generatePrivateKey(); generatePublicKey(privateKey); const hashValue = hashTheMessage(message); + /* + We now have a key pair and the right hash value. + + Call the `generateSignature()` function and pass it `hashValue` and `privateKey` as arguments. + Store the returned value as a constant named `generatedSignature`. + */ } diff --git a/rsa-cryptosystem-signature/index45.js b/rsa-cryptosystem-signature/index45.js index f4aff933..aafc442e 100644 --- a/rsa-cryptosystem-signature/index45.js +++ b/rsa-cryptosystem-signature/index45.js @@ -59,7 +59,8 @@ function sendMsgToBob(message) { } /* -Alice encrypted the hash value of his message which generated the signature. Now, we can send the message along with signature to Bob. +Alice encrypted the hash value of her message which generated her signature. +Now, we can send the message along with signature to Bob. -Create an empty function `sendAndVerify` and provide parameters for signature and message. +Create an empty function called `sendAndVerify` and provide the parameters `digitalSignature` and `message`. */ diff --git a/rsa-cryptosystem-signature/index46.js b/rsa-cryptosystem-signature/index46.js index f4b82248..4cf9ac49 100644 --- a/rsa-cryptosystem-signature/index46.js +++ b/rsa-cryptosystem-signature/index46.js @@ -58,10 +58,11 @@ function sendMsgToBob(message) { const generatedSignature = generateSignature(hashValue, privateKey); } -/* -Bob receives the data and signature. To verify the signature he first hashes the data through the same hash function which Alice used. +function sendAndVerify(digitalSignature, message) { + /* + Bob receives the data and signature. + To verify the signature he first needs to hash the received message with the same hash function Alice used. -Hash the received message using `hashTheMessage` function and store the returned value in a constant. -*/ - -function sendAndVerify(digitalSignature, message) {} + Hash the received message and store the returned value in a constant named `hashValue`. + */ +} diff --git a/rsa-cryptosystem-signature/index47.js b/rsa-cryptosystem-signature/index47.js index 8fc7600b..357f52b8 100644 --- a/rsa-cryptosystem-signature/index47.js +++ b/rsa-cryptosystem-signature/index47.js @@ -58,14 +58,14 @@ function sendMsgToBob(message) { const generatedSignature = generateSignature(hashValue, privateKey); } -/* -If you remember, Alice hashed the message then he encrypted the hash value which we called signature. +function sendAndVerify(digitalSignature, message) { + const hashValue = hashTheMessage(message); + /* + Do you remember that Alice's signature was the encrypted hash value of message? -Now, when we decrypt the signature or decrypt the encrypted hash value, we should get the original hash value of data. Makes sense? + So when Bob decrypts the signature using Alice's public key, he should get the original hash value of the message. -Decrypt the received signature using `decryptSignature` function and store the returned value in a constant. + Call the `decryptSignature()` function and pass it `digitalSignature` as an argument. + Store the returned value as a constant named `decryptedSignature`. */ - -function sendAndVerify(digitalSignature, message) { - const hashValue = hashTheMessage(message); } diff --git a/rsa-cryptosystem-signature/index48.js b/rsa-cryptosystem-signature/index48.js index c9ecf240..3f7307ed 100644 --- a/rsa-cryptosystem-signature/index48.js +++ b/rsa-cryptosystem-signature/index48.js @@ -58,13 +58,13 @@ function sendMsgToBob(message) { const generatedSignature = generateSignature(hashValue, privateKey); } -/* -The decrypted signature is the hash value of the original data. If the hash value of recevied data and decrypted signature are equal then the signature is verified and data is intact. - -Check for the equality of `hashValue` and `decryptedSignature`. If they are equal then log a success message. -*/ - function sendAndVerify(digitalSignature, message) { const hashValue = hashTheMessage(message); const decryptedSignature = decryptSignature(digitalSignature); + /* + If the hash value of received message and decrypted signature are equal then the message is intact and it is really from Alice. + + Check for the equality of `hashValue` and `decryptedSignature`. + If they are equal then log a success message to the console. + */ } diff --git a/rsa-cryptosystem-signature/index49.js b/rsa-cryptosystem-signature/index49.js index 5f58bba2..99a0a20d 100644 --- a/rsa-cryptosystem-signature/index49.js +++ b/rsa-cryptosystem-signature/index49.js @@ -58,14 +58,17 @@ function sendMsgToBob(message) { const generatedSignature = generateSignature(hashValue, privateKey); } -/* -Log a failure message when the `hashValue` and `decryptedSignature` don't match since someone must have modified the data or maybe the signature was wrong. -*/ - function sendAndVerify(digitalSignature, message) { const hashValue = hashTheMessage(message); const decryptedSignature = decryptSignature(digitalSignature); if (hashValue === decryptedSignature) { console.log("Success! Data is intact and signature is verified."); } + /* + There are two cases of failure. + First, when the original message is modified by a third party. The modified message will result in a different hash value than the original. + Second, when the signature is modified. + + So log a failure message when the `hashValue` and `decryptedSignature` don't match. + */ } diff --git a/rsa-cryptosystem-signature/index50.js b/rsa-cryptosystem-signature/index50.js index bbadc025..6938c937 100644 --- a/rsa-cryptosystem-signature/index50.js +++ b/rsa-cryptosystem-signature/index50.js @@ -51,15 +51,14 @@ function decryptSignature(digitalSignature) { return Math.pow(digitalSignature, publicKey) % N; } -/* -Call `sendAndVerify` function in `sendMsgToBob` and provide the `generatedSignature` and `message` as an argument. -*/ - function sendMsgToBob(message) { const privateKey = generatePrivateKey(); generatePublicKey(privateKey); const hashValue = hashTheMessage(message); const generatedSignature = generateSignature(hashValue, privateKey); + /* + Call `sendAndVerify()` function and provide the `generatedSignature` and `message` as an arguments. + */ } function sendAndVerify(digitalSignature, message) { diff --git a/rsa-cryptosystem-signature/index51.js b/rsa-cryptosystem-signature/index51.js index b96aa84d..86b06c56 100644 --- a/rsa-cryptosystem-signature/index51.js +++ b/rsa-cryptosystem-signature/index51.js @@ -70,5 +70,7 @@ function sendAndVerify(digitalSignature, message) { } /* -Call `sendMsgToBob` function and pass a super secret message as an argument. Observe the output of `console.log`. +Call the `sendMsgToBob()` function and pass it a super secret message as a string. + +Observe the output of `console.log`. */ diff --git a/rsa-cryptosystem-signature/index52.js b/rsa-cryptosystem-signature/index52.js index 244057c6..ce648aa6 100644 --- a/rsa-cryptosystem-signature/index52.js +++ b/rsa-cryptosystem-signature/index52.js @@ -60,6 +60,12 @@ function sendMsgToBob(message) { } function sendAndVerify(digitalSignature, message) { + /* + If the `message` is modified by a third party, it'll produce a different hash value so we should get a failure message on the console. + + Concatenate `message` with `Z` to modify it and then observe the output of `console.log`. + */ + const hashValue = hashTheMessage(message); const decryptedSignature = decryptSignature(digitalSignature); if (hashValue === decryptedSignature) { @@ -69,8 +75,5 @@ function sendAndVerify(digitalSignature, message) { } } +/* Don't change code below this line */ sendMsgToBob("Hey Bob, I'm Alice here. Bob, Buy 300 shares of TSLA!"); - -/* -Some more tests to go... -*/ diff --git a/rsa-cryptosystem-signature/index53.js b/rsa-cryptosystem-signature/index53.js new file mode 100644 index 00000000..8f9dbfc4 --- /dev/null +++ b/rsa-cryptosystem-signature/index53.js @@ -0,0 +1,79 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue % N; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); + generatePublicKey(privateKey); + const hashValue = hashTheMessage(message); + /* + We are still getting the success message on console. There's something strange going on. + + To debug what's wrong, print `hashValue` on console. + */ + const generatedSignature = generateSignature(hashValue, privateKey); + sendAndVerify(generatedSignature, message); +} + +function sendAndVerify(digitalSignature, message) { + message = message + "Z"; + const hashValue = hashTheMessage(message); + const decryptedSignature = decryptSignature(digitalSignature); + if (hashValue === decryptedSignature) { + console.log("Success! Data is intact and signature is verified."); + } else { + console.log("Failure! There's something wrong with data or signature."); + } +} + +/* Don't change code below this line */ +sendMsgToBob("Hey Bob, I'm Alice here. Bob, Buy 300 shares of TSLA!"); diff --git a/rsa-cryptosystem-signature/index54.js b/rsa-cryptosystem-signature/index54.js new file mode 100644 index 00000000..9c4f03e8 --- /dev/null +++ b/rsa-cryptosystem-signature/index54.js @@ -0,0 +1,82 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue % N; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); + generatePublicKey(privateKey); + const hashValue = hashTheMessage(message); + console.log("Original hash value =", hashValue); + const generatedSignature = generateSignature(hashValue, privateKey); + sendAndVerify(generatedSignature, message); +} + +function sendAndVerify(digitalSignature, message) { + message = message + "Z"; + const hashValue = hashTheMessage(message); + /* + We are aware that modified message should produce a different hash value. + + To see the new hash value, print `hashValue` on console. + Observe the outputs of `console.log`. + */ + + const decryptedSignature = decryptSignature(digitalSignature); + if (hashValue === decryptedSignature) { + console.log("Success! Data is intact and signature is verified."); + } else { + console.log("Failure! There's something wrong with data or signature."); + } +} + +/* Don't change code below this line */ +sendMsgToBob("Hey Bob, I'm Alice here. Bob, Buy 300 shares of TSLA!"); diff --git a/rsa-cryptosystem-signature/index55.js b/rsa-cryptosystem-signature/index55.js new file mode 100644 index 00000000..7053cba4 --- /dev/null +++ b/rsa-cryptosystem-signature/index55.js @@ -0,0 +1,86 @@ +const firstPrime = 2; +const secondPrime = 5; +const N = firstPrime * secondPrime; +const phiOfN = (firstPrime - 1) * (secondPrime - 1); +let publicKey = 0; + +function hashTheMessage(message) { + let hashValue = 0; + for (let i = 0, msgLength = message.length; i < msgLength; ++i) { + hashValue += message.charCodeAt(i); + } + return hashValue % N; +} + +function isCoPrime(smallerNum, largerNum) { + for (let i = 2; i <= smallerNum; ++i) { + if (smallerNum % i === 0 && largerNum % i === 0) { + return false; + } + } + return true; +} + +function generatePrivateKey() { + for (let privateKey = 2; privateKey < phiOfN; ++privateKey) { + if (isCoPrime(privateKey, N) && isCoPrime(privateKey, phiOfN)) { + return privateKey; + } + } + + console.log("Private key can't be generated."); + return 0; +} + +function generatePublicKey(privateKey) { + while (privateKey) { + if ((publicKey * privateKey) % phiOfN === 1 && privateKey !== publicKey) { + return; + } + ++publicKey; + } + + console.log("Public key can't be generated."); +} + +function generateSignature(hashValue, privateKey) { + return Math.pow(hashValue, privateKey) % N; +} + +function decryptSignature(digitalSignature) { + return Math.pow(digitalSignature, publicKey) % N; +} + +function sendMsgToBob(message) { + const privateKey = generatePrivateKey(); + generatePublicKey(privateKey); + const hashValue = hashTheMessage(message); + console.log("Original hash value =", hashValue); + const generatedSignature = generateSignature(hashValue, privateKey); + sendAndVerify(generatedSignature, message); +} + +function sendAndVerify(digitalSignature, message) { + message = message + "Z"; + const hashValue = hashTheMessage(message); + console.log("New hash value =", hashValue); + const decryptedSignature = decryptSignature(digitalSignature); + if (hashValue === decryptedSignature) { + console.log("Success! Data is intact and signature is verified."); + } else { + console.log("Failure! There's something wrong with data or signature."); + } +} + +sendMsgToBob("Hey Bob, I'm Alice here. Bob, Buy 300 shares of TSLA!"); + +/* +`console.log` tells us that the original hash value and the new hash value are same! +When two different messages or data result in the same hash value, we call it a hash collision. +Good hash functions have negligible chance of collision. +Our implementation is insecure and it was just to teach you the basics of hash functions. + +If you are working on a real app then you don't need to implement a hash function or RSA algorithm yourself. You should always use existing hash functions and cryptographic libraries like OpenSSL created by expert cryptographers. + +Happy coding <3 and stay secure! +*/