cert-signatures.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. function x509Error(msg, cert) {
  2. return new Error('SASL channel binding: ' + msg + ' when parsing public certificate ' + cert.toString('base64'))
  3. }
  4. function readASN1Length(data, index) {
  5. let length = data[index++]
  6. if (length < 0x80) return { length, index }
  7. const lengthBytes = length & 0x7f
  8. if (lengthBytes > 4) throw x509Error('bad length', data)
  9. length = 0
  10. for (let i = 0; i < lengthBytes; i++) {
  11. length = (length << 8) | data[index++]
  12. }
  13. return { length, index }
  14. }
  15. function readASN1OID(data, index) {
  16. if (data[index++] !== 0x6) throw x509Error('non-OID data', data) // 6 = OID
  17. const { length: OIDLength, index: indexAfterOIDLength } = readASN1Length(data, index)
  18. index = indexAfterOIDLength
  19. const lastIndex = index + OIDLength
  20. const byte1 = data[index++]
  21. let oid = ((byte1 / 40) >> 0) + '.' + (byte1 % 40)
  22. while (index < lastIndex) {
  23. // loop over numbers in OID
  24. let value = 0
  25. while (index < lastIndex) {
  26. // loop over bytes in number
  27. const nextByte = data[index++]
  28. value = (value << 7) | (nextByte & 0x7f)
  29. if (nextByte < 0x80) break
  30. }
  31. oid += '.' + value
  32. }
  33. return { oid, index }
  34. }
  35. function expectASN1Seq(data, index) {
  36. if (data[index++] !== 0x30) throw x509Error('non-sequence data', data) // 30 = Sequence
  37. return readASN1Length(data, index)
  38. }
  39. function signatureAlgorithmHashFromCertificate(data, index) {
  40. // read this thread: https://www.postgresql.org/message-id/17760-b6c61e752ec07060%40postgresql.org
  41. if (index === undefined) index = 0
  42. index = expectASN1Seq(data, index).index
  43. const { length: certInfoLength, index: indexAfterCertInfoLength } = expectASN1Seq(data, index)
  44. index = indexAfterCertInfoLength + certInfoLength // skip over certificate info
  45. index = expectASN1Seq(data, index).index // skip over signature length field
  46. const { oid, index: indexAfterOID } = readASN1OID(data, index)
  47. switch (oid) {
  48. // RSA
  49. case '1.2.840.113549.1.1.4':
  50. return 'MD5'
  51. case '1.2.840.113549.1.1.5':
  52. return 'SHA-1'
  53. case '1.2.840.113549.1.1.11':
  54. return 'SHA-256'
  55. case '1.2.840.113549.1.1.12':
  56. return 'SHA-384'
  57. case '1.2.840.113549.1.1.13':
  58. return 'SHA-512'
  59. case '1.2.840.113549.1.1.14':
  60. return 'SHA-224'
  61. case '1.2.840.113549.1.1.15':
  62. return 'SHA512-224'
  63. case '1.2.840.113549.1.1.16':
  64. return 'SHA512-256'
  65. // ECDSA
  66. case '1.2.840.10045.4.1':
  67. return 'SHA-1'
  68. case '1.2.840.10045.4.3.1':
  69. return 'SHA-224'
  70. case '1.2.840.10045.4.3.2':
  71. return 'SHA-256'
  72. case '1.2.840.10045.4.3.3':
  73. return 'SHA-384'
  74. case '1.2.840.10045.4.3.4':
  75. return 'SHA-512'
  76. // RSASSA-PSS: hash is indicated separately
  77. case '1.2.840.113549.1.1.10': {
  78. index = indexAfterOID
  79. index = expectASN1Seq(data, index).index
  80. if (data[index++] !== 0xa0) throw x509Error('non-tag data', data) // a0 = constructed tag 0
  81. index = readASN1Length(data, index).index // skip over tag length field
  82. index = expectASN1Seq(data, index).index // skip over sequence length field
  83. const { oid: hashOID } = readASN1OID(data, index)
  84. switch (hashOID) {
  85. // standalone hash OIDs
  86. case '1.2.840.113549.2.5':
  87. return 'MD5'
  88. case '1.3.14.3.2.26':
  89. return 'SHA-1'
  90. case '2.16.840.1.101.3.4.2.1':
  91. return 'SHA-256'
  92. case '2.16.840.1.101.3.4.2.2':
  93. return 'SHA-384'
  94. case '2.16.840.1.101.3.4.2.3':
  95. return 'SHA-512'
  96. }
  97. throw x509Error('unknown hash OID ' + hashOID, data)
  98. }
  99. // Ed25519 -- see https: return//github.com/openssl/openssl/issues/15477
  100. case '1.3.101.110':
  101. case '1.3.101.112': // ph
  102. return 'SHA-512'
  103. // Ed448 -- still not in pg 17.2 (if supported, digest would be SHAKE256 x 64 bytes)
  104. case '1.3.101.111':
  105. case '1.3.101.113': // ph
  106. throw x509Error('Ed448 certificate channel binding is not currently supported by Postgres')
  107. }
  108. throw x509Error('unknown OID ' + oid, data)
  109. }
  110. module.exports = { signatureAlgorithmHashFromCertificate }