mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 19:28:16 +00:00
Merge pull request #4373 from Infisical/daniel/remove-srp-from-admin-signup
fix(srp): remove srp flow from admin signup
This commit is contained in:
@@ -583,16 +583,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
email: z.string().email().trim(),
|
email: z.string().email().trim(),
|
||||||
password: z.string().trim(),
|
password: z.string().trim(),
|
||||||
firstName: z.string().trim(),
|
firstName: z.string().trim(),
|
||||||
lastName: z.string().trim().optional(),
|
lastName: z.string().trim().optional()
|
||||||
protectedKey: z.string().trim(),
|
|
||||||
protectedKeyIV: z.string().trim(),
|
|
||||||
protectedKeyTag: z.string().trim(),
|
|
||||||
publicKey: z.string().trim(),
|
|
||||||
encryptedPrivateKey: z.string().trim(),
|
|
||||||
encryptedPrivateKeyIV: z.string().trim(),
|
|
||||||
encryptedPrivateKeyTag: z.string().trim(),
|
|
||||||
salt: z.string().trim(),
|
|
||||||
verifier: z.string().trim()
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
validateOverrides
|
validateOverrides
|
||||||
} from "@app/lib/config/env";
|
} from "@app/lib/config/env";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { generateUserSrpKeys, getUserPrivateKey } from "@app/lib/crypto/srp";
|
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
||||||
@@ -465,43 +464,15 @@ export const superAdminServiceFactory = ({
|
|||||||
return updatedServerCfg;
|
return updatedServerCfg;
|
||||||
};
|
};
|
||||||
|
|
||||||
const adminSignUp = async ({
|
const adminSignUp = async ({ lastName, firstName, email, password, ip, userAgent }: TAdminSignUpDTO) => {
|
||||||
lastName,
|
|
||||||
firstName,
|
|
||||||
email,
|
|
||||||
salt,
|
|
||||||
password,
|
|
||||||
verifier,
|
|
||||||
publicKey,
|
|
||||||
protectedKey,
|
|
||||||
protectedKeyIV,
|
|
||||||
protectedKeyTag,
|
|
||||||
encryptedPrivateKey,
|
|
||||||
encryptedPrivateKeyIV,
|
|
||||||
encryptedPrivateKeyTag,
|
|
||||||
ip,
|
|
||||||
userAgent
|
|
||||||
}: TAdminSignUpDTO) => {
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
|
|
||||||
const sanitizedEmail = email.trim().toLowerCase();
|
const sanitizedEmail = email.trim().toLowerCase();
|
||||||
const existingUser = await userDAL.findOne({ username: sanitizedEmail });
|
const existingUser = await userDAL.findOne({ username: sanitizedEmail });
|
||||||
if (existingUser) throw new BadRequestError({ name: "Admin sign up", message: "User already exists" });
|
if (existingUser) throw new BadRequestError({ name: "Admin sign up", message: "User already exists" });
|
||||||
|
|
||||||
const privateKey = await getUserPrivateKey(password, {
|
|
||||||
encryptionVersion: 2,
|
|
||||||
salt,
|
|
||||||
protectedKey,
|
|
||||||
protectedKeyIV,
|
|
||||||
protectedKeyTag,
|
|
||||||
encryptedPrivateKey,
|
|
||||||
iv: encryptedPrivateKeyIV,
|
|
||||||
tag: encryptedPrivateKeyTag
|
|
||||||
});
|
|
||||||
|
|
||||||
const hashedPassword = await crypto.hashing().createHash(password, appCfg.SALT_ROUNDS);
|
const hashedPassword = await crypto.hashing().createHash(password, appCfg.SALT_ROUNDS);
|
||||||
|
|
||||||
const { iv, tag, ciphertext, encoding } = crypto.encryption().symmetric().encryptWithRootEncryptionKey(privateKey);
|
|
||||||
const userInfo = await userDAL.transaction(async (tx) => {
|
const userInfo = await userDAL.transaction(async (tx) => {
|
||||||
const newUser = await userDAL.create(
|
const newUser = await userDAL.create(
|
||||||
{
|
{
|
||||||
@@ -519,25 +490,13 @@ export const superAdminServiceFactory = ({
|
|||||||
);
|
);
|
||||||
const userEnc = await userDAL.createUserEncryption(
|
const userEnc = await userDAL.createUserEncryption(
|
||||||
{
|
{
|
||||||
salt,
|
|
||||||
encryptionVersion: 2,
|
encryptionVersion: 2,
|
||||||
protectedKey,
|
|
||||||
protectedKeyIV,
|
|
||||||
protectedKeyTag,
|
|
||||||
publicKey,
|
|
||||||
encryptedPrivateKey,
|
|
||||||
iv: encryptedPrivateKeyIV,
|
|
||||||
tag: encryptedPrivateKeyTag,
|
|
||||||
verifier,
|
|
||||||
userId: newUser.id,
|
userId: newUser.id,
|
||||||
hashedPassword,
|
hashedPassword
|
||||||
serverEncryptedPrivateKey: ciphertext,
|
|
||||||
serverEncryptedPrivateKeyIV: iv,
|
|
||||||
serverEncryptedPrivateKeyTag: tag,
|
|
||||||
serverEncryptedPrivateKeyEncoding: encoding
|
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
return { user: newUser, enc: userEnc };
|
return { user: newUser, enc: userEnc };
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -587,26 +546,14 @@ export const superAdminServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
const { tag, encoding, ciphertext, iv } = crypto.encryption().symmetric().encryptWithRootEncryptionKey(password);
|
|
||||||
const encKeys = await generateUserSrpKeys(sanitizedEmail, password);
|
const hashedPassword = await crypto.hashing().createHash(password, appCfg.SALT_ROUNDS);
|
||||||
|
|
||||||
const userEnc = await userDAL.createUserEncryption(
|
const userEnc = await userDAL.createUserEncryption(
|
||||||
{
|
{
|
||||||
userId: newUser.id,
|
userId: newUser.id,
|
||||||
encryptionVersion: 2,
|
encryptionVersion: 2,
|
||||||
protectedKey: encKeys.protectedKey,
|
hashedPassword
|
||||||
protectedKeyIV: encKeys.protectedKeyIV,
|
|
||||||
protectedKeyTag: encKeys.protectedKeyTag,
|
|
||||||
publicKey: encKeys.publicKey,
|
|
||||||
encryptedPrivateKey: encKeys.encryptedPrivateKey,
|
|
||||||
iv: encKeys.encryptedPrivateKeyIV,
|
|
||||||
tag: encKeys.encryptedPrivateKeyTag,
|
|
||||||
salt: encKeys.salt,
|
|
||||||
verifier: encKeys.verifier,
|
|
||||||
serverEncryptedPrivateKeyEncoding: encoding,
|
|
||||||
serverEncryptedPrivateKeyTag: tag,
|
|
||||||
serverEncryptedPrivateKeyIV: iv,
|
|
||||||
serverEncryptedPrivateKey: ciphertext
|
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,17 +3,8 @@ import { TEnvConfig } from "@app/lib/config/env";
|
|||||||
export type TAdminSignUpDTO = {
|
export type TAdminSignUpDTO = {
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
publicKey: string;
|
|
||||||
salt: string;
|
|
||||||
lastName?: string;
|
lastName?: string;
|
||||||
verifier: string;
|
|
||||||
firstName: string;
|
firstName: string;
|
||||||
protectedKey: string;
|
|
||||||
protectedKeyIV: string;
|
|
||||||
protectedKeyTag: string;
|
|
||||||
encryptedPrivateKey: string;
|
|
||||||
encryptedPrivateKeyIV: string;
|
|
||||||
encryptedPrivateKeyTag: string;
|
|
||||||
ip: string;
|
ip: string;
|
||||||
userAgent: string;
|
userAgent: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
import Aes256Gcm from "@app/components/utilities/cryptography/aes-256-gcm";
|
|
||||||
import { deriveArgonKey } from "@app/components/utilities/cryptography/crypto";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Object} obj
|
|
||||||
* @param {Number} obj.encryptionVersion
|
|
||||||
* @param {String} obj.encryptedPrivateKey
|
|
||||||
* @param {String} obj.iv
|
|
||||||
* @param {String} obj.tag
|
|
||||||
* @param {String} obj.password
|
|
||||||
* @param {String} obj.salt
|
|
||||||
* @param {String} obj.protectedKey
|
|
||||||
* @param {String} obj.protectedKeyIV
|
|
||||||
* @param {String} obj.protectedKeyTag
|
|
||||||
*/
|
|
||||||
const decryptPrivateKeyHelper = async ({
|
|
||||||
encryptionVersion,
|
|
||||||
encryptedPrivateKey,
|
|
||||||
iv,
|
|
||||||
tag,
|
|
||||||
password,
|
|
||||||
salt,
|
|
||||||
protectedKey,
|
|
||||||
protectedKeyIV,
|
|
||||||
protectedKeyTag
|
|
||||||
}: {
|
|
||||||
encryptionVersion: number;
|
|
||||||
encryptedPrivateKey: string;
|
|
||||||
iv: string;
|
|
||||||
tag: string;
|
|
||||||
password: string;
|
|
||||||
salt: string;
|
|
||||||
protectedKey?: string;
|
|
||||||
protectedKeyIV?: string;
|
|
||||||
protectedKeyTag?: string;
|
|
||||||
}) => {
|
|
||||||
let privateKey;
|
|
||||||
try {
|
|
||||||
if (encryptionVersion === 1) {
|
|
||||||
privateKey = Aes256Gcm.decrypt({
|
|
||||||
ciphertext: encryptedPrivateKey,
|
|
||||||
iv,
|
|
||||||
tag,
|
|
||||||
secret: password
|
|
||||||
.slice(0, 32)
|
|
||||||
.padStart(32 + (password.slice(0, 32).length - new Blob([password]).size), "0")
|
|
||||||
});
|
|
||||||
} else if (encryptionVersion === 2 && protectedKey && protectedKeyIV && protectedKeyTag) {
|
|
||||||
const derivedKey = await deriveArgonKey({
|
|
||||||
password,
|
|
||||||
salt,
|
|
||||||
mem: 65536,
|
|
||||||
time: 3,
|
|
||||||
parallelism: 1,
|
|
||||||
hashLen: 32
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!derivedKey) throw new Error("Failed to generate derived key");
|
|
||||||
|
|
||||||
const key = Aes256Gcm.decrypt({
|
|
||||||
ciphertext: protectedKey,
|
|
||||||
iv: protectedKeyIV,
|
|
||||||
tag: protectedKeyTag,
|
|
||||||
secret: Buffer.from(derivedKey.hash)
|
|
||||||
});
|
|
||||||
|
|
||||||
// decrypt back the private key
|
|
||||||
privateKey = Aes256Gcm.decrypt({
|
|
||||||
ciphertext: encryptedPrivateKey,
|
|
||||||
iv,
|
|
||||||
tag,
|
|
||||||
secret: Buffer.from(key, "hex")
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new Error("Insufficient details to decrypt private key");
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
throw new Error("Failed to decrypt private key");
|
|
||||||
}
|
|
||||||
|
|
||||||
return privateKey;
|
|
||||||
};
|
|
||||||
|
|
||||||
export { decryptPrivateKeyHelper };
|
|
||||||
@@ -74,15 +74,6 @@ export type TCreateAdminUserDTO = {
|
|||||||
password: string;
|
password: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName?: string;
|
lastName?: string;
|
||||||
protectedKey: string;
|
|
||||||
protectedKeyTag: string;
|
|
||||||
protectedKeyIV: string;
|
|
||||||
encryptedPrivateKey: string;
|
|
||||||
encryptedPrivateKeyIV: string;
|
|
||||||
encryptedPrivateKeyTag: string;
|
|
||||||
publicKey: string;
|
|
||||||
verifier: string;
|
|
||||||
salt: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AdminGetOrganizationsFilters = {
|
export type AdminGetOrganizationsFilters = {
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
import crypto from "crypto";
|
|
||||||
|
|
||||||
import jsrp from "jsrp";
|
|
||||||
|
|
||||||
import Aes256Gcm from "@app/components/utilities/cryptography/aes-256-gcm";
|
|
||||||
import { deriveArgonKey, generateKeyPair } from "@app/components/utilities/cryptography/crypto";
|
|
||||||
|
|
||||||
export const generateUserPassKey = async (
|
|
||||||
email: string,
|
|
||||||
password: string,
|
|
||||||
fipsEnabled: boolean
|
|
||||||
) => {
|
|
||||||
// eslint-disable-next-line new-cap
|
|
||||||
const client = new jsrp.client();
|
|
||||||
|
|
||||||
const { publicKey, privateKey } = await generateKeyPair(fipsEnabled);
|
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
client.init({ username: email, password }, () => resolve(null));
|
|
||||||
});
|
|
||||||
const { salt, verifier } = await new Promise<{ salt: string; verifier: string }>(
|
|
||||||
(resolve, reject) => {
|
|
||||||
client.createVerifier((err, res) => {
|
|
||||||
if (err) return reject(err);
|
|
||||||
return resolve(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const derivedKey = await deriveArgonKey({
|
|
||||||
password,
|
|
||||||
salt,
|
|
||||||
mem: 65536,
|
|
||||||
time: 3,
|
|
||||||
parallelism: 1,
|
|
||||||
hashLen: 32
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!derivedKey) throw new Error("Failed to derive key from password");
|
|
||||||
|
|
||||||
const key = crypto.randomBytes(32);
|
|
||||||
|
|
||||||
// create encrypted private key by encrypting the private
|
|
||||||
// key with the symmetric key [key]
|
|
||||||
const {
|
|
||||||
ciphertext: encryptedPrivateKey,
|
|
||||||
iv: encryptedPrivateKeyIV,
|
|
||||||
tag: encryptedPrivateKeyTag
|
|
||||||
} = Aes256Gcm.encrypt({
|
|
||||||
text: privateKey,
|
|
||||||
secret: key
|
|
||||||
});
|
|
||||||
|
|
||||||
// create the protected key by encrypting the symmetric key
|
|
||||||
// [key] with the derived key
|
|
||||||
const {
|
|
||||||
ciphertext: protectedKey,
|
|
||||||
iv: protectedKeyIV,
|
|
||||||
tag: protectedKeyTag
|
|
||||||
} = Aes256Gcm.encrypt({
|
|
||||||
text: key.toString("hex"),
|
|
||||||
secret: Buffer.from(derivedKey.hash)
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
protectedKey,
|
|
||||||
protectedKeyTag,
|
|
||||||
protectedKeyIV,
|
|
||||||
encryptedPrivateKey,
|
|
||||||
encryptedPrivateKeyIV,
|
|
||||||
encryptedPrivateKeyTag,
|
|
||||||
publicKey,
|
|
||||||
verifier,
|
|
||||||
salt,
|
|
||||||
privateKey
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -12,7 +12,6 @@ import SecurityClient from "@app/components/utilities/SecurityClient";
|
|||||||
import { Button, ContentLoader, FormControl, Input } from "@app/components/v2";
|
import { Button, ContentLoader, FormControl, Input } from "@app/components/v2";
|
||||||
import { useServerConfig } from "@app/context";
|
import { useServerConfig } from "@app/context";
|
||||||
import { useCreateAdminUser, useSelectOrganization } from "@app/hooks/api";
|
import { useCreateAdminUser, useSelectOrganization } from "@app/hooks/api";
|
||||||
import { generateUserPassKey } from "@app/lib/crypto";
|
|
||||||
|
|
||||||
const formSchema = z
|
const formSchema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -48,17 +47,11 @@ export const SignUpPage = () => {
|
|||||||
// avoid multi submission
|
// avoid multi submission
|
||||||
if (isSubmitting) return;
|
if (isSubmitting) return;
|
||||||
try {
|
try {
|
||||||
const { privateKey, ...userPass } = await generateUserPassKey(
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
config.fipsEnabled
|
|
||||||
);
|
|
||||||
const res = await createAdminUser({
|
const res = await createAdminUser({
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName
|
||||||
...userPass
|
|
||||||
});
|
});
|
||||||
|
|
||||||
SecurityClient.setToken(res.token);
|
SecurityClient.setToken(res.token);
|
||||||
|
|||||||
Reference in New Issue
Block a user