-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
311 lines (252 loc) · 12.4 KB
/
Copy pathmain.cpp
File metadata and controls
311 lines (252 loc) · 12.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
#include "seal/seal.h"
#include <iostream>
using namespace std;
using namespace seal;
class EncryptedPasswordStrengthChecker {
private:
shared_ptr<SEALContext> context; // SEAL 암호화 컨텍스트 - 암호화 매개변수들을 관리
PublicKey public_key; // 공개키 - 암호화에 사용됨 (클라이언트에게 공유 가능)
SecretKey secret_key; // 비밀키 - 복호화에 사용됨 (서버만 보유)
Evaluator evaluator; // 암호화된 데이터에 대한 연산을 수행하는 객체
RelinKeys relin_keys; // 재선형화 키 - 곱셈 연산 후 암호문 크기를 줄이는데 사용
// 점수 계산을 위한 가중치
const int LENGTH_WEIGHT = 25; // 길이 점수 (총 25점)
const int LOWERCASE_WEIGHT = 15; // 소문자 포함 (15점)
const int UPPERCASE_WEIGHT = 15; // 대문자 포함 (15점)
const int DIGIT_WEIGHT = 15; // 숫자 포함 (15점)
const int SPECIAL_CHAR_WEIGHT = 20; // 특수문자 포함 (20점)
const int SEQUENCE_PENALTY = 10; // 연속된 문자/숫자 패턴 패널티 (최대 -10점)
public:
EncryptedPasswordStrengthChecker() {
// SEAL 파라미터 설정
EncryptionParameters parms(scheme_type::bfv); // BFV 암호화 스킴 사용
// poly_modulus_degree는 동형암호의 보안 수준과 연산 능력을 결정하는 핵심 매개변수입니다.
// 값이 클수록 더 많은 연산이 가능하지만, 계산 비용이 증가합니다.
// 8192는 일반적으로 사용되는 값으로, 적절한 보안과 효율성 제공
size_t poly_modulus_degree = 8192;
parms.set_poly_modulus_degree(poly_modulus_degree);
// coeff_modulus는 암호화된 데이터의 정밀도와 연산 가능 횟수를 결정합니다.
// BFVDefault 함수는 poly_modulus_degree에 따라 적절한 값을 자동으로 설정
parms.set_coeff_modulus(CoeffModulus::BFVDefault(poly_modulus_degree));
// plain_modulus는 평문에서 사용되는 모듈러스 값으로, 계산 정확도와 관련됩니다.
// Batching 기법을 사용하면 하나의 암호문에 여러 값을 동시에 암호화할 수 있습니다.
parms.set_plain_modulus(PlainModulus::Batching(poly_modulus_degree, 20));
// 설정된 매개변수로 SEAL 컨텍스트 생성
context = make_shared<SEALContext>(parms);
// 암호화/복호화에 필요한 키 생성
KeyGenerator keygen(context);
secret_key = keygen.secret_key(); // 비밀키 생성 (복호화용)
keygen.create_public_key(public_key); // 공개키 생성 (암호화용)
// 재선형화 키 생성 - 암호화된 데이터 간의 곱셈 후 암호문 크기를 줄이는데 사용
// 이 키가 없으면 곱셈 연산 후 암호문 크기가 계속 커지게 됩니다
keygen.create_relin_keys(relin_keys);
// 암호화된 데이터에 대한 연산을 수행할 Evaluator 객체 초기화
evaluator = Evaluator(context);
}
// 암호화된 비밀번호의 길이 점수 계산
// 길이가 길수록 높은 점수를 부여합니다
Ciphertext checkLength(const vector<Ciphertext>& encrypted_password) {
// Encryptor 객체 생성 - 암호화를 수행
Encryptor encryptor(context, public_key);
// 길이 계산 (암호화된 문자 벡터의 크기)
int length = encrypted_password.size();
// 점수 계산 로직
// 길이에 따라 차등 점수 부여 (8자 미만: 낮은 점수, 8-12자: 중간 점수, 12자 이상: 높은 점수)
Plaintext length_score;
if (length < 8) {
length_score = length * (LENGTH_WEIGHT / 8);
}
else if (length < 12) {
length_score = LENGTH_WEIGHT * 0.7;
}
else {
length_score = LENGTH_WEIGHT;
}
// 계산된 점수를 암호화하여 반환
// 이렇게 암호화된 상태로 반환하면 다른 암호화된 점수와 연산이 가능합니다
Ciphertext encrypted_score;
encryptor.encrypt(length_score, encrypted_score);
return encrypted_score;
}
// 암호화된 비밀번호의 문자 유형 점수 계산 (소문자, 대문자, 숫자, 특수문자)
// 다양한 문자 유형을 포함할수록 높은 점수를 부여합니다
Ciphertext checkCharTypes(const vector<Ciphertext>& encrypted_password) {
Encryptor encryptor(context, public_key);
// 각 문자 유형 존재 여부를 검사하기 위한 암호화된 플래그 (0 또는 1)
// encrypt_zero()는 0을 암호화한 상태로 초기화
Ciphertext has_lowercase = encryptor.encrypt_zero();
Ciphertext has_uppercase = encryptor.encrypt_zero();
Ciphertext has_digit = encryptor.encrypt_zero();
Ciphertext has_special = encryptor.encrypt_zero();
// 각 암호화된 문자에 대해 문자 유형 검사
for (const auto& encrypted_char : encrypted_password) {
// ASCII 값 범위에 따라 문자 유형 확인
// 소문자 검사 (ASCII 97-122: a-z)
checkCharInRange(encrypted_char, 97, 122, has_lowercase);
// 대문자 검사 (ASCII 65-90: A-Z)
checkCharInRange(encrypted_char, 65, 90, has_uppercase);
// 숫자 검사 (ASCII 48-57: 0-9)
checkCharInRange(encrypted_char, 48, 57, has_digit);
// 특수문자 검사 (여러 ASCII 범위)
// 간소화를 위해 ASCII 33-47 범위만 확인
checkCharInRange(encrypted_char, 33, 47, has_special);
}
// 점수 합산 (암호화된 상태에서)
Ciphertext total_score = encryptor.encrypt_zero();
// 소문자 포함 시 점수 추가
// 1. 소문자 가중치를 암호화
Plaintext lowercase_score(to_string(LOWERCASE_WEIGHT));
Ciphertext encrypted_lowercase_score;
encryptor.encrypt(lowercase_score, encrypted_lowercase_score);
// 2. 소문자 존재 여부(0 또는 1)와 가중치를 곱함
// 소문자가 있으면 has_lowercase가 1이 되어 가중치가 그대로 반영됨
evaluator.multiply(encrypted_lowercase_score, has_lowercase, encrypted_lowercase_score);
// 3. 암호문 크기 축소를 위한 재선형화 수행
// 곱셈 연산 후 암호문 크기가 커지므로 이를 원래 크기로 줄이는 작업
evaluator.relinearize(encrypted_lowercase_score, relin_keys);
// 4. 총점에 가산
evaluator.add(total_score, encrypted_lowercase_score, total_score);
// 대문자, 숫자, 특수문자에 대해서도 동일한 과정 반복
// 대문자 포함 점수
Plaintext uppercase_score(to_string(UPPERCASE_WEIGHT));
Ciphertext encrypted_uppercase_score;
encryptor.encrypt(uppercase_score, encrypted_uppercase_score);
evaluator.multiply(encrypted_uppercase_score, has_uppercase, encrypted_uppercase_score);
evaluator.relinearize(encrypted_uppercase_score, relin_keys);
evaluator.add(total_score, encrypted_uppercase_score, total_score);
// 숫자 포함 점수
Plaintext digit_score(to_string(DIGIT_WEIGHT));
Ciphertext encrypted_digit_score;
encryptor.encrypt(digit_score, encrypted_digit_score);
evaluator.multiply(encrypted_digit_score, has_digit, encrypted_digit_score);
evaluator.relinearize(encrypted_digit_score, relin_keys);
evaluator.add(total_score, encrypted_digit_score, total_score);
// 특수문자 포함 점수
Plaintext special_score(to_string(SPECIAL_CHAR_WEIGHT));
Ciphertext encrypted_special_score;
encryptor.encrypt(special_score, encrypted_special_score);
evaluator.multiply(encrypted_special_score, has_special, encrypted_special_score);
evaluator.relinearize(encrypted_special_score, relin_keys);
evaluator.add(total_score, encrypted_special_score, total_score);
return total_score;
}
// 암호화된 문자가 특정 ASCII 범위에 있는지 확인하는 보조 함수
// 예: 'a'~'z' 범위인지, '0'~'9' 범위인지 등을 확인
void checkCharInRange(const Ciphertext& encrypted_char, int lower, int upper, Ciphertext& result) {
Encryptor encryptor(context, public_key);
// 범위 체크를 위한 임시 변수
Ciphertext temp_result = encryptor.encrypt_zero();
// 하한 체크: encrypted_char >= lower
// 1. 하한값을 암호화
Plaintext lower_plain(to_string(lower));
Ciphertext lower_bound;
encryptor.encrypt(lower_plain, lower_bound);
// 2. 문자값에서 하한값을 뺌 (암호화된 상태에서)
// 양수이면 하한보다 크다는 의미
evaluator.sub(encrypted_char, lower_bound, lower_bound);
// 상한 체크: encrypted_char <= upper
// 1. 상한값을 암호화
Plaintext upper_plain(to_string(upper));
Ciphertext upper_bound;
encryptor.encrypt(upper_plain, upper_bound);
// 2. 상한값에서 문자값을 뺌 (암호화된 상태에서)
// 양수이면 상한보다 작다는 의미
evaluator.sub(upper_bound, encrypted_char, upper_bound);
// 범위 내 여부 계산 (암호화된 상태에서 부울 연산을 시뮬레이션)
// 주의: 실제 구현에서는 더 복잡한 논리가 필요합니다.
// 동형암호에서 부울 연산은 직접적으로 지원되지 않아 산술 연산으로 시뮬레이션해야 함
// 결과 업데이트: 하나라도 범위 내에 있으면 1로 설정 (OR 연산 시뮬레이션)
// 간소화를 위해 여기서는 기본 연산만 표현했지만, 실제로는 더 정교한 구현 필요
evaluator.add(result, temp_result, result);
}
// 암호화된 비밀번호의 연속 패턴 검사
// 예: '123', 'abc'와 같은 연속된 패턴은 보안에 취약하므로 패널티 부여
Ciphertext checkSequencePatterns(const vector<Ciphertext>& encrypted_password) {
// 연속된 문자나 숫자 패턴을 감지
// 동형암호에서 패턴 감지는 복잡하므로 단순화를 위해 기본 패널티 점수 반환
// 실제 구현에서는 더 정교한 알고리즘이 필요합니다
Encryptor encryptor(context, public_key);
Plaintext penalty(to_string(0)); // 기본값으로 0 패널티 설정
Ciphertext encrypted_penalty;
encryptor.encrypt(penalty, encrypted_penalty);
return encrypted_penalty;
}
// 암호화된 비밀번호의 전체 강도 점수 계산 및 등급 결정
string evaluatePasswordStrength(const vector<Ciphertext>& encrypted_password) {
// 각 항목별 점수 계산 (암호화된 상태로 유지)
Ciphertext length_score = checkLength(encrypted_password);
Ciphertext char_types_score = checkCharTypes(encrypted_password);
Ciphertext sequence_penalty = checkSequencePatterns(encrypted_password);
// 총점 계산 (암호화된 상태에서 덧셈, 뺄셈 연산)
Ciphertext total_score;
evaluator.add(length_score, char_types_score, total_score);
evaluator.sub(total_score, sequence_penalty, total_score);
// 최종 점수만 복호화
// 중요: 이 단계에서만 복호화하므로 비밀번호 자체는 서버에서 알 수 없음
Decryptor decryptor(context, secret_key);
Plaintext decrypted_score;
decryptor.decrypt(total_score, decrypted_score);
// 복호화된 점수를 문자열로 변환
string score_str = decrypted_score.to_string();
int score = stoi(score_str);
// 강도 등급 판정 및 결과 반환
if (score >= 80) {
return "매우 강함 (점수: " + score_str + ")";
}
else if (score >= 60) {
return "강함 (점수: " + score_str + ")";
}
else if (score >= 40) {
return "보통 (점수: " + score_str + ")";
}
else {
return "약함 (점수: " + score_str + ")";
}
}
// 테스트용 함수 - 공개키 접근 메소드
// 실제 서비스에서는 시스템 구현 방식에 따라 달라질 수 있음
PublicKey getPublicKey() const {
return public_key;
}
// 컨텍스트 접근 메소드 (테스트용)
shared_ptr<SEALContext> getContext() const {
return context;
}
};
// 클라이언트 측 시뮬레이션 (비밀번호 암호화)
// 실제로는 클라이언트 애플리케이션에서 이루어지는 과정
vector<Ciphertext> encryptPassword(const string& password, const SEALContext& context, const PublicKey& public_key) {
// Encryptor 객체 생성 - 공개키로 암호화 수행
Encryptor encryptor(context, public_key);
vector<Ciphertext> encrypted_password;
// 비밀번호의 각 문자를 개별적으로 암호화
for (char c : password) {
// 문자의 ASCII 값을 문자열로 변환 후 Plaintext 객체로 생성
Plaintext plain(to_string(static_cast<int>(c)));
// 공개키를 사용하여 암호화
Ciphertext encrypted;
encryptor.encrypt(plain, encrypted);
// 암호화된 문자를 벡터에 추가
encrypted_password.push_back(encrypted);
}
// 암호화된 비밀번호 벡터 반환
return encrypted_password;
}
// 메인 함수 (테스트용)
int main() {
// 비밀번호 강도 체커 객체 생성
EncryptedPasswordStrengthChecker checker;
// 테스트할 비밀번호
string test_password = "P@ssw0rd123";
// 비밀번호 암호화 (클라이언트 측 시뮬레이션)
auto encrypted_password = encryptPassword(
test_password,
*checker.getContext(),
checker.getPublicKey()
);
// 암호화된 비밀번호 강도 평가 (서버 측)
string strength = checker.evaluatePasswordStrength(encrypted_password);
// 결과 출력
cout << "비밀번호 강도: " << strength << endl;
return 0;
}