music: strengthen auxio uids

Strengthen Auxio-style UIDs.

These UIDs now leverage SHA-256 hashes with null values now writing
themselves as 0 in order to avoid possible message collissions from
other value arrangements.
This commit is contained in:
Alexander Capehart 2022-12-25 20:32:35 -07:00
parent 9cf8d54353
commit cc8f429044
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
3 changed files with 33 additions and 29 deletions

View file

@ -61,8 +61,9 @@ class DetailViewModel(application: Application) :
private val _currentSong = MutableStateFlow<DetailSong?>(null)
/**
* The current [DetailSong] to display. Null if there is nothing to show. TODO: De-couple Song
* and Properties?
* The current [DetailSong] to display. Null if there is nothing to show.
*
* TODO: De-couple Song and Properties?
*/
val currentSong: StateFlow<DetailSong?>
get() = _currentSong

View file

@ -191,9 +191,9 @@ sealed class Music : Item {
// tags in a music item. For easier use with MusicBrainz IDs, we transform
// this into a UUID too.
val uuid =
MessageDigest.getInstance("MD5").run {
MessageDigest.getInstance("SHA-256").run {
updates()
digest().toUuid()
digestUUID()
}
return UID(Format.AUXIO, mode, uuid)
@ -1369,8 +1369,6 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
// --- MUSIC UID CREATION UTILITIES ---
// TODO: Use a stronger hash (SHA-2?, append a 0 for null values to further waterfall effect??)
/**
* Update a [MessageDigest] with a lowercase [String].
* @param string The [String] to hash. If null, it will not be hashed.
@ -1378,6 +1376,8 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
private fun MessageDigest.update(string: String?) {
if (string != null) {
update(string.lowercase().toByteArray())
} else {
update(0)
}
}
@ -1388,6 +1388,8 @@ private fun MessageDigest.update(string: String?) {
private fun MessageDigest.update(date: Date?) {
if (date != null) {
update(date.toString().toByteArray())
}else {
update(0)
}
}
@ -1406,35 +1408,36 @@ private fun MessageDigest.update(strings: List<String?>) {
private fun MessageDigest.update(n: Int?) {
if (n != null) {
update(byteArrayOf(n.toByte(), n.shr(8).toByte(), n.shr(16).toByte(), n.shr(24).toByte()))
}else {
update(0)
}
}
/**
* Convert a [ByteArray] to a [UUID]. Assumes that the [ByteArray] has a length of 16.
* @return A [UUID] derived from the [ByteArray]'s contents. Internally, the two [Long]s in the
* [UUID] will be little-endian.
* Digest a 8-byte+ hash into a [UUID].
* @return A [UUID] derived from the first 8 bytes of the digest.
*/
fun ByteArray.toUuid(): UUID {
check(size == 16)
fun MessageDigest.digestUUID(): UUID {
val digest = digest()
return UUID(
get(0)
digest[0]
.toLong()
.shl(56)
.or(get(1).toLong().and(0xFF).shl(48))
.or(get(2).toLong().and(0xFF).shl(40))
.or(get(3).toLong().and(0xFF).shl(32))
.or(get(4).toLong().and(0xFF).shl(24))
.or(get(5).toLong().and(0xFF).shl(16))
.or(get(6).toLong().and(0xFF).shl(8))
.or(get(7).toLong().and(0xFF)),
get(8)
.or(digest[1].toLong().and(0xFF).shl(48))
.or(digest[2].toLong().and(0xFF).shl(40))
.or(digest[3].toLong().and(0xFF).shl(32))
.or(digest[4].toLong().and(0xFF).shl(24))
.or(digest[5].toLong().and(0xFF).shl(16))
.or(digest[6].toLong().and(0xFF).shl(8))
.or(digest[7].toLong().and(0xFF)),
digest[8]
.toLong()
.shl(56)
.or(get(9).toLong().and(0xFF).shl(48))
.or(get(10).toLong().and(0xFF).shl(40))
.or(get(11).toLong().and(0xFF).shl(32))
.or(get(12).toLong().and(0xFF).shl(24))
.or(get(13).toLong().and(0xFF).shl(16))
.or(get(14).toLong().and(0xFF).shl(8))
.or(get(15).toLong().and(0xFF)))
.or(digest[9].toLong().and(0xFF).shl(48))
.or(digest[10].toLong().and(0xFF).shl(40))
.or(digest[11].toLong().and(0xFF).shl(32))
.or(digest[12].toLong().and(0xFF).shl(24))
.or(digest[13].toLong().and(0xFF).shl(16))
.or(digest[14].toLong().and(0xFF).shl(8))
.or(digest[15].toLong().and(0xFF)))
}

View file

@ -76,11 +76,11 @@ abstract class ViewBindingFragment<VB : ViewBinding> : Fragment() {
}
/**
* Delegate to automatically create and destroy an object derived from the [ViewBinding]. TODO:
* Phase this out, it's really dumb
* Delegate to automatically create and destroy an object derived from the [ViewBinding].
* @param create Block to create the object from the [ViewBinding].
*/
fun <T> lifecycleObject(create: (VB) -> T): ReadOnlyProperty<Fragment, T> {
// TODO: Phase this out.
lifecycleObjects.add(LifecycleObject(null, create))
return object : ReadOnlyProperty<Fragment, T> {