summaryrefslogtreecommitdiff
path: root/second_imei_attestation.cpp
blob: 1f003a006276035d01f82fcefad3a868ee40c704 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "second_imei_attestation.h"

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

namespace keymaster {

// Calculates the checksum digit of a given number according to the
// Luhn algorithm.
// The algorithm:
// * Starting from the rightmost digit, moving left: Double the value of every
//   second digit.
// * Sum the digits of the resulting value in each position (if the value
//   was not multiplied, use it as-is) as s.
// * Caclculate the sum digit to be (10 - (s % 10)) % 10.
uint8_t calculate_luhn_checksum_digit(uint64_t val) {
    int iteration_counter = 0;
    uint32_t sum_digits = 0;
    while (val != 0) {
        int curr_digit = val % 10;
        int multiplier = (iteration_counter % 2) == 0 ? 2 : 1;
        int digit_multiplied = curr_digit * multiplier;
        sum_digits += (digit_multiplied % 10) + (digit_multiplied / 10);
        val = val / 10;
        iteration_counter++;
    }

    return (10 - (sum_digits % 10)) % 10;
}

// Validate that the second IMEI sent by the platform is the one following
// the first IMEI, and that the checksum digit matches.
// On most devices with two IMEIs, the IMEIs are sequential. This enables
// providing attestation for the 2nd IMEI even if KeyMint was not provisioned
// with it.
bool validate_second_imei(const keymaster_blob_t& received_second_imei,
                          uint64_t first_imei) {
    // The first IMEI includes the checksum digit, so get rid of it and increase
    // by 1 to get the value of the 2nd IMEI.
    const uint64_t second_imei_no_checksum = (first_imei / 10) + 1;
    const uint8_t checksum_digit =
            calculate_luhn_checksum_digit(second_imei_no_checksum);
    const uint64_t second_imei = second_imei_no_checksum * 10 + checksum_digit;
    // Compare the second IMEI with the caller-provided value.
    char calculated_second_imei[64];
    const size_t calculated_second_imei_len =
            snprintf(calculated_second_imei, sizeof(calculated_second_imei),
                     "%" PRIu64, second_imei);
    bool result =
            (calculated_second_imei_len == received_second_imei.data_length) &&
            (memcmp(calculated_second_imei, received_second_imei.data,
                    calculated_second_imei_len) == 0);
    return result;
}

}  // namespace keymaster