373 lines
12 KiB
C++
373 lines
12 KiB
C++
// SPDX-License-Identifier: MPL-2.0
|
|
#include <exception>
|
|
#include <sstream>
|
|
extern "C" {
|
|
#include "api.h"
|
|
#include "argon2.h"
|
|
#include "sha3.h"
|
|
}
|
|
#undef str
|
|
#include <fstream>
|
|
#include <filesystem>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <string.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/prctl.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
using namespace std;
|
|
namespace fs=filesystem;
|
|
const map<string,string> azerty_to_qwerty={
|
|
{"a","q"},
|
|
{"A","Q"},
|
|
{"z","w"},
|
|
{"Z","W"},
|
|
{"q","a"},
|
|
{"Q","A"},
|
|
{"m",";"},
|
|
{"M",":"},
|
|
{"w","z"},
|
|
{"W","Z"},
|
|
{",","m"},
|
|
{"?","M"},
|
|
{"²","`"},
|
|
{"&","1"},
|
|
{"é","2"},
|
|
{"\"","3"},
|
|
{"'","4"},
|
|
{"(","5"},
|
|
{"-","6"},
|
|
{"è","7"},
|
|
{"_","8"},
|
|
{"ç","9"},
|
|
{"à","0"},
|
|
{"~","2"},
|
|
{"#","3"},
|
|
{"{","4"},
|
|
{"[","5"},
|
|
{"|","6"},
|
|
{"`","7"},
|
|
{"\\","8"},
|
|
{"^","9"},
|
|
{"@","0"},
|
|
{"1","!"},
|
|
{"2","@"},
|
|
{"3","#"},
|
|
{"4","$"},
|
|
{"5","%"},
|
|
{"6","^"},
|
|
{"7","&"},
|
|
{"8","*"},
|
|
{"9","("},
|
|
{"0",")"},
|
|
{";",","},
|
|
{".","<"},
|
|
{":","."},
|
|
{"/",">"},
|
|
{"!","/"},
|
|
{"§","?"},
|
|
{"<","\\"},
|
|
{">","|"},
|
|
{"ù","'"},
|
|
{"%","\""},
|
|
{"^","["},
|
|
{"¨","{"},
|
|
{"$","]"},
|
|
{"£","}"},
|
|
{"µ","|"}
|
|
};
|
|
void secure_erase(void *address,size_t size) {
|
|
explicit_bzero(address,size);
|
|
}
|
|
vector<uint8_t> get_urandom(size_t n) {
|
|
vector<uint8_t> out(n);
|
|
ifstream urandom("/dev/urandom",ios::binary);
|
|
if (!urandom.is_open()) {
|
|
throw runtime_error("open");
|
|
}
|
|
urandom.read(reinterpret_cast<char*>(out.data()),n);
|
|
if (urandom.gcount()!=static_cast<streamsize>(n)) {
|
|
throw runtime_error("error");
|
|
}
|
|
return out;
|
|
}
|
|
int main(int argc,char **argv) {
|
|
ofstream core("/proc/self/coredump_filter");
|
|
if (!core) {
|
|
cout<<"[Keygen] Error: can't setup security."<<endl;
|
|
return -1;
|
|
}
|
|
core<<"0";
|
|
core.close();
|
|
if (prctl(PR_SET_DUMPABLE,0,0,0,0)!=0) {
|
|
cout<<"[Keygen] Error: can't setup security."<<endl;
|
|
return -1;
|
|
}
|
|
struct rlimit rl_mem;
|
|
rl_mem.rlim_cur=(1<<31);
|
|
rl_mem.rlim_max=(1<<31);
|
|
if (setrlimit(RLIMIT_MEMLOCK,&rl_mem)!=0) {
|
|
cout<<"[Keygen] Error: can't setup security."<<endl;
|
|
return -1;
|
|
}
|
|
if (mlockall(MCL_CURRENT | MCL_FUTURE)!=0) {
|
|
cout<<"[Keygen] Error: can't setup security."<<endl;
|
|
return -1;
|
|
}
|
|
vector<string> files;
|
|
if (argc>1) {
|
|
for (int i=1;i<argc;++i) {
|
|
files.push_back(string(argv[i]));
|
|
}
|
|
} else {
|
|
cout<<"[Keygen] Error: no file provided."<<endl;
|
|
return -1;
|
|
}
|
|
for (int i=0;i<files.size();i++) {
|
|
if (!fs::exists(files[i])) {
|
|
cout<<"[Keygen] Error: file "<<files[i]<<" don't exist."<<endl;
|
|
return -1;
|
|
}
|
|
}
|
|
vector<unsigned char> fbuf;
|
|
vector<unsigned char> sigsbuf(files.size()*CRYPTO_BYTES);
|
|
vector<unsigned char> pksbuf(files.size()*CRYPTO_PUBLICKEYBYTES);
|
|
if (mlock(sigsbuf.data(),sigsbuf.size())!=0) {
|
|
cout<<"[Keygen] Error: can't setup security."<<endl;
|
|
return -1;
|
|
}
|
|
if (mlock(pksbuf.data(),pksbuf.size())!=0) {
|
|
cout<<"[Keygen] Error: can't setup security."<<endl;
|
|
return -1;
|
|
}
|
|
unsigned char pk[CRYPTO_PUBLICKEYBYTES];
|
|
unsigned char sk[CRYPTO_SECRETKEYBYTES];
|
|
mlock(pk,sizeof(pk));
|
|
mlock(sk,sizeof(sk));
|
|
madvise(pk,sizeof(pk),MADV_DONTDUMP);
|
|
madvise(sk,sizeof(sk),MADV_DONTDUMP);
|
|
unsigned char sig[CRYPTO_BYTES];
|
|
mlock(sig,sizeof(sig));
|
|
madvise(sig,sizeof(sig),MADV_DONTDUMP);
|
|
for (int i=0;i<files.size();++i) {
|
|
ifstream file_content(files[i],ios::binary);
|
|
if (!file_content) {
|
|
cout<<"[Keygen] Error: can't read file: "<<files[i]<<endl;
|
|
return -1;
|
|
}
|
|
file_content.seekg(0,ios::end);
|
|
streamsize size=file_content.tellg();
|
|
file_content.seekg(0,ios::beg);
|
|
fbuf.resize(size);
|
|
if (!file_content.read(reinterpret_cast<char*>(fbuf.data()),size)) {
|
|
cout<<"[Keygen] Error: can't read file: "<<files[i]<<endl;
|
|
return -1;
|
|
}
|
|
if (crypto_sign_keypair(pk,sk)) {
|
|
cout<<"[Keygen] Error: can't generate keypair for file: "<<files[i]<<endl;
|
|
return -1;
|
|
}
|
|
size_t siglen;
|
|
if (size<1024*1024) {
|
|
if (crypto_sign_signature(sig,&siglen,fbuf.data(),fbuf.size(),sk)) {
|
|
cout<<"[Keygen] Error: can't generate signature for file: "<<files[i]<<endl;
|
|
secure_erase(pk,sizeof(pk));
|
|
secure_erase(sk,sizeof(sk));
|
|
secure_erase(sig,siglen);
|
|
return -1;
|
|
}
|
|
} else {
|
|
vector<unsigned char> hash(64);
|
|
sha3(fbuf.data(),size,hash.data(),hash.size());
|
|
if (crypto_sign_signature(sig,&siglen,hash.data(),hash.size(),sk)) {
|
|
cout<<"[Keygen] Error: can't generate signature for file: "<<files[i]<<endl;
|
|
secure_erase(pk,sizeof(pk));
|
|
secure_erase(sk,sizeof(sk));
|
|
secure_erase(sig,siglen);
|
|
return -1;
|
|
}
|
|
}
|
|
if (siglen!=CRYPTO_BYTES) {
|
|
cout<<"[Keygen] Error: signature isn't the expected size for file: "<<files[i]<<endl;
|
|
secure_erase(pk,sizeof(pk));
|
|
secure_erase(sk,sizeof(sk));
|
|
secure_erase(sig,siglen);
|
|
return -1;
|
|
}
|
|
memcpy(&pksbuf[i*CRYPTO_PUBLICKEYBYTES],pk,CRYPTO_PUBLICKEYBYTES);
|
|
memcpy(&sigsbuf[i*CRYPTO_BYTES],sig,CRYPTO_BYTES);
|
|
secure_erase(sk,sizeof(sk));
|
|
secure_erase(pk,sizeof(pk));
|
|
secure_erase(sig,siglen);
|
|
if (!fbuf.empty()) {
|
|
secure_erase(fbuf.data(),fbuf.size());
|
|
fbuf.clear();
|
|
}
|
|
}
|
|
if (!filesystem::exists("./sign")) {
|
|
filesystem::create_directory("./sign");
|
|
}
|
|
for (int i=0;i<files.size();i++) {
|
|
ofstream sigfile("./sign/"+fs::path(files[i]).filename().string()+".sig",ios::binary);
|
|
if (!sigfile) {
|
|
cout<<"[Keygen] Error: can't open signature file for file: "<<files[i]<<endl;
|
|
return -1;
|
|
}
|
|
sigfile.write(reinterpret_cast<const char*>(sigsbuf.data()+i*CRYPTO_BYTES),CRYPTO_BYTES);
|
|
sigfile.close();
|
|
}
|
|
secure_erase(sigsbuf.data(),sigsbuf.size());
|
|
cout<<"[Keygen] You are about to define a boot password. This password will be asked everytime you boot this instance of Vystem."<<endl;
|
|
cout<<"[Keygen] It will be used to sign every public key on this instance of Vystem. Make sure it's secure. Here are the requirements:\n - It should be at least 12 characters.\n - Password can only include ASCII characters.\n - Password shouldn't be longer than 512 characters.\n - DO NOT use the numerical pad at all. It's not supported in the EFI environment."<<endl;
|
|
cout<<"[Keygen] Leave empty for us to generate entropy as a password."<<endl;
|
|
char* pwd=getpass("[Keygen] Enter boot password: ");
|
|
string password;
|
|
if (strlen(pwd)==0) {
|
|
cout<<"[Keygen] You asked for entropy. It will be provided as hexadecimal characters. Please enter the amount of hexadecimal characters you want."<<endl;
|
|
cout<<"[Keygen] The amount of hex characters shouldn't be bigger than 512."<<endl;
|
|
cout<<"[Keygen] Amount of hex characters (should be a multiple of 2 and at least 16 characters to be a minimum secure): ";
|
|
string amount;
|
|
getline(cin,amount);
|
|
uint64_t num;
|
|
try {
|
|
num=stoi(amount);
|
|
} catch (const exception e) {
|
|
cout<<"[Keygen] Value entered isn't a number. Using default amount of 16 characters."<<endl;
|
|
num=8;
|
|
}
|
|
if (num%2!=0) {
|
|
cout<<"[Keygen] Number entered isn't a multiple of 2. Using default amount of 16 characters."<<endl;
|
|
num=8;
|
|
} else if (num<513) {
|
|
num=num/2;
|
|
} else {
|
|
cout<<"[Keygen] Error: password is too long."<<endl;
|
|
return -1;
|
|
}
|
|
vector<uint8_t> passhex;
|
|
passhex.resize(num);
|
|
try {
|
|
passhex=get_urandom(num);
|
|
} catch (const exception e) {
|
|
if (string(e.what())=="open") {
|
|
cout<<"[Keygen] Error: can't open secure source of randomness."<<endl;
|
|
return -1;
|
|
} else if (string(e.what())=="error") {
|
|
cout<<"[Keygen] Error: can't read enought secure entropy."<<endl;
|
|
return -1;
|
|
}
|
|
}
|
|
ostringstream oss;
|
|
oss<<hex<<setfill('0');
|
|
for (uint8_t byte:passhex) {
|
|
oss<<setw(2)<<std::uppercase<<static_cast<int>(byte);
|
|
}
|
|
password=oss.str();
|
|
secure_erase(passhex.data(),passhex.size());
|
|
} else if (strlen(pwd)<=512){
|
|
password=string(pwd);
|
|
char* confirm_pwd=getpass("[Keygen] Confirm password: ");
|
|
string confirm_password(confirm_pwd);
|
|
if (confirm_password!=password) {
|
|
cout<<"[Keygen] Error: password don't correspond. Please try again."<<endl;
|
|
return -1;
|
|
}
|
|
secure_erase(confirm_pwd,strlen(confirm_pwd));
|
|
} else {
|
|
cout<<"[Keygen] Error: password is too long."<<endl;
|
|
return -1;
|
|
}
|
|
string pass=password;
|
|
string rep;
|
|
cout<<"[Keygen] Do you use a keyboard with another layout than QWERTY (y/N) ? : ";
|
|
getline(cin,rep);
|
|
if (rep=="y") {
|
|
cout<<"[Keygen] Do you want to automatically translate your password into what it should look like into a QWERTY layout (y/N) ?"<<endl;
|
|
cout<<"[Keygen] Please note that for the moment, only AZERTY to QWERTY is supported."<<endl;
|
|
cout<<"[Keygen] Your answer : ";
|
|
getline(cin,rep);
|
|
if (rep=="y") {
|
|
for (int i=0;i<password.size();i++) {
|
|
try {
|
|
password[i]=azerty_to_qwerty.at(string(1,password.at(i)))[0];
|
|
} catch (const exception e) {
|
|
continue;
|
|
}
|
|
}
|
|
} else {
|
|
cout<<"[Keygen] Skipping password translation."<<endl;
|
|
}
|
|
} else {
|
|
cout<<"[Keygen] No password translation needed."<<endl;
|
|
}
|
|
cout<<"[Keygen] Your password is set to be: "<<pass<<endl;
|
|
cout<<"[Keygen] The password that will be hashed is: "<<password<<endl;
|
|
cout<<"[Keygen] If you have a keyboard layout other than QWERTY and didn't selected to translate it, you will have to type the second provided password."<<endl;
|
|
cout<<"[Keygen] If you have a keyboard layout other than QWERTY and selected to translate it, just type it like you would do in your keyboard layout."<<endl;
|
|
cout<<"[Keygen] If you don't remember it or write it down somewhere else, you will not be able to boot into your system."<<endl;
|
|
cout<<"[Keygen] Press enter to continue.";
|
|
string dummy;
|
|
getline(cin,dummy);
|
|
cout<<endl;
|
|
secure_erase(pwd,strlen(pwd));
|
|
for (unsigned char c:password) {
|
|
if (c>=0x80) {
|
|
cout<<"[Keygen] Error: one character isn't ASCII."<<endl;
|
|
return -1;
|
|
}
|
|
}
|
|
vector<unsigned char> passutf16(password.size()*2);
|
|
for (int i=0;i<password.size();++i) {
|
|
passutf16[i*2]=password[i];
|
|
passutf16[i*2+1]=0x00;
|
|
}
|
|
vector<uint8_t> salt(32);
|
|
salt=get_urandom(32);
|
|
vector<unsigned char> seed;
|
|
seed.resize(CRYPTO_SEEDBYTES);
|
|
if (argon2id_hash_raw(3,262144,1,passutf16.data(),passutf16.size(),salt.data(),salt.size(),seed.data(),CRYPTO_SEEDBYTES)!=0) {
|
|
cout<<"[Keygen] Error: can't generate seed from password."<<endl;
|
|
return -1;
|
|
}
|
|
if (crypto_sign_seed_keypair(pk,sk,seed.data())!=0) {
|
|
cout<<"[Keygen] Error: can't generate keys from seed."<<endl;
|
|
return -1;
|
|
}
|
|
secure_erase(pk,sizeof(pk));
|
|
secure_erase(seed.data(),seed.size());
|
|
size_t siglen;
|
|
if (crypto_sign_signature(sig,&siglen,pksbuf.data(),pksbuf.size(),sk)!=0) {
|
|
cout<<"[Keygen] Error: can't generate signature from all publics keys."<<endl;
|
|
return -1;
|
|
}
|
|
secure_erase(sk,sizeof(sk));
|
|
ofstream keyfile("key.h");
|
|
keyfile<<"#ifndef BP_LIB_KEY_H\n#define BP_LIB_KEY_H\n#include <Uefi.h>\nUINT8 bp_key_mainsig["<<to_string(CRYPTO_BYTES)<<"]={";
|
|
for (int i=0;i<CRYPTO_BYTES;++i) {
|
|
keyfile<<"0x"<<hex<<setw(2)<<setfill('0')<<(int)sig[i];
|
|
if (i+1!=CRYPTO_BYTES) keyfile<<",";
|
|
}
|
|
keyfile<<"};\nUINT8 bp_key_pkblob["<<to_string(pksbuf.size())<<"]={";
|
|
for (int i=0;i<pksbuf.size();++i) {
|
|
keyfile<<"0x"<<hex<<setw(2)<<setfill('0')<<(int)pksbuf[i];
|
|
if (i+1!=pksbuf.size()) keyfile<<",";
|
|
}
|
|
keyfile<<"};\nUINT8 bp_key_pwdsalt["<<to_string(salt.size())<<"]={";
|
|
for (int i=0;i<salt.size();++i) {
|
|
keyfile<<"0x"<<hex<<setw(2)<<setfill('0')<<(int)salt[i];
|
|
if (i+1!=salt.size()) keyfile<<",";
|
|
}
|
|
keyfile<<"};\nSTATIC const CHAR16 * const bp_key_files["<<files.size()<<"]={";
|
|
for (int i=0;i<files.size();++i) {
|
|
keyfile<<"L\""<<fs::path(files[i]).filename().string()+"\"";
|
|
if (i+1!=files.size()) keyfile<<",";
|
|
}
|
|
keyfile<<"};\n#endif";
|
|
keyfile.close();
|
|
return 0;
|
|
}
|