use javax.crypto.Cipher for raw signing

- using Cipher "RSA/ECB/NoPadding" to do raw sign
 - also add .gitignore
pull/20/head
cfig 7 years ago
parent 02be1f4b9e
commit ec00936463
No known key found for this signature in database
GPG Key ID: B104C307F0FDABB7

2
.gitignore vendored

@ -0,0 +1,2 @@
.idea
.gradle

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

@ -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<Object[]> formats = new ArrayList<>();
private ByteOrder byteOrder = ByteOrder.LITTLE_ENDIAN;
private List<Object[]> 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 {
}
}

@ -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<Descriptor>,
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
}

@ -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")
}
}

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

@ -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<String>) {
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,26 +28,24 @@ fun main(args: Array<String>) {
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 (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" -> {
Packer().pack(mkbootimgBin = args[2], mkbootfsBin = args[5])
}
"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<String>) {
}
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
}

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

@ -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<Any?> {
var ret: Array<Any?> = 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
}
}
}

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

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

@ -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())
}
}

@ -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())
}
}
}

@ -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())
val padding: ByteArray = byteArrayOf(),
val defaultKey: String ="")

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

@ -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())
}
}
}

@ -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())
}
}
}

@ -0,0 +1 @@
org.slf4j.simpleLogger.defaultLogLevel = info

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

@ -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("<Q8b").calcsize())
assertEquals(2, Struct(">h").calcsize())
assertEquals(3, Struct(">3s").calcsize())
assertEquals(4, Struct("!Hh").calcsize())
assertEquals(16, Struct("<2i4b4b").calcSize())
assertEquals(16, Struct("<Q8b").calcSize())
assertEquals(2, Struct(">h").calcSize())
assertEquals(3, Struct(">3s").calcSize())
assertEquals(4, Struct("!Hh").calcSize())
Struct("<2i4b4b").dump()
try {
Struct("abcd")

Loading…
Cancel
Save