Skip to content

Commit 35f21fe

Browse files
committed
cleanup RSArsaOrHmacMess
1 parent b8ad36e commit 35f21fe

File tree

46 files changed

+732
-430
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+732
-430
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464

6565

6666

67+
6768
### 3.14.0 (Supreme 0.7.0)
6869

6970
* Certificate Improvements:

docs/docs/indispensable-asn1.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ An alternative exists, taking a `Tag` instead of an `Ulong`. in both cases a tag
139139
the content of the ASN.1 primitive. Moreover, a non-throwing `decodeOrNull` variant is present.
140140
In addition, the following self-describing shorthands are defined:
141141

142-
| Function | Description |
142+
| Function | Descrption |
143143
|---------------------------------------------|-------------------------------------------------------------------------------------|
144144
| `Asn1Primitive.decodeToBoolean()` | throws |
145145
| `Asn1Primitive.decodeToBooleanOrNull()` | returns `null` on error |

docs/docs/indispensable.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ The following functions provide interop functionality with platform types.
148148
* `CryptoSignature.jcaSignatureBytes` returns the JCA-native encoded representation of a signature
149149
* `CryptoSignature.parseFromJca()` returns a signature object form a JCA-native encoded representation of a signature
150150
* `CryptoSignature.EC.parseFromJca()` returns an EC signature object form a JCA-native encoded representation of a signature
151-
* `CryptoSignature.RSAorHMAC.parseFromJca()` returns an RSA signature object form a JCA-native encoded representation of a signature
151+
* `CryptoSignature.RSA.parseFromJca()` returns an RSA signature object form a JCA-native encoded representation of a signature
152152
* `CryptoSignature.EC.parseFromJcaP1363` parses a signature produced by the JCA digestWithECDSAinP1363Format algorithm.
153153
* `X509Certificate.toJcaCertificate()` converts the certificate to a JCA-native `X509Certificate`
154154
* `java.security.cert.X509Certificate.toKmpCertificate()` converts a JCA-native certificate to a Signum `X509Certificate`
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
@file:Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
2+
13
package at.asitplus.signum.indispensable.cosef
24

