mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 12:27:28 +00:00
feat: use hash as pw & move to symmetric encrpytion
This commit is contained in:
@@ -224,76 +224,6 @@ const decryptSymmetric = ({ ciphertext, iv, tag, key }: DecryptSymmetricProps):
|
||||
return plaintext;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return new base64, NaCl, public-secret key pair for signing.
|
||||
* @returns {Object} obj
|
||||
* @returns {String} obj.publicKey - base64, NaCl, public key
|
||||
* @returns {String} obj.secretKey - base64, NaCl, secret key
|
||||
*/
|
||||
const generateSignKeyPair = (): {
|
||||
publicKey: string;
|
||||
secretKey: string;
|
||||
} => {
|
||||
const pair = nacl.sign.keyPair();
|
||||
|
||||
return {
|
||||
publicKey: nacl.util.encodeBase64(pair.publicKey),
|
||||
secretKey: nacl.util.encodeBase64(pair.secretKey)
|
||||
};
|
||||
};
|
||||
|
||||
type SignAsymmetricProps = {
|
||||
message: string;
|
||||
privateKey: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns asymmetrically signed [message] using [privateKey]
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.message - message to sign
|
||||
* @param {String} obj.privateKey - base64-encoded private key
|
||||
* @returns {String} signedMessage - base64-encoded signed message
|
||||
*/
|
||||
const signAssymmetric = ({ message, privateKey }: SignAsymmetricProps): string => {
|
||||
let signedMessage;
|
||||
try {
|
||||
signedMessage = nacl.sign(nacl.util.decodeUTF8(message), nacl.util.decodeBase64(privateKey));
|
||||
} catch (err) {
|
||||
console.log("Failed to sign message", err);
|
||||
process.exit(1);
|
||||
}
|
||||
return nacl.util.encodeBase64(signedMessage);
|
||||
};
|
||||
|
||||
type OpenSignedAsymmetricProps = {
|
||||
signedMessage: string;
|
||||
publicKey: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns asymmetrically decrypted [message] using [publicKey]
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.signedMessage - signed message to decrypt
|
||||
* @param {String} obj.publicKey - base64-encoded public key
|
||||
* @returns {String} signedMessage - base64-encoded decrypted message
|
||||
*/
|
||||
const openSignedAssymmetric = ({ signedMessage, publicKey }: OpenSignedAsymmetricProps): string => {
|
||||
let originalMessage;
|
||||
try {
|
||||
originalMessage = nacl.sign.open(
|
||||
nacl.util.decodeBase64(signedMessage),
|
||||
nacl.util.decodeBase64(publicKey)
|
||||
);
|
||||
if (!originalMessage) {
|
||||
throw new Error("Signature verification failed");
|
||||
}
|
||||
originalMessage = nacl.util.encodeUTF8(originalMessage);
|
||||
} catch (err) {
|
||||
console.log("Failed to verify signature", err);
|
||||
}
|
||||
return originalMessage;
|
||||
};
|
||||
|
||||
export {
|
||||
decryptAssymmetric,
|
||||
decryptSymmetric,
|
||||
@@ -301,8 +231,5 @@ export {
|
||||
encryptAssymmetric,
|
||||
encryptSymmetric,
|
||||
generateKeyPair,
|
||||
generateSignKeyPair,
|
||||
openSignedAssymmetric,
|
||||
signAssymmetric,
|
||||
verifyPrivateKey
|
||||
};
|
||||
|
||||
@@ -16,15 +16,17 @@ export const useGetSharedSecrets = () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetActiveSharedSecretById = (id: string) => {
|
||||
export const useGetActiveSharedSecretByIdAndHashedHex = (id: string, hashedHex: string) => {
|
||||
return useQuery<TViewSharedSecretResponse, [string]>({
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TViewSharedSecretResponse>(
|
||||
`/api/v1/secret-sharing/public/${id}`
|
||||
`/api/v1/secret-sharing/public/${id}?hashedHex=${hashedHex}`
|
||||
);
|
||||
return {
|
||||
name: data.name,
|
||||
signedValue: data.signedValue,
|
||||
encryptedValue: data.encryptedValue,
|
||||
iv: data.iv,
|
||||
tag: data.tag,
|
||||
expiresAt: data.expiresAt
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
export type TSharedSecret = {
|
||||
id: string;
|
||||
name: string;
|
||||
signedValue: string;
|
||||
encryptedValue: string;
|
||||
iv: string;
|
||||
tag: string;
|
||||
hashedHex: string;
|
||||
userId: string;
|
||||
expiresAt: Date;
|
||||
createdAt: Date;
|
||||
@@ -10,13 +13,18 @@ export type TSharedSecret = {
|
||||
|
||||
export type TCreateSharedSecretRequest = {
|
||||
name: string;
|
||||
signedValue: string;
|
||||
encryptedValue: string;
|
||||
iv: string;
|
||||
tag: string;
|
||||
hashedHex: string;
|
||||
expiresAt: Date;
|
||||
};
|
||||
|
||||
export type TViewSharedSecretResponse = {
|
||||
name: string;
|
||||
signedValue: string;
|
||||
encryptedValue: string;
|
||||
iv: string;
|
||||
tag: string;
|
||||
expiresAt: Date;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import crypto from "crypto";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons";
|
||||
@@ -8,8 +10,7 @@ import * as yup from "yup";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import {
|
||||
generateSignKeyPair,
|
||||
signAssymmetric
|
||||
encryptSymmetric,
|
||||
} from "@app/components/utilities/cryptography/crypto";
|
||||
import {
|
||||
Button,
|
||||
@@ -91,7 +92,7 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
const { currentOrg } = useOrganization();
|
||||
const [newSharedSecret, setnewSharedSecret] = useState("");
|
||||
const hasSharedSecret = Boolean(newSharedSecret);
|
||||
const [isUrlCopied,, setIsUrlCopied] = useTimedReset<boolean>({
|
||||
const [isUrlCopied, , setIsUrlCopied] = useTimedReset<boolean>({
|
||||
initialState: false,
|
||||
});
|
||||
|
||||
@@ -109,12 +110,14 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
try {
|
||||
if (!currentOrg?.id) return;
|
||||
|
||||
const signingKeyPair = generateSignKeyPair();
|
||||
const signedMessage = signAssymmetric({
|
||||
message: value,
|
||||
privateKey: signingKeyPair.secretKey
|
||||
const key = crypto.randomBytes(16).toString("hex");
|
||||
const hashedHex = crypto.createHash("sha256").update(key).digest("hex");
|
||||
const { ciphertext, iv, tag } = encryptSymmetric({
|
||||
plaintext: value,
|
||||
key
|
||||
});
|
||||
|
||||
|
||||
const expiresAt = new Date();
|
||||
const updateExpiresAt = expirationUnitsAndActions.find(
|
||||
(item) => item.unit === expiresInUnit
|
||||
@@ -125,13 +128,14 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
|
||||
const { id } = await createSharedSecret.mutateAsync({
|
||||
name,
|
||||
signedValue: signedMessage,
|
||||
encryptedValue: ciphertext,
|
||||
iv,
|
||||
tag,
|
||||
hashedHex,
|
||||
expiresAt,
|
||||
});
|
||||
setnewSharedSecret(
|
||||
`${window.location.origin}/shared/secret/${id}?key=${encodeURIComponent(
|
||||
signingKeyPair.publicKey
|
||||
)}`
|
||||
`${window.location.origin}/shared/secret/${id}?key=${encodeURIComponent(hashedHex)}-${encodeURIComponent(key)}`
|
||||
);
|
||||
|
||||
createNotification({
|
||||
@@ -195,10 +199,10 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
errorText={error?.message}
|
||||
>
|
||||
<SecretInput
|
||||
isVisible
|
||||
{...field}
|
||||
containerClassName="py-1.5 rounded-md transition-all group-hover:mr-2 text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-mineshaft-900 px-2"
|
||||
/>
|
||||
isVisible
|
||||
{...field}
|
||||
containerClassName="py-1.5 rounded-md transition-all group-hover:mr-2 text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-mineshaft-900 px-2"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -4,15 +4,16 @@ import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import { openSignedAssymmetric } from "@app/components/utilities/cryptography/crypto";
|
||||
import { decryptSymmetric } from "@app/components/utilities/cryptography/crypto";
|
||||
import { useTimedReset } from "@app/hooks";
|
||||
import { useGetActiveSharedSecretById } from "@app/hooks/api/secretSharing";
|
||||
import { useGetActiveSharedSecretByIdAndHashedHex } from "@app/hooks/api/secretSharing";
|
||||
|
||||
import { DragonMainImage, SecretTable } from "./components";
|
||||
|
||||
export const ShareSecretPublicPage = () => {
|
||||
const router = useRouter();
|
||||
const { id, key: urlEncodedPublicKey } = router.query;
|
||||
const [hashedHex, key] = urlEncodedPublicKey!.toString().split("-");
|
||||
|
||||
const publicKey = decodeURIComponent(urlEncodedPublicKey as string);
|
||||
useEffect(() => {
|
||||
@@ -21,12 +22,14 @@ export const ShareSecretPublicPage = () => {
|
||||
}
|
||||
}, [id, publicKey]);
|
||||
|
||||
const { isLoading, data } = useGetActiveSharedSecretById(id as string);
|
||||
const { isLoading, data } = useGetActiveSharedSecretByIdAndHashedHex(id as string, hashedHex as string );
|
||||
const decryptedSecret = useMemo(() => {
|
||||
if (data && data.signedValue && publicKey) {
|
||||
const res = openSignedAssymmetric({
|
||||
signedMessage: data.signedValue,
|
||||
publicKey: publicKey as string
|
||||
if (data && data.encryptedValue && publicKey) {
|
||||
const res = decryptSymmetric({
|
||||
ciphertext: data.encryptedValue,
|
||||
iv: data.iv,
|
||||
tag: data.tag,
|
||||
key,
|
||||
});
|
||||
return res;
|
||||
}
|
||||
@@ -34,7 +37,7 @@ export const ShareSecretPublicPage = () => {
|
||||
}, [data, publicKey]);
|
||||
|
||||
const [timeLeft, setTimeLeft] = useState("");
|
||||
const [isUrlCopied,, setIsUrlCopied] = useTimedReset<boolean>({
|
||||
const [isUrlCopied, , setIsUrlCopied] = useTimedReset<boolean>({
|
||||
initialState: false,
|
||||
});
|
||||
|
||||
@@ -44,11 +47,11 @@ export const ShareSecretPublicPage = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const updateTimer = () => {
|
||||
if (data && data.expiresAt) {
|
||||
if (data && data.expiresAt) {
|
||||
const expirationTime = new Date(data.expiresAt).getTime();
|
||||
const currentTime = new Date().getTime();
|
||||
const timeDifference = expirationTime - currentTime;
|
||||
|
||||
|
||||
if (timeDifference < 0) {
|
||||
setTimeLeft("Expired");
|
||||
} else {
|
||||
@@ -59,7 +62,7 @@ export const ShareSecretPublicPage = () => {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const timer = setInterval(updateTimer, 1000);
|
||||
return () => clearInterval(timer);
|
||||
}, [data?.expiresAt]);
|
||||
|
||||
Reference in New Issue
Block a user