github.com/chain5j/chain5j-pkg@v1.0.7/crypto/icap/icap.go (about) 1 // Package icap 2 // 3 // @author: xwc1125 4 // @date: 2021/4/14 5 package icap 6 7 import ( 8 "errors" 9 "log" 10 "math/big" 11 "strconv" 12 "strings" 13 14 "github.com/chain5j/chain5j-pkg/crypto/base/base36" 15 "github.com/chain5j/chain5j-pkg/types" 16 ) 17 18 var ( 19 errICAPLength = errors.New("invalid ICAP length") 20 errICAPEncoding = errors.New("invalid ICAP encoding") 21 errICAPChecksum = errors.New("invalid ICAP checksum") 22 errICAPCountryCode = errors.New("invalid ICAP country code") 23 errICAPAssetIdent = errors.New("invalid ICAP asset identifier") 24 errICAPInstCode = errors.New("invalid ICAP institution code") 25 errICAPClientIdent = errors.New("invalid ICAP client identifier") 26 ) 27 28 var ( 29 Big1 = big.NewInt(1) 30 Big97 = big.NewInt(97) 31 Big98 = big.NewInt(98) 32 ) 33 34 func ToICAP(customer Customer) (*IBanInfo, error) { 35 enc := base36.EncodeBytes(customer.Customer()) 36 currencyLen := len(customer.Currency()) 37 orgCodeLen := len(customer.OrgCode()) 38 // 减去2位校验码 39 interLen := customer.ResultLen() - currencyLen - orgCodeLen - 2 - len(enc) 40 if interLen > 0 { 41 enc = join(strings.Repeat("0", interLen), enc) 42 } 43 icap := join(customer.Currency(), checkDigits(customer.Currency(), customer.OrgCode(), enc), customer.OrgCode(), enc) 44 return NewIBanInfo(currencyLen, orgCodeLen, len(customer.customer), icap), nil 45 } 46 47 func ParseICAP(iban IBanInfo) (*Customer, error) { 48 if err := ValidCheckSumWithLen(iban.currencyLen, iban.orgCodeLen, iban.iban); err != nil { 49 return nil, err 50 } 51 // checksum is ISO13616, Ethereum address is base-36 52 l := iban.currencyLen + 2 + iban.orgCodeLen 53 // bigAddr, _ := new(big.Int).SetString(iban.iban[l:], 36) 54 // hex := hexutil.EncodeBig(bigAddr) 55 bytes := base36.DecodeToBytes(true, iban.iban[l:]) 56 return &Customer{ 57 currency: iban.iban[:iban.currencyLen], 58 orgCode: iban.iban[iban.currencyLen+2 : iban.currencyLen+2+iban.orgCodeLen], 59 resultLen: len(bytes) + 2, 60 customer: bytes, 61 }, nil 62 } 63 64 //export ConvertAddressToICAP 65 func ConvertAddressToICAP(prefix string, orgCode string, a types.Address) string { 66 prefix = strings.ToUpper(prefix) 67 enc := base36.EncodeBytes(a.Bytes()) 68 // zero padd encoded address to Direct ICAP length if needed 69 if len(enc) < 31 { 70 enc = join(strings.Repeat("0", 31-len(enc)), enc) 71 } 72 icap := join(prefix, checkDigits(prefix, orgCode, enc), orgCode, enc) 73 74 l := 31 + len(orgCode) + len(prefix) + 2 75 if len(icap) != l { 76 log.Println("生成的地址出错", "addr", a.Hex()) 77 } 78 return strings.ToLower(icap) 79 } 80 81 //export ConvertICAPToAddress 82 func ConvertICAPToAddress(prefix string, orgCodeLen int, s string) (types.Address, error) { 83 prefix = strings.ToUpper(prefix) 84 s = strings.ToUpper(s) 85 l := 31 + orgCodeLen + len(prefix) + 2 86 switch len(s) { 87 case 35: // "XE" + 2 digit checksum + 31 base-36 chars of address 88 return parseICAP(prefix, s) 89 case 34: // "XE" + 2 digit checksum + 30 base-36 chars of address 90 return parseICAP(prefix, s) 91 case 20: // "XE" + 2 digit checksum + 3-char asset identifier + 92 // 4-char institution identifier + 9-char institution client identifier 93 return parseIndirectICAP(prefix, s) 94 case l: // "prefix" + 2 digit checksum + orgCodeLen + 3-char asset identifier + 95 // 4-char institution identifier + 9-char institution client identifier 96 return parseSelfICAP(prefix, orgCodeLen, s) 97 default: 98 return types.Address{}, errICAPLength 99 } 100 } 101 func parseSelfICAP(prefix string, orgCodeLen int, s string) (types.Address, error) { 102 if !strings.HasPrefix(s, prefix) { 103 return types.Address{}, errICAPCountryCode 104 } 105 if orgCodeLen > 0 { 106 if err := ValidCheckSumWithLen(len(prefix), orgCodeLen, s); err != nil { 107 return types.Address{}, err 108 } 109 } else { 110 if err := validCheckSum(s); err != nil { 111 return types.Address{}, err 112 } 113 } 114 // checksum is ISO13616, Ethereum address is base-36 115 l := len(prefix) + 2 + orgCodeLen 116 bigAddr, _ := new(big.Int).SetString(s[l:], 36) 117 return types.BigToAddress(bigAddr), nil 118 } 119 func parseICAP(prefix string, s string) (types.Address, error) { 120 if !strings.HasPrefix(s, prefix) { 121 return types.Address{}, errICAPCountryCode 122 } 123 if err := validCheckSum(s); err != nil { 124 return types.Address{}, err 125 } 126 // checksum is ISO13616, Ethereum address is base-36 127 bigAddr, _ := new(big.Int).SetString(s[4:], 36) 128 return types.BigToAddress(bigAddr), nil 129 } 130 func parseIndirectICAP(prefix string, s string) (types.Address, error) { 131 if !strings.HasPrefix(s, prefix) { 132 return types.Address{}, errICAPCountryCode 133 } 134 if s[4:7] != "ETH" { 135 return types.Address{}, errICAPAssetIdent 136 } 137 if err := validCheckSum(s); err != nil { 138 return types.Address{}, err 139 } 140 return types.Address{}, errors.New("not implemented") 141 } 142 143 // https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN 144 func validCheckSum(s string) error { 145 s = join(s[4:], s[:4]) 146 expanded, err := iso13616Expand(s) 147 if err != nil { 148 return err 149 } 150 checkSumNum, _ := new(big.Int).SetString(expanded, 10) 151 if checkSumNum.Mod(checkSumNum, Big97).Cmp(Big1) != 0 { 152 return errICAPChecksum 153 } 154 return nil 155 } 156 157 func ValidCheckSumWithLen(prefixLen, orgCodeLen int, s string) error { 158 // s=prefix+check+orgCode+addr 159 l := prefixLen + 2 160 s1 := join(s[l:], s[:prefixLen], s[prefixLen:prefixLen+2]) // orgCode+addr+prefix+check 161 expanded, err := iso13616Expand(s1) 162 if err != nil { 163 return err 164 } 165 checkSumNum, _ := new(big.Int).SetString(expanded, 10) 166 if checkSumNum.Mod(checkSumNum, Big97).Cmp(Big1) != 0 { 167 return errICAPChecksum 168 } 169 return nil 170 } 171 172 func checkDigits(prefix, orgCode, s string) string { 173 prefix = strings.ToUpper(prefix) 174 expanded, _ := iso13616Expand(join(orgCode, s, prefix+"00")) // orgCode+addr+prefix+00 175 num, _ := new(big.Int).SetString(expanded, 10) 176 num.Sub(Big98, num.Mod(num, Big97)) 177 178 checkDigits := num.String() 179 // zero padd checksum 180 if len(checkDigits) == 1 { 181 checkDigits = join("0", checkDigits) 182 } 183 return checkDigits 184 } 185 186 // not base-36, but expansion to decimal literal: A = 10, B = 11, ... Z = 35 187 func iso13616Expand(s string) (string, error) { 188 var parts []string 189 for _, c := range s { 190 i := uint64(c) 191 // 0-9 or A-Z 192 if i < 48 || (i > 57 && i < 65) || i > 90 { 193 return "", errICAPEncoding 194 } 195 196 if i > 97 { 197 parts = append(parts, strconv.FormatUint(uint64(c)-87, 10)) 198 } else if i >= 65 { 199 parts = append(parts, strconv.FormatUint(uint64(c)-55, 10)) 200 } else { 201 parts = append(parts, string(c)) 202 } 203 } 204 return join(parts...), nil 205 } 206 207 func join(s ...string) string { 208 return strings.Join(s, "") 209 } 210 211 func LeftJoin(str string, resultLen int) string { 212 if len(str) < resultLen { 213 str = join(strings.Repeat("0", resultLen-len(str)), str) 214 } 215 return str 216 }