Skip to content

Commit

Permalink
Merge pull request #35 from ase-101/release-1.x.x
Browse files Browse the repository at this point in the history
Cherry-picked bug fixes from develop to release branch
  • Loading branch information
ase-101 authored Jan 25, 2024
2 parents 22ba35d + b509612 commit 3107f71
Show file tree
Hide file tree
Showing 16 changed files with 142 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void sendAuditTransaction(AuditEvent auditEvent, AuditEventType eventType
restRequestWrapper.setRequesttime(getUTCDateTime());

String description = signUpException != null ?
ExceptionUtils.getStackTrace(signUpException) : null;
ExceptionUtils.getStackTrace(signUpException) : auditEvent.toString() + " " + eventType.toString();
if (description != null && description.length() > auditDescriptionMaxLength) {
description = description.substring(0, auditDescriptionMaxLength);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,54 @@
package io.mosip.signup.helper;

import io.mosip.esignet.core.util.IdentityProviderUtil;
import io.mosip.kernel.core.util.UUIDUtils;
import io.mosip.signup.exception.SignUpException;
import io.mosip.signup.services.CacheUtilService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import static io.mosip.kernel.core.util.UUIDUtils.NAMESPACE_OID;

@Slf4j
@Component
public class CryptoHelper {

private static final String AES_TRANSFORMATION = "AES/CFB/PKCS5Padding";
public static final String CACHE_KEY = "aes";
public static final String ALIAS_CACHE_KEY = "CURRENT_ACTIVE_ALIAS";

@Value("${mosip.signup.cache.symmetric-algorithm-name}")
private String symmetricAlgorithm;

@Value("${mosip.signup.cache.symmetric-key.algorithm-name:AES}")
private String symmetricKeyAlgorithm;

@Value("${mosip.signup.cache.symmetric-key.size:256}")
private int symmetricKeySize;

@Autowired
private CacheUtilService cacheUtilService;

public String symmetricEncrypt(String transactionId, String data, SecretKey secretKey) {
public String symmetricEncrypt(String data) {
try {
Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);

String keyAlias = getActiveKeyAlias();
SecretKey secretKey = getSecretKey(keyAlias);

Cipher cipher = Cipher.getInstance(symmetricAlgorithm);
byte[] initializationVector = IdentityProviderUtil.generateSalt(cipher.getBlockSize());
byte[] secretDataBytes = data.getBytes(StandardCharsets.UTF_8);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(initializationVector));
byte[] encryptedBytes = cipher.doFinal(secretDataBytes, 0, secretDataBytes.length);

String keyAlias = getKeyAlias(transactionId);
byte[] keyAliasBytes = keyAlias.getBytes();
cacheUtilService.setSecretKeyBasedOnAlias(keyAlias, IdentityProviderUtil.b64Encode(secretKey.getEncoded()));

byte[] output = new byte[cipher.getOutputSize(secretDataBytes.length)+cipher.getBlockSize()+keyAliasBytes.length];
System.arraycopy(encryptedBytes, 0, output, 0, encryptedBytes.length);
Expand All @@ -49,12 +64,12 @@ public String symmetricEncrypt(String transactionId, String data, SecretKey secr

public String symmetricDecrypt(String encryptedData) {
try {
Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
Cipher cipher = Cipher.getInstance(symmetricAlgorithm);

byte[] data = IdentityProviderUtil.b64Decode(encryptedData);
byte[] keyAlias = Arrays.copyOfRange(data, data.length - 10, data.length);
byte[] iv = Arrays.copyOfRange(data, data.length-(cipher.getBlockSize()+10), data.length-10);
byte[] encryptedBytes = Arrays.copyOfRange(data, 0, data.length-(cipher.getBlockSize()+10));
byte[] keyAlias = Arrays.copyOfRange(data, data.length-36, data.length);
byte[] iv = Arrays.copyOfRange(data, data.length-(cipher.getBlockSize()+36), data.length-36);
byte[] encryptedBytes = Arrays.copyOfRange(data, 0, data.length-(cipher.getBlockSize()+36));

String encodedSecretKey = cacheUtilService.getSecretKey(new String(keyAlias));
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(IdentityProviderUtil.b64Decode(encodedSecretKey), "AES"),
Expand All @@ -67,23 +82,31 @@ public String symmetricDecrypt(String encryptedData) {
}


public SecretKey getSecretKey() {
String encodedSecretKey = cacheUtilService.getSecretKey();
public SecretKey getSecretKey(String alias) {
String encodedSecretKey = cacheUtilService.getSecretKey(alias);
return new SecretKeySpec(IdentityProviderUtil.b64Decode(encodedSecretKey), "AES");
}

private String getActiveKeyAlias() {
String alias = cacheUtilService.getActiveKeyAlias();
if(alias != null)
return alias;

log.debug("No active alias found, generating new alias and AES key.");
alias = UUIDUtils.getUUID(NAMESPACE_OID, "signup-service").toString();
generateSecretKey(alias);
return alias;
}

private void generateSecretKey(String alias) {
try {
if(encodedSecretKey == null) {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(256);
cacheUtilService.setSecretKey(CACHE_KEY, IdentityProviderUtil.b64Encode(keyGenerator.generateKey().getEncoded()));
encodedSecretKey = cacheUtilService.getSecretKey();
}
return new SecretKeySpec(IdentityProviderUtil.b64Decode(encodedSecretKey), "AES");
} catch (Exception e) {
log.error("Error getting secret key", e);
KeyGenerator keyGenerator = KeyGenerator.getInstance(symmetricKeyAlgorithm);
keyGenerator.init(symmetricKeySize);
cacheUtilService.setSecretKey(alias, IdentityProviderUtil.b64Encode(keyGenerator.generateKey().getEncoded()));
cacheUtilService.setActiveKeyAlias(ALIAS_CACHE_KEY, alias);
} catch (NoSuchAlgorithmException e) {
log.error("Error generating secret key", e);
throw new SignUpException("crypto_error");
}
}

private String getKeyAlias(String transactionId) {
return transactionId.substring(transactionId.length()-10);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ public String setSecretKey(String key, String secretKey) {
return secretKey;
}

@Cacheable(value = SignUpConstants.KEY_ALIAS, key = "#alias")
public String setSecretKeyBasedOnAlias(String alias, String secretKey) {
return secretKey;
@Cacheable(value = SignUpConstants.KEY_ALIAS, key = "#key")
public String setActiveKeyAlias(String key, String alias) {
return alias;
}

//---Getter---
Expand All @@ -75,11 +75,11 @@ public boolean isIdentifierBlocked(String identifier) {
return value == null ? false : true;
}

public String getSecretKey() {
return cacheManager.getCache(SignUpConstants.KEYSTORE).get(CryptoHelper.CACHE_KEY, String.class);
public String getSecretKey(String keyAlias) {
return cacheManager.getCache(SignUpConstants.KEYSTORE).get(keyAlias, String.class);
}

public String getSecretKey(String alias) {
return cacheManager.getCache(SignUpConstants.KEY_ALIAS).get(alias, String.class);
public String getActiveKeyAlias() {
return cacheManager.getCache(SignUpConstants.KEY_ALIAS).get(CryptoHelper.ALIAS_CACHE_KEY, String.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -405,9 +405,7 @@ private void checkActiveIdentityExists(String transactionId,
}

//set UIN in the cache to be further used for update UIN endpoint
SecretKey secretKey = cryptoHelper.getSecretKey();
registrationTransaction.setUin(cryptoHelper.symmetricEncrypt(transactionId,
restResponseWrapper.getResponse().getIdentity().getUIN(), secretKey));
registrationTransaction.setUin(cryptoHelper.symmetricEncrypt(restResponseWrapper.getResponse().getIdentity().getUIN()));

}

Expand Down
14 changes: 9 additions & 5 deletions signup-service/src/main/resources/application-default.properties
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ mosip.signup.supported.challenge.otp.length=6

## ------------------------------------- Cache configuration -----------------------------------------------------------

mosip.signup.cache.symmetric-algorithm-name=AES/CFB/PKCS5Padding
spring.cache.type=simple

#spring.cache.type=redis
Expand All @@ -50,13 +51,16 @@ mosip.esignet.cache.size={'challenge_generated': 200, \
'challenge_verified': 200,\
'status_check': 200,\
'blocked_identifier':2000,\
'keystore' : 5, \
'key_alias' : 200 }
'keystore' : 10, \
'key_alias' : 1 }

## Note: keystore TTL should be more than the key_alias cache TTL.
## So that key rotation happens before the actual key is removed from the keystore cache.
mosip.esignet.cache.expire-in-seconds={'challenge_generated': ${mosip.signup.unauthenticated.txn.timeout},\
'challenge_verified': ${mosip.signup.verified.txn.timeout},\
'status_check': ${mosip.signup.status-check.txn.timeout}, \
'blocked_identifier': ${mosip.signup.generate-challenge.blocked.timeout},\
'keystore' : 10, \
'keystore' : 600, \
'key_alias' : ${mosip.signup.verified.txn.timeout} }

## ------------------------------------- Auth adapter ------------------------------------------------------------------
Expand Down Expand Up @@ -141,8 +145,8 @@ mosip.signup.sms-notification-template.send-otp.khm=4Z6U4Z+S4Z6a4Z6+IHtjaGFsbGVu
mosip.signup.sms-notification-template.send-otp.eng=Use {challenge} to verify your KhID account.
mosip.signup.sms-notification-template.registration.khm=4Z6i4Z+S4Z6T4Z6A4Z6U4Z624Z6T4Z6F4Z674Z+H4Z6I4Z+S4Z6Y4Z+E4Z+H4Z6C4Z6O4Z6T4Z64IEtoSUQg4Z6K4Z+E4Z6Z4Z6H4Z+E4Z6C4Z6H4Z+Q4Z6Z4Z+U
mosip.signup.sms-notification-template.registration.eng=You successfully registered to KhID account.
mosip.signup.sms-notification-template.forgot-passsword.khm=4Z6i4Z+S4Z6T4Z6A4Z6U4Z624Z6T4Z6V4Z+S4Z6b4Z624Z6f4Z+L4Z6U4Z+S4Z6K4Z684Z6a4Z6W4Z624Z6A4Z+S4Z6Z4Z6f4Z6Y4Z+S4Z6E4Z624Z6P4Z+LIEtoSUQg4Z6K4Z+E4Z6Z4Z6H4Z+E4Z6C4Z6H4Z+Q4Z6Z4Z+U
mosip.signup.sms-notification-template.forgot-passsword.eng=You successfully changed KhID password.
mosip.signup.sms-notification-template.forgot-password.khm=4Z6i4Z+S4Z6T4Z6A4Z6U4Z624Z6T4Z6V4Z+S4Z6b4Z624Z6f4Z+L4Z6U4Z+S4Z6K4Z684Z6a4Z6W4Z624Z6A4Z+S4Z6Z4Z6f4Z6Y4Z+S4Z6E4Z624Z6P4Z+LIEtoSUQg4Z6K4Z+E4Z6Z4Z6H4Z+E4Z6C4Z6H4Z+Q4Z6Z4Z+U
mosip.signup.sms-notification-template.forgot-password.eng=You successfully changed KhID password.

#------------------------------------------ Others ---------------------------------------------------------------------
logging.level.io.mosip.signup=DEBUG
1 change: 1 addition & 0 deletions signup-service/src/main/resources/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ invalid_challenge_channel=Invalid Challenge channel provided.
invalid_no_of_challenges=Null or empty authentication challenges not allowed.
invalid_auth_factor_type=Null or empty authentication factor type not allowed.
invalid_challenge=Invalid Authentication challenge provided.
crypto_error=Internal Error, Please try again.
14 changes: 9 additions & 5 deletions signup-service/src/test/resources/application-test.properties
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ mosip.signup.identifier.regex=^\\+855[1-9]\\d{7,8}$
mosip.signup.identifier.prefix=+855
mosip.signup.supported-languages={'khm','eng'}
mosip.signup.password.pattern=^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[\\x5F\\W])(?=.{8,20})[a-zA-Z0-9\\x5F\\W]{8,20}$
mosip.signup.fullname.pattern=^[\\u1780-\\u17FF\\u19E0-\\u19FF\\u1A00-\\u1A9F\\u0020]{1,30}$
mosip.signup.password.max-length=20
mosip.signup.password.min-length=6
mosip.signup.generate-challenge.blocked.timeout=300
mosip.signup.challenge.timeout=60
mosip.signup.audit.description.max-length=2048
mosip.signup.password.min-length=8
mosip.signup.fullname.pattern=^[\\u1780-\\u17FF\\u19E0-\\u19FF\\u1A00-\\u1A9F\\u0020]{1,30}$

## Time given to generate and verify the challenge in seconds.
## Default resend delay is 60 seconds, with 3 attempts, so 60*3=180 seconds.
Expand All @@ -55,7 +58,7 @@ mosip.signup.supported.challenge-types={'OTP', 'KBA'}
mosip.signup.supported.challenge.otp.length=6

## ------------------------------------- Cache configuration -----------------------------------------------------------

mosip.signup.cache.symmetric-algorithm-name=AES/CFB/PKCS5Padding
spring.cache.type=simple

#spring.cache.type=redis
Expand All @@ -70,12 +73,13 @@ mosip.esignet.cache.size={'challenge_generated': 200, \
'status_check': 200,\
'blocked_identifier':2000,\
'keystore' : 5, \
'key_alias' : 200 }
'key_alias' : 1 }

mosip.esignet.cache.expire-in-seconds={'challenge_generated': ${mosip.signup.unauthenticated.txn.timeout},\
'challenge_verified': ${mosip.signup.verified.txn.timeout},\
'status_check': ${mosip.signup.status-check.txn.timeout}, \
'blocked_identifier': ${mosip.signup.generate-challenge.blocked.timeout},\
'keystore' : 10, \
'keystore' : 600, \
'key_alias' : ${mosip.signup.verified.txn.timeout} }

## ------------------------------------- Auth adapter ------------------------------------------------------------------
Expand Down
6 changes: 3 additions & 3 deletions signup-ui/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@
"not_registered": "Not Registered",
"reset_pwd_failed": "Reset Password Failed",
"identifier_already_registered": "Identifier already registered",
"identifier_not_found": "Identifier not found",
"invalid_kba_challenge": "Invalid Fullname challenge",
"kba_challenge_not_found": "Fullname challenge not found",
"identifier_not_found": "Invalid number or name. Please enter a registered mobile number and full name.",
"invalid_kba_challenge": "Invalid number or name. Please enter a registered mobile number and full name.",
"kba_challenge_not_found": "Invalid number or name. Please enter a registered mobile number and full name.",
"identity_inactive": "Identifier inactive",
"fetch_identity_failed": "Fetch Identifier Failed",
"challenge_format_and_type_mismatch": "Challenge format and type mismatch",
Expand Down
8 changes: 4 additions & 4 deletions signup-ui/public/locales/km.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,12 @@
"not_registered": "មិនបានចុះឈ្មោះ",
"reset_pwd_failed": "ការកំណត់ពាក្យសម្ងាត់ឡើងវិញបានបរាជ័យ",
"identifier_already_registered": "អត្តសញ្ញាណបានចុះឈ្មោះរួចហើយ",
"identifier_not_found": "រកមិនឃើញអត្តសញ្ញាណ",
"invalid_kba_challenge": "ឈ្មោះមិនត្រឹមត្រូវ",
"kba_challenge_not_found": "រកមិនឃើញឈ្មោះ",
"identifier_not_found": "លេខទូរស័ព្ទ ឬឈ្មោះមិនត្រឹមត្រូវ។ សូមបញ្ចូលលេខទូរស័ព្ទនិងឈ្មោះដែលបានចុះឈ្មោះ។",
"invalid_kba_challenge": "លេខទូរស័ព្ទ ឬឈ្មោះមិនត្រឹមត្រូវ។ សូមបញ្ចូលលេខទូរស័ព្ទនិងឈ្មោះដែលបានចុះឈ្មោះ។",
"kba_challenge_not_found": "លេខទូរស័ព្ទ ឬឈ្មោះមិនត្រឹមត្រូវ។ សូមបញ្ចូលលេខទូរស័ព្ទនិងឈ្មោះដែលបានចុះឈ្មោះ។",
"identity_inactive": "អត្តសញ្ញាណអសកម្ម",
"fetch_identity_failed": "ទាញ​យក​លេខ​សម្គាល់​បាន​បរាជ័យ",
"challenge_format_and_type_mismatch": "ទម្រង់និងប្រភេទមិនត្រូវគ្នា",
"challenge_format_and_type_mismatch": "លេខទូរស័ព្ទ ឬឈ្មោះមិនត្រឹមត្រូវ។ សូមបញ្ចូលលេខទូរស័ព្ទនិងឈ្មោះដែលបានចុះឈ្មោះ។",
"knowledgebase_mismatch": "លេខទូរស័ព្ទ ឬឈ្មោះមិនត្រឹមត្រូវ។ សូមបញ្ចូលលេខទូរស័ព្ទនិងឈ្មោះដែលបានចុះឈ្មោះ។",
"IDR-IDC-001": "រកមិនឃើញតម្លៃដែលបានបញ្ចូល",
"IDR-IDC-002": "តម្លៃដែលបានបញ្ចូលមិនត្រឹមត្រូវ",
Expand Down
6 changes: 5 additions & 1 deletion signup-ui/src/components/ui/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
FormProvider,
useFormContext,
} from "react-hook-form";
import { useTranslation } from "react-i18next";

import { Label } from "~components/ui/label";
import { cn } from "~utils/cn";
Expand Down Expand Up @@ -146,7 +147,10 @@ const FormMessage = React.forwardRef<
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField();
const body = error ? String(error?.message) : children;
const { t } = useTranslation();

// @ts-ignore
const body: React.ReactNode = error ? t(String(error.message)) : children;

if (!body) {
return null;
Expand Down
3 changes: 2 additions & 1 deletion signup-ui/src/pages/ResetPasswordPage/Otp/Otp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
ResetPasswordForm,
SettingsDto,
VerifyChallengeRequestDto,
ResetPasswordPossibleInvalid,
} from "~typings/types";

import { resetPasswordFormDefaultValues } from "../ResetPasswordPage";
Expand Down Expand Up @@ -203,7 +204,7 @@ export const Otp = ({ methods, settings }: OtpProps) => {
onSuccess: ({ errors }) => {
if (errors.length > 0) {
if (
["invalid_transaction", "knowledgebase_mismatch"].includes(
["invalid_transaction", ...ResetPasswordPossibleInvalid].includes(
errors[0].errorCode
)
) {
Expand Down
15 changes: 7 additions & 8 deletions signup-ui/src/pages/ResetPasswordPage/ResetPasswordPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
validatePassword,
validateUsername,
} from "~pages/shared/validation";
import { ResetPasswordForm, SettingsDto } from "~typings/types";
import { ResetPasswordForm, ResetPasswordPossibleInvalid, SettingsDto } from "~typings/types";

import Otp from "./Otp";
import ResetPassword from "./ResetPassword";
Expand Down Expand Up @@ -59,21 +59,20 @@ export const ResetPasswordPage = ({ settings }: ResetPasswordPageProps) => {
() => [
// Step 1 - UserInfo
yup.object({
username: validateUsername(settings, t),
fullname: validateFullName(settings, t),
captchaToken: validateCaptchaToken(t),
username: validateUsername(settings),
fullname: validateFullName(settings),
captchaToken: validateCaptchaToken(),
}),
// Step 2 - Otp
yup.object({
otp: validateOtp(settings),
}),
// Step 3 - ResetPassword
yup.object({
newPassword: validatePassword(settings, t),
newPassword: validatePassword(settings),
confirmNewPassword: validateConfirmPassword(
"newPassword",
settings,
t,
false
),
}),
Expand Down Expand Up @@ -108,7 +107,7 @@ export const ResetPasswordPage = ({ settings }: ResetPasswordPageProps) => {
if (
step === ResetPasswordStep.ResetPasswordConfirmation ||
(criticalError &&
["invalid_transaction", "knowledgebase_mismatch"].includes(
["invalid_transaction", ...ResetPasswordPossibleInvalid].includes(
criticalError.errorCode
))
)
Expand Down Expand Up @@ -149,7 +148,7 @@ export const ResetPasswordPage = ({ settings }: ResetPasswordPageProps) => {
return (
<>
{criticalError &&
["invalid_transaction", "knowledgebase_mismatch"].includes(
["invalid_transaction", ...ResetPasswordPossibleInvalid].includes(
criticalError.errorCode
) && <ResetPasswordPopover />}
<Form {...methods}>
Expand Down
Loading

0 comments on commit 3107f71

Please sign in to comment.