Skip to content

Commit 20f8cf7

Browse files
committed
Increase v3 test coverage.
1 parent 5dcd56e commit 20f8cf7

4 files changed

Lines changed: 908 additions & 83 deletions

File tree

paseto/src/main/kotlin/net/aholbrook/paseto/crypto/Ecdsa.kt

Lines changed: 81 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import org.bouncycastle.crypto.signers.ECDSASigner
2323
import org.bouncycastle.crypto.signers.HMacDSAKCalculator
2424
import org.bouncycastle.crypto.util.PrivateKeyFactory
2525
import org.bouncycastle.crypto.util.PublicKeyFactory
26+
import org.bouncycastle.math.ec.ECPoint
2627
import org.bouncycastle.util.BigIntegers
2728
import java.math.BigInteger
2829

@@ -34,9 +35,7 @@ internal fun ecdsaP384Sign(m: ByteArray, sk: ByteArray, enforceLowS: Boolean = f
3435
if (m.isEmpty()) {
3536
throw ByteArrayLengthException("m", 0, 1, false)
3637
}
37-
if (sk.size !=
38-
ECDSA_P384_SECRETKEYBYTES
39-
) {
38+
if (sk.size != ECDSA_P384_SECRETKEYBYTES) {
4039
throw ByteArrayLengthException("sk", sk.size, ECDSA_P384_SECRETKEYBYTES)
4140
}
4241

@@ -59,9 +58,20 @@ internal fun ecdsaP384Sign(m: ByteArray, sk: ByteArray, enforceLowS: Boolean = f
5958

6059
val components = signer.generateSignature(hash)
6160
val r = components[0]
62-
var s = components[1]
63-
if (enforceLowS && s > params.n shr 1) {
64-
s = params.n - s
61+
val s = components[1]
62+
63+
return ecdsaFormatSignature(r, s, params.n, enforceLowS)
64+
}
65+
66+
internal fun ecdsaFormatSignature(
67+
r: BigInteger,
68+
s: BigInteger,
69+
n: BigInteger,
70+
enforceLowS: Boolean = false
71+
): ByteArray {
72+
var s = s
73+
if (enforceLowS && s > n shr 1) {
74+
s = n - s
6575
}
6676

6777
return BigIntegers.asUnsignedByteArray(ECDSA_P384_BYTES / 2, r) +
@@ -120,31 +130,29 @@ internal fun p384SkToPk(sk: ByteArray): ByteArray {
120130
return q.getEncoded(true)
121131
}
122132

123-
internal fun p384VerifyPk(pk: ByteArray) {
133+
internal fun p384VerifyPk(pk: ByteArray): ECPoint {
124134
if (pk.size != ECDSA_P384_PUBLICKEYBYTES) {
125135
throw ByteArrayLengthException("pk", pk.size, ECDSA_P384_PUBLICKEYBYTES)
126136
}
127137
if (pk[0] != 0x02.toByte() && pk[0] != 0x03.toByte()) {
128138
throw KeyV3Exception("must use point compression")
129139
}
130140

131-
try {
132-
val params = CustomNamedCurves.getByOID(SECObjectIdentifiers.secp384r1)
133-
val q = try {
134-
params.curve.decodePoint(pk)
135-
} catch (ex: IllegalArgumentException) {
136-
throw KeyV3Exception("decode point", ex)
137-
}
141+
val params = CustomNamedCurves.getByOID(SECObjectIdentifiers.secp384r1)
142+
val q = try {
143+
params.curve.decodePoint(pk)
144+
} catch (ex: IllegalArgumentException) {
145+
throw KeyV3Exception("decode point", ex)
146+
}
138147

139-
if (q.isInfinity) {
140-
throw KeyV3Exception("Point at infinity")
141-
}
142-
if (!q.isValid) {
143-
throw KeyV3Exception("Point not on curve")
144-
}
145-
} catch (e: IllegalArgumentException) {
146-
throw KeyV3Exception(e.message ?: "", e)
148+
if (q.isInfinity) {
149+
throw KeyV3Exception("Point at infinity")
147150
}
151+
if (!q.isValid) {
152+
throw KeyV3Exception("Point not on curve")
153+
}
154+
155+
return q
148156
}
149157

150158
internal fun p384EncodeSkPkcs8(sk: ByteArray): ByteArray {
@@ -170,20 +178,24 @@ internal fun p384EncodeSkPkcs8(sk: ByteArray): ByteArray {
170178
}
171179

172180
internal fun p384DecodeSkPkcs8(der: ByteArray): ByteArray {
173-
val params = PrivateKeyFactory.createKey(der) as? ECPrivateKeyParameters
174-
?: throw KeyV3Exception("Private key is not on secp384r1")
175-
val domain = params.parameters
176-
val expected = CustomNamedCurves.getByOID(SECObjectIdentifiers.secp384r1)
181+
try {
182+
val params = PrivateKeyFactory.createKey(der) as? ECPrivateKeyParameters
183+
?: throw KeyV3Exception("Private key is not on secp384r1")
184+
val domain = params.parameters
185+
val expected = CustomNamedCurves.getByOID(SECObjectIdentifiers.secp384r1)
177186

178-
if (domain.curve != expected.curve || domain.g != expected.g || domain.n != expected.n || domain.h != expected.h) {
179-
throw KeyV3Exception("Private key is not on secp384r1")
180-
}
187+
if (domain.curve != expected.curve || domain.g != expected.g || domain.n != expected.n || domain.h != expected.h) {
188+
throw KeyV3Exception("Private key is not on secp384r1")
189+
}
181190

182-
if (params.d.signum() <= 0 || params.d >= domain.n) {
183-
throw KeyV3Exception("Invalid private key")
184-
}
191+
if (params.d.signum() <= 0 || params.d >= domain.n) {
192+
throw KeyV3Exception("Invalid private key")
193+
}
185194

186-
return BigIntegers.asUnsignedByteArray(ECDSA_P384_SECRETKEYBYTES, params.d)
195+
return BigIntegers.asUnsignedByteArray(ECDSA_P384_SECRETKEYBYTES, params.d)
196+
} catch (ex: Throwable) {
197+
throw KeyV3Exception("invalid private key", ex)
198+
}
187199
}
188200

189201
internal fun p384EncodeSkSec1(sk: ByteArray): ByteArray {
@@ -203,70 +215,62 @@ internal fun p384EncodeSkSec1(sk: ByteArray): ByteArray {
203215
}
204216

205217
internal fun p384DecodeSkSec1(der: ByteArray): ByteArray {
206-
val key = ECPrivateKey.getInstance(ASN1Primitive.fromByteArray(der))
207-
val curveParams = CustomNamedCurves.getByOID(SECObjectIdentifiers.secp384r1)
218+
try {
219+
val key = ECPrivateKey.getInstance(ASN1Primitive.fromByteArray(der))
220+
?: throw KeyV3Exception("Invalid private key")
221+
val curveParams = CustomNamedCurves.getByOID(SECObjectIdentifiers.secp384r1)
222+
223+
key.parametersObject?.let { params ->
224+
when (params) {
225+
is ASN1ObjectIdentifier -> {
226+
if (params != SECObjectIdentifiers.secp384r1) {
227+
throw KeyV3Exception("SEC1 key not on secp384r1")
228+
}
229+
}
208230

209-
key.parametersObject?.let { params ->
210-
when (params) {
211-
is ASN1ObjectIdentifier -> {
212-
if (params != SECObjectIdentifiers.secp384r1) {
213-
throw KeyV3Exception("SEC1 key not on secp384r1")
231+
else -> {
232+
throw KeyV3Exception("Unsupported curve parameters")
214233
}
215234
}
235+
} ?: throw KeyV3Exception("SEC1 key must include curve parameters")
216236

217-
else -> {
218-
throw KeyV3Exception("Unsupported curve parameters")
219-
}
237+
val d = key.key
238+
if (d.signum() <= 0 || d >= curveParams.n) {
239+
throw KeyV3Exception("Invalid P-384 private key")
220240
}
221-
} ?: throw KeyV3Exception("SEC1 key must include curve parameters")
222241

223-
val d = key.key
224-
if (d.signum() <= 0 || d >= curveParams.n) {
225-
throw KeyV3Exception("Invalid P-384 private key")
242+
return BigIntegers.asUnsignedByteArray(ECDSA_P384_SECRETKEYBYTES, d)
243+
} catch (ex: Throwable) {
244+
throw KeyV3Exception("Invalid private key", ex)
226245
}
227-
228-
return BigIntegers.asUnsignedByteArray(ECDSA_P384_SECRETKEYBYTES, d)
229246
}
230247

231248
internal fun p384EncodePkSpki(pk: ByteArray): ByteArray {
232-
val params = CustomNamedCurves.getByOID(SECObjectIdentifiers.secp384r1)
233-
val point = try {
234-
params.curve.decodePoint(pk)
235-
} catch (ex: IllegalArgumentException) {
236-
throw KeyV3Exception("decode point", ex)
237-
}
238-
239-
if (point.isInfinity) {
240-
throw KeyV3Exception("Point at infinity")
241-
}
242-
if (!point.isValid) {
243-
throw KeyV3Exception("Point not on curve")
244-
}
249+
val q = p384VerifyPk(pk)
245250

246251
return SubjectPublicKeyInfo(
247252
AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SECObjectIdentifiers.secp384r1),
248-
point.getEncoded(true),
253+
q.getEncoded(true),
249254
).encoded
250255
}
251256

252257
internal fun p384DecodePkSpki(der: ByteArray): ByteArray {
253-
val params = PublicKeyFactory.createKey(der) as? ECPublicKeyParameters
254-
?: throw KeyV3Exception("Public key is not on secp384r1")
255-
val domain = params.parameters
256-
val expected = CustomNamedCurves.getByOID(SECObjectIdentifiers.secp384r1)
258+
try {
259+
val params = PublicKeyFactory.createKey(der) as? ECPublicKeyParameters
260+
?: throw KeyV3Exception("Public key is not on secp384r1")
261+
val domain = params.parameters
262+
val expected = CustomNamedCurves.getByOID(SECObjectIdentifiers.secp384r1)
257263

258-
if (domain.curve != expected.curve || domain.g != expected.g || domain.n != expected.n || domain.h != expected.h) {
259-
throw KeyV3Exception("Public key is not on secp384r1")
260-
}
264+
if (domain.curve!=expected.curve || domain.g!=expected.g || domain.n!=expected.n || domain.h!=expected.h) {
265+
throw KeyV3Exception("Public key is not on secp384r1")
266+
}
261267

262-
if (params.q.isInfinity) {
263-
throw KeyV3Exception("Point at infinity")
264-
}
265-
if (!params.q.isValid) {
266-
throw KeyV3Exception("Point not on curve")
268+
return params.q.getEncoded(true).also {
269+
p384VerifyPk(it)
270+
}
271+
} catch (ex: Throwable) {
272+
throw KeyV3Exception("invalid public key", ex)
267273
}
268-
269-
return params.q.getEncoded(true)
270274
}
271275

272276
internal fun p384Generate(): Pair<ByteArray, ByteArray> {

paseto/src/test/kotlin/net/aholbrook/paseto/TestHelpers.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@ private val keyV1Local_ by lazy { SymmetricKey.generate(Version.V1) }
1010
private val keyV1Public_ by lazy { KeyPair.generate(Version.V1) }
1111
private val keyV2Local_ by lazy { SymmetricKey.generate(Version.V2) }
1212
private val keyV2Public_ by lazy { KeyPair.generate(Version.V2) }
13+
private val keyV3Local_ by lazy { SymmetricKey.generate(Version.V3) }
14+
private val keyV3Public_ by lazy { KeyPair.generate(Version.V3) }
1315
private val keyV4Local_ by lazy { SymmetricKey.generate(Version.V4) }
1416
private val keyV4Public_ by lazy { KeyPair.generate(Version.V4) }
1517

1618
val keyV1Local: SymmetricKey get() = keyV1Local_.copy()
1719
val keyV1Public: KeyPair get() = keyV1Public_.copy()
1820
val keyV2Local: SymmetricKey get() = keyV2Local_.copy()
1921
val keyV2Public: KeyPair get() = keyV2Public_.copy()
22+
val keyV3Local: SymmetricKey get() = keyV3Local_.copy()
23+
val keyV3Public: KeyPair get() = keyV3Public_.copy()
2024
val keyV4Local: SymmetricKey get() = keyV4Local_.copy()
2125
val keyV4Public: KeyPair get() = keyV4Public_.copy()
2226

0 commit comments

Comments
 (0)