pem_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. package cert
  2. import (
  3. "bufio"
  4. "strings"
  5. "testing"
  6. "github.com/stretchr/testify/assert"
  7. "github.com/stretchr/testify/require"
  8. )
  9. func scanAll(t *testing.T, input string) ([]string, error) {
  10. t.Helper()
  11. scanner := bufio.NewScanner(strings.NewReader(input))
  12. scanner.Split(SplitPEM)
  13. var blocks []string
  14. for scanner.Scan() {
  15. blocks = append(blocks, scanner.Text())
  16. }
  17. return blocks, scanner.Err()
  18. }
  19. func TestSplitPEM_Single(t *testing.T) {
  20. input := "-----BEGIN TEST-----\ndata\n-----END TEST-----\n"
  21. blocks, err := scanAll(t, input)
  22. require.NoError(t, err)
  23. require.Len(t, blocks, 1)
  24. require.Equal(t, input, blocks[0])
  25. }
  26. func TestSplitPEM_Multiple(t *testing.T) {
  27. block1 := "-----BEGIN TEST-----\naaa\n-----END TEST-----\n"
  28. block2 := "-----BEGIN TEST-----\nbbb\n-----END TEST-----\n"
  29. blocks, err := scanAll(t, block1+block2)
  30. require.NoError(t, err)
  31. require.Len(t, blocks, 2)
  32. require.Equal(t, block1, blocks[0])
  33. require.Equal(t, block2, blocks[1])
  34. }
  35. func TestSplitPEM_CommentsAndWhitespaceBetweenBlocks(t *testing.T) {
  36. input := "# comment\n\n-----BEGIN TEST-----\naaa\n-----END TEST-----\n\n# another comment\n\n-----BEGIN TEST-----\nbbb\n-----END TEST-----\n"
  37. blocks, err := scanAll(t, input)
  38. require.NoError(t, err)
  39. require.Len(t, blocks, 2)
  40. }
  41. func TestSplitPEM_Empty(t *testing.T) {
  42. blocks, err := scanAll(t, "")
  43. require.NoError(t, err)
  44. require.Empty(t, blocks)
  45. }
  46. func TestSplitPEM_WhitespaceOnly(t *testing.T) {
  47. blocks, err := scanAll(t, " \n\t\n ")
  48. require.NoError(t, err)
  49. require.Empty(t, blocks)
  50. }
  51. func TestSplitPEM_TrailingGarbage(t *testing.T) {
  52. input := "-----BEGIN TEST-----\ndata\n-----END TEST-----\ngarbage"
  53. blocks, err := scanAll(t, input)
  54. require.ErrorIs(t, err, ErrTruncatedPEMBlock)
  55. require.Len(t, blocks, 1)
  56. }
  57. func TestSplitPEM_TruncatedBlock(t *testing.T) {
  58. input := "-----BEGIN TEST-----\npartial data with no end"
  59. _, err := scanAll(t, input)
  60. require.ErrorIs(t, err, ErrTruncatedPEMBlock)
  61. }
  62. func TestSplitPEM_NoEndNewline(t *testing.T) {
  63. input := "-----BEGIN TEST-----\ndata\n-----END TEST-----"
  64. blocks, err := scanAll(t, input)
  65. require.NoError(t, err)
  66. require.Len(t, blocks, 1)
  67. require.Equal(t, input, blocks[0])
  68. }
  69. func TestSplitPEM_GarbageOnly(t *testing.T) {
  70. _, err := scanAll(t, "this is not PEM data")
  71. require.ErrorIs(t, err, ErrTruncatedPEMBlock)
  72. }
  73. func TestUnmarshalCertificateFromPEM(t *testing.T) {
  74. goodCert := []byte(`
  75. # A good cert
  76. -----BEGIN NEBULA CERTIFICATE-----
  77. CkAKDm5lYnVsYSByb290IGNhKJfap9AFMJfg1+YGOiCUQGByMuNRhIlQBOyzXWbL
  78. vcKBwDhov900phEfJ5DN3kABEkDCq5R8qBiu8sl54yVfgRcQXEDt3cHr8UTSLszv
  79. bzBEr00kERQxxTzTsH8cpYEgRoipvmExvg8WP8NdAJEYJosB
  80. -----END NEBULA CERTIFICATE-----
  81. `)
  82. badBanner := []byte(`# A bad banner
  83. -----BEGIN NOT A NEBULA CERTIFICATE-----
  84. CkAKDm5lYnVsYSByb290IGNhKJfap9AFMJfg1+YGOiCUQGByMuNRhIlQBOyzXWbL
  85. vcKBwDhov900phEfJ5DN3kABEkDCq5R8qBiu8sl54yVfgRcQXEDt3cHr8UTSLszv
  86. bzBEr00kERQxxTzTsH8cpYEgRoipvmExvg8WP8NdAJEYJosB
  87. -----END NOT A NEBULA CERTIFICATE-----
  88. `)
  89. invalidPem := []byte(`# Not a valid PEM format
  90. -BEGIN NEBULA CERTIFICATE-----
  91. CkAKDm5lYnVsYSByb290IGNhKJfap9AFMJfg1+YGOiCUQGByMuNRhIlQBOyzXWbL
  92. vcKBwDhov900phEfJ5DN3kABEkDCq5R8qBiu8sl54yVfgRcQXEDt3cHr8UTSLszv
  93. bzBEr00kERQxxTzTsH8cpYEgRoipvmExvg8WP8NdAJEYJosB
  94. -END NEBULA CERTIFICATE----`)
  95. certBundle := appendByteSlices(goodCert, badBanner, invalidPem)
  96. // Success test case
  97. cert, rest, err := UnmarshalCertificateFromPEM(certBundle)
  98. assert.NotNil(t, cert)
  99. assert.Equal(t, rest, append(badBanner, invalidPem...))
  100. require.NoError(t, err)
  101. // Fail due to invalid banner.
  102. cert, rest, err = UnmarshalCertificateFromPEM(rest)
  103. assert.Nil(t, cert)
  104. assert.Equal(t, rest, invalidPem)
  105. require.EqualError(t, err, "bytes did not contain a proper certificate banner")
  106. // Fail due to invalid PEM format, because
  107. // it's missing the requisite pre-encapsulation boundary.
  108. cert, rest, err = UnmarshalCertificateFromPEM(rest)
  109. assert.Nil(t, cert)
  110. assert.Equal(t, rest, invalidPem)
  111. require.EqualError(t, err, "input did not contain a valid PEM encoded block")
  112. }
  113. func TestUnmarshalSigningPrivateKeyFromPEM(t *testing.T) {
  114. privKey := []byte(`# A good key
  115. -----BEGIN NEBULA ED25519 PRIVATE KEY-----
  116. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
  117. -----END NEBULA ED25519 PRIVATE KEY-----
  118. `)
  119. privP256Key := []byte(`# A good key
  120. -----BEGIN NEBULA ECDSA P256 PRIVATE KEY-----
  121. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
  122. -----END NEBULA ECDSA P256 PRIVATE KEY-----
  123. `)
  124. shortKey := []byte(`# A short key
  125. -----BEGIN NEBULA ED25519 PRIVATE KEY-----
  126. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  127. -----END NEBULA ED25519 PRIVATE KEY-----
  128. `)
  129. invalidBanner := []byte(`# Invalid banner
  130. -----BEGIN NOT A NEBULA PRIVATE KEY-----
  131. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
  132. -----END NOT A NEBULA PRIVATE KEY-----
  133. `)
  134. invalidPem := []byte(`# Not a valid PEM format
  135. -BEGIN NEBULA ED25519 PRIVATE KEY-----
  136. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
  137. -END NEBULA ED25519 PRIVATE KEY-----`)
  138. keyBundle := appendByteSlices(privKey, privP256Key, shortKey, invalidBanner, invalidPem)
  139. // Success test case
  140. k, rest, curve, err := UnmarshalSigningPrivateKeyFromPEM(keyBundle)
  141. assert.Len(t, k, 64)
  142. assert.Equal(t, rest, appendByteSlices(privP256Key, shortKey, invalidBanner, invalidPem))
  143. assert.Equal(t, Curve_CURVE25519, curve)
  144. require.NoError(t, err)
  145. // Success test case
  146. k, rest, curve, err = UnmarshalSigningPrivateKeyFromPEM(rest)
  147. assert.Len(t, k, 32)
  148. assert.Equal(t, rest, appendByteSlices(shortKey, invalidBanner, invalidPem))
  149. assert.Equal(t, Curve_P256, curve)
  150. require.NoError(t, err)
  151. // Fail due to short key
  152. k, rest, curve, err = UnmarshalSigningPrivateKeyFromPEM(rest)
  153. assert.Nil(t, k)
  154. assert.Equal(t, rest, appendByteSlices(invalidBanner, invalidPem))
  155. require.EqualError(t, err, "key was not 64 bytes, is invalid Ed25519 private key")
  156. // Fail due to invalid banner
  157. k, rest, curve, err = UnmarshalSigningPrivateKeyFromPEM(rest)
  158. assert.Nil(t, k)
  159. assert.Equal(t, rest, invalidPem)
  160. require.EqualError(t, err, "bytes did not contain a proper Ed25519/ECDSA private key banner")
  161. // Fail due to invalid PEM format, because
  162. // it's missing the requisite pre-encapsulation boundary.
  163. k, rest, curve, err = UnmarshalSigningPrivateKeyFromPEM(rest)
  164. assert.Nil(t, k)
  165. assert.Equal(t, rest, invalidPem)
  166. require.EqualError(t, err, "input did not contain a valid PEM encoded block")
  167. }
  168. func TestUnmarshalPrivateKeyFromPEM(t *testing.T) {
  169. privKey := []byte(`# A good key
  170. -----BEGIN NEBULA X25519 PRIVATE KEY-----
  171. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
  172. -----END NEBULA X25519 PRIVATE KEY-----
  173. `)
  174. privP256Key := []byte(`# A good key
  175. -----BEGIN NEBULA P256 PRIVATE KEY-----
  176. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
  177. -----END NEBULA P256 PRIVATE KEY-----
  178. `)
  179. shortKey := []byte(`# A short key
  180. -----BEGIN NEBULA X25519 PRIVATE KEY-----
  181. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
  182. -----END NEBULA X25519 PRIVATE KEY-----
  183. `)
  184. invalidBanner := []byte(`# Invalid banner
  185. -----BEGIN NOT A NEBULA PRIVATE KEY-----
  186. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
  187. -----END NOT A NEBULA PRIVATE KEY-----
  188. `)
  189. invalidPem := []byte(`# Not a valid PEM format
  190. -BEGIN NEBULA X25519 PRIVATE KEY-----
  191. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
  192. -END NEBULA X25519 PRIVATE KEY-----`)
  193. keyBundle := appendByteSlices(privKey, privP256Key, shortKey, invalidBanner, invalidPem)
  194. // Success test case
  195. k, rest, curve, err := UnmarshalPrivateKeyFromPEM(keyBundle)
  196. assert.Len(t, k, 32)
  197. assert.Equal(t, rest, appendByteSlices(privP256Key, shortKey, invalidBanner, invalidPem))
  198. assert.Equal(t, Curve_CURVE25519, curve)
  199. require.NoError(t, err)
  200. // Success test case
  201. k, rest, curve, err = UnmarshalPrivateKeyFromPEM(rest)
  202. assert.Len(t, k, 32)
  203. assert.Equal(t, rest, appendByteSlices(shortKey, invalidBanner, invalidPem))
  204. assert.Equal(t, Curve_P256, curve)
  205. require.NoError(t, err)
  206. // Fail due to short key
  207. k, rest, curve, err = UnmarshalPrivateKeyFromPEM(rest)
  208. assert.Nil(t, k)
  209. assert.Equal(t, rest, appendByteSlices(invalidBanner, invalidPem))
  210. require.EqualError(t, err, "key was not 32 bytes, is invalid CURVE25519 private key")
  211. // Fail due to invalid banner
  212. k, rest, curve, err = UnmarshalPrivateKeyFromPEM(rest)
  213. assert.Nil(t, k)
  214. assert.Equal(t, rest, invalidPem)
  215. require.EqualError(t, err, "bytes did not contain a proper private key banner")
  216. // Fail due to invalid PEM format, because
  217. // it's missing the requisite pre-encapsulation boundary.
  218. k, rest, curve, err = UnmarshalPrivateKeyFromPEM(rest)
  219. assert.Nil(t, k)
  220. assert.Equal(t, rest, invalidPem)
  221. require.EqualError(t, err, "input did not contain a valid PEM encoded block")
  222. }
  223. func TestUnmarshalPublicKeyFromPEM(t *testing.T) {
  224. t.Parallel()
  225. pubKey := []byte(`# A good key
  226. -----BEGIN NEBULA X25519 PUBLIC KEY-----
  227. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
  228. -----END NEBULA X25519 PUBLIC KEY-----
  229. `)
  230. pubP256Key := []byte(`# A good key
  231. -----BEGIN NEBULA P256 PUBLIC KEY-----
  232. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  233. AAAAAAAAAAAAAAAAAAAAAAA=
  234. -----END NEBULA P256 PUBLIC KEY-----
  235. `)
  236. signingKey := []byte(`# A signing key has the wrong scope for this function
  237. -----BEGIN NEBULA ECDSA P256 PUBLIC KEY-----
  238. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  239. AAAAAAAAAAAAAAAAAAAAAAA=
  240. -----END NEBULA ECDSA P256 PUBLIC KEY-----
  241. `)
  242. shortKey := []byte(`# A short key
  243. -----BEGIN NEBULA X25519 PUBLIC KEY-----
  244. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
  245. -----END NEBULA X25519 PUBLIC KEY-----
  246. `)
  247. invalidBanner := []byte(`# Invalid banner
  248. -----BEGIN NOT A NEBULA PUBLIC KEY-----
  249. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
  250. -----END NOT A NEBULA PUBLIC KEY-----
  251. `)
  252. invalidPem := []byte(`# Not a valid PEM format
  253. -BEGIN NEBULA X25519 PUBLIC KEY-----
  254. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
  255. -END NEBULA X25519 PUBLIC KEY-----`)
  256. keyBundle := appendByteSlices(pubKey, pubP256Key, signingKey, shortKey, invalidBanner, invalidPem)
  257. // X25519 key
  258. k, rest, curve, err := UnmarshalPublicKeyFromPEM(keyBundle)
  259. assert.Len(t, k, 32)
  260. require.NoError(t, err)
  261. assert.Equal(t, rest, appendByteSlices(pubP256Key, signingKey, shortKey, invalidBanner, invalidPem))
  262. assert.Equal(t, Curve_CURVE25519, curve)
  263. // P256 key
  264. k, rest, curve, err = UnmarshalPublicKeyFromPEM(rest)
  265. assert.Len(t, k, 65)
  266. require.NoError(t, err)
  267. assert.Equal(t, rest, appendByteSlices(signingKey, shortKey, invalidBanner, invalidPem))
  268. assert.Equal(t, Curve_P256, curve)
  269. // Reject a signing public key (Ed25519/ECDSA banner)
  270. k, rest, _, err = UnmarshalPublicKeyFromPEM(rest)
  271. assert.Nil(t, k)
  272. assert.Equal(t, rest, appendByteSlices(shortKey, invalidBanner, invalidPem))
  273. require.EqualError(t, err, "bytes did not contain a proper public key banner")
  274. // Fail due to short key
  275. k, rest, _, err = UnmarshalPublicKeyFromPEM(rest)
  276. assert.Nil(t, k)
  277. assert.Equal(t, rest, appendByteSlices(invalidBanner, invalidPem))
  278. require.EqualError(t, err, "key was not 32 bytes, is invalid CURVE25519 public key")
  279. // Fail due to invalid banner
  280. k, rest, _, err = UnmarshalPublicKeyFromPEM(rest)
  281. assert.Nil(t, k)
  282. require.EqualError(t, err, "bytes did not contain a proper public key banner")
  283. assert.Equal(t, rest, invalidPem)
  284. // Fail due to invalid PEM format, because
  285. // it's missing the requisite pre-encapsulation boundary.
  286. k, rest, _, err = UnmarshalPublicKeyFromPEM(rest)
  287. assert.Nil(t, k)
  288. assert.Equal(t, rest, invalidPem)
  289. require.EqualError(t, err, "input did not contain a valid PEM encoded block")
  290. }
  291. func TestUnmarshalSigningPublicKeyFromPEM(t *testing.T) {
  292. t.Parallel()
  293. pubKey := []byte(`# A good key
  294. -----BEGIN NEBULA ED25519 PUBLIC KEY-----
  295. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
  296. -----END NEBULA ED25519 PUBLIC KEY-----
  297. `)
  298. pubP256Key := []byte(`# A good key
  299. -----BEGIN NEBULA ECDSA P256 PUBLIC KEY-----
  300. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  301. AAAAAAAAAAAAAAAAAAAAAAA=
  302. -----END NEBULA ECDSA P256 PUBLIC KEY-----
  303. `)
  304. ecdhKey := []byte(`# A key-agreement key has the wrong scope for this function
  305. -----BEGIN NEBULA X25519 PUBLIC KEY-----
  306. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
  307. -----END NEBULA X25519 PUBLIC KEY-----
  308. `)
  309. shortKey := []byte(`# A short key
  310. -----BEGIN NEBULA ED25519 PUBLIC KEY-----
  311. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
  312. -----END NEBULA ED25519 PUBLIC KEY-----
  313. `)
  314. invalidBanner := []byte(`# Invalid banner
  315. -----BEGIN NOT A NEBULA PUBLIC KEY-----
  316. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
  317. -----END NOT A NEBULA PUBLIC KEY-----
  318. `)
  319. invalidPem := []byte(`# Not a valid PEM format
  320. -BEGIN NEBULA ED25519 PUBLIC KEY-----
  321. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
  322. -END NEBULA ED25519 PUBLIC KEY-----`)
  323. keyBundle := appendByteSlices(pubKey, pubP256Key, ecdhKey, shortKey, invalidBanner, invalidPem)
  324. // Ed25519 key
  325. k, rest, curve, err := UnmarshalSigningPublicKeyFromPEM(keyBundle)
  326. assert.Len(t, k, 32)
  327. require.NoError(t, err)
  328. assert.Equal(t, rest, appendByteSlices(pubP256Key, ecdhKey, shortKey, invalidBanner, invalidPem))
  329. assert.Equal(t, Curve_CURVE25519, curve)
  330. // ECDSA P256 key
  331. k, rest, curve, err = UnmarshalSigningPublicKeyFromPEM(rest)
  332. assert.Len(t, k, 65)
  333. require.NoError(t, err)
  334. assert.Equal(t, rest, appendByteSlices(ecdhKey, shortKey, invalidBanner, invalidPem))
  335. assert.Equal(t, Curve_P256, curve)
  336. // Reject a key-agreement public key (X25519/P256 banner)
  337. k, rest, _, err = UnmarshalSigningPublicKeyFromPEM(rest)
  338. assert.Nil(t, k)
  339. assert.Equal(t, rest, appendByteSlices(shortKey, invalidBanner, invalidPem))
  340. require.EqualError(t, err, "bytes did not contain a proper Ed25519/ECDSA public key banner")
  341. // Fail due to short key
  342. k, rest, _, err = UnmarshalSigningPublicKeyFromPEM(rest)
  343. assert.Nil(t, k)
  344. assert.Equal(t, rest, appendByteSlices(invalidBanner, invalidPem))
  345. require.EqualError(t, err, "key was not 32 bytes, is invalid CURVE25519 public key")
  346. // Fail due to invalid banner
  347. k, rest, _, err = UnmarshalSigningPublicKeyFromPEM(rest)
  348. assert.Nil(t, k)
  349. require.EqualError(t, err, "bytes did not contain a proper Ed25519/ECDSA public key banner")
  350. assert.Equal(t, rest, invalidPem)
  351. // Fail due to invalid PEM format, because
  352. // it's missing the requisite pre-encapsulation boundary.
  353. k, rest, _, err = UnmarshalSigningPublicKeyFromPEM(rest)
  354. assert.Nil(t, k)
  355. assert.Equal(t, rest, invalidPem)
  356. require.EqualError(t, err, "input did not contain a valid PEM encoded block")
  357. }