cs-3333: add rsa project

This commit is contained in:
Price Hiller 2024-11-12 08:10:21 -06:00
parent 7bba42bf07
commit e08b77cb1b
Signed by: Price
GPG Key ID: C3FADDE7A8534BEB
5 changed files with 457 additions and 0 deletions

View File

@ -0,0 +1,244 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCaseColons: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowBreakBeforeNoexceptSpecifier: Never
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAdjacentStringLiterals: true
BreakAfterAttributes: Leave
BreakAfterJavaFieldAnnotations: false
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Attach
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: "^ IWYU pragma:"
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: ".*"
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: "(Test)?$"
IncludeIsMainSourceRegex: ""
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
KeepEmptyLinesAtEOF: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ""
MacroBlockEnd: ""
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: BinPack
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakScopeResolution: 500
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
PPIndentWidth: -1
QualifierAlignment: Leave
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SkipMacroDefinitionBody: false
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterPlacementOperator: true
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
InCStyleCasts: false
InConditionalStatements: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE

View File

@ -0,0 +1,213 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
bool isPrime(unsigned long num) {
for (int i = 2; i * i <= num; i++) {
if (num % i == 0) {
return false;
}
}
return true;
}
/**
* @brief Calculate the great common divisor (GCD) of two numbers recursively
*
* @param a first number
* @param b second number
* @return the gcd of both numbers
*/
unsigned long gcd(unsigned long a, unsigned long b) {
// NOTE: This really should be iterative with a while loop, recursion here
// is not ideal
return (b == 0) ? a : gcd(b, a % b);
}
/**
* @brief Calculate the great common divisor (GCD) of two numbers recursively
* via the Extended Euclidean Algorithm
*
* @param a First number to find GCD against b
* @param b Second number to find GCD against a
* @return the gcd of both numbers
*/
signed long long gcdExtended(signed long long a, signed long long b,
signed long long *x, signed long long *y) {
// Base Case
if (a == 0) {
*x = 0, *y = 1;
return b;
}
// To store results of recursive call
signed long long x1, y1;
signed long long gcd = gcdExtended(b % a, a, &x1, &y1);
// Update x and y using results of recursive
// call
*x = y1 - (b / a) * x1;
*y = x1;
return gcd;
}
/**
* @brief Calculate the modulo inverse using the extended Euclidean Algorithm
*
* @param a The value to be inverted mod a
* @param m The modulus, a positive integer greater than 1
*/
unsigned long modInverse(unsigned long a, unsigned long m) {
// You might notice the evil casting going on below. There's a really good
// reason for that! The extended euclidean algo. pretty much has a hard
// requirement on using signed integers. To satisfy this condition, we're
// using `signed long long` so we can safely fit the *unsigned long* value
// within. This allows "safe" casts back and forth without any loss.
//
// Some more enlightening information on this problem can be found at
// https://jeffhurchalla.com/2018/10/13/implementing-the-extended-euclidean-algorithm-with-unsigned-inputs/
//
// Frankly, there are much, MUCH, faster ways of doing this if we didn't
// *have* to use the extended euclidean algo. (mostly in the form of cursed
// bitshifting which FIPS-186-5 has some resources on 😉).
signed long long x, y, a_0 = (signed long long)a, m_0 = (signed long long)m;
signed long long g = gcdExtended(a_0, m_0, &x, &y);
if (g != 1) {
fprintf(stderr,
"Failed to determine modular inverse for `%lu` and `%lu`, they "
"may not be coprime!\n",
a, m);
exit(EXIT_FAILURE);
}
return (unsigned long)((x % m_0 + m_0) % m_0);
}
/**
* @brief Modular Exponentiation
*
* See
* https://www.cs.ucf.edu/~dmarino/ucf/cis3362/lectures/newlecs/FastModExpo.pdf
* for more information
*/
unsigned long modExp(unsigned long base, unsigned long exp, unsigned long num) {
if (exp == 0)
return 1;
if (exp == 1)
return base % num;
if (exp % 2 == 0) {
int t = modExp(base, exp / 2, num);
return (t * t) % num;
}
return (base * modExp(base, exp - 1, num)) % num;
}
unsigned long rprime(unsigned n) {
unsigned r = rand(), t;
while ((t = gcd(r, n)) > 1) {
r /= t;
}
return r;
}
/**
* @brief Generate keys for RSA encryption given some p & q primes
*
* @param p first secret large prime number
* @param q second secret large primer number
* @param n will be set to the result of p * q
* @param e randomly chosen such that e < φ(n) and e & φ (n) are coprime
* @param d the mod inverse of e % φ(n), where e*d 1 (mod φ(n))
*/
void generateKeys(unsigned long p, unsigned long q, unsigned long *n,
unsigned long *e, unsigned long *d) {
*n = p * q;
unsigned long phi = (p - 1) * (q - 1);
while (gcd(*e, phi) != 1 && *e < phi) {
(*e)++;
}
if (*e >= phi) {
fprintf(stderr,
"Failed to find valid `e` value for given `phi` value! `e`: "
"'%lu' | `phi`: '%lu'\n",
*e, phi);
exit(EXIT_FAILURE);
}
*d = modInverse(*e, phi);
}
/**
* @brief Encrypt plaintext with RSA
*
* @param plaintext
* @param e Encryption key
* @param n Combined secret values
*/
unsigned long encrypt(unsigned long plaintext, unsigned long e,
unsigned long n) {
return modExp(plaintext, e, n);
}
// Function to decrypt ciphertext using RSA
/**
* @brief Decrypt RSA encrypted ciphertext
*
* @param ciphertext The text to decrypt
* @param d Decryption key
* @param n Combined secret values
*/
unsigned long decrypt(unsigned long ciphertext, unsigned long d,
unsigned long n) {
return modExp(ciphertext, d, n);
}
int main() {
unsigned long p = 7; // prime number, try 3, 5, 7, 11, 13, ...
unsigned long q = 541; // prime number, try 3, 5, 7, 11, 13, ...
unsigned long e = 7;
unsigned long n, d;
// printf("Enter p and q: ");
// scanf("%lu %lu", &p, &q);
// Generate RSA keys
generateKeys(p, q, &n, &e, &d);
if (!isPrime(p)) {
fprintf(stderr, "Given `p` value was not prime, received '%lu'!\n", p);
exit(EXIT_FAILURE);
}
if (!isPrime(q)) {
fprintf(stderr, "Given `q` value was not prime, received '%lu'!\n", q);
exit(EXIT_FAILURE);
}
printf("Public key (e, n): (%lu, %lu)\n", e, n);
printf("Private key (d, n): (%lu, %lu)\n\n", d, n);
// Encrypt and decrypt a sample plaintext, which we assume is given as an
// integer value
unsigned long plaintext;
printf("Enter an integer between 0 and %lu as plain text to be encrypted: ",
n - 1);
int _ = scanf("%lu", &plaintext);
if (plaintext > n - 1) {
fprintf(stderr,
"Unable to RSA encrypt & decrypt given `plaintext`: "
"'%lu'!\nPlaintext value was more than `n-1`: '%lu'\n",
plaintext, n - 1);
exit(EXIT_FAILURE);
}
printf("Original plaintext: %lu\n", plaintext);
unsigned long ciphertext = encrypt(plaintext, e, n);
printf("Encrypted ciphertext: %lu\n", ciphertext);
unsigned long decrypted = decrypt(ciphertext, d, n);
printf("Decrypted plaintext: %lu\n\n", decrypted);
return 0;
}