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-stdlib-jdk8")
implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("cc.cfig:io:0.2")
implementation("org.slf4j:slf4j-simple:1.7.32") implementation("org.slf4j:slf4j-simple:1.7.32")
implementation("org.slf4j:slf4j-api:1.7.32") implementation("org.slf4j:slf4j-api:1.7.32")
implementation("com.fasterxml.jackson.core:jackson-annotations:2.13.1") implementation("com.fasterxml.jackson.core:jackson-annotations:2.13.1")

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

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

@ -18,7 +18,7 @@ import avb.alg.Algorithm
import avb.desc.* import avb.desc.*
import cfig.helper.CryptoHelper import cfig.helper.CryptoHelper
import cfig.helper.Helper import cfig.helper.Helper
import cc.cfig.io.Struct3 import cc.cfig.io.Struct
import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import org.bouncycastle.asn1.pkcs.RSAPrivateKey import org.bouncycastle.asn1.pkcs.RSAPrivateKey
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -98,7 +98,7 @@ class AuxBlob(
val auxSize = Helper.round_to_multiple( val auxSize = Helper.round_to_multiple(
(encodedDesc.size + encodedKey.size + encodedPkmd.size).toLong(), (encodedDesc.size + encodedKey.size + encodedPkmd.size).toLong(),
64) 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 { fun populateDescriptors(descriptors: List<Descriptor>): AuxBlob {
@ -139,7 +139,7 @@ class AuxBlob(
if (key == null) { if (key == null) {
algKey = Files.readAllBytes((Paths.get(alg.defaultKey))) 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) encodedKey = CryptoHelper.KeyBox.encodeRSAkey(rsa)
assert(alg.public_key_num_bytes == encodedKey.size) assert(alg.public_key_num_bytes == encodedKey.size)
} else { } else {

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

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

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

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

@ -17,7 +17,7 @@ package avb.desc
import avb.blob.Header import avb.blob.Header
import cfig.helper.CryptoHelper import cfig.helper.CryptoHelper
import cfig.helper.Helper import cfig.helper.Helper
import cc.cfig.io.Struct3 import cc.cfig.io.Struct
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.* import java.io.*
import java.security.MessageDigest import java.security.MessageDigest
@ -52,7 +52,7 @@ class HashTreeDescriptor(
constructor(data: InputStream, seq: Int = 0) : this() { constructor(data: InputStream, seq: Int = 0) : this() {
this.sequence = seq 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.tag = (info[0] as ULong).toLong()
this.num_bytes_following = (info[1] as ULong).toLong() this.num_bytes_following = (info[1] as ULong).toLong()
this.dm_verity_version = (info[2] as UInt).toInt() 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") 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.partition_name = info2[0] as String
this.salt = info2[1] as ByteArray this.salt = info2[1] as ByteArray
this.root_digest = info2[2] 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 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 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 padding_size = nbf_with_padding - this.num_bytes_following.toLong()
val desc = Struct3(FORMAT_STRING).pack( val desc = Struct(FORMAT_STRING).pack(
TAG, TAG,
nbf_with_padding.toULong(), nbf_with_padding.toULong(),
this.dm_verity_version, this.dm_verity_version,
@ -104,7 +104,7 @@ class HashTreeDescriptor(
this.flags, this.flags,
null 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) 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()) val bos = ByteArrayOutputStream(hashSize.toInt())
run hashing@{ run hashing@{
val padSz = calcSingleHashSize(true) - calcSingleHashSize(false) val padSz = calcSingleHashSize(true) - calcSingleHashSize(false)
val padding = Struct3("${padSz}x").pack(0) val padding = Struct("${padSz}x").pack(0)
var totalRead = 0L var totalRead = 0L
while (true) { while (true) {
val data = ByteArray(blockSz) val data = ByteArray(blockSz)
@ -179,7 +179,7 @@ class HashTreeDescriptor(
}//hashing }//hashing
if (hashSize > bos.size()) { 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() return bos.toByteArray()
} }

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

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

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

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

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

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

@ -14,7 +14,7 @@
package cfig.bootimg package cfig.bootimg
import cc.cfig.io.Struct3 import cc.cfig.io.Struct
import cfig.utils.EnvironmentVerifier import cfig.utils.EnvironmentVerifier
import cfig.bootimg.cpio.AndroidCpio import cfig.bootimg.cpio.AndroidCpio
import cfig.utils.DTC import cfig.utils.DTC
@ -363,7 +363,7 @@ class Common {
fun probeHeaderVersion(fileName: String): Int { fun probeHeaderVersion(fileName: String): Int {
return FileInputStream(fileName).let { fis -> return FileInputStream(fileName).let { fis ->
fis.skip(40) 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 package cfig.bootimg.cpio
import cc.cfig.io.Struct3 import cc.cfig.io.Struct
import org.apache.commons.compress.archivers.cpio.CpioConstants import org.apache.commons.compress.archivers.cpio.CpioConstants
import java.util.* import java.util.*
@ -38,13 +38,13 @@ class NewAsciiCpio(
var c_check: Int = 0 var c_check: Int = 0
) { ) {
init { init {
if (SIZE != Struct3(FORMAT_STRING).calcSize()) { if (SIZE != Struct(FORMAT_STRING).calcSize()) {
throw RuntimeException("cpio format check failed") throw RuntimeException("cpio format check failed")
} }
} }
fun encode(): ByteArray { fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack( return Struct(FORMAT_STRING).pack(
String.format(Locale.getDefault(), "%s", c_magic), String.format(Locale.getDefault(), "%s", c_magic),
String.format(Locale.getDefault(), "%08x", c_ino), String.format(Locale.getDefault(), "%08x", c_ino),
String.format(Locale.getDefault(), "%08x", c_mode), String.format(Locale.getDefault(), "%08x", c_mode),

@ -14,7 +14,7 @@
package cfig.bootimg.v2 package cfig.bootimg.v2
import cc.cfig.io.Struct3 import cc.cfig.io.Struct
import cfig.helper.Helper import cfig.helper.Helper
import cfig.bootimg.Common import cfig.bootimg.Common
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -58,7 +58,7 @@ open class BootHeaderV2(
return return
} }
log.warn("BootImgHeader constructor") log.warn("BootImgHeader constructor")
val info = Struct3(FORMAT_STRING).unpack(iS) val info = Struct(FORMAT_STRING).unpack(iS)
assert(20 == info.size) assert(20 == info.size)
if (info[0] != magic) { if (info[0] != magic) {
throw IllegalArgumentException("stream doesn't look like Android Boot Image Header") 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()) } (11..14).forEach { add(2.0.pow(it).toLong()) }
} }
assert(pageSizeChoices.contains(pageSize.toLong())) { "invalid parameter [pageSize=$pageSize], (choose from $pageSizeChoices)" } assert(pageSizeChoices.contains(pageSize.toLong())) { "invalid parameter [pageSize=$pageSize], (choose from $pageSizeChoices)" }
return Struct3(FORMAT_STRING).pack( return Struct(FORMAT_STRING).pack(
magic, magic,
//10I //10I
kernelLength, kernelLength,
@ -168,7 +168,7 @@ open class BootHeaderV2(
const val BOOT_IMAGE_HEADER_V0_SIZE = 0 const val BOOT_IMAGE_HEADER_V0_SIZE = 0
init { 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 package cfig.bootimg.v3
import cc.cfig.io.Struct3 import cc.cfig.io.Struct
import cfig.bootimg.Common import cfig.bootimg.Common
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.InputStream import java.io.InputStream
@ -35,7 +35,7 @@ class BootHeaderV3(
return return
} }
log.warn("BootImgHeaderV3/V4 constructor") log.warn("BootImgHeaderV3/V4 constructor")
val info = Struct3(FORMAT_STRING).unpack(iS) val info = Struct(FORMAT_STRING).unpack(iS)
assert(12 == info.size) assert(12 == info.size)
if (info[0] != magic) { if (info[0] != magic) {
throw IllegalArgumentException("stream doesn't look like Android Boot Image V3 Header") throw IllegalArgumentException("stream doesn't look like Android Boot Image V3 Header")
@ -56,7 +56,7 @@ class BootHeaderV3(
} }
fun encode(): ByteArray { fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack( return Struct(FORMAT_STRING).pack(
magic, magic,
kernelSize, kernelSize,
ramdiskSize, ramdiskSize,
@ -106,7 +106,7 @@ class BootHeaderV3(
const val pageSize: Int = 4096 const val pageSize: Int = 4096
init { 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 " "internal error: expected size $BOOT_IMAGE_HEADER_V4_SIZE "
} }
} }

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

@ -14,7 +14,7 @@
package cfig.bootimg.v3 package cfig.bootimg.v3
import cc.cfig.io.Struct3 import cc.cfig.io.Struct
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.InputStream import java.io.InputStream
@ -41,7 +41,7 @@ class VendorBootHeader(
return return
} }
log.warn("VendorBootHeader constructor") log.warn("VendorBootHeader constructor")
val info = Struct3(FORMAT_STRING).unpack(iS) val info = Struct(FORMAT_STRING).unpack(iS)
assert(16 == info.size) assert(16 == info.size)
if (info[0] != magic) { if (info[0] != magic) {
throw IllegalArgumentException("stream doesn't look like Android Vendor Boot Image") throw IllegalArgumentException("stream doesn't look like Android Vendor Boot Image")
@ -91,7 +91,7 @@ class VendorBootHeader(
} }
fun encode(): ByteArray { fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack( return Struct(FORMAT_STRING).pack(
magic, magic,
headerVersion, headerVersion,
pageSize, pageSize,
@ -134,7 +134,7 @@ class VendorBootHeader(
"I" //[v4] bootconfig size "I" //[v4] bootconfig size
init { 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 package utils
import avb.AVBInfo import avb.AVBInfo
import cc.cfig.io.Struct3 import cc.cfig.io.Struct
import cfig.Avb import cfig.Avb
import cfig.bootimg.Common import cfig.bootimg.Common
import cfig.bootimg.Signer import cfig.bootimg.Signer
@ -44,7 +44,7 @@ class Dtbo(
internal const val SIZE = 32 internal const val SIZE = 32
init { init {
assert(Struct3(FORMAT_STRING).calcSize() == SIZE) assert(Struct(FORMAT_STRING).calcSize() == SIZE)
} }
} }
@ -52,7 +52,7 @@ class Dtbo(
if (iS == null) { if (iS == null) {
return return
} }
val info = Struct3(FORMAT_STRING).unpack(iS) val info = Struct(FORMAT_STRING).unpack(iS)
assert(8 == info.size) assert(8 == info.size)
if ((info[0] as UInt).toLong() != magic) { if ((info[0] as UInt).toLong() != magic) {
throw IllegalArgumentException("stream doesn't look like DTBO header") throw IllegalArgumentException("stream doesn't look like DTBO header")
@ -73,7 +73,7 @@ class Dtbo(
} }
fun encode(): ByteArray { fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack( return Struct(FORMAT_STRING).pack(
magic, magic,
totalSize, totalSize,
headerSize, headerSize,
@ -103,12 +103,12 @@ class Dtbo(
internal const val SIZE = 32 internal const val SIZE = 32
init { init {
assert(Struct3(FORMAT_STRING).calcSize() == SIZE) assert(Struct(FORMAT_STRING).calcSize() == SIZE)
} }
} }
constructor(iS: InputStream) : this() { constructor(iS: InputStream) : this() {
val info = Struct3(FORMAT_STRING).unpack(iS) val info = Struct(FORMAT_STRING).unpack(iS)
assert(8 == info.size) assert(8 == info.size)
entrySize = info[0] as Int entrySize = info[0] as Int
entryOffset = info[1] as Int entryOffset = info[1] as Int
@ -121,7 +121,7 @@ class Dtbo(
} }
fun encode(): ByteArray { fun encode(): ByteArray {
return Struct3(FORMAT_STRING).pack( return Struct(FORMAT_STRING).pack(
entrySize, entrySize,
entryOffset, entryOffset,
id, id,

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

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

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

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

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

@ -2,52 +2,29 @@
package cfig.helper package cfig.helper
import cfig.helper.OpenSslHelper.KeyFormat import org.bouncycastle.asn1.ASN1Primitive
import org.bouncycastle.util.io.pem.PemReader 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 org.slf4j.LoggerFactory
import java.io.* import java.io.File
import java.security.cert.Certificate 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 java.util.*
import kotlin.system.exitProcess import kotlin.system.exitProcess
class Launcher { class Launcher {
companion object { 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 = val allSupportedCommands =
listOf("genrsa", "toPublicKey", "decodePEM", "toPk8", "toPk1", "toJks", "toCsr", "toCrt").toString() listOf("genrsa", "toPublicKey", "decodePEM", "toPk8", "toPk1", "toJks", "toCsr", "toCrt").toString()
private val log = LoggerFactory.getLogger(Launcher::class.java) 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? { fun getSize(size: Long): String? {
val kilo = 1024L val kilo = 1024L
val mega = kilo * kilo 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) { fun processCommand(inFile: String, info2: Properties) {
FileInputStream(inFile).use { fis -> FileInputStream(inFile).use { fis ->
info2.load(fis) info2.load(fis)
} }
log.warn("CMD=" + info2.getProperty("cmd")) log.warn("CMD=" + info2.getProperty("cmd"))
val kFile = info2.getProperty("file") val kFile = info2.getProperty("file")
when (val cmd = info2.getProperty("cmd")) { when (val cmd = info2.getProperty("cmd")) {
"genrsa" -> { "genrsa" -> {
val kLen = info2.getProperty("rsa.len").toInt() val kLen = info2.getProperty("rsa.len").toInt()
OpenSslHelper.PK1Key.generate(kLen).apply { val keyPair = KeyPairGenerator.getInstance("RSA").let {
writeTo(kFile) it.initialize(kLen)
it.generateKeyPair()
} }
log.info("RSA key(len=$kLen) written to $kFile") FileOutputStream(kFile, false).use {
} it.write(keyPair.private.encoded)
"toPublicKey" -> { log.info("RSA priv key(len=$kLen) written to $kFile")
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 -> {
}
} }
}
"decodePEM" -> { dumpPem(PemObject("RSA PRIVATE KEY", pk8toPk1(keyPair.private).encoded), "$kFile.pem.pk1")
val hint = "(PEM) --> (DER)"
val outFile = File(kFile).name + ".raw" FileOutputStream("$kFile.pub", false).use {
val decodeFromPem = CryptoHelper.KeyBox.decodePem(File(kFile).readText()) it.write(keyPair.public.encoded)
File(outFile).writeBytes(decodeFromPem) log.info("RSA pub key(len=$kLen) written to $kFile.pub")
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"
} }
OpenSslHelper.PK1Key(KeyFormat.PEM, inBytes).toCsr(info2.getProperty("csr.info")).writeTo(outFile)
log.info("$hint: $kFile => $outFile")
} }
"toJks" -> { "toPub" -> {
val hint = "RSA ==> JKS" val k = CryptoHelper.KeyBox.parse4(File(kFile).readBytes())
val outFile = File(kFile).name + ".jks" if (k.key is org.bouncycastle.asn1.pkcs.RSAPrivateKey) {
val pk8 = CryptoHelper.KeyBox.parseToPk8(kFile) dumpPublicKey(k.key.modulus, k.key.publicExponent, "pub.k")
val crt = pk8.toPk1().toV1Cert(info2.getProperty("csr.info")) } else if (k.key is sun.security.rsa.RSAPrivateCrtKeyImpl) {
OpenSslHelper.Pfx( dumpPublicKey(k.key.modulus, k.key.publicExponent, "pub.k")
name = info2.getProperty("pfx.alias") ?: "androiddebugkey", } else {
thePassword = info2.getProperty("pfx.password") ?: "somepassword" throw IllegalArgumentException("not supported")
)
.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"
} }
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" -> { "parse" -> {
val inBytes = File(kFile).readBytes() val k = CryptoHelper.KeyBox.parse4(File(kFile).readBytes())
val k = CryptoHelper.KeyBox.parse2(inBytes) as Array<*> log.info("fmt=" + k.fmt.toString())
val kType = if ((k[1] as String) == "PEM") KeyFormat.PEM else KeyFormat.DER log.info("clazz=" + k.clazz)
val outFileStem = File(info2.getProperty("file")).name log.info("key=" + k.key)
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")
} }
else -> { else -> {
//pass //pass
@ -199,157 +126,41 @@ class Launcher {
val preFile = info2.getProperty("file") val preFile = info2.getProperty("file")
info2["file"] = (if (preFile == null) "" else "$preFile,") + File(inFile).canonicalFile.path info2["file"] = (if (preFile == null) "" else "$preFile,") + File(inFile).canonicalFile.path
info2["propFile"] = File(info2.getProperty("file")).name + ".prop" info2["propFile"] = File(info2.getProperty("file")).name + ".prop"
val k = CryptoHelper.KeyBox.parse2(File(inFile).readBytes()) as Array<*> val k = CryptoHelper.KeyBox.parse4(File(inFile).readBytes())
val bSuccess = k[0] as Boolean info2["fileType"] = k.fmt
val bType = k[1] as String info2["fileClazz"] = k.clazz
if (bSuccess) { log.info("Recognized ${k.fmt}: " + k.clazz)
info2["fileType"] = bType if (k.key is sun.security.x509.X509CertImpl) {
info2["fileClazz"] = k[2]!!::class.toString() val crt = k.key
log.info("Recognized $bType: " + k[2]!!::class) val crtSubj = (crt.get("x509.info") as sun.security.x509.X509CertInfo).get("subject").toString()
if (k[2] is sun.security.x509.X509CertImpl) { val subj2 = crtSubj
val crt = k[2] as sun.security.x509.X509CertImpl .replace(", ", "/")
val crtSubj = (crt.get("x509.info") as sun.security.x509.X509CertInfo).get("subject").toString() .replace("EMAILADDRESS", "emailAddress")
val subj2 = crtSubj .replace("\\s".toRegex(), "")
.replace(", ", "/") info2.setProperty("csr.info", "/$subj2")
.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")
}
} }
} if (k.key is sun.security.rsa.RSAPrivateCrtKeyImpl) {
val pk8 = k.key
fun todo(args: Array<String>) { info2.setProperty("rsa.len", pk8.modulus.bitLength().toString())
when (args[0]) { info2["rsa.modulus"] = Helper.toHexString(pk8.modulus.toByteArray())
"crypo.list" -> { info2["rsa.privateExponent"] = Helper.toHexString(pk8.privateExponent.toByteArray())
CryptoHelper.listAll() info2["rsa.publicExponent"] = Helper.toHexString(pk8.publicExponent.toByteArray())
} info2["rsa.primeP"] = Helper.toHexString(pk8.primeP.toByteArray())
"crypto.key.5" -> { info2["rsa.primeQ"] = Helper.toHexString(pk8.primeQ.toByteArray())
val hint = "RSA private(PK8): => Public Key(PK8, PEM)" }
val kFile = args[1] if (k.key is org.bouncycastle.asn1.pkcs.RSAPrivateKey) {
val outFile = args[2] val rsa = k.key
require((CryptoHelper.KeyBox.parse2(File(kFile).readBytes()) as Array<*>)[2] is org.bouncycastle.asn1.pkcs.RSAPrivateKey) info2.setProperty("rsa.len", rsa.modulus.bitLength().toString())
val pk8rsa = OpenSslHelper.PK8RsaKey(KeyFormat.PEM, File(kFile).readBytes()) info2["rsa.modulus"] = Helper.toHexString(rsa.modulus.toByteArray())
pk8rsa.getPublicKey().writeTo(outFile) info2["rsa.privateExponent"] = Helper.toHexString(rsa.privateExponent.toByteArray())
log.info("$hint: $kFile => $outFile") info2["rsa.publicExponent"] = Helper.toHexString(rsa.publicExponent.toByteArray())
} info2["rsa.primeP"] = Helper.toHexString(rsa.prime1.toByteArray())
"crypto.key.10" -> { info2["rsa.primeQ"] = Helper.toHexString(rsa.prime2.toByteArray())
//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)
}
} }
} }
} }
fun main(args: Array<String>) { fun main(args: Array<String>) {
val log = LoggerFactory.getLogger("main") val log = LoggerFactory.getLogger("main")
val allSupportedCommands = 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 package cfig.helper
import org.apache.commons.exec.CommandLine import org.apache.commons.exec.CommandLine
import org.bouncycastle.util.encoders.Hex import org.apache.commons.exec.DefaultExecutor
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.ByteArrayInputStream
import java.io.File 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 { class OpenSslHelper {
enum class KeyFormat { companion object {
PEM, //header + metadata + base64 der private val log = LoggerFactory.getLogger(OpenSslHelper::class.java)
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])
}
fun generate(keyLength: Int, password: String): PK1Key { //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
val ret = Helper.powerRun("openssl genrsa -aes256 -passout pass:$password $keyLength", null) //openssl req -text -in server.csr -noout -verify
log.info("generateRSA:stderr: ${String(ret[1])}") //\"/C=US/ST=Utah/L=Lehi/O=Your Company, Inc./OU=IT/CN=yourdomain.com\"
return PK1Key(format = KeyFormat.PEM, data = ret[0]) 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( fun toJks(
val format: KeyFormat = KeyFormat.PEM, pk8: String, x509Pem: String,
override val data: ByteArray = byteArrayOf(), outFile: String,
override val name: String = "PK8 Private" paramSrcPass: String = "somepassword",
) : IKey { paramDstPass: String = "somepassword",
alias: String = "androiddebugkey"
/* ) {
file based: File(outFile).let {
openssl pkcs8 -nocrypt -in $(pk8_key) -inform DER -out $(rsa_key).converted.tmp if (it.exists()) it.delete()
openssl rsa -in $(rsa_key).converted.tmp -out $(rsa_key).converted }
stream based: val privKey = File.createTempFile("key.", ".tmp").let {
openssl pkcs8 -nocrypt -in - -inform DER it.deleteOnExit()
openssl rsa -in - -out - val ret = Helper.powerRun2("openssl pkcs8 -in $pk8 -inform DER -outform PEM -nocrypt", null)
*/ if (ret[0] as Boolean) {
fun toPk1(): PK1Key { it.writeBytes(ret[1] as ByteArray)
val transformed =
if (this.format != KeyFormat.PEM) {
log.warn("Only pk8+pem can be converted to RSA, transforming ...")
this.transform(KeyFormat.DER, KeyFormat.PEM)
} else { } 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( it
"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()
} }
}
/* val pk12 = File.createTempFile("key.", ".tmp").let {
openssl pkcs8 -nocrypt -in - -inform DER it.deleteOnExit()
*/ val ret = Helper.powerRun2(
fun transform(inFormat: KeyFormat, outFormat: KeyFormat): PK8RsaKey { "openssl pkcs12 -export -in $x509Pem -password pass:$paramSrcPass -inkey ${privKey.path} -name androiddebugkey",
val ret = Helper.powerRun2( null
"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)
if (ret[0] as Boolean) { if (ret[0] as Boolean) {
log.info("Jks.gen:stdout: ${String(ret[1] as ByteArray)}") it.writeBytes(ret[1] as ByteArray)
log.info("Jks.gen:error: ${String(ret[2] as ByteArray)}")
} else { } else {
log.error("Jks.gen:stdout: " + String(ret[1] as ByteArray)) log.error("stdout: " + String(ret[1] as ByteArray))
log.error("Jks.gen:stderr: " + String(ret[2] as ByteArray)) log.error("stderr: " + String(ret[2] as ByteArray))
throw RuntimeException() 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( val ret = Helper.powerRun2(
"keytool -importcert -file ${crtFile.path}" + "keytool -importkeystore " +
" -deststorepass $paramDstPass -srcstorepass $paramSrcPass " + " -deststorepass $paramDstPass -destkeystore $outFile" +
" -alias $alias " + " -srckeystore ${pk12.path} -srcstoretype PKCS12 -srcstorepass $paramSrcPass" +
" -keystore ${outFile.path}", " -alias $alias",
ByteArrayInputStream("yes\n".toByteArray()) null
) )
if (ret[0] as Boolean) { if (ret[0] as Boolean) {
log.info("toJks:error: ${String(ret[2] as ByteArray)}") log.info("$outFile is ready")
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)}")
} else { } else {
log.error("stdout: " + String(ret[1] as ByteArray)) log.error("stdout: " + String(ret[1] as ByteArray))
log.error("stderr: " + String(ret[2] as ByteArray)) log.error("stderr: " + String(ret[2] as ByteArray))
throw RuntimeException() throw java.lang.RuntimeException()
} }
} }
} }
} }

@ -14,7 +14,7 @@
package cfig.helper 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_call
import cfig.helper.Helper.Companion.check_output import cfig.helper.Helper.Companion.check_output
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
@ -159,7 +159,7 @@ class ZipHelper {
sizeFileHeader = struct.calcsize(structFileHeader) sizeFileHeader = struct.calcsize(structFileHeader)
*/ */
fun ZipArchiveEntry.getEntryOffset(): Long { fun ZipArchiveEntry.getEntryOffset(): Long {
val zipFileHeaderSize = Struct3("<4s2B4HL2L2H").calcSize() val zipFileHeaderSize = Struct("<4s2B4HL2L2H").calcSize()
val funGetLocalHeaderOffset = ZipArchiveEntry::class.declaredFunctions.filter { funcItem -> val funGetLocalHeaderOffset = ZipArchiveEntry::class.declaredFunctions.filter { funcItem ->
funcItem.name == "getLocalHeaderOffset" funcItem.name == "getLocalHeaderOffset"
}[0] }[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 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.junit.Test
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.File import java.io.File
import kotlin.test.assertTrue
class OpenSslHelperTest { class OpenSslHelperTest {
private val log = LoggerFactory.getLogger(OpenSslHelperTest::class.java) private val log = LoggerFactory.getLogger(OpenSslHelperTest::class.java)
@Test @Test
fun PKCS1test() { fun testCsr() {
//private RSA key OpenSslHelper.createCsr(
val rsa = OpenSslHelper.PK1Key.generate(4096).apply { "test.key",
val fileName = "1_rsa.key" "test.csr",
writeTo(fileName) "/C=US/ST=Utah/L=Lehi/O=Your Company, Inc./OU=IT/CN=yourdomain.com"
} )
//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()
}
} }
@Test @Test
fun PKCS8Test() { fun parsePk8() {
//private RSA key val pk8 = CryptoHelperTest::class.java.classLoader.getResource("platform.pk8").file
val rsa = OpenSslHelper.PK1Key.generate(4096) val x509 = CryptoHelperTest::class.java.classLoader.getResource("platform.x509.pem").file
val kb = CryptoHelper.KeyBox.parse4(File(pk8).readBytes())
run { //check equality: 8 == 4,5 println(kb.key::class)
val pk8Pub = rsa.getPk8PublicKey() peekKeyBox(kb)
val pk8Pub2 = rsa.toPk8(OpenSslHelper.KeyFormat.PEM).getPublicKey() OpenSslHelper.toJks(pk8, x509, "platform.jks")
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()
} }
@Test private fun peekKeyBox(kb: CryptoHelper.KeyBox) {
fun androidCertTest() { if (kb.fmt == CryptoHelper.KeyFormat.PEM) {
//platform.x509.pem: Certificate, PEM println("fmt=[PEM] type=[" + kb.clazz.qualifiedName + "]")
val crt = OpenSslHelper.Crt(data = this.javaClass.classLoader.getResourceAsStream("platform.x509.pem").readBytes()) } else {
val jks = crt.toJks() println("type=[" + kb.clazz.qualifiedName + "]")
//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()
}
} }
} }
} }

@ -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