1
+ @file:Suppress(" SERIALIZER_TYPE_INCOMPATIBLE" )
2
+
1
3
package at.asitplus.signum.indispensable.cosef
2
4
5
+ import at.asitplus.KmmResult
3
6
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
9
13
import kotlinx.serialization.KSerializer
10
14
import kotlinx.serialization.Serializable
11
15
import kotlinx.serialization.descriptors.PrimitiveKind
@@ -18,47 +22,151 @@ import kotlinx.serialization.encoding.Encoder
18
22
* See [COSE Algorithm Registry](https://www.iana.org/assignments/cose/cose.xhtml)
19
23
*/
20
24
@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
+
51
126
}
52
127
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 )
57
128
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
61
168
}
169
+
62
170
}
63
171
64
172
object CoseAlgorithmSerializer : KSerializer<CoseAlgorithm> {
@@ -78,37 +186,62 @@ object CoseAlgorithmSerializer : KSerializer<CoseAlgorithm> {
78
186
}
79
187
80
188
/* * 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 {
82
190
when (this ) {
83
191
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
87
195
else -> throw IllegalArgumentException (" ECDSA with ${this .digest} is unsupported by COSE" )
88
196
}
197
+
89
198
is SignatureAlgorithm .RSA -> when (this .padding) {
90
199
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
95
204
}
205
+
96
206
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
100
210
else -> throw IllegalArgumentException (" RSA-PSS with ${this .digest} is unsupported by COSE" )
101
211
}
102
212
}
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" )
109
242
}
110
243
}
111
244
112
245
/* * 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 > =
114
247
this .algorithm.toCoseAlgorithm()
0 commit comments