diff options
author | Doug Zongker <dougz@android.com> | 2012-07-25 12:08:33 -0700 |
---|---|---|
committer | Doug Zongker <dougz@android.com> | 2012-07-25 13:09:17 -0700 |
commit | 35d9ad5ae72de846967b91aed97060f0e8558661 (patch) | |
tree | d94d48e75beabd007dbe2bf3001b933f02cac649 | |
parent | 237c80bf4cbc8e8fa3936ec8382b247aa4e96be2 (diff) | |
download | system_core-35d9ad5ae72de846967b91aed97060f0e8558661.zip system_core-35d9ad5ae72de846967b91aed97060f0e8558661.tar.gz system_core-35d9ad5ae72de846967b91aed97060f0e8558661.tar.bz2 |
support e=65537 for libmincrypt, DumpPublicKey
The output produced by DumpPublicKey now has a version tag on each
line (ie, each key). The existing keys are retroactively dubbed
"version 1", and we add a version 2 for 2048-bit e=65537 keys.
Change-Id: I204ec615c8f2346670220a1aeb99269e4abd5f81
-rw-r--r-- | include/mincrypt/rsa.h | 11 | ||||
-rw-r--r-- | libmincrypt/Android.mk | 4 | ||||
-rw-r--r-- | libmincrypt/rsa.c | 188 | ||||
-rw-r--r-- | libmincrypt/rsa_e_3.c | 202 | ||||
-rw-r--r-- | libmincrypt/rsa_e_f4.c | 196 | ||||
-rw-r--r-- | libmincrypt/tools/DumpPublicKey.java | 37 |
6 files changed, 457 insertions, 181 deletions
diff --git a/include/mincrypt/rsa.h b/include/mincrypt/rsa.h index 7d7d571..d7429fc 100644 --- a/include/mincrypt/rsa.h +++ b/include/mincrypt/rsa.h @@ -13,14 +13,14 @@ ** be used to endorse or promote products derived from this software ** without specific prior written permission. ** -** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO ** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ @@ -42,6 +42,7 @@ typedef struct RSAPublicKey { uint32_t n0inv; /* -1 / n[0] mod 2^32 */ uint32_t n[RSANUMWORDS]; /* modulus as little endian array */ uint32_t rr[RSANUMWORDS]; /* R^2 as little endian array */ + int exponent; /* 3 or 65537 */ } RSAPublicKey; int RSA_verify(const RSAPublicKey *key, diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk index b09a941..f58eab9 100644 --- a/libmincrypt/Android.mk +++ b/libmincrypt/Android.mk @@ -4,13 +4,13 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c sha.c +LOCAL_SRC_FILES := rsa.c rsa_e_3.c rsa_e_f4.c sha.c include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c sha.c +LOCAL_SRC_FILES := rsa.c rsa_e_3.c rsa_e_f4.c sha.c include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libmincrypt/rsa.c b/libmincrypt/rsa.c index d7124fb..b4ee6af 100644 --- a/libmincrypt/rsa.c +++ b/libmincrypt/rsa.c @@ -1,6 +1,6 @@ /* rsa.c ** -** Copyright 2008, The Android Open Source Project +** Copyright 2012, The Android Open Source Project ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are met: @@ -13,186 +13,42 @@ ** be used to endorse or promote products derived from this software ** without specific prior written permission. ** -** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO ** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "mincrypt/rsa.h" -#include "mincrypt/sha.h" -/* a[] -= mod */ -static void subM(const RSAPublicKey *key, uint32_t *a) { - int64_t A = 0; - int i; - for (i = 0; i < key->len; ++i) { - A += (uint64_t)a[i] - key->n[i]; - a[i] = (uint32_t)A; - A >>= 32; - } -} - -/* return a[] >= mod */ -static int geM(const RSAPublicKey *key, const uint32_t *a) { - int i; - for (i = key->len; i;) { - --i; - if (a[i] < key->n[i]) return 0; - if (a[i] > key->n[i]) return 1; - } - return 1; /* equal */ -} - -/* montgomery c[] += a * b[] / R % mod */ -static void montMulAdd(const RSAPublicKey *key, - uint32_t* c, - const uint32_t a, - const uint32_t* b) { - uint64_t A = (uint64_t)a * b[0] + c[0]; - uint32_t d0 = (uint32_t)A * key->n0inv; - uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; - int i; - - for (i = 1; i < key->len; ++i) { - A = (A >> 32) + (uint64_t)a * b[i] + c[i]; - B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; - c[i - 1] = (uint32_t)B; - } - - A = (A >> 32) + (B >> 32); - - c[i - 1] = (uint32_t)A; - - if (A >> 32) { - subM(key, c); - } -} - -/* montgomery c[] = a[] * b[] / R % mod */ -static void montMul(const RSAPublicKey *key, - uint32_t* c, - const uint32_t* a, - const uint32_t* b) { - int i; - for (i = 0; i < key->len; ++i) { - c[i] = 0; - } - for (i = 0; i < key->len; ++i) { - montMulAdd(key, c, a[i], b); - } -} - -/* In-place public exponentiation. -** Input and output big-endian byte array in inout. -*/ -static void modpow3(const RSAPublicKey *key, - uint8_t* inout) { - uint32_t a[RSANUMWORDS]; - uint32_t aR[RSANUMWORDS]; - uint32_t aaR[RSANUMWORDS]; - uint32_t *aaa = aR; /* Re-use location. */ - int i; - - /* Convert from big endian byte array to little endian word array. */ - for (i = 0; i < key->len; ++i) { - uint32_t tmp = - (inout[((key->len - 1 - i) * 4) + 0] << 24) | - (inout[((key->len - 1 - i) * 4) + 1] << 16) | - (inout[((key->len - 1 - i) * 4) + 2] << 8) | - (inout[((key->len - 1 - i) * 4) + 3] << 0); - a[i] = tmp; - } +int RSA_e_f4_verify(const RSAPublicKey* key, + const uint8_t* signature, + const int len, + const uint8_t* sha); - montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */ - montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */ - montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */ +int RSA_e_3_verify(const RSAPublicKey *key, + const uint8_t *signature, + const int len, + const uint8_t *sha); - /* Make sure aaa < mod; aaa is at most 1x mod too large. */ - if (geM(key, aaa)) { - subM(key, aaa); - } - - /* Convert to bigendian byte array */ - for (i = key->len - 1; i >= 0; --i) { - uint32_t tmp = aaa[i]; - *inout++ = tmp >> 24; - *inout++ = tmp >> 16; - *inout++ = tmp >> 8; - *inout++ = tmp >> 0; - } -} - -/* Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. -** Has the 0-length optional parameter encoded in the ASN1 (as opposed to the -** other flavor which omits the optional parameter entirely). This code does not -** accept signatures without the optional parameter. -*/ -static const uint8_t padding[RSANUMBYTES - SHA_DIGEST_SIZE] = { - 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, - 0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00, - 0x04,0x14 -}; - -/* Verify a 2048 bit RSA PKCS1.5 signature against an expected SHA-1 hash. -** Returns 0 on failure, 1 on success. -*/ int RSA_verify(const RSAPublicKey *key, const uint8_t *signature, const int len, const uint8_t *sha) { - uint8_t buf[RSANUMBYTES]; - int i; - - if (key->len != RSANUMWORDS) { - return 0; /* Wrong key passed in. */ - } - - if (len != sizeof(buf)) { - return 0; /* Wrong input length. */ - } - - for (i = 0; i < len; ++i) { - buf[i] = signature[i]; - } - - modpow3(key, buf); - - /* Check pkcs1.5 padding bytes. */ - for (i = 0; i < (int) sizeof(padding); ++i) { - if (buf[i] != padding[i]) { + switch (key->exponent) { + case 3: + return RSA_e_3_verify(key, signature, len, sha); + break; + case 65537: + return RSA_e_f4_verify(key, signature, len, sha); + break; + default: return 0; - } } - - /* Check sha digest matches. */ - for (; i < len; ++i) { - if (buf[i] != *sha++) { - return 0; - } - } - - return 1; } diff --git a/libmincrypt/rsa_e_3.c b/libmincrypt/rsa_e_3.c new file mode 100644 index 0000000..c8c02c4 --- /dev/null +++ b/libmincrypt/rsa_e_3.c @@ -0,0 +1,202 @@ +/* rsa_e_3.c +** +** Copyright 2008, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" + +/* a[] -= mod */ +static void subM(const RSAPublicKey *key, uint32_t *a) { + int64_t A = 0; + int i; + for (i = 0; i < key->len; ++i) { + A += (uint64_t)a[i] - key->n[i]; + a[i] = (uint32_t)A; + A >>= 32; + } +} + +/* return a[] >= mod */ +static int geM(const RSAPublicKey *key, const uint32_t *a) { + int i; + for (i = key->len; i;) { + --i; + if (a[i] < key->n[i]) return 0; + if (a[i] > key->n[i]) return 1; + } + return 1; /* equal */ +} + +/* montgomery c[] += a * b[] / R % mod */ +static void montMulAdd(const RSAPublicKey *key, + uint32_t* c, + const uint32_t a, + const uint32_t* b) { + uint64_t A = (uint64_t)a * b[0] + c[0]; + uint32_t d0 = (uint32_t)A * key->n0inv; + uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; + int i; + + for (i = 1; i < key->len; ++i) { + A = (A >> 32) + (uint64_t)a * b[i] + c[i]; + B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; + c[i - 1] = (uint32_t)B; + } + + A = (A >> 32) + (B >> 32); + + c[i - 1] = (uint32_t)A; + + if (A >> 32) { + subM(key, c); + } +} + +/* montgomery c[] = a[] * b[] / R % mod */ +static void montMul(const RSAPublicKey *key, + uint32_t* c, + const uint32_t* a, + const uint32_t* b) { + int i; + for (i = 0; i < key->len; ++i) { + c[i] = 0; + } + for (i = 0; i < key->len; ++i) { + montMulAdd(key, c, a[i], b); + } +} + +/* In-place public exponentiation. +** Input and output big-endian byte array in inout. +*/ +static void modpow3(const RSAPublicKey *key, + uint8_t* inout) { + uint32_t a[RSANUMWORDS]; + uint32_t aR[RSANUMWORDS]; + uint32_t aaR[RSANUMWORDS]; + uint32_t *aaa = aR; /* Re-use location. */ + int i; + + /* Convert from big endian byte array to little endian word array. */ + for (i = 0; i < key->len; ++i) { + uint32_t tmp = + (inout[((key->len - 1 - i) * 4) + 0] << 24) | + (inout[((key->len - 1 - i) * 4) + 1] << 16) | + (inout[((key->len - 1 - i) * 4) + 2] << 8) | + (inout[((key->len - 1 - i) * 4) + 3] << 0); + a[i] = tmp; + } + + montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */ + montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */ + montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */ + + /* Make sure aaa < mod; aaa is at most 1x mod too large. */ + if (geM(key, aaa)) { + subM(key, aaa); + } + + /* Convert to bigendian byte array */ + for (i = key->len - 1; i >= 0; --i) { + uint32_t tmp = aaa[i]; + *inout++ = tmp >> 24; + *inout++ = tmp >> 16; + *inout++ = tmp >> 8; + *inout++ = tmp >> 0; + } +} + +/* Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. +** Has the 0-length optional parameter encoded in the ASN1 (as opposed to the +** other flavor which omits the optional parameter entirely). This code does not +** accept signatures without the optional parameter. +*/ +static const uint8_t padding[RSANUMBYTES - SHA_DIGEST_SIZE] = { + 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, + 0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00, + 0x04,0x14 +}; + +/* Verify a 2048 bit RSA e=3 PKCS1.5 signature against an expected SHA-1 hash. +** Returns 0 on failure, 1 on success. +*/ +int RSA_e_3_verify(const RSAPublicKey *key, + const uint8_t *signature, + const int len, + const uint8_t *sha) { + uint8_t buf[RSANUMBYTES]; + int i; + + if (key->len != RSANUMWORDS) { + return 0; /* Wrong key passed in. */ + } + + if (len != sizeof(buf)) { + return 0; /* Wrong input length. */ + } + + if (key->exponent != 3) { + return 0; // Wrong exponent. + } + + for (i = 0; i < len; ++i) { + buf[i] = signature[i]; + } + + modpow3(key, buf); + + /* Check pkcs1.5 padding bytes. */ + for (i = 0; i < (int) sizeof(padding); ++i) { + if (buf[i] != padding[i]) { + return 0; + } + } + + /* Check sha digest matches. */ + for (; i < len; ++i) { + if (buf[i] != *sha++) { + return 0; + } + } + + return 1; +} diff --git a/libmincrypt/rsa_e_f4.c b/libmincrypt/rsa_e_f4.c new file mode 100644 index 0000000..6701bcc --- /dev/null +++ b/libmincrypt/rsa_e_f4.c @@ -0,0 +1,196 @@ +/* rsa_e_f4.c +** +** Copyright 2012, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" + +// a[] -= mod +static void subM(const RSAPublicKey* key, + uint32_t* a) { + int64_t A = 0; + int i; + for (i = 0; i < key->len; ++i) { + A += (uint64_t)a[i] - key->n[i]; + a[i] = (uint32_t)A; + A >>= 32; + } +} + +// return a[] >= mod +static int geM(const RSAPublicKey* key, + const uint32_t* a) { + int i; + for (i = key->len; i;) { + --i; + if (a[i] < key->n[i]) return 0; + if (a[i] > key->n[i]) return 1; + } + return 1; // equal +} + +// montgomery c[] += a * b[] / R % mod +static void montMulAdd(const RSAPublicKey* key, + uint32_t* c, + const uint32_t a, + const uint32_t* b) { + uint64_t A = (uint64_t)a * b[0] + c[0]; + uint32_t d0 = (uint32_t)A * key->n0inv; + uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; + int i; + + for (i = 1; i < key->len; ++i) { + A = (A >> 32) + (uint64_t)a * b[i] + c[i]; + B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; + c[i - 1] = (uint32_t)B; + } + + A = (A >> 32) + (B >> 32); + + c[i - 1] = (uint32_t)A; + + if (A >> 32) { + subM(key, c); + } +} + +// montgomery c[] = a[] * b[] / R % mod +static void montMul(const RSAPublicKey* key, + uint32_t* c, + const uint32_t* a, + const uint32_t* b) { + int i; + for (i = 0; i < key->len; ++i) { + c[i] = 0; + } + for (i = 0; i < key->len; ++i) { + montMulAdd(key, c, a[i], b); + } +} + +// In-place public exponentiation. +// Input and output big-endian byte array in inout. +static void modpowF4(const RSAPublicKey* key, + uint8_t* inout) { + uint32_t a[RSANUMWORDS]; + uint32_t aR[RSANUMWORDS]; + uint32_t aaR[RSANUMWORDS]; + uint32_t* aaa = aaR; // Re-use location. + int i; + + // Convert from big endian byte array to little endian word array. + for (i = 0; i < key->len; ++i) { + uint32_t tmp = + (inout[((key->len - 1 - i) * 4) + 0] << 24) | + (inout[((key->len - 1 - i) * 4) + 1] << 16) | + (inout[((key->len - 1 - i) * 4) + 2] << 8) | + (inout[((key->len - 1 - i) * 4) + 3] << 0); + a[i] = tmp; + } + + montMul(key, aR, a, key->rr); // aR = a * RR / R mod M + for (i = 0; i < 16; i += 2) { + montMul(key, aaR, aR, aR); // aaR = aR * aR / R mod M + montMul(key, aR, aaR, aaR); // aR = aaR * aaR / R mod M + } + montMul(key, aaa, aR, a); // aaa = aR * a / R mod M + + // Make sure aaa < mod; aaa is at most 1x mod too large. + if (geM(key, aaa)) { + subM(key, aaa); + } + + // Convert to bigendian byte array + for (i = key->len - 1; i >= 0; --i) { + uint32_t tmp = aaa[i]; + *inout++ = tmp >> 24; + *inout++ = tmp >> 16; + *inout++ = tmp >> 8; + *inout++ = tmp >> 0; + } +} + +// Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. +// Has the 0-length optional parameter encoded in the ASN1 (as opposed to the +// other flavor which omits the optional parameter entirely). This code does not +// accept signatures without the optional parameter. +/* +static const uint8_t padding[RSANUMBYTES] = { +0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; +*/ + +// SHA-1 of PKCS1.5 signature padding for 2048 bit, as above. +// At the location of the bytes of the hash all 00 are hashed. +static const uint8_t kExpectedPadShaRsa2048[SHA_DIGEST_SIZE] = { + 0xdc, 0xbd, 0xbe, 0x42, 0xd5, 0xf5, 0xa7, 0x2e, 0x6e, 0xfc, + 0xf5, 0x5d, 0xaf, 0x9d, 0xea, 0x68, 0x7c, 0xfb, 0xf1, 0x67 +}; + +// Verify a 2048 bit RSA e=65537 PKCS1.5 signature against an expected +// SHA-1 hash. Returns 0 on failure, 1 on success. +int RSA_e_f4_verify(const RSAPublicKey* key, + const uint8_t* signature, + const int len, + const uint8_t* sha) { + uint8_t buf[RSANUMBYTES]; + int i; + + if (key->len != RSANUMWORDS) { + return 0; // Wrong key passed in. + } + + if (len != sizeof(buf)) { + return 0; // Wrong input length. + } + + if (key->exponent != 65537) { + return 0; // Wrong exponent. + } + + for (i = 0; i < len; ++i) { // Copy input to local workspace. + buf[i] = signature[i]; + } + + modpowF4(key, buf); // In-place exponentiation. + + // Xor sha portion, so it all becomes 00 iff equal. + for (i = len - SHA_DIGEST_SIZE; i < len; ++i) { + buf[i] ^= *sha++; + } + + // Hash resulting buf, in-place. + SHA(buf, len, buf); + + // Compare against expected hash value. + for (i = 0; i < SHA_DIGEST_SIZE; ++i) { + if (buf[i] != kExpectedPadShaRsa2048[i]) { + return 0; + } + } + + return 1; // All checked out OK. +} diff --git a/libmincrypt/tools/DumpPublicKey.java b/libmincrypt/tools/DumpPublicKey.java index d2935e0..b83a757 100644 --- a/libmincrypt/tools/DumpPublicKey.java +++ b/libmincrypt/tools/DumpPublicKey.java @@ -34,27 +34,42 @@ import sun.misc.BASE64Encoder; class DumpPublicKey { /** * @param key to perform sanity checks on + * @return version number of key. Supported versions are: + * 1: 2048-bit key with e=3 + * 2: 2048-bit key with e=65537 * @throws Exception if the key has the wrong size or public exponent + */ - static void check(RSAPublicKey key) throws Exception { + static int check(RSAPublicKey key) throws Exception { BigInteger pubexp = key.getPublicExponent(); BigInteger modulus = key.getModulus(); + int version; + + if (pubexp.equals(BigInteger.valueOf(3))) { + version = 1; + } else if (pubexp.equals(BigInteger.valueOf(65537))) { + version = 2; + } else { + throw new Exception("Public exponent should be 3 or 65537 but is " + + pubexp.toString(10) + "."); + } - if (!pubexp.equals(BigInteger.valueOf(3))) - throw new Exception("Public exponent should be 3 but is " + - pubexp.toString(10) + "."); - - if (modulus.bitLength() != 2048) + if (modulus.bitLength() != 2048) { throw new Exception("Modulus should be 2048 bits long but is " + modulus.bitLength() + " bits."); + } + + return version; } /** * @param key to output - * @return a C initializer representing this public key. + * @return a String representing this public key. If the key is a + * version 1 key, the string will be a C initializer; this is + * not true for newer key versions. */ static String print(RSAPublicKey key) throws Exception { - check(key); + int version = check(key); BigInteger N = key.getModulus(); @@ -62,6 +77,12 @@ class DumpPublicKey { int nwords = N.bitLength() / 32; // # of 32 bit integers in modulus + if (version > 1) { + result.append("v"); + result.append(Integer.toString(version)); + result.append(" "); + } + result.append("{"); result.append(nwords); |