Skip to content

Commit 6ae9dbf

Browse files
committed
[PEM] add more tests and improve toString
1 parent 9b66bc3 commit 6ae9dbf

File tree

2 files changed

+176
-1
lines changed

2 files changed

+176
-1
lines changed

cryptography-serialization/pem/src/commonMain/kotlin/PemDocument.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public class PemDocument(
143143
* **Avoid logging if the [content] is sensitive**
144144
*/
145145
override fun toString(): String {
146-
return "PemDocument(label=$label, content=$content)"
146+
return "PemDocument(label=${label.value}, content=$content)"
147147
}
148148

149149
public companion object {

cryptography-serialization/pem/src/commonTest/kotlin/PemTest.kt

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,129 @@ import kotlin.test.*
1010

1111
class PemTest {
1212

13+
@Test
14+
fun testByteArrayConstructorImmutability() {
15+
val content = byteArrayOf(1, 2, 3, 4)
16+
val expected = ByteString(content)
17+
val doc = PemDocument(PemLabel("LABEL"), content)
18+
// mutate the original byte array after construction
19+
content[0] = 9
20+
// content must be unaffected (constructor copies input)
21+
assertEquals(expected, doc.content)
22+
}
23+
24+
@Test
25+
fun testEqualsHashCode() {
26+
val doc = PemDocument(PemLabel("LABEL"), byteArrayOf(1, 2, 3, 4))
27+
28+
// equals/hashCode with the same logical content
29+
val doc2 = PemDocument(PemLabel("LABEL"), ByteString(1, 2, 3, 4))
30+
assertEquals(doc.content, doc2.content)
31+
assertEquals(doc, doc2)
32+
assertEquals(doc.hashCode(), doc2.hashCode())
33+
34+
// different label -> not equal
35+
val doc3 = PemDocument(PemLabel("OTHER"), ByteString(1, 2, 3, 4))
36+
assertNotEquals(doc, doc3)
37+
38+
// different content -> not equal
39+
val doc4 = PemDocument(PemLabel("LABEL"), ByteString(4, 3, 2, 1))
40+
assertNotEquals(doc, doc4)
41+
42+
}
43+
44+
@Test
45+
fun testToString() {
46+
assertEquals(
47+
"PemDocument(label=LABEL, content=ByteString(size=4 hex=01020304))",
48+
PemDocument(PemLabel("LABEL"), byteArrayOf(1, 2, 3, 4)).toString()
49+
)
50+
}
51+
52+
@Test
53+
fun testDecodeToSequenceEmptyAndNoPem() {
54+
testPemDecodeSequence(emptyList(), "")
55+
testPemDecodeSequence(emptyList(), "no pem here")
56+
}
57+
58+
@Test
59+
fun testDecodeToSequenceWithContentAroundAndBetween(): Unit = testPemDecodeSequence(
60+
expected = listOf(
61+
PemDocument(PemLabel.Certificate, "ONE".encodeToByteString()),
62+
PemDocument(PemLabel.Certificate, "TWO".encodeToByteString())
63+
),
64+
document = """
65+
Some heading that should be ignored
66+
-----BEGIN CERTIFICATE-----
67+
T05F
68+
-----END CERTIFICATE-----
69+
Some text between documents should be ignored too
70+
-----BEGIN CERTIFICATE-----
71+
VFdP
72+
-----END CERTIFICATE-----
73+
Final trailing text that should be ignored as well
74+
""".trimIndent()
75+
)
76+
77+
@Test
78+
fun testDecodeSourcePartialConsumption() {
79+
val pem = """
80+
IGNORE
81+
-----BEGIN X-----
82+
QQ==
83+
-----END X-----
84+
BETWEEN
85+
-----BEGIN X-----
86+
Qg==
87+
-----END X-----
88+
AFTER
89+
""".trimIndent()
90+
91+
val buffer = Buffer()
92+
buffer.writeString(pem)
93+
94+
assertEquals(
95+
expected = PemDocument(PemLabel("X"), "A".encodeToByteString()),
96+
// read from buffer one document
97+
actual = PemDocument.decode(buffer)
98+
)
99+
assertEquals(
100+
expected = PemDocument(PemLabel("X"), "B".encodeToByteString()),
101+
// read from buffer second document
102+
actual = PemDocument.decode(buffer)
103+
)
104+
105+
assertEquals("AFTER", buffer.readString())
106+
}
107+
108+
@Test
109+
fun testDecodeToSequenceSourcePartialConsumption() {
110+
val pem = """
111+
IGNORE
112+
-----BEGIN X-----
113+
QQ==
114+
-----END X-----
115+
BETWEEN
116+
-----BEGIN X-----
117+
Qg==
118+
-----END X-----
119+
AFTER
120+
""".trimIndent()
121+
122+
val buffer = Buffer()
123+
buffer.writeString(pem)
124+
125+
assertEquals(
126+
listOf(
127+
PemDocument(PemLabel("X"), "A".encodeToByteString()),
128+
PemDocument(PemLabel("X"), "B".encodeToByteString())
129+
),
130+
PemDocument.decodeToSequence(buffer).take(2).toList()
131+
)
132+
133+
assertEquals("AFTER", buffer.readString())
134+
}
135+
13136
@Test
14137
fun testHelloWorld() = testPem(
15138
label = "UNKNOWN",
@@ -57,6 +180,14 @@ class PemTest {
57180
assertEquals("Invalid PEM format: missing BEGIN label", it.message)
58181
}
59182

183+
@Test
184+
fun testDecodingWithEmpty() = testPemDecodeFailure(
185+
document = ""
186+
) {
187+
assertIs<IllegalArgumentException>(it)
188+
assertEquals("Invalid PEM format: missing BEGIN label", it.message)
189+
}
190+
60191
@Test
61192
fun testDecodingWithNoEndLabel() = testPemDecodeFailure(
62193
document = "-----BEGIN UNKNOWN-----\nSGVsbG8gV29ybGQ="
@@ -77,6 +208,31 @@ class PemTest {
77208
assertEquals("Invalid PEM format: BEGIN(UNKNOWN1) and END(UNKNOWN2) labels mismatch", it.message)
78209
}
79210

211+
@Test
212+
fun testDecodingWithMissingNewLineAfterBegin() = testPemDecodeFailure(
213+
document = "-----BEGIN X-----"
214+
) {
215+
assertIs<IllegalArgumentException>(it)
216+
assertEquals("Invalid PEM format: missing new line after BEGIN label", it.message)
217+
}
218+
219+
@Test
220+
fun testDecodingWithMissingBeginLabelSuffix() = testPemDecodeFailure(
221+
document = "-----BEGIN X\nQQ==\n-----END X-----"
222+
) {
223+
assertIs<IllegalArgumentException>(it)
224+
assertEquals("Invalid PEM format: missing BEGIN label suffix", it.message)
225+
}
226+
227+
@Test
228+
fun testDecodingWithMissingEndLabelSuffix() = testPemDecodeFailure(
229+
document = "-----BEGIN X-----\nQQ==\n-----END X"
230+
) {
231+
assertIs<IllegalArgumentException>(it)
232+
assertEquals("Invalid PEM format: missing END label suffix", it.message)
233+
}
234+
235+
80236
private fun testPem(
81237
label: String,
82238
content: ByteString,
@@ -104,6 +260,25 @@ class PemTest {
104260
} as Source).buffered()))
105261
}
106262

263+
private fun testPemDecodeSequence(
264+
expected: List<PemDocument>,
265+
document: String,
266+
) {
267+
fun assertSequenceEquals(expectedDocs: List<PemDocument>, actualSeq: Sequence<PemDocument>) {
268+
assertEquals(expectedDocs, actualSeq.toList())
269+
}
270+
271+
assertSequenceEquals(expected, PemDocument.decodeToSequence(document))
272+
assertSequenceEquals(expected, PemDocument.decodeToSequence(document.encodeToByteArray()))
273+
assertSequenceEquals(expected, PemDocument.decodeToSequence(document.encodeToByteString()))
274+
assertSequenceEquals(expected, PemDocument.decodeToSequence(Buffer().also {
275+
it.write(document.encodeToByteArray())
276+
}))
277+
assertSequenceEquals(expected, PemDocument.decodeToSequence((Buffer().also {
278+
it.write(document.encodeToByteArray())
279+
} as Source).buffered()))
280+
}
281+
107282
private fun testPemDecodeFailure(
108283
document: String,
109284
assertThrowable: (Throwable) -> Unit,

0 commit comments

Comments
 (0)