From ec00936463f754bbf4622179743b973f1236e5e8 Mon Sep 17 00:00:00 2001 From: cfig Date: Mon, 9 Jul 2018 11:56:42 +0800 Subject: [PATCH] use javax.crypto.Cipher for raw signing - using Cipher "RSA/ECB/NoPadding" to do raw sign - also add .gitignore --- .gitignore | 2 + README.expert.md | 4 +- README.md | 8 +- README.other.md | 8 +- avb/avb_test_data/testkey_rsa2048.pk8 | Bin 0 -> 1217 bytes avb/avb_test_data/testkey_rsa4096.pk8 | Bin 0 -> 2375 bytes avb/avb_test_data/testkey_rsa8192.pk8 | Bin 0 -> 4680 bytes bbootimg/src/main/java/cfig/io/Struct.java | 24 +++--- bbootimg/src/main/kotlin/Avb.kt | 71 ++++++---------- bbootimg/src/main/kotlin/Helper.kt | 39 +++++++-- bbootimg/src/main/kotlin/Parser.kt | 12 +-- bbootimg/src/main/kotlin/R.kt | 25 +++--- bbootimg/src/main/kotlin/Signer.kt | 35 ++++---- bbootimg/src/main/kotlin/UnifiedConfig.kt | 22 +++++ bbootimg/src/main/kotlin/{ => avb}/AVBInfo.kt | 4 +- bbootimg/src/main/kotlin/avb/Blob.kt | 17 +++- bbootimg/src/main/kotlin/avb/Footer.kt | 2 +- bbootimg/src/main/kotlin/avb/Header.kt | 2 +- bbootimg/src/main/kotlin/avb/alg/Algorithm.kt | 3 +- .../src/main/kotlin/avb/alg/Algorithms.kt | 18 ++-- .../avb/desc/KernelCmdlineDescriptor.kt | 2 +- .../main/kotlin/avb/desc/UnknownDescriptor.kt | 2 +- .../main/resources/simplelogger.properties | 1 + bbootimg/src/test/kotlin/HelperTest.kt | 78 ++++++++++++++++++ .../src/test/kotlin/cfig/io/StructTest.kt | 14 ++-- 25 files changed, 264 insertions(+), 129 deletions(-) create mode 100644 .gitignore create mode 100644 avb/avb_test_data/testkey_rsa2048.pk8 create mode 100644 avb/avb_test_data/testkey_rsa4096.pk8 create mode 100644 avb/avb_test_data/testkey_rsa8192.pk8 rename bbootimg/src/main/kotlin/{ => avb}/AVBInfo.kt (98%) create mode 100644 bbootimg/src/main/resources/simplelogger.properties create mode 100644 bbootimg/src/test/kotlin/HelperTest.kt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..23841a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +.gradle diff --git a/README.expert.md b/README.expert.md index 71a850d..fe398f1 100644 --- a/README.expert.md +++ b/README.expert.md @@ -133,7 +133,7 @@ | +--------------------------------+-------------------------+ | | Padding | align by block_size | +------+--------------------------------+-------------------------+ --> + (block_size * n) - + +---------------------------------------+-------------------------+ | | | | | | @@ -141,7 +141,7 @@ | | | | | | +--------------------------------------- -------------------------+ - + +---------------------------------------+-------------------------+ --> partition_size - block_size | Padding | block_size - 64 | +---------------------------------------+-------------------------+ --> partition_size - 64 diff --git a/README.md b/README.md index 83fb5fd..28cfd49 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,10 @@ Then put your boot.img at **$(CURDIR)/boot.img**, then start gradle 'unpack' tas Your get the flattened kernel and /root filesystem under **$(CURDIR)/build/unzip\_boot**: build/unzip_boot/ - ├── bootimg.json + ├── boot.img.avb.json (AVB only) + ├── bootimg.json (boot image info) ├── kernel - ├── second + ├── second (2nd bootloader, if exists) └── root Then you can edit the actual file contents, like rootfs or kernel. @@ -76,3 +77,6 @@ https://android.googlesource.com/platform/external/bouncycastle cpio / fs\_config https://android.googlesource.com/platform/system/core + +AVB +https://android.googlesource.com/platform/external/avb/ diff --git a/README.other.md b/README.other.md index 1e909e7..0b2f222 100644 --- a/README.other.md +++ b/README.other.md @@ -6,10 +6,10 @@ | - tag | 8 | --> +0 | - num_bytes_following | 8 | --> +8 | - hash algorithm | 8 | --> +16 - | - partition name | 32 | - | - salt length | 4 | - | - digest length | 4 | - | - reserved | 60 | + | - partition name | 32 | + | - salt length | 4 | + | - digest length | 4 | + | - reserved | 60 | +--------------------------------+-------------------------+ | Partition name | | +--------------------------------+-------------------------+ diff --git a/avb/avb_test_data/testkey_rsa2048.pk8 b/avb/avb_test_data/testkey_rsa2048.pk8 new file mode 100644 index 0000000000000000000000000000000000000000..46b15b4212d7e0e1bcdde66d884f25a5662e4e15 GIT binary patch literal 1217 zcmV;y1U~yPf&{$+0RS)!1_>&LNQUrr!ay9qXGc{0)hbn0LE2O-7+Mi z;10Zg{k@`7fLLjM^-4g8m5@|fJypv8HZ&Ug;oy)e8Zu~G^WUut`LWRV>Z?Z|cD@^! zOJ}2E`rqcY`0jHSU%0V?-bsnoJ=ds0nSpi4*o1q%RH!0kM7%Tx5Il*((eo?=Ws$L< z(a0fI6`4pNCv@)tdUmA?YyIQ;?G5=4dO`Yj1vg3lG*3$bN#vOKiH^-SsyQ90eO+r> zQqgs3sNa0v92@!B8C$nUOu29_t>xB;eC?8MMhFnKb^|^MtWCBo%@P2;)TaZ}8<7Yj zHd_(?uUTocJcuMWEn&LFnNb**`6EJ>pNB5ZvQ1y9B=SYt;$eT{(vb8>490I%U0@Z=X?Uu)C}P3RcRc)T2scf6 z$lAz*9gLM%JvCdifq?+) zJD;{0sMeZ->MwkUQAW&hX1#JG41==1u-_qwm^N6B?i}G}J>}~T+C%pg4k^*j#f1TR zpyu_g8;213cj?MY;h~-@&fIFG->a?GWDBFAHfgWY=>-<#FT6j*ZsVm7+?J3$uzbfE z3qPIdaiHr$>`is>SdoJ8|BcvXGa~&0fq?+m*S{(a)|zE~J+Vi`4vi*$Lw-{KGQLEa8S=u zRcm5vVaoF_A}niYl7Lw$4nSyC_K5{N`p~$CuST4f5Gtc^fF(1B&!8QCSmUY#i8+x1 zfq-I@vGP^^b1;1THo7p{)|iG(PsXieu`CYFfHXx>#59O;FqtHWd-`ow`gCY(Ckstu zXu+L+*haRi9TzlOh4<5*ilFUw)6)}B#t*5|&W!mby_*Fx-PoO5m!KivD@z`bZ-sBW7$ zLGJpW$Ue3v;?PWDTrA2SCtNIsfHF&LvLDQ>3ISlPAJXujAk~EGFc^Pqh17nrZN$Ug f@p=6=C;|9ZQ$RFHfme$VHl>!n#w&1Hwyft%;Ui9a literal 0 HcmV?d00001 diff --git a/avb/avb_test_data/testkey_rsa4096.pk8 b/avb/avb_test_data/testkey_rsa4096.pk8 new file mode 100644 index 0000000000000000000000000000000000000000..1bdd26b5afda2901e3fe7e3a3f03617c5fd7ce64 GIT binary patch literal 2375 zcmV-N3Apw!f(b(c0RS)!1_>&LNQUwEii%!DFOii0)heo0N4bt`rRq0~M@ef|i7ILdZd@N;3l(iNs0J{e769-dF`DD2$dK-k8h}dVOBl)LG z+)5AkIvB20s1^ROt*kZLJFO?Yujtb+H#F8!D{NXP*OmsaYq)^Q`Y^Pyg|khns>2pE z8o1}j`uPcWtCy4OM6S;uyzqb#+anA|ak*{QdtgV07FLtD!YZ1uOaTlq*pr9w?QV;{ zFkAMfuib!rMP84QlU_vrxPk}ndCI>rb7ENI&BVOI5gnmQzy2qEq4qVSUdr58FFx?e zU50A-uV$z1&A9EMU%H!x8r2D|lukR(%UsEMfcMO%N~mhs(=ieG_5x!n8ZFTpW*Zhk zy}kEMz)IyONnd|h<0mcU$z~2xI2Jhj7VA?#=KblSUL{%Bc}4#NOeUfLT8_d|p%wXi z^jv|P7)LeOEd}YV4>fcZqmGNrC)#{n68KbL?_Kf$%Ta9^i4 zuSg;iMK1LZhmzui0r?US6cr@SiJ9)X#20XI73#jmY#(B?^eq;L`&J%BtFAKSUXkrn zW?sGRt>zLr4TH((Yiup0?!;PVu8f!04Qxrr^~91fiwW*C+Nn3TNf`JJU`5C?k?$Z^ z5WaXVgC^F>VfioMzXdQ-S`r)pr(Mi^F2$9LqdV|JE?(UL+X4dt009Dm0st1EzYqOu zB(^(}!MFY>b8N0r@nviHPY!}G%HRq_lvcy z(7kWl53aF+m0*h-I*igr?j`((UDtP?WT<=v4sVnZN(@8$Ye!O%E1-IZonk_1{t3|s zpkI!1lG$H5gG&7I%MAt9@?5`qH#gYM7*77-QqztlJF4+ep!DULn`UU$3yk$~2~6;* z=3V}yoAW!Yp_6DRv^&G!V4VZ)C4;W_L-yQrl(&53m4=J3w z(CCxm4Wu@gSagW3m;(#ii*2?ccmR_+0=AEn#ud&(%T7xuFLtv$9Gd1*C=88<-3!yT zE9qWE&&a1Cur!uow`zWwY76!B4mx)Imwb+sc_Xj2X(_2rX7I=IQpc|X!_-I9c+zrW+EE41TN1~ZDEA;Kg}lXZA?xUe=!hp&p}+p#y4*WPg1 zN}0Wu<45vh#(l=)xj|G?&Ni z`O)>Ng%9^Z&Y%PPb_-rfz@L7&Re<^k-E;V7Dw2(%|6SP1PzFwDB}ntZBM>xi$*?smiT zqDkZgv5tu1H~lHFug{aM1xCNDgrnl#G*HwK;||a!80)hbmA2Ac0owcus zq3WD7(1p~0*soM2JH&*Ez)0k_QI)BS>ou+H{RZTtiUYCZ2HF9hE#rgM>@Duf-#B!4 z)E+_*Z*nRjM04Cl!P^MQTG7QZ0)hbn0Ji1k4Uq0T z@@hV87Q|ecWTK+xzA!Qxvqhti1@>B1AEq?C+m?}XO+82pQV=fb7AUqcgf^kI?!5fY z-_JddPLpHzu%j+i!~`TXMfko+HG zHw`V8`GGWvr-^H8*(EKl(`B>-W#SBt_aTt#Ga}f(3=fRy(uACy6E(;Wj7yhGO^-71 zqj8cn4&!2}W^UDd3343nvl1_*m6Fl}D56}RYG>Uy3VD8v5;l?{KQ^S(lHK+oUFt&J z#V?BJ#49Wu>*Ck+bF~5&P%~Oiuw210XUUOoWMmnZ5C>r0$`L``Gv=PJLR-Q@tL~)6 z5AcyrEWFMMUpO48S`vSakS1S&_XeI?7&HYnvC~;vmxpZg!S4{MgwiQF8wi7LzQnvR z@DrsQX4D)agp(kKLxGBw74&ut2x#90$wtGMhmH4?LVRDh0uMUpfL73+Qmdx}3tkDd tNQE5u*ZHP{4BofP6t4TX`Wxd9xjzI-a)hUgATg|WdT$J*U=J0$CvU2PjrITl literal 0 HcmV?d00001 diff --git a/avb/avb_test_data/testkey_rsa8192.pk8 b/avb/avb_test_data/testkey_rsa8192.pk8 new file mode 100644 index 0000000000000000000000000000000000000000..3890c1b76ecf3bf2735aa78bdb7504dc9d569dff GIT binary patch literal 4680 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 KW;)CB#t-JGq6fnO literal 0 HcmV?d00001 diff --git a/bbootimg/src/main/java/cfig/io/Struct.java b/bbootimg/src/main/java/cfig/io/Struct.java index cee99a8..b6efbdb 100644 --- a/bbootimg/src/main/java/cfig/io/Struct.java +++ b/bbootimg/src/main/java/cfig/io/Struct.java @@ -19,8 +19,8 @@ import static org.junit.Assert.assertEquals; public class Struct { private static Logger log = LoggerFactory.getLogger(Struct.class); - public ByteOrder byteOrder = ByteOrder.LITTLE_ENDIAN; - public List formats = new ArrayList<>(); + private ByteOrder byteOrder = ByteOrder.LITTLE_ENDIAN; + private List formats = new ArrayList<>(); public Struct(String formatString) { Matcher m = Pattern.compile("(\\d*)([a-zA-Z])").matcher(formatString); @@ -97,11 +97,11 @@ public class Struct { } } - public Integer calcsize() { + public Integer calcSize() { Integer ret = 0; for (Object[] format : formats) { if (format[0] == Byte.class || format[0] == Character.class || format[0] == PadByte.class) { - ret += 1 * (int) format[1]; + ret += (int) format[1]; continue; } if (format[0] == Short.class) { @@ -145,7 +145,8 @@ public class Struct { for (Object[] format : this.formats) { //return 'null' for padding bytes if (format[0] == PadByte.class) { - iS.skip((Integer) format[1]); + long skipped = iS.skip((Integer) format[1]); + assertEquals((long) (Integer) format[1], skipped); ret.add(null); continue; } @@ -233,7 +234,7 @@ public class Struct { throw new IllegalArgumentException("argument size " + args.length + " doesn't match format size " + this.formats.size()); } - ByteBuffer bf = ByteBuffer.allocate(this.calcsize()); + ByteBuffer bf = ByteBuffer.allocate(this.calcSize()); bf.order(this.byteOrder); for (int i = 0; i < args.length; i++) { Object arg = args[i]; @@ -269,7 +270,7 @@ public class Struct { log.error("container size " + size + ", value size " + ((byte[]) arg).length); throw new IllegalArgumentException("Index[" + i + "] arg [" + arg + "] with type [" + format + "] size overflow"); } else { - //perfect match + log.debug("perfect match, paddingSize is zero"); } continue; } @@ -338,22 +339,21 @@ public class Struct { } else { throw new IllegalArgumentException("Index[" + i + "] Unsupported arg [" + arg + "] with type [" + format + "]"); } - continue; } } log.debug("Pack Result:" + Helper.Companion.toHexString(bf.array())); return bf.array(); } - public static class UnsignedInt { + private static class UnsignedInt { } - public static class UnsignedLong { + private static class UnsignedLong { } - public static class UnsignedShort { + private static class UnsignedShort { } - public static class PadByte { + private static class PadByte { } } diff --git a/bbootimg/src/main/kotlin/Avb.kt b/bbootimg/src/main/kotlin/Avb.kt index 57e2775..0996c2e 100755 --- a/bbootimg/src/main/kotlin/Avb.kt +++ b/bbootimg/src/main/kotlin/Avb.kt @@ -29,7 +29,7 @@ class Avb { partition_name: String, rollback_index: Long, common_algorithm: String, - common_key_path: String) { + inReleaseString: String?) { var original_image_size = 0L //required libavb version if (use_persistent_digest || do_not_use_ab) { @@ -81,7 +81,6 @@ class Avb { if (!use_persistent_digest) hd.digest = digest log.info("encoded hash descriptor:" + Hex.encodeHexString(hd.encode())) val vbmeta_blob = generateVbMetaBlob(common_algorithm, - common_key_path, null, arrayOf(hd as Descriptor), null, @@ -89,15 +88,8 @@ class Avb { 0, null, null, - null, - false, - null, - false, - null, - null, - null, - false, - 0) + 0, + inReleaseString) log.debug("vbmeta_blob: " + Helper.toHexString(vbmeta_blob)) if (hd.image_size % BLOCK_SIZE != 0L) { @@ -124,41 +116,33 @@ class Avb { footer.originalImageSize = original_image_size footer.vbMetaOffset = vbmeta_offset footer.vbMetaSize = vbmeta_blob.size.toLong() - val footer_blob = footer.encode() - val footer_blob_with_padding = Helper.join( - Struct("${BLOCK_SIZE - Footer.SIZE}x").pack(null), footer_blob) - log.info("footer:" + Helper.toHexString(footer_blob)) + val footerBob = footer.encode() + val footerBlobWithPadding = Helper.join( + Struct("${BLOCK_SIZE - Footer.SIZE}x").pack(null), footerBob) + log.info("footer:" + Helper.toHexString(footerBob)) log.info(footer.toString()) FileOutputStream(image_file, true).use { fos -> - fos.write(footer_blob_with_padding) + fos.write(footerBlobWithPadding) } } fun generateVbMetaBlob(algorithm_name: String, - key_path: String?, public_key_metadata_path: String?, descriptors: Array, chain_partitions: String?, inRollbackIndex: Long, inFlags: Long, props: String?, - props_from_file: String?, kernel_cmdlines: String?, - setup_rootfs_from_kernel: Boolean, - ht_desc_to_setup: String?, - include_descriptors_from_image: Boolean, - signing_helper: String?, - signing_helper_with_files: String?, - release_string: String?, - append_to_release_string: Boolean, - required_libavb_version_minor: Int): ByteArray { + required_libavb_version_minor: Int, + inReleaseString: String?): ByteArray { //encoded descriptors var encodedDesc: ByteArray = byteArrayOf() descriptors.forEach { encodedDesc = Helper.join(encodedDesc, it.encode()) } //algorithm val alg = Algorithms.get(algorithm_name)!! //encoded pubkey - val encodedKey = Blob.encodePubKey(alg, Files.readAllBytes(Paths.get(key_path))) + val encodedKey = Blob.encodePubKey(alg) //3 - whole aux blob val auxBlob = Blob.getAuxDataBlob(encodedDesc, encodedKey) @@ -179,6 +163,9 @@ class Avb { signature_offset = alg.hash_num_bytes.toLong() signature_size = alg.signature_num_bytes.toLong() + descriptors_offset = 0 + descriptors_size = encodedDesc.size.toLong() + public_key_offset = descriptors_size public_key_size = encodedKey.size.toLong() @@ -186,15 +173,16 @@ class Avb { public_key_metadata_size = 0 public_key_metadata_offset = public_key_offset + public_key_size - descriptors_offset = 0 - descriptors_size = encodedDesc.size.toLong() - rollback_index = inRollbackIndex flags = inFlags + if (inReleaseString != null) { + log.info("Using preset release string: $inReleaseString") + this.release_string = inReleaseString + } }.encode() //2 - auth blob - var authBlob = Blob.getAuthBlob(headerBlob, auxBlob, algorithm_name, key_path) + var authBlob = Blob.getAuthBlob(headerBlob, auxBlob, algorithm_name) return Helper.join(headerBlob, authBlob, auxBlob) } @@ -322,12 +310,12 @@ class Avb { return ai } - fun packVbMeta(key_path: String, info: AVBInfo? = null): ByteArray { + fun packVbMeta(info: AVBInfo? = null): ByteArray { val ai = info ?: ObjectMapper().readValue(File(getJsonFileName("vbmeta.img")), AVBInfo::class.java) val alg = Algorithms.get(ai.header!!.algorithm_type.toInt())!! val encodedDesc = ai.auxBlob!!.encodeDescriptors() //encoded pubkey - val encodedKey = Blob.encodePubKey(alg, Files.readAllBytes(Paths.get(key_path))) + val encodedKey = Blob.encodePubKey(alg, Files.readAllBytes(Paths.get(alg.defaultKey))) //3 - whole aux blob var auxBlob = byteArrayOf() @@ -348,6 +336,9 @@ class Avb { authentication_data_block_size = Helper.round_to_multiple( (alg.hash_num_bytes + alg.signature_num_bytes).toLong(), 64) + descriptors_offset = 0 + descriptors_size = encodedDesc.size.toLong() + hash_offset = 0 hash_size = alg.hash_num_bytes.toLong() @@ -360,15 +351,12 @@ class Avb { //TODO: support pubkey metadata public_key_metadata_size = 0 public_key_metadata_offset = public_key_offset + public_key_size - - descriptors_offset = 0 - descriptors_size = encodedDesc.size.toLong() }.encode() //2 - auth blob var authBlob = byteArrayOf() if (ai.authBlob != null) { - authBlob = Blob.getAuthBlob(headerBlob, auxBlob, alg.name, key_path) + authBlob = Blob.getAuthBlob(headerBlob, auxBlob, alg.name) } else { log.info("No auth blob") } @@ -376,8 +364,8 @@ class Avb { return Helper.join(headerBlob, authBlob, auxBlob) } - fun packVbMetaWithPadding(key_path: String, info: AVBInfo? = null) { - val rawBlob = packVbMeta(key_path, info) + fun packVbMetaWithPadding(info: AVBInfo? = null) { + val rawBlob = packVbMeta(info) val paddingSize = Helper.round_to_multiple(rawBlob.size.toLong(), BLOCK_SIZE) - rawBlob.size val paddedBlob = Helper.join(rawBlob, Struct("${paddingSize}x").pack(null)) log.info("raw vbmeta size ${rawBlob.size}, padding size $paddingSize, total blob size ${paddedBlob.size}") @@ -391,13 +379,8 @@ class Avb { val AVB_VERSION_MINOR = 1 val AVB_VERSION_SUB = 0 - //Keep in sync with libavb/avb_footer.h. - val AVB_FOOTER_VERSION_MAJOR = 1 - val AVB_FOOTER_VERSION_MINOR = 0 - fun getJsonFileName(image_file: String): String { val fileName = File(image_file).name -// val jsonFile = fileName.substring(0, fileName.lastIndexOf(".")) + ".json" val jsonFile = "$fileName.avb.json" return UnifiedConfig.workDir + jsonFile } diff --git a/bbootimg/src/main/kotlin/Helper.kt b/bbootimg/src/main/kotlin/Helper.kt index 7537916..7db51e3 100644 --- a/bbootimg/src/main/kotlin/Helper.kt +++ b/bbootimg/src/main/kotlin/Helper.kt @@ -1,5 +1,6 @@ package cfig +import avb.alg.Algorithms import cfig.io.Struct import com.google.common.math.BigIntegerMath import org.apache.commons.codec.binary.Hex @@ -18,8 +19,14 @@ import java.io.* import java.math.BigInteger import java.math.RoundingMode import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.nio.file.Paths +import java.security.KeyFactory +import java.security.PrivateKey +import java.security.spec.PKCS8EncodedKeySpec import java.util.zip.GZIPInputStream import java.util.zip.GZIPOutputStream +import javax.crypto.Cipher class Helper { companion object { @@ -144,7 +151,7 @@ class Helper { } } - fun extractImageData(fileName: String, outImgName: String, offset: Long, length: Int) { + fun extractFile(fileName: String, outImgName: String, offset: Long, length: Int) { if (0 == length) { return } @@ -167,7 +174,6 @@ class Helper { } } - /* read RSA private key assert exp == 65537 @@ -179,15 +185,14 @@ class Helper { fun encodeRSAkey(key: ByteArray): ByteArray { val p2 = PemReader(InputStreamReader(ByteArrayInputStream(key))).readPemObject() Assert.assertEquals("RSA PRIVATE KEY", p2.type) - val rsa = RSAPrivateKey.getInstance(p2.content) Assert.assertEquals(65537.toBigInteger(), rsa.publicExponent) val numBits: Int = BigIntegerMath.log2(rsa.modulus, RoundingMode.CEILING) log.debug("modulus: " + rsa.modulus) - log.debug("numBits: " + numBits) + log.debug("numBits: $numBits") val b = BigInteger.valueOf(2).pow(32) val n0inv = (b - rsa.modulus.modInverse(b)).toLong() - log.debug("n0inv = " + n0inv) + log.debug("n0inv = $n0inv") val r = BigInteger.valueOf(2).pow(numBits) val rrModn = (r * r).mod(rsa.modulus) log.debug("BB: " + numBits / 8 + ", mod_len: " + rsa.modulus.toByteArray().size + ", rrmodn = " + rrModn.toByteArray().size) @@ -203,9 +208,22 @@ class Helper { return ret } + //inspired by + // https://stackoverflow.com/questions/40242391/how-can-i-sign-a-raw-message-without-first-hashing-it-in-bouncy-castle + // "specifying Cipher.ENCRYPT mode or Cipher.DECRYPT mode doesn't make a difference; + // both simply perform modular exponentiation" fun rawSign(keyPath: String, data: ByteArray): ByteArray { -// openssl rsautl -sign -inkey /Users/yu/work/boot/avb/avb_test_data/testkey_rsa4096.pem -raw - log.debug("Raw sign data: SIZE = " + data.size) + val privk = Helper.readPrivateKey(keyPath) + val cipher = Cipher.getInstance("RSA/ECB/NoPadding").apply { + this.init(Cipher.ENCRYPT_MODE, privk) + this.update(data) + } + return cipher.doFinal() + } + + fun rawSignOpenSsl(keyPath: String, data: ByteArray): ByteArray { + log.debug("raw input: " + Hex.encodeHexString(data)) + log.debug("Raw sign data size = ${data.size}, key = $keyPath") var ret = byteArrayOf() val exe = DefaultExecutor() val stdin = ByteArrayInputStream(data) @@ -215,6 +233,7 @@ class Helper { try { exe.execute(CommandLine.parse("openssl rsautl -sign -inkey $keyPath -raw")) ret = stdout.toByteArray() + log.debug("Raw signature size = " + ret.size) } catch (e: ExecuteException) { log.error("Execute error") } finally { @@ -238,6 +257,12 @@ class Helper { } } + @Throws(Exception::class) + fun readPrivateKey(filename: String): PrivateKey { + val spec = PKCS8EncodedKeySpec(Files.readAllBytes(Paths.get(filename))) + return KeyFactory.getInstance("RSA").generatePrivate(spec) + } + private val log = LoggerFactory.getLogger("Helper") } } diff --git a/bbootimg/src/main/kotlin/Parser.kt b/bbootimg/src/main/kotlin/Parser.kt index c361b57..538b360 100644 --- a/bbootimg/src/main/kotlin/Parser.kt +++ b/bbootimg/src/main/kotlin/Parser.kt @@ -201,20 +201,20 @@ class Parser { log.info(imgArgs.toString()) log.info(imgInfo.toString()) - Helper.extractImageData(imgArgs.output, imgArgs.kernel, imgInfo.kernelPosition.toLong(), imgInfo.kernelLength) + Helper.extractFile(imgArgs.output, imgArgs.kernel, imgInfo.kernelPosition.toLong(), imgInfo.kernelLength) log.info("kernel dumped to ${imgArgs.kernel}") imgArgs.ramdisk?.let { ramdisk -> log.info("ramdisk dumped to ${imgArgs.ramdisk}") - Helper.extractImageData(imgArgs.output, ramdisk, imgInfo.ramdiskPosition.toLong(), imgInfo.ramdiskLength) + Helper.extractFile(imgArgs.output, ramdisk, imgInfo.ramdiskPosition.toLong(), imgInfo.ramdiskLength) Helper.unGnuzipFile(ramdisk, workDir + "ramdisk.img") unpackRamdisk(imgArgs) } imgArgs.second?.let { second -> - Helper.extractImageData(imgArgs.output, second, imgInfo.secondBootloaderPosition.toLong(), imgInfo.secondBootloaderLength) + Helper.extractFile(imgArgs.output, second, imgInfo.secondBootloaderPosition.toLong(), imgInfo.secondBootloaderLength) log.info("second bootloader dumped to ${imgArgs.second}") } imgArgs.dtbo?.let { dtbo -> - Helper.extractImageData(imgArgs.output, dtbo, imgInfo.recoveryDtboPosition.toLong(), imgInfo.recoveryDtboLength) + Helper.extractFile(imgArgs.output, dtbo, imgInfo.recoveryDtboPosition.toLong(), imgInfo.recoveryDtboLength) log.info("dtbo dumped to ${imgArgs.dtbo}") } val cfg = UnifiedConfig.fromArgs(imgArgs, imgInfo) @@ -226,10 +226,6 @@ class Parser { companion object { private val log = LoggerFactory.getLogger("Parser")!! - fun readValues(iS: InputStream, vararg key: Any) { - - } - fun readShort(iS: InputStream): Short { val bf = ByteBuffer.allocate(128) bf.order(ByteOrder.LITTLE_ENDIAN) diff --git a/bbootimg/src/main/kotlin/R.kt b/bbootimg/src/main/kotlin/R.kt index 767f40b..8078595 100755 --- a/bbootimg/src/main/kotlin/R.kt +++ b/bbootimg/src/main/kotlin/R.kt @@ -1,5 +1,6 @@ package cfig +import avb.AVBInfo import com.fasterxml.jackson.databind.ObjectMapper import org.slf4j.LoggerFactory import java.io.File @@ -15,7 +16,7 @@ fun main(args: Array) { Avb().parseVbMeta(args[1]) } "pack" -> { - Avb().packVbMetaWithPadding("/Users/yu/work/boot/avb/avb_test_data/testkey_rsa4096.pem") + Avb().packVbMetaWithPadding() } "sign" -> { log.info("vbmeta is already signed") @@ -27,10 +28,13 @@ fun main(args: Array) { if (File(UnifiedConfig.workDir).exists()) File(UnifiedConfig.workDir).deleteRecursively() File(UnifiedConfig.workDir).mkdirs() Parser().parseAndExtract(fileName = args[1], avbtool = args[3]) - Avb().parseVbMeta(args[1]) - if (File("vbmeta.img").exists()) { - Avb().parseVbMeta("vbmeta.img") + if (UnifiedConfig.readBack()[2] is ImgInfo.AvbSignature) { + log.info("continue to analyze vbmeta info in " + args[1]) + Avb().parseVbMeta(args[1]) + if (File("vbmeta.img").exists()) { + Avb().parseVbMeta("vbmeta.img") + } } } "pack" -> { @@ -38,15 +42,10 @@ fun main(args: Array) { } "sign" -> { Signer.sign(avbtool = args[3], bootSigner = args[4]) - - val readBack = ObjectMapper().readValue(File(UnifiedConfig.workDir + "bootimg.json"), - UnifiedConfig::class.java).toArgs() - val imgArgs = readBack[0] as ImgArgs - val info = readBack[1] as ImgInfo - if (imgArgs.verifyType == ImgArgs.VerifyType.AVB) { + val readBack = UnifiedConfig.readBack() + if ((readBack[0] as ImgArgs).verifyType == ImgArgs.VerifyType.AVB) { if (File("vbmeta.img").exists()) { - val sig = ObjectMapper().readValue( - Signer.mapToJson(info.signature as LinkedHashMap<*, *>), ImgInfo.AvbSignature::class.java) + val sig = readBack[2] as ImgInfo.AvbSignature val newBootImgInfo = Avb().parseVbMeta(args[1] + ".signed") val hashDesc = newBootImgInfo.auxBlob!!.hashDescriptors[0] val origVbMeta = ObjectMapper().readValue(File(Avb.getJsonFileName("vbmeta.img")), @@ -60,7 +59,7 @@ fun main(args: Array) { } ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(Avb.getJsonFileName("vbmeta.img")), origVbMeta) log.info("vbmeta info updated") - Avb().packVbMetaWithPadding("/Users/yu/work/boot/avb/avb_test_data/testkey_rsa4096.pem") + Avb().packVbMetaWithPadding() } else { //no vbmeta provided } diff --git a/bbootimg/src/main/kotlin/Signer.kt b/bbootimg/src/main/kotlin/Signer.kt index 77b4a1d..be32914 100644 --- a/bbootimg/src/main/kotlin/Signer.kt +++ b/bbootimg/src/main/kotlin/Signer.kt @@ -1,5 +1,7 @@ package cfig +import avb.AVBInfo +import avb.alg.Algorithms import com.fasterxml.jackson.databind.ObjectMapper import org.apache.commons.exec.CommandLine import org.apache.commons.exec.DefaultExecutor @@ -13,46 +15,51 @@ class Signer { fun sign(avbtool: String, bootSigner: String) { log.info("Loading config from ${workDir}bootimg.json") - val cfg = ObjectMapper().readValue(File(workDir + "bootimg.json"), UnifiedConfig::class.java) - val readBack = cfg.toArgs() + val readBack = UnifiedConfig.readBack() val args = readBack[0] as ImgArgs - val info = readBack[1] as ImgInfo when (args.verifyType) { ImgArgs.VerifyType.VERIFY -> { log.info("Signing with verified-boot 1.0 style") - val sig = ObjectMapper().readValue( - mapToJson(info.signature as LinkedHashMap<*, *>), ImgInfo.VeritySignature::class.java) + val sig = readBack[2] as ImgInfo.VeritySignature DefaultExecutor().execute(CommandLine.parse("java -jar $bootSigner " + "${sig.path} ${args.output}.clear ${sig.verity_pk8} ${sig.verity_pem} ${args.output}.signed")) } ImgArgs.VerifyType.AVB -> { log.info("Adding hash_footer with verified-boot 2.0 style") - val sig = ObjectMapper().readValue( - mapToJson(info.signature as LinkedHashMap<*, *>), ImgInfo.AvbSignature::class.java) + val sig = readBack[2] as ImgInfo.AvbSignature File(args.output + ".clear").copyTo(File(args.output + ".signed")) - val cmdlineStr = "$avbtool add_hash_footer " + + val ai = ObjectMapper().readValue(File(Avb.getJsonFileName(args.output)), AVBInfo::class.java) + val signKey = Algorithms.get(sig.algorithm!!) + var cmdlineStr = "$avbtool add_hash_footer " + "--image ${args.output}.signed " + "--partition_size ${sig.imageSize} " + "--salt ${sig.salt} " + "--partition_name ${sig.partName} " + "--hash_algorithm ${sig.hashAlgorithm} " + - "--algorithm ${sig.algorithm} " + - "--key avb/avb_test_data/testkey_rsa4096.pem" + "--algorithm ${sig.algorithm}" + if (signKey!!.defaultKey.isNotBlank()) { + cmdlineStr += "--key $signKey" + } log.warn(cmdlineStr) - DefaultExecutor().execute(CommandLine.parse(cmdlineStr)) + val cmdLine = CommandLine.parse(cmdlineStr) + cmdLine.addArgument("--internal_release_string") + cmdLine.addArgument(ai.header!!.release_string, false) + DefaultExecutor().execute(cmdLine) verifyAVBIntegrity(args, avbtool) File(args.output + ".clear").copyTo(File(args.output + ".signed2")) + val alg = Algorithms.get(ai.header!!.algorithm_type.toInt()) Avb().add_hash_footer(args.output + ".signed2", sig.imageSize!!.toLong(), - false, false, + false, + false, salt = sig.salt, hash_algorithm = sig.hashAlgorithm!!, partition_name = sig.partName!!, - rollback_index = 0, + rollback_index = ai.header!!.rollback_index, common_algorithm = sig.algorithm!!, - common_key_path = "avb/avb_test_data/testkey_rsa4096.pem") + inReleaseString = ai.header!!.release_string) } } } diff --git a/bbootimg/src/main/kotlin/UnifiedConfig.kt b/bbootimg/src/main/kotlin/UnifiedConfig.kt index 87fccf8..17173d6 100644 --- a/bbootimg/src/main/kotlin/UnifiedConfig.kt +++ b/bbootimg/src/main/kotlin/UnifiedConfig.kt @@ -1,7 +1,9 @@ package cfig import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.databind.ObjectMapper import org.slf4j.LoggerFactory +import java.io.File @JsonInclude(JsonInclude.Include.NON_NULL) data class UnifiedConfig( @@ -136,5 +138,25 @@ data class UnifiedConfig( return ret } + + fun readBack(): Array { + var ret: Array = arrayOfNulls(3) + val readBack = ObjectMapper().readValue(File(workDir + "bootimg.json"), + UnifiedConfig::class.java).toArgs() + val imgArgs = readBack[0] as ImgArgs + val info = readBack[1] as ImgInfo + if (imgArgs.verifyType == ImgArgs.VerifyType.AVB) { + val sig = ObjectMapper().readValue( + Signer.mapToJson(info.signature as LinkedHashMap<*, *>), ImgInfo.AvbSignature::class.java) + ret[2] = sig + } else { + val sig2 = ObjectMapper().readValue( + Signer.mapToJson(info.signature as LinkedHashMap<*, *>), ImgInfo.VeritySignature::class.java) + ret[2] = sig2 + } + ret[0] = imgArgs + ret[1] = info + return ret + } } } diff --git a/bbootimg/src/main/kotlin/AVBInfo.kt b/bbootimg/src/main/kotlin/avb/AVBInfo.kt similarity index 98% rename from bbootimg/src/main/kotlin/AVBInfo.kt rename to bbootimg/src/main/kotlin/avb/AVBInfo.kt index 4010be0..8258da8 100755 --- a/bbootimg/src/main/kotlin/AVBInfo.kt +++ b/bbootimg/src/main/kotlin/avb/AVBInfo.kt @@ -1,7 +1,7 @@ -package cfig +package avb -import avb.* import avb.desc.* +import cfig.Helper /* a wonderfaul base64 encoder/decoder: https://cryptii.com/base64-to-hex diff --git a/bbootimg/src/main/kotlin/avb/Blob.kt b/bbootimg/src/main/kotlin/avb/Blob.kt index 88a9f25..177c3f6 100644 --- a/bbootimg/src/main/kotlin/avb/Blob.kt +++ b/bbootimg/src/main/kotlin/avb/Blob.kt @@ -12,6 +12,18 @@ import java.security.MessageDigest class Blob { companion object { + fun encodePubKey(alg: Algorithm): ByteArray { + var encodedKey = byteArrayOf() + if (alg.public_key_num_bytes > 0) { + encodedKey = Helper.encodeRSAkey(Files.readAllBytes((Paths.get(alg.defaultKey)))) + log.info("encodePubKey(): size = ${alg.public_key_num_bytes}, algorithm key size: ${encodedKey.size}") + Assert.assertEquals(alg.public_key_num_bytes, encodedKey.size) + } else { + log.info("encodePubKey(): No key to encode for algorithm " + alg.name) + } + return encodedKey + } + fun encodePubKey(alg: Algorithm, key: ByteArray): ByteArray { var encodedKey = byteArrayOf() if (alg.public_key_num_bytes > 0) { @@ -35,8 +47,7 @@ class Blob { fun getAuthBlob(header_data_blob: ByteArray, aux_data_blob: ByteArray, - algorithm_name: String, - key_path: String?): ByteArray { + algorithm_name: String): ByteArray { val alg = Algorithms.get(algorithm_name)!! val authBlockSize = Helper.round_to_multiple((alg.hash_num_bytes + alg.signature_num_bytes).toLong(), 64) if (authBlockSize == 0L) { @@ -53,7 +64,7 @@ class Blob { update(header_data_blob) update(aux_data_blob) }.digest() - binarySignature = Helper.rawSign(key_path!!, Helper.join(alg.padding, binaryHash)) + binarySignature = Helper.rawSign(alg.defaultKey.replace(".pem", ".pk8"), Helper.join(alg.padding, binaryHash)) } val authData = Helper.join(binaryHash, binarySignature) return Helper.join(authData, Struct("${authBlockSize - authData.size}x").pack(0)) diff --git a/bbootimg/src/main/kotlin/avb/Footer.kt b/bbootimg/src/main/kotlin/avb/Footer.kt index 45f0a75..15a8b6e 100644 --- a/bbootimg/src/main/kotlin/avb/Footer.kt +++ b/bbootimg/src/main/kotlin/avb/Footer.kt @@ -20,7 +20,7 @@ data class Footer constructor( private const val FORMAT_STRING = "!4s2L3Q${RESERVED}x" init { - Assert.assertEquals(SIZE, Struct(FORMAT_STRING).calcsize()) + Assert.assertEquals(SIZE, Struct(FORMAT_STRING).calcSize()) } } diff --git a/bbootimg/src/main/kotlin/avb/Header.kt b/bbootimg/src/main/kotlin/avb/Header.kt index d5ba7c7..bdeee84 100644 --- a/bbootimg/src/main/kotlin/avb/Header.kt +++ b/bbootimg/src/main/kotlin/avb/Header.kt @@ -84,7 +84,7 @@ data class Header( const val FORMAT_STRING = ("!4s2L2QL11QL${REVERSED0}x47sx" + "${REVERSED}x") init { - Assert.assertEquals(SIZE, Struct(FORMAT_STRING).calcsize()) + Assert.assertEquals(SIZE, Struct(FORMAT_STRING).calcSize()) } } } \ No newline at end of file diff --git a/bbootimg/src/main/kotlin/avb/alg/Algorithm.kt b/bbootimg/src/main/kotlin/avb/alg/Algorithm.kt index 0d9ff77..33dd92a 100644 --- a/bbootimg/src/main/kotlin/avb/alg/Algorithm.kt +++ b/bbootimg/src/main/kotlin/avb/alg/Algorithm.kt @@ -7,4 +7,5 @@ data class Algorithm( val hash_num_bytes: Int = 0, val signature_num_bytes: Int = 0, val public_key_num_bytes: Int = 0, - val padding: ByteArray = byteArrayOf()) \ No newline at end of file + val padding: ByteArray = byteArrayOf(), + val defaultKey: String ="") \ No newline at end of file diff --git a/bbootimg/src/main/kotlin/avb/alg/Algorithms.kt b/bbootimg/src/main/kotlin/avb/alg/Algorithms.kt index 8a22d37..e407336 100644 --- a/bbootimg/src/main/kotlin/avb/alg/Algorithms.kt +++ b/bbootimg/src/main/kotlin/avb/alg/Algorithms.kt @@ -34,7 +34,8 @@ class Algorithms { byteArrayOf(0x00), intArrayOf(0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, - 0x00, 0x04, 0x20))) + 0x00, 0x04, 0x20)), + defaultKey = "avb/avb_test_data/testkey_rsa2048.pem") val SHA256_RSA4096 = Algorithm( name = "SHA256_RSA4096", @@ -50,7 +51,8 @@ class Algorithms { intArrayOf(0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20) - ) + ), + defaultKey = "avb/avb_test_data/testkey_rsa4096.pem" ) val SHA256_RSA8192 = Algorithm( @@ -66,7 +68,8 @@ class Algorithms { 0x00, intArrayOf(0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, - 0x00, 0x04, 0x20))) + 0x00, 0x04, 0x20)), + defaultKey = "avb/avb_test_data/testkey_rsa8192.pem") val SHA512_RSA2048 = Algorithm( name = "SHA512_RSA2048", @@ -81,7 +84,8 @@ class Algorithms { 0x00, intArrayOf(0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, - 0x00, 0x04, 0x40))) + 0x00, 0x04, 0x40)), + defaultKey = "avb/avb_test_data/testkey_rsa2048.pem") val SHA512_RSA4096 = Algorithm( name = "SHA512_RSA4096", @@ -96,7 +100,8 @@ class Algorithms { 0x00, intArrayOf(0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, - 0x00, 0x04, 0x40))) + 0x00, 0x04, 0x40)), + defaultKey = "avb/avb_test_data/testkey_rsa4096.pem") val SHA512_RSA8192 = Algorithm( name = "SHA512_RSA8192", @@ -112,7 +117,8 @@ class Algorithms { 0x00, intArrayOf(0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, - 0x00, 0x04, 0x40))) + 0x00, 0x04, 0x40)), + defaultKey = "avb/avb_test_data/testkey_rsa8192.pem") algMap[NONE.name] = NONE diff --git a/bbootimg/src/main/kotlin/avb/desc/KernelCmdlineDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/KernelCmdlineDescriptor.kt index fe21340..bdd1894 100755 --- a/bbootimg/src/main/kotlin/avb/desc/KernelCmdlineDescriptor.kt +++ b/bbootimg/src/main/kotlin/avb/desc/KernelCmdlineDescriptor.kt @@ -45,7 +45,7 @@ class KernelCmdlineDescriptor( const val flagHashTreeDisabled = 2 init { - Assert.assertEquals(SIZE, Struct(FORMAT_STRING).calcsize()) + Assert.assertEquals(SIZE, Struct(FORMAT_STRING).calcSize()) } } } \ No newline at end of file diff --git a/bbootimg/src/main/kotlin/avb/desc/UnknownDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/UnknownDescriptor.kt index 984c2ab..34078ad 100755 --- a/bbootimg/src/main/kotlin/avb/desc/UnknownDescriptor.kt +++ b/bbootimg/src/main/kotlin/avb/desc/UnknownDescriptor.kt @@ -101,7 +101,7 @@ class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0, 0, } init { - Assert.assertEquals(SIZE, Struct(FORMAT).calcsize()) + Assert.assertEquals(SIZE, Struct(FORMAT).calcSize()) } } } diff --git a/bbootimg/src/main/resources/simplelogger.properties b/bbootimg/src/main/resources/simplelogger.properties new file mode 100644 index 0000000..bfc9e7f --- /dev/null +++ b/bbootimg/src/main/resources/simplelogger.properties @@ -0,0 +1 @@ +org.slf4j.simpleLogger.defaultLogLevel = info diff --git a/bbootimg/src/test/kotlin/HelperTest.kt b/bbootimg/src/test/kotlin/HelperTest.kt new file mode 100644 index 0000000..4f1665a --- /dev/null +++ b/bbootimg/src/test/kotlin/HelperTest.kt @@ -0,0 +1,78 @@ +import avb.alg.Algorithms +import cfig.Helper +import org.apache.commons.codec.binary.Hex +import org.bouncycastle.jce.provider.BouncyCastleProvider +import org.junit.Assert.* +import org.junit.Test +import java.security.KeyFactory +import java.security.KeyPairGenerator +import java.security.Security +import java.security.Signature +import java.security.spec.PKCS8EncodedKeySpec +import java.security.spec.X509EncodedKeySpec +import javax.crypto.Cipher + +class HelperTest { + @Test + fun rawSignTest() { + val data = Hex.decodeHex("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004206317a4c8d86accc8258c1ac23ef0ebd18bc33010d7afb43b241802646360b4ab") + val expectedSig = "28e17bc57406650ed78785fd558e7c1861cc4014c900d72b61c03cdbab1039e713b5bb19b556d04d276b46aae9b8a3999ccbac533a1cce00f83cfb83e2beb35ed7329f71ffec04fc2839a9b44e50abd66ea6c3d3bea6705e93e9139ecd0331170db18eba36a85a78bc49a5447260a30ed19d956cb2f8a71f6b19e57fdca43e052d1bb7840bf4c3efb47111f4d77764236d2e013fbf3b2577e4a3e01c9d166a5e890ef96210882e6e88ceca2fe3a2201f4961210d4ec6167f5dfd0e038e4a146f960caecab7d15ba65f6edcf5dbd25f5af543cfb8da4338bdbc872eec3f8e72aa8db679099e70952d3f7176c0b9111bf20ad1390eab1d09a859105816fdf92fbb" + val privkFile = "../" + Algorithms.get("SHA256_RSA2048")!!.defaultKey.replace("pem", "pk8") + val encData = Helper.rawSign(privkFile, data) + assertEquals(expectedSig, Hex.encodeHexString(encData)) + } + + @Test + fun rawSignOpenSslTest() { + val data = Hex.decodeHex("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004206317a4c8d86accc8258c1ac23ef0ebd18bc33010d7afb43b241802646360b4ab") + val expectedSig = "28e17bc57406650ed78785fd558e7c1861cc4014c900d72b61c03cdbab1039e713b5bb19b556d04d276b46aae9b8a3999ccbac533a1cce00f83cfb83e2beb35ed7329f71ffec04fc2839a9b44e50abd66ea6c3d3bea6705e93e9139ecd0331170db18eba36a85a78bc49a5447260a30ed19d956cb2f8a71f6b19e57fdca43e052d1bb7840bf4c3efb47111f4d77764236d2e013fbf3b2577e4a3e01c9d166a5e890ef96210882e6e88ceca2fe3a2201f4961210d4ec6167f5dfd0e038e4a146f960caecab7d15ba65f6edcf5dbd25f5af543cfb8da4338bdbc872eec3f8e72aa8db679099e70952d3f7176c0b9111bf20ad1390eab1d09a859105816fdf92fbb" + + val sig = Helper.rawSignOpenSsl("../" + Algorithms.get("SHA256_RSA2048")!!.defaultKey, data) + assertEquals(expectedSig, Hex.encodeHexString(sig)) + } + + @Test + fun test3() { + val data = Hex.decodeHex("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004206317a4c8d86accc8258c1ac23ef0ebd18bc3301033") + val signature = Signature.getInstance("NONEwithRSA") + val k = Helper.readPrivateKey("../" + Algorithms.get("SHA256_RSA2048")!!.defaultKey.replace("pem", "pk8")) + signature.initSign(k) + signature.update(data) + println("data size " + data.size) + println(signature.provider) + val sig = signature.sign() +// assertEquals(expectedSig, Hex.encodeHexString(sig)) + } + + @Test + fun testCipher() { + Security.addProvider(BouncyCastleProvider()) + for (p in Security.getProviders()) { + println(p.toString()) + for (entry in p.entries) { + println("\t" + entry.key.toString() + " -> " + entry.value) + } + println() + } + } + + @Test + fun testKeys() { + val kp = KeyPairGenerator.getInstance("rsa") + .apply { this.initialize(2048) } + .generateKeyPair() + val pk8Spec = PKCS8EncodedKeySpec(kp.private.encoded) //kp.private.format == PKCS#8 + val x509Spec = X509EncodedKeySpec(kp.public.encoded) //kp.public.format == X.509 + + val kf = KeyFactory.getInstance("rsa") + val privk = kf.generatePrivate(pk8Spec) + val pubk = kf.generatePublic(x509Spec) + + val cipher = Cipher.getInstance("RSA").apply { + this.init(Cipher.ENCRYPT_MODE, privk) + this.update("Good".toByteArray()) + } + val encryptedText = Hex.encodeHexString(cipher.doFinal()) + println(encryptedText) + } +} diff --git a/bbootimg/src/test/kotlin/cfig/io/StructTest.kt b/bbootimg/src/test/kotlin/cfig/io/StructTest.kt index 3f05f67..d545a37 100644 --- a/bbootimg/src/test/kotlin/cfig/io/StructTest.kt +++ b/bbootimg/src/test/kotlin/cfig/io/StructTest.kt @@ -1,7 +1,5 @@ -import cfig.Avb import cfig.Helper import cfig.io.Struct -import org.junit.Assert import org.junit.Test import org.junit.Assert.* @@ -10,11 +8,13 @@ import java.io.ByteArrayInputStream class StructTest { @Test fun constructTest() { - assertEquals(16, Struct("<2i4b4b").calcsize()) - assertEquals(16, Struct("h").calcsize()) - assertEquals(3, Struct(">3s").calcsize()) - assertEquals(4, Struct("!Hh").calcsize()) + assertEquals(16, Struct("<2i4b4b").calcSize()) + assertEquals(16, Struct("h").calcSize()) + assertEquals(3, Struct(">3s").calcSize()) + assertEquals(4, Struct("!Hh").calcSize()) + + Struct("<2i4b4b").dump() try { Struct("abcd")