//go:build plugin_oracle || !plugin_selective package services /* Minimal Oracle TNS authentication probe. Parts of the packet layout and password verifier handling are derived from github.com/sijms/go-ora/v2, which is licensed under the MIT License: Copyright (c) 2020 Samy Sultan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ import ( "bytes" "context" "crypto/aes" "crypto/cipher" "crypto/des" "crypto/hmac" "crypto/md5" "crypto/rand" "crypto/sha1" "crypto/sha512" "encoding/binary" "encoding/hex" "errors" "fmt" "io" "net" "strconv" "strings" "time" ) const ( oraclePacketConnect = 1 oraclePacketAccept = 2 oraclePacketRefuse = 4 oraclePacketRedirect = 5 oraclePacketData = 6 oraclePacketResend = 11 oracleNoNewPass = 0x1 oracleUserAndPass = 0x100 oracleTypeRepNative int16 = 0 oracleTypeRepUniversal int16 = 1 oracleTypeRepOracle int16 = 10 ) var errOracleAuthFailed = errors.New("oracle authentication failed") type oracleSession struct { conn net.Conn in []byte out bytes.Buffer index int version uint16 negotiatedOptions uint16 sessionDataUnit uint32 transportDataUnit uint32 acfl0 uint8 acfl1 uint8 handshakeComplete bool ttcVersion uint8 hasEOSCapability bool hasFSAPCapability bool useBigClrChunks bool clrChunkSize int timeout time.Duration summary *oracleSummary } type oracleTCPNego struct { serverCharset int serverFlags uint8 serverNCharset int serverCompileTimeCaps []byte serverRuntimeCaps []byte } type oracleSummary struct { retCode int errorMessage []byte } func oracleRawAuth(ctx context.Context, host string, port int, serviceName, username, password string, timeout time.Duration) error { addr := net.JoinHostPort(host, strconv.Itoa(port)) dialer := net.Dialer{Timeout: timeout} conn, err := dialer.DialContext(ctx, "tcp", addr) if err != nil { return err } defer conn.Close() s := &oracleSession{ conn: conn, version: 317, sessionDataUnit: 0x200000, transportDataUnit: 0x200000, clrChunkSize: 0x40, timeout: timeout, } if err := s.connect(ctx, host, port, serviceName); err != nil { return err } if s.acfl0&1 != 0 && s.acfl0&4 == 0 && s.acfl1&8 == 0 { if err := s.advancedNegotiation(); err != nil { return fmt.Errorf("ANO: %w", err) } } nego, err := s.protocolNegotiation() if err != nil { return err } if err := s.dataTypeNegotiation(nego); err != nil { return err } return s.authenticate(nego, host, port, serviceName, username, password) } func (s *oracleSession) connect(ctx context.Context, host string, port int, serviceName string) error { connectData := oracleConnectData(host, port, serviceName) packetLen := 70 + len(connectData) if len(connectData) > 230 { packetLen = 70 } buf := make([]byte, packetLen) binary.BigEndian.PutUint16(buf[0:2], uint16(packetLen)) buf[4] = oraclePacketConnect binary.BigEndian.PutUint16(buf[8:10], 317) binary.BigEndian.PutUint16(buf[10:12], 300) binary.BigEndian.PutUint16(buf[12:14], 1|2048) binary.BigEndian.PutUint16(buf[14:16], 0xffff) binary.BigEndian.PutUint16(buf[16:18], 0xffff) buf[18] = 79 buf[19] = 152 binary.BigEndian.PutUint16(buf[22:24], 1) binary.BigEndian.PutUint16(buf[24:26], uint16(len(connectData))) binary.BigEndian.PutUint16(buf[26:28], 70) buf[32] = 1 buf[33] = 1 binary.BigEndian.PutUint32(buf[58:62], s.sessionDataUnit) binary.BigEndian.PutUint32(buf[62:66], s.transportDataUnit) if len(connectData) <= 230 { copy(buf[70:], connectData) } sendConnect := func() error { if err := s.writeRaw(ctx, buf); err != nil { return err } if len(connectData) > 230 { s.reset() s.putBytes([]byte(connectData)...) return s.writeData() } return nil } if err := sendConnect(); err != nil { return err } var p *oraclePacket for resends := 0; resends < 3; resends++ { var err error p, err = s.readPacket() if err != nil { return err } if p.typ != oraclePacketResend { break } if err := sendConnect(); err != nil { return err } } switch p.typ { case oraclePacketAccept: if len(p.raw) < 40 { return errors.New("short oracle accept packet") } s.version = binary.BigEndian.Uint16(p.raw[8:10]) s.negotiatedOptions = binary.BigEndian.Uint16(p.raw[10:12]) s.sessionDataUnit = uint32(binary.BigEndian.Uint16(p.raw[12:14])) s.transportDataUnit = uint32(binary.BigEndian.Uint16(p.raw[14:16])) if s.version >= 315 { s.sessionDataUnit = binary.BigEndian.Uint32(p.raw[32:36]) s.transportDataUnit = binary.BigEndian.Uint32(p.raw[36:40]) } if s.transportDataUnit < s.sessionDataUnit { s.sessionDataUnit = s.transportDataUnit } s.acfl0 = p.raw[22] s.acfl1 = p.raw[23] if s.version >= 315 { s.handshakeComplete = true } return nil case oraclePacketRefuse: return oracleRefuseError(p.raw) case oraclePacketRedirect: return errors.New("oracle redirect is not supported by lightweight auth") default: return fmt.Errorf("unexpected oracle packet type %d", p.typ) } } func oracleConnectData(host string, port int, serviceName string) string { address := fmt.Sprintf("(ADDRESS=(PROTOCOL=tcp)(HOST=%s)(PORT=%d))", host, port) connectData := "(CONNECT_DATA=(SERVICE_NAME=" + serviceName + "))" return "(DESCRIPTION=" + address + connectData + ")" } type oraclePacket struct { typ uint8 flag uint8 raw []byte data []byte } func (s *oracleSession) readPacket() (*oraclePacket, error) { header := make([]byte, 8) if err := s.readFull(header); err != nil { return nil, err } var length uint32 if s.handshakeComplete && s.version >= 315 { length = binary.BigEndian.Uint32(header[0:4]) } else { length = uint32(binary.BigEndian.Uint16(header[0:2])) } if length < 8 || length > 16*1024*1024 { return nil, fmt.Errorf("invalid oracle packet length %d", length) } raw := make([]byte, length) copy(raw, header) if err := s.readFull(raw[8:]); err != nil { return nil, err } p := &oraclePacket{typ: raw[4], flag: raw[5], raw: raw} if p.typ == oraclePacketData { if len(raw) < 10 { return nil, errors.New("short oracle data packet") } p.data = raw[10:] s.in = append(s.in, p.data...) } return p, nil } func (s *oracleSession) readFull(buf []byte) error { if s.timeout > 0 { _ = s.conn.SetReadDeadline(time.Now().Add(s.timeout)) } _, err := io.ReadFull(s.conn, buf) return err } func (s *oracleSession) writeRaw(ctx context.Context, buf []byte) error { if deadline, ok := ctx.Deadline(); ok { _ = s.conn.SetWriteDeadline(deadline) } else if s.timeout > 0 { _ = s.conn.SetWriteDeadline(time.Now().Add(s.timeout)) } _, err := s.conn.Write(buf) return err } func (s *oracleSession) writeData() error { payload := s.out.Bytes() if len(payload) == 0 { return s.writeDataPacket(nil, 0) } segmentLen := int(s.sessionDataUnit) - 64 if segmentLen <= 0 { segmentLen = len(payload) } for len(payload) > segmentLen { if err := s.writeDataPacket(payload[:segmentLen], 0); err != nil { s.out.Reset() return err } payload = payload[segmentLen:] } err := s.writeDataPacket(payload, 0) s.out.Reset() return err } func (s *oracleSession) writeDataPacket(payload []byte, dataFlag uint16) error { length := uint32(len(payload) + 10) buf := make([]byte, length) if s.handshakeComplete && s.version >= 315 { binary.BigEndian.PutUint32(buf[0:4], length) } else { binary.BigEndian.PutUint16(buf[0:2], uint16(length)) } buf[4] = oraclePacketData binary.BigEndian.PutUint16(buf[8:10], dataFlag) copy(buf[10:], payload) if s.timeout > 0 { _ = s.conn.SetWriteDeadline(time.Now().Add(s.timeout)) } _, err := s.conn.Write(buf) return err } func (s *oracleSession) reset() { s.in = nil s.out.Reset() s.index = 0 s.summary = nil } func (s *oracleSession) read(n int) ([]byte, error) { for s.index+n > len(s.in) { p, err := s.readPacket() if err != nil { return nil, err } if p.typ == oraclePacketResend { return nil, errors.New("oracle resend is not supported") } if p.typ != oraclePacketData { return nil, fmt.Errorf("expected oracle data packet, got %d", p.typ) } } ret := s.in[s.index : s.index+n] s.index += n return ret, nil } func (s *oracleSession) putBytes(data ...byte) { s.out.Write(data) } func (s *oracleSession) putString(v string) { s.putClr([]byte(v)) } func (s *oracleSession) putInt(v interface{}, size uint8, bigEndian, compress bool) { num := toInt64(v) if compress { neg := num < 0 encoded := uint64(num) if neg { encoded = uint64(-(num + 1)) + 1 } temp := make([]byte, 8) binary.BigEndian.PutUint64(temp, encoded) temp = bytes.TrimLeft(temp, "\x00") if size > uint8(len(temp)) { size = uint8(len(temp)) } if size == 0 { s.out.WriteByte(0) return } if neg { size |= 0x80 } s.out.WriteByte(size) s.out.Write(temp) return } if size == 1 { s.out.WriteByte(uint8(num)) return } temp := make([]byte, size) if bigEndian { switch size { case 2: binary.BigEndian.PutUint16(temp, uint16(num)) case 4: binary.BigEndian.PutUint32(temp, uint32(num)) case 8: binary.BigEndian.PutUint64(temp, uint64(num)) } } else { switch size { case 2: binary.LittleEndian.PutUint16(temp, uint16(num)) case 4: binary.LittleEndian.PutUint32(temp, uint32(num)) case 8: binary.LittleEndian.PutUint64(temp, uint64(num)) } } s.out.Write(temp) } func (s *oracleSession) putUint(v interface{}, size uint8, bigEndian, compress bool) { num := toUint64(v) if size == 1 { s.out.WriteByte(uint8(num)) return } if compress { temp := make([]byte, 8) binary.BigEndian.PutUint64(temp, num) temp = bytes.TrimLeft(temp, "\x00") if size > uint8(len(temp)) { size = uint8(len(temp)) } if size == 0 { s.out.WriteByte(0) return } s.out.WriteByte(size) s.out.Write(temp) return } temp := make([]byte, size) if bigEndian { switch size { case 2: binary.BigEndian.PutUint16(temp, uint16(num)) case 4: binary.BigEndian.PutUint32(temp, uint32(num)) case 8: binary.BigEndian.PutUint64(temp, num) } } else { switch size { case 2: binary.LittleEndian.PutUint16(temp, uint16(num)) case 4: binary.LittleEndian.PutUint32(temp, uint32(num)) case 8: binary.LittleEndian.PutUint64(temp, num) } } s.out.Write(temp) } func (s *oracleSession) putClr(data []byte) { if len(data) > 0xfc { s.out.WriteByte(0xfe) for start := 0; start < len(data); start += s.clrChunkSize { end := start + s.clrChunkSize if end > len(data) { end = len(data) } chunk := data[start:end] if s.useBigClrChunks { s.putInt(len(chunk), 4, true, true) } else { s.out.WriteByte(uint8(len(chunk))) } s.out.Write(chunk) } s.out.WriteByte(0) return } if len(data) == 0 { s.out.WriteByte(0) return } s.out.WriteByte(uint8(len(data))) s.out.Write(data) } func (s *oracleSession) putKeyValString(key, val string, num uint8) { s.putKeyVal([]byte(key), []byte(val), num) } func (s *oracleSession) putKeyVal(key, val []byte, num uint8) { if len(key) == 0 { s.out.WriteByte(0) } else { s.putUint(len(key), 4, true, true) s.putClr(key) } if len(val) == 0 { s.out.WriteByte(0) } else { s.putUint(len(val), 4, true, true) s.putClr(val) } s.putInt(num, 4, true, true) } func (s *oracleSession) getByte() (uint8, error) { b, err := s.read(1) if err != nil { return 0, err } return b[0], nil } func (s *oracleSession) getBytes(n int) ([]byte, error) { return s.read(n) } func (s *oracleSession) getInt(size int, compress, bigEndian bool) (int, error) { v, err := s.getInt64(size, compress, bigEndian) return int(v), err } func (s *oracleSession) getInt64(size int, compress, bigEndian bool) (int64, error) { neg := false if compress { b, err := s.read(1) if err != nil { return 0, err } size = int(b[0]) if size&0x80 != 0 { neg = true size &= 0x7f } bigEndian = true } if size == 0 { return 0, nil } if size > 8 { return 0, fmt.Errorf("invalid oracle integer size %d", size) } b, err := s.read(size) if err != nil { return 0, err } tmp := make([]byte, 8) if bigEndian { copy(tmp[8-size:], b) v := int64(binary.BigEndian.Uint64(tmp)) if neg { v = -v } return v, nil } copy(tmp[:size], b) v := int64(binary.LittleEndian.Uint64(tmp)) if neg { v = -v } return v, nil } func (s *oracleSession) getNullTermString(maxSize int) (string, error) { oldIndex := s.index b, err := s.read(maxSize) if err != nil { return "", err } if i := bytes.IndexByte(b, 0); i >= 0 { s.index = oldIndex + i + 1 return string(b[:i]), nil } return string(b), nil } func (s *oracleSession) getClr() ([]byte, error) { nb, err := s.getByte() if err != nil { return nil, err } if nb == 0 || nb == 0xff || nb == 0xfd { return nil, nil } chunkSize := int(nb) var out bytes.Buffer if chunkSize == 0xfe { for { if s.useBigClrChunks { chunkSize, err = s.getInt(4, true, true) } else { nb, err = s.getByte() chunkSize = int(nb) } if err != nil || chunkSize == 0 { return out.Bytes(), err } chunk, err := s.getBytes(chunkSize) if err != nil { return nil, err } out.Write(chunk) } } chunk, err := s.getBytes(chunkSize) if err != nil { return nil, err } out.Write(chunk) return out.Bytes(), nil } func (s *oracleSession) getDlc() ([]byte, error) { length, err := s.getInt(4, true, true) if err != nil || length <= 0 { return nil, err } out, err := s.getClr() if len(out) > length { out = out[:length] } return out, err } func (s *oracleSession) getKeyVal() ([]byte, []byte, int, error) { key, err := s.getDlc() if err != nil { return nil, nil, 0, err } val, err := s.getDlc() if err != nil { return nil, nil, 0, err } num, err := s.getInt(4, true, true) return key, val, num, err } func toInt64(v interface{}) int64 { switch n := v.(type) { case int: return int64(n) case int16: return int64(n) case int32: return int64(n) case int64: return n case uint8: return int64(n) case uint16: return int64(n) case uint32: return int64(n) case uint64: return int64(n) case uint: return int64(n) default: panic("oracle integer encoder expects an integer") } } func toUint64(v interface{}) uint64 { switch n := v.(type) { case int: return uint64(n) case int16: return uint64(n) case int32: return uint64(n) case int64: return uint64(n) case uint8: return uint64(n) case uint16: return uint64(n) case uint32: return uint64(n) case uint64: return n case uint: return uint64(n) default: panic("oracle integer encoder expects an integer") } } func (s *oracleSession) advancedNegotiation() error { // 按 go-ora 参考实现,构造 ANO 请求 // Service 4 (supervisor): version + cid + servArray // Service 1 (auth): version + UB2(0xE0E1) + status(0xFCFF) // Service 2 (encrypt): version + algorithms([0]=rejected) + UB1(1) // Service 3 (data integrity): version + algorithms([0]=rejected) // 构建 ANO body 到临时 buffer 计算精确 length var ab oracleSession ab.clrChunkSize = s.clrChunkSize // Service 4 (supervisor): cid + service array ab.writeANOServiceHeader(4, 3) ab.writeANOVersion() ab.writeANOBytes([]byte{0, 0, 16, 28, 102, 236, 40, 234}) ab.writeANOUB2Array([]int{4, 1, 2, 3}) // Service 1 (auth): UB2(0xE0E1) + status(0xFCFF) ab.writeANOServiceHeader(1, 3) ab.writeANOVersion() ab.writeANOPacketHeader(2, 3) ab.putInt(0xE0E1, 2, true, false) ab.writeANOStatus(0xfcff) // Service 2 (encrypt): supported algos + driver ab.writeANOServiceHeader(2, 3) ab.writeANOVersion() ab.writeANOBytes([]byte{0, 1, 8, 10, 6, 2, 15, 16, 17}) ab.writeANOUB1(1) // Service 3 (data integrity): supported algos ab.writeANOServiceHeader(3, 2) ab.writeANOVersion() ab.writeANOBytes([]byte{0, 1, 3, 4, 5, 6}) body := ab.out.Bytes() s.reset() s.writeANOHeader(13+len(body), 4, 0) s.putBytes(body...) if err := s.writeData(); err != nil { return err } header, err := s.readANOHeader() if err != nil { return err } for i := 0; i < header.serviceCount; i++ { serviceType, subPackets, errCode, err := s.readANOServiceHeader() if err != nil { return err } if errCode != 0 { return fmt.Errorf("oracle advanced negotiation error ora-%d", errCode) } if err := s.readANOServiceData(serviceType, subPackets); err != nil { return err } } return nil } type oracleANOHeader struct { serviceCount int } func (s *oracleSession) writeANOHeader(length, serviceCount int, flags uint8) { s.putInt(uint64(0xdeadbeef), 4, true, false) s.putInt(length, 2, true, false) s.putInt(0x0b200200, 4, true, false) s.putInt(serviceCount, 2, true, false) s.putBytes(flags) } func (s *oracleSession) writeANOServiceHeader(serviceType, subPackets int) { s.putInt(serviceType, 2, true, false) s.putInt(subPackets, 2, true, false) s.putInt(0, 4, true, false) } func (s *oracleSession) writeANOPacketHeader(length, typ int) { s.putInt(length, 2, true, false) s.putInt(typ, 2, true, false) } func (s *oracleSession) writeANOVersion() { s.writeANOPacketHeader(4, 5) s.putInt(0x0b200200, 4, true, false) } func (s *oracleSession) writeANOStatus(status int) { s.writeANOPacketHeader(2, 6) s.putInt(status, 2, true, false) } func (s *oracleSession) writeANOBytes(b []byte) { s.writeANOPacketHeader(len(b), 1) s.putBytes(b...) } func (s *oracleSession) writeANOUB1(v uint8) { s.writeANOPacketHeader(1, 2) s.putBytes(v) } func (s *oracleSession) writeANOUB2Array(v []int) { s.writeANOPacketHeader(10+len(v)*2, 1) s.putInt(uint64(0xdeadbeef), 4, true, false) s.putInt(3, 2, true, false) s.putInt(len(v), 4, true, false) for _, n := range v { s.putInt(n, 2, true, false) } } func (s *oracleSession) readANOHeader() (*oracleANOHeader, error) { magic, err := s.getInt64(4, false, true) if err != nil { return nil, err } if magic != 0xdeadbeef { return nil, errors.New("oracle advanced negotiation header mismatch") } if _, err = s.getInt(2, false, true); err != nil { return nil, err } if _, err = s.getInt(4, false, true); err != nil { return nil, err } count, err := s.getInt(2, false, true) if err != nil { return nil, err } if _, err = s.getByte(); err != nil { return nil, err } return &oracleANOHeader{serviceCount: count}, nil } func (s *oracleSession) readANOServiceHeader() (int, int, int, error) { serviceType, err := s.getInt(2, false, true) if err != nil { return 0, 0, 0, err } subPackets, err := s.getInt(2, false, true) if err != nil { return 0, 0, 0, err } errCode, err := s.getInt(4, false, true) return serviceType, subPackets, errCode, err } func (s *oracleSession) readANOPacketHeader(expectType int) (int, error) { length, err := s.getInt(2, false, true) if err != nil { return 0, err } typ, err := s.getInt(2, false, true) if err != nil { return 0, err } if typ != expectType { return 0, fmt.Errorf("oracle advanced negotiation type mismatch: %d", typ) } return length, nil } func (s *oracleSession) readANOServiceData(serviceType, subPackets int) error { switch serviceType { case 1: if _, err := s.readANOVersion(); err != nil { return err } status, err := s.readANOStatus() if err != nil { return err } if status == 0xfaff && subPackets > 2 { if _, err = s.readANOUB1(); err != nil { return err } name, err := s.readANOString() if err != nil { return err } if name != "" && name != "TCPS" { return fmt.Errorf("unsupported oracle authentication service %s", name) } } else if status != 0xfbff { return errors.New("oracle advanced authentication negotiation failed") } case 2, 3: if _, err := s.readANOVersion(); err != nil { return err } algo, err := s.readANOUB1() if err != nil { return err } if algo != 0 { return fmt.Errorf("unsupported oracle advanced service algorithm %d", algo) } for i := 2; i < subPackets; i++ { if err := s.skipANOPacket(); err != nil { return err } } case 4: if _, err := s.readANOVersion(); err != nil { return err } if _, err := s.readANOStatus(); err != nil { return err } _, err := s.readANOBytes() return err default: for i := 0; i < subPackets; i++ { if err := s.skipANOPacket(); err != nil { return err } } } return nil } func (s *oracleSession) readANOVersion() (uint32, error) { if _, err := s.readANOPacketHeader(5); err != nil { return 0, err } v, err := s.getInt(4, false, true) return uint32(v), err } func (s *oracleSession) readANOStatus() (int, error) { if _, err := s.readANOPacketHeader(6); err != nil { return 0, err } return s.getInt(2, false, true) } func (s *oracleSession) readANOUB1() (uint8, error) { if _, err := s.readANOPacketHeader(2); err != nil { return 0, err } return s.getByte() } func (s *oracleSession) readANOString() (string, error) { length, err := s.readANOPacketHeader(0) if err != nil { return "", err } b, err := s.getBytes(length) return string(b), err } func (s *oracleSession) readANOBytes() ([]byte, error) { length, err := s.readANOPacketHeader(1) if err != nil { return nil, err } return s.getBytes(length) } func (s *oracleSession) skipANOPacket() error { length, err := s.getInt(2, false, true) if err != nil { return err } if _, err = s.getInt(2, false, true); err != nil { return err } if length > 0 { _, err = s.getBytes(length) } return err } func (s *oracleSession) protocolNegotiation() (*oracleTCPNego, error) { s.reset() s.putBytes(1, 6, 0) s.putBytes([]byte("OracleClientGo\x00")...) if err := s.writeData(); err != nil { return nil, err } msg, err := s.getByte() if err != nil { return nil, err } if msg != 1 { return nil, fmt.Errorf("oracle protocol negotiation expected message 1, got %d", msg) } proto, err := s.getByte() if err != nil { return nil, err } if proto != 4 && proto != 5 && proto != 6 { return nil, errors.New("unsupported oracle server protocol version") } if _, err = s.getByte(); err != nil { return nil, err } if _, err = s.getNullTermString(50); err != nil { return nil, err } serverCharset, err := s.getInt(2, false, false) if err != nil { return nil, err } serverFlags, err := s.getByte() if err != nil { return nil, err } charsetElem, err := s.getInt(2, false, false) if err != nil { return nil, err } if charsetElem > 0 { if _, err = s.getBytes(charsetElem * 5); err != nil { return nil, err } } len1, err := s.getInt(2, false, true) if err != nil { return nil, err } numArray, err := s.getBytes(len1) if err != nil { return nil, err } if len(numArray) < 11 { return nil, errors.New("short oracle charset negotiation") } offset := int(6 + numArray[5] + numArray[6]) if len(numArray) < offset+5 { return nil, errors.New("short oracle ncharset negotiation") } serverNCharset := int(binary.BigEndian.Uint16(numArray[offset+3 : offset+5])) len2, err := s.getByte() if err != nil { return nil, err } compileCaps, err := s.getBytes(int(len2)) if err != nil { return nil, err } len3, err := s.getByte() if err != nil { return nil, err } runtimeCaps, err := s.getBytes(int(len3)) if err != nil { return nil, err } if len(compileCaps) < 8 { return nil, errors.New("oracle server compile caps too short") } if len(compileCaps) > 15 && compileCaps[15]&1 != 0 { s.hasEOSCapability = true } if len(compileCaps) > 16 && compileCaps[16]&1 != 0 { s.hasFSAPCapability = true } if len(compileCaps) > 37 && compileCaps[37]&32 != 0 { s.useBigClrChunks = true s.clrChunkSize = 0x7fff } return &oracleTCPNego{ serverCharset: serverCharset, serverFlags: serverFlags | 2, serverNCharset: serverNCharset, serverCompileTimeCaps: compileCaps, serverRuntimeCaps: runtimeCaps, }, nil } func (s *oracleSession) dataTypeNegotiation(nego *oracleTCPNego) error { compileCaps := []byte{ 6, 1, 0, 0, 106, 1, 1, 11, 1, 1, 1, 1, 1, 1, 0, 41, 144, 3, 7, 3, 0, 1, 0, 235, 1, 0, 5, 1, 0, 0, 0, 24, 0, 0, 7, 32, 2, 58, 0, 0, 5, 0, 0, 0, 8, } if len(nego.serverCompileTimeCaps) <= 27 || nego.serverCompileTimeCaps[27] == 0 { compileCaps[27] = 0 } if len(nego.serverCompileTimeCaps) > 7 && nego.serverCompileTimeCaps[7] < 7 { compileCaps[36] = 0 } if len(nego.serverCompileTimeCaps) <= 37 || nego.serverCompileTimeCaps[37]&2 != 2 { compileCaps[37] = 0 compileCaps[1] = 0 } runtimeCaps := []byte{2, 1, 0, 0, 0, 0, 0} if len(nego.serverRuntimeCaps) < 2 || nego.serverRuntimeCaps[1]&1 != 1 { runtimeCaps[1] = 0 } if len(nego.serverRuntimeCaps) > 6 { if nego.serverRuntimeCaps[6]&4 == 4 { runtimeCaps[6] |= 4 } if nego.serverRuntimeCaps[6]&2 == 2 { runtimeCaps[6] |= 2 } } typeReps := oracleTypeReps(nego, compileCaps) s.reset() s.putBytes(2) s.putInt(nego.serverCharset, 2, false, false) s.putInt(nego.serverCharset, 2, false, false) s.putBytes(nego.serverFlags, uint8(len(compileCaps))) s.putBytes(compileCaps...) s.putBytes(uint8(len(runtimeCaps))) s.putBytes(runtimeCaps...) if runtimeCaps[1]&1 == 1 { s.putBytes(oracleTZBytes()...) if compileCaps[37]&2 == 2 { s.putInt(0x20, 4, true, false) } } s.putInt(nego.serverNCharset, 2, false, false) if compileCaps[27] == 0 { for _, v := range typeReps { s.putBytes(uint8(v)) } s.putBytes(0) } else { for _, v := range typeReps { s.putInt(v, 2, true, false) } s.putBytes(0, 0) } if err := s.writeData(); err != nil { return err } msg, err := s.getByte() if err != nil { return err } if msg != 2 { return fmt.Errorf("oracle data type negotiation expected message 2, got %d", msg) } if runtimeCaps[1] == 1 { if _, err = s.getBytes(11); err != nil { return err } if compileCaps[37]&2 == 2 { if _, err = s.getInt(4, false, true); err != nil { return err } } } level := 0 for { var n int if compileCaps[27] == 0 { n, err = s.getInt(1, false, false) } else { n, err = s.getInt(2, false, true) } if err != nil { return err } if n == 0 && level == 0 { break } if n == 0 && level == 1 { level = 0 continue } if level == 3 { level = 0 continue } level++ } if len(compileCaps) > 7 && len(nego.serverCompileTimeCaps) > 7 { s.ttcVersion = compileCaps[7] if nego.serverCompileTimeCaps[7] < s.ttcVersion { s.ttcVersion = nego.serverCompileTimeCaps[7] } } return nil } func oracleTypeReps(nego *oracleTCPNego, compileCaps []byte) []int16 { reps := make([]int16, 0, 96) add := func(dty, ndty, rep int16) { reps = append(reps, dty, ndty) if ndty != 0 { reps = append(reps, rep, 0) } } add(1, 1, oracleTypeRepUniversal) // NCHAR add(2, 2, oracleTypeRepOracle) // NUMBER add(8, 8, oracleTypeRepUniversal) // LONG add(12, 12, oracleTypeRepOracle) // DATE add(23, 23, oracleTypeRepUniversal) // RAW add(24, 24, oracleTypeRepUniversal) // LONG RAW add(25, 25, oracleTypeRepUniversal) add(26, 26, oracleTypeRepUniversal) add(27, 27, oracleTypeRepUniversal) add(28, 28, oracleTypeRepUniversal) add(29, 29, oracleTypeRepUniversal) add(30, 30, oracleTypeRepUniversal) add(31, 31, oracleTypeRepUniversal) add(32, 32, oracleTypeRepUniversal) add(33, 33, oracleTypeRepUniversal) add(10, 10, oracleTypeRepUniversal) add(11, 11, oracleTypeRepUniversal) // ROWID add(3, 2, oracleTypeRepOracle) add(4, 2, oracleTypeRepOracle) add(5, 1, oracleTypeRepUniversal) add(6, 2, oracleTypeRepOracle) add(7, 2, oracleTypeRepOracle) add(9, 1, oracleTypeRepUniversal) // VARCHAR add(15, 1, oracleTypeRepUniversal) add(39, 120, oracleTypeRepUniversal) add(58, 0, oracleTypeRepNative) add(68, 2, oracleTypeRepOracle) add(69, 0, oracleTypeRepNative) add(70, 0, oracleTypeRepNative) add(74, 0, oracleTypeRepNative) add(76, 0, oracleTypeRepNative) add(91, 2, oracleTypeRepOracle) add(94, 1, oracleTypeRepUniversal) add(95, 23, oracleTypeRepUniversal) add(96, 96, oracleTypeRepUniversal) add(97, 96, oracleTypeRepUniversal) add(100, 100, oracleTypeRepUniversal) add(101, 101, oracleTypeRepUniversal) add(102, 102, oracleTypeRepUniversal) add(104, 11, oracleTypeRepUniversal) add(105, 0, oracleTypeRepNative) add(106, 106, oracleTypeRepUniversal) add(112, 112, oracleTypeRepUniversal) add(113, 113, oracleTypeRepUniversal) add(114, 114, oracleTypeRepUniversal) add(115, 115, oracleTypeRepUniversal) add(116, 102, oracleTypeRepUniversal) add(118, 0, oracleTypeRepNative) add(119, 119, oracleTypeRepNative) // JSON add(127, 127, oracleTypeRepUniversal) add(208, 208, oracleTypeRepUniversal) _ = nego _ = compileCaps return reps } func oracleTZBytes() []byte { _, offset := time.Now().Zone() hours := int8(offset / 3600) minutes := int8((offset / 60) % 60) seconds := int8(offset % 60) return []byte{128, 0, 0, 0, uint8(hours + 60), uint8(minutes + 60), uint8(seconds + 60), 128, 0, 0, 0} } func (s *oracleSession) authenticate(nego *oracleTCPNego, host string, port int, serviceName, username, password string) error { s.reset() s.putBytes(3, 0x76, 0, 1) s.putUint(len(username), 4, true, true) s.putUint(oracleNoNewPass, 4, true, true) s.putBytes(1, 1, 5, 1, 1) if username != "" { s.putString(username) } s.putKeyValString("AUTH_TERMINAL", "", 0) s.putKeyValString("AUTH_PROGRAM_NM", "", 0) s.putKeyValString("AUTH_MACHINE", "", 0) s.putKeyValString("AUTH_PID", "0", 0) s.putKeyValString("AUTH_SID", "", 0) if err := s.writeData(); err != nil { return err } auth, err := s.readAuthChallenge(username, password, nego) if err != nil { return err } return s.writeAuthResponse(auth, nego, host, port, serviceName, username) } type oracleAuthObject struct { eServerSessKey string eClientSessKey string ePassword string eSpeedyKey string serverSessKey []byte clientSessKey []byte salt string pbkdf2ChkSalt string pbkdf2VgenCount int pbkdf2SderCount int verifierType int customHash bool } func (s *oracleSession) readAuthChallenge(username, password string, nego *oracleTCPNego) (*oracleAuthObject, error) { auth := &oracleAuthObject{customHash: len(nego.serverCompileTimeCaps) > 4 && nego.serverCompileTimeCaps[4]&32 != 0} for { msg, err := s.getByte() if err != nil { return nil, err } switch msg { case 8: dictLen, err := s.getInt(4, true, true) if err != nil { return nil, err } for i := 0; i < dictLen; i++ { key, val, num, err := s.getKeyVal() if err != nil { return nil, err } switch string(key) { case "AUTH_SESSKEY": if auth.eServerSessKey == "" { auth.eServerSessKey = string(val) } case "AUTH_VFR_DATA": if auth.salt == "" { auth.salt = string(val) auth.verifierType = num } case "AUTH_PBKDF2_CSK_SALT": auth.pbkdf2ChkSalt = string(val) if len(auth.pbkdf2ChkSalt) != 32 { return nil, errors.New("oracle authentication protocol internal error") } case "AUTH_PBKDF2_VGEN_COUNT": auth.pbkdf2VgenCount, _ = strconv.Atoi(string(val)) if auth.pbkdf2VgenCount < 4096 || auth.pbkdf2VgenCount > 100000000 { auth.pbkdf2VgenCount = 4096 } case "AUTH_PBKDF2_SDER_COUNT": auth.pbkdf2SderCount, _ = strconv.Atoi(string(val)) if auth.pbkdf2SderCount < 3 || auth.pbkdf2SderCount > 100000000 { auth.pbkdf2SderCount = 3 } } } default: err := s.readMsg(msg) if err != nil { return nil, err } if msg == 4 { if s.hasError() { return nil, s.oracleError() } return auth.finish(username, password, nego) } } } } func (auth *oracleAuthObject) finish(username, password string, nego *oracleTCPNego) (*oracleAuthObject, error) { if len(auth.eServerSessKey) != 64 && len(auth.eServerSessKey) != 96 { return nil, errors.New("oracle session key should be either 64 or 96 bytes long") } var key []byte var speedyKey []byte padding := false var err error switch auth.verifierType { case 2361: key, err = oracleKeyFromUserPass(username, password) case 6949: if len(nego.serverCompileTimeCaps) > 4 && nego.serverCompileTimeCaps[4]&2 == 0 { padding = true } salt, err := hex.DecodeString(auth.salt) if err != nil { return nil, err } h := sha1.New() _, _ = h.Write(append([]byte(password), salt...)) key = append(h.Sum(nil), 0, 0, 0, 0) case 18453: salt, err := hex.DecodeString(auth.salt) if err != nil { return nil, err } message := append(salt, []byte("AUTH_PBKDF2_SPEEDY_KEY")...) speedyKey = oracleGenerateSpeedyKey(message, []byte(password), auth.pbkdf2VgenCount) h := sha512.New() _, _ = h.Write(append(speedyKey, salt...)) key = h.Sum(nil)[:32] default: return nil, fmt.Errorf("unsupported oracle verifier type %d", auth.verifierType) } if err != nil { return nil, err } auth.serverSessKey, err = oracleDecryptSessionKey(padding, key, auth.eServerSessKey) if err != nil { return nil, err } auth.clientSessKey = make([]byte, len(auth.serverSessKey)) for { if _, err = rand.Read(auth.clientSessKey); err != nil { return nil, err } if !bytes.Equal(auth.clientSessKey, auth.serverSessKey) { break } } auth.eClientSessKey, err = oracleEncryptSessionKey(padding, key, auth.clientSessKey) if err != nil { return nil, err } newKey, err := auth.passwordEncKey(nego) if err != nil { return nil, err } auth.ePassword, err = oracleEncryptPassword([]byte(password), newKey, true) if err != nil { return nil, err } if auth.verifierType == 18453 { auth.eSpeedyKey, err = oracleEncryptPassword(speedyKey, newKey, false) if err != nil { return nil, err } } return auth, nil } func (s *oracleSession) writeAuthResponse(auth *oracleAuthObject, nego *oracleTCPNego, host string, port int, serviceName, username string) error { keys := []struct { key string val string flag uint8 }{ {"AUTH_SESSKEY", auth.eClientSessKey, 1}, {"AUTH_PASSWORD", auth.ePassword, 0}, } if auth.eSpeedyKey != "" { keys = append(keys, struct { key string val string flag uint8 }{"AUTH_PBKDF2_SPEEDY_KEY", auth.eSpeedyKey, 0}) } keys = append(keys, struct { key string val string flag uint8 }{"AUTH_TERMINAL", "", 0}, struct { key string val string flag uint8 }{"AUTH_PROGRAM_NM", "", 0}, struct { key string val string flag uint8 }{"AUTH_MACHINE", "", 0}, struct { key string val string flag uint8 }{"AUTH_PID", "0", 0}, struct { key string val string flag uint8 }{"AUTH_SID", "", 0}, struct { key string val string flag uint8 }{"AUTH_CONNECT_STRING", oracleConnectData(host, port, serviceName), 0}, struct { key string val string flag uint8 }{"SESSION_CLIENT_CHARSET", strconv.Itoa(nego.serverCharset), 0}, struct { key string val string flag uint8 }{"SESSION_CLIENT_LIB_TYPE", "0", 0}, struct { key string val string flag uint8 }{"SESSION_CLIENT_DRIVER_NAME", "fscan", 0}, struct { key string val string flag uint8 }{"SESSION_CLIENT_VERSION", "2.0.0.0", 0}, struct { key string val string flag uint8 }{"SESSION_CLIENT_LOBATTR", "1", 0}, struct { key string val string flag uint8 }{"AUTH_ALTER_SESSION", oracleAlterSession(), 1}, ) s.reset() s.putBytes(3, 0x73, 0) if username != "" { s.putBytes(1) s.putInt(len(username), 4, true, true) } else { s.putBytes(0, 0) } s.putUint(oracleUserAndPass|oracleNoNewPass, 4, true, true) s.putBytes(1) s.putUint(len(keys), 4, true, true) s.putBytes(1, 1) if username != "" { s.putString(username) } for _, kv := range keys { if kv.val == "" && (kv.key == "AUTH_SESSKEY" || kv.key == "AUTH_PASSWORD") { continue } s.putKeyValString(kv.key, kv.val, kv.flag) } if err := s.writeData(); err != nil { return err } for { msg, err := s.getByte() if err != nil { return err } if err := s.readMsg(msg); err != nil { return err } if msg == 4 || msg == 9 { if s.hasError() { err := s.oracleError() if classifyOracleErrorType(err) == ErrorTypeAuth { return fmt.Errorf("%w: %v", errOracleAuthFailed, err) } return err } return nil } } } func oracleAlterSession() string { _, offset := time.Now().Zone() hours := int8(offset / 3600) minutes := int8((offset / 60) % 60) if minutes < 0 { minutes = -minutes } tz := fmt.Sprintf("%+03d:%02d", hours, minutes) return fmt.Sprintf("ALTER SESSION SET NLS_LANGUAGE='AMERICAN' NLS_TERRITORY='AMERICA' TIME_ZONE='%s'\x00", tz) } func (s *oracleSession) readMsg(msg uint8) error { switch msg { case 4: sum, err := s.readSummary() if err != nil { return err } s.summary = sum case 8: size, err := s.getInt(2, true, true) if err != nil { return err } for i := 0; i < size; i++ { if _, err = s.getInt(4, true, true); err != nil { return err } } if _, err = s.getInt(2, true, true); err != nil { return err } size, err = s.getInt(2, true, true) if err != nil { return err } for i := 0; i < size; i++ { if _, _, _, err = s.getKeyVal(); err != nil { return err } } if s.ttcVersion >= 4 { qLen, err := s.getInt(4, true, true) if err != nil { return err } if qLen > 0 { if _, err = s.getBytes(qLen); err != nil { return err } } } if s.ttcVersion >= 7 { length, err := s.getInt(4, true, true) if err != nil { return err } for i := 0; i < length; i++ { if _, err = s.getInt(8, true, true); err != nil { return err } } } case 9: if s.hasEOSCapability { if _, err := s.getInt(4, true, true); err != nil { return err } } if s.hasFSAPCapability { if _, err := s.getInt(2, true, true); err != nil { return err } } case 15: if _, err := s.getInt(2, true, true); err != nil { return err } length, err := s.getInt(2, true, true) if err != nil { return err } if _, err = s.getInt(2, true, true); err != nil { return err } if length > 0 { _, err = s.getClr() return err } default: return fmt.Errorf("oracle TTC unexpected message %d", msg) } return nil } func (s *oracleSession) readSummary() (*oracleSummary, error) { sum := &oracleSummary{} var err error if s.hasEOSCapability { if _, err = s.getInt(4, true, true); err != nil { return nil, err } } if s.ttcVersion >= 3 && s.hasFSAPCapability { if _, err = s.getInt(2, true, true); err != nil { return nil, err } } if _, err = s.getInt(4, true, true); err != nil { return nil, err } sum.retCode, err = s.getInt(2, true, true) if err != nil { return nil, err } fields := []struct { size int compress bool }{ {2, true}, {2, true}, {2, true}, {2, true}, } for _, f := range fields { if _, err = s.getInt(f.size, f.compress, true); err != nil { return nil, err } } if _, err = s.getByte(); err != nil { return nil, err } if _, err = s.getByte(); err != nil { return nil, err } if s.ttcVersion >= 4 { if _, err = s.getInt(2, true, true); err != nil { return nil, err } if _, err = s.getInt(2, true, true); err != nil { return nil, err } } else { if _, err = s.getByte(); err != nil { return nil, err } if _, err = s.getByte(); err != nil { return nil, err } } for _, size := range []int{1, 1, 4, 2, 1, 4, 2, 4, 1, 1, 2, 4} { if size == 1 { if _, err = s.getByte(); err != nil { return nil, err } } else { if _, err = s.getInt(size, true, true); err != nil { return nil, err } } } _, _ = s.getDlc() if s.ttcVersion < 7 { _, _ = s.getDlc() _, _ = s.getDlc() _, _ = s.getDlc() } else { if err = s.skipSummaryBindBlocks(); err != nil { return nil, err } sum.retCode, err = s.getInt(4, true, true) if err != nil { return nil, err } if _, err = s.getInt(8, true, true); err != nil { return nil, err } } if sum.retCode != 0 { sum.errorMessage, err = s.getClr() if err != nil { return nil, err } } return sum, nil } func (s *oracleSession) skipSummaryBindBlocks() error { length, err := s.getInt(2, true, true) if err != nil { return err } if length > 0 { flag, err := s.getByte() if err != nil { return err } chunked := flag == 0xfe for i := 0; i < length; i++ { if chunked { if s.useBigClrChunks { if _, err = s.getInt(4, true, true); err != nil { return err } } else if _, err = s.getByte(); err != nil { return err } } if _, err = s.getInt(2, true, true); err != nil { return err } } if chunked { if _, err = s.getByte(); err != nil { return err } } } length, err = s.getInt(4, true, true) if err != nil { return err } if length > 0 { flag, err := s.getByte() if err != nil { return err } chunked := flag == 0xfe for i := 0; i < length; i++ { if chunked { if s.useBigClrChunks { if _, err = s.getInt(4, true, true); err != nil { return err } } else if _, err = s.getByte(); err != nil { return err } } if _, err = s.getInt(4, true, true); err != nil { return err } } if chunked { if _, err = s.getByte(); err != nil { return err } } } length, err = s.getInt(2, true, true) if err != nil { return err } for i := 0; i < length; i++ { if _, err = s.getByte(); err != nil { return err } if _, err = s.getInt(2, true, true); err != nil { return err } if _, err = s.getClr(); err != nil { return err } if _, err = s.getByte(); err != nil { return err } if _, err = s.getByte(); err != nil { return err } } return nil } func (s *oracleSession) hasError() bool { return s.summary != nil && s.summary.retCode != 0 && s.summary.retCode != 1403 } func (s *oracleSession) oracleError() error { if s.summary == nil { return errors.New("oracle error") } msg := string(s.summary.errorMessage) if msg == "" { msg = fmt.Sprintf("ORA-%05d", s.summary.retCode) } return fmt.Errorf("%s", msg) } func oracleRefuseError(raw []byte) error { if len(raw) < 12 { return errors.New("oracle connection refused") } dataLen := int(binary.BigEndian.Uint16(raw[10:12])) if len(raw) < 12+dataLen { return errors.New("oracle connection refused") } msg := string(raw[12 : 12+dataLen]) code := oracleExtractCode(msg) if code == 0 { return fmt.Errorf("oracle connection refused: %s", msg) } return fmt.Errorf("ORA-%05d: %s", code, msg) } func oracleExtractCode(msg string) int { upper := strings.ToUpper(msg) for _, marker := range []string{"ERR=", "CODE="} { idx := strings.Index(upper, marker) if idx < 0 { continue } idx += len(marker) for idx < len(upper) && (upper[idx] < '0' || upper[idx] > '9') { idx++ } start := idx for idx < len(upper) && upper[idx] >= '0' && upper[idx] <= '9' { idx++ } if start < idx { code, _ := strconv.Atoi(upper[start:idx]) return code } } return 0 } func oracleGenerateSpeedyKey(buffer, key []byte, turns int) []byte { mac := hmac.New(sha512.New, key) mac.Write(append(buffer, 0, 0, 0, 1)) firstHash := mac.Sum(nil) tempHash := make([]byte, len(firstHash)) copy(tempHash, firstHash) for i := 2; i <= turns; i++ { mac.Reset() mac.Write(tempHash) tempHash = mac.Sum(nil) for j := 0; j < 64; j++ { firstHash[j] ^= tempHash[j] } } return firstHash } func oracleKeyFromUserPass(username, password string) ([]byte, error) { username = strings.ToUpper(username) password = strings.ToUpper(password) extend := func(s string) []byte { out := make([]byte, len(s)*2) for i, c := range []byte(s) { out[i*2] = 0 out[i*2+1] = c } return out } buf := append(extend(username), extend(password)...) if len(buf)%8 != 0 { buf = append(buf, make([]byte, 8-len(buf)%8)...) } desEnc := func(input, key []byte) ([]byte, error) { ret := make([]byte, 8) enc, err := des.NewCipher(key) if err != nil { return nil, err } for i := 0; i < len(input)/8; i++ { for j := 0; j < 8; j++ { ret[j] ^= input[i*8+j] } out := make([]byte, 8) enc.Encrypt(out, ret) copy(ret, out) } return ret, nil } key1, err := desEnc(buf, []byte{1, 35, 69, 103, 137, 171, 205, 239}) if err != nil { return nil, err } key2, err := desEnc(buf, key1) if err != nil { return nil, err } return append(key2, make([]byte, 8)...), nil } func oracleDecryptSessionKey(padding bool, encKey []byte, sessionKey string) ([]byte, error) { data, err := hex.DecodeString(sessionKey) if err != nil { return nil, err } blk, err := aes.NewCipher(encKey) if err != nil { return nil, err } out := make([]byte, len(data)) cipher.NewCBCDecrypter(blk, make([]byte, 16)).CryptBlocks(out, data) cut := 0 if padding && len(out) > 0 { n := int(out[len(out)-1]) if n < blk.BlockSize() && n <= len(out) { ok := true for i := len(out) - n; i < len(out); i++ { if out[i] != byte(n) { ok = false break } } if ok { cut = n } } } return out[:len(out)-cut], nil } func oracleEncryptSessionKey(padding bool, encKey, sessionKey []byte) (string, error) { blk, err := aes.NewCipher(encKey) if err != nil { return "", err } origLen := len(sessionKey) sessionKey = oraclePKCS5Padding(sessionKey, blk.BlockSize()) out := make([]byte, len(sessionKey)) cipher.NewCBCEncrypter(blk, make([]byte, 16)).CryptBlocks(out, sessionKey) if !padding { return fmt.Sprintf("%X", out[:origLen]), nil } return fmt.Sprintf("%X", out), nil } func oracleEncryptPassword(password, key []byte, padding bool) (string, error) { prefix := make([]byte, 0x10) if _, err := rand.Read(prefix); err != nil { return "", err } return oracleEncryptSessionKey(padding, key, append(prefix, password...)) } func (auth *oracleAuthObject) passwordEncKey(nego *oracleTCPNego) ([]byte, error) { hash := md5.New() key1 := auth.serverSessKey key2 := auth.clientSessKey start := 16 if len(nego.serverCompileTimeCaps) > 4 && nego.serverCompileTimeCaps[4]&32 != 0 { var keyBuffer string var retLen int switch auth.verifierType { case 2361: keyBuffer = fmt.Sprintf("%X", append(key2[:len(key2)/2], key1[:len(key1)/2]...)) retLen = 16 case 6949: keyBuffer = fmt.Sprintf("%X", append(key2[:24], key1[:24]...)) retLen = 24 case 18453: keyBuffer = fmt.Sprintf("%X", append(key2, key1...)) retLen = 32 default: return nil, errors.New("unsupported oracle verifier type") } df2key, err := hex.DecodeString(auth.pbkdf2ChkSalt) if err != nil { return nil, err } return oracleGenerateSpeedyKey(df2key, []byte(keyBuffer), auth.pbkdf2SderCount)[:retLen], nil } switch auth.verifierType { case 2361: buf := make([]byte, 16) for i := 0; i < 16; i++ { buf[i] = key1[i+start] ^ key2[i+start] } _, _ = hash.Write(buf) return hash.Sum(nil), nil case 6949: buf := make([]byte, 24) for i := 0; i < 24; i++ { buf[i] = key1[i+start] ^ key2[i+start] } _, _ = hash.Write(buf[:16]) ret := hash.Sum(nil) hash.Reset() _, _ = hash.Write(buf[16:]) ret = append(ret, hash.Sum(nil)...) return ret[:24], nil default: return nil, errors.New("unsupported oracle verifier type") } } func oraclePKCS5Padding(src []byte, blockSize int) []byte { padding := blockSize - len(src)%blockSize return append(src, bytes.Repeat([]byte{byte(padding)}, padding)...) }