Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support for added security state downstream
Browse files Browse the repository at this point in the history
quh4gko8 committed Jan 5, 2025
1 parent d0398c6 commit 9409e5e
Showing 3 changed files with 254 additions and 15 deletions.
49 changes: 40 additions & 9 deletions src/main/java/app/attestation/server/AttestationProtocol.java
Original file line number Diff line number Diff line change
@@ -812,14 +812,22 @@ private static String toYesNoString(final boolean value) {
return value ? "yes" : "no";
}

record SecurityStateExt(short autoRebootMinutes, byte portSecurityMode, byte userCount) {
static int UNKNOWN_VALUE = -1;
static int INVALID_VALUE = -2;
static SecurityStateExt UNKNOWN = new SecurityStateExt(
(short) UNKNOWN_VALUE, (byte) UNKNOWN_VALUE, (byte) UNKNOWN_VALUE);
}

private static void verify(final byte[] fingerprint,
final Cache<ByteBuffer, Boolean> pendingChallenges, final long userId,
final boolean paired, final ByteBuffer signedMessage, final byte[] signature,
final Certificate[] attestationCertificates, final boolean userProfileSecure,
final boolean accessibility, final boolean deviceAdmin,
final boolean deviceAdminNonSystem, final boolean adbEnabled,
final boolean addUsersWhenLocked, final boolean enrolledBiometrics,
final boolean oemUnlockAllowed, final boolean systemUser)
final boolean oemUnlockAllowed, final boolean systemUser,
final SecurityStateExt securityStateExt)
throws GeneralSecurityException, IOException, SQLiteException {
final String fingerprintHex = BaseEncoding.base16().encode(fingerprint);
final byte[] currentFingerprint = getFingerprint(attestationCertificates[0]);
@@ -957,6 +965,9 @@ private static void verify(final byte[] fingerprint,
addUsersWhenLocked = ?,
oemUnlockAllowed = ?,
systemUser = ?,
autoRebootMinutes = ?,
portSecurityMode = ?,
userCount = ?,
verifiedTimeLast = ?
WHERE fingerprint = ?""");
try {
@@ -979,8 +990,11 @@ private static void verify(final byte[] fingerprint,
update.bind(13, addUsersWhenLocked ? 1 : 0);
update.bind(14, oemUnlockAllowed ? 1 : 0);
update.bind(15, systemUser ? 1 : 0);
update.bind(16, now);
update.bind(17, fingerprint);
update.bind(16, securityStateExt.autoRebootMinutes);
update.bind(17, securityStateExt.portSecurityMode);
update.bind(18, securityStateExt.userCount);
update.bind(19, now);
update.bind(20, fingerprint);
update.step();
} finally {
update.dispose();
@@ -1010,6 +1024,9 @@ INSERT INTO Devices (
addUsersWhenLocked,
oemUnlockAllowed,
systemUser,
autoRebootMinutes,
portSecurityMode,
userCount,
verifiedTimeFirst,
verifiedTimeLast,
userId
@@ -1039,9 +1056,12 @@ INSERT INTO Devices (
insert.bind(18, addUsersWhenLocked ? 1 : 0);
insert.bind(19, oemUnlockAllowed ? 1 : 0);
insert.bind(20, systemUser ? 1 : 0);
insert.bind(21, now);
insert.bind(22, now);
insert.bind(23, userId);
insert.bind(21, securityStateExt.autoRebootMinutes);
insert.bind(22, securityStateExt.portSecurityMode);
insert.bind(23, securityStateExt.userCount);
insert.bind(24, now);
insert.bind(25, now);
insert.bind(26, userId);
insert.step();
} finally {
insert.dispose();
@@ -1066,8 +1086,11 @@ INSERT INTO Attestations (
adbEnabled,
addUsersWhenLocked,
oemUnlockAllowed,
systemUser
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""");
systemUser,
autoRebootMinutes,
portSecurityMode,
userCount
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""");
try {
insert.bind(1, fingerprint);
insert.bind(2, now);
@@ -1090,6 +1113,9 @@ INSERT INTO Attestations (
insert.bind(15, addUsersWhenLocked ? 1 : 0);
insert.bind(16, oemUnlockAllowed ? 1 : 0);
insert.bind(17, systemUser ? 1 : 0);
insert.bind(18, securityStateExt.autoRebootMinutes);
insert.bind(19, securityStateExt.portSecurityMode);
insert.bind(20, securityStateExt.userCount);

insert.step();
} finally {
@@ -1194,10 +1220,14 @@ static void verifySerialized(final byte[] attestationResult,
throw new GeneralSecurityException("invalid device administrator state");
}

SecurityStateExt securityStateExt;
if (version >= 6) {
final short autoRebootMinutes = deserializer.getShort();
final byte portSecurityMode = deserializer.get();
final byte userCount = deserializer.get();
securityStateExt = new SecurityStateExt(autoRebootMinutes, portSecurityMode, userCount);
} else {
securityStateExt = SecurityStateExt.UNKNOWN;
}

final int signatureLength = deserializer.remaining();
@@ -1209,6 +1239,7 @@ static void verifySerialized(final byte[] attestationResult,

verify(fingerprint, pendingChallenges, userId, paired, deserializer.asReadOnlyBuffer(), signature,
certificates, userProfileSecure, accessibility, deviceAdmin, deviceAdminNonSystem,
adbEnabled, addUsersWhenLocked, enrolledBiometrics, oemUnlockAllowed, systemUser);
adbEnabled, addUsersWhenLocked, enrolledBiometrics, oemUnlockAllowed, systemUser,
securityStateExt);
}
}
164 changes: 158 additions & 6 deletions src/main/java/app/attestation/server/AttestationServer.java
Original file line number Diff line number Diff line change
@@ -234,6 +234,9 @@ adbEnabled INTEGER NOT NULL CHECK (adbEnabled in (0, 1)),
addUsersWhenLocked INTEGER NOT NULL CHECK (addUsersWhenLocked in (0, 1)),
oemUnlockAllowed INTEGER NOT NULL CHECK (oemUnlockAllowed in (0, 1)),
systemUser INTEGER NOT NULL CHECK (systemUser in (0, 1)),
autoRebootMinutes INTEGER NOT NULL CHECK (autoRebootMinutes in (-2, -1) OR autoRebootMinutes >= 0),
portSecurityMode INTEGER NOT NULL CHECK (portSecurityMode in (-2, -1) OR portSecurityMode >= 0),
userCount INTEGER NOT NULL CHECK (userCount in (-2, -1) OR userCount >= 1),
verifiedTimeFirst INTEGER NOT NULL,
verifiedTimeLast INTEGER NOT NULL,
expiredTimeLast INTEGER,
@@ -261,7 +264,10 @@ deviceAdmin INTEGER NOT NULL CHECK (deviceAdmin in (0, 1, 2)),
adbEnabled INTEGER NOT NULL CHECK (adbEnabled in (0, 1)),
addUsersWhenLocked INTEGER NOT NULL CHECK (addUsersWhenLocked in (0, 1)),
oemUnlockAllowed INTEGER NOT NULL CHECK (oemUnlockAllowed in (0, 1)),
systemUser INTEGER NOT NULL CHECK (systemUser in (0, 1))
systemUser INTEGER NOT NULL CHECK (systemUser in (0, 1)),
autoRebootMinutes INTEGER NOT NULL CHECK (autoRebootMinutes in (-2, -1) OR autoRebootMinutes >= 0),
portSecurityMode INTEGER NOT NULL CHECK (portSecurityMode in (-2, -1) OR portSecurityMode >= 0),
userCount INTEGER NOT NULL CHECK (userCount in (-2, -1) OR userCount >= 1)
) STRICT""";

private static final String CREATE_ATTESTATION_INDICES = """
@@ -575,6 +581,140 @@ INSERT INTO Attestations (
logger.info("Migrated to schema version: " + userVersion);
}

// add failureAlertTime column to Devices
targetUserVersion = 15;
if (userVersion < targetUserVersion) {
conn.exec("PRAGMA foreign_keys = OFF");
conn.exec("BEGIN IMMEDIATE TRANSACTION");

conn.exec("ALTER TABLE Devices RENAME TO OldDevices");
conn.exec("ALTER TABLE Attestations RENAME TO OldAttestations");

conn.exec(CREATE_ATTESTATION_TABLES);

conn.exec("""
INSERT INTO Devices (
fingerprint,
pinnedCertificates,
attestKey,
pinnedVerifiedBootKey,
verifiedBootHash,
pinnedOsVersion,
pinnedOsPatchLevel,
pinnedVendorPatchLevel,
pinnedBootPatchLevel,
pinnedAppVersion,
pinnedAppVariant,
pinnedSecurityLevel,
userProfileSecure,
enrolledBiometrics,
accessibility,
deviceAdmin,
adbEnabled,
addUsersWhenLocked,
oemUnlockAllowed,
systemUser,
autoRebootMinutes,
portSecurityMode,
userCount,
verifiedTimeFirst,
verifiedTimeLast,
expiredTimeLast,
failureTimeLast,
failureAlertTime,
userId,
deletionTime)
SELECT
fingerprint,
pinnedCertificates,
attestKey,
pinnedVerifiedBootKey,
verifiedBootHash,
pinnedOsVersion,
pinnedOsPatchLevel,
pinnedVendorPatchLevel,
pinnedBootPatchLevel,
pinnedAppVersion,
pinnedAppVariant,
pinnedSecurityLevel,
userProfileSecure,
enrolledBiometrics,
accessibility,
deviceAdmin,
adbEnabled,
addUsersWhenLocked,
oemUnlockAllowed,
systemUser,
-1,
-1,
-1,
verifiedTimeFirst,
verifiedTimeLast,
expiredTimeLast,
failureTimeLast,
failureAlertTime,
userId,
deletionTime
FROM OldDevices""");

conn.exec("""
INSERT INTO Attestations (
id,
fingerprint,
time,
strong,
osVersion,
osPatchLevel,
vendorPatchLevel,
bootPatchLevel,
verifiedBootHash,
appVersion,
userProfileSecure,
enrolledBiometrics,
accessibility,
deviceAdmin,
adbEnabled,
addUsersWhenLocked,
oemUnlockAllowed,
systemUser,
autoRebootMinutes,
portSecurityMode,
userCount
) SELECT
id,
fingerprint,
time,
strong,
osVersion,
osPatchLevel,
vendorPatchLevel,
bootPatchLevel,
verifiedBootHash,
appVersion,
userProfileSecure,
enrolledBiometrics,
accessibility,
deviceAdmin,
adbEnabled,
addUsersWhenLocked,
oemUnlockAllowed,
systemUser,
-1,
-1,
-1
FROM OldAttestations""");

conn.exec("DROP TABLE OldDevices");
conn.exec("DROP TABLE OldAttestations");

conn.exec(CREATE_ATTESTATION_INDICES);
conn.exec("PRAGMA user_version = " + targetUserVersion);
conn.exec("COMMIT TRANSACTION");
userVersion = targetUserVersion;
conn.exec("PRAGMA foreign_keys = ON");
logger.info("Migrated to schema version: " + userVersion);
}

logger.info("Finished database setup for " + ATTESTATION_DATABASE);
} finally {
conn.dispose();
@@ -1392,6 +1532,9 @@ private static void writeDevicesJson(final HttpExchange exchange, final long use
addUsersWhenLocked,
oemUnlockAllowed,
systemUser,
autoRebootMinutes,
portSecurityMode,
userCount,
verifiedTimeFirst,
verifiedTimeLast,
(SELECT min(id) FROM Attestations WHERE Attestations.fingerprint = Devices.fingerprint),
@@ -1460,10 +1603,13 @@ private static void writeDevicesJson(final HttpExchange exchange, final long use
device.add("addUsersWhenLocked", select.columnInt(17));
device.add("oemUnlockAllowed", select.columnInt(18));
device.add("systemUser", select.columnInt(19));
device.add("verifiedTimeFirst", select.columnLong(20));
device.add("verifiedTimeLast", select.columnLong(21));
device.add("minId", select.columnLong(22));
device.add("maxId", select.columnLong(23));
device.add("autoRebootMinutes", select.columnInt(20));
device.add("portSecurityMode", select.columnInt(21));
device.add("userCount", select.columnInt(22));
device.add("verifiedTimeFirst", select.columnLong(23));
device.add("verifiedTimeLast", select.columnLong(24));
device.add("minId", select.columnLong(25));
device.add("maxId", select.columnLong(26));
devices.add(device);
}
} finally {
@@ -1533,7 +1679,10 @@ private static void writeAttestationHistoryJson(final HttpExchange exchange, fin
Attestations.adbEnabled,
Attestations.addUsersWhenLocked,
Attestations.oemUnlockAllowed,
Attestations.systemUser
Attestations.systemUser,
Attestations.autoRebootMinutes,
Attestations.portSecurityMode,
Attestations.userCount
FROM Attestations INNER JOIN Devices ON
Attestations.fingerprint = Devices.fingerprint
WHERE Devices.fingerprint = ? AND userid = ?
@@ -1568,6 +1717,9 @@ private static void writeAttestationHistoryJson(final HttpExchange exchange, fin
attestation.add("addUsersWhenLocked", history.columnInt(14));
attestation.add("oemUnlockAllowed", history.columnInt(15));
attestation.add("systemUser", history.columnInt(16));
attestation.add("autoRebootMinutes", history.columnInt(17));
attestation.add("portSecurityMode", history.columnInt(18));
attestation.add("userCount", history.columnInt(19));
attestations.add(attestation);
rowCount += 1;
}
56 changes: 56 additions & 0 deletions static/monitoring.js
Original file line number Diff line number Diff line change
@@ -85,6 +85,56 @@ function toSecurityLevelString(securityLevel, attestKey) {
throw new Error("Invalid security level");
}

function autoRebootTimeoutString(autoRebootMinutes) {
if (autoRebootMinutes >= 0) {
if (autoRebootMinutes < 60) {
return autoRebootMinutes + " minutes";
}
const autoRebootHours = autoRebootMinutes / 60;
const autoRebootMinutesRemainder = autoRebootMinutes / 60;
if (autoRebootHours < 24) {
return autoRebootHours + " hours " + autoRebootMinutesRemainder + " minutes";
}

const autoRebootDays = autoRebootHours / 24;
return autoRebootDays + " days " + autoRebootHours + " hours " + autoRebootMinutesRemainder + " minutes";
} else if (autoRebootMinutes == -1) {
return "Unknown";
} else if (autoRebootMinutes == -2) {
return "Invalid";
}
throw new Error("Invalid auto reboot minutes value");
}

function usbPortSecurityModeString(portSecurityMode) {
if (portSecurityMode >= 0) {
switch (portSecurityMode) {
case 0: return "Off";
case 1: return "Charging-only";
case 2: return "Charging-only when locked";
case 3: return "Charging-only when locked, except before first unlock";
case 4: return "On";
default: break;
}
} else if (portSecurityMode == -1) {
return "Unknown";
} else if (portSecurityMode == -2) {
return "Invalid";
}
throw new Error("Invalid port security mode value");
}

function userCountString(userCount) {
if (userCount > 0) {
return userCount + " users";
} else if (userCount == -1) {
return "Unknown";
} else if (userCount == -2) {
return "Invalid";
}
throw new Error("Invalid port security mode value");
}

function reloadQrCode() {
qr.src = "/placeholder.gif";
qr.alt = "";
@@ -177,6 +227,9 @@ function fetchHistory(parent, nextOffset) {
appendLine(parent, "Add users from lock screen: " + toYesNoString(attestation.addUsersWhenLocked));
appendLine(parent, "OEM unlocking allowed: " + toYesNoString(attestation.oemUnlockAllowed));
appendLine(parent, "Main user account: " + toYesNoString(attestation.systemUser));
appendLine(parent, "Auto reboot timeout: " + autoRebootTimeoutString(attestation.autoRebootMinutes));
appendLine(parent, "USB-C port security mode: " + usbPortSecurityModeString(attestation.portSecurityMode));
appendLine(parent, "User count: " + userCountString(attestation.useCount));
}
const earliestCurrentId = attestations.slice(-1)[0].id;
function fetchHistoryNextPage() {
@@ -301,6 +354,9 @@ function fetchDevices() {
appendLine(info, "Add users from lock screen: " + toYesNoString(device.addUsersWhenLocked));
appendLine(info, "OEM unlocking allowed: " + toYesNoString(device.oemUnlockAllowed));
appendLine(info, "Main user account: " + toYesNoString(device.systemUser));
appendLine(info, "Auto reboot timeout: " + autoRebootTimeoutString(device.systemUser));
appendLine(info, "USB-C port security mode: " + usbPortSecurityModeString(device.systemUser));
appendLine(info, "User count: " + userCountString(device.systemUser));

info.appendChild(create("h3", "Attestation history"));
appendLine(info, "First verified time: " + new Date(device.verifiedTimeFirst));

0 comments on commit 9409e5e

Please sign in to comment.