5+
import at.asitplus.KmmResult
36
import at.asitplus.catching
4-
import at.asitplus.signum.indispensable.Digest
5-
import at.asitplus.signum.indispensable.ECCurve
6-
import at.asitplus.signum.indispensable.RSAPadding
7-
import at.asitplus.signum.indispensable.SignatureAlgorithm
8-
import at.asitplus.signum.indispensable.SpecializedSignatureAlgorithm
7+
import at.asitplus.signum.indispensable.*
8+
import at.asitplus.signum.indispensable.mac.HMAC
9+
import at.asitplus.signum.indispensable.mac.MessageAuthenticationCode
10+
import at.asitplus.signum.indispensable.misc.BitLength
11+
import at.asitplus.signum.indispensable.misc.bit
12+
import at.asitplus.signum.indispensable.symmetric.SymmetricEncryptionAlgorithm
913
import kotlinx.serialization.KSerializer
1014
import kotlinx.serialization.Serializable
1115
import kotlinx.serialization.descriptors.PrimitiveKind
@@ -18,47 +22,151 @@ import kotlinx.serialization.encoding.Encoder
1822
* See [COSE Algorithm Registry](https://www.iana.org/assignments/cose/cose.xhtml)
1923
*/
2024
@Serializable(with = CoseAlgorithmSerializer::class)
21-
enum class CoseAlgorithm(val value: Int): SpecializedSignatureAlgorithm {
22-
23-
// ECDSA with SHA-size
24-
ES256(-7),
25-
ES384(-35),
26-
ES512(-36),
27-
28-
// HMAC-size with SHA-size
29-
HS256(5),
30-
HS384(6),
31-
HS512(7),
32-
33-
// RSASSA-PSS with SHA-size
34-
PS256(-37),
35-
PS384(-38),
36-
PS512(-39),
37-
38-
// RSASSA-PKCS1-v1_5 with SHA-size
39-
RS256(-257),
40-
RS384(-258),
41-
RS512(-259),
42-
43-
// RSASSA-PKCS1-v1_5 using SHA-1
44-
RS1(-65535);
45-
46-
val digest: Digest get() = when(this) {
47-
RS1 -> Digest.SHA1
48-
ES256, HS256, PS256, RS256 -> Digest.SHA256
49-
ES384, HS384, PS384, RS384 -> Digest.SHA384
50-
ES512, HS512, PS512, RS512 -> Digest.SHA512
25+
sealed interface CoseAlgorithm {
26+
27+
sealed interface Symmetric : CoseAlgorithm {
28+
companion object {
29+
val entries: Collection<Symmetric> = MAC.entries + SymmetricEncryption.entries
30+
}
31+
}
32+
33+
val value: Int
34+
35+
@Serializable(with = CoseAlgorithmSerializer::class)
36+
sealed class DataIntegrity<D : DataIntegrityAlgorithm>(override val value: Int, val algorithm: D) : CoseAlgorithm {
37+
companion object {
38+
val entries: Collection<DataIntegrity<*>> = Signature.entries + MAC.entries
39+
}
40+
}
41+
42+
@Serializable(with = CoseAlgorithmSerializer::class)
43+
sealed class SymmetricEncryption(override val value: Int, val algorithm: SymmetricEncryptionAlgorithm<*, *, *>) :
44+
CoseAlgorithm.Symmetric {
45+
46+
47+
@Serializable(with = CoseAlgorithmSerializer::class)
48+
object A128GCM : SymmetricEncryption(1, SymmetricEncryptionAlgorithm.AES_128.GCM)
49+
50+
@Serializable(with = CoseAlgorithmSerializer::class)
51+
object A192GCM : SymmetricEncryption(2, SymmetricEncryptionAlgorithm.AES_192.GCM)
52+
53+
@Serializable(with = CoseAlgorithmSerializer::class)
54+
object A256GCM : SymmetricEncryption(3, SymmetricEncryptionAlgorithm.AES_256.GCM)
55+
56+
57+
@Serializable(with = CoseAlgorithmSerializer::class)
58+
object ChaCha20Poly1305 : SymmetricEncryption(24, SymmetricEncryptionAlgorithm.ChaCha20Poly1305)
59+
60+
companion object {
61+
val entries: Collection<SymmetricEncryption> = listOf(A128GCM, A192GCM, A256GCM, ChaCha20Poly1305)
62+
}
63+
}
64+
65+
66+
@Serializable(with = CoseAlgorithmSerializer::class)
67+
sealed class Signature(value: Int, algorithm: SignatureAlgorithm) :
68+
DataIntegrity<SignatureAlgorithm>(value, algorithm),
69+
SpecializedSignatureAlgorithm {
70+
71+
// ECDSA with SHA-size
72+
@Serializable(with = CoseAlgorithmSerializer::class)
73+
object ES256 : Signature(-7, SignatureAlgorithm.ECDSAwithSHA256)
74+
75+
@Serializable(with = CoseAlgorithmSerializer::class)
76+
object ES384 : Signature(-35, SignatureAlgorithm.ECDSAwithSHA384)
77+
78+
@Serializable(with = CoseAlgorithmSerializer::class)
79+
object ES512 : Signature(-36, SignatureAlgorithm.ECDSAwithSHA512)
80+
81+
// RSASSA-PSS with SHA-size
82+
@Serializable(with = CoseAlgorithmSerializer::class)
83+
object PS256 : Signature(-37, SignatureAlgorithm.RSAwithSHA256andPSSPadding)
84+
85+
@Serializable(with = CoseAlgorithmSerializer::class)
86+
object PS384 : Signature(-38, SignatureAlgorithm.RSAwithSHA384andPSSPadding)
87+
88+
@Serializable(with = CoseAlgorithmSerializer::class)
89+
object PS512 : Signature(-39, SignatureAlgorithm.RSAwithSHA512andPSSPadding)
90+
91+
// RSASSA-PKCS1-v1_5 with SHA-size
92+
@Serializable(with = CoseAlgorithmSerializer::class)
93+
object RS256 : Signature(-257, SignatureAlgorithm.RSAwithSHA256andPKCS1Padding)
94+
95+
@Serializable(with = CoseAlgorithmSerializer::class)
96+
object RS384 : Signature(-258, SignatureAlgorithm.RSAwithSHA384andPKCS1Padding)
97+
98+
@Serializable(with = CoseAlgorithmSerializer::class)
99+
object RS512 : Signature(-259, SignatureAlgorithm.RSAwithSHA512andPKCS1Padding)
100+
101+
// RSASSA-PKCS1-v1_5 using SHA-1
102+
@Serializable(with = CoseAlgorithmSerializer::class)
103+
object RS1 : Signature(-65535, SignatureAlgorithm.RSA(Digest.SHA1, RSAPadding.PKCS1))
104+
105+
open val digest: Digest?
106+
get() = when (algorithm) {
107+
is SignatureAlgorithm.ECDSA -> digest
108+
is SignatureAlgorithm.RSA -> digest
109+
}
110+
111+
companion object {
112+
val entries: Collection<Signature> = listOf(
113+
ES256,
114+
ES384,
115+
ES512,
116+
PS256,
117+
PS384,
118+
PS512,
119+
RS256,
120+
RS384,
121+
RS512,
122+
RS1,
123+
)
124+
}
125+
51126
}
52127

53-
override val algorithm: SignatureAlgorithm get() = when (this) {
54-
ES256 -> SignatureAlgorithm.ECDSA(Digest.SHA256, ECCurve.SECP_256_R_1)
55-
ES384 -> SignatureAlgorithm.ECDSA(Digest.SHA384, ECCurve.SECP_384_R_1)
56-
ES512 -> SignatureAlgorithm.ECDSA(Digest.SHA512, ECCurve.SECP_521_R_1)
57128

58-
HS256, HS384, HS512 -> SignatureAlgorithm.HMAC(this.digest)
59-
PS256, PS384, PS512 -> SignatureAlgorithm.RSA(this. digest, RSAPadding.PSS)
60-
RS1, RS256, RS384, RS512 -> SignatureAlgorithm.RSA(this.digest, RSAPadding.PKCS1)
129+
@Serializable(with = CoseAlgorithmSerializer::class)
130+
sealed class MAC(
131+
value: Int, algorithm: MessageAuthenticationCode,
132+
/**
133+
* The tag length for COSE might not be the same as for the underlying HMAC
134+
*/
135+
val tagLength: BitLength
136+
) :
137+
DataIntegrity<MessageAuthenticationCode>(value, algorithm), Symmetric {
138+
// HMAC-size with SHA-size
139+
/**HMAC w/ SHA-256 truncated to 64 bits*/
140+
141+
@Serializable(with = CoseAlgorithmSerializer::class)
142+
object HS256_64 : MAC(4, HMAC.SHA256, 64.bit)
143+
144+
@Serializable(with = CoseAlgorithmSerializer::class)
145+
object HS256 : MAC(5, HMAC.SHA256, 256.bit)
146+
147+
@Serializable(with = CoseAlgorithmSerializer::class)
148+
object HS384 : MAC(6, HMAC.SHA384, 384.bit)
149+
150+
@Serializable(with = CoseAlgorithmSerializer::class)
151+
object HS512 : MAC(7, HMAC.SHA512, 512.bit)
152+
153+
@Serializable(with = CoseAlgorithmSerializer::class)
154+
object UNOFFICIAL_HS1 : MAC(-2341169 /*random inside private use range*/, HMAC.SHA1, 160.bit)
155+
156+
companion object {
157+
val entries: Collection<MAC> = listOf(
158+
HS256,
159+
HS384,
160+
HS512,
161+
UNOFFICIAL_HS1,
162+
)
163+
}
164+
}
165+
166+
companion object {
167+
val entries: Collection<CoseAlgorithm> = DataIntegrity.entries + SymmetricEncryption.entries
61168
}
169+
62170
}
63171

64172
object CoseAlgorithmSerializer : KSerializer<CoseAlgorithm> {
@@ -78,37 +186,62 @@ object CoseAlgorithmSerializer : KSerializer<CoseAlgorithm> {
78186
}
79187

80188
/** Tries to find a matching COSE algorithm. Note that COSE imposes curve restrictions on ECDSA based on the digest. */
81-
fun SignatureAlgorithm.toCoseAlgorithm() = catching {
189+
fun SignatureAlgorithm.toCoseAlgorithm(): KmmResult<CoseAlgorithm.Signature> = catching {
82190
when (this) {
83191
is SignatureAlgorithm.ECDSA -> when (this.digest) {
84-
Digest.SHA256 -> CoseAlgorithm.ES256
85-
Digest.SHA384 -> CoseAlgorithm.ES384
86-
Digest.SHA512 -> CoseAlgorithm.ES512
192+
Digest.SHA256 -> CoseAlgorithm.Signature.ES256
193+
Digest.SHA384 -> CoseAlgorithm.Signature.ES384
194+
Digest.SHA512 -> CoseAlgorithm.Signature.ES512
87195
else -> throw IllegalArgumentException("ECDSA with ${this.digest} is unsupported by COSE")
88196
}
197+
89198
is SignatureAlgorithm.RSA -> when (this.padding) {
90199
RSAPadding.PKCS1 -> when (this.digest) {
91-
Digest.SHA1 -> CoseAlgorithm.RS1
92-
Digest.SHA256 -> CoseAlgorithm.RS256
93-
Digest.SHA384 -> CoseAlgorithm.RS384
94-
Digest.SHA512 -> CoseAlgorithm.RS512
200+
Digest.SHA1 -> CoseAlgorithm.Signature.RS1
201+
Digest.SHA256 -> CoseAlgorithm.Signature.RS256
202+
Digest.SHA384 -> CoseAlgorithm.Signature.RS384
203+
Digest.SHA512 -> CoseAlgorithm.Signature.RS512
95204
}
205+
96206
RSAPadding.PSS -> when (this.digest) {
97-
Digest.SHA256 -> CoseAlgorithm.PS256
98-
Digest.SHA384 -> CoseAlgorithm.PS384
99-
Digest.SHA512 -> CoseAlgorithm.PS512
207+
Digest.SHA256 -> CoseAlgorithm.Signature.PS256
208+
Digest.SHA384 -> CoseAlgorithm.Signature.PS384
209+
Digest.SHA512 -> CoseAlgorithm.Signature.PS512
100210
else -> throw IllegalArgumentException("RSA-PSS with ${this.digest} is unsupported by COSE")
101211
}
102212
}
103-
is SignatureAlgorithm.HMAC -> when (this.digest) {
104-
Digest.SHA256 -> CoseAlgorithm.HS256
105-
Digest.SHA384 -> CoseAlgorithm.HS384
106-
Digest.SHA512 -> CoseAlgorithm.HS512
107-
else -> throw IllegalArgumentException("HMAC with ${this.digest} is unsupported by COSE")
108-
}
213+
}
214+
}
215+
216+
fun DataIntegrityAlgorithm.toCoseAlgorithm(): KmmResult<CoseAlgorithm> = catching {
217+
when (this) {
218+
is SignatureAlgorithm -> toCoseAlgorithm().getOrThrow()
219+
is MessageAuthenticationCode -> toCoseAlgorithm().getOrThrow()
220+
else -> throw IllegalArgumentException("Algorithm $this not supported by COSE")
221+
}
222+
}
223+
224+
/** Tries to find a matching COSE algorithm. Note that [CoseAlgorithm.MAC.HS256_64] cannot be mapped automatically. */
225+
fun MessageAuthenticationCode.toCoseAlgorithm(): KmmResult<CoseAlgorithm.MAC> = catching {
226+
when (this) {
227+
HMAC.SHA1 -> CoseAlgorithm.MAC.UNOFFICIAL_HS1
228+
HMAC.SHA256 -> CoseAlgorithm.MAC.HS256
229+
HMAC.SHA384 -> CoseAlgorithm.MAC.HS384
230+
HMAC.SHA512 -> CoseAlgorithm.MAC.HS512
231+
}
232+
}
233+
234+
/** Tries to find a matching COSE algorithm. Note that only AES-GCM and ChaCha/Poly are supported. */
235+
fun SymmetricEncryptionAlgorithm<*, *, *>.toCoseAlgorithm(): KmmResult<CoseAlgorithm.SymmetricEncryption> = catching {
236+
when (this) {
237+
SymmetricEncryptionAlgorithm.ChaCha20Poly1305 -> CoseAlgorithm.SymmetricEncryption.ChaCha20Poly1305
238+
SymmetricEncryptionAlgorithm.AES_128.GCM -> CoseAlgorithm.SymmetricEncryption.A128GCM
239+
SymmetricEncryptionAlgorithm.AES_192.GCM -> CoseAlgorithm.SymmetricEncryption.A192GCM
240+
SymmetricEncryptionAlgorithm.AES_256.GCM -> CoseAlgorithm.SymmetricEncryption.A256GCM
241+
else -> throw IllegalArgumentException("$this has no COSE algorithm mapping")
109242
}
110243
}
111244

112245
/** Tries to find a matching COSE algorithm. Note that COSE imposes curve restrictions on ECDSA based on the digest. */
113-
fun SpecializedSignatureAlgorithm.toCoseAlgorithm() =
246+
fun SpecializedSignatureAlgorithm.toCoseAlgorithm(): KmmResult<CoseAlgorithm.Signature> =
114247
this.algorithm.toCoseAlgorithm()

0 commit comments

Comments
 (0)