esim_der.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package device
  2. import "fmt"
  3. // DER/BER-TLV encoding helpers — the encoder counterpart to the decoder
  4. // (derParse/derDecodeOne) in esim.go. These build the ES10 request bodies the
  5. // eUICC consumes (AuthenticateServer, PrepareDownload, LoadBoundProfilePackage,
  6. // …), whose tags mix one-byte (0x30, 0x04) and two-byte (0x5F37, 0xBF38) forms
  7. // and whose payloads can exceed the 0x80 short-length threshold.
  8. // derEncodeTag emits a tag's identifier bytes (1 byte for tags < 0x100, more for
  9. // long-form tags such as 0x5F37 or 0xBF38).
  10. func derEncodeTag(tag int) []byte {
  11. if tag < 0x100 {
  12. return []byte{byte(tag)}
  13. }
  14. out := make([]byte, 0, 3)
  15. started := false
  16. for shift := 24; shift >= 0; shift -= 8 {
  17. b := byte(tag >> shift)
  18. if b != 0 || started {
  19. out = append(out, b)
  20. started = true
  21. }
  22. }
  23. return out
  24. }
  25. // derEncodeLength emits a length in short form (< 0x80) or long form (0x81/0x82/0x83).
  26. func derEncodeLength(length int) []byte {
  27. switch {
  28. case length < 0x80:
  29. return []byte{byte(length)}
  30. case length < 0x100:
  31. return []byte{0x81, byte(length)}
  32. case length < 0x10000:
  33. return []byte{0x82, byte(length >> 8), byte(length)}
  34. default:
  35. return []byte{0x83, byte(length >> 16), byte(length >> 8), byte(length)}
  36. }
  37. }
  38. // derEncode builds one complete BER-TLV element: tag + length + value.
  39. func derEncode(tag int, value []byte) []byte {
  40. out := derEncodeTag(tag)
  41. out = append(out, derEncodeLength(len(value))...)
  42. return append(out, value...)
  43. }
  44. // derConstruct builds a constructed element whose value is the concatenation of
  45. // already-encoded child elements.
  46. func derConstruct(tag int, children ...[]byte) []byte {
  47. var value []byte
  48. for _, child := range children {
  49. value = append(value, child...)
  50. }
  51. return derEncode(tag, value)
  52. }
  53. // derFindValue parses data and returns the value of the first node with tag,
  54. // searching recursively. Use this (not derValue) when the target may be nested
  55. // inside an enclosing element — e.g. transactionId (0x80) inside serverSigned1.
  56. func derFindValue(data []byte, tag int) []byte {
  57. nodes := derFindAll(derParse(data), tag)
  58. if len(nodes) == 0 {
  59. return nil
  60. }
  61. return nodes[0].value
  62. }
  63. // derElementAt decodes the single BER-TLV element starting at buf[offset] and
  64. // reports its tag, the number of header bytes (tag + length), and the total
  65. // element length (header + value). It performs no recursion, so callers can walk
  66. // a buffer by explicit offset — exactly what LoadBoundProfilePackage segmentation
  67. // needs to slice the package at TLV boundaries.
  68. func derElementAt(buf []byte, offset int) (tag int, headerLen int, totalLen int, err error) {
  69. index := offset
  70. if index >= len(buf) {
  71. return 0, 0, 0, fmt.Errorf("esim: element at %d out of range", offset)
  72. }
  73. first := buf[index]
  74. index++
  75. tag = int(first)
  76. if first&0x1F == 0x1F { // long-form tag
  77. for index < len(buf) {
  78. b := buf[index]
  79. index++
  80. tag = tag<<8 | int(b)
  81. if b&0x80 == 0 {
  82. break
  83. }
  84. }
  85. }
  86. if index >= len(buf) {
  87. return 0, 0, 0, fmt.Errorf("esim: truncated tag at %d", offset)
  88. }
  89. lengthByte := buf[index]
  90. index++
  91. length := 0
  92. if lengthByte&0x80 == 0 {
  93. length = int(lengthByte)
  94. } else {
  95. count := int(lengthByte & 0x7F)
  96. if count == 0 || count > 4 || index+count > len(buf) {
  97. return 0, 0, 0, fmt.Errorf("esim: bad length at %d", offset)
  98. }
  99. for i := 0; i < count; i++ {
  100. length = length<<8 | int(buf[index])
  101. index++
  102. }
  103. }
  104. headerLen = index - offset
  105. if headerLen+length > len(buf)-offset {
  106. return 0, 0, 0, fmt.Errorf("esim: element at %d overruns buffer", offset)
  107. }
  108. return tag, headerLen, headerLen + length, nil
  109. }