@yossiz כתב במה האלגוריתם ליצירת ההאש לסיסמה בווינדוס ?:
(נראה לי ש-DES נחשב היום ממש חלש, יש את 3DES שיותר טוב)
הערת אגב, היום ה-AES נחשב המקום הראשון של ההצפנות הסימטריות.
@yossiz כתב במה האלגוריתם ליצירת ההאש לסיסמה בווינדוס ?:
(נראה לי ש-DES נחשב היום ממש חלש, יש את 3DES שיותר טוב)
הערת אגב, היום ה-AES נחשב המקום הראשון של ההצפנות הסימטריות.
@אף-אחד-3 סתם בשביל הסקרנות, מה אפשר לעשות בקובץ שעם רשימת ההאשים??
@dovid כתב בלמה DoForEach לא קיים ב-LINQ?:
א. ToList מצריך לולאה + משתנה. זה הרבה יותר משמעותי מלולאה נטו.
למה ToList חוסך לולאה ומשתנה? זה לכאורה עושה return new List<TSource>(enum)
ג. הנקודה היא לא מה חוזר. קוראים לזה תכנות פונקציונלי.
פונקציה שנכתבה לפי כללי התכנות הפונקציונלי בהכרח תחזיר ערך כי אחרת מה היא עושה, כי מה שקורה בגוף הפונקציה חייב להיות פרטי ובלתי נוגע לשום דבר אחר בעולם מלבד פלט.
אולי בגלל שלא מובן לי עד הסוף מה זה 'תכנות פונקציונלי' אז אני לא מצליח להבין מדוע חייב לחזור ערך. אין כזה דבר בתכנות פונקציונלי שחוזר ערך void
?
הפילוספיה היא שזה מתודות שאפשר להשתמש בהם בבטחה בלי לחשוש שהם ישפיעו על חלקים אחרים בתכנית שלך. לעולם לא יקרה שינוי לחלק בתכנית מפעולת LINQ שלא הושמה לתוך משתנה. בתוך המתודה ToList או ToArray קורה מה שקורה, אבל זה בסקופ סגור ובלתי משפיע, וזה יוצר פלט שאתה בוחר איך להשתמש בו.
לכאורה המתודה ForEach שומרת על הכלל הזה, מכיוון שהיא לא תוכל לשנות ערכים כל שהם, (הייעוד שלה זה לעשות פעולה חיצונית עבור כל אחד מהערכים) היא אמנם תוכל לשנות מאפיינים בתוך ערכים, אבל זה גם בעיה שנמצאת ב-Select וב-Where אם כי זה לא הייעוד שלהם, אבל הם לא מספקים את ההגנה הזאת.
אני מבין במקצת את הרעיון של ההגנה מפני שינוי על הערכים ברצף, אבל מדוע זה כלל גורף ב-LINQ? למה שלא יהיה אפשר לעשות שינוי למאפיינים באמצעות LINQ? יכול להיות שזה בהחלט הסיבה שגרמה למפתחים של LINQ לא להכניס כזאת פונקציה.
@dovid
אכן קראתי התשובה הוא מסביר שם נפלא למה ForEach אין מקומו ב-LINQ.
אבל עדיין יש כמה תמיהות כדלהלן:
בקשר לטענות הכותב:
א. למה דווקא ForEach מיותר וכי ה-ToList לא מיותר?! אדרבה, בעידן הנוכחי שהתכנות הפונקציונלי די פופולרי, קטע הקוד הבא יכול להיות מאוד אלגנטי:
Enumerable.Range(1, 20)
.Select(n => n * 3)
.Where(n => n % 2 == 0)
.DoForEach(n => Console.WriteLine(n));
הדרך המקבילה ב-foreach אמורה להיות קצת יותר מעצבנת... ובפרט כשהשרשור של האופרטורים יותר ארוך מהדוגמה.
ב. כנ"ל.
ג. זה באמת הנקודה שהכי דגדגה לי, כי היות והערך המוחזר מ-ForEach הוא void
יוצא שהיא המתודה היחידה שלא מחזירה ערך כלשהו. אבל גם על זה אפשר להתפלסף, כי הרעיון של ForEach זהה לרעיון של ToList ו-ToArray אלא שספציפית הפעולה של ForEach לא אמורה להחזיר ערך כלשהו.
וחוץ מזה מדוע העיקרון הגורף של LINQ הוא להחזיר ערך כלשהו? למה לא להשתמש איתו כדי להשפיע על הרצף בלי להחזיר שום ערך? איזה דבר רע (או לא פילוסופי) יש בזה?!
ועוד משהו, הכותב הנזכר בעצמו מעלה פה בסוף המאמר טיעונים חזקים המפריכים (במקצת) את התיאוריה שהוא כתב, ויש לציין שהטיעונים הללו נותרו ללא תשובות.
@dovid אבל ב-<>List
זה כן קיים, אז משמע שיש לזה יתרון כלשהו.
@dovid כתב בלמה DoForEach לא קיים ב-LINQ?:
גם אני לעיתים מתחשק לי ForEach כמתודה במקום בלולאה, אבל אני לא מוצא לזה ייתרון שכלי, זה סה"כ נטיה רגשית לזרום יותר מידי עם תכנות פונקציונלי.
אני מסכים עם דבריך, אבל לפי זה מדוע המפתחים טרחו לכתוב את ההרחבה ()ToList
?! לכאורה גם לה אין שום יתרון שכלי. כי אפשר לקבל את אותה התוצאה באמצעות הכתיבה הזאת:
var list = new List<int>(enumerable)
@חגי תודה על התשובות המקצועיות.
אני לא יודע אם Enumerable ו-LINQ קשורים בהכרח אחד לשני, אבל בכל מקרה יש הרבה הרחבות במחלקה Enumerable
שלא מקיימות את התנאי הזה, קח לדוגמא את ()ToArray
שזו נטו חיסכון בקוד. אז אם כן שאלתי עדין במקומה עומדת מדוע 'חסרה' פונקציה שתעשה איטרציה על כל הרצף?
זה שיש את זה ב-List
זה מחזק את השאלה מדוע לא הוסיפו את זה גם ל-Enumerable
כמו שלדוגמה ()ToArray
מופיע בשניהם.
ודרך אגב, הרעיון המרכזי ב-LINQ הוא להפסיק לכתוב "איך" ולהתחיל לכתוב "מה", וגם לפי זה ההיגיון הפשוט הוא היה להוסיף פונקציית עזר כדוגמת ForEach.
שאלה שמנקרת בראשי הרבה זמן.
המחלקה Enumerable
ב-dotnet עשירה בעשרות פונקציות שמקילות על הפיתוח וחוסכות כתיבת קוד.
אבל אני תוהה מדוע 'חסרה' שם פונקציה פשוטה? אפשר לקרוא לה לצורך הדוגמא DoForEach שתעבור על כל הרצף ותעשה פעולה זהה לכל אחד מהערכים.
במקום לעבור על זה בלולאת foreach אפשר לקרוא לפונקציה פשוטה שתקבל את ה-action בארגומנט.
אני לדוגמא בכל פרויקט מוסיף את ההרחבה הזאת:
public static class EnumerableExtensions
{
public static void DoForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
{
action(item);
}
}
}
יש למישהו הסבר מניח את הדעת מדוע מפתחי LINQ בחרו שלא לכלול פונקציה כזאת ב-LINQ.
@dovid לא ציפיתי לכזו תגובה חריפה
נכון שעשיתי 'קונצים' בעבר, אבל אולי חזרתי בתשובה?
אני מבחינתי פותח דף חדש.
@dovid אני אוהב לעזור לכל מי שמבקש, התחום שאני חזק בו זה #C ו WPF, ובינתיים לא נתקלתי בבקשת עזרה בתחום הזה. אם אני יתקל בבקשת עזרה בתחום הזה, אהיה בל"נ הראשון שיעזור.
רוב השאלות שראיתי פה בפורום הם בנושא VBA או WinForm שאני לא מומחה בהם, בלשון המעטה.
עכשיו לגבי הקוד שפורץ את הרעיון, אני חושב שאני לא צריך להסביר לך שזה בגלל שהמפתחות שהקוד שהכנתי מייצר הם מפתחות קטנים, ולכן קל לשחזר אותם. אני בחרתי לייצר מספרים קטנים רק בגלל שיקולים של ביצועים.
קח לדוגמא את הספרות האלו:
1194494
719311
1944435
1477276
874876
1765046
1615810
1097120
1085181
1495561
1968819
1743848
171395
217310
1476345
אני משוכנע שזה כמעט לא אפשרי לשחזר את המפתח המקורי שלהם, (אולי זה ייקח כמה שנים...) וזה בגלל שעכשיו בחרתי למפתחות מספרים די גבוהים.
אני רוצה להוסיף עוד משהו קטן.
עכשיו קראתי בויקיפדיה על ה-RSA והתבאר לי משהו שלא היו מובן לי עד עכשיו.
אם אני מצפין באמצעות התרגיל שהצגתי (שהוא כאמור RSA) את הספרה 1 אז גם זה הפלט המוצפן (משום שהכפלה של 1 תמיד תחזיר 1) אז יש פה איזה חולשה.
קראתי שם על עוד כמה חולשות, לדוג' שאם הפורץ שולח קלט קצר והוא מקבל את התוכן המוצפן אז יתכן שהוא יוכל לפרוץ את זה בקלות (הבעיה המרכזית בנושא הזה).
אז הפתרון לבעיות האלו (שמשתמשים בו היום) הוא להשתמש עם ריפוד, ובעצם כבר הזכירו את זה פה @חגי ו @nigun שאפשר להשתמש עם מספרים רנדומליים שיטשטשו את המבנה של המספר המקורי.
אז המסקנה שלי שבשביל ערפול (או הצפנה) מוצלח צריך את שני הדברים הללו (התרגיל עצמו וטשטוש המבנה של ההודעה המקורית), וכל אחד בפני עצמו לא יהיה מספיק טוב.
אפשר לקרוא על זה פה תחת התת כותרת "ריפוד".
טוב, @dovid אני רוצה לחשוף את כל הקלפים.
עשיתי פה קונץ קטן, אנא קבל את התנצלותי "...ומודה ועוזב ירוחם".
הפתרון שהצגתי הוא לא רעיון שלי (ודאי שלא, לא היתה הו"א בכלל) הוא פתרון שכיום נמצא בשימוש נרחב בתעשייה העולמית, ואפשר למצוא אותו כמעט בכל פינה. הפרוטוקולים המאובטחים של האינטרנט מבוססים עליו, והוא המלך של ההצפנות הא-סימטריות - הלא הוא ה-RSA!
אז כאמור, קבל את התנצלויותי על כך שלא חשפתי את הפרט הזה. חששתי להשתמש בנגזרת של צ.פ.נ. מפני שהבקשה הייתה מפורשת לא להשתמש בהצפנה אלא בתרגיל. ניסיתי להציע את זה בהתחלה (מבלי לצרף דוגמא), אבל אז חטפתי על הראש שלא קראתי היטב את השאלה. לכן בחרתי להציג את זה כתרגיל מתמטי ולא כהצפנה.
מה, וכי אני אשם שה-RSA משתמש בתרגיל המתמטי הפשוט הזה?!
@dovid נדמה לי שעכשיו אתה לא צריך הוכחות על הביטחון של התרגיל הזה.
@dovid כתב בתרגיל מתמטי של הסתרת מזהה רץ:
בינתיים לא הצגת קוד זהב שעושה זאת בלי שום ידע מוקדם מלבד הנקודות שהזכרת (השיטה והרצף של ה30).
אני כבר אמרתי שאין לי משהו מיוחד בקוד, הסיבה הפשוטה שאני לא מביא אותו כאן זה בגלל שזה נמצא במקום אחר ולא על המחשב שאני כותב בו עכשיו, וגם שם אני לא בטוח שהוא נשאר בשלמותו.
אם אתה לא יודע להסביר את המתמטיקה, הבטחון שלך שקול בעיני לאמונה אינטואטיבית. אני נשנעתי על הסמכות של הכותבים של הרעיון.
אוקיי, בא נעשה סדר.
אתה יודע מי הכותבים האלה? אני לא מכיר אותם, ואני בטוח שזה לא רעיון שלהם, ועם כל זה הם כותבים במפורש שזה לא מוסיף אבטחה:
this adds no actual security to a system; it’s obscurity, at best.
מי אני אתה גם לא יודע, אבל אני לפחות אומר לך במפורש שאם משתמשים במספרים גדולים, זה כן מוסיף אבטחה. אתה לא חייב לסמוך עלי, אבל זה דעתי. לתרום לדיון בעניין הזה כבר אמרתי שקשה לי (כי זה לא ברור לי דיו).
@dovid החולשה של הפתרון שלך הוא בעיקר בגלל שאפשר לעלות עליו עם Brute Force, וזה בעצם הדבר שהתרגיל שהצגתי עושה אותו (הרבה) יותר קשה.
מבחינת המתמטיקה אני בטוח שאין דרך לפענח את זה. החולשה היחידה האפשרית זה Brute Force.
יש ל-vscode המון הרחבות של ערכת נושא, אבל אני רוצה להביא שילוב של שני הרחבות שאני אישית מאוד אוהב, וזה אחרי בדיקה של עשרות themeים...
ההרחבות הם:
מצרף צילומי מסך:
זה הערכת נושא הכהה הרגילה של vscode:
וזה אחרי שני התוספים הנ"ל:
@dovid
קשה לי להסביר למה הוא יותר טוב (משום שלי לא ברור עד הסוף), אבל הוא בטוח יותר טוב מסיבה פשוטה:
מתבצע בו העלאה בחזקה (במקום הכפלה בתרגיל שלך) שזה לוקח זמן למחשב, ולכן ל-Brute Force ייקח הרבה זמן.
לדוגמא, התוצאה של התרגיל הזה: 12345 ** 12345
זה מספר עם 50,510 ספרות, אז בשביל פעם אחת האיטיות לא תהיה מורגשת, אבל לחישוב של אלפי מספרים האיטיות תהיה מורגשת מאוד.
אבל זה גם לא מאה אחוז. ככל שהמספרים של המפתחות יהיו גדולים כך זה יהיה יותר בטוח (אבל גם איטי).
@dovid כתב בתרגיל מתמטי של הסתרת מזהה רץ:
יש לך מה להשתפשף בקידוד, כי הקוד לא מספיק טוב
אשמח אם תצביע לי על מקום שיכול להיכתב יותר טוב, ואני בל"נ אתקן.
@dovid מצאתי פתרון אחר שקצת מזכיר את הרעיון שלך אבל הוא נראה לי יותר טוב.
התרגיל הזה קצת יותר 'יקר' למחשב כי הוא מחייב שימוש ב-BigInt, אבל הוא הרבה יותר בטוח וקשה לפענוח.
המפתחות הם כמו בתרגיל הקודם - שני מספרים כלשהם ומודולו שהוא המקסימום.
היצירה של המפתחות בפעם הראשונה היא פעולה קצת מורכבת, אז הכנתי פונקציה ב-#C שתיצור את המפתחות.
יש שלושה מפתחות: d ,e ו - n, ה - n הוא המודולו והוא המספר המקסימלי שאפשר לערפל ולשחזר למספר המקורי.
אחרי שיש את המפתחות הערפול נעשה בצורה כזאת: id ** e % n
והשחזור למספר המקורי נעשה הפוך: obfuscated ** d % n
הפתרון הזה יותר לוקח זמן כי מתבצע בערפול ובשחזור העלאה בחזקה מה שלא היה בפתרון הקודם,
אז צריך לשקול מה יותר חשוב, האבטחה או הביצועים.
זה הפונקציה ליצירת המפתחות: (GenerateKeys)
void GenerateKeys(out int n, out int e, out int d)
{
Random random = new();
int p = GeneratePrime(128, 512, random);
int q = GeneratePrime(128, 512, random);
n = p * q;
int ef = Lcm(p - 1, q - 1);
GenerateE:
e = random.Next(32, 512);
if (Gcd(e, ef) != 1)
goto GenerateE;
d = GenerateD(ef, e);
if (d < 0)
goto GenerateE;
}
bool IsPrime(int num)
{
for (int i = num - 1; i > 1; i--)
{
if (num % i == 0)
return false;
}
return true;
}
int Gcd(int a, int b)
{
return b == 0 ? a : Gcd(b, a % b);
}
int Gcf(int a, int b)
{
while (b != 0)
{
int temp = b;
b = a % b;
a = temp;
}
return a;
}
int Lcm(int a, int b)
{
return a / Gcf(a, b) * b;
}
int GeneratePrime(int min, int max, Random random)
{
int p;
do
{
p = random.Next(min, max);
} while (!IsPrime(p));
return p;
}
int GenerateD(int ef, int e)
{
int y1 = 1, y2 = 0;
while (e > 0)
{
int q = ef / e;
int y = y2 - (q * y1);
int r = ef % e;
ef = e;
e = r;
y2 = y1;
y1 = y;
}
return y2;
}
בשביל למהר את הביצועים מומלץ להשתמש במפתחות עם המודולו הקטן ביותר שנצרך, לדוג' אם המספר הגבוה ביותר שצריך לעפל הוא 10,000, אז כדי לקרוא לפונקציה שיוצרת את המפתחות שוב ושוב עד שיצא מספר מקסימלי (n) הכי נמוך אבל מעל 10,000.
טסט לדוגמא ב-#C:
GenerateKeys(out var n, out var e, out var d);
Console.WriteLine($"n: {n}, e: {e}, d: {d}");
foreach (var num in Enumerable.Range(3, 15))
{
int c = (int)(BigInteger.Pow(num, e) % n);
int m = (int)(BigInteger.Pow(c, d) % n);
Console.Write("num: " + num);
Console.CursorLeft = 15;
Console.Write("c: " + c);
Console.CursorLeft = 30;
Console.WriteLine("m: " + m);
}
הפלט:
n: 71219, e: 287, d: 12683
num: 3 c: 36878 m: 3
num: 4 c: 51738 m: 4
num: 5 c: 44630 m: 5
num: 6 c: 54772 m: 6
num: 7 c: 26540 m: 7
num: 8 c: 21046 m: 8
num: 9 c: 60079 m: 9
num: 10 c: 43447 m: 10
num: 11 c: 66289 m: 11
num: 12 c: 36954 m: 12
num: 13 c: 61380 m: 13
num: 14 c: 64087 m: 14
num: 15 c: 65269 m: 15
אשמח לקבל הארות ותגובות.