724 {
728
729 const std::string key = extract_activation_key(raw_key);
730 if (key.empty()) {
731 status.
message =
"No activation key installed.";
732 return status;
733 }
734
735 const auto apply_common_field_checks = [&](const std::string& product,
736 const std::string& key_machine_raw,
737 const std::string& key_motherboard_raw,
738 const std::string& owner,
739 const std::string& edition,
740 const std::string& expires) -> bool {
741 if (product != kProductId) {
742 status.
message =
"Activation key is for a different product.";
743 return false;
744 }
745
746 if (key_machine_raw.empty()) {
747 status.
message =
"Activation key does not contain a Machine ID.";
748 return false;
749 }
750
751 if (key_motherboard_raw.empty()) {
752 status.
message =
"Activation key does not contain a motherboard serial.";
753 return false;
754 }
755
757 status.
message =
"Motherboard serial could not be read on this computer.";
758 return false;
759 }
760
764
765 if (key_machine != current_machine && key_machine != legacy_machine) {
766 status.
message =
"Activation key is for a different machine-id.";
767 return false;
768 }
769
772
773 if (key_motherboard != current_motherboard) {
774 status.
message =
"Activation key is for a different motherboard serial.";
775 return false;
776 }
777
778 status.
owner = owner.empty() ?
"Donation supporter" : owner;
779 status.
edition = edition.empty() ?
"Donation" : edition;
781
784 status.
message =
"Activation key contains an invalid expiry date.";
785 return false;
786 }
787
790 status.
message =
"Activation key has expired.";
791 return false;
792 }
793
795 status.
message =
"Activation is valid.";
796 return true;
797 };
798
799
800
801
802
803
804 if (key.find(std::string(kKeyPrefixV3)) != std::string::npos) {
814
815 if (
field_or_empty(fields,
"FORMAT") != std::string(kKeyPrefixV3)) {
816 status.
message =
"Invalid BGRD3 activation key: BGRD3 format line is missing.";
817 return status;
818 }
819
820 if (code.size() != 25) {
821 status.
message =
"Invalid BGRD3 activation key: CODE must contain 25 letters/numbers.";
822 return status;
823 }
824
825 if (engine.empty()) {
826 status.
message =
"Invalid BGRD3 activation key: ENGINE field is missing.";
827 return status;
828 }
829
832 status.
message =
"Activation engine mismatch. App engine is " + current_engine +
833 " but key engine is " + engine +
834 ". Rebuild and reinstall the app and generate the keygen from the same package.";
835 return status;
836 }
837
840 if (code != expected_code) {
841 status.
message =
"BGRD3 activation code is not valid. The key was not saved. Generate the key again with the matching keygen and copy the whole BEGIN/END block.";
842 return status;
843 }
844
845 apply_common_field_checks(product, machine, motherboard, owner, edition, expires);
846 return status;
847 }
848
849
850
851
852 if (key.find(std::string(kKeyPrefixV2)) != std::string::npos) {
862
863 if (signature.size() != 40) {
864 status.
message =
"Invalid BGRD2 activation key: SIGNATURE must contain 40 hexadecimal characters.";
865 return status;
866 }
867 for (unsigned char c : signature) {
868 if (std::isxdigit(c) == 0) {
869 status.
message =
"Invalid BGRD2 activation key: SIGNATURE contains a non-hexadecimal character.";
870 return status;
871 }
872 }
873
874 if (engine.empty()) {
875 status.
message =
"Invalid BGRD2 activation key: ENGINE field is missing.";
876 return status;
877 }
878
881 status.
message =
"Activation engine mismatch. App engine is " + current_engine +
882 " but key engine is " + engine +
883 ". Rebuild and reinstall the app and generate the keygen from the same package.";
884 return status;
885 }
886
888 const std::string expected_signature =
sign_payload_v2(canonical_payload);
889 if (signature != expected_signature) {
890 status.
message =
"BGRD2 activation key signature is not valid. The key was not saved. Generate the key again with the matching keygen and copy the whole BEGIN/END block.";
891 return status;
892 }
893
894 apply_common_field_checks(product, machine, motherboard, owner, edition, expires);
895 return status;
896 }
897
898
899 const std::string prefix = std::string(kKeyPrefixV1) + ".";
900 if (key.rfind(prefix, 0) != 0) {
901 status.
message =
"Invalid activation key prefix. Paste a BGRD3 activation block generated by the matching keygen.";
902 return status;
903 }
904
905 const auto first_dot = key.find('.', prefix.size());
906 if (first_dot == std::string::npos) {
907 status.
message =
"Malformed legacy activation key.";
908 return status;
909 }
910
911 const std::string payload_b64 = key.substr(prefix.size(), first_dot - prefix.size());
912 const std::string signature =
uppercase(key.substr(first_dot + 1));
913
914 if (signature.size() != 40) {
915 status.
message =
"Invalid legacy activation key signature length.";
916 return status;
917 }
918
920 if (!decoded_payload.has_value()) {
921 status.
message =
"Legacy activation key payload is not valid base64url.";
922 return status;
923 }
924
925 const std::string expected_signature =
sign_payload_v1(*decoded_payload);
926 if (signature != expected_signature) {
927 status.
message =
"Legacy activation key signature is not valid. Generate a new BGRD3 key with the matching keygen.";
928 return status;
929 }
930
932 const auto product_it = fields.find("product");
933 const auto machine_it = fields.find("machine");
934 const auto motherboard_it = fields.find("motherboard");
935 const auto owner_it = fields.find("owner");
936 const auto edition_it = fields.find("edition");
937 const auto expires_it = fields.find("expires");
938
939 apply_common_field_checks(
940 product_it != fields.end() ? product_it->second : std::string{},
941 machine_it != fields.end() ? machine_it->second : std::string{},
942 motherboard_it != fields.end() ? motherboard_it->second : std::string{},
943 owner_it != fields.end() ? owner_it->second : std::string{},
944 edition_it != fields.end() ? edition_it->second : std::string{},
945 expires_it != fields.end() ? expires_it->second : std::string{}
946 );
947 return status;
948}
std::string lowercase(std::string value)
std::string normalize_activation_code(std::string value)
bool has_expired(const std::string &expires)
std::string code_payload_v3(const std::map< std::string, std::string > &fields)
std::map< std::string, std::string > parse_key_block(const std::string &key)
bool is_valid_expiry(const std::string &expires)
std::map< std::string, std::string > parse_payload(const std::string &payload)
std::string generate_activation_code_v3(const std::string &canonical_payload)
std::string legacy_application_machine_id()
std::string canonical_v2_payload(const std::map< std::string, std::string > &fields)
std::string canonical_serial(std::string value)
std::string normalize_engine_id(const std::string &value)
std::optional< std::string > base64url_decode(const std::string &input)
std::string sign_payload_v2(const std::string &canonical_payload)
std::string field_or_empty(const std::map< std::string, std::string > &fields, const std::string &name)
std::string uppercase(std::string value)
std::string sign_payload_v1(const std::string &payload)
std::string compact_hex_machine_id(const std::string &raw)
std::string motherboard_serial()
std::string activation_engine_id()
std::string trim(std::string value)
std::string motherboard_serial