progressive refactor: using cc.cfig.io.Struct from mavenCentral()

pull/94/head
cfig 3 years ago
parent 3ae5d567e3
commit 2d7e164da5
No known key found for this signature in database
GPG Key ID: B104C307F0FDABB7

@ -33,6 +33,7 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("cc.cfig:io:0.2")
implementation("org.slf4j:slf4j-simple:1.7.32")
implementation("org.slf4j:slf4j-api:1.7.32")
implementation("com.fasterxml.jackson.core:jackson-annotations:2.13.1")

@ -14,7 +14,7 @@
package avb.alg
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
class Algorithms {
companion object {
@ -42,7 +42,7 @@ class Algorithms {
hash_num_bytes = 32,
signature_num_bytes = 256,
public_key_num_bytes = 8 + 2 * 2048 / 8,
padding = Struct3("2b202x1b19B").pack(
padding = Struct("2b202x1b19B").pack(
byteArrayOf(0x00, 0x01),
0xff,
byteArrayOf(0x00),
@ -58,7 +58,7 @@ class Algorithms {
hash_num_bytes = 32,
signature_num_bytes = 512,
public_key_num_bytes = 8 + 2 * 4096 / 8,
padding = Struct3("2b458x1x19B").pack(
padding = Struct("2b458x1x19B").pack(
byteArrayOf(0x00, 0x01),
0xff,
0x00,
@ -76,7 +76,7 @@ class Algorithms {
hash_num_bytes = 32,
signature_num_bytes = 1024,
public_key_num_bytes = 8 + 2 * 8192 / 8,
padding = Struct3("2b970x1x19B").pack(
padding = Struct("2b970x1x19B").pack(
intArrayOf(0x00, 0x01),
0xff,
0x00,
@ -92,7 +92,7 @@ class Algorithms {
hash_num_bytes = 64,
signature_num_bytes = 256,
public_key_num_bytes = 8 + 2 * 2048 / 8,
padding = Struct3("2b170x1x19B").pack(
padding = Struct("2b170x1x19B").pack(
intArrayOf(0x00, 0x01),
0xff,
0x00,
@ -108,7 +108,7 @@ class Algorithms {
hash_num_bytes = 64,
signature_num_bytes = 512,
public_key_num_bytes = 8 + 2 * 4096 / 8,
padding = Struct3("2b426x1x19B").pack(
padding = Struct("2b426x1x19B").pack(
intArrayOf(0x00, 0x01),
0xff,
0x00,
@ -125,7 +125,7 @@ class Algorithms {
signature_num_bytes = 1024,
public_key_num_bytes = 8 + 2 * 8192 / 8,
padding = Struct3("2b938x1x19B").pack(
padding = Struct("2b938x1x19B").pack(
intArrayOf(0x00, 0x01),
0xff,
0x00,

@ -17,7 +17,7 @@ package avb.blob
import avb.alg.Algorithms
import cfig.helper.CryptoHelper
import cfig.helper.Helper
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import org.slf4j.LoggerFactory
import java.nio.file.Files
import java.nio.file.Paths
@ -55,9 +55,8 @@ data class AuthBlob(
return if (alg.name == "NONE") {
byteArrayOf()
} else {
val k = CryptoHelper.KeyBox.parse2(Files.readAllBytes(Paths.get(alg.defaultKey.replace(".pem", ".pk8")))) as Array<*>
assert(k[0] as Boolean)
CryptoHelper.Signer.rawRsa(k[2] as PrivateKey, Helper.join(alg.padding, hash))
val k = CryptoHelper.KeyBox.parse4(Files.readAllBytes(Paths.get(alg.defaultKey.replace(".pem", ".pk8"))))
CryptoHelper.Signer.rawRsa(k.key as PrivateKey, Helper.join(alg.padding, hash))
}
}
@ -77,7 +76,7 @@ data class AuthBlob(
val binaryHash = calcHash(header_data_blob, aux_data_blob, algorithm_name)
val binarySignature = calcSignature(binaryHash, algorithm_name)
val authData = Helper.join(binaryHash, binarySignature)
return Helper.join(authData, Struct3("${authBlockSize - authData.size}x").pack(0))
return Helper.join(authData, Struct("${authBlockSize - authData.size}x").pack(0))
}
private val log = LoggerFactory.getLogger(AuthBlob::class.java)

@ -18,7 +18,7 @@ import avb.alg.Algorithm
import avb.desc.*
import cfig.helper.CryptoHelper
import cfig.helper.Helper
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import org.bouncycastle.asn1.pkcs.RSAPrivateKey
import org.slf4j.LoggerFactory
@ -98,7 +98,7 @@ class AuxBlob(
val auxSize = Helper.round_to_multiple(
(encodedDesc.size + encodedKey.size + encodedPkmd.size).toLong(),
64)
return Struct3("${auxSize}b").pack(Helper.join(encodedDesc, encodedKey, encodedPkmd))
return Struct("${auxSize}b").pack(Helper.join(encodedDesc, encodedKey, encodedPkmd))
}
fun populateDescriptors(descriptors: List<Descriptor>): AuxBlob {
@ -139,7 +139,7 @@ class AuxBlob(
if (key == null) {
algKey = Files.readAllBytes((Paths.get(alg.defaultKey)))
}
val rsa = (CryptoHelper.KeyBox.parse2(algKey!!) as Array<*>)[2] as RSAPrivateKey //BC RSA
val rsa = CryptoHelper.KeyBox.parse4(algKey!!).key as RSAPrivateKey //BC RSA
encodedKey = CryptoHelper.KeyBox.encodeRSAkey(rsa)
assert(alg.public_key_num_bytes == encodedKey.size)
} else {

@ -14,7 +14,7 @@
package avb.blob
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
@ -46,7 +46,7 @@ data class Footer constructor(
) {
@Throws(IllegalArgumentException::class)
constructor(iS: InputStream) : this() {
val info = Struct3(FORMAT_STRING).unpack(iS)
val info = Struct(FORMAT_STRING).unpack(iS)
assert(7 == info.size)
if (MAGIC != (info[0] as String)) {
throw IllegalArgumentException("stream doesn't look like valid AVB Footer")
@ -75,7 +75,7 @@ data class Footer constructor(
}
fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack(MAGIC, //4s
return Struct(FORMAT_STRING).pack(MAGIC, //4s
this.versionMajor, //L
this.versionMinor, //L
this.originalImageSize, //Q
@ -93,7 +93,7 @@ data class Footer constructor(
private const val FORMAT_STRING = "!4s2L3Q${RESERVED}x"
init {
assert(SIZE == Struct3(FORMAT_STRING).calcSize())
assert(SIZE == Struct(FORMAT_STRING).calcSize())
}
}
}

@ -15,7 +15,7 @@
package avb.blob
import cfig.Avb
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import java.io.InputStream
//avbtool::AvbVBMetaHeader
@ -40,7 +40,7 @@ data class Header(
var release_string: String = "avbtool ${Avb.AVB_VERSION_MAJOR}.${Avb.AVB_VERSION_MINOR}.${Avb.AVB_VERSION_SUB}") {
@Throws(IllegalArgumentException::class)
constructor(iS: InputStream) : this() {
val info = Struct3(FORMAT_STRING).unpack(iS)
val info = Struct(FORMAT_STRING).unpack(iS)
assert(22 == info.size)
if (info[0] != magic) {
throw IllegalArgumentException("stream doesn't look like valid VBMeta Header")
@ -67,7 +67,7 @@ data class Header(
}
fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack(
return Struct(FORMAT_STRING).pack(
magic, //4s
this.required_libavb_version_major, this.required_libavb_version_minor, //2L
this.authentication_data_block_size, this.auxiliary_data_block_size, //2Q
@ -121,7 +121,7 @@ data class Header(
private const val FORMAT_STRING = ("!4s2L2QL11QL${REVERSED0}x47sx" + "${REVERSED}x")
init {
assert(SIZE == Struct3(FORMAT_STRING).calcSize())
assert(SIZE == Struct(FORMAT_STRING).calcSize())
}
}
}

@ -17,7 +17,7 @@ package avb.desc
import avb.AVBInfo
import cfig.Avb
import cfig.helper.Helper
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import java.io.File
import java.io.InputStream
import java.security.MessageDigest
@ -37,14 +37,14 @@ class ChainPartitionDescriptor(
this.num_bytes_following = SIZE + this.partition_name_len + this.public_key_len - 16
val nbf_with_padding = Helper.round_to_multiple(this.num_bytes_following, 8).toULong()
val padding_size = nbf_with_padding - this.num_bytes_following.toUInt()
val desc = Struct3(FORMAT_STRING + "${RESERVED}x").pack(
val desc = Struct(FORMAT_STRING + "${RESERVED}x").pack(
TAG,
nbf_with_padding,
this.rollback_index_location,
this.partition_name.length.toUInt(),
this.public_key_len,
null)
val padding = Struct3("${padding_size}x").pack(null)
val padding = Struct("${padding_size}x").pack(null)
return Helper.join(desc, this.partition_name.toByteArray(), this.pubkey, padding)
}
@ -57,11 +57,11 @@ class ChainPartitionDescriptor(
}
constructor(data: InputStream, seq: Int = 0) : this() {
if (SIZE - RESERVED != Struct3(FORMAT_STRING).calcSize().toLong()) {
if (SIZE - RESERVED != Struct(FORMAT_STRING).calcSize().toLong()) {
throw RuntimeException("ChainPartitionDescriptor size check failed")
}
this.sequence = seq
val info = Struct3(FORMAT_STRING + "${RESERVED}s").unpack(data)
val info = Struct(FORMAT_STRING + "${RESERVED}s").unpack(data)
this.tag = (info[0] as ULong).toLong()
this.num_bytes_following = (info[1] as ULong).toLong()
this.rollback_index_location = (info[2] as UInt).toInt()
@ -71,7 +71,7 @@ class ChainPartitionDescriptor(
if (this.tag != TAG || this.num_bytes_following != expectedSize) {
throw IllegalArgumentException("Given data does not look like a chain/delegation descriptor")
}
val info2 = Struct3("${this.partition_name_len}s${this.public_key_len}b").unpack(data)
val info2 = Struct("${this.partition_name_len}s${this.public_key_len}b").unpack(data)
this.partition_name = info2[0] as String
this.pubkey = info2[1] as ByteArray
val md = MessageDigest.getInstance("SHA1").let {

@ -16,7 +16,7 @@ package avb.desc
import avb.blob.Header
import cfig.helper.Helper
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import org.apache.commons.codec.binary.Hex
import org.slf4j.LoggerFactory
import java.io.File
@ -44,7 +44,7 @@ class HashDescriptor(var flags: Int = 0,
}
constructor(data: InputStream, seq: Int = 0) : this() {
val info = Struct3(FORMAT_STRING).unpack(data)
val info = Struct(FORMAT_STRING).unpack(data)
this.tag = (info[0] as ULong).toLong()
this.num_bytes_following = (info[1] as ULong).toLong()
this.image_size = (info[2] as ULong).toLong()
@ -59,7 +59,7 @@ class HashDescriptor(var flags: Int = 0,
if (this.tag != TAG || expectedSize != this.num_bytes_following) {
throw IllegalArgumentException("Given data does not look like a |hash| descriptor")
}
val payload = Struct3("${this.partition_name_len}s${this.salt_len}b${this.digest_len}b").unpack(data)
val payload = Struct("${this.partition_name_len}s${this.salt_len}b${this.digest_len}b").unpack(data)
assert(3 == payload.size)
this.partition_name = payload[0] as String
this.salt = payload[1] as ByteArray
@ -70,7 +70,7 @@ class HashDescriptor(var flags: Int = 0,
val payload_bytes_following = SIZE + this.partition_name.length + this.salt.size + this.digest.size - 16L
this.num_bytes_following = Helper.round_to_multiple(payload_bytes_following, 8)
val padding_size = num_bytes_following - payload_bytes_following
val desc = Struct3(FORMAT_STRING).pack(
val desc = Struct(FORMAT_STRING).pack(
TAG,
this.num_bytes_following,
this.image_size,
@ -80,7 +80,7 @@ class HashDescriptor(var flags: Int = 0,
this.digest.size,
this.flags,
null)
val padding = Struct3("${padding_size}x").pack(null)
val padding = Struct("${padding_size}x").pack(null)
return Helper.join(desc, partition_name.toByteArray(), this.salt, this.digest, padding)
}

@ -17,7 +17,7 @@ package avb.desc
import avb.blob.Header
import cfig.helper.CryptoHelper
import cfig.helper.Helper
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import org.slf4j.LoggerFactory
import java.io.*
import java.security.MessageDigest
@ -52,7 +52,7 @@ class HashTreeDescriptor(
constructor(data: InputStream, seq: Int = 0) : this() {
this.sequence = seq
val info = Struct3(FORMAT_STRING).unpack(data)
val info = Struct(FORMAT_STRING).unpack(data)
this.tag = (info[0] as ULong).toLong()
this.num_bytes_following = (info[1] as ULong).toLong()
this.dm_verity_version = (info[2] as UInt).toInt()
@ -75,7 +75,7 @@ class HashTreeDescriptor(
throw IllegalArgumentException("Given data does not look like a hashtree descriptor")
}
val info2 = Struct3("${partition_name_len}s${salt_len}b${root_digest_len}b").unpack(data)
val info2 = Struct("${partition_name_len}s${salt_len}b${root_digest_len}b").unpack(data)
this.partition_name = info2[0] as String
this.salt = info2[1] as ByteArray
this.root_digest = info2[2] as ByteArray
@ -85,7 +85,7 @@ class HashTreeDescriptor(
this.num_bytes_following = SIZE + this.partition_name.length + this.salt.size + this.root_digest.size - 16
val nbf_with_padding = Helper.round_to_multiple(this.num_bytes_following.toLong(), 8)
val padding_size = nbf_with_padding - this.num_bytes_following.toLong()
val desc = Struct3(FORMAT_STRING).pack(
val desc = Struct(FORMAT_STRING).pack(
TAG,
nbf_with_padding.toULong(),
this.dm_verity_version,
@ -104,7 +104,7 @@ class HashTreeDescriptor(
this.flags,
null
)
val padding = Struct3("${padding_size}x").pack(null)
val padding = Struct("${padding_size}x").pack(null)
return Helper.join(desc, this.partition_name.toByteArray(), this.salt, this.root_digest, padding)
}
@ -155,7 +155,7 @@ class HashTreeDescriptor(
val bos = ByteArrayOutputStream(hashSize.toInt())
run hashing@{
val padSz = calcSingleHashSize(true) - calcSingleHashSize(false)
val padding = Struct3("${padSz}x").pack(0)
val padding = Struct("${padSz}x").pack(0)
var totalRead = 0L
while (true) {
val data = ByteArray(blockSz)
@ -179,7 +179,7 @@ class HashTreeDescriptor(
}//hashing
if (hashSize > bos.size()) {
bos.write(Struct3("${hashSize - bos.size()}x").pack(0))
bos.write(Struct("${hashSize - bos.size()}x").pack(0))
}
return bos.toByteArray()
}

@ -15,7 +15,7 @@
package avb.desc
import cfig.helper.Helper
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import java.io.InputStream
class KernelCmdlineDescriptor(
@ -36,7 +36,7 @@ class KernelCmdlineDescriptor(
@Throws(IllegalArgumentException::class)
constructor(data: InputStream, seq: Int = 0) : this() {
val info = Struct3(FORMAT_STRING).unpack(data)
val info = Struct(FORMAT_STRING).unpack(data)
this.tag = (info[0] as ULong).toLong()
this.num_bytes_following = (info[1] as ULong).toLong()
this.flags = (info[2] as UInt).toInt()
@ -46,19 +46,19 @@ class KernelCmdlineDescriptor(
if ((this.tag != TAG) || (this.num_bytes_following != expectedSize.toLong())) {
throw IllegalArgumentException("Given data does not look like a kernel cmdline descriptor")
}
this.cmdline = Struct3("${this.cmdlineLength}s").unpack(data)[0] as String
this.cmdline = Struct("${this.cmdlineLength}s").unpack(data)[0] as String
}
override fun encode(): ByteArray {
val num_bytes_following = SIZE - 16 + cmdline.toByteArray().size
val nbf_with_padding = Helper.round_to_multiple(num_bytes_following.toLong(), 8)
val padding_size = nbf_with_padding - num_bytes_following
val desc = Struct3(FORMAT_STRING).pack(
val desc = Struct(FORMAT_STRING).pack(
TAG,
nbf_with_padding,
this.flags,
cmdline.length)
val padding = Struct3("${padding_size}x").pack(null)
val padding = Struct("${padding_size}x").pack(null)
return Helper.join(desc, cmdline.toByteArray(), padding)
}
@ -72,7 +72,7 @@ class KernelCmdlineDescriptor(
const val flagHashTreeDisabled = 2
init {
assert(SIZE == Struct3(FORMAT_STRING).calcSize())
assert(SIZE == Struct(FORMAT_STRING).calcSize())
}
}
}

@ -15,21 +15,21 @@
package avb.desc
import cfig.helper.Helper
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import java.io.InputStream
class PropertyDescriptor(
var key: String = "",
var value: String = "") : Descriptor(TAG, 0, 0) {
override fun encode(): ByteArray {
if (SIZE != Struct3(FORMAT_STRING).calcSize().toUInt()) {
if (SIZE != Struct(FORMAT_STRING).calcSize().toUInt()) {
throw RuntimeException("PropertyDesc size check failed")
}
this.num_bytes_following = (SIZE + this.key.length.toUInt() + this.value.length.toUInt() + 2U - 16U).toLong()
val nbfWithPadding = Helper.round_to_multiple(this.num_bytes_following.toLong(), 8).toULong()
val paddingSize = nbfWithPadding - num_bytes_following.toUInt()
val padding = Struct3("${paddingSize}x").pack(0)
val desc = Struct3(FORMAT_STRING).pack(
val padding = Struct("${paddingSize}x").pack(0)
val desc = Struct(FORMAT_STRING).pack(
TAG,
nbfWithPadding,
this.key.length,
@ -41,7 +41,7 @@ class PropertyDescriptor(
}
constructor(data: InputStream, seq: Int = 0) : this() {
val info = Struct3(FORMAT_STRING).unpack(data)
val info = Struct(FORMAT_STRING).unpack(data)
this.tag = (info[0] as ULong).toLong()
this.num_bytes_following = (info[1] as ULong).toLong()
val keySize = (info[2] as ULong).toUInt()
@ -52,7 +52,7 @@ class PropertyDescriptor(
}
this.sequence = seq
val info2 = Struct3("${keySize}sx${valueSize}s").unpack(data)
val info2 = Struct("${keySize}sx${valueSize}s").unpack(data)
this.key = info2[0] as String
this.value = info2[2] as String
}

@ -14,7 +14,7 @@
package avb.desc
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import cfig.helper.Helper
import org.apache.commons.codec.binary.Hex
import org.slf4j.LoggerFactory
@ -25,7 +25,7 @@ class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0, 0,
@Throws(IllegalArgumentException::class)
constructor(stream: InputStream, seq: Int = 0) : this() {
this.sequence = seq
val info = Struct3(FORMAT).unpack(stream)
val info = Struct(FORMAT).unpack(stream)
this.tag = (info[0] as ULong).toLong()
this.num_bytes_following = (info[1] as ULong).toLong()
log.debug("UnknownDescriptor: tag = $tag, len = ${this.num_bytes_following}")
@ -36,7 +36,7 @@ class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0, 0,
}
override fun encode(): ByteArray {
return Helper.join(Struct3(FORMAT).pack(this.tag, this.data.size.toLong()), data)
return Helper.join(Struct(FORMAT).pack(this.tag, this.data.size.toLong()), data)
}
override fun toString(): String {
@ -120,7 +120,7 @@ class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0, 0,
}
init {
assert(SIZE == Struct3(FORMAT).calcSize())
assert(SIZE == Struct(FORMAT).calcSize())
}
}
}

@ -14,7 +14,7 @@
package cfig.bcb
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import org.slf4j.LoggerFactory
import java.io.File
import java.io.FileInputStream
@ -35,12 +35,12 @@ data class BootloaderMsg(//offset 0, size 2k
private val log = LoggerFactory.getLogger("BootloaderMsg")
init {
assert(SIZE == Struct3(FORMAT_STRING).calcSize())
assert(SIZE == Struct(FORMAT_STRING).calcSize())
}
}
constructor(fis: FileInputStream) : this() {
val info = Struct3(FORMAT_STRING).unpack(fis)
val info = Struct(FORMAT_STRING).unpack(fis)
this.command = info[0] as String
this.status = info[1] as String
this.recovery = info[2] as String
@ -49,7 +49,7 @@ data class BootloaderMsg(//offset 0, size 2k
}
fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack(
return Struct(FORMAT_STRING).pack(
this.command,
this.stage,
this.recovery,
@ -71,7 +71,7 @@ data class BootloaderMsg(//offset 0, size 2k
if (File(miscFile).exists()) {
log.info("readBootloaderMsg() from $miscFile")
val fis = FileInputStream(miscFile)
val info = Struct3(FORMAT_STRING).unpack(fis)
val info = Struct(FORMAT_STRING).unpack(fis)
this.command = info[0] as String
this.status = info[1] as String
this.recovery = info[2] as String

@ -14,7 +14,7 @@
package cfig.bcb
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import org.slf4j.LoggerFactory
import java.io.FileInputStream
@ -29,19 +29,19 @@ class BootloaderMsgAB( //offset 2k, size 2k
private val log = LoggerFactory.getLogger(BootloaderMsgAB::class.java.simpleName)
init {
assert(SIZE == Struct3(FORMAT_STRING).calcSize())
assert(SIZE == Struct(FORMAT_STRING).calcSize())
}
}
constructor(fis: FileInputStream) : this() {
val info = Struct3(FORMAT_STRING).unpack(fis)
val info = Struct(FORMAT_STRING).unpack(fis)
this.slotSuffix = info[0] as String
this.updateChannel = info[1] as String
this.reserved = info[2] as ByteArray
}
fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack(
return Struct(FORMAT_STRING).pack(
this.slotSuffix,
this.updateChannel,
byteArrayOf())

@ -14,7 +14,7 @@
package cfig.bcb
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import cfig.helper.Helper
import org.slf4j.LoggerFactory
import java.io.FileInputStream
@ -33,12 +33,12 @@ data class VirtualABMsg(
private const val MAGIC = "b00a7456"
init {
assert(SIZE == Struct3(FORMAT_STRING).calcSize())
assert(SIZE == Struct(FORMAT_STRING).calcSize())
}
}
constructor(fis: FileInputStream) : this() {
val info = Struct3(FORMAT_STRING).unpack(fis)
val info = Struct(FORMAT_STRING).unpack(fis)
this.version = (info[0] as ByteArray)[0].toInt()
this.magic = info[1] as ByteArray
this.mergeStatus = (info[2] as ByteArray)[0].toInt()
@ -50,7 +50,7 @@ data class VirtualABMsg(
}
fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack(
return Struct(FORMAT_STRING).pack(
this.version,
this.magic,
this.mergeStatus,

@ -14,7 +14,7 @@
package cfig.bootimg
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import cfig.utils.EnvironmentVerifier
import cfig.bootimg.cpio.AndroidCpio
import cfig.utils.DTC
@ -363,7 +363,7 @@ class Common {
fun probeHeaderVersion(fileName: String): Int {
return FileInputStream(fileName).let { fis ->
fis.skip(40)
Struct3.IntShip().get(fis, ByteOrder.LITTLE_ENDIAN)
Struct.IntShip().get(fis, ByteOrder.LITTLE_ENDIAN)
}
}

@ -14,7 +14,7 @@
package cfig.bootimg.cpio
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import org.apache.commons.compress.archivers.cpio.CpioConstants
import java.util.*
@ -38,13 +38,13 @@ class NewAsciiCpio(
var c_check: Int = 0
) {
init {
if (SIZE != Struct3(FORMAT_STRING).calcSize()) {
if (SIZE != Struct(FORMAT_STRING).calcSize()) {
throw RuntimeException("cpio format check failed")
}
}
fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack(
return Struct(FORMAT_STRING).pack(
String.format(Locale.getDefault(), "%s", c_magic),
String.format(Locale.getDefault(), "%08x", c_ino),
String.format(Locale.getDefault(), "%08x", c_mode),

@ -14,7 +14,7 @@
package cfig.bootimg.v2
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import cfig.helper.Helper
import cfig.bootimg.Common
import org.slf4j.LoggerFactory
@ -58,7 +58,7 @@ open class BootHeaderV2(
return
}
log.warn("BootImgHeader constructor")
val info = Struct3(FORMAT_STRING).unpack(iS)
val info = Struct(FORMAT_STRING).unpack(iS)
assert(20 == info.size)
if (info[0] != magic) {
throw IllegalArgumentException("stream doesn't look like Android Boot Image Header")
@ -110,7 +110,7 @@ open class BootHeaderV2(
(11..14).forEach { add(2.0.pow(it).toLong()) }
}
assert(pageSizeChoices.contains(pageSize.toLong())) { "invalid parameter [pageSize=$pageSize], (choose from $pageSizeChoices)" }
return Struct3(FORMAT_STRING).pack(
return Struct(FORMAT_STRING).pack(
magic,
//10I
kernelLength,
@ -168,7 +168,7 @@ open class BootHeaderV2(
const val BOOT_IMAGE_HEADER_V0_SIZE = 0
init {
assert(BOOT_IMAGE_HEADER_V2_SIZE == Struct3(FORMAT_STRING).calcSize())
assert(BOOT_IMAGE_HEADER_V2_SIZE == Struct(FORMAT_STRING).calcSize())
}
}

@ -14,7 +14,7 @@
package cfig.bootimg.v3
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import cfig.bootimg.Common
import org.slf4j.LoggerFactory
import java.io.InputStream
@ -35,7 +35,7 @@ class BootHeaderV3(
return
}
log.warn("BootImgHeaderV3/V4 constructor")
val info = Struct3(FORMAT_STRING).unpack(iS)
val info = Struct(FORMAT_STRING).unpack(iS)
assert(12 == info.size)
if (info[0] != magic) {
throw IllegalArgumentException("stream doesn't look like Android Boot Image V3 Header")
@ -56,7 +56,7 @@ class BootHeaderV3(
}
fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack(
return Struct(FORMAT_STRING).pack(
magic,
kernelSize,
ramdiskSize,
@ -106,7 +106,7 @@ class BootHeaderV3(
const val pageSize: Int = 4096
init {
assert(BOOT_IMAGE_HEADER_V4_SIZE == Struct3(FORMAT_STRING).calcSize()) {
assert(BOOT_IMAGE_HEADER_V4_SIZE == Struct(FORMAT_STRING).calcSize()) {
"internal error: expected size $BOOT_IMAGE_HEADER_V4_SIZE "
}
}

@ -15,7 +15,7 @@
package cfig.bootimg.v3
import avb.AVBInfo
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import cfig.Avb
import cfig.utils.EnvironmentVerifier
import cfig.bootimg.Common.Companion.deleleIfExists
@ -124,7 +124,7 @@ data class VendorBoot(
const val SIZE = 108
init {
assert(Struct3(FORMAT_STRING).calcSize() == SIZE)
assert(Struct(FORMAT_STRING).calcSize() == SIZE)
}
}
@ -132,19 +132,19 @@ data class VendorBoot(
if (iS == null) {
return
}
val info = Struct3(FORMAT_STRING).unpack(iS)
val info = Struct(FORMAT_STRING).unpack(iS)
assert((3 + 1 + 1) == info.size)
this.size = (info[0] as UInt).toInt()
this.offset = (info[1] as UInt).toInt()
this.type = VrtType.fromInt((info[2] as UInt).toInt())
this.name = info[3] as String
this.boardId = info[4] as ByteArray
this.boardIdStr = Struct3.StringFleet().get(boardId, ByteOrder.LITTLE_ENDIAN)
this.boardIdStr = Struct.StringFleet().get(boardId, ByteOrder.LITTLE_ENDIAN)
this.file = dumpFile
}
fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack(this.size, this.offset, this.type.ordinal, this.name, this.boardId)
return Struct(FORMAT_STRING).pack(this.size, this.offset, this.type.ordinal, this.name, this.boardId)
}
override fun toString(): String {
@ -445,7 +445,7 @@ data class VendorBoot(
ramdisk_table.ramdidks.forEachIndexed { index, it ->
log.info("dumping vendor ramdisk ${index + 1}/${ramdisk_table.ramdidks.size} ...")
addArgument("--ramdisk_type").addArgument(it.type.toString())
Struct3("${VrtEntry.VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}i").unpack(ByteArrayInputStream(it.boardId))
Struct("${VrtEntry.VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}i").unpack(ByteArrayInputStream(it.boardId))
.forEachIndexed { boardIdIndex, boardIdValue ->
addArgument("--board_id$boardIdIndex")
addArgument("0x" + Integer.toHexString((boardIdValue as Int)))

@ -14,7 +14,7 @@
package cfig.bootimg.v3
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import org.slf4j.LoggerFactory
import java.io.InputStream
@ -41,7 +41,7 @@ class VendorBootHeader(
return
}
log.warn("VendorBootHeader constructor")
val info = Struct3(FORMAT_STRING).unpack(iS)
val info = Struct(FORMAT_STRING).unpack(iS)
assert(16 == info.size)
if (info[0] != magic) {
throw IllegalArgumentException("stream doesn't look like Android Vendor Boot Image")
@ -91,7 +91,7 @@ class VendorBootHeader(
}
fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack(
return Struct(FORMAT_STRING).pack(
magic,
headerVersion,
pageSize,
@ -134,7 +134,7 @@ class VendorBootHeader(
"I" //[v4] bootconfig size
init {
assert(Struct3(FORMAT_STRING).calcSize() == VENDOR_BOOT_IMAGE_HEADER_V4_SIZE)
assert(Struct(FORMAT_STRING).calcSize() == VENDOR_BOOT_IMAGE_HEADER_V4_SIZE)
}
}

@ -1,7 +1,7 @@
package utils
import avb.AVBInfo
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import cfig.Avb
import cfig.bootimg.Common
import cfig.bootimg.Signer
@ -44,7 +44,7 @@ class Dtbo(
internal const val SIZE = 32
init {
assert(Struct3(FORMAT_STRING).calcSize() == SIZE)
assert(Struct(FORMAT_STRING).calcSize() == SIZE)
}
}
@ -52,7 +52,7 @@ class Dtbo(
if (iS == null) {
return
}
val info = Struct3(FORMAT_STRING).unpack(iS)
val info = Struct(FORMAT_STRING).unpack(iS)
assert(8 == info.size)
if ((info[0] as UInt).toLong() != magic) {
throw IllegalArgumentException("stream doesn't look like DTBO header")
@ -73,7 +73,7 @@ class Dtbo(
}
fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack(
return Struct(FORMAT_STRING).pack(
magic,
totalSize,
headerSize,
@ -103,12 +103,12 @@ class Dtbo(
internal const val SIZE = 32
init {
assert(Struct3(FORMAT_STRING).calcSize() == SIZE)
assert(Struct(FORMAT_STRING).calcSize() == SIZE)
}
}
constructor(iS: InputStream) : this() {
val info = Struct3(FORMAT_STRING).unpack(iS)
val info = Struct(FORMAT_STRING).unpack(iS)
assert(8 == info.size)
entrySize = info[0] as Int
entryOffset = info[1] as Int
@ -121,7 +121,7 @@ class Dtbo(
}
fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack(
return Struct(FORMAT_STRING).pack(
entrySize,
entryOffset,
id,

@ -25,7 +25,6 @@ import java.math.RoundingMode
import java.nio.file.Files
import java.nio.file.Paths
import java.security.*
import java.security.interfaces.RSAPrivateKey
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import javax.crypto.Cipher
@ -35,14 +34,15 @@ class KeyUtilTest {
fun parseKeys() {
val keyFile = "../" + Algorithms.get("SHA256_RSA2048")!!.defaultKey
println("Key: $keyFile")
val k = (CryptoHelper.KeyBox.parse2(File(keyFile.replace("pem", "pk8")).readBytes()) as Array<*>)[2] as RSAPrivateKey
println(k.privateExponent)
println(k.modulus)
val k2 = (CryptoHelper.KeyBox.parse2(File(keyFile).readBytes()) as Array<*>)[2] as org.bouncycastle.asn1.pkcs.RSAPrivateKey
println(k2.privateExponent)
println(k2.modulus)
val pk8File = keyFile.replace("pem", "pk8")
val k3 = CryptoHelper.KeyBox.parse4(File(pk8File).readBytes())
//println((k3.key as RSAPrivateKey).privateExponent)
//println((k3.key as RSAPrivateKey).modulus)
println("Key: $pk8File")
val k2 = CryptoHelper.KeyBox.parse4(File(keyFile).readBytes()).key as org.bouncycastle.asn1.pkcs.RSAPrivateKey
//println(k2.privateExponent)
//println(k2.modulus)
//KeyHelper2.parseRsaPk8(FileInputStream(keyFile).readAllBytes())
CryptoHelper.KeyBox.parse2(File(keyFile.replace("pem", "pk8")).readBytes())
}
@Test
@ -87,7 +87,7 @@ class KeyUtilTest {
val expectedSig =
"28e17bc57406650ed78785fd558e7c1861cc4014c900d72b61c03cdbab1039e713b5bb19b556d04d276b46aae9b8a3999ccbac533a1cce00f83cfb83e2beb35ed7329f71ffec04fc2839a9b44e50abd66ea6c3d3bea6705e93e9139ecd0331170db18eba36a85a78bc49a5447260a30ed19d956cb2f8a71f6b19e57fdca43e052d1bb7840bf4c3efb47111f4d77764236d2e013fbf3b2577e4a3e01c9d166a5e890ef96210882e6e88ceca2fe3a2201f4961210d4ec6167f5dfd0e038e4a146f960caecab7d15ba65f6edcf5dbd25f5af543cfb8da4338bdbc872eec3f8e72aa8db679099e70952d3f7176c0b9111bf20ad1390eab1d09a859105816fdf92fbb"
val privkFile = "../" + Algorithms.get("SHA256_RSA2048")!!.defaultKey.replace("pem", "pk8")
val k = (CryptoHelper.KeyBox.parse2(Files.readAllBytes(Paths.get(privkFile))) as Array<*>)[2] as PrivateKey
val k = CryptoHelper.KeyBox.parse4(Files.readAllBytes(Paths.get(privkFile))).key as PrivateKey
val encData = CryptoHelper.Signer.rawRsa(k, data)
assertEquals(expectedSig, Helper.toHexString(encData))
}
@ -108,7 +108,7 @@ class KeyUtilTest {
Helper.fromHexString("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004206317a4c8d86accc8258c1ac23ef0ebd18bc3301033")
val signature = Signature.getInstance("NONEwithRSA")
val keyFile = "../" + Algorithms.get("SHA256_RSA2048")!!.defaultKey.replace("pem", "pk8")
val k = (CryptoHelper.KeyBox.parse2(Files.readAllBytes(Paths.get(keyFile))) as Array<*>)[2] as PrivateKey
val k = CryptoHelper.KeyBox.parse4(Files.readAllBytes(Paths.get(keyFile))).key as PrivateKey
signature.initSign(k)
signature.update(data)
println("data size " + data.size)
@ -195,9 +195,8 @@ class KeyUtilTest {
val data = KeyUtilTest::class.java.classLoader.getResourceAsStream("data").readAllBytes()
println(Helper.toHexString(data))
//@formatter:off
val privKey = (
(CryptoHelper.KeyBox.parse2(KeyUtilTest::class.java.classLoader.getResourceAsStream("testkey.pk8").readAllBytes()) as Array<*>)[2]
) as PrivateKey
val keyData = KeyUtilTest::class.java.classLoader.getResourceAsStream("testkey.pk8").readAllBytes()
val privKey = CryptoHelper.KeyBox.parse4(keyData).key as PrivateKey
//@formatter:on
println("sha256=" + Helper.toHexString(CryptoHelper.Hasher.sha256(data)))
val signedHash = CryptoHelper.Signer.sha256rsa(data, privKey)

@ -34,8 +34,7 @@ class BlobTest {
assertEquals(expectedKeyEnc, Helper.toHexString(encodedKey))
run {//decode pub key and check
val decodedKey = CryptoHelper.KeyBox.decodeRSAkey(encodedKey)
//val rsa = KeyHelper.parsePemPrivateKeyBC(ByteArrayInputStream(Helper.fromHexString(keyStr))) //BC RSA
val rsa = (CryptoHelper.KeyBox.parse2(Helper.fromHexString(keyStr)) as Array<*>)[2] as RSAPrivateKey //BC RSA
val rsa = CryptoHelper.KeyBox.parse4(Helper.fromHexString(keyStr)).key as RSAPrivateKey //BC RSA
assert(rsa.modulus.equals(decodedKey.modulus))
assert(rsa.publicExponent.equals(decodedKey.publicExponent))
}

@ -33,6 +33,7 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("cc.cfig:io:0.2")
implementation("com.google.guava:guava:31.0.1-jre")
implementation("org.slf4j:slf4j-api:1.7.32")
implementation("org.slf4j:slf4j-simple:1.7.32")

@ -1,7 +1,8 @@
@file:Suppress("JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE")
package cfig.helper
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import com.google.common.math.BigIntegerMath
import org.apache.commons.exec.CommandLine
import org.apache.commons.exec.DefaultExecutor
@ -10,13 +11,17 @@ import org.apache.commons.exec.PumpStreamHandler
import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.bouncycastle.util.io.pem.PemReader
import org.slf4j.LoggerFactory
import java.io.*
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.InputStreamReader
import java.math.BigInteger
import java.math.RoundingMode
import java.security.KeyFactory
import java.security.KeyStore
import java.security.MessageDigest
import java.security.Security
import java.security.cert.Certificate
import java.security.cert.CertificateFactory
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.RSAPrivateKeySpec
@ -24,94 +29,84 @@ import java.security.spec.RSAPublicKeySpec
import java.security.spec.X509EncodedKeySpec
import java.util.*
import javax.crypto.Cipher
import kotlin.reflect.KClass
class CryptoHelper {
class KeyBox {
enum class KeyFormat {
PEM, //header + metadata + base64 der
DER, // der format
}
class KeyBox(val fmt: KeyFormat, val clazz: KClass<*>, val key: Any) {
companion object {
fun parseToPk8(kFile: String): OpenSslHelper.PK8RsaKey {
val inBytes = File(kFile).readBytes()
val k = parse2(inBytes) as Array<*>
val kType = if ((k[1] as String) == "PEM") OpenSslHelper.KeyFormat.PEM else OpenSslHelper.KeyFormat.DER
return when (k[2]) {
is org.bouncycastle.asn1.pkcs.RSAPrivateKey -> {
OpenSslHelper.PK1Key(kType, inBytes).toPk8(OpenSslHelper.KeyFormat.PEM)
}
is java.security.interfaces.RSAPrivateKey -> {
OpenSslHelper.PK8RsaKey(kType, inBytes).let {
if (it.format == OpenSslHelper.KeyFormat.DER) {
it.transform(OpenSslHelper.KeyFormat.DER, OpenSslHelper.KeyFormat.PEM)
} else {
it
}
}
}
else -> {
throw IllegalArgumentException("unknown PK1/PK8 private key")
}
}
}
fun parse2(data: ByteArray): Any {
var bSuccess = false
var retType = "NA"
fun parse4(data: ByteArray): KeyBox {
var ret: Any = false
var retClazz: KClass<*>
val p = PemReader(InputStreamReader(ByteArrayInputStream(data))).readPemObject()
if (p != null) {
log.debug("parse PEM: " + p.type)
bSuccess = true
retType = "PEM"
log.info("parse PEM: " + p.type)
ret = when (p.type) {
"RSA PUBLIC KEY" -> {
org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(p.content) as org.bouncycastle.asn1.pkcs.RSAPublicKey
}
"RSA PRIVATE KEY" -> {
org.bouncycastle.asn1.pkcs.RSAPrivateKey.getInstance(p.content) as org.bouncycastle.asn1.pkcs.RSAPrivateKey
}
"PUBLIC KEY" -> {
val keySpec = X509EncodedKeySpec(p.content)
KeyFactory.getInstance("RSA")
.generatePublic(keySpec) as java.security.interfaces.RSAPublicKey
"RSA PUBLIC KEY", "PUBLIC KEY" -> {
try {
retClazz = org.bouncycastle.asn1.pkcs.RSAPublicKey::class
org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(p.content) as org.bouncycastle.asn1.pkcs.RSAPublicKey
} catch (e: Exception) {
val keySpec = X509EncodedKeySpec(p.content)
retClazz = java.security.interfaces.RSAPublicKey::class
KeyFactory.getInstance("RSA")
.generatePublic(keySpec) as java.security.interfaces.RSAPublicKey
}
}
"PRIVATE KEY" -> {
val keySpec = PKCS8EncodedKeySpec(p.content)
KeyFactory.getInstance("RSA")
.generatePrivate(keySpec) as java.security.interfaces.RSAPrivateKey
"RSA PRIVATE KEY", "PRIVATE KEY" -> {
try {
retClazz = org.bouncycastle.asn1.pkcs.RSAPrivateKey::class
org.bouncycastle.asn1.pkcs.RSAPrivateKey.getInstance(p.content) as org.bouncycastle.asn1.pkcs.RSAPrivateKey
} catch (e: Exception) {
retClazz = sun.security.rsa.RSAPrivateCrtKeyImpl::class
val keySpec = PKCS8EncodedKeySpec(p.content)
KeyFactory.getInstance("RSA")
.generatePrivate(keySpec) as sun.security.rsa.RSAPrivateCrtKeyImpl
}
}
"CERTIFICATE REQUEST" -> {
retClazz = PKCS10CertificationRequest::class
PKCS10CertificationRequest(p.content)
}
"CERTIFICATE" -> {
retClazz = Certificate::class
CertificateFactory.getInstance("X.509").generateCertificate(ByteArrayInputStream(p.content))
}
else -> throw IllegalArgumentException("unsupported type: ${p.type}")
}
return arrayOf(bSuccess, retType, ret)
return KeyBox(KeyFormat.PEM, retClazz, ret)
} else {
var bSuccess = false
//try 1
try {
val spec = PKCS8EncodedKeySpec(data)
val privateKey = KeyFactory.getInstance("RSA").generatePrivate(spec)
log.debug("Parse PKCS8:Private")
ret = privateKey
retType = "RAW"
bSuccess = true
} catch (e: java.security.spec.InvalidKeySpecException) {
log.debug("not PKCS8:Private")
log.info("not PKCS8:Private")
}
if (bSuccess) return arrayOf(bSuccess, retType, ret)
if (bSuccess) return KeyBox(
KeyFormat.DER,
sun.security.rsa.RSAPrivateCrtKeyImpl::class,
ret
)
//try 2
try {
log.debug("Parse X509:Public")
val spec = X509EncodedKeySpec(data)
retType = "RAW"
ret = KeyFactory.getInstance("RSA").generatePublic(spec)
bSuccess = true
} catch (e: java.security.spec.InvalidKeySpecException) {
log.debug(e.toString())
log.debug("not X509:Public")
}
if (bSuccess) return arrayOf(bSuccess, retType, ret)
if (bSuccess) return KeyBox(KeyFormat.DER, String::class, ret)
//try 3: jks
try {
@ -120,24 +115,21 @@ class CryptoHelper {
val ks = KeyStore.getInstance("JKS")
ks.load(ByteArrayInputStream(data), envPassword.toCharArray())
ret = ks
retType = "JKS"
bSuccess = true
} catch (e: IOException) {
if (e.toString().contains("Keystore was tampered with, or password was incorrect")) {
log.info("JKS password wrong #1")
bSuccess = false
retType = "JKS"
ret = KeyStore.getInstance("JKS")
}
if (e.toString().contains("keystore password was incorrect")) {
log.info("JKS password wrong #2")
bSuccess = false
retType = "JKS"
ret = KeyStore.getInstance("JKS")
}
}
//at last
return arrayOf(bSuccess, retType, ret)
return KeyBox(KeyFormat.DER, String::class, ret)
}
}
@ -186,7 +178,7 @@ class CryptoHelper {
val n0inv = b.minus(rsa.modulus.modInverse(b)).toLong()
val rrModn = BigInteger.valueOf(4).pow(numBits).rem(rsa.modulus)
val unsignedModulo = rsa.modulus.toByteArray().sliceArray(1..numBits / 8) //remove sign byte
return Struct3("!II${numBits / 8}b${numBits / 8}b").pack(
return Struct("!II${numBits / 8}b${numBits / 8}b").pack(
numBits,
n0inv,
unsignedModulo,
@ -195,15 +187,15 @@ class CryptoHelper {
}
fun decodeRSAkey(key: ByteArray): java.security.interfaces.RSAPublicKey {
val ret = Struct3("!II").unpack(ByteArrayInputStream(key))
val ret = Struct("!II").unpack(ByteArrayInputStream(key))
val numBits = (ret[0] as UInt).toInt()
val n0inv = (ret[1] as UInt).toLong()
val ret2 = Struct3("!II${numBits / 8}b${numBits / 8}b").unpack(ByteArrayInputStream(key))
val ret2 = Struct("!II${numBits / 8}b${numBits / 8}b").unpack(ByteArrayInputStream(key))
val unsignedModulo = ret2[2] as ByteArray
val rrModn = BigInteger(ret2[3] as ByteArray)
log.debug("n0inv=$n0inv, unsignedModulo=${Helper.toHexString(unsignedModulo)}, rrModn=$rrModn")
val exponent = 65537L
val modulus = BigInteger(Helper.join(Struct3("x").pack(0), unsignedModulo))
val modulus = BigInteger(Helper.join(Struct("x").pack(0), unsignedModulo))
val keySpec = RSAPublicKeySpec(modulus, BigInteger.valueOf(exponent))
return KeyFactory.getInstance("RSA").generatePublic(keySpec) as java.security.interfaces.RSAPublicKey
}

@ -14,7 +14,7 @@
package cfig.helper
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import org.apache.commons.exec.CommandLine
import org.apache.commons.exec.DefaultExecutor
import org.apache.commons.exec.ExecuteException
@ -61,9 +61,9 @@ class Helper {
val paddingNeeded = round_to_multiple(this.size.toUInt(), pageSize) - this.size.toUInt()
return if (paddingNeeded > 0u) {
if (paddingHead) {
join(Struct3("${paddingNeeded}x").pack(null), this)
join(Struct("${paddingNeeded}x").pack(null), this)
} else {
join(this, Struct3("${paddingNeeded}x").pack(null))
join(this, Struct("${paddingNeeded}x").pack(null))
}
} else {
this

@ -2,52 +2,29 @@
package cfig.helper
import cfig.helper.OpenSslHelper.KeyFormat
import org.bouncycastle.util.io.pem.PemReader
import org.bouncycastle.asn1.ASN1Primitive
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo
import org.bouncycastle.util.io.pem.PemObject
import org.bouncycastle.util.io.pem.PemWriter
import org.slf4j.LoggerFactory
import java.io.*
import java.security.cert.Certificate
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.FileWriter
import java.math.BigInteger
import java.security.KeyFactory
import java.security.KeyPairGenerator
import java.security.spec.RSAPublicKeySpec
import java.util.*
import kotlin.system.exitProcess
class Launcher {
companion object {
private const val hint1 = "private RSA key -> RSA public key(PEM)"
private const val hint2 = "private RSA key -> RSA public key(DER)"
private const val hint3 = "(PEM) --> (DER)"
private const val hint4 = "RSA private: PK1 <=> PK8(PEM)"
private const val hint5 = "RSA private(PK8): => Public Key(PK8, PEM)"
private const val hint7 = "RSA private: PK1 => PK8(DER)"
private const val hint10 = "PK8 RSA: PEM <=> DER"
private const val hint11 = "RSA public(PK8): PEM => DER"
private const val hintCsr = "PK1 RSA PEM ==> CSR"
private const val hintCrt = "PK1 RSA PEM ==> CRT"
private const val hint22 = "CRT ==> JKS"
private const val hintJks = "Generate JKS"
val allSupportedCommands =
listOf("genrsa", "toPublicKey", "decodePEM", "toPk8", "toPk1", "toJks", "toCsr", "toCrt").toString()
private val log = LoggerFactory.getLogger(Launcher::class.java)
fun help() {
println("Help:")
println("\tcrypo.list")
println("\tcrypto.key.parse <file>")
println("\tcrypto.key.0 : <key_len> <out>")
println("\tcrypto.key.1 : $hint1")
println("\tcrypto.key.2 : $hint2")
println("\tcrypto.key.3 : $hint3")
println("\tcrypto.key.4 : $hint4")
println("\tcrypto.key.5 : $hint5")
println("\tcrypto.key.7 : $hint7")
println("\tcrypto.key.10 : $hint10")
println("\tcrypto.key.11 : $hint11")
println("\tcrypto.key.22 : $hint22")
println("\tcrypto.key.csr : $hintCsr")
println("\tcrypto.key.21 : $hintCrt")
println("\tcrypto.key.jks : $hintJks")
}
fun getSize(size: Long): String? {
val kilo = 1024L
val mega = kilo * kilo
@ -73,121 +50,71 @@ class Launcher {
}
}
//https://stackoverflow.com/questions/7611383/generating-rsa-keys-in-pkcs1-format-in-java
private fun dumpPem(pem: PemObject, outFile: String) {
FileWriter(outFile).use { fw ->
log.info("Writing ${pem.type}to $outFile")
PemWriter(fw).use { pw ->
pw.writeObject(pem)
}
}
}
private fun dumpPublicKey(n: BigInteger, e: BigInteger, outFile: String) {
val pubK = KeyFactory.getInstance("RSA").generatePublic(RSAPublicKeySpec(n, e))
FileWriter(outFile).use { fw ->
log.info("Writing public key to $outFile")
PemWriter(fw).use { pw ->
pw.writeObject(PemObject("RSA PUBLIC KEY", pubK.encoded))
}
}
}
private fun pk8toPk1(pk8: java.security.PrivateKey): ASN1Primitive {
return PrivateKeyInfo.getInstance(pk8.encoded).parsePrivateKey().toASN1Primitive()
}
fun processCommand(inFile: String, info2: Properties) {
FileInputStream(inFile).use { fis ->
info2.load(fis)
}
log.warn("CMD=" + info2.getProperty("cmd"))
val kFile = info2.getProperty("file")
when (val cmd = info2.getProperty("cmd")) {
"genrsa" -> {
val kLen = info2.getProperty("rsa.len").toInt()
OpenSslHelper.PK1Key.generate(kLen).apply {
writeTo(kFile)
val keyPair = KeyPairGenerator.getInstance("RSA").let {
it.initialize(kLen)
it.generateKeyPair()
}
log.info("RSA key(len=$kLen) written to $kFile")
}
"toPublicKey" -> {
val k = CryptoHelper.KeyBox.parse2(File(kFile).readBytes()) as Array<*>
val bSuccess = k[0] as Boolean
val bType = k[1] as String
var outFile = File(kFile).name + ".pub"
when (k[2]) {
is org.bouncycastle.asn1.pkcs.RSAPrivateKey -> {
val rsa = OpenSslHelper.PK1Key(data = File(kFile).readBytes())
run { //action 1
val hint = "private RSA key -> RSA public key(PEM)"
outFile = File(kFile).name + ".pem.pub"
val rsaPubPEM = rsa.getPublicKey(KeyFormat.PEM).apply {
writeTo(outFile)
log.info("$hint: $kFile => $outFile")
}
}
run { //action 2
val hint = "private RSA key -> RSA public key(DER)"
outFile = File(kFile).name + ".der.pub"
val rsaPubDer = rsa.getPublicKey(KeyFormat.DER).apply {
writeTo(outFile)
log.info("$hint: $kFile => $outFile")
}
}
}
else -> {
}
FileOutputStream(kFile, false).use {
it.write(keyPair.private.encoded)
log.info("RSA priv key(len=$kLen) written to $kFile")
}
}
"decodePEM" -> {
val hint = "(PEM) --> (DER)"
val outFile = File(kFile).name + ".raw"
val decodeFromPem = CryptoHelper.KeyBox.decodePem(File(kFile).readText())
File(outFile).writeBytes(decodeFromPem)
log.info("$hint: $kFile => $outFile")
}
"toCrt" -> {
val hint = "PK1 RSA PEM ==> CRT"
val outFile = File(kFile).name + ".crt"
CryptoHelper.KeyBox.parseToPk8(kFile)
.toPk1()
.toV1Cert(info2.getProperty("csr.info"))
.writeTo(outFile)
log.info("$hint: $kFile => $outFile")
}
"toCsr" -> {
val hint = "PK1 RSA PEM ==> CSR"
val outFile = File(kFile).name + ".csr"
val inBytes = File(kFile).readBytes()
val k = (CryptoHelper.KeyBox.parse2(inBytes) as Array<*>)[2]
require(k is org.bouncycastle.asn1.pkcs.RSAPrivateKey) {
"${k!!::class} is not org.bouncycastle.asn1.pkcs.RSAPrivateKey"
dumpPem(PemObject("RSA PRIVATE KEY", pk8toPk1(keyPair.private).encoded), "$kFile.pem.pk1")
FileOutputStream("$kFile.pub", false).use {
it.write(keyPair.public.encoded)
log.info("RSA pub key(len=$kLen) written to $kFile.pub")
}
OpenSslHelper.PK1Key(KeyFormat.PEM, inBytes).toCsr(info2.getProperty("csr.info")).writeTo(outFile)
log.info("$hint: $kFile => $outFile")
}
"toJks" -> {
val hint = "RSA ==> JKS"
val outFile = File(kFile).name + ".jks"
val pk8 = CryptoHelper.KeyBox.parseToPk8(kFile)
val crt = pk8.toPk1().toV1Cert(info2.getProperty("csr.info"))
OpenSslHelper.Pfx(
name = info2.getProperty("pfx.alias") ?: "androiddebugkey",
thePassword = info2.getProperty("pfx.password") ?: "somepassword"
)
.generate(pk8.toPk1(), crt)
.toJks()
.writeTo(outFile)
log.info("$hint: $kFile => $outFile")
}
"toPk1" -> {
val inBytes = File(kFile).readBytes()
val k = CryptoHelper.KeyBox.parse2(inBytes) as Array<*>
val kType = if ((k[1] as String) == "PEM") KeyFormat.PEM else KeyFormat.DER
val outFile = File(info2.getProperty("file")).name + ".pk1"
require(k[2] is java.security.interfaces.RSAPrivateKey) {
"${k[2]!!::class} is NOT java.security.interfaces.RSAPrivateKey"
"toPub" -> {
val k = CryptoHelper.KeyBox.parse4(File(kFile).readBytes())
if (k.key is org.bouncycastle.asn1.pkcs.RSAPrivateKey) {
dumpPublicKey(k.key.modulus, k.key.publicExponent, "pub.k")
} else if (k.key is sun.security.rsa.RSAPrivateCrtKeyImpl) {
dumpPublicKey(k.key.modulus, k.key.publicExponent, "pub.k")
} else {
throw IllegalArgumentException("not supported")
}
val hint = "RSA private: PK8($kType) => PK1(PEM)"
log.info("Running: $hint")
OpenSslHelper.PK8RsaKey(format = kType, data = File(kFile).readBytes())
.toPk1()
.also {
log.info("$kFile -> $outFile")
}
.writeTo(outFile)
}
"toPk8" -> {
val inBytes = File(kFile).readBytes()
val k = CryptoHelper.KeyBox.parse2(inBytes) as Array<*>
val kType = if ((k[1] as String) == "PEM") KeyFormat.PEM else KeyFormat.DER
val outFileStem = File(info2.getProperty("file")).name
require(k[2] is org.bouncycastle.asn1.pkcs.RSAPrivateKey)
val hint = "RSA private: PK1 => PK8(PEM,DER)"
log.info("Running: $hint")
OpenSslHelper.PK1Key(data = File(kFile).readBytes()).let { rsa ->
rsa.toPk8(KeyFormat.PEM).writeTo("$outFileStem.pem_pk8")
rsa.toPk8(KeyFormat.DER).writeTo("$outFileStem.der_pk8")
}
log.info("$hint: $kFile => $outFileStem.pem_pk8, $outFileStem.der_pk8")
"parse" -> {
val k = CryptoHelper.KeyBox.parse4(File(kFile).readBytes())
log.info("fmt=" + k.fmt.toString())
log.info("clazz=" + k.clazz)
log.info("key=" + k.key)
}
else -> {
//pass
@ -199,157 +126,41 @@ class Launcher {
val preFile = info2.getProperty("file")
info2["file"] = (if (preFile == null) "" else "$preFile,") + File(inFile).canonicalFile.path
info2["propFile"] = File(info2.getProperty("file")).name + ".prop"
val k = CryptoHelper.KeyBox.parse2(File(inFile).readBytes()) as Array<*>
val bSuccess = k[0] as Boolean
val bType = k[1] as String
if (bSuccess) {
info2["fileType"] = bType
info2["fileClazz"] = k[2]!!::class.toString()
log.info("Recognized $bType: " + k[2]!!::class)
if (k[2] is sun.security.x509.X509CertImpl) {
val crt = k[2] as sun.security.x509.X509CertImpl
val crtSubj = (crt.get("x509.info") as sun.security.x509.X509CertInfo).get("subject").toString()
val subj2 = crtSubj
.replace(", ", "/")
.replace("EMAILADDRESS", "emailAddress")
.replace("\\s".toRegex(), "")
info2.setProperty("csr.info", "/$subj2")
}
if (k[2] is sun.security.rsa.RSAPrivateCrtKeyImpl) {
val pk8 = k[2] as sun.security.rsa.RSAPrivateCrtKeyImpl
info2.setProperty("rsa.len", pk8.modulus.bitLength().toString())
info2["rsa.modulus"] = Helper.toHexString(pk8.modulus.toByteArray())
info2["rsa.privateExponent"] = Helper.toHexString(pk8.privateExponent.toByteArray())
info2["rsa.publicExponent"] = Helper.toHexString(pk8.publicExponent.toByteArray())
info2["rsa.primeP"] = Helper.toHexString(pk8.primeP.toByteArray())
info2["rsa.primeQ"] = Helper.toHexString(pk8.primeQ.toByteArray())
}
if (k[2] is org.bouncycastle.asn1.pkcs.RSAPrivateKey) {
val rsa = k[2] as org.bouncycastle.asn1.pkcs.RSAPrivateKey
info2.setProperty("rsa.len", rsa.modulus.bitLength().toString())
info2["rsa.modulus"] = Helper.toHexString(rsa.modulus.toByteArray())
info2["rsa.privateExponent"] = Helper.toHexString(rsa.privateExponent.toByteArray())
info2["rsa.publicExponent"] = Helper.toHexString(rsa.publicExponent.toByteArray())
info2["rsa.primeP"] = Helper.toHexString(rsa.prime1.toByteArray())
info2["rsa.primeQ"] = Helper.toHexString(rsa.prime2.toByteArray())
}
} else {
if (bType == "NA") {
log.warn("Unrecognized file $inFile")
} else {
log.warn("Recognized but not parsed $bType: $inFile")
}
val k = CryptoHelper.KeyBox.parse4(File(inFile).readBytes())
info2["fileType"] = k.fmt
info2["fileClazz"] = k.clazz
log.info("Recognized ${k.fmt}: " + k.clazz)
if (k.key is sun.security.x509.X509CertImpl) {
val crt = k.key
val crtSubj = (crt.get("x509.info") as sun.security.x509.X509CertInfo).get("subject").toString()
val subj2 = crtSubj
.replace(", ", "/")
.replace("EMAILADDRESS", "emailAddress")
.replace("\\s".toRegex(), "")
info2.setProperty("csr.info", "/$subj2")
}
}
fun todo(args: Array<String>) {
when (args[0]) {
"crypo.list" -> {
CryptoHelper.listAll()
}
"crypto.key.5" -> {
val hint = "RSA private(PK8): => Public Key(PK8, PEM)"
val kFile = args[1]
val outFile = args[2]
require((CryptoHelper.KeyBox.parse2(File(kFile).readBytes()) as Array<*>)[2] is org.bouncycastle.asn1.pkcs.RSAPrivateKey)
val pk8rsa = OpenSslHelper.PK8RsaKey(KeyFormat.PEM, File(kFile).readBytes())
pk8rsa.getPublicKey().writeTo(outFile)
log.info("$hint: $kFile => $outFile")
}
"crypto.key.10" -> {
//Action-10:
var hint = "PK8 RSA: PEM <=> DER"
val kFile = args[1]
val outFile = args[2]
val inBytes = File(kFile).readBytes()
require((CryptoHelper.KeyBox.parse2(inBytes) as Array<*>)[2] is java.security.interfaces.RSAPrivateKey)
val p = PemReader(InputStreamReader(ByteArrayInputStream(File(kFile).readBytes()))).readPemObject()
if (p != null) {//pem
hint = "PK8 RSA: PEM => DER"
OpenSslHelper.PK8RsaKey(KeyFormat.PEM, inBytes).transform(KeyFormat.PEM, KeyFormat.DER)
.writeTo(outFile)
} else {//der
hint = "PK8 RSA: DER => PEM"
OpenSslHelper.PK8RsaKey(KeyFormat.DER, inBytes).transform(KeyFormat.DER, KeyFormat.PEM)
.writeTo(outFile)
}
log.info("$hint: $kFile => $outFile")
}
"crypto.key.11" -> {
val hint = "RSA public(PK8): PEM => DER"
val kFile = args[1]
val outFile = args[2]
File(outFile).writeBytes(
CryptoHelper.KeyBox.decodePem(File(kFile).readText())
)
log.info("$hint: $kFile => $outFile")
}
"crypto.key.22" -> {
//Action-xx:
val hint = "CRT ==> JKS"
val kFile = args[1]
val crtFile = args[2]
val outFile = args[3]
require((CryptoHelper.KeyBox.parse2(File(crtFile).readBytes()) as Array<*>)[2] is Certificate)
val envPassword = System.getProperty("password") ?: "secretpassword"
val envAlias = System.getProperty("alias") ?: "someUnknownAlias"
val crt = OpenSslHelper.Crt(File(crtFile).readBytes())
val rsa = OpenSslHelper.PK1Key(KeyFormat.PEM, File(kFile).readBytes())
OpenSslHelper.Pfx(name = envAlias, thePassword = envPassword).generate(rsa, crt).toJks()
.writeTo(outFile)
log.info("$hint: $kFile => $outFile")
}
"crypto.key.jks" -> {
//Action-xx:
val hint = "Generate JKS"
val keypass = args[1]
val storepass = args[2]
val alias = args[3]
val outFile = args[4]
OpenSslHelper.Jks.generate(keypass, storepass, alias, null, outFile)
log.info("$hint: ==> $outFile")
}
"crypto.key.23" -> {
//Action-xx:
val hint = "PK1 ==> JKS"
val kFile = args[1]
val outFile = args[2]
val rsa = OpenSslHelper.PK1Key(KeyFormat.PEM, File(kFile).readBytes())
val crt = rsa.toV1Cert()
OpenSslHelper.Pfx(name = "androiddebugkey", thePassword = "somepassword").generate(rsa, crt).toJks()
.writeTo(outFile)
log.info("$hint: $kFile => $outFile")
}
"crypto.key.xx" -> {
//Action-xx:
val hint = ""
val kFile = args[1]
val k = CryptoHelper.KeyBox.parse2(File(kFile).readBytes()) as Array<*>
println(k[2]!!.toString())
val crt = k[2] as sun.security.x509.X509CertImpl
log.info("type=" + crt.type + ", name=" + crt.name)
for (item in crt.elements) {
log.info("# $item")
}
val crtInfo = crt.get("x509.info") as sun.security.x509.X509CertInfo
for (item in crtInfo.elements) {
log.info("## $item")
}
log.info("subject=>")
log.info(crtInfo.get("subject").toString())
log.info("issuer=>")
log.info(crtInfo.get("issuer").toString())
log.info("$hint: $kFile => ")
}
else -> {
Launcher.help()
exitProcess(1)
}
if (k.key is sun.security.rsa.RSAPrivateCrtKeyImpl) {
val pk8 = k.key
info2.setProperty("rsa.len", pk8.modulus.bitLength().toString())
info2["rsa.modulus"] = Helper.toHexString(pk8.modulus.toByteArray())
info2["rsa.privateExponent"] = Helper.toHexString(pk8.privateExponent.toByteArray())
info2["rsa.publicExponent"] = Helper.toHexString(pk8.publicExponent.toByteArray())
info2["rsa.primeP"] = Helper.toHexString(pk8.primeP.toByteArray())
info2["rsa.primeQ"] = Helper.toHexString(pk8.primeQ.toByteArray())
}
if (k.key is org.bouncycastle.asn1.pkcs.RSAPrivateKey) {
val rsa = k.key
info2.setProperty("rsa.len", rsa.modulus.bitLength().toString())
info2["rsa.modulus"] = Helper.toHexString(rsa.modulus.toByteArray())
info2["rsa.privateExponent"] = Helper.toHexString(rsa.privateExponent.toByteArray())
info2["rsa.publicExponent"] = Helper.toHexString(rsa.publicExponent.toByteArray())
info2["rsa.primeP"] = Helper.toHexString(rsa.prime1.toByteArray())
info2["rsa.primeQ"] = Helper.toHexString(rsa.prime2.toByteArray())
}
}
}
fun main(args: Array<String>) {
val log = LoggerFactory.getLogger("main")
val allSupportedCommands =

@ -1,412 +1,81 @@
// Copyright 2021 yuyezhong@gmail.com
//
// 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.
package cfig.helper
import org.apache.commons.exec.CommandLine
import org.bouncycastle.util.encoders.Hex
import org.apache.commons.exec.DefaultExecutor
import org.slf4j.LoggerFactory
import java.io.ByteArrayInputStream
import java.io.File
import java.io.FileOutputStream
import java.lang.RuntimeException
import java.util.*
//https://www.digicert.com/kb/ssl-support/openssl-quick-reference-guide.htm
class OpenSslHelper {
enum class KeyFormat {
PEM, //header + metadata + base64 der
DER // der format
}
interface IKey {
val name: String
val data: ByteArray
fun writeTo(fileName: String) {
FileOutputStream(fileName, false).use {
it.write(data)
}
}
}
class PK1Key(
val format: KeyFormat = KeyFormat.PEM,
override val data: ByteArray = byteArrayOf(),
override val name: String = "RSA Private"
) : IKey {
/*
PEM private key -> PEM/DER public key
*/
fun getPublicKey(pubKeyFormat: KeyFormat): PK1PubKey {
if (format != KeyFormat.PEM) {
throw IllegalArgumentException("can not handle $format private key")
}
val ret = Helper.powerRun(
"openssl rsa -in $stdin -pubout -outform ${pubKeyFormat.name}",
ByteArrayInputStream(data)
)
log.info("privateToPublic:stderr: ${String(ret[1])}")
return PK1PubKey(format = pubKeyFormat, data = ret[0])
}
/*
file based:
openssl rsa -in private.pem -pubout -out public.pem
stream based:
openssl rsa -in - -pubout
*/
fun getPk8PublicKey(): Pk8PubKey {
if (this.format != KeyFormat.PEM) {
throw java.lang.IllegalArgumentException("Only PEM key is supported")
}
val ret = Helper.powerRun2(
"openssl rsa -in $stdin -pubout",
ByteArrayInputStream(data)
)
if (ret[0] as Boolean) {
log.info("getPk8PublicKey:error: ${String(ret[2] as ByteArray)}")
return Pk8PubKey(KeyFormat.PEM, ret[1] as ByteArray)
} else {
log.error("stdout: " + String(ret[1] as ByteArray))
log.error("stderr: " + String(ret[2] as ByteArray))
throw RuntimeException()
}
}
/*
file based:
openssl pkcs8 -nocrypt -in $(rsa_key) -topk8 -outform DER -out $(pk8_key)
stream based:
openssl pkcs8 -nocrypt -in - -topk8 -outform DER
*/
fun toPk8(pk8Format: KeyFormat): PK8RsaKey {
val ret = Helper.powerRun(
"openssl pkcs8 -nocrypt -in $stdin -topk8 -outform ${pk8Format.name}",
ByteArrayInputStream(data)
)
log.info("toPk8Private:stderr: ${String(ret[1])}")
return PK8RsaKey(format = pk8Format, data = ret[0])
}
fun toCsr(info: String? = null): Csr {
val defaultInfo = "/C=CN/ST=Shanghai/L=Shanghai/O=XXX/OU=infra/CN=gerrit/emailAddress=webmaster@XX.com"
val cmdLine = CommandLine.parse("openssl req -new -key $stdin -subj").apply {
this.addArgument(info ?: defaultInfo, true)
}
val ret = Helper.powerRun3(cmdLine, ByteArrayInputStream(data))
if (ret[0] as Boolean) {
log.info("toCsr:error: ${String(ret[2] as ByteArray)}")
return Csr(data = ret[1] as ByteArray)
} else {
log.error("stdout: " + String(ret[1] as ByteArray))
log.error("stderr: " + String(ret[2] as ByteArray))
throw RuntimeException()
}
}
fun toV1Cert(info: String? = null): Crt {
//full command:
// openssl x509 -req -in 2017key.csr -signkey 2017key.rsa.pem -out theCert.crt -days 180
//send RSA key as input stream:
// openssl x509 -req -in 2017key.csr -signkey - -out theCert.crt -days 180
//send RSA key as input stream, crt as output stream:
// openssl x509 -req -in 2017key.csr -signkey - -days 180
val csr = this.toCsr(info)
val tmpFile = File.createTempFile("pk1.", ".csr")
tmpFile.writeBytes(csr.data)
tmpFile.deleteOnExit()
val ret = Helper.powerRun2(
"openssl x509 -req -in ${tmpFile.path} -signkey $stdin -days 180",
ByteArrayInputStream(data)
)
if (ret[0] as Boolean) {
log.info("toCrt:error: ${String(ret[2] as ByteArray)}")
return Crt(ret[1] as ByteArray)
} else {
log.error("stdout: " + String(ret[1] as ByteArray))
log.error("stderr: " + String(ret[2] as ByteArray))
throw RuntimeException()
}
}
companion object {
/*
-> PEM RSA private key
*/
fun generate(keyLength: Int): PK1Key {
val ret = Helper.powerRun("openssl genrsa $keyLength", null)
log.info("generateRSA:stderr: ${String(ret[1])}")
return PK1Key(format = KeyFormat.PEM, data = ret[0])
}
companion object {
private val log = LoggerFactory.getLogger(OpenSslHelper::class.java)
fun generate(keyLength: Int, password: String): PK1Key {
val ret = Helper.powerRun("openssl genrsa -aes256 -passout pass:$password $keyLength", null)
log.info("generateRSA:stderr: ${String(ret[1])}")
return PK1Key(format = KeyFormat.PEM, data = ret[0])
}
//openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr -subj "/C=US/ST=Utah/L=Lehi/O=Your Company, Inc./OU=IT/CN=yourdomain.com"\n
//openssl req -text -in server.csr -noout -verify
//\"/C=US/ST=Utah/L=Lehi/O=Your Company, Inc./OU=IT/CN=yourdomain.com\"
fun createCsr(outKey: String, outCsr: String, subj: String, keyLen: Int = 2048) {
DefaultExecutor().execute(
CommandLine.parse("openssl req -new -newkey rsa:$keyLen -nodes").apply {
addArguments("-keyout $outKey")
addArguments("-out $outCsr")
addArgument("-subj").addArgument("$subj", false)
})
DefaultExecutor().execute(CommandLine.parse("openssl req -text -in $outCsr -noout -verify"))
}
}
class PK8RsaKey(
val format: KeyFormat = KeyFormat.PEM,
override val data: ByteArray = byteArrayOf(),
override val name: String = "PK8 Private"
) : IKey {
/*
file based:
openssl pkcs8 -nocrypt -in $(pk8_key) -inform DER -out $(rsa_key).converted.tmp
openssl rsa -in $(rsa_key).converted.tmp -out $(rsa_key).converted
stream based:
openssl pkcs8 -nocrypt -in - -inform DER
openssl rsa -in - -out -
*/
fun toPk1(): PK1Key {
val transformed =
if (this.format != KeyFormat.PEM) {
log.warn("Only pk8+pem can be converted to RSA, transforming ...")
this.transform(KeyFormat.DER, KeyFormat.PEM)
fun toJks(
pk8: String, x509Pem: String,
outFile: String,
paramSrcPass: String = "somepassword",
paramDstPass: String = "somepassword",
alias: String = "androiddebugkey"
) {
File(outFile).let {
if (it.exists()) it.delete()
}
val privKey = File.createTempFile("key.", ".tmp").let {
it.deleteOnExit()
val ret = Helper.powerRun2("openssl pkcs8 -in $pk8 -inform DER -outform PEM -nocrypt", null)
if (ret[0] as Boolean) {
it.writeBytes(ret[1] as ByteArray)
} else {
this
log.error("stdout: " + String(ret[1] as ByteArray))
log.error("stderr: " + String(ret[2] as ByteArray))
throw java.lang.RuntimeException()
}
val ret = Helper.powerRun2(
"openssl rsa -in $stdin",
ByteArrayInputStream(transformed.data)
)
if (ret[0] as Boolean) {
log.info("toRsaPrivate:error: ${String(ret[2] as ByteArray)}")
return PK1Key(KeyFormat.PEM, ret[1] as ByteArray)
} else {
log.error("stdout: " + String(ret[1] as ByteArray))
log.error("stderr: " + String(ret[2] as ByteArray))
throw RuntimeException()
it
}
}
/*
openssl pkcs8 -nocrypt -in - -inform DER
*/
fun transform(inFormat: KeyFormat, outFormat: KeyFormat): PK8RsaKey {
val ret = Helper.powerRun2(
"openssl pkcs8 -nocrypt -in $stdin -inform ${inFormat.name} -outform ${outFormat.name}",
ByteArrayInputStream(data)
)
if (ret[0] as Boolean) {
log.info("transform:error: ${String(ret[2] as ByteArray)}")
return PK8RsaKey(data = ret[1] as ByteArray)
} else {
log.error("stdout: " + String(ret[1] as ByteArray))
log.error("stderr: " + String(ret[2] as ByteArray))
throw IllegalArgumentException()
}
}
/*
file based:
openssl rsa -in pkcs8.pem -pubout -out public_pkcs8.pem
stream based:
openssl rsa -in - -pubout
*/
fun getPublicKey(): Pk8PubKey {
if (this.format != KeyFormat.PEM) {
throw java.lang.IllegalArgumentException("Only PEM key is supported")
}
val ret = Helper.powerRun2(
"openssl rsa -in $stdin -pubout",
ByteArrayInputStream(data)
)
if (ret[0] as Boolean) {
log.info("getPublicKey:error: ${String(ret[2] as ByteArray)}")
return Pk8PubKey(KeyFormat.PEM, ret[1] as ByteArray)
} else {
log.error("stdout: " + String(ret[1] as ByteArray))
log.error("stderr: " + String(ret[2] as ByteArray))
throw RuntimeException()
}
}
}
class PK1PubKey(
val format: KeyFormat = KeyFormat.PEM,
override val data: ByteArray = byteArrayOf(),
override val name: String = "RSA Public"
) : IKey
class Pk8PubKey(
val format: KeyFormat = KeyFormat.PEM,
override val data: ByteArray = byteArrayOf(),
override val name: String = "Pk8 Public"
) : IKey
class Csr(override val name: String = "CSR", override val data: ByteArray = byteArrayOf()) : IKey
class Jks(override val name: String = "Java Keystore", override val data: ByteArray = byteArrayOf()) : IKey {
//keytool -list -v -deststorepass $(thePassword) -keystore $(jks_file)
fun check(passWord: String = "secretpassword") {
val tmpFile = File.createTempFile("tmp.", ".jks").apply { this.deleteOnExit() }
tmpFile.writeBytes(this.data)
val ret = Helper.powerRun2(
"keytool -list -v -deststorepass $passWord -keystore $tmpFile",
null
)
if (ret[0] as Boolean) {
log.info("Jks.check:stdout: ${String(ret[1] as ByteArray)}")
log.info("Jks.check:error: ${String(ret[2] as ByteArray)}")
} else {
log.error("stdout: " + String(ret[1] as ByteArray))
log.error("stderr: " + String(ret[2] as ByteArray))
throw RuntimeException()
}
}
companion object {
fun generate(keyPass: String, storePass: String, alias: String, dname: String?, outFile: String) {
//val cmd = "keytool -genkey -noprompt -alias $alias -dname \"$defaultInfo\" -keystore $outFile -storepass $storePass -keypass $keyPass"
val defaultInfo =
dname ?: "CN=Unknown, OU=Unknown, O=Unknown Software Company, L=Unknown, ST=Unknown, C=Unknown"
val cmd = CommandLine.parse("keytool -genkey -noprompt -keyalg RSA").apply {
addArgument("-alias").addArgument(alias)
addArgument("-dname").addArgument(defaultInfo, false)
addArgument("-keystore").addArgument(outFile)
addArgument("-storepass").addArgument(storePass)
addArgument("-keypass").addArgument(keyPass)
}
val ret = Helper.powerRun3(cmd, null)
val pk12 = File.createTempFile("key.", ".tmp").let {
it.deleteOnExit()
val ret = Helper.powerRun2(
"openssl pkcs12 -export -in $x509Pem -password pass:$paramSrcPass -inkey ${privKey.path} -name androiddebugkey",
null
)
if (ret[0] as Boolean) {
log.info("Jks.gen:stdout: ${String(ret[1] as ByteArray)}")
log.info("Jks.gen:error: ${String(ret[2] as ByteArray)}")
it.writeBytes(ret[1] as ByteArray)
} else {
log.error("Jks.gen:stdout: " + String(ret[1] as ByteArray))
log.error("Jks.gen:stderr: " + String(ret[2] as ByteArray))
throw RuntimeException()
log.error("stdout: " + String(ret[1] as ByteArray))
log.error("stderr: " + String(ret[2] as ByteArray))
throw java.lang.RuntimeException()
}
it
}
}
}
class Crt(override val data: ByteArray = byteArrayOf(), override val name: String = "crt") : IKey {
//Result: trustedCertEntry
//keytool -importcert -file 2017key.crt -deststorepass secretpassword -srcstorepass secretpassword -keystore 2017key.2.jks
fun toJks(
paramSrcPass: String = "secretpassword",
paramDstPass: String = "secretpassword",
alias: String = "awesomeKey"
): Jks {
val crtFile = File.createTempFile("tmp.", ".crt").apply { this.deleteOnExit() }
crtFile.writeBytes(this.data)
val outFile = File.createTempFile("tmp.", ".jks").apply { this.delete() }
val ret = Helper.powerRun2(
"keytool -importcert -file ${crtFile.path}" +
" -deststorepass $paramDstPass -srcstorepass $paramSrcPass " +
" -alias $alias " +
" -keystore ${outFile.path}",
ByteArrayInputStream("yes\n".toByteArray())
"keytool -importkeystore " +
" -deststorepass $paramDstPass -destkeystore $outFile" +
" -srckeystore ${pk12.path} -srcstoretype PKCS12 -srcstorepass $paramSrcPass" +
" -alias $alias",
null
)
if (ret[0] as Boolean) {
log.info("toJks:error: ${String(ret[2] as ByteArray)}")
log.info("toJks:stdout: ${String(ret[1] as ByteArray)}")
} else {
log.error("stdout: " + String(ret[1] as ByteArray))
log.error("stderr: " + String(ret[2] as ByteArray))
throw RuntimeException()
}
if (!outFile.exists()) {
throw RuntimeException()
}
val outData = outFile.readBytes()
outFile.delete()
return Jks(data = outData)
}
}
class Pfx(
override val name: String = "androiddebugkey",
var thePassword: String = "secretpassword",
override var data: ByteArray = byteArrayOf()
) : IKey {
fun generate(pk1: PK1Key, crt: Crt): Pfx {
val pk1File = File.createTempFile("tmp.", ".file").apply { this.deleteOnExit() }
pk1File.writeBytes(pk1.data)
val crtFile = File.createTempFile("tmp.", ".file").apply { this.deleteOnExit() }
crtFile.writeBytes(crt.data)
//openssl pkcs12 -export -out $(pfx_cert) -inkey $(rsa_key) -in $(crt_file) -password pass:$(thePassword) -name $(thePfxName)
val cmd = "openssl pkcs12 -export " +
" -inkey ${pk1File.path} " +
" -in ${crtFile.path} " +
" -password pass:${this.thePassword} -name ${this.name}"
val ret = Helper.powerRun2(cmd, null)
if (ret[0] as Boolean) {
log.info("toPfx:error: ${String(ret[2] as ByteArray)}")
log.info("toPfx:stdout: ${Hex.toHexString(ret[1] as ByteArray)}")
this.data = ret[1] as ByteArray
} else {
log.error("stdout: " + String(ret[1] as ByteArray))
log.error("stderr: " + String(ret[2] as ByteArray))
throw RuntimeException()
}
return this
}
//keytool -importkeystore -deststorepass $(thePassword) -destkeystore $(jks_file) -srckeystore $(pfx_cert) -srcstoretype PKCS12 -srcstorepass $(thePassword)
fun toJks(): Jks {
val jksFile = File.createTempFile("tmp.", ".file").apply { this.delete() }
val thisFile = File.createTempFile("tmp.", ".file").apply { this.deleteOnExit() }
thisFile.writeBytes(this.data)
val cmd = "keytool -importkeystore " +
" -srcstorepass $thePassword -deststorepass $thePassword " +
" -destkeystore ${jksFile.path} " +
" -srckeystore $thisFile -srcstoretype PKCS12"
val ret = Helper.powerRun2(cmd, null)
if (ret[0] as Boolean) {
log.info("toJks:error: " + String(ret[2] as ByteArray))
log.info("toJks:stdout: " + String(ret[1] as ByteArray))
this.data = ret[1] as ByteArray
} else {
log.error("stdout: " + String(ret[1] as ByteArray))
log.error("stderr: " + String(ret[2] as ByteArray))
throw RuntimeException()
}
val outDate = jksFile.readBytes()
jksFile.delete()
return Jks(this.name, outDate)
}
}
companion object {
private val log = LoggerFactory.getLogger(OpenSslHelper::class.java)
val stdin = if (System.getProperty("os.name").contains("Mac")) "/dev/stdin" else "-"
fun toPfx(password: String = "secretpassword", keyName: String = "androiddebugkey", pk1: PK1Key, crt: Crt) {
val pk1File = File.createTempFile("tmp.", ".file").apply { this.deleteOnExit() }
pk1File.writeBytes(pk1.data)
val crtFile = File.createTempFile("tmp.", ".file").apply { this.deleteOnExit() }
crtFile.writeBytes(crt.data)
//openssl pkcs12 -export -out $(pfx_cert) -inkey $(rsa_key) -in $(crt_file) -password pass:$(thePassword) -name $(thePfxName)
val cmd =
"openssl pkcs12 -export -inkey ${pk1File.path} -in ${crtFile.path} -password pass:$password -name $keyName"
val ret = Helper.powerRun2(cmd, null)
if (ret[0] as Boolean) {
log.info("toPfx:error: ${String(ret[2] as ByteArray)}")
log.info("toPfx:stdout: ${Hex.toHexString(ret[1] as ByteArray)}")
log.info("$outFile is ready")
} else {
log.error("stdout: " + String(ret[1] as ByteArray))
log.error("stderr: " + String(ret[2] as ByteArray))
throw RuntimeException()
throw java.lang.RuntimeException()
}
}
}
}
}

@ -14,7 +14,7 @@
package cfig.helper
import cc.cfig.io.Struct3
import cc.cfig.io.Struct
import cfig.helper.Helper.Companion.check_call
import cfig.helper.Helper.Companion.check_output
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
@ -159,7 +159,7 @@ class ZipHelper {
sizeFileHeader = struct.calcsize(structFileHeader)
*/
fun ZipArchiveEntry.getEntryOffset(): Long {
val zipFileHeaderSize = Struct3("<4s2B4HL2L2H").calcSize()
val zipFileHeaderSize = Struct("<4s2B4HL2L2H").calcSize()
val funGetLocalHeaderOffset = ZipArchiveEntry::class.declaredFunctions.filter { funcItem ->
funcItem.name == "getLocalHeaderOffset"
}[0]

@ -1,539 +0,0 @@
// Copyright 2022 yuyezhong@gmail.com
//
// 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.
package cc.cfig.io
import java.io.IOException
import java.io.InputStream
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.charset.StandardCharsets
import java.util.*
import java.util.regex.Pattern
class Struct3(inFormatString: String) {
private val formatString: String = inFormatString
private var byteOrder = ByteOrder.LITTLE_ENDIAN
private val warships = ArrayList<IWarShip<*>>()
init {
require(inFormatString.isNotEmpty()) { "FORMAT_STRING must not be empty" }
val m = Pattern.compile("(\\d*)([a-zA-Z])").matcher(formatString)
this.byteOrder = when (formatString[0]) {
'>', '!' -> ByteOrder.BIG_ENDIAN
'@', '=' -> ByteOrder.nativeOrder()
else -> ByteOrder.LITTLE_ENDIAN
}
while (m.find()) {
//item[0]: Type, item[1]: multiple
// if need to expand format items, explode it
// eg: "4L" will be exploded to "1L 1L 1L 1L", so it's treated as primitive
// eg: "10x" won't be exploded, it's still "10x", so it's treated as non-primitive
require(m.group(2).length == 1)
val marker = m.group(2)[0]
val count = if (m.group(1).isEmpty()) 1 else Integer.decode(m.group(1))
warships.addAll(makeWarship(marker, count))
}
}
private fun makeWarship(marker: Char, count: Int): List<IWarShip<*>> {
return when (marker) {
//primitive types
's' -> listOf(StringFleet(count)) //string (exploded)
'x' -> listOf(PaddingFleet(count)) //byte 1 (exploded)
'b' -> listOf(ByteFleet(count)) //byte 1 (exploded)
'B' -> listOf(UByteFleet(count)) //UByte 1 (exploded)
//zippable types, which need to be exploded with multiple=1
'c' -> List(count) { CharShip() } //1
'h' -> List(count) { ShortShip() } //2
'H' -> List(count) { UShortShip() } //2
'i', 'l' -> List(count) { IntShip() } //4
'I', 'L' -> List(count) { UIntShip() } //4
'q' -> List(count) { LongShip() } //8
'Q' -> List(count) { ULongShip() } //8
else -> throw IllegalArgumentException("type [$marker] not supported")
}
}
fun calcSize(): Int {
return warships.sumOf { it.multiple * it.sz }
}
override fun toString(): String {
val sb = StringBuilder("Struct[$byteOrder]")
warships.map { it.toString() }.reduce { acc, s -> "$acc$s" }
return sb.toString()
}
@Throws(IllegalArgumentException::class)
fun pack(vararg args: Any?): ByteArray {
if (args.size != this.warships.size) {
throw IllegalArgumentException("argument size " + args.size + " doesn't match format size " + this.warships.size)
}
return ByteBuffer.allocate(this.calcSize()).let { bf ->
bf.order(this.byteOrder)
args.forEachIndexed { index, arg ->
warships[index].put(bf, arg)
}
bf.array()
}
}
@Throws(IOException::class, IllegalArgumentException::class)
fun unpack(iS: InputStream): List<*> {
return warships.map { it.get(iS, byteOrder) }
}
private interface IWarShip<T> {
val sz: Int
var multiple: Int
fun get(stream: InputStream, byteOrder: ByteOrder): T
fun get(ba: ByteArray, byteOrder: ByteOrder): T
fun put(bf: ByteBuffer, arg: Any?)
}
private interface IBaseShip<T> : IWarShip<T> {
override var multiple: Int
get() = 1
set(@Suppress("UNUSED_PARAMETER") value) {}
}
private class ShortShip : IBaseShip<Short> {
override fun get(stream: InputStream, byteOrder: ByteOrder): Short {
val data = ByteArray(Short.SIZE_BYTES)
check(Short.SIZE_BYTES == stream.read(data))
return get(data, byteOrder)
}
override fun get(ba: ByteArray, byteOrder: ByteOrder): Short {
val typeSize = Short.SIZE_BYTES / Byte.SIZE_BYTES
check(typeSize == ba.size) { "Short must have $typeSize bytes" }
return ByteBuffer.allocate(ba.size).let {
it.order(byteOrder)
it.put(ba)
it.flip()
it.short
}
}
override fun put(bf: ByteBuffer, arg: Any?) {
//h: Short
when (arg) {
is Int -> {
require(arg in Short.MIN_VALUE..Short.MAX_VALUE) { "[$arg] is truncated as type Short.class" }
bf.putShort(arg.toShort())
}
is Short -> bf.putShort(arg) //instance Short
else -> throw IllegalArgumentException("[$arg](${arg!!::class.java}) is NOT Short/Int")
}
}
override fun toString(): String {
return "h"
}
override val sz: Int = Short.SIZE_BYTES
}
private class UShortShip : IBaseShip<UShort> {
override fun get(stream: InputStream, byteOrder: ByteOrder): UShort {
val data = ByteArray(UShort.SIZE_BYTES)
check(UShort.SIZE_BYTES == stream.read(data))
return get(data, byteOrder)
}
override fun get(ba: ByteArray, byteOrder: ByteOrder): UShort {
val typeSize = UShort.SIZE_BYTES / Byte.SIZE_BYTES
assert(typeSize == ba.size) { "UShort must have $typeSize bytes" }
return ByteBuffer.allocate(ba.size).let {
it.order(byteOrder)
it.put(ba)
it.flip()
it.short.toUShort()
}
}
override fun put(bf: ByteBuffer, arg: Any?) {
require(arg is UShort || arg is UInt || arg is Int) { "[$arg](${arg!!::class.java}) is NOT UShort/UInt/Int" }
when (arg) {
is Int -> {
require(arg >= UShort.MIN_VALUE.toInt() && arg <= UShort.MAX_VALUE.toInt()) { "[$arg] is truncated as type UShort" }
bf.putShort(arg.toShort())
}
is UInt -> {
require(arg >= UShort.MIN_VALUE && arg <= UShort.MAX_VALUE) { "[$arg] is truncated as type UShort" }
bf.putShort(arg.toShort())
}
is UShort -> bf.putShort(arg.toShort())
else -> throw IllegalArgumentException("[$arg](${arg!!::class.java}) is NOT valid for UShort")
}
}
override fun toString(): String {
return "H"
}
override val sz: Int = UShort.SIZE_BYTES
}
//i, l: Int
class IntShip : IBaseShip<Int> {
override fun get(stream: InputStream, byteOrder: ByteOrder): Int {
val data = ByteArray(Int.SIZE_BYTES)
check(Int.SIZE_BYTES == stream.read(data))
return get(data, byteOrder)
}
override fun get(ba: ByteArray, byteOrder: ByteOrder): Int {
val typeSize = Int.SIZE_BYTES / Byte.SIZE_BYTES
assert(typeSize == ba.size) { "Int must have $typeSize bytes" }
return ByteBuffer.allocate(ba.size).let {
it.order(byteOrder)
it.put(ba)
it.flip()
it.int
}
}
override fun put(bf: ByteBuffer, arg: Any?) {
require(arg is Int) { "[$arg](${arg!!::class.java}) is NOT Int" }
bf.putInt(arg)
}
override fun toString(): String {
return "i"
}
override val sz: Int = Int.SIZE_BYTES
}
//I, L: UInt
class UIntShip : IBaseShip<UInt> {
override fun get(stream: InputStream, byteOrder: ByteOrder): UInt {
val data = ByteArray(UInt.SIZE_BYTES)
check(UInt.SIZE_BYTES == stream.read(data))
return get(data, byteOrder)
}
override fun get(ba: ByteArray, byteOrder: ByteOrder): UInt {
val typeSize = UInt.SIZE_BYTES / Byte.SIZE_BYTES
assert(typeSize == ba.size) { "UInt must have $typeSize bytes" }
return ByteBuffer.allocate(ba.size).let {
it.order(byteOrder)
it.put(ba)
it.flip()
it.int.toUInt()
}
}
override fun put(bf: ByteBuffer, arg: Any?) {
when (arg) {
is Int -> {
require(arg >= 0) { "[$arg] is invalid as type UInt" }
bf.putInt(arg)
}
is UInt -> bf.putInt(arg.toInt())
is Long -> {
require(arg >= 0) { "[$arg] is invalid as type UInt" }
bf.putInt(arg.toInt())
}
else -> throw IllegalArgumentException("[$arg](${arg!!::class.java}) is NOT valid UInt")
}
}
override fun toString(): String {
return "I"
}
override val sz: Int = UInt.SIZE_BYTES
}
//q: Long
private class LongShip : IBaseShip<Long> {
override fun get(stream: InputStream, byteOrder: ByteOrder): Long {
val data = ByteArray(Long.SIZE_BYTES)
check(Long.SIZE_BYTES == stream.read(data))
return get(data, byteOrder)
}
override fun get(ba: ByteArray, byteOrder: ByteOrder): Long {
val typeSize = Long.SIZE_BYTES / Byte.SIZE_BYTES
check(typeSize == ba.size) { "Long must have $typeSize bytes" }
return ByteBuffer.allocate(ba.size).let {
it.order(byteOrder)
it.put(ba)
it.flip()
it.long
}
}
override fun put(bf: ByteBuffer, arg: Any?) {
when (arg) {
is Long -> bf.putLong(arg)
is Int -> bf.putLong(arg.toLong())
else -> throw IllegalArgumentException("[$arg](${arg!!::class.java}) is NOT valid Long")
}
}
override fun toString(): String {
return "q"
}
override val sz: Int = Long.SIZE_BYTES
}
//Q: ULong
private class ULongShip : IBaseShip<ULong> {
override fun get(stream: InputStream, byteOrder: ByteOrder): ULong {
val data = ByteArray(ULong.SIZE_BYTES)
check(ULong.SIZE_BYTES == stream.read(data))
return get(data, byteOrder)
}
override fun get(ba: ByteArray, byteOrder: ByteOrder): ULong {
val typeSize = ULong.SIZE_BYTES / Byte.SIZE_BYTES
assert(typeSize == ba.size) { "ULong must have $typeSize bytes" }
return ByteBuffer.allocate(ba.size).let {
it.order(byteOrder)
it.put(ba)
it.flip()
it.long.toULong()
}
}
override fun put(bf: ByteBuffer, arg: Any?) {
when (arg) {
is Int -> {
require(arg >= 0) { "[$arg] is invalid as type ULong" }
bf.putLong(arg.toLong())
}
is Long -> {
require(arg >= 0) { "[$arg] is invalid as type ULong" }
bf.putLong(arg)
}
is ULong -> bf.putLong(arg.toLong())
else -> throw IllegalArgumentException("[$arg](${arg!!::class.java}) is NOT valid ULong")
}
}
override fun toString(): String {
return "Q"
}
override val sz: Int = ULong.SIZE_BYTES
}
//c: character
private class CharShip : IBaseShip<Char> {
override fun get(stream: InputStream, byteOrder: ByteOrder): Char {
val data = ByteArray(Byte.SIZE_BYTES)
check(Byte.SIZE_BYTES == stream.read(data))
return data[0].toInt().toChar()
}
override fun get(ba: ByteArray, byteOrder: ByteOrder): Char {
return ba[0].toInt().toChar()
}
override fun put(bf: ByteBuffer, arg: Any?) {
require(arg is Char) { "[$arg](${arg!!::class.java}) is NOT Char" }
if (arg !in '\u0000'..'\u00ff') {
throw IllegalArgumentException("arg[${arg.code}] exceeds 8-bit bound")
}
bf.put(arg.code.toByte())
}
override fun toString(): String {
return "c"
}
override val sz: Int = 1
}
private interface IBaseFleet<T> : IWarShip<T> {
fun appendPadding(bf: ByteBuffer, b: Byte, bufSize: Int) {
when {
bufSize == 0 -> {
//"paddingSize is zero, perfect match"
return
}
bufSize < 0 -> {
throw IllegalArgumentException("illegal padding size: $bufSize")
}
else -> {
//"paddingSize $bufSize"
}
}
val padding = ByteArray(bufSize)
Arrays.fill(padding, b)
bf.put(padding)
}
@Throws(IllegalArgumentException::class)
fun appendByteArray(bf: ByteBuffer, inByteArray: ByteArray, bufSize: Int) {
val paddingSize = bufSize - inByteArray.size
if (paddingSize < 0) throw IllegalArgumentException("arg length [${inByteArray.size}] exceeds limit: $bufSize")
//data
bf.put(inByteArray)
//padding: "paddingSize $paddingSize"
appendPadding(bf, 0.toByte(), paddingSize)
}
fun appendByteArray(bf: ByteBuffer, inIntArray: IntArray, bufSize: Int) {
val arg2 = mutableListOf<Byte>()
inIntArray.toMutableList().mapTo(arg2) {
if (it in Byte.MIN_VALUE..Byte.MAX_VALUE) {
it.toByte()
} else {
throw IllegalArgumentException("$it is not valid Byte")
}
}
appendByteArray(bf, arg2.toByteArray(), bufSize)
}
fun appendUByteArray(bf: ByteBuffer, inIntArray: IntArray, bufSize: Int) {
val arg2 = mutableListOf<UByte>()
inIntArray.toMutableList().mapTo(arg2) {
if (it in UByte.MIN_VALUE.toInt()..UByte.MAX_VALUE.toInt())
it.toUByte()
else {
throw IllegalArgumentException("$it is not valid Byte")
}
}
appendUByteArray(bf, arg2.toUByteArray(), bufSize)
}
fun appendUByteArray(bf: ByteBuffer, inUByteArray: UByteArray, bufSize: Int) {
val bl = mutableListOf<Byte>()
inUByteArray.toMutableList().mapTo(bl) { it.toByte() }
appendByteArray(bf, bl.toByteArray(), bufSize)
}
}
//x: padding:
private class PaddingFleet(override var multiple: Int, override val sz: Int = 1) : IBaseFleet<Byte> {
override fun get(stream: InputStream, byteOrder: ByteOrder): Byte {
val data = ByteArray(Byte.SIZE_BYTES)
check(Byte.SIZE_BYTES == stream.read(data)) //sample the 1st byte
val skipped = stream.skip(multiple.toLong() - Byte.SIZE_BYTES)//skip remaining to save memory
check(multiple.toLong() - Byte.SIZE_BYTES == skipped)
return data[0]
}
override fun get(ba: ByteArray, byteOrder: ByteOrder): Byte {
return ba[0]
}
override fun put(bf: ByteBuffer, arg: Any?) {
when (arg) {
null -> appendPadding(bf, 0, multiple)
is Byte -> appendPadding(bf, arg, multiple)
is Int -> appendPadding(bf, arg.toByte(), multiple)
else -> throw IllegalArgumentException("Unsupported arg [$arg]")
}
}
override fun toString(): String {
return "${multiple}x"
}
}
//b: byte array
private class ByteFleet(override var multiple: Int = 0) : IBaseFleet<ByteArray> {
override fun get(stream: InputStream, byteOrder: ByteOrder): ByteArray {
val data = ByteArray(multiple)
check(multiple == stream.read(data))
return data
}
override fun get(ba: ByteArray, byteOrder: ByteOrder): ByteArray {
check(multiple == ba.size)
return ba
}
override fun put(bf: ByteBuffer, arg: Any?) {
when (arg) {
is IntArray -> appendByteArray(bf, arg, multiple)
is ByteArray -> appendByteArray(bf, arg, multiple)
else -> throw IllegalArgumentException("[$arg](${arg!!::class.java}) is NOT ByteArray/IntArray")
}
}
override fun toString(): String {
return "${multiple}b"
}
override val sz: Int = Byte.SIZE_BYTES
}
//B: UByte array
private class UByteFleet(override var multiple: Int = 0) : IBaseFleet<UByteArray> {
override fun get(stream: InputStream, byteOrder: ByteOrder): UByteArray {
val data = ByteArray(multiple)
check(multiple == stream.read(data))
val innerData2 = mutableListOf<UByte>()
data.toMutableList().mapTo(innerData2) { it.toUByte() }
return innerData2.toUByteArray()
}
override fun get(ba: ByteArray, byteOrder: ByteOrder): UByteArray {
return ba.toUByteArray()
}
override fun put(bf: ByteBuffer, arg: Any?) {
when (arg) {
is ByteArray -> appendByteArray(bf, arg, multiple)
is UByteArray -> appendUByteArray(bf, arg, multiple)
is IntArray -> appendUByteArray(bf, arg, multiple)
else -> throw IllegalArgumentException("[$arg](${arg!!::class.java}) is NOT ByteArray/IntArray")
}
}
override fun toString(): String {
return "${multiple}B"
}
override val sz: Int
get() = UByte.SIZE_BYTES
}
//s: String
class StringFleet(override var multiple: Int = 0) : IBaseFleet<String> {
override fun get(stream: InputStream, byteOrder: ByteOrder): String {
val data = ByteArray(multiple)
check(multiple == stream.read(data))
return get(data, byteOrder)
}
override fun get(ba: ByteArray, byteOrder: ByteOrder): String {
return ba.toString(StandardCharsets.UTF_8).let { str ->
str.indexOf(Character.MIN_VALUE).let { nullPos ->
if (nullPos >= 0) str.substring(0, nullPos) else str
}
}
}
override fun put(bf: ByteBuffer, arg: Any?) {
requireNotNull(arg) { "arg can not be NULL for String" }
require(arg is String) { "[$arg](${arg::class.java}) is NOT String" }
appendByteArray(bf, arg.toByteArray(), multiple)
}
override fun toString(): String {
return "${multiple}s"
}
override val sz: Int = 1
}
}

@ -14,115 +14,41 @@
package cfig.helper
import org.bouncycastle.asn1.est.CsrAttrs
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.cert.crmf.CertificateRequestMessageBuilder
import org.junit.Test
import org.slf4j.LoggerFactory
import java.io.File
import kotlin.test.assertTrue
class OpenSslHelperTest {
private val log = LoggerFactory.getLogger(OpenSslHelperTest::class.java)
@Test
fun PKCS1test() {
//private RSA key
val rsa = OpenSslHelper.PK1Key.generate(4096).apply {
val fileName = "1_rsa.key"
writeTo(fileName)
}
//Action-1: private RSA key -> RSA public key(PEM)
val rsaPubPEM = rsa.getPublicKey(OpenSslHelper.KeyFormat.PEM).apply {
writeTo("2_rsa_pub.pem.key")
}
//Action-3: RSA public key(PEM) --> RSA public key(DER)
val decodeFromPem = CryptoHelper.KeyBox.decodePem(String(rsaPubPEM.data))
//Action-2: private RSA key -> RSA public key(DER)
val rsaPubDer = rsa.getPublicKey(OpenSslHelper.KeyFormat.DER).apply {
writeTo("3_rsa_pub.der.key")
}
//check equality: 1,3 == 2
assertTrue(decodeFromPem.contentEquals(rsaPubDer.data))
run {
File("1_rsa.key").delete()
File("2_rsa_pub.pem.key").delete()
File("3_rsa_pub.der.key").delete()
}
fun testCsr() {
OpenSslHelper.createCsr(
"test.key",
"test.csr",
"/C=US/ST=Utah/L=Lehi/O=Your Company, Inc./OU=IT/CN=yourdomain.com"
)
}
@Test
fun PKCS8Test() {
//private RSA key
val rsa = OpenSslHelper.PK1Key.generate(4096)
run { //check equality: 8 == 4,5
val pk8Pub = rsa.getPk8PublicKey()
val pk8Pub2 = rsa.toPk8(OpenSslHelper.KeyFormat.PEM).getPublicKey()
assert(pk8Pub.data.contentEquals(pk8Pub2.data))
}
run { //check equality: 8 == 4,5
val pk8Pub = rsa.getPk8PublicKey()
val action8_11 = CryptoHelper.KeyBox.decodePem(String(pk8Pub.data))
// val pk8Pub2 = rsa.toPk8(OpenSslHelper.KeyFormat.PEM).getPublicKey()
// assert(pk8Pub.data.contentEquals(pk8Pub2.data))
}
//check equality: 4,4' == original RSA
rsa.toPk8(OpenSslHelper.KeyFormat.PEM).let { pk8Pem ->
val shortConversion = pk8Pem.toPk1()
assert(shortConversion.data.contentEquals(rsa.data))
}
//check equality: 7,10,4' == original RSA
rsa.toPk8(OpenSslHelper.KeyFormat.DER).let { pk8der ->
val longConversion = pk8der
.transform(OpenSslHelper.KeyFormat.DER, OpenSslHelper.KeyFormat.PEM) //pk8 PEM
.toPk1() //pk1 PEM
assert(longConversion.data.contentEquals(rsa.data))
}
}
@Test
fun CertTest() {
//private RSA key
val rsa = OpenSslHelper.PK1Key.generate(4096)
val crt = rsa.toV1Cert()
val jks = crt.toJks()
//jks.writeTo("good.jks")
val pfx = OpenSslHelper.Pfx(name = "androiddebugkey", thePassword = "somepassword")
pfx.generate(rsa, crt)
val jks2 = pfx.toJks()
fun parsePk8() {
val pk8 = CryptoHelperTest::class.java.classLoader.getResource("platform.pk8").file
val x509 = CryptoHelperTest::class.java.classLoader.getResource("platform.x509.pem").file
val kb = CryptoHelper.KeyBox.parse4(File(pk8).readBytes())
println(kb.key::class)
peekKeyBox(kb)
OpenSslHelper.toJks(pk8, x509, "platform.jks")
}
@Test
fun androidCertTest() {
//platform.x509.pem: Certificate, PEM
val crt = OpenSslHelper.Crt(data = this.javaClass.classLoader.getResourceAsStream("platform.x509.pem").readBytes())
val jks = crt.toJks()
//jks.writeTo("platform.jks")
jks.check()
}
@Test
fun androidPk8Test() {
//platform.pk8: Private Key, PKCS#8, DER encoding
val pk8rsa = OpenSslHelper.PK8RsaKey(OpenSslHelper.KeyFormat.DER,
this.javaClass.classLoader.getResourceAsStream("platform.pk8").readBytes())
val pk1 = pk8rsa
.transform(OpenSslHelper.KeyFormat.DER, OpenSslHelper.KeyFormat.PEM)
.toPk1()
val crt = OpenSslHelper.Crt(data = this.javaClass.classLoader.getResourceAsStream("platform.x509.pem").readBytes())
val pfx = OpenSslHelper.Pfx(name = "androiddebugkey", thePassword = "somepassword").apply {
this.generate(pk1, crt)
}
pfx.toJks().writeTo("platform.jks")
File("platform.jks").let {
if (it.exists()) {
it.delete()
}
private fun peekKeyBox(kb: CryptoHelper.KeyBox) {
if (kb.fmt == CryptoHelper.KeyFormat.PEM) {
println("fmt=[PEM] type=[" + kb.clazz.qualifiedName + "]")
} else {
println("type=[" + kb.clazz.qualifiedName + "]")
}
}
}

@ -1,492 +0,0 @@
package cc.cfig.io
import cfig.helper.Helper
import org.junit.Assert
import org.junit.Test
import java.io.ByteArrayInputStream
class Struct3Test {
private fun constructorTestFun1(inFormatString: String) {
println(Struct3(inFormatString))
}
@Test
fun constructorTest() {
constructorTestFun1("3s")
constructorTestFun1("5x")
constructorTestFun1("5b")
constructorTestFun1("5B")
constructorTestFun1("2c")
constructorTestFun1("1h")
constructorTestFun1("1H")
constructorTestFun1("1i")
constructorTestFun1("3I")
constructorTestFun1("3q")
constructorTestFun1("3Q")
constructorTestFun1(">2b202x1b19B")
constructorTestFun1("<2b2x1b3i2q")
}
@Test
fun calcSizeTest() {
Assert.assertEquals(3, Struct3("3s").calcSize())
Assert.assertEquals(5, Struct3("5x").calcSize())
Assert.assertEquals(5, Struct3("5b").calcSize())
Assert.assertEquals(5, Struct3("5B").calcSize())
Assert.assertEquals(9, Struct3("9c").calcSize())
Assert.assertEquals(8, Struct3("2i").calcSize())
Assert.assertEquals(8, Struct3("2I").calcSize())
Assert.assertEquals(24, Struct3("3q").calcSize())
Assert.assertEquals(24, Struct3("3Q").calcSize())
}
@Test
fun toStringTest() {
println(Struct3("!4s2L2QL11QL4x47sx80x"))
println(Struct3("@4s2L2QL11QL4x47sx80x"))
}
//x
@Test
fun paddingTest() {
Assert.assertEquals("0000000000", Helper.toHexString(Struct3("5x").pack(null)))
Assert.assertEquals("0000000000", Helper.toHexString(Struct3("5x").pack(0)))
Assert.assertEquals("0101010101", Helper.toHexString(Struct3("5x").pack(1)))
Assert.assertEquals("1212121212", Helper.toHexString(Struct3("5x").pack(0x12)))
//Integer高位被截掉
Assert.assertEquals("2323232323", Helper.toHexString(Struct3("5x").pack(0x123)))
// minus 0001_0011 -> 补码 1110 1101ie. 0xed
Assert.assertEquals("ededededed", Helper.toHexString(Struct3("5x").pack(-0x13)))
//0xff
Assert.assertEquals("ffffffffff", Helper.toHexString(Struct3("5x").pack(-1)))
try {
Struct3("5x").pack("bad")
Assert.assertTrue("should throw exception here", false)
} catch (e: IllegalArgumentException) {
}
//unpack
Struct3("3x").unpack(ByteArrayInputStream(Helper.fromHexString("000000"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(0.toByte(), it[0])
}
Struct3("x2xx").unpack(ByteArrayInputStream(Helper.fromHexString("01121210"))).let {
Assert.assertEquals(3, it.size)
Assert.assertEquals(0x1.toByte(), it[0])
Assert.assertEquals(0x12.toByte(), it[1])
Assert.assertEquals(0x10.toByte(), it[2])
}
}
//c
@Test
fun characterTest() {
//constructor
Struct3("c")
//calcSize
Assert.assertEquals(3, Struct3("3c").calcSize())
//pack illegal
try {
Struct3("c").pack("a")
Assert.fail("should throw exception here")
} catch (e: Throwable) {
Assert.assertTrue(e is AssertionError || e is IllegalArgumentException)
}
//pack legal
Assert.assertEquals(
"61",
Helper.toHexString(Struct3("!c").pack('a'))
)
Assert.assertEquals(
"61",
Helper.toHexString(Struct3("c").pack('a'))
)
Assert.assertEquals(
"616263",
Helper.toHexString(Struct3("3c").pack('a', 'b', 'c'))
)
//unpack
Struct3("3c").unpack(ByteArrayInputStream(Helper.fromHexString("616263"))).let {
Assert.assertEquals(3, it.size)
Assert.assertEquals('a', it[0])
Assert.assertEquals('b', it[1])
Assert.assertEquals('c', it[2])
}
}
//b
@Test
fun bytesTest() {
//constructor
Struct3("b")
//calcSize
Assert.assertEquals(3, Struct3("3b").calcSize())
//pack
Assert.assertEquals(
"123456", Helper.toHexString(
Struct3("3b").pack(byteArrayOf(0x12, 0x34, 0x56))
)
)
Assert.assertEquals(
"123456", Helper.toHexString(
Struct3("!3b").pack(byteArrayOf(0x12, 0x34, 0x56))
)
)
Assert.assertEquals(
"123400", Helper.toHexString(
Struct3("3b").pack(byteArrayOf(0x12, 0x34))
)
)
//unpack
Struct3("3b").unpack(ByteArrayInputStream(Helper.fromHexString("123400"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals("123400", Helper.toHexString(it[0] as ByteArray))
}
Struct3("bbb").unpack(ByteArrayInputStream(Helper.fromHexString("123400"))).let {
Assert.assertEquals(3, it.size)
Assert.assertEquals("12", Helper.toHexString(it[0] as ByteArray))
Assert.assertEquals("34", Helper.toHexString(it[1] as ByteArray))
Assert.assertEquals("00", Helper.toHexString(it[2] as ByteArray))
}
}
//B: UByte array
@Test
fun uBytesTest() {
//constructor
Struct3("B")
//calcSize
Assert.assertEquals(3, Struct3("3B").calcSize())
//pack
Assert.assertEquals(
"123456", Helper.toHexString(
Struct3("3B").pack(byteArrayOf(0x12, 0x34, 0x56))
)
)
Assert.assertEquals(
"123456", Helper.toHexString(
Struct3("!3B").pack(byteArrayOf(0x12, 0x34, 0x56))
)
)
Assert.assertEquals(
"123400", Helper.toHexString(
Struct3("3B").pack(byteArrayOf(0x12, 0x34))
)
)
//unpack
Struct3("3B").unpack(ByteArrayInputStream(Helper.fromHexString("123400"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals("123400", Helper.toHexString(it[0] as UByteArray))
}
Struct3("BBB").unpack(ByteArrayInputStream(Helper.fromHexString("123400"))).let {
Assert.assertEquals(3, it.size)
Assert.assertEquals("12", Helper.toHexString(it[0] as UByteArray))
Assert.assertEquals("34", Helper.toHexString(it[1] as UByteArray))
Assert.assertEquals("00", Helper.toHexString(it[2] as UByteArray))
}
}
//s
@Test
fun stringTest() {
//constructor
Struct3("s")
//calcSize
Assert.assertEquals(3, Struct3("3s").calcSize())
//pack
Struct3("3s").pack("a")
Struct3("3s").pack("abc")
try {
Struct3("3s").pack("abcd")
Assert.fail("should throw exception here")
} catch (e: Throwable) {
Assert.assertTrue(e.toString(), e is AssertionError || e is IllegalArgumentException)
}
//unpack
Struct3("3s").unpack(ByteArrayInputStream(Helper.fromHexString("616263"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals("abc", it[0])
}
Struct3("3s").unpack(ByteArrayInputStream(Helper.fromHexString("610000"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals("a", it[0])
}
}
//h
@Test
fun shortTest() {
//constructor
Struct3("h")
//calcSize
Assert.assertEquals(6, Struct3("3h").calcSize())
//pack
Assert.assertEquals("ff7f", Helper.toHexString(Struct3("h").pack(0x7fff)))
Assert.assertEquals("0080", Helper.toHexString(Struct3("h").pack(-0x8000)))
Assert.assertEquals("7fff0000", Helper.toHexString(Struct3(">2h").pack(0x7fff, 0)))
//unpack
Struct3(">2h").unpack(ByteArrayInputStream(Helper.fromHexString("7fff0000"))).let {
Assert.assertEquals(2, it.size)
Assert.assertEquals(0x7fff.toShort(), it[0])
Assert.assertEquals(0.toShort(), it[1])
}
}
//H
@Test
fun uShortTest() {
//constructor
Struct3("H")
//calcSize
Assert.assertEquals(6, Struct3("3H").calcSize())
//pack
Assert.assertEquals("0100", Helper.toHexString(Struct3("H").pack((1U).toUShort())))
Assert.assertEquals("0100", Helper.toHexString(Struct3("H").pack(1U)))
Assert.assertEquals("ffff", Helper.toHexString(Struct3("H").pack(65535U)))
Assert.assertEquals("ffff", Helper.toHexString(Struct3("H").pack(65535)))
try {
Struct3("H").pack(-1)
Assert.fail("should throw exception here")
} catch (e: Throwable) {
Assert.assertTrue(e is AssertionError || e is IllegalArgumentException)
}
//unpack
Struct3("H").unpack(ByteArrayInputStream(Helper.fromHexString("ffff"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(65535U.toUShort(), it[0])
}
}
//i, l
@Test
fun intTest() {
//constructor
Struct3("i")
Struct3("l")
//calcSize
Assert.assertEquals(12, Struct3("3i").calcSize())
Assert.assertEquals(12, Struct3("3l").calcSize())
//pack
Struct3("i").pack(65535 + 1)
Struct3("i").pack(-1)
//unpack
Struct3("i").unpack(ByteArrayInputStream(Helper.fromHexString("00000100"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(65536, it[0])
}
Struct3("i").unpack(ByteArrayInputStream(Helper.fromHexString("ffffffff"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(-1, it[0])
}
}
//I, L
@Test
fun uIntTest() {
//constructor
Struct3("I")
Struct3("L")
//calcSize
Assert.assertEquals(12, Struct3("3I").calcSize())
Assert.assertEquals(12, Struct3("3L").calcSize())
//pack
Assert.assertEquals(
"01000000", Helper.toHexString(
Struct3("I").pack(1U)
)
)
Assert.assertEquals(
"80000000", Helper.toHexString(
Struct3(">I").pack(Int.MAX_VALUE.toUInt() + 1U)
)
)
//unpack
Struct3("I").unpack(ByteArrayInputStream(Helper.fromHexString("01000000"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(1U, it[0])
}
Struct3(">I").unpack(ByteArrayInputStream(Helper.fromHexString("80000000"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(Int.MAX_VALUE.toUInt() + 1U, it[0])
}
}
//q: Long
@Test
fun longTest() {
//constructor
Struct3("q")
//calcSize
Assert.assertEquals(24, Struct3("3q").calcSize())
//pack
Assert.assertEquals(
"8000000000000000", Helper.toHexString(
Struct3(">q").pack(Long.MIN_VALUE)
)
)
Assert.assertEquals(
"7fffffffffffffff", Helper.toHexString(
Struct3(">q").pack(Long.MAX_VALUE)
)
)
Assert.assertEquals(
"ffffffffffffffff", Helper.toHexString(
Struct3(">q").pack(-1L)
)
)
//unpack
Struct3(">q").unpack(ByteArrayInputStream(Helper.fromHexString("8000000000000000"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(Long.MIN_VALUE, it[0])
}
Struct3(">q").unpack(ByteArrayInputStream(Helper.fromHexString("7fffffffffffffff"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(Long.MAX_VALUE, it[0])
}
Struct3(">q").unpack(ByteArrayInputStream(Helper.fromHexString("ffffffffffffffff"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(-1L, it[0])
}
}
//Q: ULong
@Test
fun uLongTest() {
//constructor
Struct3("Q")
//calcSize
Assert.assertEquals(24, Struct3("3Q").calcSize())
//pack
Assert.assertEquals(
"7fffffffffffffff", Helper.toHexString(
Struct3(">Q").pack(Long.MAX_VALUE)
)
)
Assert.assertEquals(
"0000000000000000", Helper.toHexString(
Struct3(">Q").pack(ULong.MIN_VALUE)
)
)
Assert.assertEquals(
"ffffffffffffffff", Helper.toHexString(
Struct3(">Q").pack(ULong.MAX_VALUE)
)
)
try {
Struct3(">Q").pack(-1L)
} catch (e: Throwable) {
Assert.assertTrue(e is AssertionError || e is IllegalArgumentException)
}
//unpack
Struct3(">Q").unpack(ByteArrayInputStream(Helper.fromHexString("7fffffffffffffff"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(Long.MAX_VALUE.toULong(), it[0])
}
Struct3(">Q").unpack(ByteArrayInputStream(Helper.fromHexString("0000000000000000"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(ULong.MIN_VALUE, it[0])
}
Struct3(">Q").unpack(ByteArrayInputStream(Helper.fromHexString("ffffffffffffffff"))).let {
Assert.assertEquals(1, it.size)
Assert.assertEquals(ULong.MAX_VALUE, it[0])
}
}
@Test
fun legacyTest() {
Assert.assertTrue(
Struct3("<2i4b4b").pack(
1, 7321, byteArrayOf(1, 2, 3, 4), byteArrayOf(200.toByte(), 201.toByte(), 202.toByte(), 203.toByte())
)
.contentEquals(Helper.fromHexString("01000000991c000001020304c8c9cacb"))
)
Assert.assertTrue(
Struct3("<2i4b4B").pack(
1, 7321, byteArrayOf(1, 2, 3, 4), intArrayOf(200, 201, 202, 203)
)
.contentEquals(Helper.fromHexString("01000000991c000001020304c8c9cacb"))
)
Assert.assertTrue(Struct3("b2x").pack(byteArrayOf(0x13), null).contentEquals(Helper.fromHexString("130000")))
Assert.assertTrue(
Struct3("b2xi").pack(byteArrayOf(0x13), null, 55).contentEquals(Helper.fromHexString("13000037000000"))
)
Struct3("5s").pack("Good").contentEquals(Helper.fromHexString("476f6f6400"))
Struct3("5s1b").pack("Good", byteArrayOf(13)).contentEquals(Helper.fromHexString("476f6f64000d"))
}
@Test
fun legacyIntegerLE() {
//int (4B)
Assert.assertTrue(Struct3("<2i").pack(1, 7321).contentEquals(Helper.fromHexString("01000000991c0000")))
val ret = Struct3("<2i").unpack(ByteArrayInputStream(Helper.fromHexString("01000000991c0000")))
Assert.assertEquals(2, ret.size)
Assert.assertTrue(ret[0] is Int)
Assert.assertTrue(ret[1] is Int)
Assert.assertEquals(1, ret[0] as Int)
Assert.assertEquals(7321, ret[1] as Int)
//unsigned int (4B)
Assert.assertTrue(Struct3("<I").pack(2L).contentEquals(Helper.fromHexString("02000000")))
Assert.assertTrue(Struct3("<I").pack(2).contentEquals(Helper.fromHexString("02000000")))
//greater than Int.MAX_VALUE
Assert.assertTrue(Struct3("<I").pack(2147483748L).contentEquals(Helper.fromHexString("64000080")))
Assert.assertTrue(Struct3("<I").pack(2147483748).contentEquals(Helper.fromHexString("64000080")))
try {
Struct3("<I").pack(-12)
throw Exception("should not reach here")
} catch (e: Throwable) {
Assert.assertTrue(e is AssertionError || e is IllegalArgumentException)
}
//negative int
Assert.assertTrue(Struct3("<i").pack(-333).contentEquals(Helper.fromHexString("b3feffff")))
}
@Test
fun legacyIntegerBE() {
run {
Assert.assertTrue(Struct3(">2i").pack(1, 7321).contentEquals(Helper.fromHexString("0000000100001c99")))
val ret = Struct3(">2i").unpack(ByteArrayInputStream(Helper.fromHexString("0000000100001c99")))
Assert.assertEquals(1, ret[0] as Int)
Assert.assertEquals(7321, ret[1] as Int)
}
run {
Assert.assertTrue(Struct3("!i").pack(-333).contentEquals(Helper.fromHexString("fffffeb3")))
val ret2 = Struct3("!i").unpack(ByteArrayInputStream(Helper.fromHexString("fffffeb3")))
Assert.assertEquals(-333, ret2[0] as Int)
}
}
}
Loading…
Cancel
Save